Cg 程式設計/Unity/紋理層

本教程介紹了多重紋理,即在著色器中使用多個紋理影像。
它擴充套件了“紋理球體”部分的著色器程式碼以支援多個紋理,並展示了一種組合它們的 方法。如果你還沒有閱讀過該教程,現在是一個很好的機會去閱讀它。
許多真實表面(例如左側影像中的人類皮膚)由幾層不同顏色、透明度、反射率等組成。如果最頂層是不透明的並且不透光,這對於渲染表面來說並不重要。然而,在許多情況下,最頂層是(半)透明的,因此表面的準確渲染必須考慮多層。
事實上,Phong 反射模型中包含的鏡面反射(參見“鏡面高光”部分)通常對應於反射光的透明層:人類皮膚上的汗液、水果上的蠟、包含嵌入色素粒子的透明塑膠等。另一方面,漫反射對應於最頂層透明層下面的層。
照明此類分層表面不需要層的幾何模型:它們可以用單個無限薄的多邊形網格表示。然而,照明計算必須為不同層計算不同的反射,並且必須考慮光在層之間的透射(光進入層時和光離開層時)。這些方法的示例包含在 Nvidia 的“Dawn”演示中(參見“GPU Gems”一書的第 3 章,該書可以在網上獲取)和 Nvidia 的“Human Head”演示中(參見“GPU Gems 3”一書的第 14 章,該書也可以在網上獲取)。
對這些過程的完整描述超出了本教程的範圍。需要說明的是,層通常與紋理影像相關聯以指定它們的特性。這裡我們只展示如何使用兩個紋理和一種特定的組合方式。實際上,這個示例與層無關,因此說明了多重紋理的應用範圍比表面層更廣泛。


由於人類活動,地球的背陰面並不完全黑暗。相反,人造光線標出了城市的位置和範圍,如左側影像所示。因此,地球的漫反射照明不應僅僅是調暗陽光照射面的紋理影像,而應該將其與背陰面的紋理影像混合。請注意,陽光照射的地球比背陰面的人造燈光亮得多;但是,我們降低了這種對比度,以便展示夜間紋理。
著色器程式碼擴充套件了“紋理球體”部分中的程式碼以支援兩個紋理影像,並使用“漫反射”部分中描述的計算方法來處理單個方向光源
根據這個公式,漫反射照明級別levelOfLighting為max(0, N·L)。然後,我們根據levelOfLighting混合白天紋理和夜間紋理的顏色。這可以透過將白天顏色乘以levelOfLighting,並將夜間顏色乘以1.0 - levelOfLighting來實現,然後再將它們相加以確定片段的顏色。或者,可以使用內建的 Cg 函式lerp(lerp(a, b, w) = b*w + a*(1.0-w)),該函式可能效率更高。因此,片段著色器可以為
float4 frag(vertexOutput input) : COLOR
{
float4 nighttimeColor =
tex2D(_MainTex, input.tex.xy);
float4 daytimeColor =
tex2D(_DecalTex, input.tex.xy);
return lerp(nighttimeColor, daytimeColor,
input.levelOfLighting);
// = daytimeColor * levelOfLighting
// + nighttimeColor * (1.0 - levelOfLighting)
}
請注意,這種混合與“透明度”部分中討論的 alpha 混合非常相似,不同之處在於我們在片段著色器內部執行混合,並使用levelOfLighting代替 alpha 分量(即紋理的透明度),該紋理應該與其他紋理“混合”。事實上,如果_DecalTex指定了 alpha 分量(參見“透明紋理”部分),我們可以使用此 alpha 分量將_DecalTex與_MainTex混合。這實際上是 Unity 的標準Decal著色器所做的,它對應於一個部分透明的層,位於一個不透明層的頂部,該層在最頂層透明的地方可見。
著色器屬性的名稱選擇與回退著色器的屬性名稱一致——在本例中為Decal著色器(請注意,回退Decal著色器和標準Decal著色器似乎以相反的方式使用這兩個紋理)。此外,引入了額外的屬性_Color,並將其(按分量)乘以夜間紋理的紋理顏色,以控制其整體亮度。此外,光源的顏色_LightColor0也(按分量)乘以白天紋理的顏色,以考慮彩色光源。
Shader "Cg multitexturing of Earth" {
Properties {
_DecalTex ("Daytime Earth", 2D) = "white" {}
_MainTex ("Nighttime Earth", 2D) = "white" {}
_Color ("Nighttime Color Filter", Color) = (1,1,1,1)
}
SubShader {
Pass {
Tags { "LightMode" = "ForwardBase" }
// pass for the first, directional light
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform float4 _LightColor0;
// color of light source (from "Lighting.cginc")
uniform sampler2D _MainTex;
uniform sampler2D _DecalTex;
uniform float4 _Color;
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 tex : TEXCOORD0;
float levelOfLighting : TEXCOORD1;
// level of diffuse lighting computed in vertex shader
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
float4x4 modelMatrix = unity_ObjectToWorld;
float4x4 modelMatrixInverse = unity_WorldToObject;
float3 normalDirection = normalize(
mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
float3 lightDirection = normalize(
_WorldSpaceLightPos0.xyz);
output.levelOfLighting =
max(0.0, dot(normalDirection, lightDirection));
output.tex = input.texcoord;
output.pos = UnityObjectToClipPos(input.vertex);
return output;
}
float4 frag(vertexOutput input) : COLOR
{
float4 nighttimeColor =
tex2D(_MainTex, input.tex.xy) * _Color;
float4 daytimeColor =
tex2D(_DecalTex, input.tex.xy) * _LightColor0;
return lerp(nighttimeColor, daytimeColor,
input.levelOfLighting);
// = daytimeColor * levelOfLighting
// + nighttimeColor * (1.0 - levelOfLighting)
}
ENDCG
}
}
Fallback "Decal"
}
執行此著色器時,請確保場景中有一個啟用的方向光源。
恭喜!你已經完成了最後一個關於基本紋理的教程。我們已經瞭解了
- 表面層如何影響材料的外觀(例如人類皮膚、打蠟水果、塑膠等)
- 在紋理化代表地球的球體時,如何考慮背陰面的燈光。
- 如何在著色器中實現這種技術。
- 這與將 alpha 紋理與第二個不透明紋理混合有關。
如果你還想了解更多
- 關於基本紋理,你可以閱讀“紋理球體”部分.
- 關於漫反射,你可以閱讀“漫反射”部分.
- 關於 alpha 紋理,你可以閱讀“透明紋理”部分.
- 關於高階皮膚渲染,你可以閱讀“GPU Gems”一書中 Curtis Beeson 和 Kevin Bjorke 撰寫的第 3 章“‘Dawn’演示中的皮膚”,該書由 Randima Fernando(編輯)於 2004 年由 Addison-Wesley 出版,可以在網上獲取,以及“GPU Gems 3”一書中 Eugene d’Eon 和 David Luebke 撰寫的第 14 章“用於逼真即時皮膚渲染的高階技術”,該書由 Hubert Nguyen(編輯)於 2007 年由 Addison-Wesley 出版,也可以在網上獲取。