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光照模型,它简单有效地模拟了光线照射在表面时的漫反射效果。