阴影映射使用OpenGL和GLSL的“阴影痤疮”工件

时间:2014-12-06 00:22:49

标签: opengl glsl opengl-3 opengl-4 shadow-mapping

我编写了一个简单的3D应用程序,使用着名的正面剔除技术实现硬和PCF阴影映射算法。不幸的是,这种技术的问题只是密封网格可以产生投射阴影。例如,平面不能产生这样的效果,因为平面本身就是一个正面。

因此解决方案是使用函数'glPolygonOffset',其目标是在“光视图”可见的每个顶点的深度渲染路径中稍微修改深度值。换句话说,需要此功能来避免“阴影痤疮”瑕疵,保持此时所有网格正面。

这是使用硬阴影映射算法显示此类渲染:

enter image description here

正如你所看到的,阴影渲染是完美的,没有任何文物!

这是定义深度纹理渲染路径的C ++客户端代码:

/*glEnable(GL_CULL_FACE);   //OLD TECHNIQUE
    glCullFace(GL_FRONT);*/

for (uint32_t idy = 0; idy < lightSceneNodeList.size(); idy++)
{   
    if (lightSceneNodeList[idy]->IsShadowEnabled())
    {
        type::ShadowCasterPtr pShadowCaster = ShadowManager::GetSingleton()
            .FindShadowCasterByName(lightSceneNodeList[idy]->GetName());
        {
            pShadowCaster->Bind(TARGET_FBO);
            {
                glEnable(GL_POLYGON_OFFSET_FILL);   //NEW TECHNIQUE
                glPolygonOffset(1.1f, 4.0f);

                glClear(GL_DEPTH_BUFFER_BIT);
                glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
                {
                    pShadowCaster->UpdateFrustrumPosition(
                        lightSceneNodeList[idy]->GetParentModelMatrix());
                    pShadowCaster->SetViewport();
                    {
                        for (uint32_t idx = 0; idx < pBatchList.size(); idx++)
                            pBatchList[idx]->Render(pShadowCaster);
                    }
                }
                glDisable(GL_POLYGON_OFFSET_FILL);
            }
            pShadowCaster->Unbind(TARGET_FBO);
        }
    }
}
//glDisable(GL_CULL_FACE);

现在片段着色器代码中使用的代码在第二个渲染路径中计算阴影因子:

/*
** \brief Recover the depth value from shadow map by projection
*/
float Tex2D_Proj(sampler2DShadow shadowSampler, vec4 LightToVertexDir_LS)
{
    float ShadowFactor = 1.0f;
    {
        vec3 LightToVertexDir_CS = LightToVertexDir_LS.xyz/LightToVertexDir_LS.w;

        ShadowFactor = texture(shadowSampler, LightToVertexDir_CS);
    }
    return (ShadowFactor);
}
/*
** \brief Returns biased hard shadow factor.
*/
float Get_2D_Hard_ShadowFactor(sampler2DShadow shadowSampler, int index)
{
    float shadowFactor = 1.0f;
    {
        if (ShadowCoords[index].z <= MaxShadowDist[index])
        {
            if (ShadowCoords[index].w > 0.0f);
            {
                shadowFactor = Tex2D_Proj(shadowSampler, ShadowCoords[index]);
            }
        }
    }
    return (shadowFactor);
}

'ShadowCoords'均匀变量是光空间中的顶点位置,'index'是光指数。

但是现在我在使用PCF阴影映射算法(一个带有4个样本的例子)时遇到了问题,在第一个路径中也使用了'glPolygonOffset'函数:

enter image description here

正如您所看到的,我们可以清楚地看到“阴影痤疮”的神器!

这是片段着色器中的代码:

float Get_2D_PCF_ShadowFactor(sampler2DShadow shadowSampler, int index)
{
    float shadowFactor = 0.0f;
    {
        int kernel_base = int(PCFKernelType[index])/2;
        float kernel_count = pow(int(PCFKernelType[index]), 2.0f);

        if (ShadowCoords[index].z <= MaxShadowDist[index])
        {
            if (ShadowCoords[index].w > 0.0f)
            {
                shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, 1));
                shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(1, 1));
                shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(1, -1));
                shadowFactor += textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, -1));

                shadowFactor *= 0.25f;
            }
        }
    }
    return (shadowFactor);
}

'textureProjOffset'代码等于以下代码:

float Tex2D_Proj_Offset(sampler2DShadow shadowSampler, vec4 LightToVertexDir_LS, vec2 offsetCoords, vec2 shadowMapSize)
{
    float offset_x = 1.0f/shadowMapSize.x;
    float offset_y = 1.0f/shadowMapSize.y;

    float ShadowFactor = 1.0f;
    {
        vec3 LightToVertexDir_CS = LightToVertexDir_LS.xyz/LightToVertexDir_LS.w;
        vec2 ShadowTexCoords = vec2(LightToVertexDir_CS.x, LightToVertexDir_CS.y);

        vec2 DerivedShadowTexCoords = vec2(
            ShadowTexCoords.x + offsetCoords.x * offset_x,
                ShadowTexCoords.y + offsetCoords.y * offset_y);

        ShadowFactor = texture(shadowSampler, vec3(
            DerivedShadowTexCoords, LightToVertexDir_CS.z));
    }
    return (ShadowFactor);
}

