兰伯特光照模型shader

TMaize 于 2024-10-08 发布

Lambert光照模型的Unity Shader

简介

这篇博客是笔者在上一篇easy shader的基础上,跟着B站up主庄懂写的第二篇非常基础的shader代码——Lambert光照模型。Lambert光照模式是一个非常简单的漫反射光照模型,它假设光线从表面反射的强度与光线和表面法线之间的夹角有关。作者在此记录该shader的代码,了解并且加深记忆该shader的工作原理。

管线

管线

shader代码

Shader "shader/lambert" {
    Properties {
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "FORWARD"
            Tags {
                "LightMode"="ForwardBase"
            }
            
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "UnityPBSLighting.cginc"
            #include "UnityStandardBRDF.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma target 3.0
            // 输入结构
            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            // 输出结构
            struct VertexOutput {
                float4 pos : SV_POSITION;     //由模型顶点信息换算而来的顶点屏幕位置
                float3 normalDir : TEXCOORD;  //由模型法线信息换算而来的世界空间法线信息
            };
             // 输入结构>>>顶点Shader>>>输出结构
            VertexOutput vert (VertexInput v) {
                VertexOutput o = (VertexOutput)0;                               // 新建一个输出结构
                o.normalDir = UnityObjectToWorldNormal(v.normal);               // 变换法线信息 并将其塞给输出结构  
                o.pos = UnityObjectToClipPos( v.vertex );                       // 变换顶点信息 并将其塞给输出结构
                return o;                                                       // 变换法线信息 并将其塞给输出结构
            }
             // 输出结构>>>像素
            float4 frag(VertexOutput i) : COLOR {
                float3 normaldir=i.normalDir;                                  // 获取ndir
                float3 lightdirection=normalize(_WorldSpaceLightPos0.xyz);     // 获取ldir
                float ndotl=dot(normaldir,lightdirection);                     //ndir点乘ldir
                float lamber = max(0.0,normaldir);                             //截断负值
                return float4(lamber,lamber,lamber,1);                         //输出最终颜色
                return float4(1.0,0.5,0.5,1.0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

代码解析

1.顶点着色器:vert

顶点着色器负责接收每个顶点的位置信息和发现信息,并将这些数据转换为屏幕坐标和世界坐标系下的法线方向。

·unityobjectToworldNormal:物体法线从对象空间转换到世界空间。

·unityobjectToclipPos:将物体的顶点位置转换为裁剪空间的坐标,使其能够在屏幕上正确显示。

o.normalDir = UnityObjectToWorldNormal(v.normal);   // 转换法线方向
o.pos = UnityObjectToClipPos(v.vertex);            // 转换顶点位置

2.像素着色器frag

像素着色器通过计算法线和光源方向的夹角来确定像素的最终颜色。

·法线方向:法线方向通过normalize(i,normaldir)函数进行归一化,确保方向向量的长度为1。

·光源方向:使用_worldspaceLightpos0.xyz获取世界光源位置,并将其归一化单位向量。

·点积:使用dot(normaldir,lightdirection)计算法线方向和光源方向的点积,它反应了光源和表面之间的夹角角度。

点积

·截断负值:max(0.0),ndotl)保证了光照模型不会是负值,使得中间成过渡色,当法线方向和光源方向的点积为负时,即表面背对光源时,使其值为0,零×任何数都等于0。

·返回颜色:Lambert光照模型的输出是灰度值,最终会返回一个灰度值的颜色。

float ndotl = max(0.0, dot(normaldir, lightdirection)); // 计算光照强度
return float4(ndotl, ndotl, ndotl, 1);                 // 返回灰度值颜色

总结

通过这次学习,笔者理解到了lambert光照模型,它简单有效地模拟了光线照射在表面时的漫反射效果。