这
gl_Position = projection * model * view * vec4(vertexData, 1);
是错的。矩阵乘法不可交换,即运算顺序很重要。顶点位置上的变换,按顺序是:
OpenGL 使用的列向量的矩阵乘法是左结合的,即从右到左。因此,语句 R 侧的表达式应该是
gl_Position = projection * view * model * vec4(vertexPosition, 1);
但是,您可以将视图和模型转换收缩为复合模型视图(第一个模型,然后是视图)转换。这节省了一个完整的矩阵乘法
gl_Position = projection * modelview * vec4(vertexPosition, 1);
投影应保持独立,因为其他着色步骤可能需要顶点的眼睛空间位置,这是modelview * position
未应用投影的结果。
顺便说一句:您正在转换顶点位置,而不是数据。顶点包含大量属性(不仅仅是位置),因此称其为“数据”在语义上是错误的。
平移和旋转对象的最佳方法是什么?
这些是模型视图转换的一部分。您应该在 CPU 上只创建一次转换矩阵并将其传递给 GPU。在着色器中执行此操作将强制 GPU 为每个顶点重做整个计算。你不想这样做。
因评论而更新
假设您正在使用我的→linmath.h。然后在你的绘图功能中,你已经为你的场景设置了脚手架,即设置视口、构建的投影和视图矩阵
#include <linmath.h>
/* ... */
void display(void)
{
mat4x4 projection;
mat4x4 view;
glClear(…),
glViewport(…);
mat4x4_frustum(projection, …);
// linmath.h doesn't have a look_at function... yet
// I'll add it soon
mat4x4_look_at(view, …);
然后对于每个对象,您都有一个位置和方向(平移和旋转)。方向最方便地存储在四元数中,但对于处理向量,矩阵表示效果更好。所以我们迭代场景中的对象
for(int i_object = 0; i_object < scene->n_objects; i++) {
Object * const obj = scene->objects + i;
mat4x4 translation, orientation, model_view;
mat4x4_translate(translation, obj->pos.x, obj->pos.y, obj->pos.z);
mat4x4_from_quat(orientation, obj->orientation);
mat4x4_mul(model_view, translation, orientation);
model_view
现在包含模型矩阵。接下来我们将view
矩阵相乘。请记住,矩阵乘法是从右到左的(mat4x4_mul 可以输出到其输入操作数之一)。
mat4x4_mul(model_view, view, model_view);
现在model_view
包含完整的复合模型方向和平移和视图矩阵。我们现在需要做的就是绑定用于对象的着色器程序
glUseProgram(obj->shader->program);
设置制服
glUniformMatrix4f(obj->shader->location.projection, 1, GL_FALSE, projection);
glUniformMatrix4f(obj->shader->location.modelview, 1, GL_FALSE, model_view);
// and a few others...
并绘制对象
object_draw(obj);
}
/* ... */
}