OpenGL第一人称相机旋转和平移

OpenGL first person camera rotation and translation

本文关键字:旋转 第一人称 相机 OpenGL      更新时间:2023-10-16

我正在尝试编写一个简单的迷宫游戏,而不使用任何不推荐使用的OpenGL API(即没有即时模式)。我为迷宫中的每个瓦片使用一个顶点缓冲对象,它本质上是四个Vertex的组合:

class Vertex {
public:
    GLfloat x, y, z; // coords
    GLfloat tx, ty;  // texture coords
    Vertex();
};

并存储在VBO中,如下所示:

void initVBO()
{
    Vertex vertices[4];
    vertices[0].x = -0.5;
    vertices[0].y = -0.5;
    vertices[0].z = 0.0;
    vertices[0].tx = 0.0;
    vertices[0].ty = 1.0;
    vertices[1].x = -0.5;
    vertices[1].y = 0.5;
    vertices[1].z = 0.0;
    vertices[1].tx = 0.0;
    vertices[1].ty = 0.0;
    vertices[2].x = 0.5;
    vertices[2].y = 0.5;
    vertices[2].z = 0.0;
    vertices[2].tx = 1.0;
    vertices[2].ty = 0.0;
    vertices[3].x = 0.5;
    vertices[3].y = -0.5;
    vertices[3].z = 0.0;
    vertices[3].tx = 1.0;
    vertices[3].ty = 1.0;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*4, &vertices[0].x, GL_STATIC_DRAW);
    ushort indices[4];
    indices[0] = 0;
    indices[1] = 1;
    indices[2] = 2;
    indices[3] = 3;
    glGenBuffers(1, &ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(ushort) * 4, indices, GL_STATIC_DRAW);
}

现在,我被摄像机的动作卡住了。在我项目的早期版本中,我使用glRotatefglTranslatef平移和旋转场景,然后使用glBegin()/glEnd()模式渲染每个瓦片。但这两个函数现在已经不推荐使用了,而且我没有找到任何关于在仅使用VBO的上下文中创建相机的教程。哪种方法是正确的?我应该在每个瓦片之间循环吗?根据新的相机位置修改顶点的位置?

但这两个函数现在已经不推荐使用了,而且我没有找到任何关于在仅使用VBO的上下文中创建相机的教程。

VBO与此无关。

即时模式和矩阵堆栈是两种不同的鞋子。VBO处理将几何数据获取到渲染器,矩阵堆栈处理在那里进行转换。只有几何数据会受到VBO的影响。

至于你的问题:你自己计算矩阵,并通过均匀将它们传递给着色器。同样重要的是要理解OpenGL的矩阵函数从未被GPU加速过(除了一台机器,SGI的Onyx),所以这甚至没有提供一些性能提升。实际上,使用OpenGL的矩阵堆栈对整体性能产生了负面影响,因为它执行了冗余操作,而这些操作也必须在程序中的其他地方进行。

要获得一个简单的矩阵数学库,请查看我的linmath.hhttp://github.com/datenwolf/linmath.h

我将添加到datenwolf的答案中。我假设只有着色器管道对您可用。

要求

在OpenGL 4.0+OpenGL中,当它离开固定函数管道时,它不会为您做任何渲染。如果现在在不使用着色器的情况下渲染几何体,则使用的是不推荐使用的管道。在没有一些基本框架的情况下启动和运行将是困难的(并非不可能,但我建议使用基本框架)。首先,我建议使用GLUT(这将为您创建一个窗口,并对空闲函数和输入进行基本回调)、GLEW(用于设置渲染上下文)和gLTools(用于快速设置的矩阵堆栈、通用着色器和着色器管理器,以便您至少可以开始渲染)。

设置

我会在这里给出重要的部分,然后你可以把它们拼凑起来。在这一点上,我假设你已经正确设置了GLUT(搜索如何设置它),并且你能够向它注册更新循环并创建一个窗口(也就是说,每个帧都调用你选择的函数之一的循环[注意,这不可能是一个方法])。有关此方面的帮助,请参阅上面的链接。

  • 首先,初始化glew(通过调用glewInit()
  • 设置场景。这包括使用GLBatch类(来自glTools)创建一组要渲染为三角形的顶点,并通过调用其InitializeStockShaders()函数初始化GLShaderManager类别(也来自glTools)及其库存着色器
  • 在空闲循环中,调用着色器管理器的UseStockShader()函数以启动新的批处理。对顶点批处理调用draw()函数。有关glTools的完整概述,请访问此处
  • 不要忘记在渲染前清除窗口,并通过分别调用glClear()glSwapBuffers()在渲染后交换缓冲区

请注意,我上面给出的大多数函数都接受参数。你应该能够通过查看各自图书馆的文档来弄清楚这些。

MVP矩阵(编辑:忘记添加此部分)

OpenGL从z轴向下渲染-1,1坐标中的所有内容。它没有相机的概念,也不关心这些坐标之外的任何东西。模型视图投影矩阵是将场景转换为适合这些坐标的矩阵。

作为一个起点,在屏幕上渲染了一些东西之前,不要担心这一点(确保给顶点批处理的所有坐标都小于1)。完成后,使用glTools中的GLFrustum类设置投影矩阵(默认投影为正交投影)。您将从这个类中获得投影矩阵,并将其与模型视图矩阵相乘。模型视图矩阵是模型的变换矩阵和摄影机的变换的组合(请记住,没有摄影机,所以本质上是在移动场景)。将所有矩阵相乘形成一个矩阵后,使用UseStockShader()函数将其传递给着色器。

在GLTools中使用库存着色器(例如GLT_SHADER_FLAT),然后开始创建自己的着色器。

参考

最后,我强烈建议你读这本书:OpenGL SuperBible,综合教程和参考(第五版-确保是这个版本)

如果您真的想坚持使用最新的OpenGL API,其中删除了许多功能以支持可编程管道(OpenGL 4和OpenGL ES 2),则必须自己编写顶点和片段着色器,并在其中实现转换。您必须手动创建着色器中使用的所有属性,特别是示例中使用的坐标和纹理坐标。如果你想模仿旧的固定功能OpenGL的行为,你还需要两个统一的变量,一个用于模型视图矩阵,另一个用于投影矩阵。

你习惯做的旋转/平移是矩阵运算。在管道的顶点变换阶段(现在由您提供的顶点着色器执行),您必须将4x4变换矩阵乘以顶点位置(4个坐标,解释为4x1矩阵,如果您没有做任何太花哨的事情,其中第4个坐标通常为1)。根据该变换,得到的矢量将处于正确的相对位置。然后将投影矩阵乘以该向量,并将结果输出到片段着色器。

您可以通过查看glRotateglTranslategluPerspective的文档来了解所有这些矩阵是如何构建的。记住,矩阵乘法是非可换的,所以你把它们相乘的顺序很重要(这正是为什么你称之为glRotateglTranslate也很重要)。

关于学习GLSL和如何使用着色器,我在这里学到了,但这些教程与OpenGL 1.4和2有关,现在已经很旧了。主要区别在于,顶点着色器的预定义输入变量(如gl_Vertexgl_ModelViewMatrix)已不存在,您必须自己创建它们。