繁体   English   中英

如何在OpenGL中缩放模型?

[英]How to scale a model in OpenGL?

我正在尝试在 OpenGL 中缩放模型,但我不知道从哪里开始,我正在尝试使用 glScalef(),但我不知道是否是这种方式,我对 openGL 了解不多,我对理论了解更多(那我必须将向量乘以矩阵,但我没有找到任何关于此的好的教程)...我的代码是:

bool res = loadOBJ(object, vertices, uvs, normals);
indexVBO(vertices, uvs, normals, indices, indexed_vertices, indexed_uvs, indexed_normals);

glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_vertices.size() * sizeof(glm::vec3), &indexed_vertices[0], GL_STATIC_DRAW);


glGenBuffers(1, &uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_uvs.size() * sizeof(glm::vec2), &indexed_uvs[0], GL_STATIC_DRAW);


glGenBuffers(1, &normalbuffer);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glBufferData(GL_ARRAY_BUFFER, indexed_normals.size() * sizeof(glm::vec3), &indexed_normals[0], GL_STATIC_DRAW);

// Generate a buffer for the indices as well

glGenBuffers(1, &elementbuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned short), &indices[0], GL_STATIC_DRAW);

// 1rst attribute buffer : vertices
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glVertexAttribPointer(
    0,                  // attribute
    3,                  // size
    GL_FLOAT,           // type
    GL_FALSE,           // normalized?
    0,                  // stride
    (void*)0            // array buffer offset
    );

// 2nd attribute buffer : UVs
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glVertexAttribPointer(
    1,                                // attribute
    2,                                // size
    GL_FLOAT,                         // type
    GL_FALSE,                         // normalized?
    0,                                // stride
    (void*)0                          // array buffer offset
    );

// 3rd attribute buffer : normals
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, normalbuffer);
glVertexAttribPointer(
    2,                                // attribute
    3,                                // size
    GL_FLOAT,                         // type
    GL_FALSE,                         // normalized?
    0,                                // stride
    (void*)0                          // array buffer offset
    );

// Index buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementbuffer);

glScalef(10, 10, 10);

glDrawElements(
    GL_TRIANGLES,        // mode
    indices.size(),      // count
    GL_UNSIGNED_SHORT,   // type
    (void*)0             // element array buffer offset
    );

缩放是您可以对模型矩阵进行的三种变换之一,以及平移和旋转。

模型矩阵是将顶点转换为屏幕上像素的三个矩阵之一,以及视图和投影矩阵。

由于您想要的缩放类型仅适用于模型矩阵,我们现在将跳过其他两个。 然而,我建议阅读所有这三个以及它们如何交互,因为它们对 OpenGL 至关重要。

在我们开始之前,我建议选择一个像GLM这样的库,因为它会为我们做很多繁重的工作。 从这里开始,我将使用 GLM 语法来保持简洁。

首先让我们将比例存储在 3d 向量中:

glm::vec3 scale = glm::vec3(10f, 10f, 10f);

现在我们需要一个没有转换的基本模型矩阵:

glm::mat4 modelMatrix = glm::mat4();

现在我们可以将比例应用于我们的模型矩阵:

modelMatrix = glm::scale(modelMatrix, scale);

现在我们有了一个矩阵,它可以应用于任何一组顶点,并在所有三个维度上将它们缩放 10。

接下来我们需要将这些信息传递给着色器。 就像glVertexAttribPointer告诉着色器在哪里可以找到顶点属性一样,我们将使用glUniform发送我们的矩阵:

GLuint location = getUniformLocation("model");
glUniformMatrix4fv(location, 1, false, glm::value_ptr(modelMatrix));

在这里,我们向着色器查询“模型”制服的位置。 然后我们将 1 个统一矩阵(我们的modelMatrix )提交到该位置。

最后,我们需要在着色器中使用该矩阵。 这是一个超级简单的顶点着色器,可以满足我们的需求:

#version 400 core

in vec3 position;
in vec3 normal;
in vec2 uv;
uniform mat4 model;

void main() {
    gl_Position = model * vec4(position, 1.0f);
}

通常我们会将法线和 uv 信息传递给片段着色器,但我现在省略它以保持简单。

