第一次阴影贴图问题


9

我第一次使用着色器在OpenGL中实现了基本的阴影映射,现在遇到了一些问题。在下面,您可以看到我渲染的场景的示例:

在此处输入图片说明

我要进行的阴影映射的过程是,我使用“视图矩阵”从光的角度以及用于正常渲染的投影和模型矩阵将场景渲染到帧缓冲区。

在第二遍中,我将上述MVP矩阵从光的角度发送到顶点着色器,该顶点着色器将位置转换为光空间。片段着色器进行透视划分,并将位置更改为纹理坐标。

这是我的顶点着色器,


#version 150 core

uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 MVPMatrix;
uniform mat4 lightMVP;

uniform float scale;

in vec3 in_Position;
in vec3 in_Normal;
in vec2 in_TexCoord;

smooth out vec3 pass_Normal;
smooth out vec3 pass_Position;
smooth out vec2 TexCoord;
smooth out vec4 lightspace_Position;

void main(void){
    pass_Normal = NormalMatrix * in_Normal; 
    pass_Position = (ModelViewMatrix * vec4(scale * in_Position, 1.0)).xyz;
    lightspace_Position = lightMVP * vec4(scale * in_Position, 1.0);
    TexCoord = in_TexCoord;

    gl_Position = MVPMatrix * vec4(scale * in_Position, 1.0);
}

还有我的片段着色器


#version 150 core

struct Light{
    vec3 direction;
};

uniform Light light;
uniform sampler2D inSampler;
uniform sampler2D inShadowMap;

smooth in vec3 pass_Normal;
smooth in vec3 pass_Position;
smooth in vec2 TexCoord;
smooth in vec4 lightspace_Position;

out vec4 out_Color;


