现代OpenGL教程(五):obj文件和obj模型加载(imgui+OpenGL3.3)

前言:该系列教程主要参考自网站www.opengl-tutorial.org,基于开源GUI框架imgui v1.61实现,imgui自带的例子里面直接集成了glfw+gl3w环境,本系列教程将gl3w换成了glew,glew具体环境配置可参考:OpenGL环境配置教程:VS2012 + GLEW + GLFW + GLM

教程目录(持续更新中):
现代OpenGL教程(一):绘制三角形(ImGui+OpenGL3.3)
现代OpenGL教程(二):矩阵变换(ImGui+OpenGL3.3)
现代OpenGL教程(三):绘制彩色立方体(ImGui+OpenGL3.3)
现代OpenGL教程(四):立方体纹理贴图(ImGui+OpenGL3.3)
现代OpenGL教程(五):obj模型加载(ImGui+OpenGL3.3)
现代OpenGL教程(六):鼠标和键盘(ImGui+OpenGL3.3)
现代OpenGL教程(七):基础光照——颜色(ImGui+OpenGL3.3)
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)
现代OpenGL教程(九):基础光照——材质(ImGui+OpenGL3.3)

本节教程在上一节( 现代OpenGL教程(四):立方体纹理贴图(imgui+OpenGL3.3))的基础上,实现了obj文件的读取和在OpenGL中的显示。完整代码下载地址:https://github.com/maijiaquan/blog-code/tree/master/opengl-loadObj

运行效果

obj文件示例

# Blender3D v249 OBJ File: untitled.blend
# www.blender3d.org
mtllib cube.mtl
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.748573 0.750412
vt 0.749279 0.501284
vt 0.999110 0.501077
vt 0.999455 0.750380
vt 0.250471 0.500702
vt 0.249682 0.749677
vt 0.001085 0.750380
vt 0.001517 0.499994
vt 0.499422 0.500239
vt 0.500149 0.750166
vt 0.748355 0.998230
vt 0.500193 0.998728
vt 0.498993 0.250415
vt 0.748953 0.250920
vn 0.000000 0.000000 -1.000000
vn -1.000000 -0.000000 -0.000000
vn -0.000000 -0.000000 1.000000
vn -0.000001 0.000000 1.000000
vn 1.000000 -0.000000 0.000000
vn 1.000000 0.000000 0.000001
vn 0.000000 1.000000 -0.000000
vn -0.000000 -1.000000 0.000000
usemtl Material_ray.png
s off
f 5/1/1 1/2/1 4/3/1
f 5/1/1 4/3/1 8/4/1
f 3/5/2 7/6/2 8/7/2
f 3/5/2 8/7/2 4/8/2
f 2/9/3 6/10/3 3/5/3
f 6/10/4 7/6/4 3/5/4
f 1/2/5 5/1/5 2/9/5
f 5/1/6 6/10/6 2/9/6
f 5/1/7 8/11/7 6/10/7
f 8/11/7 7/12/7 6/10/7
f 1/2/8 2/9/8 3/13/8
f 1/2/8 3/13/8 4/14/8

其中:

  1. v代表顶点
  2. vt代表顶点的纹理坐标
  3. vn代表顶点的法线
  4. f代表面

v vt vn都很好理解。f比较复杂,例如f 8/11/7 7/12/7 6/10/7
假设有三角形△ABC,则

  1. 6/10/7描述了三角形的第一个顶点A
  2. 7/12/7描述了三角形的第二个顶点B
  3. 8/11/7描述了三角形的第三个顶点C

对于第三个顶点C:

  1. 8指向顶点C的坐标的索引。此例中是(-1.000000 1.000000 -1.000000)(索引从1开始,和C++中从0开始不同)
  2. 11指向顶点C要用的纹理坐标。此例中是(0.748355 0.998230)
  3. 7指向要用的法线。此例中是(0.000000 1.000000 -0.000000)

第一步:实现obj文件加载函数

