返回

OpenGL ES 入门:点亮金字塔——色彩、纹理、混合与 GLKit 实现

IOS

在本文中,我们将深入浅出地探讨 OpenGL ES 的世界,通过创建一个金字塔模型并赋予其色彩、纹理和混合效果,逐步揭开其渲染管线的奥秘。我们将使用 GLKit 这个易于使用的框架,简化 OpenGL ES 的使用。准备好开启这段激动人心的渲染之旅了吗?

渲染管线:从顶点到片段

OpenGL ES 的渲染管线是一个多阶段的过程,将原始顶点数据转换为屏幕上的像素。该管线的主要阶段包括:

  • 顶点着色器: 处理顶点数据,进行变换、照明和其它顶点级操作。
  • 片段着色器: 处理单个像素,计算其颜色、深度和其它片段级属性。
  • 光栅化: 将顶点组装成原始片段。
  • 混合: 根据混合规则,将片段颜色与帧缓冲中的现有颜色混合。

绘制金字塔

让我们开始创建我们的金字塔模型。首先,我们需要定义顶点数据:

const float vertices[] = {
    // 前面
    0.0f,  1.0f,  0.0f,
   -1.0f, -1.0f,  1.0f,
    1.0f, -1.0f,  1.0f,

    // 右面
    0.0f,  1.0f,  0.0f,
    1.0f, -1.0f,  1.0f,
    1.0f, -1.0f, -1.0f,

    // 后面
    0.0f,  1.0f,  0.0f,
    1.0f, -1.0f, -1.0f,
   -1.0f, -1.0f, -1.0f,

    // 左面
    0.0f,  1.0f,  0.0f,
   -1.0f, -1.0f, -1.0f,
   -1.0f, -1.0f,  1.0f
};

接下来,我们需要定义用于应用于顶点的颜色数据:

const float colors[] = {
    // 前面
    1.0f, 0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,

    // 右面
    1.0f, 0.0f, 0.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 0.0f, 1.0f,

    // 后面
    1.0f, 0.0f, 0.0f, 1.0f,
    0.0f, 1.0f, 0.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,

    // 左面
    1.0f, 0.0f, 0.0f, 1.0f,
    0.0f, 0.0f, 1.0f, 1.0f,
    0.0f, 1.0f, 0.0f, 1.0f
};

使用 GLKit

GLKit 是一个 Apple 提供的框架,可以简化 OpenGL ES 的使用。它提供了高层次的接口,使我们能够轻松配置渲染状态、创建缓冲区和加载着色器。

首先,我们需要创建一个 GLKView,这是 OpenGL ES 的视图类:

let glkView = GLKView(frame: self.view.bounds)

然后,我们需要将视图添加到我们的视图控制器并设置其委托:

self.view.addSubview(glkView)
glkView.delegate = self

顶点着色器

顶点着色器用于变换和处理每个顶点数据。我们使用 GLKit 的 GLKMatrix4 类来定义模型视图投影矩阵:

var modelViewProjectionMatrix: GLKMatrix4!

glkView(_:drawIn:) 方法中,我们更新矩阵并设置 uniform 变量:

modelViewProjectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(60.0), Float(self.view.bounds.width / self.view.bounds.height), 0.1, 100.0)

顶点着色器代码如下:

#version 300 es
in vec4 position;
in vec4 sourceColor;

uniform mat4 modelViewProjectionMatrix;

out vec4 vColor;

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

片段着色器

片段着色器用于计算每个片段的颜色。它接收顶点着色器输出的 vColor 变量并混合纹理颜色:

#version 300 es
precision mediump float;

in vec4 vColor;

uniform sampler2D texture;

out vec4 fragColor;

void main() {
    fragColor = texture2D(texture, gl_PointCoord) * vColor;
}

纹理和混合

为了给金字塔添加纹理,我们需要加载图像并创建纹理对象:

let texture = try! GLKTextureLoader.textureWithContentsOfFile("texture.png", options: nil)

glkView(_:drawIn:) 方法中,我们启用纹理并设置 uniform 变量:

glEnable(GLenum(GL_TEXTURE_2D))
glBindTexture(GLenum(GL_TEXTURE_2D), texture.name)
glUniform1i(uniforms["texture"], 0)

混合使我们能够将纹理颜色与顶点颜色进行混合。我们使用 GLKit 的 GLKMesh 类来渲染网格并启用混合:

let mesh = GLKMesh(vertexCount: vertices.count, vertices: vertices, textureCoordinates: nil, colors: colors, indices: nil)
glkView.enable(GLKView.EAGLContext.GLES2.GLKViewDrawableColorFormat.RGBA8888, forTextureOf: mesh)

完整代码

以下是完整代码:

import GLKit

class ViewController: UIViewController {

    var glkView: GLKView!
    var modelViewProjectionMatrix: GLKMatrix4!
    var uniforms: [String: GLint] = [:]

    override func viewDidLoad() {
        super.viewDidLoad()

        glkView = GLKView(frame: self.view.bounds)
        self.view.addSubview(glkView)
        glkView.delegate = self
    }

    override func glkView(_ view: GLKView, drawIn rect: CGRect) {
        glClearColor(0.0, 0.0, 0.0, 1.0)
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT))
        glEnable(GLenum(GL_DEPTH_TEST))

        let program = GLKProgram()
        let vertexShader = try! loadShader("VertexShader.glsl", type: GL_VERTEX_SHADER)
        let fragmentShader = try! loadShader("FragmentShader.glsl", type: GL_FRAGMENT_SHADER)
        program.attachShader(vertexShader)
        program.attachShader(fragmentShader)
        program.link()
        glUseProgram(program.program)

        modelViewProjectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(60.0), Float(self.view.bounds.width / self.view.bounds.height), 0.1, 100.0)
        uniforms["modelViewProjectionMatrix"] = glGetUniformLocation