在我的阴影中的条纹

时间:2017-09-20 11:12:00

标签: opengl glsl shadow-mapping

我根据另一个纹理中的值在屏幕空间纹理上投射阴影。

我的“深度”纹理,它不是真正的深度,只是为高度采样的颜色值,如下所示:

red shadows on black background

我们可以说红色通道是我的高度图。

我使用以下碎片着色器在全屏四边形上绘制草纹理:

#version 400

layout(location=0) out vec4 frag_colour;

in vec2 texelCoords;

uniform sampler2D uTexture;
uniform sampler2D uTextureHeightmap;
uniform float uSunDistance = -10000000.0;
uniform float uSunInclination;
uniform float uSunAzimuth;
uniform float uQuality;

void main()
{
    vec4 c = texture(uTexture,texelCoords);

    vec2 textureD = textureSize(uTexture,0);
    float d = max(textureD.x,textureD.y);
    float aspectCorrection = textureD.x / textureD.y;

    vec3 sunPosition = vec3(textureD.x/2,textureD.y/2,0) + vec3(    uSunDistance*sin(uSunInclination)*cos(uSunAzimuth),
                                                                    uSunDistance*sin(uSunInclination)*sin(uSunAzimuth),
                                                                    uSunDistance*cos(uSunInclination)   );

    vec4 heights = texture(uTextureHeightmap, texelCoords);
    float height = max(max(heights.r,heights.g),heights.b);
    vec3 direction = normalize(vec3(texelCoords,height) - sunPosition);
    direction.y *= aspectCorrection;

    float sampleDistance = 0;

    float samples = d*uQuality;

    float stepSize = 1.0 / ((samples/d) * d);

    for(int i = 0; i < samples; i++)
    {
        sampleDistance += stepSize;

        vec3 newPoint = vec3(texelCoords,height) + direction * sampleDistance;
        if(newPoint.z > 1.0)
            break;

        vec4 h = texture(uTextureHeightmap,newPoint.xy);
        float base = h.r;
        float middle = h.g;
        float top = h.b;

        if(newPoint.z < base)
        {
            c *= 0.5;
            break;
        }
        if(newPoint.z >= middle && newPoint.z <= top)
        {
            c *= 0.5;
            break;
        }
    }

    frag_colour = c;
}

输出样本为:

green stuff

不想要条纹。我可以使用同样的方法,使用更清晰的“边缘”而不是平滑的轮廓,一切看起来都很棒。像这样的渐变会导致问题。

1 个答案:

答案 0 :(得分:1)

为了提高算法的质量,当然可以增加样本数量。通过限制步幅距离,也可以实现质量改进。

步长的最小有意义距离由高度贴图的分辨率决定,因为测试高度贴图的相同高度两次是没有意义的。

float d = max(textureD.x,textureD.y);
float minStepSize = 1.0 / d;

如果光束从地面上的位置到太阳位置的距离达到1.0的高度,则达到最大有意义距离。

heightmap maximum distance

通过将最大距离除以样本数来给出一步的距离,但它至少应该是高度图分辨率给出的最小距离:

float minStepSize = 1.0 / d;
float maxDist     = (1.0 - height) * length(direction.xy) / abs(direction.z);
float stepSize    = max( minStepSize, maxDist / samples );
float shadow      = 1.0;
vec3  startPoint  = vec3( texelCoords, height );
for ( float sampleDist = stepSize; sampleDist <= maxDist; sampleDist += stepSize )
{
    vec3 samplePoint   = startPoint + direction * sampleDist;
    vec4 sampleHeight  = texture( uTextureHeightmap, samplePoint.xy );
    if ( samplePoint.z < sampleHeight.r )
    {
        shadow *= 0.5;
        break;
    }
}
frag_colour = vec4( c.rgb * shadow, c.a );


对于软阴影算法,必须区分全阴影和阴影过渡。为此,必须研究光束到高度图的距离 如果距离很远,则片段处于完全阴影中:

heightmap full shadow

但是在距离很小的情况下,会出现阴影过渡:

heightmap shadow transition

阴影强度的指示是光束与高度图高度之间的最大距离。该距离可以通过样本点与高度图的最大高度差来计算。可以通过GLSL函数smoothstep计算平滑的阴影过渡。

vec3  startPoint  = vec3( texelCoords, height );
float maxHeight   = 0.0;
for ( float sampleDist = stepSize; sampleDist <= maxDist; sampleDist += stepSize )
{
    vec3 samplePoint   = startPoint + direction * sampleDist;
    vec4 sampleHeight  = texture( uTextureHeightmap, samplePoint.xy );
    maxHeight          = max(maxHeight, sampleHeight.r - samplePoint.z);
}
const float minShadow        = 0.5; 
const float transitionHeight = 0.05;
shadow    = smoothstep(1.0, minShadow, clamp(maxHeight / transitionHeight, 0.0, 1.0 ));
fragColor = vec4( height, height * shadow, height, 1.0 );

在上面的代码中,变量maxHeight包含光束与高度图高度之间的最大距离。如果它大于0.0且小于transitionHeight,则存在阴影过渡。如果它大于transitionHeight,则会有一个完整的阴影。完整阴影的强度由minShadow设置。