Python + OpenGL 如何加载.obj三维模型文件并利用三视图投影法获取六张二维视图图片

Python + OpenGL 如何加载.obj三维模型文件并利用三视图投影法获取六张二维视图图片

9 回复

obj 模型存储的应该是是模型三角面信息,理论上讲,不需要 opengl,直接使用三角面坐标进行矩阵运算然后忽略一个坐标纬度,然后在图片里面绘制就可以了


我来给你一个完整的解决方案。这个需求可以分解为:用PyOpenGL加载.obj文件,然后从六个正交投影方向(前、后、左、右、上、下)渲染模型并保存为图片。

import numpy as np
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *
import pygame
from pygame.locals import *
from PIL import Image
import io

class OBJLoader:
    def __init__(self):
        self.vertices = []
        self.faces = []
        
    def load(self, filename):
        with open(filename, 'r') as f:
            for line in f:
                if line.startswith('v '):
                    vertex = list(map(float, line.strip().split()[1:4]))
                    self.vertices.append(vertex)
                elif line.startswith('f '):
                    face = []
                    for v in line.strip().split()[1:]:
                        face.append(int(v.split('/')[0]) - 1)
                    self.faces.append(face)
        
        # 计算模型边界用于调整视图
        vertices_array = np.array(self.vertices)
        self.min_coords = vertices_array.min(axis=0)
        self.max_coords = vertices_array.max(axis=0)
        self.center = (self.min_coords + self.max_coords) / 2
        self.size = np.max(self.max_coords - self.min_coords) * 1.2
        
        print(f"Loaded {len(self.vertices)} vertices, {len(self.faces)} faces")

class ModelRenderer:
    def __init__(self, obj_file, output_size=(512, 512)):
        self.obj = OBJLoader()
        self.obj.load(obj_file)
        self.output_size = output_size
        
        # 初始化Pygame和OpenGL
        pygame.init()
        self.screen = pygame.display.set_mode(output_size, DOUBLEBUF | OPENGL)
        glEnable(GL_DEPTH_TEST)
        glClearColor(1.0, 1.0, 1.0, 1.0)  # 白色背景
        
    def setup_ortho_projection(self):
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        size = self.obj.size
        glOrtho(-size, size, -size, size, -size*2, size*2)
        glMatrixMode(GL_MODELVIEW)
        
    def render_view(self, view_name):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glLoadIdentity()
        
        # 根据视图名称设置相机位置
        if view_name == 'front':
            gluLookAt(0, 0, self.obj.size, 0, 0, 0, 0, 1, 0)
        elif view_name == 'back':
            gluLookAt(0, 0, -self.obj.size, 0, 0, 0, 0, 1, 0)
        elif view_name == 'left':
            gluLookAt(-self.obj.size, 0, 0, 0, 0, 0, 0, 1, 0)
        elif view_name == 'right':
            gluLookAt(self.obj.size, 0, 0, 0, 0, 0, 0, 1, 0)
        elif view_name == 'top':
            gluLookAt(0, self.obj.size, 0, 0, 0, 0, 0, 0, -1)
        elif view_name == 'bottom':
            gluLookAt(0, -self.obj.size, 0, 0, 0, 0, 0, 0, 1)
        
        # 绘制模型
        glColor3f(0.2, 0.2, 0.2)  # 灰色模型
        glBegin(GL_TRIANGLES)
        for face in self.obj.faces:
            for vertex_idx in face:
                vertex = self.obj.vertices[vertex_idx]
                glVertex3fv(vertex)
        glEnd()
        
        # 读取像素数据
        glPixelStorei(GL_PACK_ALIGNMENT, 1)
        data = glReadPixels(0, 0, self.output_size[0], self.output_size[1], 
                           GL_RGB, GL_UNSIGNED_BYTE)
        
        # 转换为PIL Image
        image = Image.frombytes("RGB", self.output_size, data)
        image = image.transpose(Image.FLIP_TOP_BOTTOM)  # OpenGL坐标翻转
        
        return image
    
    def generate_all_views(self):
        views = ['front', 'back', 'left', 'right', 'top', 'bottom']
        images = {}
        
        for view in views:
            print(f"Rendering {view} view...")
            self.setup_ortho_projection()
            img = self.render_view(view)
            images[view] = img
            
        return images
    
    def save_views(self, images, output_dir="output"):
        import os
        os.makedirs(output_dir, exist_ok=True)
        
        for view_name, img in images.items():
            filename = f"{output_dir}/{view_name}_view.png"
            img.save(filename)
            print(f"Saved: {filename}")

# 使用示例
if __name__ == "__main__":
    # 替换为你的.obj文件路径
    obj_file = "your_model.obj"
    
    renderer = ModelRenderer(obj_file)
    images = renderer.generate_all_views()
    renderer.save_views(images)
    
    print("六视图生成完成!")

核心要点:

  1. OBJ加载:解析.obj文件中的顶点和面数据
  2. 正交投影:使用glOrtho确保无透视变形
  3. 六视图相机:通过gluLookAt设置六个方向的观察矩阵
  4. 图像保存:用glReadPixels捕获OpenGL缓冲区并保存为PNG

依赖安装:

pip install PyOpenGL PyOpenGL-accelerate pygame pillow numpy

注意事项:

  • 确保.obj文件使用三角面片(f v1 v2 v3格式)
  • 模型尺寸过小时适当调整glOrtho参数
  • 复杂模型可能需要法线数据用于光照(这里简化了)

这个方案直接生成标准的工程三视图投影,适合机械零件、建筑模型等需要正交投影的场合。

虽然我没做过这方面的,但是投影不就是取与投影方向垂直平面上的最大值,比如 z 轴,那就是 x,y 最大的点,这应该不用重建的吧?

而且六张实际上只有三张的吧?上下投影有什么区别吗?

您有相关的资料吗?谢谢。

只是一个思路,读取 obj 模型里面的坐标信息,obj 格式是公开的,上下左右这些视角可以与 mtrix4*4 矩阵对应,比如左视角可以看成绕着 y 轴旋转 90 度,原坐标和矩阵运算后,去掉坐标 z 纬度,用 xy 纬度在图片里面用像素画出来就可以了

读 obj>matrix4×4 矩阵运算>画图



感谢二位的建议,我考虑下

请教一下您,您解决了三视图投影吗?可以交流一下吗

回到顶部