立方体纹理(Cubemap)最常见的是用于天空盒子以及环境映射,通常被用作具有反射,折射属性物体的反射源。Lighting-Environment中添加对应的材质
材质对应贴图的类型要选择Cube
反射
反射原理:根据视线方向和法线方向计算出反射方向,再把反射方向作为采样的方向,在Cubemap上采样
反射Shader
Shader "MyCustom/Reflect"
{
Properties
{
_CubeMap ("CubeMap", Cube) = "" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
//位置用float4表示,它表示是齐次坐标,比如我们这样表示一个float4(x,y,z,w),当w = 1的时候它表示点(x,y,z),
//当w= 0的时候它表示一个向量(x,y,z)。区别就在这里,当W为1时表示点,当W为0时表示向量。
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
//世界空间中的法线
float3 worldNormal : TEXCOORD0;
//世界空间中的位置
float3 worldPos : TEXCOORD1;
float4 vertex : SV_POSITION;
};
samplerCUBE _CubeMap;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = normalize(mul((float3x3)unity_ObjectToWorld, v.normal));
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//视线方向,世界空间中摄像机的位置 - 世界空间中每个点的位置
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
//注意视线方向取反
float3 worldReflect = reflect(-viewDir, i.worldNormal);
fixed4 col = texCUBE(_CubeMap, worldReflect);
return col;
}
ENDCG
}
}
}
折射
菲涅尔定律:当光从介质1沿着和表面法线夹角为θ1的方向斜射入介质2时,我们可以使用如下公式计算折射光线与法线的夹角θ2:
η1sinθ1 = η2sinθ2
其中,η1和η2分别是两个介质的折射率
折射原理:利用菲涅尔定律计算出折射方向,再把折射方向作为采样的方向,在Cubemap上采样
折射Shader
Shader "MyCustom/Refract"
{
Properties
{
_CubeMap ("CubeMap", Cube) = "" {}
//该属性是两种介质折射率的比值,例如如果光是从空气射到玻璃表面,
//那么这个参数应该是空气的折射率和玻璃的折射率之间的比值,即1/1.5
_RefractRatio("Refract Ratio", Range(0.1, 1)) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
//世界空间中的法线
float3 worldNormal : TEXCOORD0;
//世界空间中的位置
float3 worldPos : TEXCOORD1;
float4 vertex : SV_POSITION;
};
samplerCUBE _CubeMap;
float _RefractRatio;
//顶点着色器和反射一样
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = normalize(mul((float3x3)unity_ObjectToWorld, v.normal));
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//视线方向,世界空间中摄像机的位置 - 世界空间中每个点的位置
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
//折射方向
float3 worldRefract = refract(-viewDir, i.worldNormal, _RefractRatio);
fixed4 col = texCUBE(_CubeMap, worldRefract);
return col;
}
ENDCG
}
}
}
菲涅耳反射
菲涅尔反射:当光线照射到物体表面上时,一部分发生反射,一部分进入物体内部,发生折射或散射。被反射的光和入射光之间存在一定的比率关系,这个比率关系可以通过菲涅耳等式进行计算
视线越垂直于表面,反射越弱,折射越强,视线越平行于表面,反射越强。比如站在湖边,近处视线与湖面接近垂直,反射弱,能看到水面下的东西,远处视线与湖面趋于平行,只能看到水面反射的东西,看不到水下的东西
菲涅尔等式是非常复杂的,实际渲染中会用近似公式来模拟。常用的两个近似公式:
Schlick 菲涅耳近似等式
其中F0是一个反射系数,用于控制菲涅尔反射的强度,v 是视角方向, n 是表面法线
Empricial 菲涅耳近似等式
其中,bias, scale 和 power 是控制项
Shader "MyCustom/FresnelReflection"
{
Properties
{
_CubeMap ("CubeMap", Cube) = "" {}
//该属性是两种介质折射率的比值,例如如果光是从空气射到玻璃表面,
//那么这个参数应该是空气的折射率和玻璃的折射率之间的比值,即1/1.5。
_RefractRatio("Refract Ratio", Range(0.1, 1)) = 0.5
//反射系数,用于控制菲涅尔反射的强度
_FresnelRatio("Fresnel Ratio", Float) = 0.5
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f
{
//世界空间中的法线
float3 worldNormal : TEXCOORD0;
//世界空间中的位置
float3 worldPos : TEXCOORD1;
float4 vertex : SV_POSITION;
};
samplerCUBE _CubeMap;
float _RefractRatio;
float _FresnelRatio;
//顶点着色器和反射一样
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.worldNormal = normalize(mul((float3x3)unity_ObjectToWorld, v.normal));
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//视线方向,世界空间中摄像机的位置 - 世界空间中每个点的位置
float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos);
//反射方向
float3 worldReflect = reflect(-viewDir, i.worldNormal);
float4 reflectCol = texCUBE(_CubeMap, worldReflect);
//折射方向
float3 worldRefract = refract(-viewDir, i.worldNormal, _RefractRatio);
float4 refractCol = texCUBE(_CubeMap, worldRefract);
//菲涅尔公式
float fresnel = _FresnelRatio + (1 - _FresnelRatio) * pow(1 - dot(viewDir, i.worldNormal), 5);
fixed4 col = fresnel * reflectCol + (1 - fresnel) * refractCol;
return col;
}
ENDCG
}
}
}
效果对比
参考《Unity Shader入门精要》