Unity URP Shader

"Unity URP Shader"

Posted by Gumc on January 29, 2023

URPShaderCodeSample的Shader代码学习

ShadersLab Shader学习网站

Base

Texture

Shader "Lakehani/URP/Base/Texture"
{
    Properties
    {
        [MainColor] _BaseColor("BaseColor", Color) = (1,1,1,1)
        [MainTexture] _BaseMap("BaseMap", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" "Queue"="Geometry"}

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
  
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv           : TEXCOORD0;
            };

            struct Varyings
            {
                float2 uv           : TEXCOORD0;
                float4 positionHCS  : SV_POSITION;
            };

            TEXTURE2D(_BaseMap);        //Texture2D textureName
            SAMPLER(sampler_BaseMap);   //SamplerState samplerName

            CBUFFER_START(UnityPerMaterial) //cbuffer name { 常量
            float4 _BaseMap_ST;
            half4 _BaseColor;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                // 将对象的顶点坐标从对象空间(Object Space)转换到裁剪空间(Homogeneous Clip Space)
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                // 用于在纹理坐标中应用平铺和偏移变换 ((tex.xy) * name##_ST.xy + name##_ST.zw) 
                OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                return SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor; // textureName.Sample(samplerName, coord2)
            }
            ENDHLSL
        }
    }
}

Color

Shader "Lakehani/URP/Base/Texture"
{
    Properties
    {
        [MainColor] _BaseColor("BaseColor", Color) = (1,1,1,1)
        [MainTexture] _BaseMap("BaseMap", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" "Queue"="Geometry"}

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
  
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv           : TEXCOORD0;
            };

            struct Varyings
            {
                float2 uv           : TEXCOORD0;
                float4 positionHCS  : SV_POSITION;
            };

            TEXTURE2D(_BaseMap);        //Texture2D textureName
            SAMPLER(sampler_BaseMap);   //SamplerState samplerName

            CBUFFER_START(UnityPerMaterial) //cbuffer name { 常量
            float4 _BaseMap_ST;
            half4 _BaseColor;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                // 将对象的顶点坐标从对象空间(Object Space)转换到裁剪空间(Homogeneous Clip Space
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                //((tex.xy) * name##_ST.xy + name##_ST.zw) 
                OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                return SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor; // textureName.Sample(samplerName, coord2)
            }
            ENDHLSL
        }
    }
}

Transparent

Shader "Lakehani/URP/Base/Transparent"
{
    Properties
    {
        _MainColor("Color",Color)=(1,1,1,1)
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "RenderPipeline"="UniversalRenderPipeline" "Queue"="Transparent"}

        Pass
        {
            // 将禁止将像素的深度信息写入深度缓冲区。这意味着,禁用深度写入后,像素的深度值将不会影响后续像素的可见性和遮挡关系。这在一些特定的渲染效果中很有用,例如透明物体的渲染,或者需要自定义深度排序的情况下
            ZWrite Off
            // 混合(Blending)是将两个像素处理成一个的过程,是内置渲染管线(Built-in)与可编程渲染管线(SRP)都兼容的一种命令
            // 混合 发生在“合并(merging)”的阶段,它将像素的最终颜色(在片元着色器阶段处理后的像素)与像素的深度结合在一起。这个阶段位于渲染管线的末端,在片元着色器阶段之后,是执行模版缓冲(stencil-buffer)、深度缓冲(z-buffer)和颜色混合(color blending)的地方。
            // “混合”是一个函数,需要两个输入,经过一个计算公式得到屏幕上的最终颜色。根据 Unity 的官方文档,定义 混合结果的值的等式如下:B = SrcFactor * SrcValue [OP] DstFactor * DstValue.
            // 为了理解这个公式,我们需要记住这个顺序: 首先是片元着色器阶段,然后是可选的合并阶段。
            // "SrcValue" (源颜色)代表的是经过片元着色器处理的像素的RGB值。
            // "DstValue" (目标颜色) 代表的是已经写入“目标缓冲”的像素的RGB值,目标缓冲更常见的名字是渲染目标(SV_Target)。当我们没有在着色器中设置混合选项时,源颜色会直接覆盖目标值。如果我们设置了混合,源颜色与目标颜色就会混合产生一个新的颜色,再覆盖先前的目标颜色。
            // "SrcFactor" (源系数) and "DstFactor" (目标系数) 都是一个三维向量,它们主要的功能是修改源颜色和目标颜色来实现有趣的效果。

            // Off, 禁用混合选项。
            // One, (1, 1, 1)。
            // Zero, (0, 0, 0)。
            // SrcColor 等于源颜色的RGB值。
            // SrcAlpha 等于源颜色的透明度。
            // OneMinusSrcColor, 1 - 源颜色的RGB值 (1 - R, 1 - G, 1 - B)。
            // OneMinusSrcAlpha, 1 - 源颜色的透明度 (1 - A, 1 - A, 1- A)。
            // DstColor 等于目标颜色的RGB值。
            // DstAlpha 等于目标颜色的透明度。
            // OneMinusDstColor, 1 - 目标颜色的RGB值 (1 - R, 1 - G, 1 - B)。
            // OneMinusDstAlpha, 1 - 目标颜色的透明度 (1 - A, 1 - A, 1- A)。
            Blend SrcAlpha OneMinusSrcAlpha

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"


            struct Attributes
            {
                float4 positionOS   : POSITION;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
            };

            CBUFFER_START(UnityPerMaterial)
            half4 _MainColor;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                // 将对象空间(Object Space)中的坐标转换为世界空间(World Space)中的坐标。
                float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
                // 世界空间坐标转换为裁剪空间坐标
                OUT.positionHCS = TransformWorldToHClip(positionWS);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                return _MainColor;
            }

            ENDHLSL
        }
    }
}

