https://catlikecoding.com/unity/tutorials/custom-srp/complex-maps/
1 創建材質球
我們的材質已經支持光照,并且支持 Albedo 和 Emission 貼圖.創建材質球,并應用下面的電路板的圖分別作為 albedo emission
設置材質球的金屬度為 1 , 光滑度為 0.95
2 Mask Map
在 albedo 圖上的不同區域,綠色區域和金色區域的金屬度,光滑度其實都是不同的,但是現在我們只支持單一的配置.
下面我們加入 mask 圖,以在 shader 中確定每個像素的金屬度和光滑度.
參考URP,這張 mask 圖我們叫 MODS,即 rgba 通道分別用作 Metallic, Occlusion, Detail, Smoothness
下面是我們的電路板材質的 MODS 圖.由于貼圖內保存的是 mask data 而不是顏色,因此確保貼圖導入參數的 sRGB(color texture) 是 disable 狀態,否則 GPU 在采樣時會錯誤的執行 gamma-to-linear 轉換.
首先,在 Lit.shader 中,為材質增加MODS貼圖屬性
[NoScaleOffset]_MODS("Mask(MODS", 2D) = "white"{}
_Metallic("Metallic", Range(0,1)) = 0
2.1 Metallic and Smoothness
在 LitInput.hlsl 中,采樣并應用 r 通道(metallic) 和 a 通道(smoothness)
TEXTURE2D(_MODS);float4 GetMask(float2 baseUV)
{return SAMPLE_TEXTURE2D(_MODS, sampler_BaseMap, baseUV);
}float GetMetallic (float2 baseUV)
{return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Metallic) * GetMask(baseUV).r;
}float GetSmoothness (float2 baseUV)
{return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness)* GetMask(baseUV).a;
}
2.2 Occlusion
Occlusion 遮擋數據存儲在 G 通道.其思想是,表面上低矮的區域如縫隙和坑洞,通常被周圍高出的部分所遮擋,在應該不會受到間接光照的影響.
如同 metallic 和 smoothness,我們從 MODS 獲得 occlusion,并通過增加一個材質屬性 occlusion 來控制其強度.在像素著色器中,獲取并存儲到 surface.occlusion 中.最后在 IndirectBRDF 計算間接光照時,乘以該值.
////////////////////////////////////
// 在 lit.shader 材質屬性中,定義 _Occlusion
_Occlusion("Occlusion", Range(0,1)) = 1////////////////////////////////////
// 在 litinput.hlsl 中
// 定義對應的 _Occlusion 變量
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
...
float _Occlusion;
...
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)// 定義獲取 occlusion 的函數
// 該數值會被乘到間接光上
float GetOcclusion(float2 baseUV)
{float strength = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Occlusion);float occlusion = GetMask(baseUV);// strength 為 0 時,僅采用貼圖的 occlusion// 為 1 時,插值為 1,對間接光沒有影響, 即無遮擋.因此該強度會弱化遮擋效果return lerp(occlusion, 1.0, strength);
}// 在 litpass.hlsl 中的像素著色器中,為 surface.occlusion 賦值
surface.occlusion = GetOcclusion(input.uv);// 在 BRDF.hlsl 中的 IndirectBRDF 中,應用 occlusion
// 間接 BRDF 光
float3 IndirectBRDF(Surface surface, BRDF brdf, float3 giDiffuse, float3 giSpecular)
{...// 累加 diffuse 和 reflectionreturn (diffuse + reflection) * surface.occlusion;
}
3 Detail Map
"細節貼圖" 顧名思義,是用來為表面添加細節.同時,由于細節紋理以高平鋪率進行平鋪,使得其具有“高分辨率”,在距離模型特別近時,消除像素顆粒感.
細節貼圖同MODS一樣,作為數據貼圖,而不是顏色貼圖,將各種細節數據合并到一張貼圖上.HDRP中,該貼圖是ANySNx,即, R 通道是 albedo 細節數據, B 通道是 smoothness細節, G 和 A 是細節法線的 y 和 x 分量.我們將使用單獨的細節法線貼圖,因此不會用到這兩個通道.所以我們用一張RGB圖.下圖就是我們要用的細節紋理:
不將細節法線合并到細節貼圖中,是因為合并生產這樣的貼圖比較麻煩.最重要的是,法線在生成 mipmap 時,其算法跟其它貼圖通道時不同的,因此我們還是用單獨的細節法線貼圖.
3.1 Detail Albedo
首先處理 albedo detail
/////////////// lit.shader
// 聲明相關材質屬性
// 細節紋理,默認灰色,值是 0.5,將不會有細節效果.大于會變亮,小于會變暗
_DetailMap("Dtails", 2D) = "linearGray" {}
// 控制細節紋理強度
_DetailAlbedo("Detail Albedo", Range(0,1)) = 1/////////////// litpass.hlsl
// 定義細節紋理UV,在VS中計算并傳遞給FSstruct Varyings
{...float2 detailUV : TEXCOORD1; // 細節紋理UVGI_VARYINGS_DATAUNITY_VERTEX_INPUT_INSTANCE_ID
};Varyings LitPassVertex(Attributes input)
{...output.detailUV = TransformDetailUV(input.uv); // 計算細節紋理UV并傳遞到FS...
}float4 LitPassFragment(Varyings input) : SV_TARGET
{UNITY_SETUP_INSTANCE_ID(input);ClipLOD(input.positionCS, unity_LODFade.x);// 采樣 base map 并應用細節紋理float4 base = GetBase(input.uv, input.detailUV);
}/////////////// litinput.hlsl 中
// 定義材質屬性常量
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
...
float _DetailAlbedo;
float4 _EmissionColor;
float4 _DetailMap_ST;
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)// 變換細節紋理UV
float2 TransformDetailUV(float2 uv)
{float4 st = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _DetailMap_ST);return uv * st.xy + st.zw;
}// 采樣細節紋理并變換到 -1 ~ 1 之間
float4 GetDetail(float2 uv)
{return SAMPLE_TEXTURE2D(_DetailMap, sampler_DetailMap, uv) * 2.0f - 1.0f;
}// 采樣 base map 并應用細節紋理
// detailUV 給默認參數0,避免沒有該參數時報錯(如 shadowCaster pass)
float4 GetBase(float2 baseUV, float2 detailUV = 0)
{float4 map = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, baseUV);float4 color = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);// 應細節紋理數據影響 diffusefloat detail = GetDetail(detailUV).r;detail *= _DetailAlbedo;// 由于我們是在線性空間,導致“變亮”的效果比“變暗”效果更強,在 gamma 空間更好// 但是我們用一種簡單的方式來近似:sqrtmap.rgb = sqrt(map.rgb);// 細節紋理,根據MODS紋理中的 D 進行 maskfloat mask = GetMask(baseUV).b;// detail > 0 ? 根據 detail 的值,執行 map.rgb - 1 的插值// detail <= 0 ? 根據 detail 的值,執行 map.rgb - 0 的插值map.rgb = lerp(map.rgb, detail > 0 ? 1 : -1, abs(detail)* mask);return map * color;
}
現在,我們的材質增加了細節 albedo,可以看到,顏色細節更多了:
3.2 Detail Smoothness
細節貼圖的 B 通道存儲了光滑度細節.
同 albedo detail 一樣,增加一個材質屬性來控制強度,并修改 GetSmoothness 函數應用細節光滑度
/////////////// lit.shader
// 控制細節光滑度強度
_DetailSmoothness("Detail Smoothness", Range(0,1)) = 1/////////////// litinput.hlsl
// 采樣光滑度
float GetSmoothness (float2 baseUV, float2 detailUV=0)
{// 采樣獲得 MODS 中的光滑度float smoothness = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness);smoothness *= GetMask(baseUV).a;// 采樣獲得細節光滑度float detail = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _DetailSmoothness);detail *= GetDetail(baseUV).b;// 采樣獲得 maskfloat mask = GetMask(baseUV).b;// 根據細節光滑度的符號,向 0 或 1 插值.插值控制參數應用細節光滑強度控制smoothness = lerp(smoothness, detail > 0 ? 1 : 0, abs(detail) * mask);return smoothness;
}
現在,我們的材質增加了細節光滑度,可以看到高光更細膩了:
3.3 Detail Fading
我們希望只有當表面近時,才顯示細節,而當表面距離很遠時,不使用細節,因為那樣會導致像素抖動產生噪點,就像貼圖采樣了錯誤的LOD一樣.
因此 detail 需要 fade out.
通過Unity在貼圖導入中提供的 Fadeout Mip Maps選項,可以自動實現該特性
可以看到,遠處的細節效果被模糊,變淡了
4 Normal Maps
4.1 Normal Maps
光照是基于法線計算的.現在我們的法線是基于頂點法線插值的,因此顯得比較平.通過加入法線貼圖,為表面提供更多的法線細節和變化,讓表面更具立體感.下面是我們的電路板的法線貼圖
最直接的方法是用法線貼圖的 RGB 通道存儲法線的 xyz, 同時將0-1的范圍調整到0-1,一次0.5作為0.
如果假定法線方向都是向上的,則可以移除 up 分量,并將 xy 存儲到 RG 或 AG 通道中, 通過 xy 分量計算獲得.這樣通過壓縮貼圖存儲數據時,精度損失最小.這會改變貼圖的外觀,但是因為 unity 總是顯示貼圖原始的外觀,因此我們看不到變化.
法線貼圖根據平臺不同格式不同.如果格式未變化,則 UNITY_NO_DXT5nm 宏會被定義.根據該宏,我們可以選擇適當的法線解碼函數.這些函數定義在 Core RP 的 Packing.hlsl 中.
由于法線貼圖包裹幾何體,因此法線在對象空間和世界空間是不一樣的,因此定義了符合表面曲線的切線空間來定義法線.切線空間中,向上的Y軸是表面的法線,X軸是切線方向,Z是副法線,可以通過切線和法線來計算.其方向有切線的 w 分量決定.
切線方向處處不同,因此需要定義成頂點數據的一部分,存儲為 xyzw .其中 w 是 1 或 -1,定義了副法線的方向,用來反轉法線.通常動物都是對稱的,可以通過反轉法線,使對稱的兩側使用相同的法線貼圖(這種情況需要處理接縫處法線的連續性,所以很多時候為了避免該問題,會使用完整的法線圖).
有了世界空間法線,以及切線向量,我們就可以構建一個從切線空間到世界空間到變換矩陣,Unity 提供了構建該矩陣的函數 CreateTangentToWorld,傳入切線空間法線,以及切線及切線w,來構建變換矩陣(本質上是 binormal = corss(tangentWS,normalWS) * w,然后以tangentWS, binormal, normalWS 為基向量構建的變換矩陣 ),我們可以直接使用.然后就可以用該矩陣,將采樣得到的切線空間的法線,變換為世界空間法線,通過 unity 提供的 TransformTangentToWorld 函數完成.
對于 shadow normal bias 來說,我們依然需要使用世界空間頂點法線,因此將該法線存儲到 surface 中,并在計算陰影時使用
/////////////// common.hlsl
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl"// 解碼采樣法線貼圖的法線
float3 DecodeNormal(float4 sample, float scale)
{
#if defined(UNITY_NO_DXT5nm)return UnpackNormalRGB(sample, scale);
#elsereturn UnPackNormalmapRGorAG(sample, scale);
#endif
}// 將切線空間到法線,變換到世界空間
float3 NormalTangentToWorld(float3 normalTS, float3 normalWS, float4 tangentWS)
{// 構建變換矩陣,矩陣基向量為// tangentWS.xyz// normalWS// cross(tangentWS.xyz, normalWS) * tangentWS.wfloat3x3 tangentToWorld = CreateTangentToWorld(normalWS, tangentWS.xyz, tangentWS.w);// 將法線變換到世界空間return TransformTangentToWorld(normalTS, tangentToWorld);
}////////////////// litinput.hlslUNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
...
float _NormalScale; // 法線強度
float4 _EmissionColor;
float4 _DetailMap_ST;
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)TEXTURE2D(_NormalMap);// 采樣法線紋理
float3 GetNormalTS(float2 baseUV)
{float4 map = SAMPLE_TEXTURE2D(_NormalMap, sampler_BaseMap, baseUV);float scale = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _NormalScale);float3 normal = DecodeNormal(map, scale);return normal;
}////////////////// litpass.hlsl 中
// VS 輸入聲明對象空間切線
struct Attributes
{...float3 normalOS : NORMAL;float4 tangentOS : TANGENT;...
};// FS 聲明世界空間切線
struct Varyings
{...float3 normalWS : VAR_NORMAL;float4 tangentWS : VAR_TANGENT;...
};// VS 中,將世界空間切線變換到世界空間
Varyings LitPassVertex(Attributes input)
{...output.normalWS = TransformObjectToWorldNormal(input.normalOS);// 將切線變換到世界空間,連同 w 傳遞給 FSoutput.tangentWS = float4(TransformObjectToWorldDir(input.tangentOS), input.tangentOS.w);...
}// FS 中,采樣切線空間法線,并變換到世界空間
float4 LitPassFragment(Varyings input) : SV_TARGET
{...surface.position = input.positionWS;// 獲取切線空間法線并變換到世界空間surface.normal = NormalTangentToWorld(GetNormalTS(input.uv), input.normalWS, input.tangentWS);// shadow map 的 normal bias 依然需要用到世界空間中的頂點法線surface.interplotedNormal = input.normalWS;...
}////////////////// shadow.hlsl
// 計算 shadow 時,使用 interplotedNormal
float GetCascadedShadow(DirShadowData shadowData, ShadowData global, Surface surfaceWS)
{// 根據像素法線和圖素對角線長度,計算偏移float3 normalBias = surfaceWS.interplotedNormal * shadowData.normalBias * _CascadeData[global.cascadeIndex].y;...// 如果有級聯混合,則需要跟下一級級聯進行混合if (global.cascadeBlend < 1.0f){normalBias = surfaceWS.interplotedNormal * shadowData.normalBias * _CascadeData[global.cascadeIndex + 1].y;...}return shadow;
}
4.2 Detailed Normals
像細節紋理一樣,我們可以增加細節法線.將細節法線貼圖的導入選項,設置為 Normal,并設置 Fadeout Mip Maps.
/////////////// lit.shader
// 首先定義材質屬性,包括細節法線紋理和強度控制參數
// 細節法線紋理
[NoScaleOffset]_DetailNormalMap("Detail Normals", 2D) = "bump"{}
// 控制細節 albedo 強度
_DetailAlbedo("Detail Albedo", Range(0,1)) = 1
// 控制細節光滑度強度
_DetailSmoothness("Detail Smoothness", Range(0,1)) = 1
// 控制細節法線強度
_DetailNormalScale("Detail Normal Scale", Range(0,1)) = 1////////////////// litinput.hlsl
// 定義強度控制常量
UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
...
float _NormalScale; // 法線強度
float _DetailNormalScale; // 細節法線強度
...
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)TEXTURE2D(_DetailNormalMap); // 細節法線紋理// 采樣法線紋理,應用細節法線
float3 GetNormalTS(float2 baseUV, float2 detailUV)
{// 采樣法線紋理float4 map = SAMPLE_TEXTURE2D(_NormalMap, sampler_BaseMap, baseUV);float scale = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _NormalScale);float3 normal = DecodeNormal(map, scale);// 采樣細節法線紋理map = SAMPLE_TEXTURE2D(_DetailNormalMap, sampler_DetailMap, detailUV);scale = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _DetailNormalScale);float3 detail = DecodeNormal(map, scale);// 用 unity 提供的函數混合兩個法線,該函數繞著基礎法線旋轉細節法線normal = BlendNormalRNM(normal, detail);return normal;
}////////////////// litpass.hlsl
// 獲取法線時,傳入細節紋理UV
// 獲取切線空間法線并變換到世界空間
surface.normal = GetNormalTS(input.uv, input.detailUV);
surface.normal = NormalTangentToWorld(surface.normal, input.normalWS, input.tangentWS);
如下圖,我們獲得更多法線細節
5 Optional Maps
不是所有的材質都需要我們增加的這些 Maps,如果只是不對這些屬性賦值,渲染時依然會用默認貼圖執行計算,造成性能損耗.我們可以通過加入一些 shader feature 來禁用某些 map
5.1 Input Config
獲取輸入數據時,現在總是需要傳兩個參數: baseUV 和 detailUV,為了簡化,我們將其封裝到一個結構體中,并調整相關函數
////////////////// common.hlsl
struct InputConfig
{float2 baseUV;float2 detailUV;
};InputConfig GetInputConfig(float2 baseUV, float2 detailUV = 0)
{InputConfig input;input.baseUV = baseUV;input.detailUV = detailUV;return input;
}
5.2 Optional Normal Maps
為材質定義新的 shader feature 并定義相關的 Toggle,將相關代碼放到宏中
/////////////// lit.shader
// 法線紋理開關
[Toggle(_NORMAL_MAP)]_NormalMapToggle("Normal Map", Float) = 0
// 法線紋理
[NoScaleOffset]_NormalMap("Normals", 2D) = "bump"{}// custom lit pass 定義 shader feature
#pragma shader_feature _RECEIVE_SHADOWS
#pragma shader_feature _NORMAL_MAP////////////////// litpass.hlsl
// 根據宏執行不同邏輯surface.position = input.positionWS;
#if defined(_NORMAL_MAP)// 獲取切線空間法線并變換到世界空間surface.normal = GetNormalTS(config);surface.normal = NormalTangentToWorld(surface.normal, input.normalWS, input.tangentWS);// shadow map 的 normal bias 依然需要用到世界空間中的頂點法線surface.interplotedNormal = input.normalWS;
#elsesurface.normal = input.normalWS;surface.interplotedNormal = input.normalWS;
#endifsurface.viewDirection = normalize(_WorldSpaceCameraPos - input.positionWS);
5.3 Optional Mask Map
同樣定義 shader feature 并定義 Toggle,基于該宏控制 mask 開關.根據 mask 開關修改相關邏輯.
////////////////// lit.shader
[Toggle(_MASK_MAP)]_MaskMapToggle("Mask Map", Float) = 0
[NoScaleOffset]_MODS("Mask(MODS)", 2D) = "white"{}// custom lit pass 定義 shader feature
#pragma shader_feature _NORMAL_MAP
#pragme shader_feature _MASK_MAP///////////////////// common.hlsl
struct InputConfig
{float2 baseUV;float2 detailUV;bool useMask; // 是否使用 MODS
};InputConfig GetInputConfig(float2 baseUV, float2 detailUV = 0)
{InputConfig input;input.baseUV = baseUV;input.detailUV = detailUV;input.useMask = false; // 默認不使用MODSreturn input;
}///////////////////// litpass.hlsl
float4 LitPassFragment(Varyings input) : SV_TARGET
{...InputConfig config = GetInputConfig(input.uv, input.detailUV);// 定義了宏,開啟 MODS
#if defined(_MASK_MAP)config.useMask = true;
#endif...
}///////////////////// litinput.hlsl
// 我們修改 GetMask 函數
float4 GetMask(InputConfig c)
{if(c.useMask) // 使用 maskreturn SAMPLE_TEXTURE2D(_MODS, sampler_BaseMap, baseUV);else // 不使用 maskreturn 1.0;
}// 其它相關邏輯,自行修改即可
5.4 Optional Detail
與 optional mask 一樣,定義 shader feature, 相關 Toggle 材質開光,為 InputConfig 定義新的 useDetail,并根據宏設置開關,然后在相關邏輯中根據開關執行不同的邏輯
////////////////// lit.shader
// 細節紋理開關
[Toggle(_DETAIL_MAP)]_DetailMapToggle("Detail Map", Float) = 0
// 細節紋理,默認灰色,值是 0.5,將不會有細節效果.大于會變亮,小于會變暗
_DetailMap("Dtails", 2D) = "linearGray" {}// customlitpass 中,定義 shader feature
#pragma shader_feature _MASK_MAP
#pragma shader_feature _DETAIL_MAP////////////////// common.hlsl
// 為 InputConfig 定義 useDetail 開關
struct InputConfig
{float2 baseUV;float2 detailUV;bool useMask; // 是否使用 MODSbool useDetail; // 是否使用細節紋理
};InputConfig GetInputConfig(float2 baseUV, float2 detailUV = 0)
{InputConfig input;input.baseUV = baseUV;input.detailUV = detailUV;input.useMask = false; // 默認不使用MODSinput.useDetail = false; // 默認不使用細節紋理return input;
}///////////////////// litpass.hlslstruct Varyings
{...
#if defined(_DETAIL_MAP) // 根據需要傳遞細節UVfloat2 detailUV : TEXCOORD1;
#endifGI_VARYINGS_DATAUNITY_VERTEX_INPUT_INSTANCE_ID
};Varyings LitPassVertex(Attributes input)
{...
#if defined(_DETAIL_MAP) // 根據需要計算細節UVoutput.detailUV = TransformDetailUV(input.uv);
#endifTRANSFER_GI_DATA(input, output);return output;
}
float4 LitPassFragment(Varyings input) : SV_TARGET
{....InputConfig config = GetInputConfig(input.uv);// 定義了宏,開啟 MODS
#if defined(_MASK_MAP)config.useMask = true;
#endif// 定義了宏,開啟 detail
#if defined(_DETAIL_MAP)config.useDetail = true;config.detailUV = input.detailUV;
#endif...
}////////////////// litinput.hlsl
// 相關函數,判斷如果沒有啟用細節紋理,直接返回// 采樣法線紋理
float3 GetNormalTS(InputConfig c)
{// 采樣法線紋理float4 map = SAMPLE_TEXTURE2D(_NormalMap, sampler_BaseMap, c.baseUV);float scale = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _NormalScale);float3 normal = DecodeNormal(map, scale);// 沒有細節紋理,直接返回if(c.useDetail == false)return normal;// 采樣細節法線紋理map = SAMPLE_TEXTURE2D(_DetailNormalMap, sampler_DetailMap, c.detailUV);scale = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _DetailNormalScale);float3 detail = DecodeNormal(map, scale);// 用 unity 提供的函數混合兩個法線,該函數繞著基礎法線旋轉細節法線normal = BlendNormalRNM(normal, detail);return normal;
}// 采樣 base map 并應用細節紋理
// detailUV 給默認參數0,避免沒有該參數時報錯(如 shadowCaster pass)
float4 GetBase(InputConfig c)
{float4 map = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, c.baseUV);float4 color = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);// 沒有細節紋理,直接返回if(c.useDetail == false)return map * color;// 應細節紋理數據影響 diffusefloat detail = GetDetail(c).r;detail *= _DetailAlbedo;// 由于我們是在線性空間,導致“變亮”的效果比“變暗”效果更強,在 gamma 空間更好// 但是我們用一種簡單的方式來近似:sqrtmap.rgb = sqrt(map.rgb);// 細節紋理,根據MODS紋理中的 D 進行 maskfloat mask = GetMask(c).b;// detail > 0 ? 根據 detail 的值,執行 map.rgb - 1 的插值// detail <= 0 ? 根據 detail 的值,執行 map.rgb - 0 的插值map.rgb = lerp(map.rgb, detail > 0 ? 1 : -1, abs(detail)* mask);return map * color;
}// 采樣光滑度
float GetSmoothness (InputConfig c)
{// 采樣獲得 MODS 中的光滑度float smoothness = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _Smoothness);smoothness *= GetMask(c).a;// 沒有細節紋理,直接返回if(c.useDetail == false)return smoothness;// 采樣獲得細節光滑度float detail = UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _DetailSmoothness);detail *= GetDetail(c).b;// 采樣獲得 maskfloat mask = GetMask(c).b;// 根據細節光滑度的符號,向 0 或 1 插值.插值控制參數應用細節光滑強度控制smoothness = lerp(smoothness, detail > 0 ? 1 : 0, abs(detail) * mask);return smoothness;
}