|
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) |
|
|
|
|
|
width, height = metallic_img.size |
|
combined = Image.new("RGB", (width, height)) |
|
|
|
|
|
metallic_array = np.array(metallic_img) |
|
roughness_array = np.array(roughness_img) |
|
|
|
|
|
combined_array = np.zeros((height, width, 3), dtype=np.uint8) |
|
combined_array[:, :, 0] = 255 |
|
combined_array[:, :, 1] = roughness_array |
|
combined_array[:, :, 2] = metallic_array |
|
|
|
|
|
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' # 可选 |
|
} |
|
""" |
|
|
|
mesh = trimesh.load(obj_path) |
|
|
|
|
|
temp_glb = "temp.glb" |
|
mesh.export(temp_glb) |
|
|
|
|
|
gltf = pygltflib.GLTF2().load(temp_glb) |
|
|
|
|
|
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}" |
|
|
|
|
|
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 |
|
|
|
|
|
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) |
|
|
|
|
|
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 |
|
|
|
|
|
if "ao" in textures_dict: |
|
material.occlusionTexture = pygltflib.OcclusionTextureInfo(index=texture_index) |
|
|
|
|
|
gltf.images = images |
|
gltf.textures = textures |
|
gltf.materials = [material] |
|
|
|
|
|
if gltf.meshes: |
|
for primitive in gltf.meshes[0].primitives: |
|
primitive.material = 0 |
|
|
|
|
|
gltf.save(output_path) |
|
print(f"PBR GLB文件已保存: {output_path}") |
|
|
|
|
|
|