ghgh456 发表于 2024-7-15 18:17

《Unity Shader 入门精要》从Bulit-in 到URP (HLSL)Chapter13.3-基于深度纹理的雾效(屏幕后措置)

前言:

已经进入“高级篇”啦,但愿大师多多撑持,多多存眷,这将对我发生非常愉悦的正反馈~

“《Unity Shader 入门精要》从Bulit-in 到URP”是一个辅佐Unity Shader学习者以冯乐乐女神《Unity Shader 入门精要》为基础学习用HLSL语言编写URP着色器的案例教学系列。

作者自学能力有限,抛砖引玉,如有建议和问题请各位大佬和同仁交流斧正。
如安在URP中实现后措置参见:

Bulit-in版:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader ”Unity Shaders Book/Chapter 13/Fog With Depth Texture” {
        Properties {
                _MainTex (”Base (RGB)”, 2D) = ”white” {}
                _FogDensity (”Fog Density”, Float) = 1.0
                _FogColor (”Fog Color”, Color) = (1, 1, 1, 1)
                _FogStart (”Fog Start”, Float) = 0.0
                _FogEnd (”Fog End”, Float) = 1.0
        }
        SubShader {
                CGINCLUDE
               
                #include ”UnityCG.cginc”
               
                float4x4 _FrustumCornersRay;
               
                sampler2D _MainTex;
                half4 _MainTex_TexelSize;
                sampler2D _CameraDepthTexture;
                half _FogDensity;
                fixed4 _FogColor;
                float _FogStart;
                float _FogEnd;
               
                struct v2f {
                        float4 pos : SV_POSITION;
                        half2 uv : TEXCOORD0;
                        half2 uv_depth : TEXCOORD1;
                        float4 interpolatedRay : TEXCOORD2;
                };
               
                v2f vert(appdata_img v) {
                        v2f o;
                        o.pos = UnityObjectToClipPos(v.vertex);
                       
                        o.uv = v.texcoord;
                        o.uv_depth = v.texcoord;
                       
                        #if UNITY_UV_STARTS_AT_TOP
                        if (_MainTex_TexelSize.y < 0)
                                o.uv_depth.y = 1 - o.uv_depth.y;
                        #endif
                       
                        int index = 0;
                        if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) {
                                index = 0;
                        } else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) {
                                index = 1;
                        } else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) {
                                index = 2;
                        } else {
                                index = 3;
                        }

                        #if UNITY_UV_STARTS_AT_TOP
                        if (_MainTex_TexelSize.y < 0)
                                index = 3 - index;
                        #endif
                       
                        o.interpolatedRay = _FrustumCornersRay;
                                      
                        return o;
                }
               
                fixed4 frag(v2f i) : SV_Target {
                        float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
                        float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
                                               
                        float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
                        fogDensity = saturate(fogDensity * _FogDensity);
                       
                        fixed4 finalColor = tex2D(_MainTex, i.uv);
                        finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);
                       
                        return finalColor;
                }
               
                ENDCG
               
                Pass {
                        ZTest Always Cull Off ZWrite Off
                                    
                        CGPROGRAM
                       
                        #pragma vertex vert
                        #pragma fragment frag
                          
                        ENDCG
                }
        }
        FallBack Off
}

URP版:

