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

《Unity Shader 入门精要》从Bulit-in 到URP (HLSL ...

[复制链接]
发表于 2022-10-20 09:57 | 显示全部楼层 |阅读模式
前言:

已经进入“高级篇”啦,希望大家多多支持,多多关注,这将对我产生非常愉悦的正反馈~

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

作者自学能力有限,抛砖引玉,如有建议和问题请各位大佬和同仁交流指正。

如何在URP中实现后处理参见:

《Unity Shader 入门精要》从Bulit-in 到URP (HLSL)之后处理(Post-processing : RenderFeature + VolumeComponent)

Bulit-in版:

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

Shader "Unity Shaders Book/Chapter 12/Gaussian Blur" {
        Properties {
                _MainTex ("Base (RGB)", 2D) = "white" {}
                _BlurSize ("Blur Size", Float) = 1.0
        }
        SubShader {
                CGINCLUDE
               
                #include "UnityCG.cginc"
               
                sampler2D _MainTex;  
                half4 _MainTex_TexelSize;
                float _BlurSize;
                  
                struct v2f {
                        float4 pos : SV_POSITION;
                        half2 uv[5]: TEXCOORD0;
                };
                  
                v2f vertBlurVertical(appdata_img v) {
                        v2f o;
                        o.pos = UnityObjectToClipPos(v.vertex);
                       
                        half2 uv = v.texcoord;
                       
                        o.uv[0] = uv;
                        o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                        o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                        o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
                        o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
                                         
                        return o;
                }
               
                v2f vertBlurHorizontal(appdata_img v) {
                        v2f o;
                        o.pos = UnityObjectToClipPos(v.vertex);
                       
                        half2 uv = v.texcoord;
                       
                        o.uv[0] = uv;
                        o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                        o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                        o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
                        o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
                                         
                        return o;
                }
               
                fixed4 fragBlur(v2f i) : SV_Target {
                        float weight[3] = {0.4026, 0.2442, 0.0545};
                       
                        fixed3 sum = tex2D(_MainTex, i.uv[0]).rgb * weight[0];
                       
                        for (int it = 1; it < 3; it++) {
                                sum += tex2D(_MainTex, i.uv[it*2-1]).rgb * weight[it];
                                sum += tex2D(_MainTex, i.uv[it*2]).rgb * weight[it];
                        }
                       
                        return fixed4(sum, 1.0);
                }
                    
                ENDCG
               
                ZTest Always Cull Off ZWrite Off
               
                Pass {
                        NAME "GAUSSIAN_BLUR_VERTICAL"
                       
                        CGPROGRAM
                          
                        #pragma vertex vertBlurVertical  
                        #pragma fragment fragBlur
                          
                        ENDCG  
                }
               
                Pass {  
                        NAME "GAUSSIAN_BLUR_HORIZONTAL"
                       
                        CGPROGRAM  
                       
                        #pragma vertex vertBlurHorizontal  
                        #pragma fragment fragBlur
                       
                        ENDCG
                }
        }
        FallBack "Diffuse"
}


URP版:

Shader "Unlit/Chapter12-GaussianBlur"
{
    Properties {
                _MainTex ("Base (RGB)", 2D) = "white" {}
                _BlurSize ("Blur Size", 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)
            half4 _MainTex_TexelSize;
                    float _BlurSize;
        CBUFFER_END

        TEXTURE2D(_MainTex);       SAMPLER(sampler_MainTex);

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

        struct v2f {
                        float4 pos : SV_POSITION;
                        half2 uv[5] : TEXCOORD0;
        };

                v2f vertBlurVertical(appdata v) {
                        v2f o;
                        o.pos = TransformObjectToHClip(v.vertex);
                       
                        half2 uv = v.texcoord;
                       
                        o.uv[0] = uv;
                        o.uv[1] = uv + float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                        o.uv[2] = uv - float2(0.0, _MainTex_TexelSize.y * 1.0) * _BlurSize;
                        o.uv[3] = uv + float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
                        o.uv[4] = uv - float2(0.0, _MainTex_TexelSize.y * 2.0) * _BlurSize;
            //和属性_BlurSize相乘来控制采样距离。在高斯核维数不变的情况下,_BlurSize越大,模糊程度越高
                                         
                        return o;
                }
               
                v2f vertBlurHorizontal(appdata v) {
                        v2f o;
                        o.pos = TransformObjectToHClip(v.vertex);
                       
                        half2 uv = v.texcoord;
                       
                        o.uv[0] = uv;
                        o.uv[1] = uv + float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                        o.uv[2] = uv - float2(_MainTex_TexelSize.x * 1.0, 0.0) * _BlurSize;
                        o.uv[3] = uv + float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
                        o.uv[4] = uv - float2(_MainTex_TexelSize.x * 2.0, 0.0) * _BlurSize;
                                         
                        return o;
                }
               
                half4 fragBlur(v2f i) : SV_Target {
                        float weight[3] = {0.4026, 0.2442, 0.0545};
            //高斯权重
                       
                        half3 sum = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[0]).rgb * weight[0];
                       
                        for (int it = 1; it < 3; it++) {
                                sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[it*2-1]).rgb * weight[it];
                                sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.uv[it*2]).rgb * weight[it];
                        }
                       
                        return half4(sum, 1.0);
                }
                    
                ENDHLSL
               
                ZTest Always Cull Off ZWrite Off
               
                Pass {
                        NAME "GAUSSIAN_BLUR_VERTICAL"
                       
                        HLSLPROGRAM
                          
                        #pragma vertex vertBlurVertical  
                        #pragma fragment fragBlur
                          
                        ENDHLSL  
                }
               
                Pass {  
                        NAME "GAUSSIAN_BLUR_HORIZONTAL"
                       
                        HLSLPROGRAM  
                       
                        #pragma vertex vertBlurHorizontal  
                        #pragma fragment fragBlur
                       
                        ENDHLSL
                }
        }
        FallBack "Packages/com.unity.render-pipelines.universal/FallbackError"
}
URP后处理脚本

