johnsoncodehk 发表于 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;
               
                for (int z = 0; z <= 1; z++)
                {
                  float cellNoiseY;
                  
                  for (int y = 0; y <= 1; y++)
                  {
                        float cellNoiseX;
                        
                        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 = dot(cellDirection, compareVector);
                        }
                        cellNoiseY = lerp(cellNoiseX, cellNoiseX, interpolatorX);
                  }
                  cellNoiseZ = lerp(cellNoiseY, cellNoiseY, interpolatorY);
                }
                float noise = lerp(cellNoiseZ, cellNoiseZ, interpolatorZ);
                return noise;
            }

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

               
                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
      }
    }
}
页: [1]
查看完整版本: 后处理深度雾