AlphaTest

Shader "Lakehani/URP/Base/AlphaTest"
{
    Properties
    {
        _AlphaTestTexture("AlphaTest Texture", 2D) = "white" {}
        _ClipThreshold("Alpha Test Threshold",Range(0,1)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" "Queue"="AlphaTest"}

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"


            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv:TEXCOORD0;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float2 uv:TEXCOORD0;
            };

            CBUFFER_START(UnityPerMaterial)
            float4 _AlphaTestTexture_ST;
            float _ClipThreshold;
            CBUFFER_END

            TEXTURE2D(_AlphaTestTexture);
            SAMPLER(sampler_AlphaTestTexture);

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
                OUT.positionHCS = TransformWorldToHClip(positionWS);
                // 在纹理坐标中应用平铺和偏移变换
                OUT.uv = TRANSFORM_TEX(IN.uv, _AlphaTestTexture);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                // 接受纹理、采样器和纹理坐标作为输入,并返回在给定纹理坐标处采样到的颜色值
                half alpha = SAMPLE_TEXTURE2D(_AlphaTestTexture, sampler_AlphaTestTexture, IN.uv).r;
                //也可以使用 AlphaDiscard 宏,但是需要在头部定义 _ALPHATEST_ON
                //例如:#pragma shader_feature_local_fragment _ALPHATEST_ON
                //具体实现见 ShaderVariablesFunctions.hlsl 

                // clip函数会判断参数是否小于零 ,如果参数小于零则直接discard片元,不再进行fragment中后续的任何计算。
                clip(alpha - _ClipThreshold);
                // 为何不需要使用到alpha?那alpha做什么用,还是clip/AlphaTest背后做了什么。
                // 猜测是clip丢弃后面的则不再处理就是黑色的了,如果处理就返回白色了。
                return half4(1,1,1,1);
            }

            ENDHLSL
        }
    }
}

Fog

Shader "Lakehani/URP/Base/Fog"
{
    Properties
    {
        _MainColor("Color",Color) = (0,0,0,1)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" "Queue"="Geometry"}

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #pragma multi_compile_fog

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"


            struct Attributes
            {
                float4 positionOS   : POSITION;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float fogFactor : TEXCOORD0;
            };

            CBUFFER_START(UnityPerMaterial)
            half4 _MainColor;
            // 为何不需要在Properties中定义,因为通过在材质中设置 _FogFactor 的值,可以控制雾效果在渲染中的表现
            float _FogFactor;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
                OUT.positionHCS = TransformWorldToHClip(positionWS);
                // ComputeFogFactor返回real,使用real的作用是为了适配多平台之间精度不同的问题。在使用时,我们把他当作half或float即可
                OUT.fogFactor = ComputeFogFactor(OUT.positionHCS.z);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                //自带的3中计算雾的宏的类型 FOG_LINEAR、FOG_EXP 、FOG_EXP2 对应于 Lighting->Environment 面板的 Other Settings
                //具体见 ShaderVariablesFunctions.hlsl 的 MixFog 函数
                // MixFog是将颜色rgb根据fogFactor计算方式
                half3 mixColorAndFog = MixFog(_MainColor.rgb , IN.fogFactor);
                return half4(mixColorAndFog.rgb,1);
            }

            ENDHLSL
        }
    }
}

