Spaces:
Running
on
Zero
Running
on
Zero
import trimesh | |
import pygltflib | |
import numpy as np | |
from PIL import Image | |
import base64 | |
import io | |
def combine_metallic_roughness(metallic_path, roughness_path, output_path): | |
""" | |
将metallic和roughness贴图合并为一张贴图 | |
GLB格式要求metallic在B通道,roughness在G通道 | |
""" | |
# 加载贴图 | |
metallic_img = Image.open(metallic_path).convert("L") # 转为灰度 | |
roughness_img = Image.open(roughness_path).convert("L") # 转为灰度 | |
# 确保尺寸一致 | |
if metallic_img.size != roughness_img.size: | |
roughness_img = roughness_img.resize(metallic_img.size) | |
# 创建RGB图像 | |
width, height = metallic_img.size | |
combined = Image.new("RGB", (width, height)) | |
# 转为numpy数组便于操作 | |
metallic_array = np.array(metallic_img) | |
roughness_array = np.array(roughness_img) | |
# 创建合并的数组 (R, G, B) = (AO, Roughness, Metallic) | |
combined_array = np.zeros((height, width, 3), dtype=np.uint8) | |
combined_array[:, :, 0] = 255 # R通道:AO (如果没有AO贴图,设为白色) | |
combined_array[:, :, 1] = roughness_array # G通道:Roughness | |
combined_array[:, :, 2] = metallic_array # B通道:Metallic | |
# 转回PIL图像并保存 | |
combined = Image.fromarray(combined_array) | |
combined.save(output_path) | |
return output_path | |
def create_glb_with_pbr_materials(obj_path, textures_dict, output_path): | |
""" | |
使用pygltflib创建包含完整PBR材质的GLB文件 | |
textures_dict = { | |
'albedo': 'path/to/albedo.png', | |
'metallic': 'path/to/metallic.png', | |
'roughness': 'path/to/roughness.png', | |
'normal': 'path/to/normal.png', # 可选 | |
'ao': 'path/to/ao.png' # 可选 | |
} | |
""" | |
# 1. 加载OBJ文件 | |
mesh = trimesh.load(obj_path) | |
# 2. 先导出为临时GLB | |
temp_glb = "temp.glb" | |
mesh.export(temp_glb) | |
# 3. 加载GLB文件进行材质编辑 | |
gltf = pygltflib.GLTF2().load(temp_glb) | |
# 4. 准备纹理数据 | |
def image_to_data_uri(image_path): | |
"""将图像转换为data URI""" | |
with open(image_path, "rb") as f: | |
image_data = f.read() | |
encoded = base64.b64encode(image_data).decode() | |
return f"data:image/png;base64,{encoded}" | |
# 5. 合并metallic和roughness | |
if "metallic" in textures_dict and "roughness" in textures_dict: | |
mr_combined_path = "mr_combined.png" | |
combine_metallic_roughness(textures_dict["metallic"], textures_dict["roughness"], mr_combined_path) | |
textures_dict["metallicRoughness"] = mr_combined_path | |
# 6. 添加图像到GLTF | |
images = [] | |
textures = [] | |
texture_mapping = { | |
"albedo": "baseColorTexture", | |
"metallicRoughness": "metallicRoughnessTexture", | |
"normal": "normalTexture", | |
"ao": "occlusionTexture", | |
} | |
for tex_type, tex_path in textures_dict.items(): | |
if tex_type in texture_mapping and tex_path: | |
# 添加图像 | |
image = pygltflib.Image(uri=image_to_data_uri(tex_path)) | |
images.append(image) | |
# 添加纹理 | |
texture = pygltflib.Texture(source=len(images) - 1) | |
textures.append(texture) | |
# 7. 创建PBR材质 | |
pbr_metallic_roughness = pygltflib.PbrMetallicRoughness( | |
baseColorFactor=[1.0, 1.0, 1.0, 1.0], metallicFactor=1.0, roughnessFactor=1.0 | |
) | |
# 设置纹理索引 | |
texture_index = 0 | |
if "albedo" in textures_dict: | |
pbr_metallic_roughness.baseColorTexture = pygltflib.TextureInfo(index=texture_index) | |
texture_index += 1 | |
if "metallicRoughness" in textures_dict: | |
pbr_metallic_roughness.metallicRoughnessTexture = pygltflib.TextureInfo(index=texture_index) | |
texture_index += 1 | |
# 创建材质 | |
material = pygltflib.Material(name="PBR_Material", pbrMetallicRoughness=pbr_metallic_roughness) | |
# 添加法线贴图 | |
if "normal" in textures_dict: | |
material.normalTexture = pygltflib.NormalTextureInfo(index=texture_index) | |
texture_index += 1 | |
# 添加AO贴图 | |
if "ao" in textures_dict: | |
material.occlusionTexture = pygltflib.OcclusionTextureInfo(index=texture_index) | |
# 8. 更新GLTF | |
gltf.images = images | |
gltf.textures = textures | |
gltf.materials = [material] | |
# 确保mesh使用材质 | |
if gltf.meshes: | |
for primitive in gltf.meshes[0].primitives: | |
primitive.material = 0 | |
# 9. 保存最终GLB | |
gltf.save(output_path) | |
print(f"PBR GLB文件已保存: {output_path}") | |