如何在opengl中实现z-fail算法?

时间:2012-09-23 09:09:20

标签: opengl shadow stencil-buffer

我编码为NeHe导师Lesson27告诉我,但它是一个z-pass算法。当我在阴影中时,阴影消失。有人告诉我,我可以使用z-fail算法来解决这个问题。 所以我花了两天时间来研究z-fail算法。最后,我无法弄明白。我的程序永远不会像我想的那样运行。

列出wiki的z-fail算法:

深度失败 在2000年左右,几个人发现Heidmann的方法可以通过反转深度来适用于所有相机位置。而不是计算对象表面前面的阴影表面,它后面的表面可以很容易地计算,具有相同的最终结果。这解决了眼睛处于阴影中的问题,因为眼睛和物体之间的阴影体积不计算,但是引入了阴影体积的后端必须加盖的条件,或者阴影将最终丢失在体积指向的位置落后于无限。

  1. 禁用对深度和颜色缓冲区的写入。

  2. 使用正面剔除。

  3. 将模板操作设置为在深度失败时递增(仅计算对象后面的阴影)。

  4. 渲染阴影卷。

  5. 使用背面剔除。

  6. 将模板操作设置为在深度失败时递减。

  7. 渲染阴影卷。

  8. 我认为的主要问题是深度测试。在步骤3和6,模板操作基于深度失败。虽然它可以显示阴影,但它可能在它之前的对象上阴影(即:深度缓冲值小于它的对象)。所有阴影效果看起来一团糟。

    但是在z-pass算法中,模板操作是基于深度传递,这意味着它不仅可以显示阴影,而且仅在其后面的对象上阴影,符合眼睛系统。

    所以如何解决这个问题让我的深度失败算法显示出正确对象的阴影。

    这是我的z-fail算法代码(某处可能在哪里,请帮我看看,阴影效果很糟糕)

    VECTOR vec;        
    void shadowvolume(SECTOR &sec,float *lp)
    {
        unsigned int    p1, p2;
        VECTOR          v1, v2;
        int i, j, k, jj;
        for (i=0; i<sec.numplanes;i++)
        {
            if (sec.planes[i].visible)
            {
                for (j=0;j<3;j++)
                {
                    k = sec.planes[i].neigh[j];
                    if ((!k) || (!sec.planes[k-1].visible))//如果以第k个点开始的邻边没有相邻平面或者相邻平面不可见
                    {
                        // here we have an edge, we must draw a polygon
                        p1 = sec.planes[i].p[j]-1;//邻边的起点
                        jj = (j+1)%3;           
                        p2 = sec.planes[i].p[jj]-1;//邻边的终点
    
                        //calculate the length of the vector
                        v1.x = (sec.points[p1].vec.x - lp[0])*100;
                        v1.y = (sec.points[p1].vec.y - lp[1])*100;
                        v1.z = (sec.points[p1].vec.z - lp[2])*100;
    
                        v2.x = (sec.points[p2].vec.x - lp[0])*100;
                        v2.y = (sec.points[p2].vec.y - lp[1])*100;
                        v2.z = (sec.points[p2].vec.z - lp[2])*100;
    
                        glBegin(GL_TRIANGLE_STRIP);//将光源连到邻边的起点并延长,将光源连到邻边的终点的并延长,最后延长出来的梯形,画了过后模板缓冲区的值加1
                        glVertex3f(sec.points[p1].vec.x,sec.points[p1].vec.y,sec.points[p1].vec.z);
                        glVertex3f(sec.points[p1].vec.x + v1.x,sec.points[p1].vec.y + v1.y,sec.points[p1].vec.z + v1.z);
                        glVertex3f(sec.points[p2].vec.x,sec.points[p2].vec.y,sec.points[p2].vec.z);
                        glVertex3f(sec.points[p2].vec.x + v2.x,sec.points[p2].vec.y + v2.y,sec.points[p2].vec.z + v2.z);
                        glEnd();
                    }
                }
                // caps
                glBegin(GL_TRIANGLES);  
                for(k=0;k<3;k++)
                    glVertex3fv((float*)&sec.points[sec.planes[i].p[k]-1].vec);
                glEnd();
                glBegin(GL_TRIANGLES);  
    
                for(k=2;k>=0;k--)
                {
                    vec.x=sec.points[sec.planes[i].p[k]-1].vec.x+(sec.points[sec.planes[i].p[k]-1].vec.x-lp[0])*100;
                    vec.y=sec.points[sec.planes[i].p[k]-1].vec.y+(sec.points[sec.planes[i].p[k]-1].vec.y-lp[1])*100;
                    vec.z=sec.points[sec.planes[i].p[k]-1].vec.z+(sec.points[sec.planes[i].p[k]-1].vec.z-lp[2])*100;
                    glVertex3fv((float*)&vec);
                }
                glEnd();
    
            }
        }
    
    
    
    }
    void  CastShadow(SECTOR &sec, float *lp)
    {//lp是光源相对于物体的位置
        float           side;
    
        glEnable(GL_CULL_FACE); 
        int i;
        for (i=0;i<sec.numplanes;i++)
        {
            side =sec.planes[i].planeeq.a*lp[0]+sec.planes[i].planeeq.b*lp[1]+sec.planes[i].planeeq.c*lp[2]+sec.planes[i].planeeq.d*lp[3];
            if (side>0) 
                sec.planes[i].visible = TRUE;
            else
                sec.planes[i].visible = FALSE;
        }
    
        glDisable(GL_LIGHTING);
        glDepthMask(GL_FALSE);
        glDepthFunc(GL_LEQUAL);
        glEnable(GL_STENCIL_TEST);
        glColorMask(0, 0, 0, 0);
        glStencilFunc(GL_ALWAYS, 0, 0xffffffff);
    
        glCullFace(GL_FRONT);
        glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
        //glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
        shadowvolume(sec,lp);
    
        glCullFace(GL_BACK);
        glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
        //glStencilOp(GL_KEEP,GL_KEEP,  GL_INCR);
        shadowvolume(sec,lp);
    
    
    
        glColorMask(1, 1, 1, 1);
    
        //draw a shadowing rectangle covering the entire screen
        glColor4f(0.0f, 0.0f, 0.0f,0.4f);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glStencilFunc(GL_NOTEQUAL, 0, 0xffffffff);
        //glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
        glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
        glPushMatrix();
        glLoadIdentity();
        glBegin(GL_TRIANGLE_STRIP);
            glVertex3f(-0.1f, 0.1f,-0.0010f);
            glVertex3f(-0.1f,-0.1f,-0.0010f);
            glVertex3f( 0.1f, 0.1f,-0.0010f);
            glVertex3f( 0.1f,-0.1f,-0.0010f);
        glEnd();
        glPopMatrix();
        glDisable(GL_BLEND);
    
        glDepthFunc(GL_LEQUAL);
        glDepthMask(GL_TRUE);
        glEnable(GL_LIGHTING);
        glDisable(GL_STENCIL_TEST);
        glShadeModel(GL_SMOOTH);
        glDisable(GL_CULL_FACE);
    }
    

    VECTOR类是这样的:

    class VECTOR
    {
    public:
        float x,y,z;
        bool operator==(VECTOR vec)
        {
            if(x==vec.x && y==vec.y && z==vec.z)
                return true;
            return false;
        }
    };
    

    SECTOR课程和其他人是这样的:

    class PLANEEQ
    {
    public:
        float a,b,c,d;
    };
    class PLANE
    {
    public:
        unsigned int p[3];//点的序号
        VECTOR normal[3];
        unsigned int neigh[3];//平面3个相依平面的序号
        PLANEEQ planeeq;
        bool visible;
        PLANE()
        {
            neigh[0]=0;
            neigh[1]=0;
            neigh[2]=0;
            planeeq.a=0;
            planeeq.b=0;
            planeeq.c=0;
            planeeq.d=0;
            visible=false;
        }
    };
    
    class SECTOR
    {
    public:
        int numpoints;
        int numplanes;
        vector<VERTEX> points;
        vector<PLANE> planes;
        MATERIAL material;
        bool read();
        bool loadtexture();
        bool build();
        bool plane_calc();
        void SetConnectivity();
        SECTOR& SECTOR::subdivide(long depth);
        SECTOR(string str1,string str2):modelfilename(str1),texturefilename(str2)
        {
            numpoints=0;
            numplanes=0;
    
        }
        SECTOR()
        {
            numpoints=0;
            numplanes=0;
    
        }
    
    private:
        FILE *modelfilein,*texturefilein;
        string modelfilename,texturefilename;
        char oneline[255];
        UINT texturename;
        AUX_RGBImageRec *TextureImage;
    };
    class POSITION
    {
    public:
        float x,y,z,w;
    };
    

    我的main.cpp中的DrawGLScene函数是这样的:

    int DrawGLScene(GLvoid)                                 
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT);  
        glLoadIdentity();
        DrawGLRoom();
        glLoadIdentity();
        GLfloat xtrans = -xpos;
        GLfloat ztrans = -zpos;
        GLfloat ytrans = -ypos-1.2f;
        GLfloat sceneroty = 360.0f - yrot;  
    
        glRotatef(lookupdown,1.0f,0,0);
        glRotatef(sceneroty,0,1.0f,0);
        glTranslatef(xtrans, ytrans, ztrans);
        brick_sec.build();
        floor_sec.build();
        //wall_sec.build();
    
        //CastShadow(wall_sec,(float *)&lightgroup.lights[0].pos);
        CastShadow(brick_sec,(float*)&lightgroup.lights[0].pos);
        CastShadow(floor_sec,(float*)&lightgroup.lights[0].pos);    
    
    
        lightgroup.build(); 
        glColor4f(0.7f, 0.4f, 0.0f, 1.0f);  
        glDisable(GL_LIGHTING);                             
        glDepthMask(GL_FALSE);                              
        glTranslatef(lightgroup.lights[0].pos.x, lightgroup.lights[0].pos.y, lightgroup.lights[0].pos.z);               
        gluSphere(q, 0.2f, 16, 8);  
        glEnable(GL_LIGHTING);  
        glDepthMask(GL_TRUE);
        if(space_time>0)
        {
            ypos=sin(space_time*3.1415926/180);
            space_time-=4;
        }
        else
        {
            sp=false;
        }
        //glFlush();
        return TRUE;                                        // Everything Went OK
    }
    

    由于我的声望低于10,我无法捕捉到阴影效果,向你展示它看起来有多糟糕!请帮助我,我会请你注意你的时间!!! / / p> Naxzero给了我5点声望,现在我可以抓住屏幕来显示效果。我将在下面附上详细说明。

    z-pass算法效果: 当我没有效果时,没关系!(橙色的锅代表光线) enter image description here

    但是当我在wall_shadow中时,它不行!尽管brick_shadow仍在那里,但wall_shadow已经消失了。

    enter image description here

    所以我需要z-fail算法来解决这个问题。但是我的代码实现的最后一个效果是这样的: enter image description here 刻度表示阴影效果是正确的,十字表示阴影不应出现在对象上。

    另一个截图, enter image description here

1 个答案:

答案 0 :(得分:2)

哈哈,最后,我发现我的代码中存在问题。我很高兴,哈哈!!!!!!!!!

问题是gluPerspective(45.0f,(GLfloat)宽度/(GLfloat)高度,0.001f,100.0f);

正如GuentherKrass在http://www.opengl.org/discussion_boards/showthread.php/146157-Z-Fail-Stencil-Shadow-Volumes

中所说的那样

如果你这样做,一定要使用带有无限远平面的透视投影矩阵,或者使用GL_DEPTH_CLAMP来避免后剪裁被远剪裁平面剔除。

所以我只需将上面的代码更改为

gluPerspective(45.0f,(GLfloat)宽度/(GLfloat)高度,0.001f,1000000.0f);

好吧,它看起来很完美!!!!!!!!!!!!!!!!! 111 haha​​hahaaa

两天,熬夜,方便面......这该死的,真值得!!

好的,我会把最后的效果图片拿出来。如果有人想要我的代码只是给我发电子邮件(nomorefancy@gmail.com)

enter image description here enter image description here enter image description here enter image description here

注意: 砖影与墙影无关。