Fresnel

Shader "Lakehani/URP/Base/Fresnel"
{
    //同一个地方的湖面,在不同的位置去观察它,总能看到它在不同位置所呈现的效果是不一样的,近处看可以看到清澈见底的湖水,远处看却是波光粼粼的湖面。现实生活中类似这种现象非常普遍,并且每天都可以观察到,当视线垂直于表面时,反射较弱,而当视线与表面呈一定夹角时,夹角越小,反射就越明显。而要研究夹角与反射光的关系时(同时折射也会伴随影响),就需要了解菲涅尔效应。
    // 原理:首先需要明确的是菲涅尔是一个人的名字,是他提出了用数学方式来描述我们在介绍部分描绘的现象,由于是他提出的数学方程,所以该方程又被称为菲涅尔方程(或称为菲涅尔条件)。菲涅尔方程的公式如下:
    // R(θ) = R(0)+(1- R(0))*(1-cos(θ))^5
    // R(0) = (n1 - n2)²/(n1 + n2)²
    // https://zhuanlan.zhihu.com/p/151375798
    Properties
    {
        _Color("Color",Color) = (1,1,1,1)
        _Power("Power",Float) = 5
        [Toggle] _Reflection("Reflection",Float) = 1

    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" "Queue"="Geometry"}

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            //关于[Toggle] [ToggleOff]如何使用看官方文档 https://docs.unity3d.com/ScriptReference/MaterialPropertyDrawer.html
            #pragma multi_compile __ _REFLECTION_ON 

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float3 normalOS : NORMAL;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float3 normalWS : TEXCOORD0;
                float3 viewWS : TEXCOORD1;
            };

            CBUFFER_START(UnityPerMaterial)
            half4 _Color;
            half _Power;
            CBUFFER_END


            Varyings vert(Attributes IN)
            {
                Varyings OUT;

                float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
                OUT.positionHCS = TransformWorldToHClip(positionWS);
                // 将物体空间中的法线向量转换为世界空间中的法线向量
                OUT.normalWS =TransformObjectToWorldNormal(IN.normalOS);
                // 计算世界空间中的观察方向向量。
                OUT.viewWS = GetWorldSpaceViewDir(positionWS);

                return OUT;
            }

            half Fresnel(half3 normal, half3 viewDir, half power)
            {
                //因为菲涅尔的计算公式有些复杂,所以用近似实现,常见一下2种
                //一般使用Schlick近似F0代表基础反射率,F0 = ((n-1)/(n+1))^2 n代表折射率1为空气
                //非金属的F0较小,金属F0较大,处于简化目的可以通过金属度在预设F0之间插值例如:float3 F0 = 0.04;F0 = lerp(F0 , BaseColor,Metallic);
                //后面PBR的例子中你会经常看到这个
                //F_Schlick(v, n) = F0 + (1 - F0)(1 - dot(v, n))^5
                //F_Schlick_UnrealEngine(H,V)= F0 + (1.0 - F0)* exp2((-5.55473 * HdotV - 6.98316) * HdotV);

                //F_Empricial(v, n) = max(0, min(1, bias + scale * (1- dot(v, n)^power)))


                //unity中常用这种方式实现 (1 - dot(v, n))^power
                return pow((1.0 - saturate(dot(normalize(normal), normalize(viewDir)))), power);
            }

            //为了看到反射内容,我直接采样了ReflectionProbe的Cube贴图
            half3 Reflection(float3 viewDirWS, float3 normalWS)
            {
                // reflect 函数接受入射向量和表面法线作为输入,并返回反射向量。反射向量是入射向量在表面法线上的镜像反射
                // 使用 reflect 函数可以在 Shader 中模拟光线在表面上的反射效果。这对于实现镜面反射、光线折射等效果非常有用。
                float3 reflectVec = reflect(-viewDirWS, normalWS);
                return DecodeHDREnvironment(SAMPLE_TEXTURECUBE(unity_SpecCube0, samplerunity_SpecCube0, reflectVec), unity_SpecCube0_HDR);
            }

            half4 frag(Varyings IN) : SV_Target
            {
                half3 normalWS = normalize(IN.normalWS);
                half3 viewWS = SafeNormalize(IN.viewWS);
                half fresnel = Fresnel(normalWS, viewWS,_Power);
                half4 totlaColor = _Color* fresnel;
                //------- 这仅仅是演示菲涅尔和cubmap联合使用的例子,有镜面反射的感觉,基础的菲涅尔并不需要这个部分。
                #if defined(_REFLECTION_ON)
                    half3 cubemap=Reflection(viewWS,normalWS);
                    totlaColor.xyz *= cubemap;
                #endif
                //-------

                return totlaColor;
            }

            ENDHLSL
        }
    }
}

