[英]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.