只有'textureProjOffset(shadowSampler,ShadowCoords [index],ivec2(0,0))'的调用才能正常工作(当然它指的是第一种硬阴影映射技术)。

如果我只使用以下调用(所以简单的硬阴影映射但使用偏移量):

shadowFactor = textureProjOffset(shadowSampler, ShadowCoords[index], ivec2(-1, 0));

我有以下渲染:

enter image description here

正如您所看到的,只有立方体的右侧有“阴影痤疮”神器!

为了解决我的问题,我尝试了几种代码组合,添加了一些偏差值来处理光空间中的顶点深度值,但没有任何成功。

1 个答案:

答案 0 :(得分:0)

不知道这是否有帮助。也许我的问题过于复杂了。此代码来自Objective C应用程序,但相关代码主要只是C.它运行并运行。您将在场景片段着色器中看到一条语句:if(depthInShadow&gt; 0.006)。阴影数的这个深度是我必须要做的事情&#34; tweak&#34;摆脱痤疮。

这是一个编辑。重新阅读你的帖子后,我看到一个声明:if(ShadowCoords [index] .w&gt; 0.0f)。这看起来非常类似于我的场景片段着色器中的depthInShadow语句,我必须&#34; tweak&#34;为了摆脱粉刺,只需要大于0.0的东西。试一试。

我将保留下面发布的代码,以防其他任何人对影子映射的新人感兴趣。

许多变量都是在.h文件中声明的,但是你会得到这个想法。这是非常标准的阴影映射代码,所以如果您之前已经看过这一切,那么您可以在这里停下来为自己节省很长时间。

我设置了阴影缓冲区和着色器:

// ************************************* Save the Current Frame Buffer

glGetIntegerv(GL_FRAMEBUFFER_BINDING, renderBuffer);

// ************************************ Create the Shadow Map Texture

glGenTextures(1, shadowTexture);
glBindTexture(GL_TEXTURE_2D, shadowTexture[0]);

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_COMPONENT32, SHADOWMAPRATIO * VIEWWIDTH, SHADOWMAPRATIO * VIEWHEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D, shadowTexture[0]);

// ************************************ Create the Shadow Map Frame Buffer

glGenFramebuffersEXT(1, shadowBuffer);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadowBuffer[0]);

// **************************************** No Color Attachment

glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);

// ************************************* Attach the Shadow Texture to It

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, shadowTexture[0], 0);

// ******************************* Check to see if Frame Buffer is Complete

GLenum frameBufferStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT);
if(frameBufferStatus != GL_FRAMEBUFFER_COMPLETE)
{
    NSLog(@"There is a problem with the shadow frame buffer, %d", frameBufferStatus);
    if(frameBufferStatus == GL_INVALID_ENUM) NSLog(@"Invalid Enum.");
    if(frameBufferStatus == GL_INVALID_VALUE) NSLog(@"Invalid Value.");
    if(frameBufferStatus == GL_INVALID_OPERATION) NSLog(@"Invalid Operation");
    if(frameBufferStatus == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT) NSLog(@"Incomplete Attachment");
    if(frameBufferStatus == GL_FRAMEBUFFER_UNSUPPORTED) NSLog(@"Unsupported");
}

// *********************************** Reset the original Frame Buffer

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, renderBuffer[0]);

// ************************************** Compile and Link the Shadow Shaders

ShaderInfo shadowShaderInfo[] = {
        { GL_VERTEX_SHADER, "path/shadow120.vsh" },
        { GL_FRAGMENT_SHADER, "path/shadow120.fsh" },
        { GL_NONE, NULL }
    };

shadowShaders = LoadShaders(shadowShaderInfo);

glUseProgram(shadowShaders);

shadowPositionLoc = glGetAttribLocation(shadowShaders, "ShadowPosition");

shadowViewMatrixLoc= glGetUniformLocation(shadowShaders, "ShadowViewMatrix");
if(shadowViewMatrixLoc == -1) NSLog(@"View Matrix not found in shadow shader");

shadowModelMatrixLoc= glGetUniformLocation(shadowShaders, "ShadowModelMatrix");
if(shadowModelMatrixLoc == -1) NSLog(@"Model Matrix not found in shadow shader");

shadowProjectionMatrixLoc= glGetUniformLocation(shadowShaders, "ShadowProjectionMatrix");
if(shadowProjectionMatrixLoc == -1) NSLog(@"Projection Matrix not found in shadow shader");

shadowColorLoc= glGetUniformLocation(shadowShaders, "FrontColor");
if(shadowColorLoc == -1) NSLog(@"Front Color not found in shadow shader");

统一的阴影矩阵当然是从相机位置开始的。

用于渲染到阴影缓冲区的阴影着色器非常简单。

阴影顶点着色器:

#version 120

attribute vec4 ShadowPosition;

