微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决修复屏幕空间反射中的伪影

我正在尝试实现 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 举报,一经查实,本站将立刻删除。