3D模型纹理集合并【Python|C#】

使用 Substance Painter 时,将模型的各个部分分成不同的纹理集非常有用。 这可以帮助遮罩,或者只是保持层栈干净。 不幸的是,Painter 无法将多个纹理集中的所有贴图导出为单个图集,即使在创建单独对象的 UV 时考虑到了这一点。 显然,在游戏设计领域,最好将加载到内存中的大纹理数量保持在最低限度,因此,如果可以的话,我们当然不应该为游戏中的单个对象使用五个纹理集。 公平地说,首先可能有一百万种方法可以防止这个问题。 我怀疑,通过一些巧妙的 ID 屏蔽,你可以相当轻松地仅使用一组纹理来对整个模型进行纹理处理。 但事后解决这个问题应该不会那么烦人。

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

组合这些纹理是一个相对简单的过程:你只需将 UV 岛与任何背景颜色物质分开,然后将它们叠加在一起。 分离图像的哪些部分被 UV 岛覆盖可能听起来很困难,但不用担心,我们实际上不需要从模型中获取 UV。 我们只需要获取与 Substance 用于该贴图的背景填充颜色不同的所有像素,然后从给定纹理组(即所有法线贴图)中的所有贴图复制所有这些像素并将它们粘贴到一个贴图中 ,即最终纹理。 这是一个简单的过程,但手动完成仍然相当乏味,特别是当你需要频繁迭代纹理时。

所以我写了一个 python 脚本来帮我做这件事。 但这(非常)慢,所以我还编写了另一个更快的 C# 脚本。 这不仅是因为 C# 一般来说是一种更快的语言,而且还因为我对它进行了多线程处理,因此它可以立即组合我需要的所有纹理组。

我已经提供了我的代码,欢迎您使用,但请注意,我在 Substance Painter 中使用 Unity HDRP 工作流程; 如果你希望它能够在任何其他用例中正常工作,将需要进行一些更改。

