使用Blender进行ply转obj

摘要: ply文件转obj在进行转换时,时常遇到的问题是,ply转为obj后丢失了材质或者颜色信息,使用Blender可进行完美转换。


〓 Table of Contents 〓





这个方法在blender3.6LTS下已经走通



bpy脚本

〓 ReTURN 〓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import bpy
import bmesh
import sys
import os

# 获取命令行参数
argv = sys.argv
argv = argv[argv.index("--") + 1:] # 只保留 -- 之后的参数

if len(argv) < 2:
print("Usage: blender --background --python convert_ply_to_obj.py -- <input_ply_file> <output_obj_file>")
sys.exit(1)

input_ply_path = argv[0]
output_obj_path = argv[1]

def load_ply(file_path):
bpy.ops.import_mesh.ply(filepath=file_path)

def add_vertex_color_material():
obj = bpy.context.object
mat = bpy.data.materials.new(name="VertexColorMaterial")
mat.use_nodes = True
nodes = mat.node_tree.nodes

for node in nodes:
nodes.remove(node)

output_node = nodes.new(type="ShaderNodeOutputMaterial")
diffuse_node = nodes.new(type="ShaderNodeBsdfDiffuse")
vertex_color_node = nodes.new(type="ShaderNodeVertexColor")

output_node.location = (300, 0)
diffuse_node.location = (0, 0)
vertex_color_node.location = (-300, 0)

mat.node_tree.links.new(output_node.inputs['Surface'], diffuse_node.outputs['BSDF'])
mat.node_tree.links.new(diffuse_node.inputs['Color'], vertex_color_node.outputs['Color'])

if obj.data.materials:
obj.data.materials[0] = mat
else:
obj.data.materials.append(mat)

def bake_vertex_colors_to_texture(output_image_path):
bpy.context.scene.render.engine = 'CYCLES'
bpy.context.scene.cycles.device = 'CPU'

obj = bpy.context.object

mat = bpy.data.materials.new(name="BakedMaterial")
mat.use_nodes = True
nodes = mat.node_tree.nodes

for node in nodes:
nodes.remove(node)

output_node = nodes.new(type="ShaderNodeOutputMaterial")
emission_node = nodes.new(type="ShaderNodeEmission")
vertex_color_node = nodes.new(type="ShaderNodeVertexColor")

output_node.location = (300, 0)
emission_node.location = (0, 0)
vertex_color_node.location = (-300, 0)

mat.node_tree.links.new(output_node.inputs['Surface'], emission_node.outputs['Emission'])
mat.node_tree.links.new(emission_node.inputs['Color'], vertex_color_node.outputs['Color'])

if obj.data.materials:
obj.data.materials[0] = mat
else:
obj.data.materials.append(mat)

image = bpy.data.images.new(name="BakedVertexColors", width=1024, height=1024)
texture_node = nodes.new(type="ShaderNodeTexImage")
texture_node.image = image
texture_node.location = (-600, 0)

bpy.context.view_layer.objects.active = obj

bpy.ops.object.mode_set(mode='EDIT')
bpy.ops.uv.smart_project()
bpy.ops.object.mode_set(mode='OBJECT')

bpy.context.scene.render.bake.use_pass_direct = False
bpy.context.scene.render.bake.use_pass_indirect = False
bpy.context.scene.render.bake.use_pass_color = True
bpy.context.scene.render.bake.target = 'IMAGE_TEXTURES'
texture_node.select = True
nodes.active = texture_node

bpy.ops.object.bake(type='EMIT')

image.filepath_raw = output_image_path
image.file_format = 'PNG'
image.save()

def export_obj(file_path, texture_file):
bpy.ops.export_scene.obj(filepath=file_path, use_materials=True)
# 手动更新 MTL 文件以包含 map_Kd 信息
mtl_file_path = file_path.replace('.obj', '.mtl')
if os.path.exists(mtl_file_path):
with open(mtl_file_path, 'a') as mtl_file:
mtl_file.write(f"\nmap_Kd {texture_file}\n")

def main(input_ply_path, output_obj_path):
bpy.ops.wm.read_factory_settings(use_empty=True)

load_ply(input_ply_path)

add_vertex_color_material()

output_image_path = os.path.splitext(output_obj_path)[0] + "_vertex_colors.png"
bake_vertex_colors_to_texture(output_image_path)

export_obj(output_obj_path, os.path.basename(output_image_path))


main(input_ply_path, output_obj_path)




终端运行命令

〓 ReTURN 〓

1
blender --background --python bpy.py -- source.ply target.obj

输出结果会包括三个文件: .obj, .mtl, .png

其中obj文件是点云位置, mtl是材质,png是纹理贴图,若要正确打开这个obj,需要达成:

 在obj里面指明mtl文件位置,在mtl里面指明png贴图位置 

obj示例

1
2
3
4
5
6
7
# Blender v3.6.12 OBJ File: ''
# www.blender.org
mtllib target.mtl # 指明mtl材质的位置
o scene0467_00_vh_clean_2
v 1.311216 0.775513 -0.504970
v 1.280774 0.862291 -0.442830
......

mtl示例

1
2
3
4
5
6
7
8
9
10
11
12
13
# Blender MTL File: 'None'
# Material Count: 1

newmtl BakedMaterial
Ns 359.999993
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.000000
d 1.000000
illum 2
map_Kd target_vertex_colors.png # 指明纹理贴图的位置




最终可视化效果

〓 ReTURN 〓

纹理贴图示例

最终可视化效果

作者

Jiawei Li

发布于

2024-06-15

更新于

2024-06-15

许可协议