3D网格顶点颜色转纹理

顶点颜色是一种将颜色信息直接添加到网格顶点的简单方法。这通常是生成式 3D 模型(如 InstantMesh)生成网格的方式。但是,大多数应用程序更喜欢 UV 映射的纹理网格。

本教程介绍了一种将顶点颜色网格转换为 UV 映射的纹理网格的快速解决方案。其中包括用于快速获得结果的简短版本和用于深入演练的详细版本。

NSDT工具推荐: Three.js AI纹理开发包 - YOLO合成数据生成器 - GLTF/GLB在线编辑 - 3D模型格式在线转换 - 可编程3D场景编辑器 - REVIT导出3D模型插件 - 3D模型语义搜索引擎 - AI模型在线查看 - Three.js虚拟轴心开发包 - 3D模型在线减面 - STL模型在线切割 

1、简短版本

安装 InstantTexture 库以轻松转换。这是我们编写的一个小型库,它实现了下面详细版本中描述的步骤。

pip install git+https://github.com/dylanebert/InstantTexture

用法

以下代码将顶点颜色的 .obj 网格转换为 UV 映射、纹理 .glb 网格,并将其保存到 output.glb。

from instant_texture import Converterinput_mesh_path = "https://raw.githubusercontent.com/dylanebert/InstantTexture/refs/heads/main/examples/chair.obj"converter = Converter()
converter.convert(input_mesh_path)

让我们可视化输出的网格:

import trimeshmesh = trimesh.load("output.glb")
mesh.show()

就是这样!

有关详细演练,请继续阅读。

2、详细版本

安装以下依赖项:

  • numpy 用于数值运算
  • trimesh 用于加载和保存网格数据
  • xatlas 用于生成 uv 贴图
  • Pillow 用于图像处理
  • opencv-python 用于图像处理
  • httpx 用于下载输入网格
pip install numpy trimesh xatlas opencv-python pillow httpx

导入依赖:

import cv2
import numpy as np
import trimesh
import xatlas
from PIL import Image, ImageFilter

加载顶点颜色的输入网格。这应该是位于 input_mesh_path 的 .obj 文件。

如果是本地文件,请使用 trimesh.load() 而不是 trimesh.load_remote()

mesh = trimesh.load_remote(input_mesh_path)
mesh.show()

访问网格的顶点颜色。

如果失败,请确保网格是具有顶点颜色的有效 .obj 文件。

vertex_colors = mesh.visual.vertex_colors

使用 xatlas 生成 uv 贴图。

这是整个过程中最耗时的部分。

vmapping, indices, uvs = xatlas.parametrize(mesh.vertices, mesh.faces)

将顶点和顶点颜色重新映射到 UV 贴图。

vertices = mesh.vertices[vmapping]
vertex_colors = vertex_colors[vmapping]mesh.vertices = vertices
mesh.faces = indices

定义所需的纹理大小。

构建一个纹理缓冲区,该缓冲区通过 upscale_factor 进行放大,以创建更高质量的纹理。

texture_size = 1024upscale_factor = 2
buffer_size = texture_size * upscale_factortexture_buffer = np.zeros((buffer_size, buffer_size, 4), dtype=np.uint8)

使用重心插值填充 UV 映射网格的纹理。

  • 重心插值:计算由顶点 v0、v1 和 v2 定义的三角形内点 p 的插值颜色,该三角形具有相应的颜色 c0、c1 和 c2。
  • 点在三角形内测试:确定点 p 是否位于由顶点 v0、v1 和 v2 定义的三角形内。
  • 纹理填充循环:
- 迭代网格的每个面。
- 检索当前面的 UV 坐标 (uv0、uv1、uv2) 和颜色 (c0、c1、c2)。
- 将 UV 坐标转换为缓冲区坐标。
- 确定纹理缓冲区上三角形的边界框。
- 对于边界框中的每个像素,使用点在三角形内测试检查像素是否位于三角形内。
- 如果在内部,则使用重心插值计算插值颜色。
- 将颜色分配给纹理缓冲区中的相应像素。

代码如下:

