4.光照模型-漫反射光照模型-兰伯特光照模型-逐顶点光照
4.1 知识点
关键知识回顾
在实现光照效果时,基于兰伯特光照模型的计算公式如下:
漫反射光照颜色 = 光源的颜色 * 材质的漫反射颜色 * max(0, 标准化后物体表面法线向量 · 标准化后光源方向向量)
常用变量和方法:
- 光源的颜色:使用
Lighting.cginc
内置文件中的_LightColor0
表示。 - 光源的方向:
_WorldSpaceLightPos0
表示光源0在世界坐标系下的位置。 - 向量归一化方法:
normalize
。 - 取最大值方法:
max
。 - 点乘方法:
dot
。 - 环境光变量:
UNITY_LIGHTMODEL_AMBIENT.rgb
,用于模拟环境光对物体的影响,避免物体阴影部分完全黑暗。 - 将法线从模型空间转换到世界空间:使用
UnityObjectToWorldNormal
方法。
利用兰伯特光照模型实现光照效果(逐顶点光照)
创建顶点片元着色器,删除默认代码,保留基本骨架:
Shader "Unlit/Lesson04_Lambert_Vertex"
{
Properties {}
SubShader
{
Tags {}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
v2f vert(appdata v)
{
}
fixed4 frag(v2f i) : SV_Target
{
}
ENDCG
}
}
}
材质漫反射颜色属性声明
Properties
{
//材质的漫反射光照颜色
_MainColor("MainColor", Color) = (1,1,1,1)
}
Pass语句块声明CG语法材质漫反射颜色,形成对应
// 材质的漫反射颜色
fixed4 _MainColor;
渲染标签Tags设置 将LightMode光照模式 设置为ForwardBase向前渲染(通常用于不透明物体的基本渲染)
//设置我们的光照模式 ForwardBase这种向前渲染模式 主要是用来处理 不透明物体的 光照渲染的
Tags { "LightMode"="ForwardBase" }
引用内置文件UnityCG.cginc和Lighting.cginc
//引用对应的内置文件
//主要是为了之后 的 比如内置结构体使用,内置变量使用
#include "UnityCG.cginc"
#include "Lighting.cginc"
v2f结构体声明,定义顶点着色器传递给片元着色器的数据
//顶点着色器传递给片元着色器的内容
struct v2f
{
//裁剪空间下的顶点坐标信息
float4 pos:SV_POSITION;
//对应顶点的漫反射光照颜色
fixed3 color:COLOR;
};
基于公式在顶点着色器函数实现主要逻辑
//逐顶点光照 所以相关的漫反射光照颜色的计算 需要写在顶点着色器 回调函数中
v2f vert(appdata_base appdata_base)
{
//声明v2f 要传递给片元函数的
v2f v2f;
//UnityObjectToClipPos 把模型空间下的顶点转换到裁剪空间下
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//appdata_base.normal 获取到在模型空间下的法线
//UnityObjectToWorldNormal 将法线从模型空间转换成世界空间 获取到相对于世界坐标系下的法线信息
float3 normal = UnityObjectToWorldNormal(appdata_base.normal);
//_WorldSpaceLightPos0.xyz 世界坐标系下光源方向 _WorldSpaceLightPos0是四维向量 只需要xyz即可
//normalize 把向量归一化
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
//有了相关的参数 就可以用公式来进行计算了
//_LightColor0光 照颜色
//.rgb 代表只使用颜色进行计算 透明度不考虑
fixed3 color = _LightColor0.rgb * _MainColor.rgb * max(0, dot(normal, lightDir));
//记录颜色 传递给片元着色器
v2f.color = color;
return v2f;
}
在片元着色器中返回计算好的光照颜色
fixed4 frag(v2f v2f) : SV_Target
{
//把计算好的兰伯特光照的颜色 传递出去就可以了
//返回v2f中的颜色 透明度传1
return fixed4(v2f.color.rgb, 1);
}
创建材质赋值我们的shader,再把材质赋值到一个球体上,调整光源角度,位置,强度,可以看到球体会收到环境光的位置的改变效果,同时阴影部分都是黑的
为了阴影出不全黑,需要加上兰伯特环境光颜色公共变量,更加自然,同时可以看到因为逐顶点光照使用插值运算,效果会差一点
// //记录颜色 传递给片元着色器 但是阴影部分全黑 效果不好
// v2f.color = color;
//UNITY_LIGHTMODEL_AMBIENT.rgb 兰伯特光照模型环境光变量
//加上兰伯特光照模型环境光变量的目的 是希望阴影处不要全黑 不然看起来有一些不自然
//目的就是为了让表现效果更接近于真实世界 所以需要加上它
v2f.color = UNITY_LIGHTMODEL_AMBIENT.rgb + color;
加上环境光变量后,阴影区域将会带有一定的亮度,使效果更接近现实世界,同时避免阴影部分全黑的现象。
总结
通过逐顶点光照模型,我们可以模拟物体在光源影响下的光照效果。利用兰伯特光照模型结合环境光,使得场景中物体的光照效果更加自然。
4.2 知识点代码
Lesson04_光照模型_漫反射光照模型_兰伯特光照模型_逐顶点光照
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson04_光照模型_漫反射光照模型_兰伯特光照模型_逐顶点光照 : MonoBehaviour
{
void Start()
{
#region 关键知识回顾
//公式:
//漫反射光照颜色 = 光源的颜色 * 材质的漫反射颜色 * max(0, 标准化后物体表面法线向量· 标准化后光源方向向量)
//常用变量
//1.光源的颜色
// Lighting.cginc 内置文件中的 _LightColor0
//2.光源的方向
// _WorldSpaceLightPos0 表示光源0在世界坐标系下的位置
//3.向量归一化方法
// normalize
//4.取最大值方法
// max
//5.点乘方法
// dot
//6.兰伯特光照模型环境光变量(用于模拟环境光对物体的影响,避免物体阴影部分完全黑暗)
// UNITY_LIGHTMODEL_AMBIENT.rgb
//7.将法线从模型空间转换到世界空间
// UnityObjectToWorldNormal
#endregion
#region 知识点 利用兰伯特光照模型实现光照效果(逐顶点光照)
//关键步骤
//1.材质漫反射颜色属性声明
//2.渲染标签Tags设置 将LightMode光照模式 设置为ForwardBase向前渲染(通常用于不透明物体的基本渲染)
//3.引用内置文件UnityCG.cginc和Lighting.cginc
//4.结构体声明
//5.基于公式在顶点着色器函数实现主要逻辑
//
//注意:为了阴影出不全黑,需要加上兰伯特环境光颜色公共变量
#endregion
}
}
Lesson04_Lambert_Vertex.shader
Shader "Unlit/Lesson04_Lambert_Vertex"
{
Properties
{
//材质的漫反射光照颜色
_MainColor("MainColor", Color) = (1,1,1,1)
}
SubShader
{
//设置我们的光照模式 ForwardBase这种向前渲染模式 主要是用来处理 不透明物体的 光照渲染的
Tags
{
"LightMode"="ForwardBase"
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//引用对应的内置文件
//主要是为了之后 的 比如内置结构体使用,内置变量使用
#include "UnityCG.cginc"
#include "Lighting.cginc"
//材质的漫反射颜色
fixed4 _MainColor;
//顶点着色器传递给片元着色器的内容
struct v2f
{
//裁剪空间下的顶点坐标信息
float4 pos:SV_POSITION;
//对应顶点的漫反射光照颜色
fixed3 color:COLOR;
};
//逐顶点光照 所以相关的漫反射光照颜色的计算 需要写在顶点着色器 回调函数中
v2f vert(appdata_base appdata_base)
{
//声明v2f 要传递给片元函数的
v2f v2f;
//UnityObjectToClipPos 把模型空间下的顶点转换到裁剪空间下
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//appdata_base.normal 获取到在模型空间下的法线
//UnityObjectToWorldNormal 将法线从模型空间转换成世界空间 获取到相对于世界坐标系下的法线信息
float3 normal = UnityObjectToWorldNormal(appdata_base.normal);
//_WorldSpaceLightPos0.xyz 世界坐标系下光源方向 _WorldSpaceLightPos0是四维向量 只需要xyz即可
//normalize 把向量归一化
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
//有了相关的参数 就可以用公式来进行计算了
//_LightColor0光 照颜色
//.rgb 代表只使用颜色进行计算 透明度不考虑
fixed3 color = _LightColor0.rgb * _MainColor.rgb * max(0, dot(normal, lightDir));
// //记录颜色 传递给片元着色器 但是阴影部分全黑 效果不好
// v2f.color = color;
//UNITY_LIGHTMODEL_AMBIENT.rgb 兰伯特光照模型环境光变量
//加上兰伯特光照模型环境光变量的目的 是希望阴影处不要全黑 不然看起来有一些不自然
//目的就是为了让表现效果更接近于真实世界 所以需要加上它
v2f.color = UNITY_LIGHTMODEL_AMBIENT.rgb + color;
return v2f;
}
fixed4 frag(v2f v2f) : SV_Target
{
//把计算好的兰伯特光照的颜色 传递出去就可以了
//返回v2f中的颜色 透明度传1
return fixed4(v2f.color.rgb, 1);
}
ENDCG
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com