Unity项目源码:
Shader ”Unlit/Chapter13-FogWithDepthTexture”
{
    Properties {
                _MainTex (”Base (RGB)”, 2D) = ”white” {}
                _FogDensity (”Fog Density”, Float) = 1.0
                _FogColor (”Fog Color”, Color) = (1, 1, 1, 1)
                _FogStart (”Fog Start”, Float) = 0.0
                _FogEnd (”Fog End”, Float) = 1.0
        }
    SubShader
    {
      Tags { ”RenderPipeline” = ”UniversalPipeline” }
      
      HLSLINCLUDE
      #include ”Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl”
                #include ”Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl”

      CBUFFER_START(UnityPerMaterial)
            float4x4 _FrustumCornersRay;
               
            half4 _MainTex_TexelSize;
            half _FogDensity;
            half4 _FogColor;
            float _FogStart;
            float _FogEnd;
      CBUFFER_END

      TEXTURE2D(_MainTex);       SAMPLER(sampler_MainTex);
      TEXTURE2D(_CameraDepthTexture);       SAMPLER(sampler_CameraDepthTexture);
      //如果在URP Asset设置下勾选 depth texture选项系统会自动生成一张以_CameraDepthTexture为名的深度图,抽时间会写一篇相关文章并把链接补充到本篇中。

      struct appdata{
            float4 vertex : POSITION;
            float2 texcoord : TEXCOORD0;
      };

      struct v2f {
                        float4 pos : SV_POSITION;
                        half2 uv : TEXCOORD0;
                        half2 uv_depth : TEXCOORD1;
                        float4 interpolatedRay : TEXCOORD2;
      };
      v2f vert(appdata v) {
                        v2f o;
                       
                        o.pos = TransformObjectToHClip(v.vertex);
                       
                        o.uv = v.texcoord;
            o.uv_depth = v.texcoord;
            #if UNITY_UV_STARTS_AT_TOP
                        if (_MainTex_TexelSize.y < 0)
                                o.uv_depth.y = 1 - o.uv_depth.y;
                        #endif

            int index = 0;
                        if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) {
                                index = 0;
                        } else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) {
                                index = 1;
                        } else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) {
                                index = 2;
                        } else {
                                index = 3;
                        }

                        #if UNITY_UV_STARTS_AT_TOP
                        if (_MainTex_TexelSize.y < 0)
                                index = 3 - index;
                        #endif
                       
                        o.interpolatedRay = _FrustumCornersRay;
            //尽管我们这里使用了很多判断语句,但由于屏幕后措置所用的模型是一个四边形网格,只包含4个顶点,因此这些操作不会对性能造成很大影响。
                                       
                        return o;
                }

      half4 frag(v2f i) : SV_Target {
                        float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, sampler_CameraDepthTexture,i.uv_depth), _ZBufferParams);
            //使用LinearEyeDepth得到视角空间下的线性深度值
                        float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
                                               
                        float fogDensity = (_FogEnd - worldPos.y) / (_FogEnd - _FogStart);
                        fogDensity = saturate(fogDensity * _FogDensity);
                       
                        half4 finalColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv);
                        finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity);
                       
                        return finalColor;
                }
               
                ENDHLSL
               
                Pass {
                        ZTest Always Cull Off ZWrite Off
                                    
                        HLSLPROGRAM
                       
                        #pragma vertex vert
                        #pragma fragment frag
                          
                        ENDHLSL
                }
        }
        FallBack ”Packages/com.unity.render-pipelines.universal/FallbackError”
}
URP后措置脚本

