OBJ模型加载与三角形渲染实现
坐标系确定
本渲染器使用左手坐标系,判断依据:
静态分析方法
- 检查顶点变换:
1 2 3
| screen_coords[j] = vec2i((v.x+1)*fb.width/2, (v.y+1)*fb.height/2);
|
- 检查法线计算:
1 2
| vec3f normal = (v2-v0).cross(v1-v0).normalized();
|
- 检查光照计算:
1 2
| float intensity = normal.dot(lightDir.normalized());
|
动态验证方法
- 创建测试三角形:
1
| vertices = {{0,1,0}, {-1,-1,0}, {1,-1,0}};
|
- 观察不同光照方向效果:
- lightDir(0,0,1) 应可见
- lightDir(0,0,-1) 应不可见
实现概述
本次实现了OBJ模型加载和三角形渲染功能,主要包含:
- OBJ文件格式解析
- 三角形面片渲染
- 基础光照计算
- 背面剔除优化
核心实现
1. OBJ文件加载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| bool Model::loadFromObj(const std::string& filename) { if (type == "v") { vec3f v; iss >> v.x >> v.y >> v.z; vertices.push_back(v); } else if (type == "f") { while (iss >> v) { face.push_back(v - 1); if (iss.peek() == '/') { } } } }
|
2. 三角形渲染与光照
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void Model::renderSolid(Framebuffer& fb, const vec3f& color, const vec3f& lightDir) { vec3f normal = calculateFaceNormal(face); float intensity = normal.dot(lightDir.normalized()); if (intensity > 0) { vec3f shadedColor = color * intensity; fb.drawTriangle(x0,y0, x1,y1, x2,y2, shadedColor); } }
|
3. 法线计算
1 2 3 4 5
| vec3f Model::calculateFaceNormal() const { vec3f edge1 = v1 - v0; vec3f edge2 = v2 - v0; return edge1.cross(edge2).normalized(); }
|
关键技术点
OBJ格式解析:
- 支持顶点/纹理/法线坐标
- 处理多种面定义格式(v, v/vt, v//vn, v/vt/vn)
- 1-based到0-based索引转换
渲染优化:
- 背面剔除:跳过dot product ≤ 0的面
- 扫描线算法:高效三角形填充
- 法线插值:使用顶点法线或几何法线
光照模型:
- 简单Lambert漫反射
- 光线方向归一化处理
- 颜色强度线性缩放
使用方法
1 2 3 4 5 6 7 8
| Model model; model.loadFromObj("model.obj");
vec3f lightDir(0,0,1);
model.renderSolid(fb, vec3f(1,1,1), lightDir);
|
效果验证
渲染测试模型后应得到:
- 正确朝向的面片被渲染
- 背对光源的面片被剔除
- 光照强度随角度变化
后续计划
- 实现Z-buffer深度测试
- 添加纹理映射支持
- 实现Phong光照模型