立方體紋理(Cubemap)是環境映射(Environment Mapping)的一種實作方法,
環境映射可以讓物體看起來像鍍了層金屬一樣反射周圍的環境,
立方體紋理一共包含六張影像,對應了一個立方體的六個面,
采樣的時候,給出一個三維坐標,由這個三維坐標確定一個方向向量,這個向量延伸后與立方體的交點就是采樣結果,
缺點:場景發生改變的時候需要重新生成立方體紋理,
不能模擬多次反射,如兩個金屬球,請使用 PBR (基于物理的渲染)技術,
盡量使用凸多面體,(凹多面體會反射自身)
最常見的用途是天空盒子(Skybox)和環境映射,
1、天空盒 Skybox
使用天空盒的時候,整個場景被包含在一個立方體內,這個立方體每個面都使用立方體紋理映射技術,
創建一個 Skybox 材質,在 Window - Rendering - Lighting 中賦給相關設定即可,
還需要保證渲染場景的攝像機的 Camera 組件中的 Clear Hags 被設定為 Skybox,
Unity 中,天空盒是在所有不透明物體后渲染的,其背后的網格是一個立方體或者一個細分的球體,
2、創建用于環境映射的立方體紋理
創建用于環境映射的立方體紋理的方法有三種:
- 直接由一些特殊布局的紋理創建,
提供一張具有特殊布局的紋理,將紋理的 Texture Type 設定為 Cubemap 即可, - 手動創建一個 Cubemap 資源, 再把 6 張圖陚給它,
僅用于 Unity 5 以前的版本,但不支持邊緣修正、壓縮、光滑反射(glossy reflection)、HDR, - 由腳本生成,
利用 Unity 提供的 Camera.RenderToCubemap 函式實作,該函式能夠從任意位置處觀察到的場景存盤在 6 張影像中,從而創建出該位置上對應的立方體紋理,
下面使用腳本實作:
- 創建一個 Legacy - Cubemap ,勾選 Readable 選項,
- 撰寫 C# 腳本,這個腳本創建了一個新視窗,
using UnityEngine;
using UnityEditor;
using System.Collections;
public class RenderCubemapWizard : ScriptableWizard {
public Transform renderFromPosition;
public Cubemap cubemap;
void OnWizardUpdate () {
helpString = "Select transform to render from and cubemap to render into";
isValid = (renderFromPosition != null) && (cubemap != null);
}
void OnWizardCreate () {
// create temporary camera for rendering
GameObject go = new GameObject( "CubemapCamera");
go.AddComponent<Camera>();
// place it on the object
go.transform.position = renderFromPosition.position;
// render into cubemap
go.GetComponent<Camera>().RenderToCubemap(cubemap);
// destroy temporary camera
DestroyImmediate( go );
}
[MenuItem("GameObject/Render into Cubemap")]
static void RenderCubemap () {
ScriptableWizard.DisplayWizard<RenderCubemapWizard>(
"Render cubemap", "Render!");
}
}
- 這個腳本在 GameObject 中添加了 Render into Cubemap 視窗,在這個視窗將 GameObject、Cubemap 拖入,點擊 Render 即可生成 Cubemap,
3、反射
- 通過入射光線、表面法線來計算反射方向,
使用 CG 的 reflect 函式計算反射方向:
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
第一個引數為視線方向的反方向,第二個引數為法線方向 - 利用反射方向對立方體紋理采樣,
使用 texCUBE 函式對立方體采樣,不需要進行歸一化,因為只代表方向,
使用 lerp 函式利用 _ReflectAmount 混合漫反射顏色與反射顏色,并于環境光相加后回傳,
Shader "Unity Shaders Book/Chapter 10/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 multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.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;
SHADOW_COORDS(4)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
// Compute the reflect dir in world space
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);
TRANSFER_SHADOW(o);
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));
// Use the reflect dir in world space to access the cubemap
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb * _ReflectColor.rgb;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
// Mix the diffuse color with the reflected color
fixed3 color = ambient + lerp(diffuse, reflection, _ReflectAmount) * atten;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
本案例中,我們在頂點著色器中計算反射方向,
在片元著色器中計算能夠得到更加細膩的結果,(性能消耗也更大)
4、折射
4.1、原理
可以使用斯涅爾定律(Snell’s Law)來計算反射角,

其中,η1 和 η2 分別是兩個介質的折射率 (index of refraction),
折射率是一項重要的物理常數,例如真空的折射率是 1,而玻璃的折射率一般是 1.5,
對一個透明物體來說, 更準確的模擬方法需要計算兩次折射:當光線進入它的內部時、從它內部射出時,
由于效率,我們一般只模擬入射
4.2、實作
在頂點著色器中,使用 refract() 函式計算折射方向,
- 第一個引數即為入射光線的方向, 它必須是歸一化后的矢量,
- 第二個引數是表面法線, 法線方向同樣需要是歸一化后的;
- 第三個引數是入射光線所在介質的折射率和折射光線所在介質的折射率之間的比值,
例如如果光是從空氣射到玻璃表面, 那么這個引數應該是空氣的折射率和玻璃的折射率之間的比值, 即 1/1.5,
它的回傳值就是計算而得的折射方向, 它的模則等于入射光線的模,
o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);
最后使用 lerp 混合漫反射顏色與折射顏色,
Shader "Unity Shaders Book/Chapter 10/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 multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.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;
SHADOW_COORDS(4)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos);
// Compute the refract dir in world space
o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);
TRANSFER_SHADOW(o);
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));
// Use the refract dir in world space to access the cubemap
fixed3 refraction = texCUBE(_Cubemap, i.worldRefr).rgb * _RefractColor.rgb;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
// Mix the diffuse color with the refract color
fixed3 color = ambient + lerp(diffuse, refraction, _RefractAmount) * atten;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
5、菲涅爾反射
菲涅耳反射描述了一種光學現象, 即當光線照射到物體表面上時, 一部分發生反射, 一部分進入物體內部, 發生折射或散射,
被反射的光和入射光之間存在一定的比率關系, 這個比率關系可以通過菲涅耳等式進行計算,
實時渲染中,我們使用 Schlick 菲涅耳近似等式:

Fo 是一個反射系數, 用于控制菲涅耳反射的強度, v 是視角方向, n 是表面法線,
另一個應用比較廣泛的等式是 Empricial 菲涅耳近似等式:

其中,bias 、scale 和 power 是控制項 ,
常用于車漆、水面效果
下面是一個使用 Schlick 菲涅耳近似等式的 Shader:
使用 lerp 函式根據截取到(0,1)的 fresnel 變數進行插值,
Shader "Unity Shaders Book/Chapter 10/Fresnel" {
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 multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "AutoLight.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;
SHADOW_COORDS(4)
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(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);
TRANSFER_SHADOW(o);
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;
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 reflection = texCUBE(_Cubemap, i.worldRefl).rgb;
fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);
fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0, dot(worldNormal, worldLightDir));
fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
效果如下:
一些實作也會直接把 fresnel 和反射光照相乘后疊加到漫反射光照上, 模擬邊緣光照的效果,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/499627.html
標籤:其他
上一篇:Postman Mock快速入門