Noise

VertexColor

VertexAnimation

UVCheck

NormalCheck

TangentCheck

BitangentCheck

TextureScrollUV

TextureScreenSpace

Light

Lambert

光照模型假设光线在表面上均匀地散射,不考虑镜面反射。

Shader "Lakehani/URP/Lighting/Lambert"
{
    Properties
    {
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline"  "Queue"="Geometry"}

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
      
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float3 normalOS : NORMAL;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float3 normalWS : TEXCOORD0;
            };

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.normalWS=TransformObjectToWorldNormal(IN.normalOS);
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                Light light = GetMainLight();
                half3 lightColor = light.color * light.distanceAttenuation;
                // LightingLambert 是 Unity Shader 中的一个函数,用于计算 Lambert 光照模型的漫反射效果。
                // Lambert 光照模型是一种常用的光照模型,用于模拟物体表面的漫反射。它基于光线与表面法线之间的夹角来计算漫反射的强度。
                // Lambert 光照模型假设光线在表面上均匀地散射,不考虑镜面反射。
                // half3 LightingLambert(half3 lightColor, half3 lightDir, half3 normal)
                // {
                //     // 函数首先计算出表面法线和光源方向之间的点积(dot product),并使用 saturate 函数将结果限制在0到1之间。
                //     // 这个点积结果表示了光线和表面法线之间的夹角的余弦值,也被称为 "NdotL"。
                //     half NdotL = saturate(dot(normal, lightDir));
                //     // 将光源颜色(lightColor)与 NdotL 相乘,得到最终的漫反射光照强度。
                //     // 这个乘积表示了光线的强度在表面上的分布,越垂直于表面法线的光线产生的漫反射强度越高。
                //     return lightColor * NdotL;
                // }
                half3 diffuseColor=LightingLambert(lightColor,light.direction,normalize(IN.normalWS));
                half4 totlaColor = half4(diffuseColor.rgb,1);
                return totlaColor;
            }

            ENDHLSL
        }
    }
}

Half-Lambert

Half-Lambert 是由 Valve 公司创建的一种技术,用于在低光照区域中显示物体表面的光照效果。它通过增加材质的漫反射光照并将其包裹在物体表面上,使得物体在低光照条件下更加明亮。 左手边是Lambert模型,右手边是Half Lambert模型 img

Minnaert

 Minnaert 光照模型最初设计用于复制月亮的阴影,因此通常称为月亮着色器。Minnaert 适合模拟多孔或纤维表面,例如月亮或天鹅绒。这些表面会导致大量光线反向散射。这在纤维往往主要垂直于表面(例如天鹅绒、丝绒甚至地毯)的情况下尤其明显。     此模拟提供的结果非常接近 Oren-Nayar,它也经常被称为天鹅绒或月亮着色器。

Minnaert

Oren-Nayer

Oren-Nayar 反射率模型是粗糙表面上漫反射的反射率模型。该模型是一种近似粗糙但仍为朗伯表面的光效果的简单方法。 Oren-Nayer

Wrap

Banded

带状照明,与其说是一个照明模型,不如说是一个照明调整,以展示您可以在标准照明模型上进行简单的数学运算。此方法的工作原理是将照明方向分成多个带。然后可以通过将 NdotL 替换为带状 NdotL 来将此方法传递到任何标准照明模型中,如下所示: Banded-Lighting

BackLight

“Back Lighting”(背光)是指从被拍摄对象的背后照亮主体的过程。这种照明技术可以创造出一种背光效果,使被拍摄对象的边缘发光,从而增加画面的层次感和戏剧性

Phong Specular

Phong Specular(冯氏高光)是计算机图形学中的一种照明模型,用于模拟光线在光滑表面上的反射效果。它是由冯·贝·通(Bui Tuong Phong)在1973年提出的。

Phong Specular 模型通过计算光线从观察者方向看到的镜面反射来模拟光滑表面的高光效果。它基于两个关键向量:入射光线的反射向量和观察者的视线向量。通过计算这两个向量之间的夹角,可以确定高光的强度和位置。

