A-Frame PBR渲染实战
我一直在从事一个基于 A-Frame 的业余项目Streetmix3D,它创建了一个使用拖放生成器 Streetmix.net 设计的 3D 版本的街景。Streetmix 最初是一个Code for America 黑客马拉松项目,在过去的七年里一直作为开源项目维护!)
Streetmix3D 的视觉风格始于在Magicavoxel(左)中创建的体素风格化图形。然后,我尝试使用摄影测量技术制作的逼真的“自定义照片”样式纹理,并使用Substance Bitmap-to-Material(右)制作成简单的重复纹理。
虽然我喜欢“自定义照片风格”(右)近距离的外观,但“游戏就绪”纹理(中)从远处看起来更好。我最终使用来自 Textures.com 的“Game Ready”风格纹理找到了合理的质量输出。
虽然这些纹理改进比体素更详细,但它们只是从单个 JPEG 或 PNG 重复“图像映射”纹理。除了 A-Frame 和three.js的内置默认值外,它们不会展示现实生活中的照明效果。
1、超越 80/90 年代的画质
Streetmix3D 纹理的演变让我想起了从 90 年代初期的多边形 3D 风格游戏到 90 年代中后期的实时纹理映射的演变。当我还是个孩子的时候,我玩过很多赛车游戏,包括 PC MS-DOS 游戏 Stunts(实体 3d 多边形)和后来的极品飞车系列(图像贴图纹理和精灵)。
现在是时候尝试使用基于物理的渲染(PBR) 将 Streetmix3D 提升到 90 年代之后的新水平,并进入 3D 图形的新千年。
为了了解基于物理的渲染 (PBR) 和 A-Frame,我决定从一个简单的项目目标开始:在 A-Frame 中显示一个使用从第三方库购买的 PBR 纹理的平面。
2、PBR素材
我看到了使用 A-Frame 和 three.js 的 PBR 以及各种提升材质水平的纹理类型的最佳结果:
- 金属度
- 粗糙度
- 环境光遮蔽
- 图像贴图(又名漫反射贴图、底图)
- 法线贴图
在没有所有这些纹理的情况下,场景仍然可以渲染到不同程度的成功,具体取决于缺少哪些纹理以及场景的美学目标是什么。
我特别感兴趣的是更真实地描绘有轨电车和轻轨车道的铁路和沥青之间的相互作用。
我从 CGAxis找到了一个漂亮的“带电车轨道的道路”PBR 纹理——它只有 5 美元,并且拥有我需要的一切!起初缺少金属纹理,但在与支持人员交谈后,他们很快提供了一个。
PBR 材质包里面有什么?
你可能会看到不同的文件,具体取决于目标平台和引擎(Unity 和 Unreal 似乎是最常见的目标平台)。以下是我从 CG Axis“有轨电车的道路”中找到的文件:
- AO(环境遮挡)
- 漫反射(又名基础或图像映射)
- 光泽度
- 高度
- 金属度
- 普通的
- 反射(又名镜面反射)
- 粗糙度
为了开始理解这些不同的文件及其功能,我发现来自 Substance 的“PBR 指南第 2 部分”对于帮助我理解基本 PBR 工作流程的关键部分至关重要。强烈推荐周日早上的咖啡阅读!
3、为 three.js 准备纹理
A-Frame 基于使用“Metallic-Roughness”工作流程的three.js,并且纹理包中的某些文件仅适用于“Specular-Glossiness”工作流程,因此我们可以在此示例中丢弃 Glossiness 和 Reflection。
一些纹理图像只有一个颜色通道(灰度)。通过将 3 个灰度图像中的每一个映射到红色、绿色和蓝色的不同通道上,这些可以巧妙地组合成一个图像文件。实际上,我们必须将它们合并到一个文件中以供three.js 使用。
Three.js 期望 Ambient Occlusion、Roughness 和 Metalness 组合成一个图像文件作为 RGB 通道。你可以手动将其与 Photoshop 或 Substance Designer 之类的专业工具结合使用,但这需要付出努力,而且这些工具要花钱,所以我选择了免费的命令行解决方案!
为此,我使用了ImageMagick——一个多功能的命令行图像处理实用程序。使用你选择的方法安装它,我使用了MacOS 的homebrew命令行实用程序来安装:
brew install imagemagick
这将安装实用程序convert
,你可以使用该实用程序将文件组合在一起:
magick convert texture_ao.jpg texture_roughness.jpg texture_metalness.jpg -channel RGB -combine texture_combined.jpg
如果缺少一种纹理,你可以使用此方法将其替换为黑色,但这会影响最终输出的质量:
convert r.jpg g.jpg -background black -channel RG -combine combined.jpg
可以从ImageMagick 文档中了解有关 convert
命令的更多信息。
4、用 A-Frame 显示
我找不到一种方法来“开箱即用”地在 A-Frame 中定义所有这些 PBR 纹理元素,不过最终找到了一个很好的示例实现 — Stack Overflow 问题How to Apply Environment Map to glTF in A-Frame — 由Piotr Adam Milewski 编写。他还提供了一个 Glitch 项目的概念证明,该项目使用 环境图渲染“损坏的头盔”测试对象 :
我“重新混合”了 Piotr 的glitch并用平面几何体替换了 glTF 头盔。这对于 Streetmix3D 道路来说是实用的,并且在更一般的意义上允许在需要像 Blender 这样的建模软件将其应用于模型之前快速试验新的纹理。
我将 Piotr 的组件重命名为envmap
并创建了一个新组件pbr
,该组件将特定纹理应用于平面材质以支持 PBR。这两个组件的语法是:
<a-plane pbr envmap></a-plane>
该pbr
组件执行以下操作:
- 使用
THREE.TextureLoader()
从硬编码的 URL 加载纹理 - 遍历three.js 3d对象的所有材质
- 对于每种材质:将材质的贴图设置为加载的纹理:
el.material.map = textureDiffuse;
el.material.aoMap = textureCombined;
el.material.metalnessMap = textureCombined; // can't set w/ A-Frame
el.material.roughnessMap = textureCombined; // can't set w/ A-Frame
el.material.normalMap = textureNormal;
请注意,我们还必须“创建第二组 UV ”以使环境光遮蔽在 three.js 中正常工作。我找到了几行 JavaScript 来执行此操作(来自three.js 讨论组中的某处)并将它们放入pbr
组件中:
var geometry = mesh.geometry;
geometry.addAttribute( 'uv2', new THREE.BufferAttribute( geometry.attributes.uv.array, 2 ) );
效果如下:
如你所见,我们越来越接近真实效果了,但仍然存在一些问题:
- 沥青仍然太“闪亮”
- 铁轨看起来不是特别闪亮
- 沥青、轨道和轨道之间的凹槽看起来都一样,如果有更多的色差就好了
5、使用 three.js 进行改进
我在 three.js 讨论区发帖求助,收到以下提示来改善场景效果:
为什么我的输出看起来与示例渲染不同?PBR 实现总是会根据用于解释纹理的平台或引擎着色器而有所不同。“比较不同的渲染引擎并不总是像预期的那样工作,因为着色器的实现方式不同。即使使用相同的环境贴图,光照也会有很大差异。” 在你的目标平台上进行测试并从那里进行修改。
如果使用ACESFilmicToneMapping
,那么设置renderer.toneMappingExposure
低于默认值1
,例如0.75
或0.5
。
设置material.metalness = 1
,否则金属度贴图将不起作用 - 这就是为什么铁轨看起来不是特别闪亮或反光的原因。
6、使用 Photoshop 进行改进
我手动编辑了roughness
、metalness
和diffuse
纹理贴图以实现我想要的外观。
为了降低沥青的光泽度,我编辑了roughness
纹理贴图以显著增加沥青部分的亮度。我还编辑roughness
并metalness
确保“有轨电车槽轨”的正确外观。最后,我稍微加深了沥青的颜色,并使有轨电车轨道之间的“槽”几乎全黑,完全粗糙/没有金属感,看起来就像轨道之间的凹陷阴影。
这是之前/之后的纹理贴图:
我几乎忘记了最后一步——记得使用上面的 Image Magick 指令将这些新的粗糙度和金属度贴图重新组合成一个新的组合 AO/粗糙度/金属度纹理。
只是为了好玩,这里是旧的和新的样子:
最后,在结合了three.js 的改进以及纹理的手动编辑之后,输出看起来好多了!右边的那个非常接近我的“艺术目标”这个材料:)
7、进一步研究思路
- 如果允许
pbr
组件接受纹理文件的路径而不是在组件init
函数中硬编码会更好。 - 研究 PBR 纹理与简单图像贴图的性能差异,尤其是在 Quest 上
- 它在 Quest 中实际上是什么样子的?虽然好但滞后,性能较低?需要继续研究。
- 如何在不创建更多绘图调用的情况下将其扩展到当前长度之外?uv2 技巧还能用吗?是时候改用blender了吗?
原文链接:Experimenting with PBR textures using A-Frame and Three.js
BimAnt翻译整理,转载请标明出处