找回密码
 立即注册
查看: 508|回复: 0

后处理深度雾

[复制链接]
发表于 2023-2-9 09:26 | 显示全部楼层 |阅读模式
后处理深度雾相较于Unity内置的无效使用起来更加方便,不需要在每个着色器中添加相关的代码,而且性能和效果更好。并且效果可以覆盖天空盒。
使用Unity2022.2.0制作
新建FogRenderFeature.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class FogRenderFeature : ScriptableRendererFeature
{
    public Shader m_shader;
    public Color m_FogColor;
    public float m_FogStart;
    public float m_FogEnd;
    public float m_NoiseCellSize;
    public float m_NoiseRoughness;
    public float m_NoisePersistance;
    public Vector3 m_NoiseSpeed;
    public float m_NoiseScale;

    Material m_Material;

    FogPass m_RenderPass = null;

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (renderingData.cameraData.cameraType == CameraType.Game)
            renderer.EnqueuePass(m_RenderPass);
    }

    public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
    {
        if (renderingData.cameraData.cameraType == CameraType.Game)
        {
            m_RenderPass.ConfigureInput(ScriptableRenderPassInput.Color);

            m_RenderPass.SetTarget(renderer.cameraColorTargetHandle, m_FogColor, m_NoiseCellSize, m_NoiseRoughness, m_NoisePersistance, m_NoiseSpeed, m_NoiseScale, m_FogStart, m_FogEnd);
        }
    }


    public override void Create()
    {
        if (m_shader != null)
            m_Material = new Material(m_shader);

        m_RenderPass = new FogPass(m_Material);
    }

    protected override void Dispose(bool disposing)
    {
        CoreUtils.Destroy(m_Material);
    }

}新建FogPass.cs
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

internal class FogPass : ScriptableRenderPass
{
    ProfilingSampler m_ProfilingSampler = new ProfilingSampler("FogBlit");
    Material m_Material;
    RTHandle m_CameraColorTarget;
    Color m_FogColor;
    //float m_FogDensity;
    float m_FogStart;
    float m_FogEnd;
    float m_NoiseCellSize;
    float m_NoiseRoughness;
    float m_NoisePersistance;
    Vector3 m_NoiseSpeed;
    float m_NoiseScale;

    public FogPass(Material material)
    {
        m_Material = material;
        renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
    }

    public void SetTarget(RTHandle colorHandle, Color fogColor ,float noiseCellSize, float noiseRoughness, float noisePersistance ,Vector3 noiseSpeed, float noiseScale,float fogStart, float fogEnd)
    {
        m_CameraColorTarget = colorHandle;
        m_FogStart = fogStart;
        m_FogEnd = fogEnd;
        m_FogColor = fogColor;
        m_NoiseCellSize = noiseCellSize;
        m_NoiseRoughness = noiseRoughness;
        m_NoisePersistance = noisePersistance;
        m_NoiseSpeed = noiseSpeed;
        m_NoiseScale = noiseScale;
    }

    public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    {
        ConfigureTarget(m_CameraColorTarget);
    }


    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        var cameraData = renderingData.cameraData;
        if (cameraData.camera.cameraType != CameraType.Game)
            return;

        if (m_Material == null)
            return;

        CommandBuffer cmd = CommandBufferPool.Get();
        using (new ProfilingScope(cmd, m_ProfilingSampler))
        {
            m_Material.SetColor("_FogColor", m_FogColor);
            m_Material.SetFloat("_FogStart", m_FogStart);
            m_Material.SetFloat("_FogEnd", m_FogEnd);
            m_Material.SetFloat("_NoiseCellSize", m_NoiseCellSize);
            m_Material.SetFloat("_NoiseRoughness", m_NoiseRoughness);
            m_Material.SetFloat("_NoisePersistance", m_NoisePersistance);
            m_Material.SetVector("_NoiseSpeed", m_NoiseSpeed);
            m_Material.SetFloat("_NoiseScale", m_NoiseScale);
   
            m_Material.SetMatrix("_InverseView", renderingData.cameraData.camera.cameraToWorldMatrix);
            Blitter.BlitCameraTexture(cmd, m_CameraColorTarget, m_CameraColorTarget, m_Material, 0);
        }
        context.ExecuteCommandBuffer(cmd);
        cmd.Clear();

        CommandBufferPool.Release(cmd);
    }
}
新建Shader
后处理雾的实现原理:最终的颜色 = lerp(雾效颜色,物体颜色,雾效混合因子)
雾效混合因子利用深度模拟雾效的衰减
常见的雾效有三种
Linear


雾从Start开始到End越来越浓,End之后也为最大浓度。(这次就是用得这种方法)

  • z:是片段ClipSpace下的z
  • dmin:就是下图的Start
  • dmax:就是下图的End
Exponential


指数模式,有参数fogDensity(取值范围0-1),表示雾的浓度,浓度越大雾越大。
exp(-density*z)
Exponential Squared


指数平方,有参数fogDensity,越大表示雾的浓度。
exp(-(density*z)^2)