bool loadOBJ(
    const char *path,
    std::vector<glm::vec3> &out_vertices,
    std::vector<glm::vec2> &out_uvs,
    std::vector<glm::vec3> &out_normals)
{
    printf("Loading OBJ file %s...\n", path);

    std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
    std::vector<glm::vec3> temp_vertices;
    std::vector<glm::vec2> temp_uvs;
    std::vector<glm::vec3> temp_normals;

    FILE *file = fopen(path, "r");
    if (file == NULL)
    {
        printf("Impossible to open the file ! Are you in the right path ? See Tutorial 1 for details\n");
        getchar();
        return false;
    }

    while (1)
    {

        char lineHeader[128];
        // read the first word of the line
        int res = fscanf(file, "%s", lineHeader);
        if (res == EOF)
            break; // EOF = End Of File. Quit the loop.

        // else : parse lineHeader

        if (strcmp(lineHeader, "v") == 0)
        {
            glm::vec3 vertex;
            fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z);
            temp_vertices.push_back(vertex);
        }
        else if (strcmp(lineHeader, "vt") == 0)
        {
            glm::vec2 uv;
            fscanf(file, "%f %f\n", &uv.x, &uv.y);
            uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders.
            temp_uvs.push_back(uv);
        }
        else if (strcmp(lineHeader, "vn") == 0)
        {
            glm::vec3 normal;
            fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z);
            temp_normals.push_back(normal);
        }
        else if (strcmp(lineHeader, "f") == 0)
        {
            std::string vertex1, vertex2, vertex3;
            unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
            int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2]);
            if (matches != 9)
            {
                printf("File can't be read by our simple parser :-( Try exporting with other options\n");
                fclose(file);
                return false;
            }
            vertexIndices.push_back(vertexIndex[0]);
            vertexIndices.push_back(vertexIndex[1]);
            vertexIndices.push_back(vertexIndex[2]);
            uvIndices.push_back(uvIndex[0]);
            uvIndices.push_back(uvIndex[1]);
            uvIndices.push_back(uvIndex[2]);
            normalIndices.push_back(normalIndex[0]);
            normalIndices.push_back(normalIndex[1]);
            normalIndices.push_back(normalIndex[2]);
        }
        else
        {
            // Probably a comment, eat up the rest of the line
            char stupidBuffer[1000];
            fgets(stupidBuffer, 1000, file);
        }
    }

    // For each vertex of each triangle
    for (unsigned int i = 0; i < vertexIndices.size(); i++)
    {

        // Get the indices of its attributes
        unsigned int vertexIndex = vertexIndices[i];
        unsigned int uvIndex = uvIndices[i];
        unsigned int normalIndex = normalIndices[i];

        // Get the attributes thanks to the index
        glm::vec3 vertex = temp_vertices[vertexIndex - 1];
        glm::vec2 uv = temp_uvs[uvIndex - 1];
        glm::vec3 normal = temp_normals[normalIndex - 1];

        // Put the attributes in buffers
        out_vertices.push_back(vertex);
        out_uvs.push_back(uv);
        out_normals.push_back(normal);
    }
    fclose(file);
    return true;
}

第二步:在OpenGL中使用obj数据

上一节( 现代OpenGL教程(四):立方体纹理贴图(imgui+OpenGL3.3))使用硬编码的方式声明static const GLfloat g_vertex_buffer_data[] = {…}作为立方体的顶点数据。本节使用STL的vector来创建顶点坐标数组、UV坐标数组和法线数组。

std::vector vertices;
std::vector uvs;
std::vector normals;
bool res = loadOBJ("cube.obj", vertices, uvs, normals);

把数组传给OpenGL:

glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), &vertices[0], GL_STATIC_DRAW);

完整代码下载地址:https://github.com/maijiaquan/blog-code/tree/master/opengl-loadObj


参考资料:
第七课:模型加载 opengl-tutorial

  • 5
    点赞
  • 42
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要在Qt中使用OpenGL 3.3或更高版本来渲染一个OBJ文件,您需要完成以下步骤: 1. 在Qt中创建一个OpenGL窗口: ```c++ class GLWidget : public QOpenGLWidget { public: GLWidget(QWidget *parent = 0); ~GLWidget(); protected: void initializeGL() override; void paintGL() override; void resizeGL(int w, int h) override; }; ``` 2. 在initializeGL函数中初始化OpenGL环境,并编译和链接您的着色器程序: ```c++ void GLWidget::initializeGL() { initializeOpenGLFunctions(); // Create and compile your shader program QOpenGLShaderProgram* shaderProgram = new QOpenGLShaderProgram(this); shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, "vertexShader.glsl"); shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, "fragmentShader.glsl"); shaderProgram->link(); shaderProgram->bind(); } ``` 3. 在paintGL函数中使用您的着色器程序和OBJ文件中的数据绘制场景: ```c++ void GLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Set up your shader uniforms and attributes shaderProgram->setUniformValue("projection", projection); shaderProgram->setUniformValue("view", view); shaderProgram->setUniformValue("model", model); shaderProgram->setAttributeArray("position", vertices.constData(), 3, sizeof(Vertex)); shaderProgram->setAttributeArray("normal", normals.constData(), 3, sizeof(Vertex)); shaderProgram->setAttributeArray("texCoord", texCoords.constData(), 2, sizeof(Vertex)); shaderProgram->enableAttributeArray("position"); shaderProgram->enableAttributeArray("normal"); shaderProgram->enableAttributeArray("texCoord"); // Draw your OBJ file glDrawArrays(GL_TRIANGLES, 0, vertices.size()); shaderProgram->disableAttributeArray("position"); shaderProgram->disableAttributeArray("normal"); shaderProgram->disableAttributeArray("texCoord"); } ``` 4. 在resizeGL函数中更新OpenGL视口: ```c++ void GLWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); } ``` 5. 确保您的OBJ文件中包含顶点位置、法线和纹理坐标数据,并且您可以正确地解析和加载它们。 这只是一个基本的框架,您需要根据您的项目需求进行修改和扩展。您可以使用第三方库,如Assimp,来加载和解析OBJ文件中的数据。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值