32.高级纹理-立方体纹理-菲涅尔反射-菲涅尔反射基础实现
32.1 知识点
菲涅耳近似等式
Schlick 菲涅耳近似等式:
[ R(θ) = R_0 + (1−R_0) (1−V·N)^5 ]
- R(θ): 对应角度的菲涅耳反射率
- R₀: 介质反射率
- V: 视角单位向量
- N: 法线单位向量
使用该等式可以计算菲涅耳反射率,并将其用于颜色计算。
菲涅耳反射的基础实现
主要步骤
新建 Shader
复制Lesson26_ReflectBase
的代码,基于其进行修改。属性声明
将反射率变量修改为_FresnelScale
,表示菲涅耳中介质的反射率。结构体调整
使用 Schlick 菲涅耳近似等式,需要世界空间下的视角方向和法线向量,在结构体中加入这两个变量。顶点着色器
在顶点着色器中为结构体变量赋值。片元着色器
利用 Schlick 菲涅耳近似等式计算菲涅耳反射率,并参与最终颜色计算。
新建Shader,取名Lesson32_FresnelBase,复制Lesson26_ReflectBase中代码,在其基础上进行修改
Shader "Unlit/Lesson32_FresnelBase"
{
Properties
{
//立方体纹理
_Cube("Cubemap", Cube) = ""{}
//反射率
_Reflectivity("Reflectivity", Range(0,1)) = 1
}
SubShader
{
Tags
{
"RenderType"="Opaque" // 设置渲染类型为不透明(Opaque)
"Queue"="Geometry" // 设置渲染队列为Geometry,通常用于普通几何物体
}
Pass
{
// 设置LightMode标签,指定光照模式为ForwardBase
Tags
{
"LightMode"="ForwardBase" // 指定使用正向渲染中的基本光照模式
}
CGPROGRAM
#pragma vertex vert // 指定顶点着色器函数为vert
#pragma fragment frag // 指定片元着色器函数为frag
// 包含Unity内置的CG库和光照相关的库
#include "UnityCG.cginc"
#include "Lighting.cginc"
// 立方体纹理属性
samplerCUBE _Cube;
// 反射率属性
float _Reflectivity;
struct v2f
{
float4 pos:SV_POSITION; //裁剪空间下的顶点坐标
//世界空间下的反射向量
//我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
float3 worldRefl:TEXCOORD0;
};
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//计算反射光向量
//1.计算世界空间下法线向量
float3 worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//2.世界空间下的顶点坐标
fixed3 worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//3.计算视角方向 内部是用摄像机位置 - 世界坐标位置 计算反射向量时要取反
fixed3 worldViewDir = UnityWorldSpaceViewDir(worldPos);
//4.计算反射向量
v2f.worldRefl = reflect(-worldViewDir, worldNormal);
return v2f;
}
fixed4 frag(v2f v2f):SV_TARGET
{
//对立方体纹理利用对应的反射向量进行采样
fixed4 cubemapColor = texCUBE(_Cube, v2f.worldRefl);
//用采样颜色 * 反射率 决定最终的颜色效果
return cubemapColor * _Reflectivity;
}
ENDCG
}
}
}
修改属性声明及其映射,将反射率变量修改为 _FresnelScale 菲涅耳中介质的反射率
Properties
{
//立方体纹理
_Cube("Cubemap", Cube) = ""{}
//菲涅耳反射中 对应介质的反射率
_FresnelScale("FresnelScale", Range(0,1)) = 1
}
samplerCUBE _Cube;
float _FresnelScale;
修改结构体v2f,由于使用Schlick菲涅耳近似等式,需要用到世界空间下视角方向、和法线向量,因此在结构体中加入这两个变量
struct v2f
{
//裁剪空间下的顶点坐标
float4 pos:SV_POSITION;
//世界空间下的法线
float3 worldNormal:NORMAL;
//世界空间下视角的方向
float3 worldViewDir:TEXCOORD0;
//世界空间下的反射向量
//我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
float3 worldRefl:TEXCOORD1;
};
顶点函数中,把临时的世界空间下视角方向和法线向量改成赋值给v2f
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//计算反射光向量
//1.计算世界空间下法线向量
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//2.世界空间下的顶点坐标
fixed3 worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//3.计算视角方向 内部是用摄像机位置 - 世界坐标位置
v2f.worldViewDir = UnityWorldSpaceViewDir(worldPos);
//4.计算反射向量
v2f.worldRefl = reflect(-v2f.worldViewDir, v2f.worldNormal);
return v2f;
}
片元函数中,利用Schlick菲涅耳近似等式计算出菲涅耳反射率参与最终颜色计算
fixed4 frag(v2f v2f):SV_TARGET
{
//对立方体纹理利用对应的反射向量进行采样
fixed4 cubemapColor = texCUBE(_Cube, v2f.worldRefl);
//利用schlick菲涅耳近似等式 计算菲涅耳反射率
// R(θ) = R0 + ( 1− R0 )( 1 − V·𝑵)^𝟓
// R(θ):对应角度的菲涅耳反射率
// R0:介质反射率
// V:视角单位向量
// N:法线单位向量
fixed fresnel = _FresnelScale + (1-_FresnelScale) * pow(1-dot(normalize(v2f.worldViewDir), normalize(v2f.worldNormal)), 5);
//用采样颜色 * 反射率 决定最终的颜色效果
return cubemapColor * fresnel;
}
创建材质复制shader查看效果,这样直接看,和反射的区别不明显,要下一节结合了漫反射和高光反射后更明显。把反射率都改成0之后可以看到有些细微的差别
32.2 知识点代码
Lesson32_高级纹理_立方体纹理_菲涅尔反射_菲涅尔反射基础实现.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson32_高级纹理_立方体纹理_菲涅尔反射_菲涅尔反射基础实现 : MonoBehaviour
{
void Start()
{
#region 知识点一 菲涅耳近似等式
//Schlick 菲涅耳近似等式
// R(θ) = R0 + ( 1− R0 )( 1 − V·𝑵)^𝟓
// R(θ):对应角度的菲涅耳反射率
// R0:介质反射率
// V:视角单位向量
// N:法线单位向量
//使用该等式计算菲涅耳反射率,参与颜色计算
#endregion
#region 知识点二 菲涅尔反射的基础实现
//1.新建Shader 复制Lesson26_ReflectBase中代码
// 在其基础上进行修改
//2.属性声明
// 将反射率变量修改为 _FresnelScale 菲涅耳中介质的反射率
//3.结构体
// 由于使用Schlick菲涅耳近似等式
// 需要用到世界空间下视角方向、和法线向量
// 因此在结构体中加入这两个变量
//4.顶点着色器
// 关键步骤:
// 结构体中变量赋值
//5.片元着色器
// 关键步骤:
// 利用Schlick菲涅耳近似等式计算出菲涅耳反射率 参与最终颜色计算
#endregion
}
}
Lesson32_FresnelBase.shader
Shader "Unlit/Lesson32_FresnelBase"
{
Properties
{
//立方体纹理
_Cube("Cubemap", Cube) = ""{}
//菲涅耳反射中 对应介质的反射率
_FresnelScale("FresnelScale", Range(0,1)) = 1
}
SubShader
{
Tags{"RenderType"="Opaque" "Queue"="Geometry"}
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
samplerCUBE _Cube;
float _FresnelScale;
struct v2f
{
//裁剪空间下的顶点坐标
float4 pos:SV_POSITION;
//世界空间下的法线
float3 worldNormal:NORMAL;
//世界空间下视角的方向
float3 worldViewDir:TEXCOORD0;
//世界空间下的反射向量
//我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
float3 worldRefl:TEXCOORD1;
};
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//计算反射光向量
//1.计算世界空间下法线向量
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//2.世界空间下的顶点坐标
fixed3 worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//3.计算视角方向 内部是用摄像机位置 - 世界坐标位置
v2f.worldViewDir = UnityWorldSpaceViewDir(worldPos);
//4.计算反射向量
v2f.worldRefl = reflect(-v2f.worldViewDir, v2f.worldNormal);
return v2f;
}
fixed4 frag(v2f v2f):SV_TARGET
{
//对立方体纹理利用对应的反射向量进行采样
fixed4 cubemapColor = texCUBE(_Cube, v2f.worldRefl);
//利用schlick菲涅耳近似等式 计算菲涅耳反射率
// R(θ) = R0 + ( 1− R0 )( 1 − V·𝑵)^𝟓
// R(θ):对应角度的菲涅耳反射率
// R0:介质反射率
// V:视角单位向量
// N:法线单位向量
fixed fresnel = _FresnelScale + (1-_FresnelScale) * pow(1-dot(normalize(v2f.worldViewDir), normalize(v2f.worldNormal)), 5);
//用采样颜色 * 反射率 决定最终的颜色效果
return cubemapColor * fresnel;
}
ENDCG
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com