You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PRNet生成的obj、mtl、纹理文件无法用OpenGL代码渲染3D图像求助

问题描述

我使用相关代码仓库完成3D人脸重建,已成功生成.obj、.mtl格式文件和.png纹理文件。生成的obj文件包含v、vt、f字段,单独绘制该obj文件可正常显示,但结合三类文件生成3D图像时失败。下述为我使用的3D图像生成源代码,我使用该代码测试其他已生成的同类文件可正常重建3D图像,仅我生成的文件无法正常渲染。

用到的OBJ加载解析代码如下:

import os
import pygame
from OpenGL.GL import *


class OBJ:
    generate_on_init = True
    @classmethod
    def loadTexture(cls, imagefile):
        surf = pygame.image.load(imagefile)
        image = pygame.image.tostring(surf, 'RGBA', 1)
        ix, iy = surf.get_rect().size
        texid = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, texid)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ix, iy, 0, GL_RGBA, GL_UNSIGNED_BYTE, image)
        return texid

    @classmethod
    def loadMaterial(cls, filename):
        contents = {}
        mtl = None
        dirname = os.path.dirname(filename)

        for line in open(filename, "r"):
            if line.startswith('#'): continue
            values = line.split()
            if not values: continue
            if values[0] == 'newmtl':
                mtl = contents[values[1]] = {}
            elif mtl is None:
                raise ValueError("mtl file doesn't start with newmtl stmt")
            elif values[0] == 'map_Kd':
                # load the texture referred to by this declaration
                mtl[values[0]] = values[1]
                imagefile = os.path.join(dirname, mtl['map_Kd'])
                mtl['texture_Kd'] = cls.loadTexture(imagefile)
            else:
                mtl[values[0]] = list(map(float, values[1:]))
        return contents

    def __init__(self, filename, swapyz=False):
        """Loads a Wavefront OBJ file. """
        self.vertices = []
        self.normals = []
        self.texcoords = []
        self.faces = []
        self.gl_list = 0
        dirname = os.path.dirname(filename)

        material = None
        for line in open(filename, "r"):
            if line.startswith('#'): continue
            values = line.split()
            if not values: continue
            if values[0] == 'v':
                v = list(map(float, values[1:4]))
                if swapyz:
                    v = v[0], v[2], v[1]
                self.vertices.append(v)
            elif values[0] == 'vn':
                v = list(map(float, values[1:4]))
                if swapyz:
                    v = v[0], v[2], v[1]
                self.normals.append(v)
            elif values[0] == 'vt':
                self.texcoords.append(list(map(float, values[1:3])))
            elif values[0] in ('usemtl', 'usemat'):
                material = values[1]
            elif values[0] == 'mtllib':
                self.mtl = self.loadMaterial(os.path.join(dirname, values[1]))
            elif values[0] == 'f':
                face = []
                texcoords = []
                norms = []
                for v in values[1:]:
                    w = v.split('/')
                    face.append(int(w[0]))
                    if len(w) >= 2 and len(w[1]) > 0:
                        texcoords.append(int(w[1]))
                    else:
                        texcoords.append(0)
                    if len(w) >= 3 and len(w[2]) > 0:
                        norms.append(int(w[2]))
                    else:
                        norms.append(0)
                self.faces.append((face, norms, texcoords, material))
        if self.generate_on_init:
            self.generate()

    def generate(self):
        self.gl_list = glGenLists(1)
        glNewList(self.gl_list, GL_COMPILE)
        glEnable(GL_TEXTURE_2D)
        glFrontFace(GL_CCW)
        for face in self.faces:
            vertices, normals, texture_coords, material = face

            mtl = self.mtl[material]
            if 'texture_Kd' in mtl:
                # use diffuse texmap
                glBindTexture(GL_TEXTURE_2D, mtl['texture_Kd'])
            else:
                # just use diffuse colour
                glColor(*mtl['Kd'])

            glBegin(GL_POLYGON)
            for i in range(len(vertices)):
                if normals[i] > 0:
                    glNormal3fv(self.normals[normals[i] - 1])
                if texture_coords[i] > 0:
                    glTexCoord2fv(self.texcoords[texture_coords[i] - 1])
                glVertex3fv(self.vertices[vertices[i] - 1])
            glEnd()
        glDisable(GL_TEXTURE_2D)
        glEndList()

    def render(self):
        glCallList(self.gl_list)

    def free(self):
        glDeleteLists([self.gl_list])
解决方案

按照优先级依次排查以下问题即可定位:

  • 纹理坐标范围异常:检查生成的obj文件中vt字段的数值,标准纹理坐标取值范围为[0,1],如果你的生成结果超出该范围,要么对vt做归一化处理,要么在loadTexture方法中添加如下两行代码,适配超出范围的坐标:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
  • mtl配置与路径错误:打开生成的mtl文件,确认map_Kd字段填写的纹理文件名和实际的png文件名完全一致,包括大小写、后缀名,同时确保png文件和mtl、obj文件存放在同一目录下,避免路径拼接错误。
  • 法线数据异常:代码默认使用vn字段做光照计算,如果生成的obj没有vn字段,或者法线方向全部反向,会导致光照计算错误显示为全黑。可以临时注释掉generate方法中的glNormal3fv调用,或者在OpenGL初始化时开启环境光,验证是否是该问题。
  • 面顶点顺序不匹配:代码设置了glFrontFace(GL_CCW),要求面顶点为逆时针顺序,如果生成的obj中f字段顶点为顺时针顺序,会导致面被默认剔除无法显示。可以在generate方法开头添加glDisable(GL_CULL_FACE)关闭面剔除,验证是否是该问题,确认后可以调整生成obj时的顶点排序,或者修改glFrontFace参数为GL_CW。
  • 纹理格式不兼容:如果生成的png纹理没有alpha通道,pygame按RGBA加载会导致颜色数据错位。可以将loadTexture方法中的pygame.image.tostring(surf, 'RGBA', 1)改为pygame.image.tostring(surf, 'RGB', 1),同时将glTexImage2D对应的前后两个格式参数都改为GL_RGB测试。

内容的提问来源于stack exchange,提问作者Asadullah Naeem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 21:21:01