From: Anye Li <li....@gm...> - 2009-04-02 20:58:53
|
Hi, I have a program that is supposed to draw surfaces illuminated by a directional light source regardless of whether or not there is anything between the surface and the viewer. I am using depth mapping and programmable shading to do this. As an additional complication, I am incorporating a rotation in my projection matrix. I'm sure this is an exotic use of the projection matrix, but it is important in my particular application. At any rate, it seems I can do this and get the results I expect and want from NVIDIA's OpenGL implementation, but not from Mesa. I have attached at the bottom of this message the simplest program I know how to write that demonstrates what I am talking about. Unfortunately, it is not very short or simple. I built it against Mesa and MesaGLUT 7.4, with an important bug fix that otherwise prevents the vertex shader from compiling. It's supposed to draw the surfaces of a box illuminated by a directional light source pointing straight up. Linking against NVIDIA's OpenGL, I see the green face, and part of the cyan and yellow face, which is what I expect. Linking against Mesa, I don't see any of the yellow face. My guess so far is that the depth was written on the +x (red) face, which is discarded. Because the +x face is drawn before the -z (yellow) face, the -z (yellow) face fails the depth test. If I disable the depth test, the correct pixels will be lit; I just end up seeing the color that's drawn last. I think if the fragment is discarded, its depth should not be written. Is my interpretation correct, and if so, can somebody fix this? Thanks, Anye Li ---Cut from here--- /* Exotic illumination and projections. * * This program uses depth mapping and programmable shading * to draw the all the surfaces of a box illuminated by * by a directional light source, including those that would * ordinarily be obscured from view. * * As an additional twist, the projection matrix incorporates * a rotation. */ /* HARDCODED PARAMETERS */ #define MAP_SIZE 512 #define SHADER_CALCULATES_SHADOW 0 #define VISUALIZE_DEPTH 0 /* Search for MAIN to jump past the boring stuff. */ #define GL_GLEXT_PROTOTYPES #include <GL/glut.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <math.h> #define STR(x) #x #define XSTR(x) STR(x) #ifndef NDEBUG #define CHECK_GL_ERROR(s) {\ GLenum glerror=glGetError();\ if (glerror) {\ fprintf(stderr,__FILE__ \ ":" XSTR(__LINE__) \ ": OpenGL error `%s' caused by `%s'.\n",\ gluErrorString(glerror),s);}} #define CHECK_GL(expr) { expr; CHECK_GL_ERROR(#expr); } #else #define CHECK_GL_ERROR(s) ((void)0) #define CHECK_GL(expr) (expr) #endif /* UTILITY PROCEDURES */ #define XMALLOC(s) xmalloc(s,"File " __FILE__ ", line " XSTR(__LINE__)) void *xmalloc(size_t s,const char *where) { void *p=malloc(s); if (!p) { perror(where); fprintf(stderr,"O, I am slain! <falls and dies>\n"); exit(-1); } return p; } /* Rotate from (x0,y0,z0) to (x1,y1,z1); * store the matrix that does this in M. * This is just Rodrigues' formula; */ void make_rotate(double *M, double x0, double y0, double z0, double x1, double y1, double z1) { double c,s,wx,wy,wz,n; c=x0*x1+y0*y1+z0*z1; wx=y0*z1-z0*y1; wy=z0*x1-x0*z1; wz=x0*y1-y0*x1; n=c*c+wx*wx+wy*wy+wz*wz; if (n<1e-12) { c=1.; wx=wy=wz=0.; } else { n=1./sqrt(n); c*=n; wx*=n; wy*=n; wz*=n; } s=wx*wx+wy*wy+wz*wz; if (s<1e-12) { s=wx=wy=wz=0.;} else { s=sqrt(s); n=1./s; wx*=n; wy*=n; wz*=n; } n=1-c; M[0]=c+wx*wx*n; M[5]=c+wy*wy*n; M[10]=c+wz*wz*n; M[1]=wx*wy*n; M[2]=wx*wz*n; M[6]=wy*wz*n; wx*=s; wy*=s; wz*=s; M[4]=M[1]-wz; M[1]+=wz; M[8]=M[2]+wy; M[2]-=wy; M[9]=M[6]-wx; M[6]+=wx; M[3]=M[7]=M[11]=M[12]=M[13]=M[14]=0.;M[15]=1.; } /* Draw an axis-aligned solid box centered at (cx,cy,cz) * with side lengths lx,ly,lz. */ void draw_solid_box(double cx, double cy, double cz, double lx, double ly, double lz) { double hx,hy,hz; lx*=.5; ly*=.5; lz*=.5; hx=cx+lx; hy=cy+ly; hz=cz+lz; cx-=lx; cy-=ly; cz-=lz; glBegin(GL_QUADS); /* -x */ glColor3f(0.f,1.f,1.f); glNormal3f(-1.f,0.f,0.f); glVertex3d(cx,cy,cz); glVertex3d(cx,cy,hz); glVertex3d(cx,hy,hz); glVertex3d(cx,hy,cz); /* +x */ glColor3f(1.f,0.f,0.f); glNormal3f(1.f,0.f,0.f); glVertex3d(hx,cy,cz); glVertex3d(hx,hy,cz); glVertex3d(hx,hy,hz); glVertex3d(hx,cy,hz); /* -y */ glColor3f(1.f,0.f,1.f); glNormal3f(0.f,-1.f,0.f); glVertex3d(cx,cy,cz); glVertex3d(hx,cy,cz); glVertex3d(hx,cy,hz); glVertex3d(cx,cy,hz); /* +y */ glColor3f(0.f,1.f,0.f); glNormal3f(0.f,1.f,0.f); glVertex3d(cx,hy,cz); glVertex3d(cx,hy,hz); glVertex3d(hx,hy,hz); glVertex3d(hx,hy,cz); /* -z */ glColor3f(1.f,1.f,0.f); glNormal3f(0.f,0.f,-1.f); glVertex3d(cx,cy,cz); glVertex3d(cx,hy,cz); glVertex3d(hx,hy,cz); glVertex3d(hx,cy,cz); /* +z */ glColor3f(0.f,0.f,1.f); glNormal3f(0.f,0.f,1.f); glVertex3d(cx,cy,hz); glVertex3d(hx,cy,hz); glVertex3d(hx,hy,hz); glVertex3d(cx,hy,hz); glEnd(); } GLuint shader_compilecomplain(const GLchar *src, GLenum type) { GLuint shader; GLint status; CHECK_GL(shader=glCreateShader(type)); CHECK_GL(glShaderSource(shader,1,(const GLchar **)&src,NULL)); glCompileShader(shader); glGetShaderiv(shader,GL_COMPILE_STATUS,&status); if (!status) { GLchar *log; GLint len; glGetShaderiv(shader,GL_INFO_LOG_LENGTH,&len); log=(char *)XMALLOC(len); glGetShaderInfoLog(shader,len,&len,log); fprintf(stderr,"Shader compilation failed. " "Log follows.\n%s\n",log); free(log); } return shader; } void program_linkcomplain(GLint program) { glLinkProgram(program); { GLint status; glGetProgramiv(program,GL_LINK_STATUS,&status); if (!status) { GLint len; GLchar *log; glGetProgramiv(program,GL_INFO_LOG_LENGTH,&len); log=(char *)XMALLOC(len); glGetProgramInfoLog(program,len,&len,log); fprintf(stderr,"Linkage failed. Log follows.\n%s\n", log); free(log); } } } /* MAIN */ GLuint depthtex,fbo; GLuint prog,vertshader,fragshader; /* Vertex shader code. * n is the outward normal, l is the direction of light; * ndotl is dot(n,l). Assumes a directional light source. * Calculate light clip coordinates and pass them through * gl_TexCoord[0]. */ const GLchar vertsrc[]= "varying float ndotl;\n" "void main(void)\n" "{{ vec3 n=gl_NormalMatrix*gl_Normal;\n" " vec3 l=-gl_LightSource[0].position.xyz;\n" " ndotl=dot(n,l);}\n" " gl_FrontColor=gl_Color;\n" " { vec4 v=gl_ModelViewMatrix*gl_Vertex;\n" " gl_TexCoord[0].xyz=vec3(dot(gl_EyePlaneS[0],v),\n" " dot(gl_EyePlaneT[0],v),\n" " dot(gl_EyePlaneR[0],v));\n" " gl_Position=gl_ProjectionMatrix*v;}}\n"; /* Fragment shader code. * Everything facing away from the light is discarded. * Compare the light clip depth calculated by the vertex shader * with that drawn to the depth map. If the calculated depth * is farther than the value on the depth map, it is in shadow. */ const GLchar fragsrc[]= "uniform sampler2DShadow depthtex;\n" "varying float ndotl;\n" "void main(void)\n" "{ vec4 shad; if (ndotl>=0.) discard;\n" " shad=shadow2D(depthtex,gl_TexCoord[0].xyz);\n" #if SHADER_CALCULATES_SHADOW #if VISUALIZE_DEPTH " gl_FragColor=vec4(shad.aaa,1.);\n" #else " if (shad.a<gl_TexCoord[0].z) discard;\n" " gl_FragColor=gl_Color;\n" #endif #else " if (shad.a<1.0) discard;\n" " gl_FragColor=gl_Color;" #endif "}\n"; void draw_scene(void) { glRotated(10.,5.,6.,7.); draw_solid_box(0.,-.1,0.,.3,.4,.3); } void setup_depth_texture(void) { glGenTextures(1,&depthtex); glBindTexture(GL_TEXTURE_2D,depthtex); glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT32, 512,512,0,GL_DEPTH_COMPONENT,GL_FLOAT,NULL); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); glTexParameteri(GL_TEXTURE_2D,GL_DEPTH_TEXTURE_MODE,GL_ALPHA); #if !SHADER_CALCULATES_SHADOW glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL); #endif } void setup_framebuffer(void) { CHECK_GL(glGenFramebuffersEXT(1,&fbo)); CHECK_GL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fbo)); CHECK_GL(glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D,depthtex,0)); } void setup_shaders(void) { vertshader=shader_compilecomplain(vertsrc,GL_VERTEX_SHADER); fragshader=shader_compilecomplain(fragsrc,GL_FRAGMENT_SHADER); CHECK_GL(prog=glCreateProgram()); CHECK_GL(glAttachShader(prog,vertshader)); CHECK_GL(glAttachShader(prog,fragshader)); program_linkcomplain(prog); } void init(void) { glEnable(GL_DEPTH_TEST); glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); glTexGeni(GL_Q,GL_TEXTURE_GEN_MODE,GL_EYE_LINEAR); CHECK_GL(setup_depth_texture()); setup_framebuffer(); setup_shaders(); glNewList(1,GL_COMPILE); CHECK_GL(draw_scene()); glEndList(); } void display(void) { double M[16]; float s[4],t[4],r[4],q[4]; float lightpos[4]={ -.94628453310570848f, .28999526006621095f, -.14299766272230402f, 0.f}; /* This is a right-handed orthonormal matrix * (rotation matrix) whose 2nd column is * -lightpos. */ float P[16]={ /*q*/ .041766889332500293f, -.49456215170547763f, .7265665497849193f, 0.f, /*r*/ .94628453310570848f, -.28999526006621095f, .14299766272230402f, 0.f, /*p*/ -.28142208732508411f, .68156612077971923f, .48010871475250755f, 0.f, /* translation */ 0.f,0.f,0.f,1.f}; /* Draw everything from the point of view of the light source. */ CHECK_GL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fbo)); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glClear(GL_DEPTH_BUFFER_BIT); /* I am using the cull front face trick to avoid * precision issues. Since my box is closed, * this should work. */ glEnable(GL_CULL_FACE); glCullFace(GL_FRONT); /* Need to set the viewport to match the size of the * depth texture/framebuffer. */ glPushAttrib(GL_VIEWPORT_BIT); glViewport(0,0,512,512); /* Set up an orthographic projection for a directional * light source. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.,1.,-1.,1.,-1.,1.); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* The `viewer' is at (0,0,Inf). * To see things from the point of view of the light source, * rotate the light to that position. */ make_rotate(M, lightpos[0],lightpos[1],lightpos[2], 0.,0.,1.); glMultMatrixd(M); glCallList(1); /* Restore the viewport. */ glPopAttrib(); /* Draw everything from the point of view of the * viewer. */ CHECK_GL(glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0)); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); /* Activate the depth texture and auotmatic tex coord * generation. */ glBindTexture(GL_TEXTURE_2D,depthtex); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glEnable(GL_TEXTURE_GEN_Q); /* Activate the shaders. */ CHECK_GL(glUseProgram(prog)); /* Show me *everything* that's illuminated. */ glDisable(GL_CULL_FACE); /* Set up the viewer's projection. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1.,1.,-1.,1.,-1.,1.); glMultTransposeMatrixf(P); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* These are the rows of the combined * bias * projection * view matrix. */ s[0]= .5f*M[0]; s[1]= .5f*M[4]; s[2]= .5f*M[ 8]; s[3]= .5f*M[12]+.5f; t[0]= .5f*M[1]; t[1]= .5f*M[5]; t[2]= .5f*M[ 9]; t[3]= .5f*M[13]+.5f; r[0]=-.5f*M[2]; r[1]=-.5f*M[6]; r[2]=-.5f*M[10]; r[3]=-.5f*M[14]+.5f; q[0]=M[3]; q[1]=M[7]; q[2]=M[11]; q[3]=M[15]; /* Set the eye planes, with respect to the view matrix, * taken as the current MODELVIEW matrix. */ glTexGenfv(GL_S,GL_EYE_PLANE,s); glTexGenfv(GL_T,GL_EYE_PLANE,t); glTexGenfv(GL_R,GL_EYE_PLANE,r); glTexGenfv(GL_Q,GL_EYE_PLANE,q); /* Draw the scene. */ glLightfv(GL_LIGHT0,GL_POSITION,lightpos); glCallList(1); /* Deactivate the depth texture and automatic tex coord * generation.*/ glDisable(GL_TEXTURE_GEN_R); glDisable(GL_TEXTURE_GEN_Q); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_S); glBindTexture(GL_TEXTURE_2D,0); glutSwapBuffers(); } void keyboard(unsigned char key, int x, int y) { exit(0); } int main(int argc, char **argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_RGBA|GLUT_ALPHA|GLUT_DEPTH); glutCreateWindow(*argv); glutDisplayFunc(display); glutKeyboardFunc(keyboard); puts("Press any key to quit."); init(); glutMainLoop(); return 0; } |