uniform mat4 ShadowModelMatrix;
uniform mat4 ShadowViewMatrix;
uniform mat4 ShadowProjectionMatrix;

void main()
{
    gl_Position = ShadowProjectionMatrix * ShadowViewMatrix * ShadowModelMatrix * ShadowPosition;
}

Shadow片段着色器:

#version 120

uniform vec4 FrontColor;

void main()
{

    gl_FragColor = FrontColor;
}

我首先使用:

将场景渲染到阴影缓冲区
glUseProgram(shadowShaders);

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, shadowBuffer[0]);

glColorMask ( GL_FALSE , GL_FALSE , GL_FALSE , GL_FALSE );

glActiveTexture(GL_TEXTURE5);
glBindTexture(GL_TEXTURE_2D, shadowTexture[0]);

glClearDepth(1.0);
glClear(GL_DEPTH_BUFFER_BIT);

glUniformMatrix4fv(shadowModelMatrixLoc, 1, GL_FALSE, lightGLKModelMatrix.m);
glUniformMatrix4fv(shadowViewMatrixLoc, 1, GL_FALSE, lightGLKViewMatrix.m);
glUniformMatrix4fv(shadowProjectionMatrixLoc, 1, GL_FALSE, lightGLKProjectionMatrix.m);
glUniform4fv(shadowColorLoc, 1, worldAmbient);

.... rendering code ....

glDisable(GL_POLYGON_OFFSET_FILL);
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, renderBuffer[0]);
glColorMask ( GL_TRUE , GL_TRUE , GL_TRUE , GL_TRUE );

然后我使用这些着色器正常渲染场景:

场景顶点着色器:

#version 120

attribute vec4 RingPosition;
attribute vec3 RingNormal;

uniform vec4 LightPosition;

uniform mat4 RingModelMatrix;
uniform mat4 RingViewMatrix;
uniform mat4 RingProjectionMatrix;
uniform mat3 RingNormalMatrix;

uniform mat4 ShadowBiasMatrix;
uniform mat4 ShadowModelMatrix;
uniform mat4 ShadowViewMatrix;
uniform mat4 ShadowProjectionMatrix;

varying float DiffuseIntensity;
varying float SpecularIntensity;
varying vec4 ShadowCoordinate;

const float specularContribution = 1.0;
const float diffuseContribution = 1.0;

void main()
{
    mat4 ShadowMatrix =  ShadowBiasMatrix * ShadowProjectionMatrix * ShadowViewMatrix * ShadowModelMatrix;

    ShadowCoordinate = ShadowMatrix * RingPosition;

    vec3 lightPosition= vec3(LightPosition);
    float shininess = gl_FrontMaterial.shininess;

    vec3 ecPosition = vec3(RingViewMatrix * RingModelMatrix  * RingPosition);
    vec3 tnorm = normalize(RingNormalMatrix * RingNormal);
    vec3 lightVec = normalize(lightPosition - ecPosition);
    vec3 reflectVec = reflect(-lightVec, tnorm);
    vec3 viewVec = normalize(-ecPosition);

    float spec = clamp(dot(reflectVec, viewVec), 0.0, 1.0);
    SpecularIntensity = specularContribution * pow(spec, shininess / 5.0);

    DiffuseIntensity = diffuseContribution * max(dot(lightVec, tnorm), 0.0);

    gl_Position = RingProjectionMatrix * RingViewMatrix * RingModelMatrix * RingPosition;
}

Scene Fragment着色器:

#version 120

uniform sampler2D ShadowMap;

varying float DiffuseIntensity;
varying float SpecularIntensity;
varying vec4 ShadowCoordinate;

void main()
{

    vec3 emission = vec3(gl_FrontMaterial.emission);
    vec3 ambient = vec3(gl_FrontMaterial.ambient);
    vec3 diffuse = vec3(gl_FrontMaterial.diffuse);
    vec3 specular = vec3(gl_FrontMaterial.specular);

    // Normalize the Shadow Map coordinates
    // shadowCoordinateWdivide.z = current fragment depth from light

    vec3 shadowCoordinateWdivide = ShadowCoordinate.xyz / ShadowCoordinate.w ;

    float distanceFromLight = texture2D(ShadowMap, shadowCoordinateWdivide.xy).r;

    float depthInShadow = shadowCoordinateWdivide.z - distanceFromLight;

    float specularIntensity = SpecularIntensity;
    float diffuseIntensity = DiffuseIntensity;

    if (depthInShadow > 0.006)
    {
        specularIntensity = SpecularIntensity * 0.0;
        diffuseIntensity = DiffuseIntensity * 0.0;
    }

    vec3 lightColor = emission;
    lightColor = lightColor + ambient;
    lightColor = lightColor + (specularIntensity * specular);
    lightColor = lightColor + (diffuseIntensity * diffuse);

    lightColor = clamp(lightColor, 0.0, 1.0);

    gl_FragColor = vec4(lightColor, 1.0);

}

阴影偏差矩阵是:

GLfloat shadowBiasMatrix[16] = {
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};