# Barycentric interpolation
def barycentric_interpolate(v0, v1, v2, c0, c1, c2, p):v0v1 = v1 - v0v0v2 = v2 - v0v0p = p - v0d00 = np.dot(v0v1, v0v1)d01 = np.dot(v0v1, v0v2)d11 = np.dot(v0v2, v0v2)d20 = np.dot(v0p, v0v1)d21 = np.dot(v0p, v0v2)denom = d00 * d11 - d01 * d01if abs(denom) < 1e-8:return (c0 + c1 + c2) / 3v = (d11 * d20 - d01 * d21) / denomw = (d00 * d21 - d01 * d20) / denomu = 1.0 - v - wu = np.clip(u, 0, 1)v = np.clip(v, 0, 1)w = np.clip(w, 0, 1)interpolate_color = u * c0 + v * c1 + w * c2return np.clip(interpolate_color, 0, 255)# Point-in-Triangle test
def is_point_in_triangle(p, v0, v1, v2):def sign(p1, p2, p3):return (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1])d1 = sign(p, v0, v1)d2 = sign(p, v1, v2)d3 = sign(p, v2, v0)has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)return not (has_neg and has_pos)# Texture-filling loop
for face in mesh.faces:uv0, uv1, uv2 = uvs[face]c0, c1, c2 = vertex_colors[face]uv0 = (uv0 * (buffer_size - 1)).astype(int)uv1 = (uv1 * (buffer_size - 1)).astype(int)uv2 = (uv2 * (buffer_size - 1)).astype(int)min_x = max(int(np.floor(min(uv0[0], uv1[0], uv2[0]))), 0)max_x = min(int(np.ceil(max(uv0[0], uv1[0], uv2[0]))), buffer_size - 1)min_y = max(int(np.floor(min(uv0[1], uv1[1], uv2[1]))), 0)max_y = min(int(np.ceil(max(uv0[1], uv1[1], uv2[1]))), buffer_size - 1)for y in range(min_y, max_y + 1):for x in range(min_x, max_x + 1):p = np.array([x + 0.5, y + 0.5])if is_point_in_triangle(p, uv0, uv1, uv2):color = barycentric_interpolate(uv0, uv1, uv2, c0, c1, c2, p)texture_buffer[y, x] = np.clip(color, 0, 255).astype(np.uint8)

让我们看一下目前的纹理是什么样子的。

from IPython.display import displayimage_texture = Image.fromarray(texture_buffer)
display(image_texture)

我们可以看到,纹理上有很多洞。

为了解决这个问题,我们将结合 4 种技术:

  • 修复:使用周围像素的平均颜色填充洞。
  • 中值滤波器:通过将每个像素替换为其周围像素的中值颜色来消除噪声。
  • 高斯模糊:平滑纹理以消除任何剩余的噪声。
  • 下采样:使用 LANCZOS 重采样将大小调整为纹理大小。
# Inpainting
image_bgra = texture_buffer.copy()
mask = (image_bgra[:, :, 3] == 0).astype(np.uint8) * 255
image_bgr = cv2.cvtColor(image_bgra, cv2.COLOR_BGRA2BGR)
inpainted_bgr = cv2.inpaint(image_bgr, mask, inpaintRadius=3, flags=cv2.INPAINT_TELEA
)
inpainted_bgra = cv2.cvtColor(inpainted_bgr, cv2.COLOR_BGR2BGRA)
texture_buffer = inpainted_bgra[::-1]
image_texture = Image.fromarray(texture_buffer)# Median filter
image_texture = image_texture.filter(ImageFilter.MedianFilter(size=3))# Gaussian blur
image_texture = image_texture.filter(ImageFilter.GaussianBlur(radius=1))# Downsample
image_texture = image_texture.resize((texture_size, texture_size), Image.LANCZOS)# Display the final texture
display(image_texture)

我们可以看到,纹理现在更加平滑,没有孔洞。

可以使用更先进的技术或手动纹理编辑进一步改进。

最后,我们可以用生成的 UV 坐标和纹理构建一个新的网格。

material = trimesh.visual.material.PBRMaterial(baseColorFactor=[1.0, 1.0, 1.0, 1.0],baseColorTexture=image_texture,metallicFactor=0.0,roughnessFactor=1.0,
)visuals = trimesh.visual.TextureVisuals(uv=uvs, material=material)
mesh.visual = visuals
mesh.show()

