我根据另一个纹理中的值在屏幕空间纹理上投射阴影。
我的“深度”纹理,它不是真正的深度,只是为高度采样的颜色值,如下所示:
我们可以说红色通道是我的高度图。
我使用以下碎片着色器在全屏四边形上绘制草纹理:
#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;
}
输出样本为:
不想要条纹。我可以使用同样的方法,使用更清晰的“边缘”而不是平滑的轮廓,一切看起来都很棒。像这样的渐变会导致问题。
答案 0 :(得分:1)
为了提高算法的质量,当然可以增加样本数量。通过限制步幅距离,也可以实现质量改进。
步长的最小有意义距离由高度贴图的分辨率决定,因为测试高度贴图的相同高度两次是没有意义的。
float d = max(textureD.x,textureD.y);
float minStepSize = 1.0 / d;
如果光束从地面上的位置到太阳位置的距离达到1.0的高度,则达到最大有意义距离。
通过将最大距离除以样本数来给出一步的距离,但它至少应该是高度图分辨率给出的最小距离:
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 );
对于软阴影算法,必须区分全阴影和阴影过渡。为此,必须研究光束到高度图的距离
如果距离很远,则片段处于完全阴影中:
但是在距离很小的情况下,会出现阴影过渡:
阴影强度的指示是光束与高度图高度之间的最大距离。该距离可以通过样本点与高度图的最大高度差来计算。可以通过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
设置。