使用 OpenGL 和 GLKit 在 iOS 中绘制立方体

Drawing a cube in iOS with OpenGL and GLKit

我按照本教程 http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/ 了解查看的工作原理,但是当我尝试将它应用到我的 iOS 应用程序时,我遇到了很多麻烦

所以基本上我的理解是:

  1. 模型最初位于原点,相机也是如此
  2. 然后我们使用glm::lookAt将相机移动到正确的位置 (这在 iOS 中相当于什么?)
  3. 应用投影变换从相机space移动到同质单位立方体space

从基本的 iOS 教程中,我发现了投影矩阵的以下计算

float aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height);
GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f);
_modelViewProjectionMatrix = projectionMatrix;

我真的不明白...他们是怎么想出 65 的?

另一个教程是这样做的:

glViewport(0, 0, self.view.bounds.size.width,self.view.bounds.size.height);

实施: 我当前的应用程序只显示蓝屏(基本上是我的立方体的颜色) 我假设这是因为相机当前位于原点

我有以下数据集

static const GLfloat cubeVertices[] = {
    -1.0f,-1.0f,-1.0f, // triangle 1 : begin
    -1.0f,-1.0f, 1.0f,
    -1.0f, 1.0f, 1.0f, // triangle 1 : end
    1.0f, 1.0f,-1.0f, // triangle 2 : begin
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f,-1.0f, // triangle 2 : end
    1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f,-1.0f,
    1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f, 1.0f,
    -1.0f,-1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    -1.0f,-1.0f, 1.0f,
    1.0f,-1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f,-1.0f,
    1.0f,-1.0f,-1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f,-1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    1.0f, 1.0f,-1.0f,
    -1.0f, 1.0f,-1.0f,
    1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f,-1.0f,
    -1.0f, 1.0f, 1.0f,
    1.0f, 1.0f, 1.0f,
    -1.0f, 1.0f, 1.0f,
    1.0f,-1.0f, 1.0f
};

这是我的设置,非常基础 iOS 教程

- (void)setupGL {
    [EAGLContext setCurrentContext:self.context];
    [self loadShaders];

    glGenBuffers(1, &_vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);

    glVertexAttribPointer (GLKVertexAttribPosition,
                           3,
                           GL_FLOAT, GL_FALSE,
                           0,
                           BUFFER_OFFSET(0));
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    //glBindVertexArrayOES(0);
}

以及我的 drawInRect 和更新方法

- (void)update {
    //glViewport(0, 0, self.view.bounds.size.width, self.view.bounds.size.height);
    float aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height);
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(65.0f), aspect, 0.1f, 100.0f);
    _modelViewProjectionMatrix = projectionMatrix;

}

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(_program);
    glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
    glDrawArrays(GL_TRIANGLES, 0, 12*3);
}

和我的顶点着色器

attribute vec4 position;
uniform mat4 modelViewProjectionMatrix;

void main() {
    gl_Position = modelViewProjectionMatrix * position;
}

和我的片段着色器

void main() {
    gl_FragColor = vec4 (0.165, 0.427, 0.620, 1.0);
}

从答案开始,您正在寻找但缺少的是 GLKMatrix4MakeLookAt。剩下的就看你有兴趣深入了解了。

您的假设似乎是正确的,但我认为您未能理解矩阵系统和 openGL 的真正工作原理。不过,您似乎确实了解如何使用它。因此,首先我们通常看到的是 3 个矩阵分量,然后可以将它们作为乘积插入到着色器中,或者作为每个分量传递给着色器并在那里相乘。

第一个组成部分是投影矩阵。该分量反映屏幕投影,通常设置为 "ortho" 或 "frustum"。 "ortho" 是一个正交投影,这意味着无论距离远近,物体都会显示相同的大小。 "frustum" 会产生一种效果,使对象根据距离显得更大或更小。在您的情况下,您使用的是 "frustum" 和便利函数 GLKMatrix4MakePerspective。第一个参数描述了视野,在你的示例中是 65 度角,第二个参数是纵横比,它应该反映 screen/view 比率,最后两个是裁剪平面。使用 "frustum" 的等价物将是:

    GLfloat fieldOfView = M_PI_2;
    GLfloat near = .1f;
    GLfloat far = 1000.0f;
    GLfloat screenRatio = 1.0f/2.0f;

    GLfloat right = tanf(fieldOfView)*.5f * near; // half of the tagens of field of view
    GLfloat left = -right; // symetry
    GLfloat top = right*screenRatio; // scale by screen ratio
    GLfloat bottom = -top; // symetry

    GLKMatrix4MakeFrustum(left, right, bottom, top, near, far);

其次是视图矩阵,通常用作"camera"。要使用这个,最简单的方法是调用某种形式的 "lookAt",在你的例子中是 GLKMatrix4MakeLookAt。这应该可以回答您的问题 "what is the equivalent of this in iOS?".

第三个是模型矩阵,它描述了对象在你的坐标系中的位置。这通常用于您可以将模型放在所需位置,设置特定的旋转并根据需要缩放它。

所以这一切是如何结合在一起的,你在某个时候将所有矩阵相乘,并将其称为模型-视图-投影矩阵。然后使用该矩阵乘以每个顶点位置来描述它在屏幕上的投影。

glViewport 完全没有这一部分。此函数将定义您要绘制到缓冲区的哪一部分,仅此而已。尝试将所有值除以一半,看看会发生什么(比任何其他解释更好)。

从数学的角度解释一下发生了什么,openGL 实现如下:openGL 将只绘制所有轴上 [-1, 1] 框内的片段(像素)。这意味着投影没有魔法来覆盖它,但是顶点位置被转换,所以正确的值适合那里。

对于 frustum 实例,它将采用 4 个边框值(leftrighttopbottom)、near 和远 clipping 飞机。定义此方法以便 Z 值等于 near 的任何顶点位置都将转换为 -1,并且 Z 值等于 far 的每个位置都将转换为1.所有的中间都会进行线性插值。至于 XY 它们将根据转换后的 Z 值进行缩放,因此对于 Z at 0 将按 0 缩放,Z at near 乘以 1.0,然后对其余部分进行线性外推。

lookAt其实和模型矩阵很像但是颠倒了。如果你向后移动相机,它与向前移动物体是一样的,如果你向左旋转,物体将看起来被移动并向右旋转等等......

模型矩阵将使用基向量和平移简单地变换所有顶点位置。该矩阵的相关部分是顶部 3x3 部分,它们是基本向量,底部(或在某些实现中是右侧)3x1 向量 (1x3),它是翻译。最简单的想象就是在坐标系内部定义一个坐标系:零值(原点)在矩阵的平移部分,X轴是3x3矩阵的第一行(列),Y第二和 Z 第三。这3个向量的长度代表了尊重坐标的比例......它们都合在一起。