2

我正在创建基本的 OpenGL 场景,但在操作对象时遇到问题。每个都有不同的变换矩阵,还有整个场景的模型视图/平移/缩放矩阵。

在从顶点着色器执行计算之前,如何将此数据绑定到我的对象?我读过关于 gl(Push|Pop)Matrix() 的文章,但这些函数在我理解的范围内已被弃用。

我的一些代码。顶点着色器的位置:

gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;

和 C++ 函数来显示对象:

// Clear etc...

mat4 lookAt = glm::lookAt();
glLoadMatrixf(&lookAt[0][0]);
mat4 combined = lookAt * (mat4) sceneTranslation * (mat4) sceneScale;
glLoadMatrixf(&combined[0][0]);

mat4 objectTransform(1.0);
// Transformations...

// No idea if it works, but objects are affected by camera position but not individually scaled, moved etc.
GLuint gl_ModelViewMatrix = glGetUniformLocation(shaderprogram, "gl_ModelViewMatrix");
glUniformMatrix4fv(gl_ModelViewMatrix, 1, GL_FALSE, &objectTransform[0][0]);

// For example
glutSolidCube(1.0);

glutSwapBuffers();
4

2 回答 2

6

好吧,您不必使用 glLoadMatrix 和其他内置矩阵函数,因为它可能比处理您自己的矩阵更困难。

一个没有控制的简单相机示例,它是一个静态相机:

glm::mat4x4 view_matrix = glm::lookAt(
    cameraPosition,
    cameraPosition+directionVector, //or the focus point the camera is pointing to
    upVector);

它返回一个 4x4 矩阵,这是视图矩阵。

glm::mat4x4 projection_matrix = 
glm::perspective(60.0f, float(screenWidth)/float(screenHeight), 1.0f, 1000.0f);

这是投影矩阵

所以现在你有了视图和投影矩阵,你可以将它发送到着色器:

gShader->bindShader();
gShader->sendUniform4x4("view_matrix",glm::value_ptr(view_matrix));
gShader->sendUniform4x4("projection_matrix",glm::value_ptr(projection_matrix));

bindshader 是glUseProgram(shaderprog); 统一程序最简单的

void sendUniform4x4(const string& name, const float* matrix, bool transpose=false)
{
    GLuint location = getUniformLocation(name);
    glUniformMatrix4fv(location, 1, transpose, matrix);
}

您的模型矩阵对于您的每个对象都是单独的:

glm::mat4x4 model_matrix= glm::mat4(1); //this is the identity matrix, so its static
model_matrix= glm::rotate(model_matrix,
                          rotationdegree,
                          vec3(axis)); //same as opengl function.

这创建了一个模型矩阵,您也可以将其发送到您的着色器

 gShader->bindShader();
 gShader->sendUniform4x4("model_matrix",glm::value_ptr(model_matrix));

创建矩阵的glm::value_ptr(...)二维数组。

在您的着色器代码中不要使用 glModelViewMatrix 和 gl_ProjectionMatrix,矩阵是通过制服发送的。

uniform mat4 projection_matrix;
uniform mat4 view_matrix;
uniform mat4 model_matrix;

void main(){
gl_Position = projection_matrix*view_matrix*model_matrix*gl_Vertex;
//i wrote gl_Vertex because of the glutSolidTeapot.
}

我从来没有使用过这个内置的网格函数,所以我不知道它是如何工作的,假设它使用 gl_Vertex 将顶点发送到立即模式的着色器。如果您创建自己的网格,请使用 VBO vertexattribpointer 和 drawarrays/elements。

不要忘记在发送制服之前绑定着色器。

所以有一个完整的例子:

glm::mat4x4 view_matrix = glm::lookAt(2,4,2,-1,-1,-1,0,1,0);

glm::mat4x4 projection_matrix = 
    glm::perspective(60.0f, float(screenWidth)/float(screenHeight), 1.0f, 10.0f);

glm::mat4x4 model_matrix= glm::mat4(1); //this remains unchanged
glm::mat4x4 teapot_model_matrix= glm::rotate(model_matrix, //this is the teapots model matrix, apply transformations to this
                              45,
                              glm::vec3(1,1,1));
teapot_model_matrix = glm::scale(teapot_model_matrix,vec3(2,2,2);


gShader->bindShader();
gShader->sendUniform4x4("model_matrix",glm::value_ptr(model_matrix));
gShader->sendUniform4x4("view_matrix",glm::value_ptr(view_matrix));
gShader->sendUniform4x4("projection_matrix",glm::value_ptr(projection_matrix));

glutSolidCube(0.0); //i don't know what the (0.0) stands for :/
glutSwapBuffers();

//////////////////////////////

在你的着色器中:

uniform mat4 projection_matrix; //these are the matrixes you've sent
uniform mat4 view_matrix;
uniform mat4 model_matrix;

void main(){
gl_Position = projection_matrix*view_matrix*model_matrix*vec4(gl_Vertex.xyz,1);
}

现在你应该有一个位于 2,4,2 的相机,聚焦在 -1,-1,-1,并且向上的向量指向上方:)

