1 前言
立方体纹理(Cubemap)和天空盒子(Skybox)中介绍了生成立方体纹理和制作天空盒子的方法,本文将使用立方体纹理进行采样,实现反射和折射效果。
立方体纹理采样原理:从世界坐标系的坐标原点出发,发射一条射线,与边长为 1 的立方体相交(其中心在坐标原点,并且每个面与对应坐标轴垂直),交点位置的像素即为采样的像素。立方体纹理采样函数如下,cubemap 为立方体纹理,worldVec 为世界坐标系中采样方向向量,color 为采样的颜色。
// 立方体纹理采样
fixed4 color = texCUBE(cubemap, worldVec)
2 反射
Reflect.shader
Shader "MyShader/Reflection" { // 反射
Properties {
_Color("Color Tint", Color) = (1, 1, 1, 1) // 物体颜色
_ReflectColor("Reflection Color", Color) = (1, 1, 1, 1) // 反射光的颜色
_ReflectAmount("Reflect Amount", Range(0, 1)) = 1 // 反射比例(用于漫反射和反射之间插值)
_Cubemap("Reflection Cubemap", Cube) = "_Skybox" {} // 立方体纹理
}
SubShader{
Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}
Pass {
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color; // 物体颜色
fixed4 _ReflectColor; // 反射光的颜色
fixed _ReflectAmount; // 反射比例(用于漫反射和反射之间插值)
samplerCUBE _Cubemap; // 立方体纹理
struct a2v {
float4 vertex : POSITION; // 模型空间顶点坐标
float3 normal : NORMAL; // 模型空间法相向量
};
struct v2f {
float4 pos : SV_POSITION; // 裁剪空间顶点坐标
float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
fixed3 worldNormal : TEXCOORD1; // 顶点法线向量
fixed3 worldViewDir : TEXCOORD2; // 观察向量(顶点指向相机)
fixed3 worldRefl : TEXCOORD3; // 灯光向量(顶点指向光源)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
o.worldNormal = UnityObjectToWorldNormal(v.normal); // 计算世界空间中顶点法线向量(已归一化)
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 计算世界空间中顶点坐标
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos); // 计算世界空间中观察向量(顶点指向相机)
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal); // 计算观察向量的反射向量
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal); // 法线向量
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 灯光向量(顶点指向光源)
fixed3 worldViewDir = normalize(i.worldViewDir); // 观察向量(顶点指向相机)
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 环境光颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir)); // 漫反射光颜色
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb; // 反射光颜色
fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount); // 漫反射光与反射光颜色进行插值
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
_ReflectAmount 值为 1 反射效果如下:
_ReflectAmount 值由 0 至 1 渐变反射效果如下:
说明:即使没有房间模型,中间的 5 个物体也会反射房间环境,这是因为它们使用的纹理源于 Cubemap 采样,而 Cubemap 一旦生成,就与环境无关。
3 菲涅耳反射
FresnelReflect.shader
Shader "MyShader/FresnelReflect" { // 菲涅耳反射
Properties {
_Color("Color Tint", Color) = (1, 1, 1, 1) // 物体颜色
_FresnelScale("Fresnel Scale", Range(0, 1)) = 0.5 // 菲涅耳反射系数缩放值
_Cubemap("Reflection Cubemap", Cube) = "_Skybox" {} // 立方体纹理
}
SubShader{
Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}
Pass {
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color; // 物体颜色
fixed _FresnelScale; // 菲涅耳反射系数缩放值
samplerCUBE _Cubemap; // 立方体纹理
struct a2v {
float4 vertex : POSITION; // 模型空间顶点坐标
float3 normal : NORMAL; // 模型空间法相向量
};
struct v2f {
float4 pos : SV_POSITION; // 裁剪空间顶点坐标
float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
fixed3 worldNormal : TEXCOORD1; // 顶点法线向量
fixed3 worldViewDir : TEXCOORD2; // 观察向量(顶点指向相机)
fixed3 worldRefl : TEXCOORD3; // 灯光向量(顶点指向光源)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
o.worldNormal = UnityObjectToWorldNormal(v.normal); // 计算世界空间中顶点法线向量(已归一化)
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 计算世界空间中顶点坐标
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos); // 计算世界空间中观察向量(顶点指向相机)
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal); // 计算观察向量的反射向量
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal); // 法线向量
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 灯光向量(顶点指向光源)
fixed3 worldViewDir = normalize(i.worldViewDir); // 观察向量(顶点指向相机)
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 环境光颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir)); // 漫反射光颜色
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb; // 反射光颜色
fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5); // 菲涅耳反射系数
fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)); // 漫反射光与反射光颜色进行插值
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
菲涅耳反射效果如下:
4 折射
Refract.shader
Shader "MyShader/Refraction" { // 折射
Properties {
_Color("Color Tint", Color) = (1, 1, 1, 1) // 物体颜色
_RefractColor("Refraction Color", Color) = (1, 1, 1, 1) // 折射光的颜色
_RefractAmount("Refraction Amount", Range(0, 1)) = 1 // 折射比例(用于漫反射和折射之间插值)
_RefractRatio("Refraction Ratio", Range(0.1, 1)) = 0.5 // 折射比(入射介质折射率/折射介质折射率)
_Cubemap("Refraction Cubemap", Cube) = "_Skybox" {} // 立方体纹理
}
SubShader{
Tags { "RenderType" = "Opaque" "Queue" = "Geometry"}
Pass {
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color; // 物体颜色
fixed4 _RefractColor; // 折射光的颜色
float _RefractAmount; // 折射比例(用于漫反射和折射之间插值)
fixed _RefractRatio; // 折射比(入射介质折射率/折射介质折射率)
samplerCUBE _Cubemap; // 立方体纹理
struct a2v {
float4 vertex : POSITION; // 模型空间顶点坐标
float3 normal : NORMAL; // 模型空间法相向量
};
struct v2f {
float4 pos : SV_POSITION; // 裁剪空间顶点坐标
float3 worldPos : TEXCOORD0; // 世界空间顶点坐标
fixed3 worldNormal : TEXCOORD1; // 顶点法线向量
fixed3 worldViewDir : TEXCOORD2; // 观察向量(顶点指向相机)
fixed3 worldRefr : TEXCOORD3; // 灯光向量(顶点指向光源)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
o.worldNormal = UnityObjectToWorldNormal(v.normal); // 计算世界空间中顶点法线向量(已归一化)
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 计算世界空间中顶点坐标
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos); // 计算世界空间中观察向量(顶点指向相机)
o.worldRefr = refract(-normalize(o.worldViewDir), o.worldNormal, _RefractRatio); // 计算观察向量的折射向量
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal); // 法线向量
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 灯光向量(顶点指向光源)
fixed3 worldViewDir = normalize(i.worldViewDir); // 观察向量(顶点指向相机)
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; // 环境光颜色
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir)); // 漫反射光颜色
fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb; // 折射光颜色
fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount); // 漫反射光与折射光颜色进行插值
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
_RefractAmount 值为 1 折射效果如下:
_RefractAmount 值由 0 至 1 渐变折射效果如下: