阴影映射';暗疮';使用OpenGL和GLSL的工件

阴影映射';暗疮';使用OpenGL和GLSL的工件,opengl,glsl,opengl-3,opengl-4,shadow-mapping,Opengl,Glsl,Opengl 3,Opengl 4,Shadow Mapping,我编写了一个简单的3D应用程序,使用著名的正面剔除技术实现硬阴影和PCF阴影映射算法。不幸的是,这种技术的问题是只有密封的网格才能产生投射阴影。例如,平面不能产生这样的效果,因为平面本身就是正面 因此,解决方案是使用函数“glPolygonOffset”,其目标是在“灯光视图”中可见的每个顶点的深度渲染路径期间稍微修改深度值。换言之,需要此功能来避免“阴影痤疮”瑕疵,此时保留所有网格正面 下面是使用硬阴影映射算法进行渲染的显示: 正如您所见,阴影渲染是完美的,没有任何瑕疵 这里是定义深度纹理渲

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

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

下面是使用硬阴影映射算法进行渲染的显示:

正如您所见,阴影渲染是完美的,没有任何瑕疵

这里是定义深度纹理渲染路径的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);
}
只有调用“TextureProjectOffset(shadowSampler,ShadowCoords[index],ivec2(0,0))”才能正常工作(当然它指的是第一种硬阴影映射技术)

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

我有以下渲染:

正如你所看到的,只有在立方体的右边才有“暗影痤疮”人工制品


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

不知道这是否有帮助。也许我把你的问题复杂化了。这段代码来自一个Objective C应用程序,但相关的代码大多只是C。它运行并工作。您将在场景片段着色器中看到一条语句:if(depthInShadow>0.006)。这个阴影深度数字是我必须“调整”以去除粉刺的东西

这是一个编辑。重新阅读您的帖子后,我看到一条语句:if(ShadowCoords[index].w>0.0f)。这看起来非常类似于我的场景片段着色器中的depthInShadow语句,我必须“调整”到略大于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;
}
#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;
}
阴影片段着色器:

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);
}
#version 120

uniform vec4 FrontColor;

void main()
{

    gl_FragColor = FrontColor;
}
#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);

}
我首先使用以下方法将场景渲染到阴影缓冲区:

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 ShadowPosition;

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

void main()
{
    gl_Position = ShadowProjectionMatrix * ShadowViewMatrix * ShadowModelMatrix * ShadowPosition;
}
#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;
}
场景片段着色器:

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);
}
#version 120

uniform vec4 FrontColor;

void main()
{

    gl_FragColor = FrontColor;
}
#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};

它被称为shadow AcneglPolygonPoset,与前面/后面的开关结合使用——后面被渲染到位;前表面以与其坡度成比例的偏移进行渲染。无论是仅正面渲染,还是光空间中的恒定偏移,都不会以相同的方式有效。如果您需要一个平面来投射阴影,只需将其设置为双面平面即可。