Windows OpenGL implementation bug?
I am having a very tough time sorting out this strange clipping bug in my app.
Basically, for some reason, OpenGL is clipping (using the scissor test) my call to glClear(), but not the rendering I do afterwards.
The real problem, however, is that the problem goes away when I resize my window. I can guarantee that resizing the window doesn't change anything in my app or run any code. It is very stange. Worse still, simply putting
glDisable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
where I need to disable the scissor test, instead of having just one call to glDisable() solves the problem. So does removing the code all together (the scissor test is already disabled in this test case, but the code is there for when it wasn't left to disabled in previous code). It even solves the problem to put:
glEnable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
There are only two explanations I can think of. Either I am somehow calling UB (which I doubt, because opengl doesn't have UB AFAIK), or there is an implementation bug, because calling glDisable() twice with the same parameter consecutively SHOULD be the same as calling it once... if I'm not mistaken.
JUST incase it is of interest, here is the function for which the problem is happening:
void gle::Renderer::setup3DCamera(gle::CameraNode& cam, gle::Colour bkcol,
int clrmask, int skymode, gle::Texture* skytex, bool uselight) {
// Viewport
Rectangle wr(cam.getViewport()?*cam.getViewport():Rectangle(0,0,1,1));
if (cam.isRatioViewport()||(!cam.getViewport())) {
if (i_frameBind==NULL)
wr.scale(selectedWindow->getWidth(),selectedWindow->getHeight());
else wr.scale(i_frameBind->getWidth(),i_frameBind->getHeight());
}
gle::Rectangle_t<int> iport; iport.set(wr);
int winHei;
if (i_frameBind==NULL)
winHei = selectedWindow->getHeight();
else
winHei = i_frameBind->getHeight();
glViewport(iport.x1(),winHei-iport.y2(),iport.wid(),iport.hei());
// Viewport Clipping
if (cam.isClipping()) {
/* This is never executed in the test case */
glEnable(GL_SCISSOR_TEST);
glScissor(iport.x1(),winHei-iport.y2(),iport.wid(),iport.hei());
} else {
/* This is where I disable the scissor test */
glDisable(GL_SCISSOR_TEST);
glDisable(GL_SCISSOR_TEST);
}
float w=wr.wid()/2, h=wr.hei()/2;
// Projection
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
Projection proj = cam.getProjection();
gluPerspective(proj.fov,proj.aspect*(w/h),proj.cnear,proj.cfar);
// Camera
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
float m[] = { 1,0,0,0, 0,0,-1,0, 0,1,0,0, 0,0,0,1 };
glMultMatrixf(m);
static gle::Mesh *skyBox = NULL;
// Screen Clearing
switch (clrmask&GLE_CLR_COLOUR&0x00F?skymode:GLE_SKYNONE) {
case GLE_SKYNONE:
clear(clrmask&(~GLE_CLR_COLOUR)); break;
case GLE_SKYCOLOUR:
clearColour(clrmask,bkcol); break;
case GLE_SKYBOX:
glDisable(GL_DEPTH_TEST);
if (!(clrmask&GLE开发者_JAVA技巧_CLR_DEPTH&0x00F)) glDepthMask(0);
float m = (cam.getProjection().cnear+cam.getProjection().cfar)/2.0f;
if (skyBox==NULL) skyBox = gle::createStockMesh(GLE_MESHSKYBOX,GLE_WHITE,0,m);
glEnable(GL_TEXTURE_2D);
glDisable(GL_CULL_FACE);
skytex->flush();
glBindTexture(GL_TEXTURE_2D,skytex->getID());
glDisable(GL_LIGHTING);
glPushMatrix();
float m3[16];
Orientation::matrixSet(m3,cam.pos().getMatrix(GLE_ROTMATRIX));
Orientation::matrixTranspose(m3);
glMultMatrixf(m3);
if (i_reflectionOn) glMultMatrixf(Orientation::matrixGet3x3(i_reflectionTransform));
renderMesh(*skyBox,NULL,1);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
if (clrmask&GLE_CLR_DEPTH) glClear(GL_DEPTH_BUFFER_BIT);
else glDepthMask(1);
glAble(GL_DEPTH_TEST,depthmode!=GLE_ALWAYS);
break;
}
// Camera
glMultMatrixf(cam.getAbsInverseMatrix());
if (i_reflectionOn) glMultMatrixf(i_reflectionTransform);
// Lighting
i_lightOn = uselight;
glAble(GL_LIGHTING,i_lightOn);
}
This looks like a driver bug to me. However, there are two cases where this may actually be a bug in your code.
First, you might be in the middle of the glBegin() / glEnd() block when calling that glDisable(), causing some error and also ending the block, effectively making the second call to glDisable() legit and effective. Note that this is a dumb example with glBegin() / glEnd(), it could be pretty much any case of OpenGL error being caught. Insert glGetError() calls throughout your code to be sure. My guess is the first call to glDisable() generates GL_INVALID_OPERATION.
Second, you are not scissor testing, but you are still calling glViewport() with the same values. This would have the opposite effect (not clip glClear() and clip drawing) on NVIDIA, but it might very well be it does the opposite on some other driver / GL implementation.
精彩评论