使用GLSL的OpenGL阴影映射

时间:2010-12-19 19:03:42

标签: python opengl glsl shader pyopengl

我正在尝试使用GLSL进行阴影贴图。不幸的是,即使我有相当不错的深度缓冲精度,我的深度渲染结果也无法使用。它呈现像线框,下面的图像可能是一个更好的描述。

我还包括一个测试用例(包含着色器的单个文件),只有依赖是pyopengl。

alt text

# shadow mapping test
# utkualtinkaya at gmail 
# shader is from http://www.fabiensanglard.net/shadowmapping/index.php

from OpenGL.GL import *
from OpenGL.GLU import * 
from OpenGL.GLUT import *
from OpenGL.GL.shaders import *
from OpenGL.GL.framebufferobjects import *
import math

class Camera:
    def __init__(self):
        self.rotx, self.roty = math.pi/4, math.pi/4
        self.distance = 100
        self.moving = False
        self.ex, self.ey = 0, 0
        self.size = (800, 600) 

    def load_matrices(self):
        glViewport(0, 0, *self.size)
        y = math.cos(self.roty) * self.distance
        x = math.sin(self.roty) * math.cos(self.rotx) * self.distance
        z = math.sin(self.roty) * math.sin(self.rotx) * self.distance

        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(45.0, self.size[0]/float(self.size[1]), 1, 1000)

        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        gluLookAt(x,y,z, 0,0,0, 0,1,0)

    def on_mouse_button (self, b, s, x, y):
        self.moving = not s
        self.ex, self.ey = x, y
        if b in [3, 4]:
            dz = (1 if b == 3 else -1)
            self.distance += self.distance/15.0 * dz;

    def on_mouse_move(self, x, y, z = 0):
        if self.moving:            
            self.rotx += (x-self.ex) / 300.0
            self.roty += -(y-self.ey) / 300.0
            self.ex, self.ey = x, y

    def set_size(self, w, h):
        self.size = w, h

class Shader():
    def __init__(self):
        self.is_built = False
        self.uniforms = {}

    def build(self):
        self.program = compileProgram(
        compileShader('''
            uniform mat4 camMatrix;
            uniform mat4 shadowMatrix;
            varying vec4 depthProjection;
            uniform bool useShadow;

            void main() {
                gl_Position = camMatrix * gl_ModelViewMatrix * gl_Vertex;
                depthProjection = shadowMatrix * gl_ModelViewMatrix * gl_Vertex;
                gl_FrontColor = gl_Color;
            }
        ''',GL_VERTEX_SHADER),
        compileShader('''
            varying vec4 depthProjection;
            uniform sampler2D shadowMap;
            uniform bool useShadow;

            void main () {
                float shadow = 1.0;
                if (useShadow) {
                    vec4 shadowCoord = depthProjection / depthProjection.w ;
                    // shadowCoord.z -= 0.0003;            
                    float distanceFromLight = texture2D(shadowMap, shadowCoord.st).z;                                
                    if (depthProjection .w > 0.0)
                        shadow = distanceFromLight < shadowCoord.z ? 0.5 : 1.0 ;            

                    }
                gl_FragColor = shadow * gl_Color;
              }
        ''',GL_FRAGMENT_SHADER),)
        self.is_built = True

        self.uniforms['camMatrix'] = glGetUniformLocation(self.program, 'camMatrix')
        self.uniforms['shadowMatrix'] = glGetUniformLocation(self.program, 'shadowMatrix')
        self.uniforms['shadowMap'] = glGetUniformLocation(self.program, 'shadowMap')
        self.uniforms['useShadow'] = glGetUniformLocation(self.program, 'useShadow')
        print self.uniforms

    def use(self):
        if not self.is_built:
            self.build()
        glUseProgram(self.program)

