OBJ解析必须将f行三重索引(如f 1/2/3)映射为OpenGL兼容的扁平化顶点流:先缓存v/vt/vn,再组合去重生成唯一顶点,构建新顶点数组与索引缓冲,并处理负索引、1起始偏移、UV翻转及纹理单元绑定一致性。

OBJ 文件不是“读进来就能画”的纯坐标列表,直接用 fscanf 按行扫 v 和 f 会立刻出错——顶点错位、UV翻转、法线崩坏、贴图全黑,都是因为 OBJ 的索引语义和 OpenGL 的顶点数组布局根本对不上。
OBJ 解析必须处理 v/vt/vn 三元组索引映射
OBJ 的 f 行(如 f 1/2/3 4/5/6 7/8/9)不是连续下标,而是分别引用全局 v(顶点)、vt(纹理)、vn(法线)数组。OpenGL 的 VBO 只接受扁平化的顶点流,每个三角形角必须是“位置+UV+法线”打包好的一个完整顶点。
- 不能把所有
v、vt、vn分别塞进三个独立数组再靠索引间接访问——glVertexAttribPointer不支持这种跳读 - 必须做“顶点去重+索引重映射”:遍历每个
f项,组合(v_i, vt_j, vn_k)成唯一键,查哈希表生成新顶点 ID,并填充到最终的std::vector - 原始
f行的三个索引,要转换成新顶点数组里的连续整数,填入 IBO(std::vector) - 注意索引从 1 开始,解析时必须减 1;负数索引(如
f -1/-2/-3)表示倒序引用,需转为正向下标
UV 坐标必须翻转,否则贴图上下颠倒
OBJ 规范中纹理原点在左下,而 OpenGL 纹理加载(尤其是 BMP/PNG 逐行解码)默认首行是图像顶部,导致 UV.y 不翻转就必然镜像。
- libpng 或 stb_image 加载后,若未手动翻转像素行,就要在解析
vt时做uv.y = 1.0f - uv.y - MFC + OpenGL 场景下更常见的是双重翻转:PNG 解码时已翻转像素,但 OBJ 解析又翻一次 → 贴图反向拉伸。建议统一在解析层修复,加载层保持原始内存布局
- 验证方法:加载一个带文字的贴图,看文字是否镜像或倒置;若倒置,说明 UV.y 少翻一次;若镜像,说明多翻了一次
OpenGL 渲染前必须绑定正确纹理单元并校验采样器
只调 glBindTexture(GL_TEXTURE_2D, texID) 不够,着色器里 uniform sampler2D u_tex 对应的纹理单元号必须和 glActiveTexture 一致。
- 典型错误:
glActiveTexture(GL_TEXTURE0)后绑了纹理,但着色器里写的是layout(location = 1) uniform sampler2D u_tex,结果采样器读到 0(黑) - 正确流程:先
glActiveTexture(GL_TEXTURE0),再glBindTexture(GL_TEXTURE_2D, texID),最后glUniform1i(glGetUniformLocation(prog, "u_tex"), 0) - 若使用多纹理(漫反射+法线+粗糙度),每个都要分配不同纹理单元(
GL_TEXTURE0,GL_TEXTURE1, …),并在着色器中显式声明对应位置 - 检查
glGetError()在glDrawElements前是否返回GL_INVALID_OPERATION—— 很可能是纹理未绑定或采样器未设值
MFC 环境下 OpenGL 上下文生命周期极易出问题
在 CView 派生类中,wglMakeCurrent 必须与窗口设备上下文(HDC)严格配对,且不能跨消息循环持久持有。
- 常见卡死:在
OnPaint外(如定时器回调、文件读取线程)调用 OpenGL 函数,wglMakeCurrent失败但没检查返回值,后续绘图直接静默失败 - 资源泄漏:
OnDestroy中未调用wglDeleteContext(m_hRC),导致下次创建上下文失败(wglCreateContext返回 NULL) - 多屏失效:未监听
WM_DISPLAYCHANGE,分辨率变化后未重建像素格式和上下文,渲染区域错位或黑屏 - 安全做法:所有 OpenGL 调用封装在
Render()内,入口强制wglMakeCurrent(m_hDC, m_hRC),出口不主动释放,依赖消息循环自然流转
最常被忽略的其实是顶点数据对齐:Vertex 结构体若含 vec3 + vec2 + vec3,总大小不是 32 字节(8 float × 4),而是 36 字节(因 vec3 对齐到 16 字节边界),glVertexAttribPointer 的 stride 参数必须填实际字节数,填错就整批顶点偏移 —— 模型看起来像被撕裂或抖动。











