Hi,
I am developing a realtime volume renderer based on OpenGL/MFC
http://zjprogramming.../vx/vxhome.html
I have a problem about saving opengl render to a RGBA tiff image.
That is what i did read the frame buffer.
glReadBuffer(GL_BACK);
glReadPixels(0,0,width,height, GL_RGBA, GL_FLOAT, data);
Size of the data is width*height*4
Then data is written to a TIFF file using libtiff.
I have tested this approach on GLUT/linux, works fine. But on MFC/Windows, only RGB channels be OK, the ALPHA channel always being totally white. I think it is due to the pixelformat.
Do you have any idea about the solution?
Thanks in advance,
zhang
OpenGL/MFC: problem to save alpha-channel to Tiff
Started by zhang_mdev, Jun 21 2005 01:12 PM
8 replies to this topic
#1
Posted 21 June 2005 - 01:12 PM
#2
Posted 21 June 2005 - 03:38 PM
Make sure that when you set your pixel format, it includes destination alpha. You may need to call DescribePixelFormat on the number returned from ChoosePixelFormat to check this.
reedbeta.com - developer blog, OpenGL demos, and other projects
#3
Posted 22 June 2005 - 03:08 AM
BOOL CHelloView::PreCreateWindow(CREATESTRUCT& cs)
{
cs.style |= WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
return CView::PreCreateWindow(cs);
}
void CHelloView::OnDraw(CDC* pDC)
{
CHelloDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
DrawGL();
SwapBuffers(m_pDC->m_hDC);
}
int CHelloView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
m_pDC = new CClientDC(this);
SetDCPixelFormat(m_pDC->m_hDC);
m_GLRC = wglCreateContext (m_pDC->m_hDC);
wglMakeCurrent (m_pDC->m_hDC, m_GLRC);
InitGL();
return 0;
}
void CHelloView::OnDestroy()
{
wglMakeCurrent (NULL,NULL);
wglDeleteContext (m_GLRC);
delete m_pDC;
}
void CHelloView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
ViewGL(cx, cy);
_width = cx;
_height = cy;
}
int CHelloView::SetDCPixelFormat(HDC hdc)
{
static PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
32, // 32-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accumulation bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
int iPixelFormat = ChoosePixelFormat( hdc, &pfd );
if ( SetPixelFormat( hdc, iPixelFormat, &pfd ) == FALSE ) {
return FALSE;
}
if ( DescribePixelFormat( hdc, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &pfd ) == 0 ) {
return FALSE;
}
return TRUE;
}
GLint CHelloView::InitGL(void)
{
glClearColor(0,0,0.5,0);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
return 0;
}
GLint CHelloView::DrawGL()
{
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glLoadIdentity();
glTranslated(0,0,-2);
glBegin(GL_TRIANGLES);
glColor3f(1,0,0);
glVertex3f( -0.5, -0.5, -2.0);
glColor3f(0,1,0);
glVertex3f( 0.5, -0.5, -2.0);
glColor3f(0,0,1);
glVertex3f( 0.0, 0.5, -2.0);
glEnd();
return 0;
}
GLint CHelloView::ViewGL(GLint w, GLint h)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(35.0, (GLfloat)w/(GLfloat)h, 1.0, 100.0 );
glViewport(0,0,w,h);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return 0;
}
void CHelloView::OnKeyDown( UINT nChar, UINT nRepCnt, UINT nFlags )
{
switch( nChar ) {
case VK_F5:
saveImage();
AfxMessageBox("Saved");
break;
}
}
void CHelloView::saveImage()
{
TIFF *tif = TIFFOpen( "test.tiff", "w");
if(tif) {
TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, _width);
TIFFSetField(tif, TIFFTAG_IMAGELENGTH, _height);
TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
}
char * raster = (char *) malloc(sizeof(char) * _width * _height * 4);
char * data = (char *) malloc(sizeof(char) * _width * _height*4);
glReadBuffer(GL_BACK);
//glPixelStorei(GL_PACK_ALIGNMENT, 1);
glReadPixels(0, 0, _width, _height, GL_RGBA, GL_UNSIGNED_BYTE, data);
for(int h=0; h< _height; h++) {
for(int w=0; w<_width; w++) {
raster[(h*_width+w)*4 ] = data[(h*_width+w)*4];
raster[(h*_width+w)*4+1] = data[(h*_width+w)*4+1];
raster[(h*_width+w)*4+2] = data[(h*_width+w)*4+2];
raster[(h*_width+w)*4+3] = data[(h*_width+w)*4+3];
}
}
TIFFWriteEncodedStrip(tif, 0, raster, _width * _height *4);
TIFFClose(tif);
free(data);
free(raster);
}
Hi,
Above is the code i am testing. Press F5 to save image. Everything is fine except white alpha. I am sure it is not due to the TIFF writing. What is going wrong?
Thanks,
zhang
#4
Posted 22 June 2005 - 04:07 AM
32, // 32-bit color depth 0, 0, 0, 0, 0, 0, // color bits ignored 0, // no alpha buffer
This is what's going wrong. You need to request an alpha buffer. Change that last zero to an 8 and it should work.
reedbeta.com - developer blog, OpenGL demos, and other projects
#5
Posted 22 June 2005 - 04:43 AM
HI Reedbeta,
Thanks for your instant answer. I modify the code to use 8-bit alpha, and it works!