float CalcShadowFactor(vec4 lightspace_Position){

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void){

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float diffuse = max(0.2, dot(Normal, light_Direction));
    vec3 temp_Color = diffuse * vec3(1.0);

    float specular = max( 0.0, dot( Normal, half_vector) );

    float shadowFactor = CalcShadowFactor(lightspace_Position);

    if(diffuse != 0 && shadowFactor > 0.5){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

问题之一是自阴影,如您在图片中看到的那样,板条箱自身有阴影。我尝试过的是启用多边形偏移(即glEnable(POLYGON_OFFSET_FILL),glPolygonOffset(GLfloat,GLfloat)),但是它并没有太大变化。如您在片段着色器中所看到的,我将静态偏移值设置为0.001,但我必须根据光的距离更改该值,以获得更理想的效果,这不是很方便。当我渲染到帧缓冲区时,我也尝试使用正面剔除,也没有太大变化。

另一个问题是“光”视锥台之外的像素会被着色。可以投射阴影的唯一对象是板条箱。我想我应该选择更合适的投影并查看矩阵,但是我不确定该怎么做。有哪些常见做法,我应该选择正交投影吗?

通过四处搜寻,我了解到这些问题并不那么琐碎。有没有人容易解决这些问题。您能给我一些其他提示吗?

请问我是否需要有关我的代码的更多信息。

是有和没有阴影的板条箱特写的比较。自阴影更明显。


似乎您应该更详细地描述您的问题。关于问题,您有一句话,而下一句话则建议您解决。告诉我们确切的问题是什么,您为解决问题已经做了什么。
MichaelHouse

我编辑了我的帖子,希望现在更好。
Grieverheart 2012年

Answers:


6

您不应该遮挡光线背面的多边形。

我将片段着色器更改为以下代码。

float CalcShadowFactor(vec4 lightspace_Position)
{

    vec3 ProjectionCoords = lightspace_Position.xyz / lightspace_Position.w;

    vec2 UVCoords;
    UVCoords.x = 0.5 * ProjectionCoords.x + 0.5;
    UVCoords.y = 0.5 * ProjectionCoords.y + 0.5;

    float Depth = texture(inShadowMap, UVCoords).x;
    if(Depth < (ProjectionCoords.z + 0.001)) return 0.5;
    else return 1.0;
}

void main(void)
{

    vec3 Normal = normalize(pass_Normal);
    vec3 light_Direction = -normalize(light.direction);
    vec3 camera_Direction = normalize(-pass_Position);
    vec3 half_vector = normalize(camera_Direction + light_Direction);

    float fndotl = dot(Normal, light_Direction);
    float shadowFactor = CalcShadowFactor(lightspace_Position);
    float diffuse = max(0.0, fndotl) * shadowFactor + 0.2;
    vec3 temp_Color = vec3(diffuse);

    float specular = max( 0.0, dot( Normal, half_vector) );

    if(shadowFactor > 0.9){
        float fspecular = pow(specular, 128.0);
        temp_Color += fspecular;
    }

    out_Color = vec4(shadowFactor * texture(inSampler, TexCoord).xyz * temp_Color, 1.0);
}

8

关于自我屏蔽,我不确定您指的是什么-屏幕快照中的板条箱上看不到任何“阴影痤疮”。如果您的意思是说背对着光线的面孔是黑暗的,那当然是-应该!:)侧面看起来确实有些怪异,将对角线分成一半点亮和一半阴影。如果您使用N点L进行照明,并且具有良好的法线(即此立方体上的硬法线,而不是平滑法线),则不应发生这种情况。

使用多边形偏移和/或将背面绘制到阴影贴图中是阴影痤疮的标准解决方案(实际上是解决方法)。

至于投影矩阵,您应该将光视为照相机。如果是点光源,则它将是透视相机,如果是定向光,则它将像正交相机。使用光源的视场,长宽比等,以与照相机相同的方式构造投影矩阵。

为了限制像素着色器仅在灯光的视锥中绘制像素,一旦计算了阴影贴图UV,只需检查它们是否在0到1之间,否则就可以检查discard(或将灯光的颜色设置为零)。另一种方法是将阴影贴图纹理设置为“夹到边界”模式,并将边界颜色设置为零,这将确保阴影贴图之外的所有内容都完全被阴影化。


这是盒子的特写,有阴影映射链接和没有阴影映射链接的比较。自遮蔽在这里更明显。您能否解释为什么将透视相机用于点光源而将正交摄影用于方向性?
Grieverheart

好吧,在那张照片中看起来确实像暗疮粉刺。将背面拖入阴影贴图(而不是正面)应该可以解决该问题。我知道您说过您尝试过,但是也许出了什么问题?只是glCullFace(GL_FRONT)。至于点对点,都与光线有关。相机光线会聚到透视相机中的一个点,光线会聚到点光源中的一个点;对于正交摄影机,相机光线平行,对于定向光,光线平行。
内森·里德

我知道,这很有意义。 是有无脸部剔除的另一个比较。底部的一个启用了脸部剔除。我将尝试对光View矩阵使用正交投影,但是这不应该影响自阴影问题,对吗?也许我的剪辑范围太大(即我有0.1到100)?
Grieverheart

那些看起来完全一样。您在其余场景中使用背面剔除,对吗?你做了glEnable(GL_CULL_FACE)
内森·里德

它们并不完全相同,请仔细查看包装盒。在上图中,阴影线是凸的,而底部的阴影线是凹的。
Grieverheart

3

灯光视图外部的像素是暗的,因为它们是从灯光深度纹理外部进行采样的。

通过投影矩阵变换顶点时,将获得片段的剪辑坐标[lightspace_Position]。然后将其转换为纹理坐标[UVCoords]。但是,片段的剪辑空间坐标仍然可以在-1.0到1.0范围之外(离开屏幕并因此被裁剪),因此转换后的纹理坐标可以在纹理的0.0到1.0范围之外。

尝试这个:

if(UVCoords.x >= 0.0 && UVCoords.x <= 1.0 && UVCoords.y >= 0.0 && UVCoords.y <= 1.0 && (Depth < (ProjectionCoords.z + 0.001)))
    return 0.5;

您还可以将偏差矩阵乘以[lightMVP],并避免在片段着色器中进行坐标转换。


偏见矩阵通常是人们去的方式。它比片段着色器中的分支代码快得多。
伊恩·杨
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.