拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从Blinn-Phong到PBR:URP管线下的物理渲染实战指南

从Blinn-Phong到PBR:URP管线下的物理渲染实战指南

1. 从零搞懂PBR:为什么它成了现代渲染的标配

1.1 传统光照模型的痛点在哪里

刚接触渲染那会儿,我用的一直是Blinn-Phong那套光照模型。说实话,做出来的东西乍一看还行,但总觉得哪里不对劲——金属看起来像塑料,塑料看起来又像纸片,整个场景缺乏一种“真实感”。后来才明白,问题出在传统模型对光与物质交互的简化过于粗暴。

Blinn-Phong把光照拆成环境光、漫反射、高光三个部分,每一项都是拍脑袋的经验公式。比如高光项,它只关心半程向量和法线的夹角,完全不考虑物体表面的粗糙程度对高光形状的影响。结果就是,无论你渲染的是一块打磨过的铜板还是一块粗糙的砖头,高光的衰减曲线几乎一样。这显然不符合物理直觉。

更麻烦的是,传统模型里漫反射和高光反射的能量不守恒。你把高光调亮,物体总反射能量就凭空增加了,看起来就像物体自己在发光。美术同学调参数的时候经常陷入“调了高光丢了漫反射,调了漫反射高光又不对”的死循环。

PBR(Physically Based Rendering,基于物理的渲染)要解决的就是这些问题。它的核心思路很简单:用物理学的规律来约束光照计算,让渲染结果在数学上就是能量守恒的、在不同光照环境下都是一致的。

1.2 PBR的核心思想:能量守恒与微表面理论

PBR的基石是两个东西:能量守恒和微表面理论。

能量守恒好理解——一束光打到物体表面,要么被反射,要么被吸收,要么透射过去。反射的能量加上吸收的能量加上透射的能量,必须等于入射能量。在渲染里我们主要关心反射部分,所以漫反射和高光反射加起来不能超过入射光能量。这就从数学上杜绝了“越调越亮”的问题。

微表面理论稍微抽象一点。它假设物体表面在微观尺度上是由无数微小的镜面组成的,这些微表面的朝向各不相同。当光线打到表面时,实际上是在和这些微表面交互。表面越粗糙,微表面朝向越混乱,光线反射方向就越分散,高光就越模糊;表面越光滑,微表面朝向越一致,光线反射方向就越集中,高光就越锐利。

这个理论的美妙之处在于,它用一个统一的框架解释了从镜面到粗糙表面的所有反射行为。你只需要调整一个粗糙度参数,就能平滑地在各种材质之间过渡。

1.3 URP管线下的PBR有什么不同

Unity的URP(Universal Render Pipeline)管线对PBR做了一些工程上的取舍。和内置管线相比,URP更注重性能和跨平台兼容性,所以在PBR的实现上做了一些简化。

最明显的区别是URP默认使用金属度工作流(Metallic Workflow),而不是高光工作流(Specular Workflow)。金属度工作流只需要一张Albedo贴图、一张Metallic贴图和一张Smoothness贴图,美术资源的管理成本更低。高光工作流虽然控制更精细,但需要额外的Specular贴图,在移动端上显存开销更大。

另一个区别是URP的BRDF实现。内置管线用的是标准的Cook-Torrance BRDF,而URP在移动端平台上会切换到一种近似算法,牺牲一点精度换取性能。这个切换是自动的,你不需要手动干预,但了解这一点对排查渲染差异很有帮助。

注意:如果你在编辑器里看到的材质效果和真机上不一致,很可能就是BRDF近似算法导致的。这种情况下不要怀疑自己的Shader写错了,先检查一下目标平台的Shader变体。

2. PBR核心参数与贴图通道的实战解析

2.1 Albedo贴图:不只是“颜色”

很多人把Albedo贴图简单理解为物体的“颜色”,这个理解不够准确。Albedo在物理上应该叫基础反射率(Base Reflectance),它描述的是物体表面对入射光的漫反射比例。

这里有一个新手最容易踩的坑:Albedo贴图里不应该包含任何光照信息。我见过不少美术同学直接把照片贴上去当Albedo用,结果照片里自带的阴影和高光全被烘进了基础色里,渲染出来的物体怎么看怎么脏。正确的做法是,Albedo贴图应该是均匀光照下拍摄的、没有明显阴影和高光的“平光”图像。

