11.光照模型-高光反射光照模型-Phong式高光反射模型-逐片元光照
11.1 知识点
利用Phong式高光反射光照模型实现光照效果(逐片元光照)
关键步骤
- 基本和逐顶点一致
- 区别:
- 在顶点着色器中计算顶点和法线相关数据
- 在片元着色器中计算Phong式高光反射光照
创建着色器
我们将首先创建一个基本的Phong光照模型着色器,并删除所有默认的无用代码,保留最基础的顶点和片元着色器的骨架:
Shader "Unlit/Lesson11_Phong_Specular_Pixel"
{
Properties
{
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
v2f vert(appdata appdata)
{
}
fixed4 frag(v2f v2f) : SV_Target
{
}
ENDCG
}
}
}
声明颜色和光泽度属性,设置Tags
我们接下来添加高光反射的颜色和光泽度属性,并为渲染模式设置Tags。注意Tags可以放置在Pass内,这样可以避免对多个Pass产生影响。此外,引用必要的内置文件以便后续使用。
Shader "Unlit/Lesson11_Phong_Specular_Pixel"
{
Properties
{
// 高光反射颜色
_SpecularColor("SpecularColor", Color) = (1,1,1,1)
// 光泽度
_SpecularNum("SpecularNum", Range(0, 20)) = 0.5
}
SubShader
{
Pass
{
// 如果有多个Pass渲染通道时,Tags应放到对应的Pass中,避免影响其他Pass
// 设置光照模式为 ForwardBase,主要用于处理不透明物体的光照渲染
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// 引用内置文件,主要用于内置结构体和变量的使用
#include "UnityCG.cginc"
#include "Lighting.cginc"
// 对应属性中的颜色和光泽度
fixed4 _SpecularColor;
float _SpecularNum;
v2f vert(appdata_base appdata_base)
{
}
fixed4 frag(v2f v2f) : SV_Target
{
}
ENDCG
}
}
}
声明结构体,传递顶点数据
声明顶点传递给片元的结构体。该结构体需要包含裁剪空间中的顶点坐标、世界空间的法线和世界空间的顶点坐标,这样片元函数才能正确地进行光照计算。
// 顶点着色器传递给片元着色器的内容
struct v2f
{
// 裁剪空间下的顶点坐标信息
float4 pos:SV_POSITION;
// 世界空间下的法线信息
float3 wNormal:NORMAL;
// 世界空间下的顶点坐标
float3 wPos:TEXCOORD0;
};
顶点着色器中进行空间转换
在顶点着色器中,我们计算裁剪空间的顶点坐标、世界空间的法线和世界空间的顶点坐标。这些信息将传递给片元着色器进行进一步的计算。
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//1.顶点裁剪空间变换 把模型空间下的法线从变成裁剪空间下的法线信息
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//2.进行法线空间变换 把模型空间下的法线从变成世界空间下的法线信息
v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
//3.顶点转到世界空间 把模型空间下的顶点变成世界空间下的顶点坐标
//以下是三种把模型空间坐标转换成世界空间坐标的方法 但是有些可能已经过时了
//data.wPos = mul(UNITY_MATRIX_M, appdata_base.vertex).xyz;
//data.wPos = mul(_Object2World, appdata_base.vertex).xyz;
v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
return v2f;
}
在片元着色器中进行Phong光照计算
在片元着色器中,我们使用Phong光照模型进行计算,包括视角向量、反射向量和最终的高光反射计算。
fixed4 frag(v2f v2f) : SV_Target
{
//1.视角单位向量
//用观察者的位置(摄像机的位置)减去世界空间下顶点坐标 得到的就是视角方向 并且进行单位化
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
//2.光的反射单位向量
//光的方向 进行单位化
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
//光反射光线的单位向量 reflect(入射向量,顶点法向量) 返回反射向量
float3 reflectDir = reflect(-lightDir, v2f.wNormal);
//3.套公式计算
//color = 光源颜色 * 材质高光反射颜色 * pow( max(0, dot(视角单位向量, 光的反射单位向量)), 光泽度 )
fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, reflectDir)), _SpecularNum);
return fixed4(color.rgb, 1);
}
比较逐顶点与逐片元实现
与逐顶点光照模型相比,逐片元光照模型能够带来更加平滑的光照过渡。顶点光照模型由于使用插值运算,会出现明显的锯齿状效果,而逐片元光照则能避免这种现象。
11.2 知识点代码
Lesson11_光照模型_高光反射光照模型_Phong式高光反射模型_逐片元光照
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson11_光照模型_高光反射光照模型_Phong式高光反射模型_逐片元光照 : MonoBehaviour
{
void Start()
{
#region 知识点 利用Phong式高光反射光照模型实现光照效果(逐片元光照)
//关键步骤
//基本和逐顶点一致
//区别:
//1.在顶点着色器中计算顶点和法线相关数据
//2.在片元着色器中计算Phong式高光反射光照
#endregion
}
}
Lesson11_Phong_Specular_Pixel.shader
Shader "Unlit/Lesson11_Phong_Specular_Pixel"
{
Properties
{
//高光反射颜色
_SpecularColor("SpecularColor", Color) = (1,1,1,1)
//光泽度
_SpecularNum("SpecularNum", Range(0, 20)) = 0.5
}
SubShader
{
Pass
{
//如果有多个Pass渲染通道时 一般情况下会把光照模式的 Tags放到对应的Pass中 以免影响其他Pass
//设置我们的光照模式 ForwardBase这种向前渲染模式 主要是用来处理 不透明物体的 光照渲染的
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//引用对应的内置文件
//主要是为了之后 的 比如内置结构体使用,内置变量使用
#include "UnityCG.cginc"
#include "Lighting.cginc"
//对应属性当中的颜色和光泽度
fixed4 _SpecularColor;
float _SpecularNum;
//顶点着色器传递给片元着色器的内容
struct v2f
{
//裁剪空间下的顶点坐标信息
float4 pos:SV_POSITION;
//世界空间下的 法线信息
float3 wNormal:NORMAL;
//世界空间下的 顶点坐标
float3 wPos:TEXCOORD0;
};
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//1.顶点裁剪空间变换 把模型空间下的法线从变成裁剪空间下的法线信息
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//2.进行法线空间变换 把模型空间下的法线从变成世界空间下的法线信息
v2f.wNormal = UnityObjectToWorldNormal(appdata_base.normal);
//3.顶点转到世界空间 把模型空间下的顶点变成世界空间下的顶点坐标
//以下是三种把模型空间坐标转换成世界空间坐标的方法 但是有些可能已经过时了
//data.wPos = mul(UNITY_MATRIX_M, appdata_base.vertex).xyz;
//data.wPos = mul(_Object2World, appdata_base.vertex).xyz;
v2f.wPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
return v2f;
}
fixed4 frag(v2f v2f) : SV_Target
{
//1.视角单位向量
//用观察者的位置(摄像机的位置)减去世界空间下顶点坐标 得到的就是视角方向 并且进行单位化
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v2f.wPos);
//2.光的反射单位向量
//光的方向 进行单位化
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
//光反射光线的单位向量 reflect(入射向量,顶点法向量) 返回反射向量
float3 reflectDir = reflect(-lightDir, v2f.wNormal);
//3.套公式计算
//color = 光源颜色 * 材质高光反射颜色 * pow( max(0, dot(视角单位向量, 光的反射单位向量)), 光泽度 )
fixed3 color = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0, dot(viewDir, reflectDir)), _SpecularNum);
return fixed4(color.rgb, 1);
}
ENDCG
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com