瞧!网格经过 UV 映射和纹理处理。

要在本地运行时将其导出,请调用 mesh.export("output.glb")

3、结束语

如你所见,网格仍然有许多小瑕疵。

UV 映射和纹理的质量也远低于可用于生产的网格的标准。

但是,如果你正在寻找一种快速解决方案来将顶点颜色网格映射到 UV 映射网格,那么这种方法可能对你有用。


原文链接:网格顶点颜色转纹理 - BimAnt

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

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

相关文章

【Java数据结构】栈 (Stack)

【本节目标】 1. 栈的概念及使用 2. 相关 OJ 题 一、概念 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出LIFO&#xff08;Last…

Vue项目中通过插件pxtorem实现大屏响应式

一、原理 rem单位代表的是根节点的font-size大小&#xff0c;所以当我们在页面上使用rem去替代px的时候&#xff0c;就可以通过修改根节点font-size的值&#xff0c;动态地让页面上的元素根据不同浏览器宽高下去实现变化。 二、工具 1.postcss-pxtorem 作用&#xff1a;在编…

OpenAI Sora如何使用?

引言&#xff1a;OpenA推出首款AI视频模型Sora&#xff0c;震惊世界&#xff01; Sora是什么&#xff1f; Sora是OpenAI最新发布的文本生成视频&#xff08;Text to Video&#xff09;大模型&#xff0c;能生成长达60秒的视频 Sora能够创造出包括多个角色、特定动作类型以及…

10个令人惊叹的AI工具

AI 确实改变了游戏规则&#xff1b;它彻底改变了我们工作、创造和与技术互动的方式。虽然 ChatGPT、DALLE 和 Midjourney 等巨头占据了大部分头条新闻&#xff0c;但还有很多其他不为人知的 AI 工具和技术&#xff0c;大多数都同样令人惊叹。 以下是十种你可能没有听说过但绝对…

6个最受欢迎的大模型本地运行工具

运行大型语言模型 (LLM)&#xff08;如 ChatGPT 和 Claude&#xff09;通常涉及将数据发送到 OpenAI 和其他 AI 模型提供商管理的服务器。虽然这些服务是安全的&#xff0c;但一些企业更愿意将数据完全离线&#xff0c;以保护更大的隐私。 本文介绍了开发人员可以用来在本地运…

codetop标签动态规划大全C++讲解(二)!!动态规划刷穿地心!!学吐了家人们o(╥﹏╥)o

一篇只有十题左右&#xff0c;写少一点好复习 1.目标和2.分割等和子集3.完全平方数4.比特位计数5.石子游戏6.预测赢家7.不同的二叉搜索树8.解码方法9.鸡蛋掉落10.正则表达式匹配11.通配符匹配12.交错字符串 1.目标和 给你一个非负整数数组 nums 和一个整数 target 。 向数组中…

WindowsTerminal 美化-壁纸随机更换

目录 一. 相关网址二. 壁纸随机更换思路三. 指定 WindowsTermina 壁纸路径四. 编写脚本&#xff0c;随机替换壁纸4.1 powershell脚本4.2 .bat批处理脚本 四. 配置定时任务&#xff0c;添加触发器五. 效果 一. 相关网址 官方下载 Windows Terminal 官方Github微软商店 美化 Oh …

力扣之1285.找到连续区间的开始和结束

题目 sql建表语句&#xff1a; Create table If Not Exists Logs (log_id int); Truncate table Logs; insert into Logs (log_id) values (1); insert into Logs (log_id) values (2); insert into Logs (log_id) values (3); insert into Logs (log_id) values (7); inse…

白板2-数学基础

高斯分布1-极大似然估计 高斯分布2-极大似然估计-无偏&有偏 高斯分布3-从概率密度角度高斯分布4-局限性高斯分布5-边缘概率及条件概率高斯分布6-求联合概率分布

基于SpringBoot vue 医院病房信息管理系统设计与实现

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php python(flask Django) 小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不然下次找…

DELL SC compellent存储的四种访问方式

