Rendering to bitmap differs from rendering to screen

I rendered some lines to a bitmap and the same lines directly to screen and the image differs on some graphic-cards. For example on a Geforce 2 MX440 with Microsoft driver everything is fine but if i use the NVIDEA driver the lines differ. On boards with Matrox G400 everything is ok but on boards with ATI Radeon 7000 lines differ too.

Here’s a short example which can be used to reproduce the effect. It draws green circles in a bitmap and displays it on the screen. Then it draws the same circles with red color in the same window. On some PCs you can still see some green points beside the red circles.

Does anybody know how to avoid the green pixels an all graphicboards?

#include <windows.h>
#include <gl/gl.h>
#include <math.h>

HBITMAP hbitmap;
HDC memdc;
HGLRC hglrc;

LRESULT WINAPI MainWndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
WNDCLASS wc;
MSG msg;
HWND hWnd;

if(!hPrevInstance)
{
wc.lpszClassName=“OpenGLAppClass”;
wc.lpfnWndProc=MainWndProc;
wc.style=CS_OWNDC | CS_VREDRAW | CS_HREDRAW;
wc.hInstance=hInstance;
wc.hIcon=LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor=LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground=(HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName=NULL;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
RegisterClass(&wc);
}
hWnd=CreateWindow(“OpenGLAppClass”,
“OpenGL Application”,
WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
0,0,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
ShowWindow(hWnd, nCmdShow);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}

void InitGL(HWND hwnd)
{
RECT rect;

GetClientRect(hwnd,&rect);
glViewport(0,0,rect.right,rect.bottom);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glOrtho((GLdouble) -0.5,(GLdouble) (rect.right-0.5),
(GLdouble) -0.5,(GLdouble) (rect.bottom-0.5),
(GLdouble) -1.,(GLdouble) 1.);
glDrawBuffer(GL_FRONT);
glShadeModel(GL_FLAT);
glEnable(GL_LINE_STIPPLE);
glLineStipple(1,-1);
glLineWidth(1);
glFlush();
}

#define PI 3.1415926535897932384626433832795
void DrawGL(HWND hwnd)
{
RECT rect;
int i,ianzs,ix,iy;
double hr,hxm,hym;
double xh,yh,hxp,hyp;
double sinal,cosal;
double hxa,hya;
double alpha;

GetClientRect(hwnd,&rect);
hxm=((rect.right+rect.left)0.5);
hym=((rect.top+rect.bottom)0.5);
for(hr=hxm/2;hr>10.;hr-=5.)
{
alpha=acos(1.0-0.4/hr);
if(alpha<0.00001) alpha=0.00001;
ianzs=(int)((PI+PI)/alpha);
alpha=(PI+PI)/ianzs;
sinal=sin(alpha);
cosal=cos(alpha);
hxa=hxm+hr;
hya=hym;
glBegin(GL_LINE_STRIP);
ix=(int)(hxa);
iy=(int)(hya);
glVertex2i(ix,iy);
hxp=hxa;
hyp=hya;
for(i=1;i<ianzs;i++)
{
xh=hxp-hxm;
yh=hyp-hym;
hxp=xh
cosal-yh
sinal+hxm;
hyp=xhsinal+yhcosal+hym;
ix=(int)(hxp);
iy=(int)(hyp);
glVertex2i(ix,iy);
}
ix=(int)(hxa);
iy=(int)(hya);
glVertex2i(ix,iy);
glEnd();
}
glFlush();
}

void InitWindow(HWND hwnd)
{
PIXELFORMATDESCRIPTOR pfd;
int iPixelFormat;
HDC hdc;

hdc=GetDC(hwnd);
pfd.nSize=sizeof(pfd);
pfd.nVersion=1;
pfd.dwFlags=PFD_DRAW_TO_WINDOW| PFD_SUPPORT_OPENGL | PFD_SUPPORT_GDI;
pfd.cAccumAlphaBits=0;
pfd.cAccumBits=0;
pfd.cAccumBlueBits=0;
pfd.cAccumGreenBits=0;
pfd.cAccumRedBits=0;
pfd.cAlphaBits=0;
pfd.cAlphaShift=0;
pfd.cAuxBuffers=0;
pfd.cBlueBits=0;
pfd.cBlueShift=0;
pfd.cColorBits=(unsigned char) 32;
pfd.cDepthBits=32;
pfd.cGreenBits=0;
pfd.cGreenShift=0;
pfd.cRedBits=0;
pfd.cRedShift=0;
pfd.cStencilBits=0;
pfd.dwDamageMask=0;
pfd.dwLayerMask=0;
pfd.dwVisibleMask=0;
pfd.iLayerType=PFD_MAIN_PLANE;
pfd.iPixelType=PFD_TYPE_RGBA;
iPixelFormat=ChoosePixelFormat(hdc, &pfd);
DescribePixelFormat(hdc,iPixelFormat,sizeof(PIXELFORMATDESCRIPTOR),&pfd);
SetPixelFormat(hdc,iPixelFormat,&pfd);
hglrc=wglCreateContext(hdc);
ReleaseDC(hwnd,hdc);
}

void CreateGLBitmap(HWND hwnd)
{
int iPixelFormat;
int ncolor;
PIXELFORMATDESCRIPTOR pfd;
BITMAPINFO *biinfo;
void *lpbits;
RECT rect;
int size;
HGLRC hglrn;
HDC hdc;

if(memdc) DeleteDC(memdc);
if(hbitmap) DeleteObject(hbitmap);
memdc=NULL;
hbitmap=NULL;
GetClientRect(hwnd,&rect);
hdc=GetDC(hwnd);
memdc=CreateCompatibleDC(hdc);
if(!memdc) return;
iPixelFormat=GetPixelFormat(hdc);
DescribePixelFormat(hdc,iPixelFormat,sizeof(PIXELFORMATDESCRIPTOR),&pfd);
if((pfd.iPixelType==PFD_TYPE_RGBA)&&(pfd.cColorBits>8))
ncolor=0;
else
ncolor=1<<pfd.cColorBits;
biinfo=(BITMAPINFO )malloc(sizeof(BITMAPINFO)+ncolorsizeof(RGBQUAD));
if(biinfo==NULL) return;
ZeroMemory(biinfo, sizeof(BITMAPINFO));
biinfo->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
biinfo->bmiHeader.biWidth=rect.right;
biinfo->bmiHeader.biHeight=rect.bottom;
biinfo->bmiHeader.biPlanes=1;
if(pfd.cColorBits<=4)
biinfo->bmiHeader.biBitCount=4;
else if(pfd.cColorBits<=8)
biinfo->bmiHeader.biBitCount=8;
else if(pfd.cColorBits<=16)
biinfo->bmiHeader.biBitCount=16;
else if(pfd.cColorBits<=24)
biinfo->bmiHeader.biBitCount=24;
else
biinfo->bmiHeader.biBitCount=32;
biinfo->bmiHeader.biCompression=BI_RGB;
size=biinfo->bmiHeader.biWidth * biinfo->bmiHeader.biBitCount;
size=((size + 31)/32 * 4);
biinfo->bmiHeader.biSizeImage=size * biinfo->bmiHeader.biHeight;
hbitmap=CreateDIBSection(hdc,biinfo,DIB_RGB_COLORS,
&lpbits,NULL,(DWORD)0);
free(biinfo);
if(!hbitmap) return;
SelectObject(memdc,hbitmap);
pfd.nSize=sizeof(pfd);
pfd.nVersion=1;
pfd.dwFlags=PFD_DRAW_TO_BITMAP| PFD_SUPPORT_OPENGL | PFD_SUPPORT_GDI;
pfd.cAccumAlphaBits=0;
pfd.cAccumBits=0;
pfd.cAccumBlueBits=0;
pfd.cAccumGreenBits=0;
pfd.cAccumRedBits=0;
pfd.cAlphaBits=0;
pfd.cAlphaShift=0;
pfd.cAuxBuffers=0;
pfd.cBlueBits=0;
pfd.cBlueShift=0;
pfd.cColorBits=(unsigned char) biinfo->bmiHeader.biBitCount;
pfd.cDepthBits=32;
pfd.cGreenBits=0;
pfd.cGreenShift=0;
pfd.cRedBits=0;
pfd.cRedShift=0;
pfd.cStencilBits=0;
pfd.dwDamageMask=0;
pfd.dwLayerMask=0;
pfd.dwVisibleMask=0;
pfd.iLayerType=PFD_MAIN_PLANE;
pfd.iPixelType=PFD_TYPE_RGBA;
iPixelFormat=ChoosePixelFormat(memdc, &pfd);
DescribePixelFormat(memdc,iPixelFormat,sizeof(PIXELFORMATDESCRIPTOR),&pfd);
SetPixelFormat(memdc,iPixelFormat,&pfd);
hglrn=wglCreateContext(memdc);
wglMakeCurrent(memdc,hglrn);
InitGL(hwnd);
glClearColor(0.,0.,1.,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f((GLfloat)0.0,(GLfloat)1.0,(GLfloat)0.0);
DrawGL(hwnd);
wglDeleteContext(hglrn);
ReleaseDC(hwnd,hdc);
}

LRESULT CALLBACK MainWndProc(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;

switch(msg)
{
case WM_CREATE:
InitWindow(hwnd);
CreateGLBitmap(hwnd);
break;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
if(memdc) BitBlt(hdc,ps.rcPaint.left,ps.rcPaint.top,
ps.rcPaint.right-ps.rcPaint.left,ps.rcPaint.bottom-ps.rcPaint.top,
memdc,ps.rcPaint.left,ps.rcPaint.top,SRCCOPY);
wglMakeCurrent(hdc,hglrc);
InitGL(hwnd);
glColor3f((GLfloat)1.0,(GLfloat)0.0,(GLfloat)0.0);
DrawGL(hwnd);
wglMakeCurrent(NULL,NULL);
EndPaint(hwnd,&ps);
break;
case WM_DESTROY:
if(hglrc)
{
wglMakeCurrent(NULL,NULL);
wglDeleteContext(hglrc);
}
if(memdc) DeleteDC(memdc);
if(hbitmap) DeleteObject(hbitmap);
PostQuitMessage(0);
break;
case WM_SIZE:
CreateGLBitmap(hwnd);
default:
return(DefWindowProc(hwnd, msg, wParam, lParam));
}
return 0;
}

You can’t, because you can’t control where the rendering occurs (in hardware or software). And that is a likely source of the precision errors.

The only time OpenGL is guaranteed to give the same (pixel-exact) results is to render into the same target with the same command sequence. The bitmap and the screen are different targets.

Depending on the resolution you were trying to achieve in the bitmap rendering, you could always render to a back buffer of the display and copy the result to your bitmap.

Since the rendering target is the same as the display the bitmap should be identical to the display.

Originally posted by robosport:
[b]Depending on the resolution you were trying to achieve in the bitmap rendering, you could always render to a back buffer of the display and copy the result to your bitmap.

Since the rendering target is the same as the display the bitmap should be identical to the display.[/b]
Yes, but how do I copy the back buffer to a bitmap? All I can do with the back buffer is to swap it into the front buffer but I can’t define another target.

You can’t render to a bitmap and have hardware acceleration. That’s software emulation, and rendering gets done by Microsoft’s rusty old software GL implementation.

That’s also the reason why you see no difference compared to the Geforce 2 MX with “Microsoft driver”, because the card doesn’t render anything in that case, it just displays the result.

I suspect it’s the same thing with the Matrox G400. I’ve seen Matrox cards catastrophically fail at all sorts of simple things related to OpenGL. Wouldn’t surprise me at all if you in fact got Microsoft’s software implementation again.

You read the back buffer with glReadPixels. Note that this only works reliably if the window is completely visible (not obscured by other windows). If you want to handle that case, you need pbuffers.

I changed the Pixelformat-Flag in both cases to
pfd.dwFlags=PFD_DRAW_TO_BITMAP| PFD_DRAW_TO_WINDOW| PFD_SUPPORT_OPENGL | PFD_SUPPORT_GDI;
Now the same rendering-contexts are chosen and it works. But all rendering is now done by software. :frowning:
GlReadPixel (much to slow) or a bitblt (which is much faster) is no solution because parts of the window are obscured by other windows.
Pbuffers are also no solution because most cards (e.g. all cards I mentioned in my first post) don’t support pbuffers.

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.