因为和上面提供的(Post-processing : RenderFeature + VolumeComponent)模板有些许差异,因此在此提供对应源码。
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class FogWithDepthTexture : ScriptableRendererFeature
{
   
    public class CustomVolumeComponent : DingVolumeComponentBase
    {
      public ClampedFloatParameter FogDensity = new ClampedFloatParameter(1.0f, 0, 3.0f);
      public MinFloatParameter FogStart = new MinFloatParameter(0f, 0f);
      public MinFloatParameter FogEnd = new MinFloatParameter(2.0f, 0f);

      public ColorParameter FogColor = new ColorParameter(Color.white, false, false, true);
   
      public override bool IsActive() => isRender.value;
      public override bool IsTileCompatible() => false;

    }

    class CustomRenderPass : ScriptableRenderPass
    {
      
      public Material material;
      private Matrix4x4 frustumCorners;
      //RT的滤波模式
      public FilterMode filterMode {get; set;}
      //当前衬着阶段的colorRT
      //RenderTargetIdentifier、RenderTargetHandle都可以理解为RT,Identifier为camera提供的需要被应用的texture,Handle为被shader措置衬着过的RT
      private RenderTargetIdentifier source {get; set;}
      //private RenderTargetHandle destination {get; set;}
      //辅助RT
      private RenderTargetHandle tempTexture;
      string m_ProfilerTag;
      //Profiling上显示
      public CustomVolumeComponent volume;
      ProfilingSampler m_ProfilingSampler = new ProfilingSampler(”URPDing”);

      public CustomRenderPass(RenderPassEvent renderPassEvent, Shader shader, CustomVolumeComponent volume, string tag){
            //确定在哪个阶段插入衬着
            this.renderPassEvent = renderPassEvent;
            this.volume = volume;
            if(shader == null){return;}
            this.material = CoreUtils.CreateEngineMaterial(shader);
            m_ProfilerTag = tag;
            //初始化辅助RT的名字
            tempTexture.Init(”_TempRTexture”);
      }

      public void Setup(RenderTargetIdentifier source){
            this.source = source;
            //this.destination = destination;
      }
      
      
      public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
      {
            if (!volume.IsActive()) {
                return;
            }
            CommandBuffer cmd = CommandBufferPool.Get(”m_ProfilerTag”);
            //using 方式可以实此刻FrameDebug上查看衬着过程
            using(new ProfilingScope(cmd, m_ProfilingSampler)){
                Camera camera = renderingData.cameraData.camera;
                //获取摄像机
                Transform cameraTransform = camera.transform;

                float fov = camera.fieldOfView;
                float near = camera.nearClipPlane;
                float far = camera.farClipPlane;
                float aspect = camera.aspect;

                float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
                Vector3 toRight = cameraTransform.right * halfHeight * aspect;
                Vector3 toTop = cameraTransform.up * halfHeight;
                Vector3 topLeft = cameraTransform.forward * near + toTop - toRight;
                float scale = topLeft.magnitude / near;
                topLeft.Normalize();
                topLeft *= scale;
                Vector3 topRight = cameraTransform.forward * near + toRight + toTop;
                topRight.Normalize();
                topRight *= scale;
                Vector3 bottomLeft = cameraTransform.forward * near - toTop - toRight;
                bottomLeft.Normalize();
                bottomLeft *= scale;
                Vector3 bottomRight = cameraTransform.forward * near + toRight - toTop;
                bottomRight.Normalize();
                bottomRight *= scale;

                frustumCorners.SetRow(0, bottomLeft);
                frustumCorners.SetRow(1, bottomRight);
                frustumCorners.SetRow(2, topRight);
                frustumCorners.SetRow(3, topLeft);

                material.SetMatrix(”_FrustumCornersRay”, frustumCorners);
                material.SetMatrix(”_ViewProjectionInverseMatrix”, (camera.projectionMatrix * camera.worldToCameraMatrix).inverse);

                material.SetFloat(”_FogDensity”, volume.FogDensity.value);
                material.SetFloat(”_FogStart”, volume.FogStart.value);
                material.SetFloat(”_FogEnd”, volume.FogEnd.value);

                material.SetColor(”_FogColor”, volume.FogColor.value);
                //创建一张RT
                RenderTextureDescriptor cameraTextureDesc = renderingData.cameraData.cameraTargetDescriptor;
                cameraTextureDesc.depthBufferBits = 0;
                cameraTextureDesc.msaaSamples = 1;
                //打消抗锯齿措置,抗锯齿会影响unity自动在DX与OpenGL间的坐标转换
                cmd.GetTemporaryRT(tempTexture.id, cameraTextureDesc, filterMode);
               
                //将当前帧的colorRT用着色器(shader in material)衬着后输出到之前创建的贴图(辅助RT)上
                Blit(cmd, source, tempTexture.Identifier(), material, 0);
                  //将措置后的辅助RT从头衬着到当前帧的colorRT上
                Blit(cmd, tempTexture.Identifier(), source);

            }
            //执行衬着
            context.ExecuteCommandBuffer(cmd);
            //释放回收
            CommandBufferPool.Release(cmd);
      }


      public override void FrameCleanup(CommandBuffer cmd){
            base.FrameCleanup(cmd);
            cmd.ReleaseTemporaryRT(tempTexture.id);
      }
    }

   
    public class Settings{
      public RenderPassEvent Event = RenderPassEvent.AfterRenderingTransparents;
      public Shader shader;
    }

    public Settings settings = new Settings();
    CustomVolumeComponent volume;
    CustomRenderPass m_ScriptablePass;

   

    /// <inheritdoc/>
    //feature被创建时调用
    public override void Create()
    {
      var stack = VolumeManager.instance.stack;
      volume = stack.GetComponent<CustomVolumeComponent>();
      if (volume == null) {
            CoreUtils.Destroy(m_ScriptablePass.material);
            return;
      }
      m_ScriptablePass = new CustomRenderPass(settings.Event, settings.shader, volume, name);
    }

    //每一帧城市调用
    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
      var src = renderer.cameraColorTarget;
      //var dest = RenderTargetHandle.CameraTarget;

      if(settings.shader == null){
            Debug.LogWarningFormat(”shader丢掉”,GetType().Name);
            return;
      }

      //将当前衬着的colorRT传到Pass中

      m_ScriptablePass.Setup(src);

      //将Pass添加到衬着队列中
      renderer.EnqueuePass(m_ScriptablePass);
    }

    protected override void Dispose(bool disposing)
    {
      base.Dispose(disposing);
   
    }
}



效果图:

因为我在sense中使用的是图片,图片上没有深度分歧,所以无法在图片上不雅察看到深度分歧的雾效区别,图片以外区域可以感到感染到稍微明显的深度雾效。


如有收获,请留下“存眷”和“附和”~
页: [1]
查看完整版本: 《Unity Shader 入门精要》从Bulit-in 到URP (HLSL)Chapter13.3-基于深度纹理的雾效(屏幕后措置)