But msdn doc said that alpha bitplanes are not supported in the pixelformatdescriptor doc. I once tried 1 there but no luck. And alpha blending stills works when there is no alpha buffer. That is comfusing.
Thanks agian,
zhang
Thanks for your instant answer. I modify the code to use 8-bit alpha, and it works!

But msdn doc said that alpha bitplanes are not supported in the pixelformatdescriptor doc. I once tried 1 there but no luck. And alpha blending stills works when there is no alpha buffer. That is comfusing.
Thanks agian,
zhang
#6
Posted 22 June 2005 - 06:16 AM
Alpha blending and the alpha buffer (aka destination alpha) are two completely different things.
And the MSDN doc is talking only about the Microsoft "generic" implementation. But if you have a good video card the driver is free to implement features above and beyond this.
And the MSDN doc is talking only about the Microsoft "generic" implementation. But if you have a good video card the driver is free to implement features above and beyond this.
reedbeta.com - developer blog, OpenGL demos, and other projects
#7
Posted 22 June 2005 - 07:10 AM
Hi,
Thanks for your reply.
I have another problem with the frame buffer. The program renders a lot of semi-transparent slice from far to near. Each slice is at a very low opacity. It use alpha blending to achive the sense of volume.
The alpha buffer seems to be overwrite by the current slice opacity after each new slice is drawn. My question is how to get the accumulated alpha value? I want to get the opacity of the volume, not a nearest slice.
Thanks,
zhang
Thanks for your reply.
I have another problem with the frame buffer. The program renders a lot of semi-transparent slice from far to near. Each slice is at a very low opacity. It use alpha blending to achive the sense of volume.
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
The alpha buffer seems to be overwrite by the current slice opacity after each new slice is drawn. My question is how to get the accumulated alpha value? I want to get the opacity of the volume, not a nearest slice.
Thanks,
zhang
#8
Posted 22 June 2005 - 04:11 PM
By default, the blending operation is applied to both the color and alpha parts of the buffer. There is an extension that will let you set separate blending functions, called GL_EXT_blend_func_separate. With this extension, you would do:
That will combine the colors normally, but perform proper accumulation on the alpha values. However, if you don't have this extension, it is still possible to do this in two passes, by drawing to the color buffers only in the first pass, and the alpha buffer only on the second pass. You can do this using glColorMask:
Note that if you use the second method, you should call glDepthTest(GL_LEQUAL) somewhere in your initialization code, to avoid z-fighting.
glBlendFuncSeparateEXT(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
That will combine the colors normally, but perform proper accumulation on the alpha values. However, if you don't have this extension, it is still possible to do this in two passes, by drawing to the color buffers only in the first pass, and the alpha buffer only on the second pass. You can do this using glColorMask:
glColorMask(true, true, true, false); // enable RGB, disable alpha glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Draw geometry glColorMask(false, false, false, true); // disable RGB, enable alpha glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // Draw geometry again
Note that if you use the second method, you should call glDepthTest(GL_LEQUAL) somewhere in your initialization code, to avoid z-fighting.
reedbeta.com - developer blog, OpenGL demos, and other projects
#9
Posted 23 June 2005 - 09:19 AM
Hi Reedbeta,
Thanks for your help. I have just tried your solutions.
It seems that glBlendFuncSeparateEXT is not supported on my FX 5600 :(
The result of second approach appeared to be interesting. Seems that color buffer bit has never been cleared before each drawing, and the alpha still remains the same. I use GL_LEQUAL depthfunc,

Here is my workaround:
Use initail blendfunc for a normal render. When that is a need to save a output. First Read RGB from the front buffer, which has already been drawn. Second clear color and depth buffer, and set color mash just to write alpah and enable new blendfunc. Then re-draw the slice into the off-screen buffer and don't swap buffers. So that people will not notice the second render. Finally, read the alpha bit from the back buffer and revert the color mask and blendfunc to initial settings.
Not the best solution since it needs another render pass to run, fortunately i am not saving images all the time:)

Thanks,
zhang
Thanks for your help. I have just tried your solutions.
It seems that glBlendFuncSeparateEXT is not supported on my FX 5600 :(
The result of second approach appeared to be interesting. Seems that color buffer bit has never been cleared before each drawing, and the alpha still remains the same. I use GL_LEQUAL depthfunc,

Here is my workaround:
Use initail blendfunc for a normal render. When that is a need to save a output. First Read RGB from the front buffer, which has already been drawn. Second clear color and depth buffer, and set color mash just to write alpah and enable new blendfunc. Then re-draw the slice into the off-screen buffer and don't swap buffers. So that people will not notice the second render. Finally, read the alpha bit from the back buffer and revert the color mask and blendfunc to initial settings.
Not the best solution since it needs another render pass to run, fortunately i am not saving images all the time:)

Thanks,
zhang
1 user(s) are reading this topic
0 members, 1 guests, 0 anonymous users