还有一个经验法则:现实世界中的物体,Albedo值很少超过0.8,也很少低于0.04。煤炭大概在0.04左右,新鲜雪大概在0.8左右。如果你在贴图里用了纯白(1.0)或纯黑(0.0),那基本可以断定是不对的。纯白意味着物体反射了所有入射光,这在现实中几乎不存在;纯黑意味着物体吸收了所有光,那你就什么都看不见了。

在URP里,Albedo贴图的sRGB选项要勾上,因为它是颜色信息,需要从sRGB空间转换到线性空间参与光照计算。

2.2 Metallic与Smoothness:金属度工作流的双核心

金属度贴图(Metallic Map)用单通道存储,通常放在R通道或者A通道里。它的值只有0和1两种“极端”情况——要么是纯金属,要么是纯非金属。中间值只用于金属边缘的过渡区域,比如生锈的铁皮。

为什么金属度不能随便取中间值?因为金属和非金属的光学行为有本质区别。金属的折射率是复数,光进入金属后会被自由电子吸收,所以金属没有漫反射,只有带颜色的高光反射。非金属则相反,漫反射是主要表现,高光反射很弱且通常是无色的。你如果把金属度设成0.5,渲染器会同时计算金属和非金属的反射,结果就是既不金属也不非金属,看起来像塑料上镀了一层奇怪的膜。

Smoothness贴图控制的是表面的光滑程度,它和粗糙度(Roughness)是反过来的:Smoothness越高,表面越光滑,高光越锐利;Smoothness越低,表面越粗糙,高光越分散。在URP的Lit Shader里,Smoothness通常存在Metallic贴图的A通道里,这样可以节省一张贴图。

这里有一个实操技巧:Smoothness的数值不要全图均匀。现实中很少有完全光滑或完全粗糙的表面,大多数物体表面都有微小的磨损、灰尘、指纹。你可以在Smoothness贴图里画一些噪点或者手印,渲染出来的效果会真实很多。我试过在一个金属杯子上加了一点指纹的Smoothness变化,整个杯子的质感立刻从“CG感”变成了“照片感”。

2.3 Normal Map:让平面“长”出细节

法线贴图(Normal Map)是PBR里性价比最高的贴图。它通过改变表面法线的方向,让光照计算以为表面有凹凸,而实际上几何体还是平的。

法线贴图有两种格式:切线空间(Tangent Space)和对象空间(Object Space)。URP的Lit Shader默认使用切线空间法线贴图,因为切线空间法线贴图可以复用在不同朝向的表面上。对象空间法线贴图虽然计算量稍小,但只能用于特定形状的模型,通用性差。

导入法线贴图时有一个关键设置:Texture Type要选Normal Map,这样Unity会自动把贴图从sRGB转换到线性空间,并且根据平台做适当的压缩。如果你忘了改这个设置,法线贴图会被当成普通颜色贴图处理,渲染出来的凹凸方向全是错的。

还有一个常见问题:法线贴图的Y轴方向。不同软件生成的切线空间法线贴图,Y轴方向可能不同。Unity使用的是OpenGL风格的Y轴向上,而有些软件(比如早期的3ds Max)生成的是DirectX风格的Y轴向下。如果你发现法线贴图渲染出来的凹凸是反的,可以在法线贴图的导入设置里勾选“Flip Green Channel”来修正。

提示:法线贴图的强度不要调得太高。很多新手为了让凹凸更明显,把Normal Scale拉到2.0甚至3.0,结果渲染出来的表面像被锤子砸过一样。一般来说,Normal Scale在0.5到1.5之间比较合理,具体要看你的法线贴图烘焙时的强度设置。

2.4 AO贴图:环境光遮蔽的妙用

AO(Ambient Occlusion)贴图描述的是表面某一点被周围几何体遮挡了多少环境光。在URP的Lit Shader里,AO贴图会影响间接光的强度,让缝隙和角落变暗,增加模型的体积感。

AO贴图在URP里通常存在Occlusion贴图的G通道里。它的值从0到1,0表示完全被遮挡,1表示完全暴露。和Smoothness一样,AO也不应该全图均匀,否则就失去了意义。