Phong Specular 模型的强度计算公式通常使用余弦函数来表示,其中入射光线的反射向量和视线向量之间的夹角决定了高光的强度。这个模型还可以通过调整参数来控制高光的大小和形状,例如镜面反射系数(specular intensity)和高光的粗糙度。

Phong Specular

//在顶点着色器中实现的冯氏光照模型叫做Gouraud着色(Gouraud Shading),而不是冯氏着色(Phong Shading)
//该例子为冯氏着色
//Phong着色与Phong高光不同,着色模型是一个完整的效果混合,高光只是其中一项

Shader "Lakehani/URP/Lighting/Phong"
{
    Properties
    {
        _BaseColor ("BaseColor", Color) = (1,1,1,1)
        _SpecularColor ("SpecularColor", Color) = (1,1,1,1)
        _Smoothness ("Smoothness",  Range(0,1)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" "Queue"="Geometry"}

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
      
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float3 normalOS : NORMAL;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float3 normalWS : TEXCOORD0;
                float3 viewWS : TEXCOORD1;
            };

            CBUFFER_START(UnityPerMaterial)
            half4 _BaseColor;
            half4 _SpecularColor;
            half _Smoothness;
            CBUFFER_END

            half3 LightingSpecularPhong(half3 lightColor, half3 lightDir, half3 normal, half3 viewDir, half4 specular, half smoothness)
            {
                // 函数使用reflect函数计算出光源方向在表面法线上的反射方向(reflectDir)。
                half3 reflectDir = normalize(reflect(-lightDir,normal));
                // 然后,它计算出反射方向与视角方向之间的点积(LdotV),并使用saturate函数将结果限制在0到1之间。
                half LdotV = saturate(dot(reflectDir,viewDir));
                // 函数使用pow函数将LdotV的值按照光滑度进行幂运算,得到一个修饰因子(modifier)。
                // 这个修饰因子控制了镜面反射的强度,光滑度越高,修饰因子越接近1,镜面反射越强
                half modifier = pow(LdotV, smoothness);
                // 将镜面反射颜色(specular.rgb)与修饰因子相乘,得到最终的镜面反射光照强度。
                // 这个结果表示了光线在表面上的镜面反射效果。
                half3 specularReflection = specular.rgb * modifier;
				return lightColor * specularReflection;
            }

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
                OUT.positionHCS = TransformWorldToHClip(positionWS);
                OUT.normalWS =TransformObjectToWorldNormal(IN.normalOS);
                OUT.viewWS = GetWorldSpaceViewDir(positionWS);

                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                Light light = GetMainLight();
                half3 lightColor = light.color * light.distanceAttenuation;
                half smoothness = exp2(10 * _Smoothness + 1);
                half3 normalWS = normalize(IN.normalWS);
                half3 viewWS =  SafeNormalize(IN.viewWS);
                // LightingSpecularPhong函数计算出镜面反射光照强度
                half3 specularColor = LightingSpecularPhong(lightColor, light.direction, normalWS,viewWS, _SpecularColor, smoothness);
                // LightingLambert函数计算出漫反射颜色(diffuseColor)
                half3 diffuseColor = LightingLambert(lightColor,light.direction,normalWS) * _BaseColor.rgb;
                // 将unity_AmbientSky.rgb与基础颜色相乘,计算出环境光颜色(ambientColor)
                half3 ambientColor = unity_AmbientSky.rgb * _BaseColor.rgb;
                // 将漫反射颜色、镜面反射颜色和环境光颜色相加,得到总体颜色(totalColor)。
                // 这个总体颜色表示了光照模型计算得到的最终颜色。
                half4 totalColor = half4(diffuseColor + specularColor + ambientColor,1);

                return totalColor;

            }



            ENDHLSL
        }
    }
}

Blinn-Phong Specular

Blinn-Phong 是一种计算机图形学中的照明模型,用于模拟光线在光滑表面上的反射效果。它是对 Phong 照明模型的改进和扩展。

Blinn-Phong 模型在计算光照时考虑了观察者的视线向量和光源的方向向量之间的半程向量(halfway vector)。通过计算表面法线与半程向量之间的夹角,可以确定高光的强度和位置。与 Phong 模型相比,Blinn-Phong 模型使用半程向量替代了反射向量,从而在计算效率和视觉效果之间取得了平衡。

Blinn-Phong 模型的计算公式通常使用余弦函数来表示,其中夹角的余弦值决定了高光的强度。通过调整模型中的参数,如高光系数(specular intensity)和高光的粗糙度,可以控制高光的大小和形状。

