• Shadow Map 实现极其细节


         这里不介绍算法原理,只说说在实现过程中遇到的问题,以及背后的原因。开发环境:opengl 2.0  glsl 1.0。

         第一个问题:产生深度纹理。

         在opengl中每一次离屏渲染需要向opengl提供一个renderframe,一个renderframe包含一个texture和一个renderbuffer.texture是一个存储特定数据的内存区,可以存储颜色,深度以及模版。renderbuffer目前不太清楚。

         具体代码如下:

          

        glGenFramebuffers(1, &frameBuff) ;
        glBindFramebuffer(GL_FRAMEBUFFER, frameBuff) ;
        
        glGenTextures(1, &depthTxe) ;
        glBindTexture(GL_TEXTURE_2D, depthTxe) ;
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16 , mapWidth, mapHight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTxe, 0);
        glDrawBuffer(GL_NONE) ;
        glReadBuffer(GL_NONE) ; 
         GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
            if( result == GL_FRAMEBUFFER_COMPLETE) {
            cout << "Framebuffer is complete.
    " << endl ;
        } else {
            cout <<"Framebuffer is not complete.
    " << endl ;
        }

          这里需要注意的是,一定要加    glDrawBuffer(GL_NONE) ;  glReadBuffer(GL_NONE) 。分别告诉opengl没有缓冲区接受或者读取颜色数据。因为我们需要的是深度数据。如果没有这两句,那么返回的result是not complete。

          曾经尝试过使用 glTexImage2D 中使用 GL_RGB 而不是 GL_DEPTH_COMPONENT16 相应的下面的 glFramebufferTexture2D 也要使用 GL_COLOR_ATTACHMENT。在fragment shader中将片元的深度值写入color中。但是这样有个问题,就是精度不够,虽然数据是对的,精度在Shadow Map中有着极其重要的位置,稍后介绍。

          问题二:各种坐标系的转化

           这里涉及到两个变化过程。第一个是从物体坐标系-》世界坐标系-》灯光坐标系-》裁剪坐标系(齐次坐标系)-》cvv坐标系(透视除法后得到)。

           另一个是物体坐标系-》世界坐标系-》摄像机坐标系-》裁剪坐标系(齐次坐标系)-》cvv坐标系(透视除法后得到)。

           具体vertex shader 如下

           

    varying vec3 normal ;
    varying vec4 lightVertex ;
    varying vec4 color ;
    varying vec4 worldCoord ;
    uniform mat4 lightProj;
    uniform mat4 lightView;
    const  mat4 biasMatrix = mat4(0.5 , 0.0 , 0.0 , 0.0 ,
                           0.0 , 0.5 , 0.0 , 0.0 ,
                           0.0 , 0.0 , 0.5 , 0.0 ,
                           0.5 , 0.5 , 0.5 , 1.0 ) ;
    void main()
    {
         worldCoord = gl_ModelViewMatrix * gl_Vertex ;
         normal =  normalize(gl_NormalMatrix * gl_Normal);
        lightVertex = lightProj * lightView * worldCoord ;
        lightVertex = lightVertex / lightVertex.w ;
        lightVertex = biasMatrix * lightVertex ;
        //lightVertex = lightVertex / lightVertex.w ;
        gl_TexCoord[0] = gl_MultiTexCoord0 ;
        color = gl_Color ;
        gl_Position =  gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex ;
    }

           这里需要注意的是glsl中没有提供从物体坐标系到世界坐标系的转化,gl_ModelViewMatrix实现的是从物体到相机坐标系的转化,在本次shadow map是实现过程中将一直保证摄像机在世界坐标系的原点位置,方向指向(0,0,-1),向上方向为(0,1,0)。也就是说摄像机和世界坐标系重合。

            注意到 biasMatrix,这里可以推知两点:

            (1)glsl中是采用列主序的方式存放矩阵的,也就是数据先放完第一列再放第二列。

            (2)glsl纹理的坐标被映射到[0,1]之间。用本次shadow map举例,从灯光坐标系到齐次坐标系,经过裁剪以及透视除法后,得到的x,y,z的范围是在[-1,1]之间的,构成一个规范立方体(cvv),那么x , y的值就是纹理在纹理内存中的位置,z就是伪深度,用于z-buffer。但是,opengl在渲染深度纹理时,将x,y,z映射到[0,1]之间,所以要用一个biasMatrix进行平移。

            第三个问题:Shadow acne。

            这个问题的原因如下图:

                                          

            其中,斜线EF是物体表面,当一光柱照射到物体表面时,C点的深度值被写入shadow map中(这里有一个概念:texle , texture element,可以理解为一个像素)。但是,当从摄像机坐标系中看到D点时,通过坐标系转化到shadow map中 ,D点比C点的值远离光源。这样就造成了shadow acne。效果如下:

            

            这时要加上一个偏移两bias,具体计算在fragment shader中,其中的m时具体情况具体调整的,时经验值。

            问题四:阴影有锯齿。

            如下图:

             改进方法时使用多次采样,可以使用采样次sampler2Dshadow,内部会采样一个像素周围的几个像素。也可以使用本次代码中的poissonDisk进行优化。效果如下

           

          这个poissonDisk(柏松盘采样)内容目前不太了解,貌似效果还不错。

         学习心得:在碰到一个新技术时,最好把其最简单的部分实现,然后看看有什么问题,然后慢慢优化。如果不动手实现,很难体会资料上描述的问题的现象,更不用说问题产生的原因。

          一下时全部代码:

    #include <GLUT/GLUT.h>
    #include <iostream>
    #include "LoardShaderProgram.h"
    #include "BW_READBMP.h"
    using namespace std ;
    
    struct COLOR
    {
        float r , g , b, w ;
    } ;
    float rot = 0.1 ;
    float transX = 0 ;
    float transY = 0 ;
    float transZ = 0 ;
    GLint program ;
    GLint lightPosLoc ;
    GLint lightAmbientLoc ;
    GLint lightLambertLoc ;
    GLint lightProjLoc ;
    GLint lightViewLoc ;
    GLint mLoc ;
    GLint ksLoc ;
    GLint kdLoc ;
    GLint kaLoc ;
    GLint texLoc ;
    
    GLint tex ;
    GLint mapWidth =  1024;
    GLint mapHight  = 1024;
    GLuint depthTxe ;
    GLuint frameBuff ;
    GLfloat lightProj[16] ;
    GLfloat lightView[16] ;
    float alf  = 0 ;
    float ks = 1;
    float kd = 0.5 ;
    float ka = 1.0 ;
    float m = 0.0035410088 ;
    COLOR lightPos ;
    void DrawSence()
    {
        glTranslated(0, 0, -8) ;
        glBegin(GL_QUADS);
        glColor3f(1, 0, 0) ;
        
        // ground
        //glNormal3f( 0.0f, 0.0f, 1.0f);
        glTexCoord2f(0.0f, 0.0f);
        glNormal3f(0, 1, 0) ;
        glVertex3f(4.0f, -2.0f, -4.0f);
        glNormal3f(0, 1, 0) ;
        glTexCoord2f(0.0f, 1.0f);
        glVertex3f( -4.0f, -2.0f, -4.0f);
        glNormal3f(0, 1, 0) ;
        glTexCoord2f(1.0f, 1.0f);
        glVertex3f( -4.0f,  -2.0f,  4.0f);
        glNormal3f(0, 1, 0) ;
        glTexCoord2f(1.0f, 0.0f);
        glVertex3f(4.0f,  -2.0f,  4.0f);
        
        glEnd() ;
        
        glTranslated(transX, transY , transZ) ;
        glRotatef(rot,1.0,0.0,1.0);
        glutSolidTeapot(1);
    }
    void ProcessKeyboard(unsigned char key,int x,int y)
    {
        if (key == 'x' || key == 'X')
        {
            rot += 0.8;
        }
        if (key == 'c' || key == 'C')
        {
           m += 0.00001 ;
        }
        if (key == 'd' || key == 'D')
        {
            m -= 0.00001 ;
        }
        if (key == 'u' || key == 'U')
        {
            transX += 0.3 ;
        }
        if (key == 'i' || key == 'I')
        {
            transX -= 0.3 ;
        }
        if (key == 'j' || key == 'J')
        {
            transY += 0.3 ;
        }
        if (key == 'k' || key == 'K')
        {
            transY -= 0.3 ;
        }
        if (key == 'o' || key == 'O')
        {
            transZ += 0.3 ;
        }
        if (key == 'p' || key == 'P')
        {
            transZ -= 0.3 ;
        }
    
        glutPostRedisplay() ;
    }
    
    void ReSizeScene(int width , int height)
    {
        if( height==0 )
        {
            height=1;
        }//if
        
        //设置视口
        glViewport(0,0,width,height);
        //设置透视矩阵
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
        //开始对模型矩阵进行操作
        glMatrixMode(GL_MODELVIEW);
        //复位
        glLoadIdentity();
    }
    void GenDepthMap()
    {
        lightPos.r = 5; lightPos.g = 5; lightPos.b = -5; lightPos.w = 1;
        glBindFramebuffer(GL_FRAMEBUFFER, frameBuff) ;
       glDrawBuffer(GL_NONE) ;
        glViewport(0, 0, mapWidth, mapHight) ;
        glMatrixMode(GL_PROJECTION) ;
        glLoadIdentity() ;
        gluPerspective(90, 4.0/3, 0.1, 100) ;
        glGetFloatv(GL_PROJECTION_MATRIX, lightProj) ;
        glMatrixMode(GL_MODELVIEW) ;
        glLoadIdentity() ;
        gluLookAt(lightPos.r, lightPos.g, lightPos.b, 0, 0, -10, 0, 1, 0) ;
        glGetFloatv(GL_MODELVIEW_MATRIX,lightView) ;
        glDisable(GL_TEXTURE_2D) ;
       // glUseProgram(shadowPro) ;
        glClearColor(1.0, 1.0, 1.0, 1.0 );
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) ;
        DrawSence() ;
        glUseProgram(0) ;
        glBindFramebuffer(GL_FRAMEBUFFER, 0) ;
        glDrawBuffer(GL_FRONT) ;
    }
    void ShowSence()
    {
        ReSizeScene(400, 300) ;
        COLOR lightAmbient , lightLambert  ;
        COLOR CameralDir , CameralPos;
        lightAmbient.r =  0.1;lightAmbient.g = lightAmbient.b = 0.1; lightAmbient.w = 1 ;
        lightLambert.r = 1 ; lightLambert.g = lightLambert.b =  1 ;lightLambert.w = 1 ;
        
        CameralDir.r = 0 ;
        CameralDir.g = 0 ;
        CameralDir.b = -1 ;
        CameralDir.w = 0 ;
        
        CameralPos.r = 0 ;
        CameralPos.g = 0 ;
        CameralPos.b = 0 ;
        CameralPos.w = 1 ;
        ka = 1 ;
        kd = 0.7 ;
        ks = 0.4 ;
        tex = 0 ;
        glActiveTexture(GL_TEXTURE0) ;
        glBindTexture(GL_TEXTURE_2D, depthTxe) ;
        glUseProgram(program) ;
        glUniform4f(lightPosLoc, lightPos.r, lightPos.g, lightPos.b, lightPos.w) ;
        glUniform4f(lightAmbientLoc, lightAmbient.r, lightAmbient.g, lightAmbient.b, lightAmbient.w) ;
        glUniform4f(lightLambertLoc, lightLambert.r, lightLambert.g, lightLambert.b, lightLambert.w) ;
        glUniformMatrix4fv(lightProjLoc, 1, GL_FALSE, lightProj) ;
        glUniformMatrix4fv(lightViewLoc, 1, GL_FALSE, lightView) ;
        glUniform1f(mLoc, m) ;
        glUniform1f(kdLoc, kd) ;
        glUniform1f(ksLoc, ks) ;
        glUniform1f(kaLoc, ka) ;
        glUniform1i(texLoc , tex) ;
        gluLookAt(CameralPos.r, CameralPos.g , CameralPos.b, CameralDir.r, CameralDir.g , CameralDir.b, 0 ,1 , 0 ) ;
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT) ;
        DrawSence() ;
        glUseProgram(0) ;
        glFlush();
    }
    void display()
    {
        GenDepthMap() ;
        ShowSence() ;
    }
    
    void InitScene()
    {
        glClearColor(1.0, 1.0, 0.0, 1.0) ;
        glGenFramebuffers(1, &frameBuff) ;
        glBindFramebuffer(GL_FRAMEBUFFER, frameBuff) ;
        
        glGenTextures(1, &depthTxe) ;
        glBindTexture(GL_TEXTURE_2D, depthTxe) ;
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16 , mapWidth, mapHight, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTxe, 0);
        glDrawBuffer(GL_NONE) ;
        glReadBuffer(GL_NONE) ;
        GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
        if (result == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)
        {
            cout << "frambuffer GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER " << endl ;
        }
        
        if( result == GL_FRAMEBUFFER_COMPLETE) {
            cout << "Framebuffer is complete.
    " << endl ;
        } else {
            cout <<"Framebuffer is not complete.
    " << endl ;
        }
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0) ;
        glBindTexture(GL_TEXTURE_2D, 0) ;
        
        SHADERINFO shaderInfo[2] ;
        shaderInfo[0].shader_type = GL_VERTEX_SHADER ;
        shaderInfo[0].name = "ShadowMap.vertex" ;
        shaderInfo[1].shader_type = GL_FRAGMENT_SHADER ;
        shaderInfo[1].name = "ShadowMap.frag" ;
        program = BWLoadShaders(shaderInfo, 2) ;
        lightPosLoc = glGetUniformLocation(program, "lightPos") ;
        lightAmbientLoc = glGetUniformLocation(program, "lightAmbient") ;
        lightLambertLoc = glGetUniformLocation(program, "lightLambert") ;
        lightProjLoc = glGetUniformLocation(program, "lightProj") ;
        lightViewLoc = glGetUniformLocation(program, "lightView") ;
        mLoc  = glGetUniformLocation(program, "m") ;
        kdLoc = glGetUniformLocation(program, "kd") ;
        ksLoc = glGetUniformLocation(program, "ks") ;
        kaLoc = glGetUniformLocation(program, "ka") ;
        texLoc =  glGetUniformLocation(program, "tex") ;
        glEnable(GL_DEPTH_TEST) ;
        glDepthFunc(GL_LEQUAL);
        glShadeModel(GL_FLAT) ;
        
    }
    
    int main(int argc, char ** argv)
    {
        glutInit(&argc, argv);
        glutInitWindowSize(800, 800);
        glutInitDisplayMode(GLUT_RGB|GLUT_DEPTH) ;
        glutCreateWindow("SHADERTEST");
        glutKeyboardFunc(&ProcessKeyboard) ;
        InitScene() ;
        glutDisplayFunc(display);
        glutMainLoop();
    }

    ShadowMap.vertex

    varying vec3 normal ;
    varying vec4 lightVertex ;
    varying vec4 color ;
    varying vec4 worldCoord ;
    uniform mat4 lightProj;
    uniform mat4 lightView;
    uniform vec4 lightPos ;
    uniform vec4 lightLambert ;
    uniform vec4 lightAmbient ;
    
    const  mat4 biasMatrix = mat4(0.5 , 0.0 , 0.0 , 0.0 ,
                           0.0 , 0.5 , 0.0 , 0.0 ,
                           0.0 , 0.0 , 0.5 , 0.0 ,
                           0.5 , 0.5 , 0.5 , 1.0 ) ;
    void main()
    {
         worldCoord = gl_ModelViewMatrix * gl_Vertex ;
         normal =  normalize(gl_NormalMatrix * gl_Normal);
        lightVertex = lightProj * lightView * worldCoord ;
        lightVertex = lightVertex / lightVertex.w ;
        lightVertex = biasMatrix * lightVertex ;
        //lightVertex = lightVertex / lightVertex.w ;
        gl_TexCoord[0] = gl_MultiTexCoord0 ;
        color = gl_Color ;
        gl_Position =  gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex ;
    }

    ShadowMap.frag

    uniform sampler2D  tex ;
    uniform vec4 lightPos ;
    uniform vec4 lightLambert ;
    uniform vec4 lightAmbient ;
    uniform float m ;
    uniform float ka ;
    uniform float kd ;
    uniform float ks ;
    varying vec3 normal ;
    varying vec4 lightVertex ;
    varying vec4 color ;
    varying vec4 worldCoord ;
    void main()
    {
        vec2 poissonDisk[4] ;
        poissonDisk[0] = vec2( -0.94201624, -0.39906216 ) ;
        poissonDisk[1] = vec2( 0.94558609, -0.76890725 ) ;
        poissonDisk[2] = vec2( -0.094184101, -0.92938870 );
        poissonDisk[3] = vec2( 0.34495938, 0.29387760 ) ;
        vec3 l = normalize(lightPos - worldCoord).xyz ;
        vec3 n = normalize(normal) ;
        vec3 v = normalize(-worldCoord).xyz ;
        float d = max(dot(n , l) , 0.0) ;
        float s = max(dot(normalize(v + l) , n) , 0.0)  ;
        float cosTheta = dot(n , l) ;
        float bias = m*tan(acos(cosTheta));
        float shadow = 1.0 ;
        //使用poissonDisk采样优化锯齿
        for (int i = 0 ; i < 4; ++i)
        {
            if ( texture2D( tex, lightVertex.xy + poissonDisk[i]/700.0 ).z  <  lightVertex.z-bias ){
                shadow -=0.2;
            }
        }
        //不使用优化
       /*float depth = texture2D(tex , lightVertex.xy).z ;
    
        if (depth + bias < lightVertex.z  )
        {
            shadow = 0.2 ;
        }*/
       // shadow = 0.5 ;
        vec4 finalColor = vec4(0.0 , 0.0 , 0.0 , 1.0) ;
        //finalColor *= shadow ;
        finalColor = (finalColor + ks * s * lightLambert + ka*lightAmbient  + kd * d * lightLambert)*shadow;
        gl_FragColor = finalColor ;
    }
  • 相关阅读:
    poj 2112
    写给学大计python的同学的查错方法
    [Win32 API学习] Edit,ListBox的用法,字体的设置
    [Win32 API] FindFirstFile()和FindNextFile()
    初探C++ win32 api——Hello world!
    [学习笔记]舞蹈链(DLX)(C++指针版)
    2020科大回归记
    SCOI2019AFO记
    [学习笔记]动态动态规划/动态DP/DDP
    BZOJ5343[CTSC2018]混合果汁(二分答案+主席树)
  • 原文地址:https://www.cnblogs.com/BlackWalnut/p/4265255.html
Copyright © 2020-2023  润新知