纹理烘焙:原理及实现

news2024/11/18 17:31:32

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

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

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

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

  • 将应用了着色器的网格展开到 uv 坐标空间中。
  • 将展开的网格渲染为纹理。
  • 将纹理保存到磁盘。

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

1、展开网格

展开网格是将 3D 顶点坐标映射到其 2D uv 坐标空间的过程。 要在顶点着色器中执行此操作,我们可以将顶点的 x 和 y 分量设置为其 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>().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)是一种扩展形状的形态学方法,最初是为二值(黑色或白色)图像定义的:

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

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

这个实现可以在 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 项目中的代码。 以下是代码的摘要:

  • UVUnwrap.shader — 此着色器与你要烘焙的着色器重复,但顶点着色器已修改为在 UV 空间中渲染顶点。
  • Dilate.shader — 此着色器负责输出纹理的膨胀后处理。
  • ShaderBaker.cs — 将此脚本附加到相机,它负责将网格渲染为纹理。 它具有公共字段,你可以在其中放置要烘焙的对象、具有要烘焙的着色器的展开版本的材质 (UVUnwrap.shader) 以及具有扩张着色器的材质 (Dilate.shader)。 你还应该将 backgroundColor 属性设置为与着色器中的颜色不同的颜色。

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


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1270139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JMeter】菜单栏介绍

【菜单栏】 1. Tools 导入curl接口信息 作用&#xff1a;快速导入接口信息&#xff0c;响应头和缓存信息等 Log level临时修改日志等级 作用&#xff1a; 从勾选的level开始往更高等级level抓取log日志等级优先级&#xff1a; ALL < TRACE < DEBUG <INFO<WA…

汽车标定技术(十)--从CPU角度观察Overlay实现原理

目录 1.问题引入 2.功能概述 2.1 P1X 标定功能 2.2 MPC57xx标定功能 2.3 TC3xx标定功能 3.问题分析 3.1 英飞凌CPU子系统猜想 3.2 ARM内核CPU子系统分析 4.小结 1.问题引入 在分析瑞萨RH850-P1x系列、NXP S32K3系列和英飞凌TC3xx系列对标定测量功能的实现时&#xf…

JoySSL证书从申请到安装

为了保护网站和用户数据的安全&#xff0c;使用SSL证书是至关重要的一步。JoySSL是一种可靠的SSL证书提供商&#xff0c;它提供了简单易用的证书申请和安装流程。本文将详细介绍如何从申请到安装JoySSL证书的步骤。 一、申请JoySSL证书 1&#xff0c;访问JoySSL官方网站&#…

pycharm怎么同时打开2个项目?

pycharm怎么同时打开2个项目&#xff1f;当使用vue等前端的时候&#xff0c;后台也需要同时用pycharm打开操作&#xff0c;怎么用pycharm同时打开前后端呢&#xff1f; 当我们第一次用pycharm的时候&#xff0c;新建一个项目&#xff0c;习惯选择此窗口&#xff0c;而且勾选不再…

Linux:windows 和 Linux 之间文本格式转换

背景 在 Windows 上编辑的文件&#xff0c;放到 Linux 平台&#xff0c;有时会出现奇怪的问题&#xff0c;其中有一个是 ^M 引起的&#xff0c;例如这种错误&#xff1a; /bin/bash^M: bad interpreter 这个问题相信大家也碰到过&#xff0c;原因是 Windows 和 Linux 关于换行的…

2023.11.30 homework

兴趣最重要了&#xff0c;没兴趣不喜欢勉强带来的苦楚&#xff0c;并不能促使变好变优秀。 虽然我们的社会环境依旧很残酷&#xff0c;各种各样的硬性要求。

1、底层世界单片机

一、单片机简介 单片机是单片微型计算机的简称&#xff0c;MCU是Microcontroller的简称&#xff0c;也就是嵌入式微控制器。采用集成电路技术将具有数据处理能力的中央处理器CPU、随机存储器RAM、只读存储器ROM、定时器/计时器、多种I/O口和中断系统等功能集成到一块硅片上。可…

Jenkins 如何查看已经记录登录服务器的凭证密码

文章目录 一、背景描述二、解决方案一&#xff08;查看所有账号密码&#xff09;三、解决方案二&#xff08;查询指定账号密码&#xff09; 一、背景描述 在日常的开发过程中&#xff0c;有时候会出现忘记开发、测试服务器的登录密码的情况。此时恰巧 Jenkins 上记录了登录该主…