就是这样。 希望这能让你到达你想去的地方。


附带说明一下,函数glScalef在 GL3 和更新版本中已弃用。 我喜欢使用docs.gl作为参考,因为它区分版本。

编辑:如何构建着色器程序

我上面发布的代码只是一个顶点着色器的源代码,它只是一个更大的着色器程序的一部分。 着色器程序可以具有顶点着色器、几何着色器和片段着色器。 虽然现在我们只关注两个必需的; 顶点和片段着色器。

首先将上面的顶点着色器代码放入一个名为vertex.glsl的文件中。 加载文件超出了这个答案的范围,所以我假设你有一个名为loadSourceFromFile的函数,它接受一个参数,文件名。

在我们继续之前,让我们创建几个实用函数:

function Gluint compileShader(const char* source, GLuint type) {
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, source);
    glCompileShader(shader);
    return shader;
}

function void verifyShaderCompilation(GLuint shader) {
    GLunit status = glGetShaderi(shader, GL_COMPILE_STATUS);
    assert(status == GL_TRUE);
}

现在,让我们编译顶点着色器:

const char* vertexSource = loadSourceFromFile("vertex.glsl");
Gluint vertexShader = compileShader(vertexSource, GL_VERTEX_SHADER);
verifyShaderCompilation(vertexShader);

接下来我们必须构建片段着色器。 将以下代码放入另一个名为fragment.glsl文件中:

#version 400 core

out vec4 color;

void main()
{
    color = vec4(0, 0.5, 0, 1);
}

这个片段着色器将使它处理的每个片段都变成绿色。 现在让我们像编译顶点着色器一样编译它:

const char* fragmentSource = loadSourceFromFile("fragment.glsl");
Gluint fragmentShader = compileShader(fragmentSource, GL_FRAGMENT_SHADER);
verifyShaderCompilation(fragmentShader);

现在我们有两个编译的着色器。 是时候将它们链接到一个着色器程序中了:

GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);

您需要在调用glVertexAttribPointer之前完成所有这些操作,因为它需要与您构建的着色器程序进行通信。

编辑:调试 OpenGL 函数

在尝试解决问题时,我建议在每次 GL 函数调用后使用以下函数:

function void verifyNoGLError() {
    int errorCode = glGetError();
    assert(errorCode == 0);
}

OpenGL 是一个复杂的状态机,一旦出现问题,您就会想知道。

您真的应该使用 MVP(模型、视图和投影)矩阵。 矩阵是浮点数的二维数组,最常用的大小为 4x4。

那么什么是模型、视图和投影矩阵呢?

型号

模型是表示对象变换的矩阵。 我们使用矩阵,因为我们可以将平移、缩放和旋转存储在一个变量中。 位置向量存储在第一行,旋转向量存储在第二行,尺度向量存储在第三行。

查看

View 是一个表示相机的矩阵。 它的工作原理类似于模型矩阵。

投影

最后但并非最不重要的是,我们有投影矩阵。 这保存了相机投影的数据,因为我们无法将其存储在 View 矩阵中。 这个矩阵有点难以解释,但它基本上描述了事物相对于相机位置的外观。 有两种常见的投影类型,透视和正交。 透视使物体离相机越远越小。 你用眼睛看到的一切都是透视。 正交使一切看起来都一样大小,无论距离相机多远。

正交与透视

正交与透视

所以,我们有这些“矩阵”,但现在呢?

当然,在我们的代码中处理矩阵对我们来说并不容易,所以有一些库可以帮助我们做到这一点。 我最喜欢的是GLM GLM 帮助我们创建和使用矩阵。 我建议你阅读它。 一旦我们有了模型、视图和投影矩阵,我们就可以将它们与顶点位置相乘,以获得顶点的最终位置。

现在,MVP 和转换有点太高级了,无法在答案中解释。 我建议你看看这两个教程,它们解释了矩阵和变换(比我做的好得多),并告诉你如何在你的代码和着色器中使用它们:教程 1教程 2 您还可以查看下一个教程,了解如何制作出色的相机系统。

遵循这些很棒的教程,我可以确保您获得缩放模型:)

PS 这是我的第一个真正的答案,如果您觉得难以理解,那么抱歉;)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM