两种不同的PBR工作流介绍

article/2025/9/17 6:32:09

本文介绍两种常用的PBR材质工作流:金属/粗糙度工作流(Metal/Roughness)和镜面反射/光泽度工作流(Specular/Glossiness)。这两种工作流都可以用来制作一个支持PBR的材质,并用PBR渲染出逼真的效果,它们没有优劣之分,只是对同一种现象的不同实现。理解了这两种工作流,才可以更好的理解PBR着色器的一些参数及其使用背后的原理。

本文参考了Substance出版的The PBR Guide,原文连接在[1],如果想看中文版可以参考[2]。

PBR需要什么

无论是金属/粗糙度工作流还是镜面反射/光泽度工作流,他们的目的都是进行PBR渲染,获得尽可能真实的渲染结果。前文介绍了基于微表面理论的BRDF模型需要这样几个参数:

  • 粗糙度或者光滑度;
  • F 0 F_{0} F0,入射角为 0 ° 0\degree 0°时的反射光线radiance占入射光线radiance的比例;

除此之外,有一个默认参数: F 90 = 1 F_{90}=1 F90=1,入射角为 90 ° 90\degree 90°时的反射光线radiance占入射光线radiance的比例。

F 0 F_{0} F0的主要作用是,决定DiffuseColor和SpecularColor。
F 0 F_{0} F0的值与物体是否为金属有很大关系,要说明白这一点,先看一下金属、非金属的区别。

金属 vs. 非金属

金属是优良的导体,导电性和传热性都非常好。当光线打到金属表面,部分被反射,折射部分全部被吸收。由于金属对不同波长的光线的吸收程度不同,所以金属的反射光线通常是有颜色的,即 F 0 F_{0} F0的RGB三通道值不同。而这也是金属本身的颜色的来源。比如黄金会吸收高频的蓝色波段,因此黄金看起来是黄色的。

金属还有一个特性,那就是生锈(被腐蚀)。生锈的金属会失去金属光泽,变得类似非金属的材质。

对于非金属(或者称为介电质、非导体、绝缘体),导电性很差,折射进入材质的光线会被散射或者吸收,然后折射离开材质。而非金属反射的光线也比金属少,即非金属的 F 0 F_{0} F0更小,通常在0.02到0.05之间,绝大部分情况都不会超过0.04。

总结一下,金属的 F 0 F_{0} F0跟材质本身有关,非金属的 F 0 F_{0} F0约等于0.04。

金属/粗糙度工作流

mrworkflow

金属/粗糙度工作流(简写为M/R工作流)采用了三张贴图来表示PBR所需的参数:

  • baseColor,RGB贴图,表示非导体(电介质)的反照率颜色(Albedo)或金属的 F 0 F_{0} F0,它不应该含有任何光照信息;
  • metallic,灰度贴图,可以视为一个遮罩,表示金属成分的占比:1表示100%的纯金属,0表示100%的非金属,metallic贴图可以用于表示金属-非金属的混合状态;
  • roughness,灰度贴图,数值越大越粗糙;

M/R工作流的优劣:

  • 非金属的 F 0 F_{0} F0固定为0.04,无法调整;
  • 主流的工作流,用途广泛;

镜面反射/光泽度工作流

sgworkflow

镜面反射/光泽度工作流(简写为S/G工作流)

  • Diffuse,即Albedo,RGB贴图,表示非金属的Albedo,如果是金属,Diffuse贴图给出的值是纯黑(0);
  • Specular,RGB贴图,定义金属和非金属的 F 0 F_{0} F0
  • Glossiness,灰度贴图,数值越大月光滑,跟M/R工作流的roughness刚好相反;

S/G工作流的优劣:

  • 可以对金属、非金属的 F 0 F_{0} F0自由调整,但是这也非常容易做出违反能量守恒定律的材质;
  • 两张RGB贴图,对性能的要求会更高;

总结

下图展示了两个工作流的相同与不同。

workflows

通用的贴图

两个工作流有几个通用的纹理贴图,包括:

  • 环境光遮蔽/环境光吸收贴图Ambient Occlusion,灰度贴图,表示环境光对材质的影响,这个贴图会作为一个系数与环境光结合;
  • 高度贴图Height,用于displacement mapping效果,模拟表面细节;
  • 法向贴图Normal,用于normal mapping,模拟表面细节;

分辨率与纹素密度

这个话题是The PBR Guide[1]提出的,我在实践中还没有遇到过。这里根据The PBR Guide的介绍理解一下。

Resolution

这种异常的边界通常出现在金属/非金属交界的位置,出现的原因是两张纹理贴图(M/R工作流中的baseColor和Metallic,S/G工作流中的Diffuse和Specular)之间的微小的误差。比如在M/R工作流中,白边处的Metallic比较小,但是baseColor认为它还是金属,因此baseColor里包含的非常亮的金属反射值,导致了白边。而S/G工作流中,Diffuse贴图中因为原始金属并没有漫射色,所以呈现为黑色。

