Unity URP Shader 基础光照

https://zhuanlan.zhihu.com/p/232193169https://www.jianshu.com/p/5641bfe91458

兰伯特(Lambert)定律
兰伯特定律是一种描述CG当中的经验性漫反射定律,它是这样说的。

漫反射光的强度近似地服从于Lambert定律,即漫反射光的光强仅与入射光的方向和反射点处表面法向夹角的余弦成正比。

这段话中可以拆分为几个点来看。
第一,近似的服从,也就是并不完全服从,主要说明它是一个经验模型。而非基于物理的渲染(PBR)
第二,入射光的方向,即我们需要入射光的角度,(当然还需要强度)
第三,反射点出的表面法线,我们需要顶点的法线
第四,与余弦成正比,我们需要计算入射光与顶点法线的余弦值,以计算光在该点的强度。

根据上面四个点,我们来简单梳理一下这个公式。首先,入射光的方向与反射点处的表面法线夹角的余弦成正比。我们就需要计算两个方向的余弦值,且不说这两个方向是什么,我们先看一下怎么计算两个方向的余弦值。

假设我们有A和B两个向量,我们可以得到以下关系。其中θ为A和B的夹角。\(\begin{aligned} \vec{A} \times \vec{B} & =|\vec{A}| \times|\vec{B}| \times \cos \theta \\ \frac{\vec{A} \times \vec{B}}{|\vec{A}| \times|\vec{B}|} & =\cos \theta \end{aligned}\)

这样,我们计算出了A向量和B向量的夹角值。

\(\cos \theta=A_{0} \times B_{0}\)
其中\(A_{0}、B_{0}\)表示\(A、B\)的单位向量

又由于,在夹角超过90°的地方,余弦值会变成负数,而这显然不符合光强度大于等于0的现实问题,所以我们给它加上一个限度。因而漫反射可以表示为下面这样。

\(\text { diffuse }=\max (0, L_{0} \times N_{0}) \times I\)
其中\(L_{0}\)为光照方向,而\(N_{0}\)为法线方向,\(I\)为光照强度。

函数 GetMainLight() 返回一个数据结构 Light

struct Light
{
    half3   direction; // 方向
    half3   color; // 颜色&强度
    half    distanceAttenuation; // 距离衰减
    half    shadowAttenuation; // 阴影衰减
};
Shader "Example/StandardLight" {
    Properties {
        _BaseColor("Base Color", Color)=(1, 1, 1, 1)
    }

    SubShader {
        Tags {
            "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline"
        }

        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"

            CBUFFER_START(UnityPerMaterial)

            half4 _BaseColor;
            CBUFFER_END

            struct Attributes
            {
                float4 positionOS : POSITION;
                half3 normal:NORMAL;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                half3 worldNormal:TEXCOORD0;
            };

            Varyings vert(Attributes input)
            {
                Varyings output;
                // 从对象空间到裁剪空间
                output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
                output.worldNormal = TransformObjectToWorldNormal(input.normal);
                return output;
            }

            half4 frag(Varyings input): SV_Target
            {
                Light mainLight = GetMainLight();
                // saturate(value) 相当于 Mathf.Clamp01(value) 函数,限制值在 [0,1] 区间
                float power = saturate(dot(mainLight.direction, input.worldNormal)); // 计算漫反射的强度
                half4 output = _BaseColor * power * half4(mainLight.color, 1); //将表面颜色,漫反射强度和光源强度混合。
                return output;
            }
            ENDHLSL
        }
    }
}

猜你喜欢

转载自blog.csdn.net/kingBook928/article/details/129253469