Blinn-Phong Specular

/*
如果你想获得和Phong(冯氏)着色类似的效果,就必须在使用Blinn-Phong模型时将镜面反光度设置更高一点。通常我们会选择冯氏着色时反光度分量的2到4倍。
我这里因为用了Smoothness参数所以按照常规的 2到4倍并不准确,可以自己去掉Smoothness的计算,尝试一下。
*/

Shader "Lakehani/URP/Lighting/BlinnPhongSpecular"
{
    Properties
    {
        _SpecularColor ("SpecularGloss", Color) = (1,1,1,1)
        _Smoothness ("Smoothness", Range(0,1)) = 0.5
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalRenderPipeline" "Queue"="Geometry"}

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
      
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float3 normalOS : NORMAL;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float3 normalWS : TEXCOORD0;
                float3 viewWS : TEXCOORD1;
            };

            CBUFFER_START(UnityPerMaterial)
            half4 _SpecularColor;
            half _Smoothness;
            CBUFFER_END



            Varyings vert(Attributes IN)
            {
                Varyings OUT;

                float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
                OUT.positionHCS = TransformWorldToHClip(positionWS);
                OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS);
                OUT.viewWS = GetWorldSpaceViewDir(positionWS);
                return OUT;
            }

            // half3 LightingSpecular(half3 lightColor, half3 lightDir, half3 normal, half3 viewDir, half4 specular, half smoothness)
            // {
            //     // 计算出光源方向和视角方向的一半向量(halfVec),并使用SafeNormalize函数对其进行归一化处理。
            //     float3 halfVec = SafeNormalize(float3(lightDir) + float3(viewDir));
            //     // 函数计算出表面法线和一半向量之间的点积(NdotH),并使用saturate函数将结果限制在0到1之间。
            //     half NdotH = half(saturate(dot(normal, halfVec)));
            //     // 使用pow函数将NdotH的值按照光滑度进行幂运算,得到一个修饰因子(modifier)
            //     half modifier = pow(NdotH, smoothness);
            //     // 将镜面反射颜色(specular.rgb)与修饰因子相乘,得到最终的镜面反射光照强度(specularReflection)。
            //     // 然后,将光源颜色(lightColor)与镜面反射光照强度相乘,得到最终的镜面反射光照颜色。
            //     half3 specularReflection = specular.rgb * modifier;
            //     return lightColor * specularReflection;
            // }

            half4 frag(Varyings IN) : SV_Target
            {
                Light light = GetMainLight();
                // 光源颜色乘以光源的距离衰减因子,得到光源的最终颜色(lightColor)
                half3 lightColor = light.color * light.distanceAttenuation;
                // 通过使用exp2函数将光滑度(_Smoothness)进行指数运算,得到一个平滑度因子(smoothness)
                half smoothness = exp2(10 * _Smoothness + 1);
                // 通过调用LightingSpecular函数计算出镜面反射颜色(specularColor)
                half3 specularColor = LightingSpecular(lightColor, light.direction, normalize(IN.normalWS), SafeNormalize(IN.viewWS), _SpecularColor, smoothness);
                float4 totalColor = float4(specularColor.rgb,1);
                return totalColor;
            }

            ENDHLSL
        }
    }
}

Anisotropic (Hair)

“Anisotropic Lighting”(各向异性照明)是指当一个表面围绕其几何法线旋转时,其外观会发生变化的现象。各向异性照明模型用于模拟具有各向异性特性的表面的光照效果。

各向异性照明模型考虑了表面的微观结构或纹理,这些结构或纹理会导致光线在不同方向上的反射和散射行为不同。这种模型可以模拟一些日常物体的照明行为,如头发、绒面圣诞树装饰品、刷状合金车轮、CD、鼓套中的钹和黑胶唱片等。

在各向异性照明模型中,光线的反射和散射行为会根据表面的微观结构或纹理的方向而发生变化。这种模型可以通过调整参数来控制各向异性的强度和方向,以实现更真实的照明效果。

各向异性照明模型在计算机图形学中被广泛应用,特别是在渲染和着色器(Shader)中。它可以增加渲染物体的真实感和细节,使得具有各向异性特性的表面看起来更加逼真。

BumpMap

ParallaxMapping

ParallaxMapping Self-Shadowing

Ambient

Reflection Probe

Refraction

Light Probe

Lightmap

Shadow

Shadow

Phong

Blinn-Phong

Multi-Lighting

PBR Common

PBR In Unity URP

MatCap