class Test:
    def __init__(self):
        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH)
        glutInitWindowSize(800, 600)
        glutInitWindowPosition(1120/2, 100)
        self.window = glutCreateWindow("Shadow Test")
        self.cam = Camera()
        self.light = Camera()
        self.cam.set_size(800, 600)
        self.light.set_size(2048, 2048)
        self.light.distance = 100
        self.shader = Shader()
        self.initialized = False        

    def setup(self):
        self.initialized = True
        glClearColor(0,0,0,1.0);
        glDepthFunc(GL_LESS)
        glEnable(GL_DEPTH_TEST)

        self.fbo = glGenFramebuffers(1);
        self.shadowTexture = glGenTextures(1)

        glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)

        w, h = self.light.size

        glActiveTexture(GL_TEXTURE5)         
        glBindTexture(GL_TEXTURE_2D, self.shadowTexture)

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );

        glTexImage2D( GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, w, h, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, None)

        glDrawBuffer(GL_NONE)
        glReadBuffer(GL_NONE)

        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, self.fbo, 0)

        FBOstatus = glCheckFramebufferStatus(GL_FRAMEBUFFER)
        if FBOstatus != GL_FRAMEBUFFER_COMPLETE:
            print ("GL_FRAMEBUFFER_COMPLETE_EXT failed, CANNOT use FBO\n");

        glBindFramebuffer(GL_FRAMEBUFFER, 0)
        #glActiveTexture(GL_TEXTURE0)                

    def draw(self):
        glPushMatrix()
        glTranslate(0, 10 ,0)
        glColor4f(0, 1, 1, 1)
        glutSolidCube(5)
        glPopMatrix()

        glPushMatrix()
        glColor4f(0.5, 0.5, .5, 1)
        glScale(100, 1, 100)
        glutSolidCube(1)
        glPopMatrix()

    def apply_camera(self, cam):
        cam.load_matrices()
        model_view = glGetDoublev(GL_MODELVIEW_MATRIX);
        projection = glGetDoublev(GL_PROJECTION_MATRIX);
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        glMultMatrixd(projection)
        glMultMatrixd(model_view)        
        glUniformMatrix4fv(self.shader.uniforms['camMatrix'], 1, False, glGetFloatv(GL_MODELVIEW_MATRIX))
        glLoadIdentity()     

    def shadow_pass(self):
        glUniform1i(self.shader.uniforms['useShadow'], 0)

        glBindFramebuffer(GL_FRAMEBUFFER, self.fbo)
        glClear(GL_DEPTH_BUFFER_BIT)
        glCullFace(GL_FRONT)
        self.apply_camera(self.light)        
        self.draw()
        glBindFramebuffer(GL_FRAMEBUFFER, 0)

    def final_pass(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

        self.light.load_matrices()
        model_view = glGetDoublev(GL_MODELVIEW_MATRIX);
        projection = glGetDoublev(GL_PROJECTION_MATRIX);        
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
        bias = [ 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]
        glLoadMatrixd(bias)
        glMultMatrixd(projection)
        glMultMatrixd(model_view)
        glUniformMatrix4fv(self.shader.uniforms['shadowMatrix'], 1, False, glGetFloatv(GL_MODELVIEW_MATRIX))

        glActiveTexture(GL_TEXTURE5)
        glBindTexture(GL_TEXTURE_2D, self.shadowTexture)                
        glUniform1i(self.shader.uniforms['shadowMap'], 5)

        glUniform1i(self.shader.uniforms['useShadow'], 1);

        self.apply_camera(self.cam)
        glLoadIdentity()
        glCullFace(GL_BACK)
        self.draw()

    def render(self):
        if not self.initialized: self.setup()
        self.shader.use()        
        self.shadow_pass()
        self.final_pass()        
        glutSwapBuffers()

    def mouse_move(self, *args):
        self.cam.on_mouse_move(*args)
        self.light.on_mouse_move(*args)

    def mouse_button(self, b, *args):
        if b==0:
            self.light.on_mouse_button(b, *args)
        else:
            self.cam.on_mouse_button(b, *args)

    def main(self):
        glutDisplayFunc(self.render)
        glutIdleFunc(self.render)
        glutMouseFunc(self.mouse_button)
        glutMotionFunc(self.mouse_move)
        glutReshapeFunc(self.cam.set_size)
        #self.setup()
        glutMainLoop()

if __name__ == '__main__':
    test = Test()
    test.main()

2 个答案:

答案 0 :(得分:3)

解决了它,它是绑定问题,在阴影传递片段着色器中我只是检查了一个布尔值来禁用读取纹理,但这还不够。我应该在阴影传递之前取消绑定纹理,这在文档中提到:

引用OpenGL Refence: 当纹理对象当前被绑定并可能被当前顶点或片段着色器采样时,需要采取特殊预防措施以避免将纹理图像附加到当前绑定的帧缓冲区。

NVIDIA忽略了这一点,正如文档所述,ATI表现得非常“未定义”。

答案 1 :(得分:-2)

// shadowCoord.z -= 0.0003;

你注释掉的阴影偏见是解决z战斗问题的方法。将其更改为某个值,例如+ = 0.0005你应该好好去。