DELL SC存储&#xff08;国内翻译为 康贝存储&#xff0c;英文是compellent&#xff09;, compellent存储是dell在大概10多年前收购的一家存储&#xff0c;原来这个公司就叫做compellent。 本文的阅读对象是第一次接触SC存储的技术朋友们&#xff0c;如何访问和管理SC存储。总…

一条广告变现3W+,半个月涨粉30W!简直太香了!

今天给大家分享个变现很猛的赛道&#xff0c; 这个赛道&#xff0c;我一开始关注到的时候&#xff0c;是一两个月前吧&#xff0c; 当时看到的时候&#xff0c;相关的笔记流量很猛&#xff0c; 而且相关的账号&#xff0c;起的号也很多&#xff0c; 我当时是看到那么多人都…

《数据结构》--栈【概念应用、图文并茂】

本节讲完栈下次再讲一下队列&#xff0c;最后补充一个串&#xff0c;我们的线性结构基本就完事了。下图中黄色框框圈中的是我们今日份内容(分为两篇博客)&#xff1a; 知识体系图 栈(Stack-LIFO)结构 栈的基础概念 栈(Stack)是一个后进先出(Last-In-First-Out)的一个特殊数据…

五种IO模型与阻塞IO

一、前言 在网络中通信的本质其实是网络中的两台主机的进程间进行通信&#xff0c;而进程通信的本质就是IO。 IO分为输入&#xff08;input&#xff09;和输出&#xff08;output&#xff09;站在进程的角度讲&#xff0c;进程出去数据为输出&#xff0c;外部数据进入进程为输…

ubunut声卡配置 播放视频没有声音的解决方法 alsamixer和pavucontrol的使用方法

文章目录 &#x1f319;ubuntu22.04网页没有声音&#xff0c;声卡提示Dummy Output&#x1f319;方法一&#xff1a;切换内核&#x1f319;方法二&#xff1a;使用知乎的方法 &#x1f319;ubuntu22.04 连接蓝牙耳机&#xff0c;1秒后断连解决方法ubuntu声音操作alsamixerpavuc…

边缘计算插上AI的翅膀会咋样?

人工智能&#xff08;Artificial Intelligence,AI&#xff09;是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学&#xff0c;是新一轮产业革命的重要驱动力量。2022年底发布的ChatGPT将人工智能技术上升到了一个新的高度。如今&#x…

17岁孩子开发AI应用,4个月入百万,人人都是AI产品经理的时代快来了

随着AI时代的到来叠加经济下行&#xff0c;越来越多的独立开发者梦想着实现年入百万的壮举。 近日&#xff0c;这种小概率事件正在发生。 17岁高中生做了个AI APP&#xff0c;短短四个月销售额达100 万美元。 小伙儿Zach Yadegari&#xff08;下面暂称小扎克&#xff09;在X…

用IMX6UL开发板编写按键输入实验

在之前我们都是讲解如何使用IMX6UL的GPIO输出控制等功能&#xff0c;IMX6U的IO不仅能作为输出&#xff0c;而且也可以作为输入&#xff0c;而我们开发板上具有一个按键&#xff0c;按键肯定是连接了一个IO口的额&#xff0c;我们在这一节将会把IO配置成输入功能&#xff0c;读取…

codetop标签动态规划大全C++讲解(三)!!动态规划刷穿地心!!学吐了家人们o(╥﹏╥)o

每天复习一篇&#xff0c;只有十题左右 1.买卖股票的最佳时机2.买卖股票的最佳时机含手续费3.买卖股票的最佳时机III4.买卖股票的最佳时机IV5.打家劫舍6.打家劫舍II7.不同路径8.不同路径II9.最小路径和10.三角形的最小路径和11.两个字符串的删除操作12.编辑距离13.一和零 1.买卖…

强化学习笔记之【DDPG算法】

强化学习笔记之【DDPG算法】 文章目录 强化学习笔记之【DDPG算法】前言&#xff1a;原论文伪代码DDPG算法DDPG 中的四个网络代码核心更新公式 前言&#xff1a; 本文为强化学习笔记第二篇&#xff0c;第一篇讲的是Q-learning和DQN 就是因为DDPG引入了Actor-Critic模型&#x…