java后端自学总结

自学总结 MessageSource国际化接口总结 MessageSource国际化接口 今天第一次使用MessageSource接口,比较意外遇到了一些坑 messageSource是spring中的转换消息接口&#xff0c;提供了国际化信息的能力。MessageSource用于解析 消息&#xff0c;并支持消息的参数化和国际化。 S…

mount创建本地yum源报错no medium found on /dev/sr0

今天在为服务器配置本地yum源的时候系统报错&#xff1a; 原因&#xff1a;经检查发现是设置cd/dvd的问题。 只要在虚拟机设置——硬件——CD/DVD处&#xff0c;将CD/DVD设置为连接即可

mockito加junit gd 单元测试 笔记

目录 一、简介1.1 单元测试的特点1.2 mock类框架使用场景1.3 常用mock类框架1.3.1 mockito1.3.2 easymock1.3.3 powermock1.3.4 JMockit 二、mockito的单独使用2.1 mock对象与spy对象2.2 初始化mock/spy对象的方式2.3 参数匹配2.4 方法插桩2.5 InjectMocks注解的使用断言工具 三…

Apache Flink(三):Flink核心特性及应用场景

&#x1f3e1; 个人主页&#xff1a;IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 &#x1f6a9; 私聊博主&#xff1a;加入大数据技术讨论群聊&#xff0c;获取更多大数据资料。 &#x1f514; 博主个人B栈地址&#xff1a;豹哥教你大数据的个人空间-豹…

chapter10-homework-Java

第十章作业 Homework01知识点 Homework02知识点 Homework03知识点 Homework04知识点 Homework05知识点 Homework06Homework07Homework08 Homework01 分析执行结果。 public static void main(String[] args) {Car_ c new Car_();Car_ c1 new Car_(100);System.out.println(…

YOLOv5独家原创改进:自研独家创新FT_Conv,卷积高效结合傅里叶分数阶变换

💡💡💡本文自研创新改进:卷积如何有效地和频域结合,引入分数阶傅里叶变换(FrFT)和分数阶Gabor变换(FrGT),最终创新到YOLOv5。 使用方法:1)直接替换原来的C2f;2)放在backbone SPPF后使用;等 推荐指数:五星 在道路缺陷检测任务中,原始map为0.8,FT_Conv为0.82 …

FastApi接收不到Apifox发送的from-data字符串_解决方法

接收不到Apifox发送的from-data字符串_解决方法 问题描述解决方法弯路总结弯路描述纵观全局小结 问题描述 这里写了一个接口&#xff0c;功能是上传文件&#xff0c;接口参数是file文件和一个id字符串 gpt_router.post("/uploadfiles") async def create_upload_fi…

基于Java web的多功能游戏大厅系统的开发与实现

摘 要 目前&#xff0c;国内游戏市场上的网络游戏有许多种类&#xff0c;游戏在玩法上也越来越雷同&#xff0c;形式越来越单调。这种游戏性系统给玩家带来的成就感虽然是无穷的&#xff0c;但是也有随之而来的疲惫感&#xff0c;尤其是需要花费大量的时间和精力&#xff0c;这…

Ubuntu 22.04 LTS 上 安装 Redis

Ubuntu 22.04 LTS 上的Redis安装指南 Redis是一种开源的内存数据存储&#xff0c;可以用作数据库、缓存和消息代理等。本文将会介绍两种不同的安装方式&#xff0c;包括从源代码编译安装以及通过apt包管理器安装。 一、从源代码编译安装Redis 首先&#xff0c;我们需要下载最…

红队专题-fuzz技巧

红队专题 0x00 知己知彼常见 waf 收集SecureSphere (Imperva)西数WTS-WAF安全狗D盾腾讯云 waf阿里云云盾Web应用防火墙云锁UPUPW安全防护宝塔网站防火墙网防G01护卫神智创防火墙腾讯云玄武盾ISG 0x01 waf 绕过(过狗)姿势 举例SQL注入篇1.内联注释绕过2.等价替换法&#xff1a;3…

基于python 医院预约挂号系统-计算机毕业设计源码24802

摘 要 随着互联网时代的到来&#xff0c;同时计算机网络技术高速发展&#xff0c;网络管理运用也变得越来越广泛。因此&#xff0c;建立一个基于django 医院预约挂号系统 &#xff0c;会使&#xff1b;医院预约挂号系统的管理工作系统化、规范化&#xff0c;也会提高平台形象&a…