using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.Threading;public class Atlaser
{public static void Main(string[] args){/*Here I'm creating a thread to combine the maps for each texture set component.You'll want to add or remove threads depending on what kinds of maps you're using in your workflow.You'll also need to change the filename endings to match those of your textures.Finally, change the colors to match the default background color of the maps. Remember that C# formats colors as ARGB (alpha, red, green, blue) for some reason.*/Console.WriteLine("Opening Threads...");Thread normal = new Thread(Atlaser.Atlas);Thread baseColor = new Thread(Atlaser.Atlas);Thread Mask = new Thread(Atlaser.Atlas);Thread Emissive = new Thread(Atlaser.Atlas);normal.Start(new FileAndCol("_Normal.png", Color.FromArgb(255, 127, 127, 255)));baseColor.Start(new FileAndCol("_BaseMap.png", Color.FromArgb(255, 0, 0, 0)));Mask.Start(new FileAndCol("_MaskMap.png", Color.FromArgb(178, 0, 0, 0)));Emissive.Start(new FileAndCol("_Emissive.png", Color.FromArgb(255, 0, 0, 0)));}/*Atlas() grabs all the files in the current directory with the provided file extension, creates a new texture of the same size filled with the baseline/background color, then moves the deltas from the opened maps over to the new texture, and finally saves the new texture to the disk.*/private static void Atlas(object? fcobj){if(fcobj == null)return;FileAndCol fc = fcobj as FileAndCol;var watch = new Stopwatch();watch.Start();Console.WriteLine("Beginning thread for " + fc.fileExtension + " images.");List<Bitmap> maps = new List<Bitmap>();string[] files = Directory.GetFiles(Directory.GetCurrentDirectory(), "*" + fc.fileExtension);string o = "Found " + files.Length + " maps : \n ";foreach (string file in files){o += file + "\n ";maps.Add(new Bitmap(file));}Console.WriteLine(o);Bitmap finalMap = new Bitmap(maps[0].Width, maps[0].Height);using (Graphics g = Graphics.FromImage(finalMap))using (SolidBrush brush = new SolidBrush(Color.FromArgb(fc.col.ToArgb()))){Rectangle rect = new Rectangle(0, 0, finalMap.Width, finalMap.Height);g.FillRectangle(brush, rect);}//Console.WriteLine("Iterating through maps...");for (int i = 0; i < maps.Count; i++){Console.WriteLine(" Beginning map: " + files[i] + " (" + fc.fileExtension + ") " + (i+1) + " of " + maps.Count);for (int x = 0; x < finalMap.Width; x++){for (int y = 0; y < finalMap.Height; y++){Color px = maps[i].GetPixel(x, y);if (!px.Equals(fc.col)){finalMap.SetPixel(x, y, px);}}}}finalMap.Save(fc.fileExtension.Remove(fc.fileExtension.Length - 4) + "_Combined.png", ImageFormat.Png);watch.Stop();Console.WriteLine(" **" + fc.fileExtension + " thread completed in " + watch.ElapsedMilliseconds + " ms");}
}
public class FileAndCol
{public string fileExtension;public Color col;public FileAndCol(string s, Color c){fileExtension = s;col = c;}
}

总而言之,这并不是一个非常复杂的脚本。 如果你是 .NET 新手(或者只在 Unity 内部使用过 C#),那么运行它非常简单:

  • 在 Visual Studio 或 VS Code(或 Rider 或任何其他 .NET IDE)中创建新的 C# 控制台应用程序
  • 创建一个新的空 C# 脚本
  • 将上面的代码复制并粘贴到那里
  • 进行处理特定纹理所需的任何编辑,保存文件
  • 将单独的纹理贴图移动到与脚本相同的目录中
  • 通过按顶部的绿色大播放按钮 (Visual Studio) 运行脚本,或者在终端中打开目录并输入以下命令: dotnet run

一旦确定其按照你想要的方式工作,甚至可以将项目构建为可执行文件,你只需将其拖放到 Substance Painter 导出文件夹中即可。

构建一个小型 GUI 或 CLI 来精确定制哪些贴图到图集以及如何映射也很方便,但它对我来说已经足够好了,所以这就是我留下的地方。 希望你学到了一些东西,或者至少获得了一个有用的工具!

顺便说一句,如果你对我编写的 Python 版本感到好奇,这里是:

import glob, os
from PIL import Image, ImageColor
​
textures = []
​
for img in glob.glob("*BaseMap.png"):textures.append(Image.open(img))print('Found ' + img)
​
neutralColor = (0,0,0,1)
​
finalImg = Image.new('RGBA', textures[0].size, neutralColor)
​
print('Iterating through maps...')
i = 0
for img in textures:print('  Beginning map: ' + img.filename)for x in range(img.size[0]):#print("     Current col: ", x)for y in range(img.size[1]):px = img.getpixel((x,y))if px != neutralColor:finalImg.putpixel((x,y), px)i += 1finalImg.show()
finalImg.save('combinedBlackMaskTexture.png')

这是一个更简单的脚本。 它使用 Pillow 来创建图像,并遵循相同的基本算法:循环遍历每个图像的每个像素,并复制与给定基线颜色不匹配的部分。

我只得到了一个纹理集的图集,并认为 Python 对于我的需求来说有点太慢了。 事实上,绘制单个纹理组所需的时间比 C# 脚本长 2.5 倍。 由于单个集合中可以有四个或更多纹理组,并且 Python 中的并行处理可能有点困难,因此我认为切换到另一种语言是一个好主意。 尽管如此,编写这个脚本要容易得多。


原文链接:合并多个纹理集 - BimAnt

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringCloud实用-OpenFeign整合okHttp

文章目录 前言正文一、OkHttpFeignConfiguration 的启用1.1 分析配置类1.2 得出结论&#xff0c;需要增加配置1.3 调试 二、OkHttpFeignLoadBalancerConfiguration 的启用2.1 分析配置类2.2 得出结论2.3 测试 附录附1&#xff1a;本系列文章链接附2&#xff1a;OkHttpClient 增…

Spring Security 6.x 系列(6)—— 显式设置和修改登录态信息

一、前言 此篇是对上篇 Spring Security 6.x 系列&#xff08;5&#xff09;—— Servlet 认证体系结构介绍 中4.9章节显式调用SecurityContextRepository#saveContext进行详解分析。 二、设置和修改登录态 2.1 登录态存储形式 使用Spring Security框架&#xff0c;认证成功…

六、Lua运算符

文章目录 一、Lua 运算符&#xff08;一&#xff09;算术运算符&#xff08;二&#xff09;关系运算符&#xff08;三&#xff09;逻辑运算符&#xff08;四&#xff09;其他运算符 二、运算符优先级 一、Lua 运算符 运算符是一个特殊的符号&#xff0c;用于告诉解释器执行特定…

MSB3541 Files 的值“<<<<<<< HEAD”无效。路径中具有非法字符。

MSB3541 Files 的值“<<<<<<< HEAD”无效。路径中具有非法字符。 一般来说出现这个问题是因为使用git版本控制工具合并代码出现了问题&#xff0c;想要解决也很简单。 如图点击错误后定位到文件&#xff0c;发现也没有什么问题。 根据错误后边的提示&a…

P9242 [蓝桥杯 2023 省 B] 接龙数列(dp+最长接龙序列+分类)

1. 计算0~9为结尾的最长子串长度 2. 对于每个数字&#xff0c;比较其开头可连接子串长度1 与 原来以其末位为末尾的子串长度 3. 更新以其末位为末尾的子串长度 #include<iostream> #include<string.h>using namespace std;// 相当于记录…

如何运行C/C++程序

一、在线运行C/C 码曰 - 让代码在云端多飞一会&#xff1a;这是一个支持C/C&#xff0c;Java&#xff0c;Python等多种语言的在线编程&#xff0c;编译运行&#xff0c;粘贴分享的平台。你可以在这里输入你的代码&#xff0c;点击运行按钮&#xff0c;就可以看到输出结果。你也…

leetcode 283. 移动零

代码&#xff1a; class Solution {public void swap(int[] nums,int m,int n){int tmpnums[m];nums[m]nums[n];nums[n]tmp;}public void moveZeroes(int[] nums) {int cur0;int dest-1;int nnums.length;for(;cur<n;cur){if(nums[cur]!0){dest;swap(nums,cur,dest);}}} } …

一些好用的12款前端小插件

1. cropper.js Cropper.js 2.0 是一系列用于图像裁剪的 Web 组件。 官网地址&#xff1a;https://fengyuanchen.github.io/cropperjs/v2/zh/ 2. Vditor Vditor是一款浏览器端的 Markdown 编辑器&#xff0c;支持所见即所得、即时渲染&#xff08;类似 Typora&#xff09;和分…

【Python深度学习第二版】学习笔记之——什么是深度学习

机器学习是将输入&#xff08;比如图像&#xff09;映射到目标&#xff08;比如标签“猫”&#xff09;的过程。 这一过程是通过观察许多输入和目标的示例来完成的。 深度神经网络通过一系列简单的数据变换&#xff08;层&#xff09;来实现这种输入到目标的映射&#xff0c;这…

C++ day42背包理论基础01 + 滚动数组

背包问题的重中之重是01背包 01背包 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些物品装入背包里物品价值总和最大。 每一件物品其实只有两个状态&#xff0c;取或者不…

抖音视频如何无水印下载,怎么批量保存主页所有视频没水印?

现在最火的短视频平台莫过于抖音&#xff0c;当我们刷到一个视频想下载下来怎么办&#xff1f;我们知道可以通过保存到相册的方式下载&#xff0c;但用这种方法下载的视频带有水印&#xff0c;而且有些视频不能保存到相册&#xff08;这是视频作者设置了禁止下载&#xff09;。…

Breadcrumb面包屑(antd-design组件库)简单用法和自定义分隔符

1.Breadcrumb面包屑 显示当前页面在系统层级结构中的位置&#xff0c;并能向上返回。 2.何时使用 当系统拥有超过两级以上的层级结构时&#xff1b; 当需要告知用户『你在哪里』时&#xff1b; 当需要向上导航的功能时。 组件代码来自&#xff1a; 面包屑 Breadcrumb - Ant Des…

【代码随想录】算法训练计划35

贪心 1、860. 柠檬水找零 题目&#xff1a; 输入&#xff1a;bills [5,5,5,10,20] 输出&#xff1a;true 思路&#xff1a; 模拟大法 func lemonadeChange(bills []int) bool {//贪心&#xff0c;代码一刷&#xff0c; 感觉要用到hashmap&#xff0c;也不用five, ten : 0…

开源语音大语言模型来了!阿里基于Qwen-Chat提出Qwen-Audio!

论文链接&#xff1a;https://arxiv.org/pdf/2311.07919.pdf 开源代码&#xff1a;https://github.com/QwenLM/Qwen-Audio 引言 大型语言模型&#xff08;LLMs&#xff09;由于其良好的知识保留能力、复杂的推理和解决问题能力&#xff0c;在通用人工智能&#xff08;AGI&am…

Unity SRP 管线【第三讲:URP 光照】

3.2.3 以前属于Shader部分&#xff0c;Shader部分不进行讲解。 这里只涉及Unity内部管线的设置问题。 文章目录 3.2.3 向GPU发送灯光数据设置光源数据设置主光源设置额外点光源 Shader中的数据 3.2.3 向GPU发送灯光数据 在UniversalRenderPipeline.cs > RenderSingleCamera…

WPF中DataGrid解析

效果如图&#xff1a; 代码如下&#xff1a; <DataGrid Grid.Row"1" x:Name"dataGrid" ItemsSource"{Binding DataList}" AutoGenerateColumns"False"SelectedItem"{Binding SelectedItem,UpdateSourceTriggerPropertyChange…

网络安全小白自学

一、网络安全应该怎么学&#xff1f; 1.计算机基础需要过关 这一步跟网安关系暂时不大&#xff0c;是进入it行业每个人都必须掌握的基础能力。 计算机网络计算机操作系统算法与数据架构数据库 Tips:不用非要钻研至非常精通&#xff0c;可以与学习其他课程同步进行。 2.渗透技…

【UE】绘制抛物线并投射物体

效果 步骤 1. 先新建父类为Actor的蓝图&#xff0c;这里命名为“BP_发射物” 打开“BP_发射物”&#xff0c;添加一个球形的静态网格体和一个发射物移动组件 2. 新建一个父类为角色的蓝图&#xff0c;这里命名为“BP_绘制抛物线” 打开“BP_绘制抛物线” 我们希望可以通过控制…

芯片技术前沿:了解构现代集成电路的设计与制造

芯片技术前沿&#xff1a;解构现代集成电路的设计与制造 摘要&#xff1a;本文将深入探讨芯片技术的最新进展&#xff0c;重点关注集成电路的设计与制造。我们将带领读者了解芯片设计的基本流程&#xff0c;包括电路分析、版图设计和验证等步骤&#xff0c;并介绍当前主流的制…

LeetCode Hot100 236.二叉树的最近公共祖先

题目&#xff1a; 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节…