一个容易被忽略的细节:AO贴图只影响间接光,不影响直接光。也就是说,如果一束平行光直接照到表面上,AO贴图不会让这个表面变暗。这是符合物理的,因为直接光没有被遮挡。但很多美术同学期望AO能像阴影一样工作,这就产生了误解。

2.5 参数速查表

贴图通道sRGB典型值范围常见错误
AlbedoRGB是0.04-0.8包含光照信息
MetallicR否0或1取中间值
SmoothnessA否0.1-0.9全图均匀
NormalRGB否-1到1未设Normal Map类型
AOG否0.3-1.0期望影响直接光

3. 手写一个URP下的PBR Shader

3.1 Shader骨架搭建

URP下写Shader和内置管线最大的区别是,你需要引入URP的Shader库,并且使用URP提供的函数来获取光照信息。下面是一个最简PBR Shader的骨架:

Shader "Custom/PBR_Lite" { Properties { _BaseMap ("Albedo", 2D) = "white" {} _BaseColor ("Base Color", Color) = (1,1,1,1) _MetallicMap ("Metallic", 2D) = "white" {} _Metallic ("Metallic", Range(0,1)) = 0.0 _Smoothness ("Smoothness", Range(0,1)) = 0.5 _NormalMap ("Normal", 2D) = "bump" {} _NormalScale ("Normal Scale", Range(0,2)) = 1.0 _OcclusionMap ("Occlusion", 2D) = "white" {} _OcclusionStrength ("Occlusion Strength", Range(0,1)) = 1.0 } SubShader { Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" // ... ENDHLSL } } }

这个骨架里,Core.hlsl提供了基础的类型定义和工具函数,Lighting.hlsl提供了URP的光照计算函数。注意RenderPipeline标签必须设为UniversalPipeline,否则URP不会识别这个Shader。

3.2 顶点着色器:从模型空间到世界空间

顶点着色器的任务是把顶点从模型空间变换到裁剪空间,同时计算世界空间的位置、法线和切线:

struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float4 tangentOS : TANGENT; float2 uv : TEXCOORD0; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; float3 positionWS : TEXCOORD1; float3 normalWS : TEXCOORD2; float4 tangentWS : TEXCOORD3; }; Varyings vert(Attributes input) { Varyings output; VertexPositionInputs posInputs = GetVertexPositionInputs(input.positionOS.xyz); VertexNormalInputs normInputs = GetVertexNormalInputs(input.normalOS, input.tangentOS); output.positionCS = posInputs.positionCS; output.positionWS = posInputs.positionWS; output.normalWS = normInputs.normalWS; output.tangentWS = float4(normInputs.tangentWS, input.tangentOS.w); output.uv = input.uv; return output; }

这里用了URP提供的GetVertexPositionInputs和GetVertexNormalInputs函数,它们会自动处理不同平台上的坐标空间差异。tangentOS.w存储的是副切线的方向符号,用于构建切线空间到世界空间的变换矩阵。

3.3 片元着色器:BRDF计算的核心

片元着色器是PBR的核心,它要完成以下步骤:采样贴图、构建切线空间、计算BRDF、累加光照。

half4 frag(Varyings input) : SV_Target { // 采样贴图 half4 baseMap = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv); half3 albedo = baseMap.rgb * _BaseColor.rgb; half metallic = SAMPLE_TEXTURE2D(_MetallicMap, sampler_MetallicMap, input.uv).r * _Metallic; half smoothness = SAMPLE_TEXTURE2D(_MetallicMap, sampler_MetallicMap, input.uv).a * _Smoothness; half roughness = 1.0 - smoothness; half3 normalTS = UnpackNormalScale( SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, input.uv), _NormalScale); half ao = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, input.uv).g; ao = lerp(1.0, ao, _OcclusionStrength); // 构建切线空间到世界空间的矩阵 half3 normalWS = normalize(input.normalWS); half3 tangentWS = normalize(input.tangentWS.xyz); half3 bitangentWS = cross(normalWS, tangentWS) * input.tangentWS.w; half3x3 TBN = half3x3(tangentWS, bitangentWS, normalWS); half3 N = normalize(mul(normalTS, TBN)); // 计算视线方向 half3 V = normalize(GetWorldSpaceViewDir(input.positionWS)); // 获取主光源 Light mainLight = GetMainLight(); half3 L = normalize(mainLight.direction); half3 H = normalize(L + V); // 计算BRDF half NdotL = saturate(dot(N, L)); half NdotV = saturate(dot(N, V)); half NdotH = saturate(dot(N, H)); half VdotH = saturate(dot(V, H)); // 漫反射项 half3 diffuseColor = albedo * (1.0 - metallic); half3 diffuse = diffuseColor / PI; // 高光项(Cook-Torrance BRDF) half3 F0 = lerp(0.04, albedo, metallic); half3 F = F0 + (1.0 - F0) * pow(1.0 - VdotH, 5.0); half D = D_GGX(NdotH, roughness); half G = G_Smith(NdotV, NdotL, roughness); half3 specular = (D * G * F) / (4.0 * NdotV * NdotL + 0.001); // 累加直接光 half3 directLight = (diffuse + specular) * mainLight.color * NdotL; // 环境光(简化版) half3 ambient = SampleSH(N) * diffuseColor * ao; half3 finalColor = directLight + ambient; return half4(finalColor, 1.0); }

这段代码里,D_GGX和G_Smith是BRDF的两个关键函数,下面会详细解释。

3.4 BRDF三大核心函数拆解

法线分布函数D:描述微表面法线朝向的统计分布。GGX分布是目前最常用的,因为它的“长尾”特性更符合真实材质的高光衰减:

half D_GGX(half NdotH, half roughness) { half a = roughness * roughness; half a2 = a * a; half NdotH2 = NdotH * NdotH; half denom = NdotH2 * (a2 - 1.0) + 1.0; return a2 / (PI * denom * denom); }

几何遮蔽函数G:描述微表面之间的相互遮挡。Smith-GGX是最常用的组合,它把几何遮蔽拆成视线方向和光线方向两部分:

half G_Smith(half NdotV, half NdotL, half roughness) { half k = (roughness + 1.0) * (roughness + 1.0) / 8.0; half Gv = NdotV / (NdotV * (1.0 - k) + k); half Gl = NdotL / (NdotL * (1.0 - k) + k); return Gv * Gl; }

菲涅尔项F:描述不同角度下反射率的变化。Schlick近似是最常用的:

half3 F_Schlick(half3 F0, half VdotH) { return F0 + (1.0 - F0) * pow(1.0 - VdotH, 5.0); }

这三个函数乘在一起,再除以4 * NdotV * NdotL做归一化,就得到了Cook-Torrance BRDF的高光项。

注意:分母里的4 * NdotV * NdotL在NdotV或NdotL接近0时会趋向于0,导致除零错误。所以我在代码里加了0.001的偏移量。这是一个常见的工程处理,虽然会引入微小误差,但避免了渲染伪影。

4. 性能优化与常见问题排查

4.1 移动端PBR的性能取舍

在移动端上跑完整的Cook-Torrance BRDF,性能压力不小。URP提供了一些优化选项:

关闭高光抗锯齿:在URP Asset里可以设置MSAA级别,移动端建议关闭或设为2x。PBR的高光边缘本身就有一定的抗锯齿效果,MSAA的收益不如传统光照模型明显。

使用近似BRDF:URP在移动端会自动切换到一种简化的BRDF,用更少的指令数完成计算。你可以在Shader里通过#if defined(SHADER_API_GLES)之类的宏来手动控制。

降低贴图精度:Metallic和Smoothness贴图可以用半精度甚至更低,因为它们只影响光照计算的中间结果,对最终颜色的精度要求不高。Albedo贴图建议保持全精度,因为它是基础色。

减少实时光源数量:URP的Forward渲染路径下,每个光源都会增加一次Pass。移动端建议只保留一个主光源,其他光源用烘焙光照或者顶点光照代替。

4.2 常见渲染问题速查表

问题现象可能原因排查方法
材质看起来像塑料Metallic值不对检查Metallic贴图是否全黑
高光位置偏移法线贴图Y轴反了勾选Flip Green Channel
表面出现条纹法线贴图压缩过度提高贴图导入质量
暗部死黑AO贴图过暗调整AO强度或提亮贴图
高光闪烁Smoothness过高降低Smoothness或加噪点
颜色偏灰Albedo贴图sRGB未勾选检查导入设置
金属边缘发白菲涅尔项计算错误检查F0的lerp参数

4.3 实操心得:那些文档里不会写的东西

关于Smoothness的“反直觉”:很多人以为Smoothness越高越好,实际上大多数日常材质的Smoothness在0.3到0.6之间。只有抛光金属、玻璃、水面才需要0.8以上。我见过一个场景,所有材质的Smoothness都设成0.9,结果整个场景像被油泡过一样,高光糊成一片。

关于法线贴图的“强度”:法线贴图的强度不是越大越好。如果你发现法线贴图渲染出来的凹凸感太强,不要急着调Normal Scale,先检查一下法线贴图本身是不是烘焙得太夸张了。好的法线贴图应该在Normal Scale为1.0时就有合适的凹凸感。

关于AO贴图的“滥用”:AO贴图不是阴影贴图,它只能模拟环境光的遮蔽。如果你想要物体投射阴影,应该用Shadow Map或者烘焙阴影。我见过有人在AO贴图里画了投影,结果物体一动,投影也跟着动,穿帮得很明显。

关于金属度的“边界情况”:金属和非金属之间的过渡区域,Metallic值可以取中间值,但这个区域应该非常窄。如果你发现金属边缘有一圈奇怪的灰色,很可能是Metallic贴图的过渡区域太宽了。建议在Photoshop里用曲线工具把Metallic贴图的对比度拉高,让过渡更锐利。

关于环境光的“补光”:PBR场景里,环境光对最终效果的影响非常大。如果环境光太暗,金属会看起来像黑色塑料;如果环境光太亮,整个场景会显得很平。我通常会用一张HDRI贴图作为环境光源,这样金属表面能反射出周围的环境,真实感会强很多。在URP里,你可以在Lighting设置里指定Skybox材质,或者用Reflection Probe来提供环境反射。

4.4 进阶方向:从PBR到IBL

直接光的PBR只是第一步。真正让PBR出效果的是IBL(Image Based Lighting,基于图像的光照)。IBL用一张环境贴图来模拟周围环境对物体的光照贡献,包括漫反射的辐照度贴图和镜面反射的预过滤贴图。

在URP里,IBL的漫反射部分通过SampleSH函数采样球谐函数实现,镜面反射部分通过采样Reflection Probe实现。如果你想要更高质量的环境反射,可以自己生成预过滤的Cubemap,然后在Shader里手动采样。

IBL的难点在于预计算。漫反射辐照度贴图需要对环境贴图做卷积,镜面反射预过滤贴图需要按不同粗糙度做多级模糊。这些计算在运行时做太慢,通常是在编辑器里烘焙好存成贴图。Unity的Reflection Probe会自动做这些预计算,但如果你想要更精细的控制,就需要自己写烘焙工具了。

5. 从理论到落地:一个完整的材质调优案例

5.1 案例背景:一把旧铁锤

光说理论太干,我拿一个实际案例来串一遍。假设我们要渲染一把旧铁锤,锤头是生锈的铁,锤柄是磨损的木头。

锤头的材质分析:铁是金属,但生锈后表面覆盖了一层氧化铁,氧化铁是非金属。所以锤头的Metallic贴图不能全白,锈蚀区域要画成黑色。Smoothness方面,铁本身比较光滑,但锈蚀区域很粗糙,所以Smoothness贴图要有明显的明暗变化。Albedo方面,铁是银灰色,锈是红棕色,两者要有清晰的边界。

锤柄的材质分析:木头是非金属,Metallic全黑。Smoothness方面,木头本身比较粗糙,但握持区域因为长期摩擦会变得光滑一些,所以Smoothness贴图要在握持区域提亮。Albedo方面,木头有纹理,需要一张带木纹的贴图。法线贴图要体现木纹的凹凸和磨损的划痕。

5.2 贴图制作的具体步骤

第一步,在Substance Painter里导入模型,烘焙法线、AO、曲率等基础贴图。曲率贴图特别有用,它可以自动识别模型的边缘和缝隙,方便你画磨损效果。

第二步,用Fill Layer铺底色。锤头铺铁灰色,锤柄铺木色。然后添加Mask,用曲率贴图生成边缘磨损的Mask,在边缘处露出底层的金属色。

第三步,画锈蚀。用Noise贴图生成锈蚀的分布,然后用Levels调整对比度,让锈蚀集中在凹陷处。锈蚀区域的Metallic要刷成0,Smoothness要刷低,Albedo要刷成红棕色。

第四步,画木纹。用Wood纹理生成器生成木纹,然后叠加Noise做磨损。握持区域的Smoothness要提高,模拟长期摩擦的效果。

第五步,导出贴图。Albedo、Metallic(R通道)、Smoothness(A通道)、Normal、AO(G通道)分别导出,然后在Unity里组装成URP的Lit Shader需要的格式。

5.3 在URP里组装材质

在Unity里新建一个Material,Shader选Universal Render Pipeline/Lit。然后把导出的贴图拖到对应的槽位:

  • Base Map:Albedo贴图,勾选sRGB
  • Metallic Map:Metallic和Smoothness的合并贴图,不勾选sRGB
  • Normal Map:法线贴图,Texture Type设为Normal Map
  • Occlusion Map:AO贴图,不勾选sRGB

然后调整参数:Metallic设为1.0(因为贴图里已经画了锈蚀区域),Smoothness设为1.0(同理),Normal Scale设为1.0,Occlusion Strength设为1.0。

5.4 光照环境搭建

材质调好后,光照环境也很关键。我通常会用一张室内HDRI作为环境光源,强度设为1.0左右。然后加一个方向光作为主光源,强度设为1.5,角度调成侧逆光,这样能突出锤头的轮廓和锈蚀的质感。

如果发现锤头的锈蚀区域太暗,可以适当提高环境光强度,或者在锈蚀区域补一个点光源。但要注意,补光不要破坏整体的光照一致性,否则会显得很假。

提示:调材质的时候,建议把Game视图的曝光固定住,不要用自动曝光。自动曝光会让你的判断失准,你以为是材质调好了,其实是曝光在帮你“作弊”。在URP里可以在Volume里添加一个Exposure组件,把Mode设为Fixed,然后手动设置EV值。

5.5 最终效果验证

验证PBR材质是否调好,有一个简单的方法:把材质放到不同的光照环境下看。如果它在强光下不过曝,在弱光下不死黑,在暖光下不偏色,在冷光下不发灰,那基本就合格了。

另一个方法是和参考照片对比。找一张真实铁锤的照片,把你的渲染结果和照片并排放在一起,看颜色、高光、粗糙度是否接近。如果差距明显,就回到贴图里继续调整。

我个人的经验是,PBR材质调优是一个迭代的过程,不要指望一次就能调好。通常需要来回调整三四次,每次都在不同的光照环境下验证,才能得到一个比较满意的结果。

6. 写在最后:一些零散但有用的经验

PBR这东西,理论看起来复杂,但真正上手之后会发现,核心就那么几个参数。难的是对真实世界的观察——你得知道真实的金属是什么样,真实的木头是什么样,真实的光照是什么样。我建议做PBR的时候,手边常备一些参考照片,随时对比。

还有一个容易被忽略的点:PBR不是万能的。有些风格化的效果,比如卡通渲染、手绘风格,用PBR反而做不出来。PBR适合写实风格,如果你要做风格化渲染,可能需要换一套光照模型,比如liltoon那种卡通渲染方案。

最后说一个调试技巧:如果你怀疑BRDF计算有问题,可以把最终颜色直接输出成NdotL或者NdotV,看看这些中间量的分布是否合理。这个方法帮我排查过好几次法线贴图的问题——如果NdotL的分布看起来像噪声,那多半是法线贴图的方向错了。

PBR的学习曲线确实有点陡,但一旦理解了能量守恒和微表面理论,后面的事情就顺理成章了。我当初也是从Blinn-Phong一步步过渡过来的,中间踩了不少坑,但每次搞明白一个概念,都有一种“原来如此”的快感。希望这篇笔记能帮你少走一些弯路。

返回列表