修复屏幕空间反射中的伪影

问题描述

我正在尝试实现 SSR(屏幕空间反射)。我设法获得了基本效果,但您可以从下面的图像和视频中看到一些伪影。首先我想解决这些视觉问题,然后如果可能的话,我想尝试提高算法的性能

SSR 工件:https://youtu.be/LFBAJHa_mFM

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

我的 SSR 实现基于此 tutorial

您可以在下面找到生成反射贴图(或反射 UV 贴图)的像素着色器的代码在这里您可以找到 full implementation。如您所见,除了少量更改外,它与教程 (screen-space-reflection.frag) 的代码实际上相同:

  • 我不将每个像素的 3D 位置存储在离屏纹理中,而是根据函数 GetFullViewPosition 中的视图空间深度重建 3D 位置。
  • 当我将片段 NDC 转换为纹理坐标时,我乘以 float2(+0.5f,-0.5f) 而不是 float2(+0.5f,+0.5f)
  • 添加一个检查,如果当前位置超出屏幕空间,则会停止光线追踪。

SSRReflectionsMapComputePS.hlsl

cbuffer ConstantBuffer : register(b0)
{
    float4   gFrustumFarCorner[4];
    float4x4 gProj;
};

struct VertexOut
{
    float4 PositionH  : SV_POSITION;
    float3 ToFarPlane : TEXCOORD0;
    float2 TexCoord   : TEXCOORD1;
};

Texture2D gnormalDepthMap : register(t0);
SamplerState gnormalDepthSamplerState : register(s2);

float3 GetFullViewPosition(float2 uv,float z)
{
    // bilinear interpolation
    float4 p0 = lerp(gFrustumFarCorner[0],gFrustumFarCorner[3],uv.x);
    float4 p1 = lerp(gFrustumFarCorner[1],gFrustumFarCorner[2],uv.x);
    float3 ToFarPlane = lerp(p0.xyz,p1.xyz,1 - uv.y);

    // reconstruct full view space position (x,y,z)
    // find t such that p = t*pin.tofarplane
    // p.z = t*pin.tofarplane.z ==> t = p.z / pin.tofarplane.z
    return (z / ToFarPlane.z) * ToFarPlane;
}

float3 GetFullViewPosition(float2 uv)
{
    float4 normalDepth = gnormalDepthMap.SampleLevel(gnormalDepthSamplerState,uv,0);
    return GetFullViewPosition(uv,normalDepth.w);
}

// https://lettier.github.io/3d-game-shaders-for-beginners/screen-space-reflection.html
// https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/screen-space-reflection.frag

float4 main(VertexOut pin) : sv_target
{
    // view space normal and depth (z-coord) of this pixel
    float4 normaldepth = gnormalDepthMap.SampleLevel(gnormalDepthSamplerState,pin.TexCoord,0);

    float3 n = normaldepth.xyz;
    float pz = normaldepth.w;

    //float3 p = (pz / pin.tofarplane.z) * pin.tofarplane;
    float3 p = GetFullViewPosition(pin.TexCoord,pz);

    float maxdistance = 100;
    float resolution = 1;
    int   steps = 10;
    float thickness = 0.5f;

    float2 texsize;
    gnormalDepthMap.GetDimensions(texsize.x,texsize.y);
    float2 texcoord = pin.PositionH.xy / texsize;

    float3 positionfrom = p;
    float3 positionfromunit = normalize(positionfrom);
    float3 normal = normalize(n);
    float3 pivot = normalize(reflect(positionfromunit,normal));

    float3 positionto = positionfrom;
    float4 uv = 0;

    float3 viewstart = positionfrom + pivot * 0;
    float3 viewend = positionfrom + pivot * maxdistance;

    float4 fragstart = float4(viewstart,1);
    fragstart = mul(gProj,fragstart);
    fragstart.xy /= fragstart.w;
    fragstart.xy = fragstart.xy * float2(+0.5f,-0.5f) + 0.5f;
    fragstart.xy *= texsize;

    float4 fragend = float4(viewend,1);
    fragend = mul(gProj,fragend);
    fragend.xy /= fragend.w;
    fragend.xy = fragend.xy * float2(+0.5f,-0.5f) + 0.5f;
    fragend.xy *= texsize;

    float2 frag = fragstart.xy;
    uv.xy = frag / texsize;

    float deltax = fragend.x - fragstart.x;
    float deltay = fragend.y - fragstart.y;
    float usex = abs(deltax) >= abs(deltay) ? 1 : 0;
    float delta = lerp(abs(deltay),abs(deltax),usex) * clamp(resolution,1);
    float2 increment = float2(deltax,deltay) / max(delta,0.001f);

    float search0 = 0;
    float search1 = 0;

    int hit0 = 0;
    int hit1 = 0;

    float viewdistance = viewstart.z;
    float depth = thickness;

    for (int i = 0; i < int(delta); ++i)
    {
        frag += increment;
        uv.xy = frag / texsize;

        // do not sample outside the screen space
        if (any(uv.xy < float2(0,0)) || any(uv.xy > float2(1,1))) break;

        positionto = GetFullViewPosition(uv.xy);

        search1 = lerp((frag.y - fragstart.y) / deltay,(frag.x - fragstart.x) / deltax,usex);
        search1 = clamp(search1,1);

        viewdistance = (viewstart.z * viewend.z) / lerp(viewend.z,viewstart.z,search1);
        depth = viewdistance - positionto.z;

        if (depth > 0 && depth < thickness)
        {
            hit0 = 1;
            break;
        }
        else
        {
            search0 = search1;
        }
    }

    search1 = search0 + ((search1 - search0) / 2);

    steps *= hit0;

    for (int i = 0; i < steps; ++i)
    {
        frag = lerp(fragstart.xy,fragend.xy,search1);
        uv.xy = frag / texsize;

        // do not sample outside the screen space
        if (any(uv.xy < float2(0,1))) break;

        positionto = GetFullViewPosition(uv.xy);

        viewdistance = (viewstart.z * viewend.z) / lerp(viewend.z,search1);
        depth = viewdistance - positionto.z;

        if (depth > 0 && depth < thickness)
        {
            hit1 = 1;
            search1 = search0 + ((search1 - search0) / 2);
        }
        else
        {
            float temp = search1;
            search1 = search1 + ((search1 - search0) / 2);
            search0 = temp;
        }
    }

    float visibility = hit1 *
        (1 - max(dot(-positionfromunit,pivot),0)) *
        (1 - clamp(depth / thickness,1)) *
        (1 - clamp(length(positionto - positionfrom) / maxdistance,1)) *
        (uv.x < 0 || uv.x > 1 ? 0 : 1) *
        (uv.y < 0 || uv.y > 1 ? 0 : 1);

    return float4(uv.xy,saturate(visibility));
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)