Linux OpenGL 实践篇-11-shadow

OpenGL 阴影

 在三维场景中,为了使场景看起来更加的真实,通常需要为其添加阴影,OpenGL可以使用很多种技术实现阴影,其中有一种非常经典的实现是使用一种叫阴影贴图的实现,在本节中我们将使用阴影贴图来实现一个简单场景的阴影。

原理

使用阴影贴图实现阴影,原理就是使用OpenGL渲染到贴图的方式把当前场景通过深度测试的片元的深度值渲染到一张深度贴图中,然后再次渲染物体时通过深度比较判断片元是否在阴影中。

实现步骤

主要分为两个步骤:

1.从光源的角度渲染场景,这一次的渲染我们不关心场景看起来像什么,只是为了获取片元的深度值,并把这个深度值存储到一张深度贴图中,这个深度表示的是光源的光线所能达到的最大深度;

2.从摄像机的角度再次渲染场景,在渲染片元时同时计算片元在光源坐标系下的深度值,使用这个深度值和深度贴图中存储的同一片元的深度值比较,如果小于或等于深度贴图中的深度值,则表示不在阴影中,否则就是在阴影中。

代码

创建深度贴图,同时作为渲染附件附加到帧缓存中。

        //创建帧缓存
        glGenFramebuffers(1,&fbo);
        glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo);


    //创建深度纹理 glGenTextures(1,&depthTex); glBindTexture(GL_TEXTURE_2D,depthTex); glTexImage2D(GL_TEXTURE_2D,0,GL_DEPTH_COMPONENT,width,height,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_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE); //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC,GL_LEQUAL); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); //绑定 glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_TEXTURE_2D,depthTex,0);

绑定帧缓存,把本次的渲染结果存储到深度贴图中。

       //渲染阴影贴图                                         
       glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo);
       glViewport(0,0,width,height);
       glClearDepth(1.0);
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
       
       //打开多边形偏移,以避免深度数据的zfighting问题
       glEnable(GL_POLYGON_OFFSET_FILL);
       glPolygonOffset(2.0f,4.0f);

       //渲染
       tShader->Use();
       tShader->SetMatrix("model",boxModelMat.Get());
       glBindVertexArray(boxVao);
       glDrawArrays(GL_TRIANGLES,0,36);
       tShader->SetMatrix("model",planeModelMat.Get());
       glBindVertexArray(vao);
       glDrawArrays(GL_TRIANGLES,0,6);

       glDisable(GL_POLYGON_OFFSET_FILL);

渲染阴影贴图所使用的着色器,只是最简单的着色器,片元着色器甚至什么都不干。

shadow.vert

#version 330 core 

layout(location=0) in vec3 iPos;
uniform mat4 model;
uniform mat4 lightSpace;                                    

void main()
{
        gl_Position = lightSpace * model * vec4(iPos,1.0);
}

shadow.frag

#version 330 core 

void main()
{
        //gl_FragDepth = gl_FragCoord.z;
}

也可以把注释的代码放开表示显示设置片元的深度,但注释掉后更有效率,因为底层无论如何都会设置深度缓冲。

完成阴影贴图渲染后,再次渲染场景,并使用阴影贴图。

        //回到摄像机视角
        glBindFramebuffer(GL_FRAMEBUFFER,0);
        glViewport(0,0,width,height);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        //绘制场景
        planeShader->Use();
        glBindTexture(GL_TEXTURE_2D,tt);
        planeShader->SetMatrix("model",boxModelMat.Get());                  
        glBindVertexArray(boxVao);
        glDrawArrays(GL_TRIANGLES,0,36);
        planeShader->Use();
        glBindTexture(GL_TEXTURE_2D,depthTex);
        modelShader->SetMatrix("model",planeModelMat.Get());
        glBindVertexArray(vao);
        glDrawArrays(GL_TRIANGLES,0,6);
        glBindTexture(GL_TEXTURE_2D,0);

  阴影贴图着色器,注意在本次的实践中,因为只渲染了最简单的box,没有使用纹理,所以使用了默认的glBindTexture来绑定阴影贴图;但在复杂模型渲染时,一定要注意阴影贴图的绑定(在着色器中使用多张纹理)。

plane.vert

#version 330 core

layout(location=0) in vec3 iPos;
layout(location=1) in vec2 iTexcoords;

uniform mat4 model;
uniform mat4 view;
uniform mat4 proj;
uniform mat4 lightView;

out VS_OUT {
        vec3 fragPos;
        vec4 fragPosLightSpace;
}vs_out;

out vec2 texcoords;

void main()                                                              
{
        vs_out.fragPos = vec3(model * vec4(iPos,1.0));
        vs_out.fragPosLightSpace = lightView * vec4(vs_out.fragPos,1.0);
        texcoords = iTexcoords;
        gl_Position = proj * view * model * vec4(iPos,1.0);
}

plane.frag

#version 330 core                                                           

in VS_OUT {
        vec3 fragPos;
        vec4 fragPosLightSpace;
} fs_in;

in vec2 texcoords;
uniform sampler2D shadowMap;
out vec4 color;

float ShadowCalculation(vec4 fragPosLightSpace)
{
        vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
        projCoords = projCoords * 0.5 + 0.5;

        float closestDepth = texture(shadowMap, projCoords.xy).r;
        float currentDepth = projCoords.z;

        float shadow = currentDepth > closestDepth ? 1.0 : 0.0;
        return shadow;
}

void main()
{
        vec4 fragPosLightSpace = fs_in.fragPosLightSpace;
        vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
        projCoords = projCoords * 0.5 + 0.5;

        float shadow = ShadowCalculation(fs_in.fragPosLightSpace);
        vec3 red = vec3(1,0,0);
        vec3 lighting = vec3(0.1,0.1,0.1) +  (1-shadow) * red;
        color =  vec4(lighting,1.0);
}

效果

完整代码:

https://github.com/xin-lover/opengl-learn/tree/master/chapter-11-shadow

猜你喜欢

转载自www.cnblogs.com/xin-lover/p/9033822.html