技术美术自学——PBR材质通道基础 常见贴图种类列举(求dalao轻喷)

PBR材质通道基础

非共用部分
(金属/粗糙度流程 Metal / Roughness Workflow)
基础颜色贴图 Base Color Map
金属纤维贴图 Metallic (Grayscale – Linear)
光泽度 Roughness (Grayscale - Linear)

非共用部分
(高光反射/光泽度流程 Specular / Glossiness Workflow)
漫反射 Diffuse Map
高光反射贴图 Specular
Specular还定义反射颜色
光泽度Glossines (Grayscale – Linear)

共用部分
环境光遮蔽贴图 Ambient Occlusion (AO)
环境光遮蔽贴图是指表面某点能获得多少环境中的光。
高度贴图 Height
高度贴图可以用于视差映射贴图,增强法线贴图和凹凸贴图的透明度和现实感。
法线贴图 Normal
法线贴图用于模仿表面细节。RGB贴图中,每一部分都对应着表面法线的X,Y,Z坐标。RGB贴图用于存储高分率辨至低分辨率模型的投射细节。在Substance工具中,可以烘培法线贴图或将高度贴图转换为法线贴图。

Glossines+Roughness=1本质上是一个东西反相一下就一样了

<think>我们正在讨论在PBR材质中如何编写Shader以应用光照贴图(Lightmap)。根据之前的上下文,我们知道Unity的标准PBR Shader已经内置支持光照贴图,但用户可能希望自定义Shader来实现这一功能。下面将分步骤解释如何在自定义的PBR Shader中应用光照贴图。 ### 关键概念 在Unity中,光照贴图数据通常通过预计算存储在纹理中,并在渲染时通过UV2(第二组纹理坐标)进行采样。光照贴图通常包含两部分信息: 1. **间接光照(漫反射部分)**:预计算的间接光颜色。 2. **阴影和遮挡信息**:例如环境光遮蔽(AO)等。 在PBR渲染中,我们需要将光照贴图的数据与直接光照(实时或烘焙)结合。根据引用[^1],间接光照是环境光照的重要组成部分,而光照贴图(Lightmap)是处理间接光漫反射的一种方法。 ### 步骤 #### 1. 准备光照贴图数据 Unity在烘焙光照贴图后,会将光照贴图存储在全局纹理中(如`unity_Lightmap`),并通过`unity_LightmapST`提供缩放和偏移。在Shader中,我们需要声明这些变量: ```hlsl uniform sampler2D unity_Lightmap; // 光照贴图纹理 uniform float4 unity_LightmapST; // 缩放和偏移 ``` #### 2. 在顶点着色器中准备UV2 光照贴图使用第二组纹理坐标(UV2)。在顶点着色器中,我们需要对UV2应用缩放和偏移: ```hlsl struct appdata { float4 vertex : POSITION; float2 texcoord1 : TEXCOORD1; // 第二组UV,用于光照贴图 // 其他顶点属性... }; struct v2f { float2 uv2 : TEXCOORD1; // 传递光照贴图UV // 其他变量... }; v2f vert(appdata v) { v2f o; // 转换顶点位置等操作... o.uv2 = v.texcoord1 * unity_LightmapST.xy + unity_LightmapST.zw; return o; } ``` #### 3. 在片元着色器中采样光照贴图 在片元着色器中,我们使用准备好的UV2采样光照贴图。注意,Unity的光照贴图存储的是编码后的间接光信息(通常是RGBM编码,即RGB乘以Alpha的某个倍数)。因此,我们需要解码: ```hlsl fixed3 decodeLightmap(fixed4 color) { // RGBM解码:将颜色从RGBM格式转换为线性空间 return color.rgb * (color.a * 8.0); // 通常乘以8,具体取决于烘焙设置 } fixed4 frag(v2f i) : SV_Target { // 采样光照贴图 fixed4 lightmapColor = tex2D(unity_Lightmap, i.uv2); fixed3 indirectDiffuse = decodeLightmap(lightmapColor); // 后续将indirectDiffuse用于PBR计算... } ``` #### 4. 将光照贴图数据整合到PBR光照模型中 PBR光照模型通常分为直接光照和间接光照两部分。光照贴图提供的是间接光照中的漫反射部分(以及可能的AO)。根据引用[^3],PBR的渲染方程可以表示为: $$ L_o = L_{\text{direct}} + L_{\text{indirect}} $$ 其中,$L_{\text{indirect}}$ 包括漫反射和镜面反射的间接光部分。光照贴图主要提供漫反射部分。 在标准PBR Shader中,间接漫反射部分通常乘以材质的反照率(Albedo)。因此,我们可以这样整合: ```hlsl // 计算直接光照(包括漫反射和高光反射) // 假设已经计算了直接光照的漫反射部分:directDiffuse 和高光部分:specular // 间接漫反射:光照贴图提供的间接光乘以材质的反照率 fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb; fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo; // 环境光(如果使用) fixed3 indirectLight = albedo * indirectDiffuse; // 光照贴图的间接光贡献 // 总漫反射 = 直接光照的漫反射 + 间接漫反射 fixed3 diffuseTotal = directDiffuse + indirectLight; // 总颜色 = 漫反射 + 高光 fixed3 finalColor = diffuseTotal + specular; ``` #### 5. 考虑环境光遮蔽(AO) 光照贴图通常还包含AO信息(在RGB通道中已包含,或者有单独的AO通道)。如果光照贴图包含AO,则通常将AO乘以间接光照部分: ```hlsl // 假设从光照贴图中提取AO(如果单独存储,则可能需要另一张纹理) // 这里假设光照贴图的间接光已经包含了AO,或者我们可以单独采样AO图 // 例如:fixed ao = ...; // 然后:indirectLight *= ao; ``` ### 完整示例代码(简化版) 下面是一个简化的自定义PBR Shader片段,展示如何应用光照贴图: ```hlsl Shader "Custom/PBRWithLightmap" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Metallic ("Metallic", Range(0,1)) = 0.0 _Glossiness ("Smoothness", Range(0,1)) = 0.5 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Pass { Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" #include "UnityPBSLighting.cginc" // 如果需要使用PBS函数 sampler2D _MainTex; float4 _MainTex_ST; half _Metallic; half _Glossiness; fixed4 _Color; // 光照贴图相关 sampler2D unity_Lightmap; float4 unity_LightmapST; struct appdata { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; float2 texcoord1 : TEXCOORD1; // 光照贴图UV float3 normal : NORMAL; }; struct v2f { float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; // 光照贴图UV float4 pos : SV_POSITION; float3 normal : TEXCOORD2; float3 worldPos : TEXCOORD3; }; v2f vert (appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.uv2 = v.texcoord1 * unity_LightmapST.xy + unity_LightmapST.zw; o.normal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } fixed3 decodeLightmap(fixed4 color) { // 简化解码:根据Unity的设置,可能是RGBM或双LDR编码 #if defined(UNITY_LIGHTMAP_RGBM_ENCODING) return color.rgb * (color.a * 8.0); #else return color.rgb * 2.0; // 对于双LDR编码 #endif } fixed4 frag (v2f i) : SV_Target { // 采样基础纹理 fixed4 albedo = tex2D(_MainTex, i.uv) * _Color; // 采样光照贴图并解码 fixed4 lightmap = tex2D(unity_Lightmap, i.uv2); fixed3 indirectDiffuse = decodeLightmap(lightmap); // 计算直接光照(这里简化,实际应使用PBR光照模型) float3 worldNormal = normalize(i.normal); float3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); float3 viewDir = normalize(_WorldSpaceCameraPos - i.worldPos); float3 halfVec = normalize(worldLightDir + viewDir); // 直接光照漫反射 float nl = max(0, dot(worldNormal, worldLightDir)); fixed3 directDiffuse = nl * _LightColor0.rgb; // 直接光照高光(Blinn-Phong简化示例,实际应用应使用Cook-Torrance等模型) float nh = max(0, dot(worldNormal, halfVec)); float specPower = exp2(_Glossiness * 10) + 1; fixed3 specular = pow(nh, specPower) * _LightColor0.rgb; // 间接光照部分:间接漫反射乘以反照率 fixed3 ambient = albedo.rgb * indirectDiffuse; // 总颜色 = 直接漫反射 * 反照率 + 间接漫反射 + 高光 fixed3 color = albedo.rgb * directDiffuse + ambient + specular; return fixed4(color, 1); } ENDCG } } FallBack "Diffuse" } ``` ### 注意事项 - **编码方式**:Unity的光照贴图可能使用RGBM或双LDR编码,解码方式需匹配项目设置。 - **PBR模型**:上述示例使用了简化的光照模型。实际PBR应使用基于物理的BRDF模型(如引用[^3]中的公式)。建议使用Unity内置的BRDF函数(在`UnityStandardBRDF.cginc`中)。 - **全局光照设置**:如果使用烘焙间接光,确保在Unity的Lighting窗口中正确设置烘焙模式(Baked Global Illumination)。 ### 总结 在自定义PBR Shader中应用光照贴图的关键步骤包括: 1. 声明光照贴图纹理和变换参数。 2. 在顶点着色器中转换UV2。 3. 在片元着色器中采样并解码光照贴图。 4. 将解码后的间接光数据(主要影响漫反射)整合到PBR光照计算中,通常与材质的反照率相乘。 通过以上步骤,即可在自定义Shader中实现光照贴图支持,提升静态场景的渲染效果和性能。 §§相关问题§§ 1. 如何在自定义Shader中处理光照贴图的编码和解码? 2. PBR材质中如何将光照贴图与实时全局光照(如Enlighten)结合? 3. 光照贴图在移动设备上有什么优化技巧? 4. 如何为自定义Shader添加光照贴图与Light Probes的混合支持?</think>### PBR Shader中光照贴图(Lightmap)的实现指南 在自定义PBR Shader中实现光照贴图支持需要理解Unity的光照贴图数据结构和PBR渲染方程的整合方式。以下是关键实现步骤(参考引用[^1][^3]): #### 1. 声明光照贴图属性和变量 ```hlsl // 在Properties块声明 [NoScaleOffset] _LightMap ("Lightmap", 2D) = "white" {} float4 _LightMap_ST; // 光照贴图缩放偏移 // 在CGPROGRAM中声明 sampler2D _LightMap; float4 _LightMap_ST; ``` #### 2. 顶点着色器:处理光照贴图UV ```hlsl struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float2 uv2 : TEXCOORD1; // 第二组UV用于光照贴图 }; struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float2 uv_lightmap : TEXCOORD1; // 光照贴图UV }; v2f vert(appdata v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); // 应用光照贴图的缩放偏移 o.uv_lightmap = v.uv2 * _LightMap_ST.xy + _LightMap_ST.zw; return o; } ``` #### 3. 片元着色器:整合光照贴图PBR方程 核心是将光照贴图数据分解为间接漫反射和环境光遮蔽(AO),再整合到渲染方程中: ```hlsl fixed4 frag(v2f i) : SV_Target { // 采样基础材质属性 fixed4 albedo = tex2D(_MainTex, i.uv); float metallic = tex2D(_MetallicMap, i.uv).r; float roughness = tex2D(_RoughnessMap, i.uv).r; // 采样光照贴图(RGB存储间接光,A通道存储AO) fixed4 lightmapData = tex2D(_LightMap, i.uv_lightmap); float3 indirectDiffuse = lightmapData.rgb; // 间接漫反射 float ambientOcclusion = lightmapData.a; // AO因子 // 计算直接光照部分(标准PBR计算) float3 N = normalize(i.worldNormal); float3 V = normalize(_WorldSpaceCameraPos - i.worldPos); float3 F0 = lerp(0.04, albedo.rgb, metallic); // 直接光贡献(以主方向光为例) float3 L = normalize(_WorldSpaceLightPos0.xyz); float3 H = normalize(V + L); float NdotL = max(dot(N, L), 0.0); // Cook-Torrance BRDF计算 float D = DistributionGGX(N, H, roughness); float G = GeometrySmith(N, V, L, roughness); float3 F = FresnelSchlick(max(dot(H, V), 0.0), F0); float3 kS = F; float3 kD = (1.0 - kS) * (1.0 - metallic); // 直接光照贡献 float3 directSpecular = (D * G * F) / (4.0 * max(dot(N, V), 0.001) * max(NdotL, 0.001)); float3 directDiffuse = kD * albedo.rgb / UNITY_PI; // 整合光照贴图(间接光部分) float3 indirectSpecular = ComputeIndirectSpecular(roughness, F, N, V); // IBL计算 float3 finalIndirect = (indirectDiffuse * kD * albedo.rgb + indirectSpecular) * ambientOcclusion; // 最终颜色合成 float3 finalColor = (directDiffuse + directSpecular) * _LightColor0.rgb * NdotL; finalColor += finalIndirect; return fixed4(finalColor, 1.0); } ``` #### 4. 关键数学原理 光照贴图PBR方程中的整合基于辐射度理论(参考引用[^1]): 间接光贡献可表示为: $$ L_{\text{indirect}} = \underbrace{k_D \frac{c}{\pi} \otimes E}_{\text{间接漫反射}} + \underbrace{\int_{\Omega} f_r L_i (\mathbf{n} \cdot \omega_i) d\omega_i}_{\text{间接高光}} $$ 其中: - $k_D = (1 - F)(1 - \text{metallic})$ 为漫反射系数 - $E$ 为光照贴图提供的辐照度(`indirectDiffuse`) - $f_r$ 为BRDF函数(参考引用[^3]的公式) #### 5. 优化技巧 1. **编码解码优化**:Unity光照贴图默认使用RGBM编码,需解码: ```hlsl float3 DecodeLightmap(fixed4 encoded) { return encoded.rgb * (encoded.a * 8.0); } ``` 2. **动态静态混合**:结合`UNITY_LIGHT_ATTENUATION`处理实时阴影 ```hlsl UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos); finalColor *= atten; // 应用实时阴影 ``` 3. **AO优化**:将AO应用于所有间接光照项 ```hlsl finalIndirect *= ambientOcclusion; ``` #### 注意事项 - 光照贴图仅适用于静态对象,动态对象需配合Light Probes - 烘焙时需确保UV2不重叠且展平良好 - 高光部分仍需实时计算或通过IBL补充(参考引用[^1]) - 在Shader中声明`#pragma multi_compile LIGHTMAP_ON LIGHTMAP_OFF`支持开关 > 示例代码基于Unity内置PBR原理简化实现,实际开发建议参考`UnityStandardBRDF.cginc`[^2]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值