从上面的分析可以看出,出现这个问题主要原因是纹理贴图的精度不够,提高纹理的纹素密度,即纹理涂片的分辨率就可以解决这个问题。

辨析baseColor,Diffuse,Albedo,F0,DiffuseColor

这几个词在本文频繁出现,他们的意义非常接近,但是又有所不同,所以单独拎出来解释一下。

首先,baseColor和Diffuse被用在了两个工作流里面,他们的意义前面已经详细介绍过了,这两个词都只是一个对纹理的称呼,可以理解为在特定工作流里面的专有名词。

Albedo称为反射率本征图,表示物体本身的颜色,这个颜色是与光照无关的。

F 0 F_{0} F0不是一个颜色,而是一个介于 [ 0 , 1 ] [0,1] [0,1]的比例,表示反射光线radiance占入射光线radiance的比例。这个值主要是用在BRDF公式里面的菲涅尔方程里,通常这个值与albedo有关。

DiffuseColor更像是渲染里面的用词,表示入射光线或者出射光线的Diffuse分量,和SpecularColor相对。

实例

我在Github找到一个repo[3],支持了上面介绍的两个工作流,可以拿来参考一下。

这个repo的做法是,将S/G工作流的参数转为M/R工作流的参数,它提取出来的参数包括:

float perceptualRoughness;
float metallic;
vec4 baseColor;

具体的shader实现如下,细节不在赘述:

