使用 OpenGL 和 GLKit 在 iOS 中绘制立方体
Drawing a cube in iOS with OpenGL and GLKit
我按照本教程 http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/ 了解查看的工作原理,但是当我尝试将它应用到我的 iOS 应用程序时,我遇到了很多麻烦
所以基本上我的理解是:
- 模型最初位于原点,相机也是如此
- 然后我们使用glm::lookAt将相机移动到正确的位置
(这在 iOS 中相当于什么?)
- 应用投影变换从相机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 个边框值(left
、right
、top
、bottom
)、near
和远 clipping
飞机。定义此方法以便 Z
值等于 near
的任何顶点位置都将转换为 -1,并且 Z
值等于 far
的每个位置都将转换为1.所有的中间都会进行线性插值。至于 X
和 Y
它们将根据转换后的 Z
值进行缩放,因此对于 Z
at 0 将按 0 缩放,Z
at near
乘以 1.0,然后对其余部分进行线性外推。
lookAt
其实和模型矩阵很像但是颠倒了。如果你向后移动相机,它与向前移动物体是一样的,如果你向左旋转,物体将看起来被移动并向右旋转等等......
模型矩阵将使用基向量和平移简单地变换所有顶点位置。该矩阵的相关部分是顶部 3x3 部分,它们是基本向量,底部(或在某些实现中是右侧)3x1 向量 (1x3),它是翻译。最简单的想象就是在坐标系内部定义一个坐标系:零值(原点)在矩阵的平移部分,X轴是3x3矩阵的第一行(列),Y第二和 Z 第三。这3个向量的长度代表了尊重坐标的比例......它们都合在一起。
我按照本教程 http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/ 了解查看的工作原理,但是当我尝试将它应用到我的 iOS 应用程序时,我遇到了很多麻烦
所以基本上我的理解是:
- 模型最初位于原点,相机也是如此
- 然后我们使用glm::lookAt将相机移动到正确的位置 (这在 iOS 中相当于什么?)
- 应用投影变换从相机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 个边框值(left
、right
、top
、bottom
)、near
和远 clipping
飞机。定义此方法以便 Z
值等于 near
的任何顶点位置都将转换为 -1,并且 Z
值等于 far
的每个位置都将转换为1.所有的中间都会进行线性插值。至于 X
和 Y
它们将根据转换后的 Z
值进行缩放,因此对于 Z
at 0 将按 0 缩放,Z
at near
乘以 1.0,然后对其余部分进行线性外推。
lookAt
其实和模型矩阵很像但是颠倒了。如果你向后移动相机,它与向前移动物体是一样的,如果你向左旋转,物体将看起来被移动并向右旋转等等......
模型矩阵将使用基向量和平移简单地变换所有顶点位置。该矩阵的相关部分是顶部 3x3 部分,它们是基本向量,底部(或在某些实现中是右侧)3x1 向量 (1x3),它是翻译。最简单的想象就是在坐标系内部定义一个坐标系:零值(原点)在矩阵的平移部分,X轴是3x3矩阵的第一行(列),Y第二和 Z 第三。这3个向量的长度代表了尊重坐标的比例......它们都合在一起。