后处理雾效需要从View space Depth(深度图)重建世界位置
添加上3D Layered Noise 增加风吹动的效果
https://www.ronja-tutorials.com/?search=Noise
Shader "LY/Fog"
{
    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
        LOD 100
        ZWrite Off Cull Off
        Pass
        {
            Name "FogBlitPass"

            HLSLPROGRAM
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"

            #pragma vertex Vert
            #pragma fragment frag

            TEXTURE2D_X(_CameraOpaqueTexture);      SAMPLER(sampler_CameraOpaqueTexture);
            TEXTURE2D_X(_CameraDepthTexture);       SAMPLER(sampler_CameraDepthTexture);

            float _Intensity;
            float4x4 _InverseView;
            half4 _FogColor;
            float _FogDensity;
            float3 _NoiseSpeed;
            float _NoiseCellSize, _NoiseRoughness, _NoisePersistance, _NoiseScale,_FogStart,_FogEnd;

            #define OCTAVES 4

            float rand3dTo1d(float3 value, float3 dotDir = float3(12.9898, 78.233, 37.719))
            {
                //make value smaller to avoid artefacts
                float3 smallValue = cos(value);
                //get scalar value from 3d vector
                float random = dot(smallValue, dotDir);
                //make value more random by making it bigger and then taking the factional part
                random = frac(sin(random) * 143758.5453);
                return random;
            }

            float3 rand3dTo3d(float3 value)
            {
                return float3(
                    rand3dTo1d(value, float3(12.989, 78.233, 37.719)),
                    rand3dTo1d(value, float3(39.346, 11.135, 83.155)),
                    rand3dTo1d(value, float3(73.156, 52.235, 09.151))
                );
            }

            float easeIn(float interpolator)
            {
                return interpolator * interpolator;
            }

            float easeOut(float interpolator)
            {
                return 1 - easeIn(1 - interpolator);
            }

            float easeInOut(float interpolator)
            {
                float easeInValue = easeIn(interpolator);
                float easeOutValue = easeOut(interpolator);
                return lerp(easeInValue, easeOutValue, interpolator);
            }

            float perlinNoise(float3 value)
            {
                float3 fraction = frac(value);

                float interpolatorX = easeInOut(fraction.x);
                float interpolatorY = easeInOut(fraction.y);
                float interpolatorZ = easeInOut(fraction.z);

                float cellNoiseZ[2];
                [unroll]
                for (int z = 0; z <= 1; z++)
                {
                    float cellNoiseY[2];
                    [unroll]
                    for (int y = 0; y <= 1; y++)
                    {
                        float cellNoiseX[2];
                        [unroll]
                        for (int x = 0; x <= 1; x++)
                        {
                            float3 cell = floor(value) + float3(x, y, z);
                            float3 cellDirection = rand3dTo3d(cell) * 2 - 1;
                            float3 compareVector = fraction - float3x, y, z);
                            cellNoiseX[x] = dot(cellDirection, compareVector);
                        }
                        cellNoiseY[y] = lerp(cellNoiseX[0], cellNoiseX[1], interpolatorX);
                    }
                    cellNoiseZ[z] = lerp(cellNoiseY[0], cellNoiseY[1], interpolatorY);
                }
                float noise = lerp(cellNoiseZ[0], cellNoiseZ[1], interpolatorZ);
                return noise;
            }

            float sampleLayeredNoise(float3 value,float noiseRoughness ,float noisePersistance)
            {
                float noise = 0;
                float frequency = 1;
                float factor = 1;

                [unroll]
                for (int i = 0; i < OCTAVES; i++)
                {
                    noise = noise + perlinNoise(value * frequency + i * 0.72354) * factor;
                    factor *= noisePersistance;
                    frequency *= noiseRoughness;
                }

                return noise;
            }

            half4 frag(Varyings input) : SV_Target
            {
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);
                float4 color = SAMPLE_TEXTURE2D_X(_CameraOpaqueTexture, sampler_CameraOpaqueTexture, input.texcoord);
                float depth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, input.texcoord).r;
                depth = Linear01Depth(depth, _ZBufferParams);

                //从深度重建世界空间位置
                float2 p11_22 = float2(unity_CameraProjection._11, unity_CameraProjection._22);
                float3 viewPos = float3((input.texcoord * 2 - 1) / p11_22, -1) * depth * _ProjectionParams.z;
                float4 wposVP = mul(_InverseView, float4(viewPos, 1)); //_ViewToWorld
               
                //3D分层Noise,增加风吹动的效果
                float noise = sampleLayeredNoise(float3((wposVP.xyz + _NoiseSpeed * _Time.y) * _NoiseCellSize), _NoiseRoughness, _NoisePersistance);

                if (depth < 1)
                {
                    depth += depth * noise * _NoiseScale;
                }
        
                float fog = 1- saturate((_FogEnd - depth)/max(0,(_FogEnd-_FogStart)));

                return lerp(color, _FogColor, fog);
            }
            ENDHLSL
        }
    }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-12-4 01:59 , Processed in 0.092489 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表