如何在 OpenGL 中缩放模型?

Posted

技术标签:

【中文标题】如何在 OpenGL 中缩放模型?【英文标题】:How to scale a model in OpenGL? 【发布时间】:2016-04-14 23:33:48 【问题描述】:

我正在尝试在 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
    );

【问题讨论】:

在顶点着色器中搜索如何使用模型视图矩阵来变换顶点。 glScalef 在 OpenGL 3.0 及更高版本中已弃用。它不适用于顶点缓冲区。 【参考方案1】:

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

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

由于您想要的缩放类型仅适用于模型矩阵,我们现在将跳过其他两个。不过,我建议您阅读所有这三个以及它们如何交互,因为它们对 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 是一个复杂的状态机,一旦出现问题,您就会想知道。

【讨论】:

所以,我试过了,我在控制台中得到了这个:链接程序错误:找不到“void main()”的定义。 我包含的顶点着色器代码需要编译并链接到着色器程序中。我已经更新了我的答案以显示这是如何完成的。【参考方案2】:

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

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

型号

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

查看

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

投影

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

正交与透视

那么,我们有了这些“矩阵”,但现在呢?

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

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

按照这些很棒的教程,我可以确保你有一个缩放模型:)

附:这是我的第一个真正的答案,如果您觉得难以理解,非常抱歉;)

【讨论】:

以上是关于如何在 OpenGL 中缩放模型?的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL投影缩放

如何使缩放图像在 Java/OpenGL 中看起来更好?

如何在缩放投影矩阵Opengl的同时平移光标位置

opengl中如何实现图象的缩放

如何防止缩放低于定义(OpenGL)

如何在不缩放其内容的情况下调整 opengl 视口大小