本文介绍: 是计算机图形学常见技术用于着色器细节传输纹理中。如果你的着色器计算量很大,但会产生静态结果例如,这非常有用。复杂噪音。NSDT在线工具推荐:许多建模应用程序都有此功能。如果你有从 Blender/Houdini/Maya 传输资源管道,那么你可能需要在那里进行纹理烘焙。但是,如果你主要使用 Unity 进行工作,那么本教程适合你。通过这种技术,你可以有效地保存实时操作自定义着色器结果,并在任何其他具有默认着色器应用程序使用生成纹理(Sketchfab?等)。

纹理烘焙计算机图形学常见技术用于着色器细节传输纹理中。 如果你的着色器计算量很大,但会产生静态结果例如,这非常有用。 复杂噪音

NSDT在线工具推荐: Three.js AI纹理开发包 – YOLO合成数据生成器 – GLTF/GLB在线编辑 – 3D模型格式在线转换 – 可编程3D场景编辑器

许多建模应用程序都有此功能。 如果你有从 Blender/Houdini/Maya 传输资源管道,那么你可能需要在那里进行纹理烘焙。 但是,如果你主要使用 Unity 进行工作,那么本教程适合你。 通过这种技术,你可以有效地保存实时操作的自定义着色器结果,并在任何其他具有默认着色器的应用程序使用生成的纹理(Sketchfab?等)。

首先,让我们列出问题开始:

仅供参考,如果你只是想查看一些代码(就像我一直做的那样),就在这里

1、展开网格

展开网格是将 3D 顶点坐标映射到其 2D uv 坐标空间过程。 要在顶点着色器中执行此操作,我们可以顶点xy 分量设置为其 uv 坐标。 将 z 分量设置为 0 将使我们在 XY 平面中 z=0 处留下展开网格

尝试此操作,请复制烘焙的着色器,并使用以下行调整顶点位置

v.vertex = float4(v.uv.xy, 0.0, 1.0);

用于原始网格这个新着色器将向你显示展开网格

展开的unity球体,应用了 fbm 噪声着色器

解开的树,应用了 fbm 噪声着色器

2、如何这个展开的网格渲染为纹理?

正交相机! 对于正交投影没有深度线索。 无论距相机距离如何渲染图像中的对象尺寸都保持不变。 这将使我们能够绘制完美的 2D UV 网格而不失真。

透视与正交投影

要开始烘焙过程我们需要将一个脚本附加到相机处理烘焙逻辑我们将其附加到相机,因为我们想要在完成渲染场景使用 Graphics.DrawMeshNow 绘制网格。 为此,我们需要访问 OnPostRender 函数

示例项目中,我在 ShaderBaker.cs 中的“M”键按下事件上发生以下代码(因为将事物绑定随机键非常简单,哈哈)。

Mesh M = objectToBake.GetComponent<MeshFilter&gt;().mesh;
// create a new render texture 
RenderTexture rt = RenderTexture.GetTemporary(width, height);

// set the active render target 
Graphics.SetRenderTarget(rt);
// save the last camera state
GL.PushMatrix(); 
// load an orthographic camera 
GL.LoadOrtho(); 
// set the active material to be the unwrapping material we made earlier
uvMaterial.SetPass(0);
// draw the mesh, the matrix does not matter because we are not using it for any projection in the shader
Graphics.DrawMeshNow(M, Matrix4x4.identity);
// ** save to disk  here **
// reset state
Graphics.SetRenderTarget(null);
RenderTexture.ReleaseTemporary(rt);
GL.PopMatrix();

关于上面发生的事情有很多话要说,但如何说取决于你对图形管道的熟悉程度……我想这里最有趣的两件事是加载正交相机激活适当的 UV 展开材质 使用 SetPass 进行渲染。

UV 展开着色器将与你要烘焙的着色器相同,除了顶点着色器中的两条线更改之外。 前面提到的第一个变化是将顶点坐标重新映射到 UV 空间