因为和上面提供的(Post-processing : RenderFeature + VolumeComponent)模板有些许差异(降采样、两个Pass分别渲染、迭代处理),因此在此提供对应源码。
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class GaussianBlur : ScriptableRendererFeature
{
    [Serializable, VolumeComponentMenu("Ding Post-processing/12.4 GaussianBlur")]
    public class CustomVolumeComponent : DingVolumeComponentBase
    {
        public ClampedFloatParameter BlurSpread = new ClampedFloatParameter(0.5f, 0.1f, 3f);
        public ClampedIntParameter Iterations = new ClampedIntParameter(3, 1, 4);
        public ClampedIntParameter DownSample = new ClampedIntParameter(2, 1, 8);
        
        public override bool IsActive() => isRender.value;
        public override bool IsTileCompatible() => false;

    }

    class CustomRenderPass : ScriptableRenderPass
    {
        
        public Material material;
        //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 tempTexture0;
        private RenderTargetHandle tempTexture1;
        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的名字
            tempTexture0.Init("_TempRTexture0");
            tempTexture1.Init("_TempRTexture1");
        }

        public void Setup(RenderTargetIdentifier source, RenderTargetHandle destination){
            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)){

                ref var cameraData = ref renderingData.cameraData;
                int w = cameraData.camera.scaledPixelWidth / volume.DownSample.value;
                int h = cameraData.camera.scaledPixelHeight / volume.DownSample.value;

                cmd.GetTemporaryRT(tempTexture0.id, w, h, 0, filterMode);
                Blit(cmd, source, tempTexture0.Identifier());

                for(int i = 0; i < volume.Iterations.value; i++){
                    material.SetFloat("_BlurSize", 1.0f + i * volume.BlurSpread.value);
                    cmd.GetTemporaryRT(tempTexture1.id, w, h, 0, filterMode);
                    Blit(cmd, tempTexture0.Identifier(), tempTexture1.Identifier(), material, 0);
                    cmd.ReleaseTemporaryRT(tempTexture0.id);
                    tempTexture0 = tempTexture1;
                    cmd.GetTemporaryRT(tempTexture1.id, w, h, 0, filterMode);
                    Blit(cmd, tempTexture0.Identifier(), tempTexture1.Identifier(), material, 1);
                    cmd.ReleaseTemporaryRT(tempTexture0.id);
                    tempTexture0 = tempTexture1;
                }

                Blit(cmd, tempTexture0.Identifier(), source);

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

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

    [System.Serializable]
    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);
    }

    // Here you can inject one or multiple render passes in the renderer.
    // This method is called when setting up the renderer once per-camera.
    //每一帧都会调用
    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, dest);

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

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




效果图:







如有收获,请留下“关注”和“赞同”~

本帖子中包含更多资源

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

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

本版积分规则

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

GMT+8, 2024-9-21 19:56 , Processed in 0.091487 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

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