茶壶围绕 (1,1,1) 向量旋转 45 度,并在每个方向上按 2 缩放。

更改模型矩阵后,将其发送到着色器,因此如果您有更多要渲染的对象,如果您希望对每个网格应用不同的变换,请在每个对象之后发送它。一个伪代码如下所示:

camera.lookat(camerapostion,focuspoint,updirection); //sets the view
camera.project(fov,aspect ratio,near plane, far plane)  //and projection matrix
camera.sendviewmatrixtoshader;
camera.sendprojectionmatrixtoshader;

obj1.rotate(45 degrees, 1,1,1); //these functions should transform the model matrix of the object. Make sure each one has its own.
obj1.sendmodelmatrixtoshader;
obj2.scale(2,1,1);
obj2.sendmodelmatrixtoshader;

如果它不起作用,请尝试使用 vertexBuffer 以及您自己创建的简单三角形或立方体。

于 2013-11-02T12:28:10.620 回答
3

你应该使用数学库,我推荐 GLM。它具有与 OpenGL 一样的矩阵函数,并使用列主矩阵,因此您可以计算自己的矩阵,并将它们应用于对象。

首先,您的场景应该有一个矩阵类,它计算您的视图矩阵和投影矩阵。(glm::lookAt 和 glm::project)。它们的工作方式与在 openGL 中相同。您可以将它们作为制服发送到顶点着色器。

对于对象,您计算自己的矩阵,并将它们作为模型矩阵发送到着色器。

在着色器或 cpu 上计算 mv 矩阵:

vp = proj*view.

您将各个模型矩阵发送到着色器并计算最终位置:

gl_Position = vp*m*vec4(vertex.xyz,1);

模型矩阵

使用 glm,您可以轻松计算、转换矩阵。您创建一个简单的单位矩阵:

glm::mat4x4(1) //identity

您可以平移、旋转、缩放它。

glm::scale
glm::rotate
glm::translate

它们就像在 opengl 中的即时模式一样工作。

在你让你的矩阵通过制服发送它之后。

更多模型矩阵

shader->senduniform("proj", camera.projectionmatrix);
shader->senduniform("view", camera.viewmatrix);
glm::mat4 model(1);

obj1.modelmatrix = glm::translate(model,vec3(1,2,1));
shader->senduniform("model", obj1.modelmatrix);
objectloader.render(obj1);

obj2.modelmatrix = glm::rotate(model,obj2.degrees,vec3(obj2.rotationaxis));
shader->senduniform("model", obj2.modelmatrix);
objectloader.render(obj2);

这只是执行此操作的一种方法。您可以编写一个用于推送/弹出矩阵计算的类,自动执行上述方法,如下所示:

obj1.rotate(degrees,vec3(axis)); //this calculates the obj1.modelmatrix for example rotating the identity matrix.
obj1.translate(vec3(x,y,z))//more transform
obj1.render();
//continue with object 2

查看矩阵

视图矩阵与模型矩阵几乎相同。使用它来控制全局“模型矩阵”,即相机。这将全局转换您的屏幕,并且您可以为您的对象单独设置模型矩阵。

在我的相机类中,我使用 glm::lookAt(与 opengl 相同)进行计算,然后通过统一将其发送到我使用的所有着色器。

然后当我渲染一些东西时,我可以操纵它的模型矩阵,旋转或缩放它,但视图矩阵是全局的。

如果你想要一个静态对象,你不必在它上面使用模型矩阵,你可以只计算位置:

gl_Position = projmatrix*viewmatrix*staticobjectvertex;

全局模型矩阵

你也可以有一个全局模型矩阵。

像这样使用它

renderer.globmodel.rotate(axis,degree);
renderer.globmodel.scale(x,y,z);

也将其作为统一发送,并将其应用于对象的模型矩阵之后。(我用它来将海洋反射渲染到纹理上。)

总结一下:

  • 创建一个全局视图(相机)矩阵
  • 为每个场景、网格或对象创建模型矩阵
  • 分别变换对象的矩阵
  • 通过制服将投影、模型和视图矩阵发送到着色器
  • 计算最终位置:proj*camera*model*vertex
  • 移动你的物体,移动你的相机

我并不是说没有更好的方法可以做到这一点,但这对我很有效。

PS:如果你想要一些相机课,我有一个很好的;)。

于 2013-11-02T11:09:46.967 回答