struct PBRInfo
{float NdotL;                  // cos angle between normal and light directionfloat NdotV;                  // cos angle between normal and view directionfloat NdotH;                  // cos angle between normal and half vectorfloat LdotH;                  // cos angle between light direction and half vectorfloat VdotH;                  // cos angle between view direction and half vectorfloat perceptualRoughness;    // roughness value, as authored by the model creator (input to shader)float metalness;              // metallic value at the surfacevec3 reflectance0;            // full reflectance color (normal incidence angle)vec3 reflectance90;           // reflectance color at grazing anglefloat alphaRoughness;         // roughness mapped to a more linear change in the roughness (proposed by [2])vec3 diffuseColor;            // color contribution from diffuse lightingvec3 specularColor;           // color contribution from specular lighting
};// Gets metallic factor from specular glossiness workflow inputs 
float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) {float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b);float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b);if (perceivedSpecular < c_MinRoughness) {return 0.0;}float a = c_MinRoughness;float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness;float c = c_MinRoughness - perceivedSpecular;float D = max(b * b - 4.0 * a * c, 0.0);return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);
}void main()
{float perceptualRoughness;float metallic;vec4 baseColor;vec3 f0 = vec3(0.04);if (material.alphaMask == 1.0f) {if (material.baseColorTextureSet > -1) {baseColor = SRGBtoLINEAR(texture(colorMap, inUV)) * material.baseColorFactor;} else {baseColor = material.baseColorFactor;}if (baseColor.a < material.alphaMaskCutoff) {discard;}}if (material.workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS) {// Metallic and Roughness material properties are packed together// In glTF, these factors can be specified by fixed scalar values// or from a metallic-roughness mapperceptualRoughness = material.roughnessFactor;metallic = material.metallicFactor;// Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.// This layout intentionally reserves the 'r' channel for (optional) occlusion map datavec4 mrSample = texture(physicalDescriptorMap, inUV);perceptualRoughness = mrSample.g * perceptualRoughness;metallic = mrSample.b * metallic;// The albedo may be defined from a base texture or a flat colorbaseColor = SRGBtoLINEAR(texture(colorMap, inUV)) * material.baseColorFactor;}if (material.workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS) {// Values from specular glossiness workflow are converted to metallic roughnessperceptualRoughness = 1.0 - texture(physicalDescriptorMap, inUV).a;const float epsilon = 1e-6;vec4 diffuse = SRGBtoLINEAR(texture(colorMap, inUV0));vec3 specular = SRGBtoLINEAR(texture(physicalDescriptorMap, inUV0)).rgb;float maxSpecular = max(max(specular.r, specular.g), specular.b);// Convert metallic value from specular glossiness inputsmetallic = convertMetallic(diffuse.rgb, specular, maxSpecular);vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * material.diffuseFactor.rgb;vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * material.specularFactor.rgb;baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a);}vec3 diffuseColor;diffuseColor = baseColor.rgb * (vec3(1.0) - f0);diffuseColor *= 1.0 - metallic;float alphaRoughness = perceptualRoughness * perceptualRoughness;vec3 specularColor = mix(f0, baseColor.rgb, metallic);// Compute reflectance.float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);// For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.// For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);vec3 specularEnvironmentR0 = specularColor.rgb;vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;vec3 n = (material.normalTextureSet > -1) ? getNormal() : normalize(inNormal);vec3 v = normalize(ubo.camPos - inWorldPos);    // Vector from surface point to cameravec3 l = normalize(uboParams.lightDir.xyz);     // Vector from surface point to lightvec3 h = normalize(l+v);                        // Half vector between both l and vvec3 reflection = -normalize(reflect(v, n));reflection.y *= -1.0f;float NdotL = clamp(dot(n, l), 0.001, 1.0);float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0);float NdotH = clamp(dot(n, h), 0.0, 1.0);float LdotH = clamp(dot(l, h), 0.0, 1.0);float VdotH = clamp(dot(v, h), 0.0, 1.0);PBRInfo pbrInputs = PBRInfo(NdotL,NdotV,NdotH,LdotH,VdotH,perceptualRoughness,metallic,specularEnvironmentR0,specularEnvironmentR90,alphaRoughness,diffuseColor,specularColor);// ...

最后利用纹理提取出来的材质信息以及光源信息构建结构体PBRInfo,用这个结构体的数据计算输出的颜色。

Reference

  1. The PBR Guide, part 1 and part 2
  2. ISUX译文 | The PBR Guide 基于物理的渲染指引,上和下
  3. github repo: Vulkan-glTF-PBR

http://chatgpt.dhexx.cn/article/9QIm8tN8.shtml

相关文章

4.3 PBR

1. 实验目的 熟悉PBR的应用场景掌握PBR的配置方法2. 实验拓扑 PBR实验拓扑如图4-8所示: 图4-8:PBR 3. 实验步骤 (1) IP地址的配置 R1的配置 <Huawei>system-view

PBR渲染(二)——PBR皮肤渲染

PBR皮肤渲染 在PBR基础框架的基础上实现皮肤的渲染&#xff0c;要根据皮肤的渲染特性来对基础框架进行扩充和修改&#xff0c;从而实现真实感的PBR皮肤渲染。皮肤一般具有以下渲染特性&#xff1a; 1.次表面散射&#xff08;SSS&#xff09; 2.BRDF高光&#xff08;Specular …

PBR 基础知识干货总结

&#xff08;1&#xff09;什么是PBR&#xff1f; 基于物理的渲染过程。 PBR是一种着色和渲染技术&#xff0c;用于更精确的描述光如何与物体表面互动。 PBR的优势&#xff1a; &#xff08;1&#xff09;方法论和算法基于精确的计算公式&#xff0c;免除创作表面的猜想过程。 …

PBR与Blinnphong解读

我们做光栅化模式的渲染都了解有两种比较常用的渲染方式&#xff0c;一个是blinnphong的渲染&#xff0c;一个是pbr的渲染。 blinnphong&#xff1a; blinnphong的渲染模式更多的是一种经验值模拟光照对物体的效果。所以他不是一个正确的能量守恒的渲染方式。 blinnphong的渲…

【基于物理的渲染(PBR)白皮书】(一) 开篇:PBR核心知识体系总结与概览

本文由浅墨_毛星云 出品&#xff0c;首发于知乎专栏&#xff0c;转载请注明出处 文章链接&#xff1a; https://zhuanlan.zhihu.com/p/53086060 先放出PBR知识体系的架构图&#xff1a; 图很大&#xff0c;建议下载到本地放大查看。原图下载地址&#xff1a; https://raw.gi…

什么是PBR?

一、什么是PBR&#xff1f; 基于物理渲染以前的渲染是在模仿灯光的外观现在是在模仿光的实际行为试图形看起来更真实 二、PBR组成部分 灯光属性&#xff1a;直接照明、间接照明、直接高光、间接高光、阴影、环境光闭塞表面属性&#xff1a;基础色、法线、高光、粗糙度、金属度…

PBR流程介绍和模型规范

&#xff08;1&#xff09;基本流程&#xff1a; &#xff08;1&#xff09;制作中模&#xff1a; 基础模型&#xff1a; 指的是中模&#xff01;&#xff01; 中模导出为.obj 格式&#xff08;跟.fbx格式相比&#xff1a;不会出现模型大小的缩放&#xff09; 高模&#xff1a…

PBR材质:基本原理和简单制作

概要&#xff1a;介绍PBR材质的基本原理以及制作一个简单的PBR材质 参考资料&#xff1a;BASIC THEORY OF PHYSICALLY-BASED RENDERING 如有问题&#xff0c;多多指正。 侵删。 1.PBR是什么&#xff0c;光线的基本原理。 PBR即Physically-based rendering&#xff0c;基于物理…

什么是PBR?pbr入门基础干货

&#xff08;1&#xff09;什么是PBR&#xff1f; 基于物理的渲染过程。 PBR是一种着色和渲染技术&#xff0c;用于更精确的描述光如何与物体表面互动。 PBR的优势&#xff1a; &#xff08;1&#xff09;方法论和算法基于精确的计算公式&#xff0c;免除创作表面的猜想过程…

PBR——概述、基于物理的材质

PBR概述 PBR&#xff0c;即Physically Based Rendering&#xff0c;主要分为基于物理的材质、基于物理的光照和基于物理的相机三个部分&#xff0c;目前来说对大家最为所熟知的是基于物理的材质部分。本文围绕基于物理的材质进行相关介绍。 什么是PBR 其实最早听说PBR这玩意…

PBR基础理论通俗解释

PBR基础理论通俗解释 今天给大家介绍PBR的基础理论, 不会涉及比较深的具体算法, 算是一篇扫盲的文章, 尽量尝试说人话, 让大家能够对PBR有基本的了解. 什么是PBR? PBR是基于物理的渲染(Physically Based Rendering), 也就是说通过模拟物理世界的方式来渲染. 既然有基于物理…

基于物理的渲染PBR(一):pbr的基础理论和推导

初始PBR 最近刚接触pbr不久&#xff0c;我搜寻了许多文章进行阅读并了解后发现&#xff0c;pbr涉及到的知识点繁琐且不容易理解&#xff0c;所以想在博客上给自己记录并总结一下&#xff0c;方便以后回顾并加深记忆。 这里首先借用知乎上的大佬毛星云关于pbr所涉及到的知识要…

策略路由(PBR)

1、基本概念 PBR (Policy-Based Routing&#xff0c;策略路由): PBR使得网络设备不仅能够基于报文的目的IP地址进行数据转发&#xff0c;更能基于其他元素进行数据转发&#xff0c;例如源IP地址、源MAc地址、目的MAc地址、源端口号、目的端口号、VLAN-ID等等。 用户还可以使用A…

技术美术知识学习_04:PBR的个人理解

一、什么是PBR&#xff1f; PBR&#xff08;Physically Based Rendering&#xff09;&#xff0c;中文翻译为基于物理的渲染。 PBR是一种渲染方式&#xff0c;是使用基于物理原理和微平面理论的光照模型&#xff0c;以及使用从现实中测量的表面参数来准确表示真实世界材质的渲…

图灵直播|《第一行代码》作者郭霖在线Coding,今晚八点,给你留位!

图源来自Pexels “我们为什么需要 Kotlin&#xff1f;答&#xff1a;消失的 Getter 和 Setter、又见空指针、Smart Cast、打日志、再见Utils、晚安ButterKnife……” 相信很多人初识Kotlin&#xff0c;都是基于谷歌技术大牛 Steve Yegge的一篇文章《为什么说 Kotlin 比你们用的…

撸了郭霖大神写的Framework源码笔记,offer拿到手软

前不久听我一个字节的朋友说了一个神转折的故事。 一名大专生,异常执着地向他们公司投简历,屡战屡败,屡败屡战,前前后后向字节跳动投了九次简历。 你猜后面怎么着?还真让他成功了,第九次居然拿到了offer! 看到这里,不过是一个普普通通的励志故事吧,一个菜鸡凭借自己…

(郭霖)Android图片加载框架最全解析(一),Glide的基本用法

转载请注明出处&#xff1a;http://blog.csdn.net/guolin_blog/article/details/53759439 本文同步发表于我的微信公众号&#xff0c;扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注&#xff0c;每天都有文章更新。 现在Android上的图片加载框架非常成熟&#xff0c;从最早…

图灵专访:郭霖的成长之路

各位小伙伴们大家早上好&#xff0c;最近这段时间我真的是快要忙疯了&#xff0c;好多件事情同时在做&#xff0c;实在抽不出时间写原创文章。 正巧想到前段时间和图灵做了一次专访&#xff0c;记录了一些我的成长经历。这篇专访姑且也可以算是一篇原创吧&#xff0c;因为里面的…

第一行代码-第二版(郭霖著)笔记十一(Material Design)

目录 一、什么是Material Design 二、Toolbar 三、滑动菜单 1.DrawerLayout 2.NavigationView 四、悬浮按钮和可交互提示 1.FloatingActionButton&#xff1a;悬浮按钮 2.Snackbar&#xff1a;提示工具 3.CoordinatorLayout&#xff1a;加强版FrameLayout 五、卡片式…

第一行代码-第二版(郭霖著)笔记二(Activity)

目录 一、Activity的用法 1.Activity 2.Toast 3.菜单 4.销毁一个活动 二、Intent 1.使用显示Intent 2.使用隐式Intent 3.更多隐式Intent的用法 4.向下一个活动传递数据 5.返回数据给上一个活动 三、活动的生命周期 1.返回栈 2.活动的四种状态 3.活动的生存期 4…