v.vertex = float4(v.uv.xy, 0.0, 1.0);

其次,我们需要将标准行:

o.vertex = UnityObjectToClipPos(v.vertex)

替换为:

o.vertex = mul(UNITY_MATRIX_P, v.vertex);

UnityObjectToClipPos 本质上是将顶点与其世界矩阵和投影矩阵相乘。 我们不关心该对象的世界矩阵,因为我们现在渲染的 UV 坐标应始终以 0 为中心。但是,我们仍然需要将 UV 坐标投影到屏幕空间,这只需使用投影矩阵 UNITY_MATRIX_P即可

一件需要注意的事情是,Unity 文档指出 DrawMeshNow 不包含光照信息,因此这意味着此实现仅适用于无光照着色器。 如果想要烘焙一个完全集成光照和阴影的着色器,你必须考虑使用 DrawMesh 函数

3、将纹理保存磁盘

此时,我们应该在渲染纹理中拥有烘焙的着色器纹理贴图。 只需要谷歌一下就能解决这个问题,因为这部分过程常见。 这些步骤涉及将 RenderTexture 转换为 Texture2D ,然后结果编码为 PNG。 此代码可以在 ShaderBaker.cs 中找到。

WOW! 现在我们有一个 .png 纹理代表选择的未光照着色器。 然而,像往常一样,总会出现一些根本性的问题。 如果你尝试在任何默认 Unity 材质上使用新纹理作为 _MainTex,可能会在 UV 岛边缘看到黑色伪像:

精确 UV 接缝周围的黑色造成的伪影

这是因为纹理是使用精确的 UV 坐标烘焙的,并且没有考虑在边缘采样纹理时发生的采样方差。 为了解决这个问题,我发现其他建模软件在新烘焙的纹理上实现了“岛边界扩展”,这只是扩展边界的混合操作。

我决定通过输出纹理上实施第二次扩张来实现“UV岛边界扩展”。

4、解决边缘伪影问题

膨胀dilation)是一种扩展形状形态学方法,最初是为二值(黑色或白色)图像定义的:

算法采用结构元素(在本例中为 3×3 图块),并通过将图块置于像素中心迭代图像的每个像素。 如果图块中有白色元素,我们会将图块转换为白色。

要将其扩展到彩色图像,我们需要一个不同因素转换图块。 我决定让结构元素像素颜色与预定义背景颜色的差异进行操作。 如果结构元素中有一个像素颜色值与背景颜色足够远,我们可以转换给定像素

这个实现可以在 Dilate.shader 中找到。 为了将其合并到我们现有的设置中,我们将以下几行添加到烘焙代码中:

...
Graphics.DrawMeshNow(M, Matrix4x4.identity);
Graphics.SetRenderTarget(null);
...
// create a second render target 
RenderTexture rt2 = RenderTexture.GetTemporary(width, height);
// use the dilate shader on our first render target, output to rt2
Graphics.Blit(rt, rt2, dilateMat);
// save rt2 to png
SaveTexture(rt2, objectToBake.name);
// reset 
RenderTexture.ReleaseTemporary(rt);
RenderTexture.ReleaseTemporary(rt2);
GL.PopMatrix();

膨胀前和膨胀

这很好地扩展了纹理! 需要注意的是,这种膨胀实现仅在纹理的背景颜色与着色器的颜色不同时才有效。 我在 ShaderBaker.cs 中添加一个属性来设置背景颜色:)

另外,接缝处仍然有非常小的伪影,我还没有弄清楚那里发生了什么

5、结束

由于本文中的代码相当断断续续,我强烈建议你查看 github 项目中的代码。 以下是代码摘要

应该就是全部了~~对我来说是一次有趣的探索,希望它能帮助开发者:)


原文链接纹理烘焙原理及实现 – BimAnt

原文地址:https://blog.csdn.net/shebao3333/article/details/134688863

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_34826.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注