Unity URP CG/HLSL精简规范总结

使用shader graph过程中,想要实现一些多pass效果,虽然使用render feature能够一定程度上解决需求,但我认为render feature始终是一个在特定阶段做一些简单操作的实用工具,在实现多pass效果的时候总会有一些不如意,所以总结了unity官网上的CG/HLSL规范,也是下一步准备浏览大佬作品的前置条件。如果连基本规则都不懂,即使是看到一些简单操作也会一头雾水。

Shader "URPShaderTest"
{
    Properties
    {
        //Color(4,线性SRGB)、Vector(4)、Float、2D(texture)、Cube(cubmap)、Integer、Range、2DArray、3D、CubeArray
        //对应下面声明fixed4、float4、float、sampler2D、samplerCUBE
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        _MainTex("Texture", 2D) = "white" {}
        [Toggle(Switch)]
        _Switch("Switch", Integer) = 0
        _OffsetUnitScale("OffsetUnitScale", Integer) = 0
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        //特性
        /*
        [Gamma] 指示浮点或矢量特性使用sRGB值
        [HDR] 指示纹理或颜色特性使用高动态范围(HDR)值
        [HideInInspector] 藏
        [MainTexture] 设置材质的主纹理,可以使用Material.mainTexture访问该材质。默认名称为_MainTex的纹理为主纹理
        [MainColor] 设置材质的主颜色,可以使用Material.color访问该材质。默认名称为_color的颜色为主颜色
        [NoScaleOffset] 为该纹理属性隐藏平铺和偏移字段
        [Normal] 指示法线贴图纹理
        [PerRendererData] 指示纹理特性将来自MaterialPropertyBlock,inspector将显示为只读
        */
        //MaterialProperty类 https://docs.unity3d.com/2021.3/Documentation/ScriptReference/MaterialProperty.html  Material.GetFloat, Material.SetFloat等等
        //编辑器扩展MaterialPropertyDrawer、MaterialEditor、MaterialProperty、ShaderGUI https://docs.unity3d.com/2021.3/Documentation/Manual/SL-CustomEditor.html
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        //保留名称
        //_TransparencyLM ("Transmissive Texture", 2D) = "white" {}  //在光照贴图过程中启用自定义RGB透明度
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        // Cg/HLSL 可以接受uniform值 https://docs.unity3d.com/2021.3/Documentation/Manual/SL-PropertiesInPrograms.html 矩阵和数组只能作为非序列化的运行时属性存在(因为无法在properties块中定义它们)

    }
    SubShader
    {
        //SubShaderTags
        //RenderPipeline:UniversalRenderPipeline/HighDefinitionRenderPipeline  主要控制渲染用的管线
        //Queue:Background(1000)Geometry(default)(2000)AlphaTest(2450)Transparent(3000)Overlay(4000)  或者可以写具体值1300或者Background+300等等  主要控制渲染顺序
        //RenderType  没有默认值 替换材质时可以使用这个值作标识:Camera.SetReplacementShader(ShaderType,“ReplaceMePlease”);平时不必使用   貌似也用作深度图生成?
        //ForceNoShadowCasting true false  是否阻止阴影投射,有时也阻止接收
        //DisableBatching True False默认 LODFading当Lod值非0时阻止,反之不阻止 阻止unity对使用这个SubShader的几何体应用动态批处理  动态批处理会将所有几何体转换为世界空间,着色器程序无法再访问对象空间。可以避免依赖对象空间的着色器程序无法正确渲染的问题
        //IgnoreProjector True False默认 不希望任何投影类型材质或者贴图,影响我们的物体或者着色器  UI上用的多
        //PreviewType Sphere Plane Skybox 检视预览用什么方式展示 
        //CanUseSpriteAtlas 警告用户着色器依赖于原始纹理坐标,因此不应将其纹理打包到图集中 多用于使用Legacy Sprite Packer的项目中
        Tags {"RenderPipeline" = "UniversalRenderPipeline" "Queue" = "Geometry+100" "RenderType" = "ReplaceMePlease" "ForceNoShadowCasting" = "True" "DisableBatching" = "False" "IgnoreProjector" = "False" "PreviewType" = "Sphere"}
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        //这个值和与相机的距离没有关系,Unity不会自动计算着色器LOD,必须手动设置最大着色器LOD,表示它在计算上的要求有多高
        // 当有多个subshader时,LOD必须按降序排列,这是因为Unity查找选择第一个有效的SubShader,所以如果它首先找到一个LOD较低的子着色器,它将始终使用它。看来是值越高计算要求越高
        LOD 200
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        //为了支持SRP Batcher,Shader中要将所有暴露出的参数(贴图除外)给包含到CBUFFER_START(UnityPerMaterial)与CBUFFER_END之间。并且为了保证之后的每个Pass都能拥有一样的CBUFFER
        //这一段代码需要写在SubShader之内,其它Pass之前。
        //CBUFFER_START(UnityPerMaterial)
        //    float4 _MainTex_ST;
        //    float _Switch;
        //CBUFFER_END
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        //插入来自另一个着色器的命名Pass,减少Pass代码重复  这里插入的是自己的
        UsePass "URPShaderTest/URPShaderTestPass0"
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        //抓取帧缓冲区纹理到这个命名纹理中去,可以在Pass中声明使用这张纹理。如果不写名称会抓到_GrabTexture中去,但是使用自定义命名可以减少屏幕抓取次数,故尽量使用自命名
        GrabPass
        {
            "_BackgroundTexture"
        }
        /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
        Pass
        {
            //  某些着色器需要在不同的管线下运行,需要这个模块  这个模块必须在Pass或者subshader的最开始声明,看你想单独控制pass还是整个subshader
            //  这些版本必须和package manager里对应上,否则Pass或者subshader无法正常工作
            //  不同的写法有不同的行为https://docs.unity3d.com/Manual/SL-PackageRequirements.html
            PackageRequirements
            {
                "com.unity.render-pipelines.universal": "[13.0.1,14.0.7]"
                "com.unity.textmeshpro" : "3.0.6"
            }
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            Name "URPShaderTestPass0"
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //指令:也可以单独控制pass或整个subshader,但是subshader的会被pass覆盖
            //部分一:渲染状态
            //AlphaToMask On Off 根据片段着色器输出中的alpha值按比例修改multisample覆盖遮罩 在不使用MSAA的情况下启用结果可能是不可预测的
            AlphaToMask Off
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //Blend Off默认 这里要花多行讲解一下
            /*混合公式为:finalValue = sourceFactor * sourceValue operation destinationFactor * destinationValue  *这里的operation是下面的BlendOp 默认是Add*
            Blend的签名分为三种,State、Render Target、Factor。
            其中State(Off)和Render Target(0到7的整数)只会出现一次  【简单来讲,渲染目标(Render Target)就是一种可以在运行时写入的纹理。从引擎的角度讲,渲染目标会存储颜色、法线以及AO等信息】
            Factor总共有四个,出现顺序从左到右按此解读: ,  
            比如Blend 1 One Zero, Zero One就会这么解读  ,  
            但是:任何指定渲染目标的签名都需要OpenGL 4.0+、GL_ARB_draw_buffers_blend或OpenGL ES 3.2。单独的RGB和alpha混合与高级OpenGL混合操作不兼容。*/
            /*One 值为1,使用此因子来让帧缓冲区源颜色或是目标颜色完全的通过。1
            Zero 值为0,使用此因子来删除帧缓冲区源颜色或目标颜色的值。0
            SrcColor 使用此因子为将当前值乘以帧缓冲区源颜色的值 A
            SrcAlpha 使用此因子为将当前值乘以帧缓冲区源颜色Alpha的值。A*alpha
            DstColor 使用此因子为将当前值乘以帧缓冲区目标颜色的值。B
            DstAlpha 使用此因子为将当前值乘以帧缓冲区目标颜色Alpha分量的值。B*alpha
            OneMinusSrcColor 使用此因子为将当前值乘以(1 -帧缓冲区源颜色值) 1- A
            OneMinusSrcAlpha 使用此因子为将当前值乘以(1 -帧缓冲区源颜色Alpha分量的值) 1-A*lpha
            OneMinusDstColor 使用此因子为将当前值乘以(1 –目标颜色值) 1-B
            OneMinusDstAlpha使用此因子为将当前值乘以(1 –目标Alpha分量的值) 1- B*alpha*/
            //src是这个通道渲染出来的颜色值,des是帧缓冲中已经存在的颜色值,跟当前pass的渲染时间有关系,如果这个pass是在最后渲染,那么帧缓冲则是屏幕上渲染完所有其他物体后的颜色值
            Blend Zero One //  这样就是舍弃这个通道产生的颜色值而使用帧缓冲中的颜色值,也就是把这个物体透过去了
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //*添加BlendOp要保证已经存在Blend指令,且并非所有设备都支持所有混合操作!!!要注意的如下*
            /*1、Min and Max require GL_EXT_blend_minmax on OpenGL ES 2.
            2、Logical operations require DX 11.1+ or Vulkan.
            3、Advanced OpenGL blending operations require GLES3.1 AEP+, GL_KHR_blend_equation_advanced, or GL_NV_blend_equation_advanced. They can only be used with standard RGBA blending; they are not compatible with separate RGB and alpha blending.*/
            BlendOp Max //  或者使用这样的混合操作标识  Add(默认), Sub, RevSub, Min, Max等等值非常多,甚至带逻辑运算,具体看这里吧,感觉不常用 https://docs.unity3d.com/Manual/SL-BlendOp.html
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //默认情况下,GPU会写入所有通道(RGBA)。对于某些效果,您可能希望不修改某些通道,这时就需要ColorMask
            //ColorMask  
            //channels代表颜色通道,值有0、R、G、B、A或是任意组合如:RA、RGB *写哪个代表允许写入哪个通道,值0代表可以写入所有通道,和RGBA应该是一样的*
            //render target如上所说,值为0到7
            ColorMask RGBA
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //是否开启保守光栅化,GPU不考虑覆盖范围,只要有一部分被三角形覆盖的像素就进行光栅化,等于说生成的像素会变多
            //使用SystemInfo.supportsConservativeRaster API检查硬件是否支持,不支持会被忽略
            Conservative False
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //裁剪:Back(默认)、Front、Off
            Cull Back
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //深度偏移:调整深度偏移以强制GPU在相同深度的其他几何体之上绘制几何体,这个东西能防止z-fighting(相同深度面片渲染打架)
            //Offset ,   
            //factor:缩放最大Z坡度(不平行于近剪裁平面和远剪裁平面的多边形具有Z坡度),范围【-1,1】
            //units:缩放最小可解析深度缓冲区值,以产生恒定的深度偏移,范围【-1,1】 负值表示GPU将多边形绘制得更靠近摄影机。正值表示GPU绘制的多边形离摄影机更远。
            Offset -1, -1  //  一般来说这样就可以有效防止z-fighting
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            Offset -1, [_OffsetUnitScale]  //  *想用属性往任意指令里面写值的话*
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //模版缓冲区为每个像素存储一个8位整数值,模板测试失败GPU将跳过该像素,可以执行两种操作---写入和测试,它们甚至可以在一条指令中同时执行
            //使用Ref、ReadMask和Comp参数来配置模版测试。使用Ref、WriteMask、Pass、Fail和ZFail参数来配置模版写入。
            //(ref & readMask) comparisonFunction (stencilBufferValue & readMask)  测试方程,写入的话只是(ref & readMask)在通过或失败情况下怎么做
            Stencil{
            Ref 2           //这是传进去的int值(默认0【0,255】注意这是二进制11111111),如果在测试就用这个值和模板值(如果没有写入过默认0)作比较,受readMask影响、如果在写入就是写入这个值,受writeMask影响
            ReadMask 255    //这是传进去的int值(默认0【0,255】注意这是二进制掩码11111111,与Ref的二进制相与)这个掩码应该是为了不让自己写入的值影响自己,所以一般两个掩码应该是不同的,一般有通道单独写模板值,所以读取通道掩码应和那个写入通道的掩码匹配
            WriteMask 155   //这是传进去的int值(默认0【0,255】注意这是二进制掩码11111111,与Ref的二进制相与)
            Comp Always     //何时通过模板测试,这个和写入没有任何关系,还有CompBack和CompFront专门针对背面和正面,如果存在Comp则会覆盖他们,值为:Never、Less、Equal、LEqual、Greater、NotEqual、GEqual、Always
            Pass Replace    //设置对通过模板测试的像素怎么处理,还有PassBack和PassFront专门针对背面和正面,如果存在Pass则会覆盖他们,值为:Keep(保持原值)、Zero(写入模板值0)、Replace(写入Ref值)、IncrSat(加上去但不超过255)、DecrSat(相减但不小于0)、Invert(所有位取反)、IncrWrap(加上去但超过255会从0重新开始)、DecrWrap(相减但小于0会从255重新开始)
            Fail Keep       //设置对未通过模板测试的像素怎么处理,还有FailBack和FailFront专门针对背面和正面,如果存在Fail则会覆盖他们,值和Pass的相同
            ZFail Keep      //当像素通过模板测试但未通过深度测试时怎么处理,还有ZFailBack和ZFailFront专门针对背面和正面,如果存在ZFail则会覆盖他们,值和Pass的相同
            }
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //是否开启深度裁剪  该模式确定GPU如何处理近平面和远平面之外的片段
            ZClip False      //True(默认,直接切掉)、False(clamp操作,低于近平面设为近平面,远于远平面设为远平面)
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //设置深度测试通过规则 Less、LEqual(默认)、Equal、GEqual、Greater、NotEqual、Always
            ZTest LEqual
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //深度写入 On(默认)、Off
            ZWrite On
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //通道内的Tags只能配置一个LightMode
            //URP通道的LightMode可选值:
            //UniversalForward  正向渲染  计算灯光  根据所有光源照亮一个物体,之后再渲染下一个物体
            //UniversalForwardOnly  如果URP使用“延迟渲染”但某个Pass必须使用“正向渲染”渲染对象,请使用此值
            //UniversalGBuffer  延迟渲染  不计算灯光  先将所有物体都先绘制到屏幕空间的缓冲(G-buffer几何缓冲区),再逐光源对该缓冲进行着色的过程  将本来在物空 间(三维空间)进行光照计算放到了像空间(二维空间)进行处理。
            //Universal2D  2D渲染用,计算灯光
            //ShadowCaster  渲染阴影贴图使用
            //DepthOnly  渲染深度图使用
            //Meta  当选用此值时,unity仅在灯光烘焙阶段执行此Pass,而在构建时会忽略此Pass
            //SRPDefaultUnlit  默认值,多Pass时可以用它
            Tags {"LightMode" = "SRPDefaultUnlit"}
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            HLSLINCLUDE
             HLSL code that you want to share goes here
            ENDHLSL
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            HLSLPROGRAM
            #pragma enable_d3d11_debug_symbols  //  debug信息
            #pragma target 4.0  // 默认2.5 target的详细信息 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
            //#pragma require [value] 添加某些支持 https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
            #pragma vertex vert
            //#pragma hull hul  //  曲面细分阶段 这两个都需要相当高的target5.0的Tessellation,应该还用不上
            //#pragma domain dom  //  曲面细分阶段
            #pragma geometry geo  // 几何阶段,需要target4.0
            #pragma fragment frag
            //声明关键字shader_feature、multi_compile、dynamic_branch,前两个都是编译时确定,有变体。shader_feature会自动剔除没有使用的。后面是实时的,没有变体,可以使用关键字更改着色器行为
            //全局关键字限制256个,unity已经用了六十几个,为了避免不够用可以使用本地关键字【声明】_local
            #pragma multi_compile_local MyShaderKeyWord1 MyShaderKeyWord2  //这里生成了两个关键字,如果都没有去激活则默认使用第一个,否则使用激活的那个
            //删除哪些不需要的关键字
            #pragma skip_variants SKIP1 SKIP2
            //只想为某些图形API编译某些着色器程序,或是在使用并非所有平台都支持的功能时可以使用这个
            #pragma only_renderers gles gles3 d3d11 glcore vulkan
            #pragma exclude_renderers metal ps4 xboxone ps5
            //For More #pragma https://docs.unity3d.com/Manual/SL-PragmaDirectives.html
            /*----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
            //https://github.com/Unity-Technologies/Graphics/tree/master/Packages/com.unity.render-pipelines.universal/ShaderLibrary
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
            struct appdata
            {
                //顶点着色器语义 https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics?redirectedfrom=MSDN
                float4 vertex : POSITION;
                float3  normal  : NORMAL;
                float4  tan  : TANGENT;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                //像素着色器语义 https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics?redirectedfrom=MSDN
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3  normal  : NORMAL;
            };
            //从片段着色器的返回结构,SV_Target,SV_Depth等,不知道这两个之外还有能用的不。SV_Target可以从0到7,在多渲染目标(MRT)时很有用
            struct fragOutput {
                //语义 https://learn.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics?redirectedfrom=MSDN
                float4 color1 : SV_Target;
                float4 color2 : SV_Target1;
                float depth : SV_Depth;  //用自定义深度覆盖Z缓冲区值,不知道啥用,还会关闭深度缓冲的优化,能不开别开
            };

            //如果是纹理属性:
            //1.可以添加一个_ST标识平铺和偏移。xy为xy平铺值,zw为xy偏移值
            //2.可以添加_TexelSize(float4)获取大小信息。xy为1/宽、1/高,zw为宽高
            //3.纹理_HDR,如何解码潜在HDR的信息(例如RGBM编码的)纹理,这取决于所使用的颜色空间 见UnityCG.cginc着色器包含文件中的DecodeHDR函数
            sampler2D _MainTex;
            float4 _MainTex_ST; 
            float4 _MainTex_TexelSize;
            float4 _MainTex_HDR;

            v2f vert(appdata v, uint vid : SV_VertexID) // 获取顶点 ID,必须为 uint
            {
                v2f o;
                o.vertex = TransformObjectToHClip(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.normal = v.normal;
                return o;
            }

            [maxvertexcount(3)]
            void geo(triangle v2f p[3], inout LineStream istream)
            {
                for (int i = 0; i < 3; i++)
                {
                    v2f o = (v2f)0;
                    o.vertex = p[i].vertex;
                    o.normal = p[i].normal;
                    o.uv = p[i].uv;
                    //将每个顶点添加到LineStream流里
                    istream.Append(o);
                }
            }
            //https://docs.unity3d.com/cn/current/Manual/SL-ShaderSemantics.html
            //特殊:屏幕uv坐标screenPos(float2)、是否面向摄像机facing,大于0是面向,小于0是反向
            //UNITY_VPOS_TYPE screenPos : VPOS,屏幕空间像素位置,感觉用起来有弊端,SV_POSITION不能放在结构体
            fragOutput frag(v2f i, float facing : VFACE) : SV_Target
            {
                fragOutput o;
                o.color1 = tex2D(_MainTex, i.uv);
                o.color2 = float4(i.uv, 0, 0);
                o.depth = 50;
                return o;
            }
            ENDHLSL
        }
    }
    Fallback Off
}

你可能感兴趣的:(unity,游戏引擎)