33.高级纹理-立方体纹理-菲涅尔反射-菲涅尔反射结合漫反射及阴影
33.1 知识点
菲涅尔反射结合漫反射
主要步骤
- 菲涅尔反射是基于反射的,因此在之前实现的反射结合漫反射的基础上进行修改。
- 新建 Shader
Lesson33_FresnelDiffuseShadow
,复制Lesson27_ReflectDiffuseShadow
。 - 修改属性:
- 去掉反射颜色。
- 修改反射率为菲涅耳相关的 R0 反射率。
- 修改
v2f
结构体,加入视角方向(需要在片元着色器中使用菲涅耳近似公式进行计算)。 - 修改顶点着色器:
- 在顶点着色器中修改视角方向临时变量。
- 修改片元着色器:
- 计算菲涅尔反射率。
- 用菲涅尔反射率参与
lerp
计算。
新建 Shader Lesson33_FresnelDiffuseShadow
,复制Lesson27_ReflectDiffuseShadow
Shader "Unlit/Lesson33_FresnelDiffuseShadow"
{
Properties
{
//漫反射颜色
_Color("Color", Color) = (1,1,1,1)
//反射颜色
_ReflectColor("ReflectColor", Color) = (1,1,1,1)
//立方体纹理
_Cube("Cubemap", Cube) = ""{}
//反射率
_Reflectivity("Reflectivity", Range(0,1)) = 1
}
SubShader
{
Tags
{
"RenderType"="Opaque" "Queue"="Geometry"
}
Pass
{
Tags
{
"LightMode"="ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//Shader变体
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
//灯光内置文件
#include "AutoLight.cginc"
fixed4 _Color;
fixed4 _ReflectColor;
samplerCUBE _Cube;
float _Reflectivity;
struct v2f
{
float4 pos:SV_POSITION; //裁剪空间下的顶点坐标
//世界空间下法线
fixed3 worldNormal:NORMAL;
//世界空间下的顶点位置
float3 worldPos:TEXCOORD0;
//世界空间下的反射向量
//我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
float3 worldRefl:TEXCOORD1;
//阴影相关
SHADOW_COORDS(2)
};
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//计算反射光向量
//1.计算世界空间下法线向量
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//2.世界空间下的顶点坐标
v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//3.计算视角方向 内部是用摄像机位置 - 世界坐标位置
fixed3 worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
//4.计算反射向量
v2f.worldRefl = reflect(-worldViewDir, v2f.worldNormal);
//阴影相关处理(接受阴影)
TRANSFER_SHADOW(v2f);
return v2f;
}
fixed4 frag(v2f v2f):SV_TARGET
{
//漫反射光照相关计算
//传入世界空间顶点 得到世界空间下光的方向并单位化
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
//漫反射颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));
//对立方体纹理利用对应的反射向量进行采样
fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefl).rgb * _ReflectColor.rgb;
//得到光照衰减以及阴影相关的衰减值
UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);
//利用lerp 在漫反射颜色和反射颜色之间 进行插值
//_Reflectivity为0或1就是极限状态
//_Reflectivity=0 没有反射效果
//_Reflectivity=1只有反射效果
//_Reflectivity在0~1之间就是两者叠加
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, _Reflectivity) * atten;
//用采样颜色 * 反射率 决定最终的颜色效果
return fixed4(color, 1.0);
}
ENDCG
}
}
//内置的阴影投射的Pass
FallBack "Reflective/VertexLit"
}
修改属性,去掉反射颜色,修改反射率命名为菲涅耳相关反射率以及相关映射
Properties
{
//漫反射颜色
_Color("Color", Color) = (1,1,1,1)
//立方体纹理
_Cube("Cubemap", Cube) = ""{}
//反射率
_FresnelScale("FresnelScale", Range(0,1)) = 1
}
fixed4 _Color;
samplerCUBE _Cube;
float _FresnelScale;
修改v2f结构体,加入视角方向,因为需要在片元着色器中用菲涅耳近似公式参与计算
struct v2f
{
//裁剪空间下的顶点坐标
float4 pos:SV_POSITION;
//世界空间下法线
fixed3 worldNormal:NORMAL;
//世界空间下的顶点位置
float3 worldPos:TEXCOORD0;
//世界空间下视角的方向
float3 worldViewDir:TEXCOORD1;
//世界空间下的反射向量
//我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
float3 worldRefl:TEXCOORD2;
//阴影相关
SHADOW_COORDS(3)
};
顶点函数中,把临时变量视角方向赋值给结构体变量
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//计算反射光向量
//1.计算世界空间下法线向量
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//2.世界空间下的顶点坐标
v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//3.计算视角方向 内部是用摄像机位置 - 世界坐标位置
v2f.worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
//4.计算反射向量
v2f.worldRefl = reflect(-v2f.worldViewDir, v2f.worldNormal);
//阴影相关处理
TRANSFER_SHADOW(v2f);
return v2f;
}
片元函数中,对立方体纹理不需要乘上反射颜色了。使用菲涅耳公式进行计算菲涅耳反射率,同时和漫反射插值时也使用菲涅耳反射率
fixed4 frag(v2f v2f):SV_TARGET
{
//漫反射光照相关计算
//得到光的方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
//漫反射颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));
//对立方体纹理利用对应的反射向量进行采样
fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefl).rgb;
//得到光照衰减以及阴影相关的衰减值
UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);
//菲涅耳反射率计算
// 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);
//我们利用lerp 在漫反射颜色和反射颜色之间 进行插值 0和1就是极限状态 0 没有反射效果 1只有反射效果 0~1之间就是两者叠加
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, fresnel) * atten;
//用采样颜色 * 反射率 决定最终的颜色效果
return fixed4(color, 1.0);
}
创建shader进行对比,修改反射程度后,可以看到菲涅尔反射边缘和普通反射的不同,类似玻璃杯的效果
33.2 知识点代码
Lesson33_高级纹理_立方体纹理_菲涅尔反射_菲涅尔反射结合漫反射及阴影.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson33_高级纹理_立方体纹理_菲涅尔反射_菲涅尔反射结合漫反射及阴影 : MonoBehaviour
{
void Start()
{
#region 知识点 菲涅尔反射结合漫反射
//由于菲涅耳反射是基于反射的
//因此我们在之前实现的反射结合漫反射的基础上修改即可
//1.新建Shader命名为Lesson33_FresnelDiffuseShadow,复制Lesson27_ReflectDiffuseShadow
//2.修改属性
// 2-1:去掉反射颜色
// 2-2:修改反射率为菲涅耳相关的R0反射率
//3.修改v2f结构体
// 加入视角方向(需要再片元着色器中用菲涅耳近似公式参与计算)
//4.顶点着色器
// 修改视角方向临时变量
//5.片元着色器
// 5-1:计算菲涅耳反射率
// 5-2:用菲涅耳反射率参与lerp计算
#endregion
}
}
Lesson33_FresnelDiffuseShadow.shader
Shader "Unlit/Lesson33_FresnelDiffuseShadow"
{
Properties
{
//漫反射颜色
_Color("Color", Color) = (1,1,1,1)
//立方体纹理
_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
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
samplerCUBE _Cube;
float _FresnelScale;
struct v2f
{
//裁剪空间下的顶点坐标
float4 pos:SV_POSITION;
//世界空间下法线
fixed3 worldNormal:NORMAL;
//世界空间下的顶点位置
float3 worldPos:TEXCOORD0;
//世界空间下视角的方向
float3 worldViewDir:TEXCOORD1;
//世界空间下的反射向量
//我们将把反射向量的计算放在顶点着色器函数中 节约性能 表现效果也不会太差 肉眼几乎分辨不出来
float3 worldRefl:TEXCOORD2;
//阴影相关
SHADOW_COORDS(3)
};
v2f vert(appdata_base appdata_base)
{
v2f v2f;
//顶点坐标转换
v2f.pos = UnityObjectToClipPos(appdata_base.vertex);
//计算反射光向量
//1.计算世界空间下法线向量
v2f.worldNormal = UnityObjectToWorldNormal(appdata_base.normal);
//2.世界空间下的顶点坐标
v2f.worldPos = mul(unity_ObjectToWorld, appdata_base.vertex).xyz;
//3.计算视角方向 内部是用摄像机位置 - 世界坐标位置
v2f.worldViewDir = UnityWorldSpaceViewDir(v2f.worldPos);
//4.计算反射向量
v2f.worldRefl = reflect(-v2f.worldViewDir, v2f.worldNormal);
//阴影相关处理
TRANSFER_SHADOW(v2f);
return v2f;
}
fixed4 frag(v2f v2f):SV_TARGET
{
//漫反射光照相关计算
//得到光的方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(v2f.worldPos));
//漫反射颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(normalize(v2f.worldNormal), worldLightDir));
//对立方体纹理利用对应的反射向量进行采样
fixed3 cubemapColor = texCUBE(_Cube, v2f.worldRefl).rgb;
//得到光照衰减以及阴影相关的衰减值
UNITY_LIGHT_ATTENUATION(atten, v2f, v2f.worldPos);
//菲涅耳反射率计算
// 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);
//我们利用lerp 在漫反射颜色和反射颜色之间 进行插值 0和1就是极限状态 0 没有反射效果 1只有反射效果 0~1之间就是两者叠加
fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse, cubemapColor, fresnel) * atten;
//用采样颜色 * 反射率 决定最终的颜色效果
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com