我正在将一个玩具GL ES 1.1代码库移植到GL ES 2.0。没有内置的modelview矩阵运算,所以我尝试用4x4"当前矩阵"上的运算来替换glScalef
、glTranslatef
、glRotatef
调用。
然而,我的矩阵数学有点粗略,乘法排序和行与列主记法等都有问题,如果有人能快速概述我应该在这里介绍的语义,我会很高兴的。
例如,假设我从一个恒等矩阵开始,然后(在ES1.1代码中)按顺序执行:
glTranslate(A);
glRotate(B);
glTranslate(C);
我需要对我的"当前矩阵"进行什么计算,才能在我发送给着色器的最终模型视图矩阵中复制此功能?我试着保留一个矩阵,对于每一次运算,都要制作一个新的矩阵来表示它,并将它乘以当前的矩阵。结果是不正确的,虽然,我认为有一些东西我不知道前/后乘法,-major,等等
有人愿意在这里对这个理论说几句话吗?谢谢(在使其超高效之前,我想先了解基本知识。)
(这:在OpenGL ES 2.0中实现固定功能的管道效率?)?问了一个类似的问题,但答案谈到了如何提高效率,并没有真正启发我数学等价性。)
基本上,您正在寻找一个opengl矩阵堆栈的替代品来进行转换。
假设您使用的是c/c++,我建议您查看一下GLM库:http://glm.g-truc.net/
它是一个只有头的库,易于使用,是opengl 2.0之前矩阵堆栈的完美替代品。它甚至为不推荐使用的glu函数提供了功能,如gluOrtho()和gluPerspective()。Glm的矩阵可以很容易地传递到着色器,因为它们是在考虑着色器的情况下构思的。
您将不得不对代码进行一些更改;例如,将您自己的顶点/矩阵定义转换为glm::vec3/glm::mat4。
以下是我如何构造传递给顶点着色器的mvp矩阵的示例:在这种情况下,actorInstance类有一些属性,如位置(在世界中)和旋转,定义为glm::vec3。构建的模型、模型/视图和模型/视图/投影矩阵都是类的属性:
void CActorInstance::update(glm::mat4 viewMatrix, glm::mat4 projectionMatrix)
{
// act according to class behavior
this->actorClass->act(&input, &world, &direction, &rotation);
// calculate the translation matrix
glm::mat4 translate = glm::mat4();
translate = glm::translate( glm::mat4(), world);
// calculate the rotation matrix
glm::mat4 rotateX = glm::rotate( glm::mat4(1.0f), rotation.x, glm::vec3(1,0,0));
glm::mat4 rotateY = glm::rotate( glm::mat4(1.0f), rotation.y, glm::vec3(0,1,0));
glm::mat4 rotateZ = glm::rotate( glm::mat4(1.0f), rotation.z, glm::vec3(0,0,1));
glm::mat4 rotate = rotateX * rotateY * rotateZ;
// calculate the model matrix
mMatrix = translate * rotate;
// calculate the model/view matrix
mvMatrix = viewMatrix * mMatrix;
// calculate the model/view/projection matrix
mvpMatrix = projectionMatrix * mvMatrix;
};
显然,每个对象的mvp矩阵在每帧都会根据对象的位置和旋转进行更新。viewMatrix和projectionMatrix是从我的相机课上传下来的。然后使用该矩阵来渲染网格:
void CMesh::renderMesh(GLuint program, glm::mat4 *mvp)
{
glUseProgram(program);
int mvpLocation = glGetUniformLocation(program, "mvpMatrix");
int texLocation = glGetUniformLocation(program, "baseMap");
glUniformMatrix4fv( mvpLocation, 1, GL_FALSE, glm::value_ptr(*mvp));
// rendering code ommitted
};
希望这有帮助:)
EDIT:实现矩阵堆栈的建议
stl::stack<glm::mat4> matrixStack; // the matrix stack
matrixStack.push_back(glm::mat4()); // push an identity matrix on the stack
这就是你的glLoadIdentity()。。。
我试着保留一个矩阵,对于每个操作表示它的矩阵,并将其乘以当前矩阵。
好吧,事情就是这样做的。没有什么特别的。
调用glRotate
、glTranslate
和glScale
(当然还有glOrtho
、glFrustum
、gluLookAt
和gluPerspective
)的相应矩阵很容易构建,如链接中所示。
您只需将当前矩阵M
:上的各个变换矩阵T
进行右乘即可
M' = M * T
因为我们(或您想要复制的旧OpenGL)希望在绘制之前调用的最后一个变换首先应用于对象。
然后,你还必须与矩阵的存储保持一致(行主与列主),无论你选择哪一个。但是,由于您使用的是OpenGL,列主存储将是一个好主意,因为它可以让您更容易地将它们上传到GLSL(尽管没有反对转置标志),与矢量化SIMD指令(如x86的SSE)配合得更好,而且与为OpenGL设计的其他库配合得更好(出于同样的原因使用列主矩阵)。你只需要与存储保持一致,不要在不同的函数调用中混淆它。