------ 从"画出第一个三角形"理解现代图形渲染流程
🔰 写在前面
OpenGL 是一个状态机型的图形 API。第二章《你好,三角形》是整个图形开发的起点,它帮助我们掌握从「准备绘制数据」到「渲染出第一个像素」的完整流程。
这一章最核心的任务是:绘制一个基本三角形。但在这个过程中,你会接触到一系列"图形学世界"的关键角色,包括顶点着色器、片段着色器、缓冲对象、绘图指令等。
🎨 一、画三角形的本质:像素从哪来?
我们想要让屏幕上出现一个图形,比如三角形,关键问题是:
屏幕像素是怎么被决定颜色的?
答案是:通过 OpenGL 渲染管线(Render Pipeline)中两种关键"程序":
- 顶点着色器(Vertex Shader):处理"每个点"的位置。
- 片段着色器(Fragment Shader):处理"每个像素"的颜色。
这个过程像是一场接力赛:
- 顶点 ➜ 顶点着色器 ➜ 几何组装 ➜ 光栅化成像素 ➜ 片段着色器 ➜ 最终屏幕像素。
🛠️ 二、你必须掌握的 6 个核心步骤
🔹 1. 初始化 EGL/OpenGL 上下文(略)
- 这一部分通常由 Android
GLSurfaceView
或其他框架封装了。 - 真正从 OpenGL ES API 调用 开始的,是下面这些步骤。
🔹 2. 编写 & 编译着色器(Shader)
glsl
// 顶点着色器
#version 300 es
layout(location = 0) in vec4 vPosition;
void main() {
gl_Position = vPosition;
}
glsl
// 片段着色器
#version 300 es
precision mediump float;
out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 0.0, 0.0, 1.0); // 红色
}
对比记忆:
着色器类型 | 功能 | 输入 | 输出 |
---|---|---|---|
顶点着色器 | 定位图形结构 | 顶点属性(位置) | gl_Position |
片段着色器 | 决定颜色 | 插值变量等 | 输出像素颜色 |
编译步骤:
kotlin
val vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vertexCode)
glCompileShader(vertexShader)
🔹 3. 链接程序对象(Program)
将编译好的"顶点+片段"组合成一个完整程序:
kotlin
val program = glCreateProgram()
glAttachShader(program, vertexShader)
glAttachShader(program, fragmentShader)
glLinkProgram(program)
👉 相当于"搭建了一个可以用来画图的工具箱"。
🔹 4. 准备顶点数据:VBO(Vertex Buffer Object)
kotlin
val vertices = floatArrayOf(
0.0f, 0.5f, // 顶部
-0.5f, -0.5f, // 左下
0.5f, -0.5f // 右下
)
glGenBuffers(1, vbo, 0)
glBindBuffer(GL_ARRAY_BUFFER, vbo[0])
glBufferData(GL_ARRAY_BUFFER, vertices.size * 4, buffer, GL_STATIC_DRAW)
🔹 5. 配置 VAO(Vertex Array Object)
VAO 保存着 VBO 的属性绑定信息。你不一定必须用 VAO(在OpenGL ES 2.0里没有),但在 ES 3.0 中是标准流程:
kotlin
glGenVertexArrays(1, vao, 0)
glBindVertexArray(vao[0])
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0)
🔹 6. 执行绘制(Draw)
kotlin
glUseProgram(program)
glBindVertexArray(vao[0])
glDrawArrays(GL_TRIANGLES, 0, 3)
注意绘制模式对比:
模式 | 含义 |
---|---|
GL_POINTS |
绘制点 |
GL_LINES |
绘制线段 |
GL_TRIANGLES |
每3个顶点构成一个三角形 |
🧠 总结记忆口诀
👉 用下面这句话记住整个流程:
"写好程序 → 加载数据 → 安排绑定 → 开始绘图"
也可以简记为 4 个核心词:Shader → Buffer → Bind → Draw
📌 你还需注意的几个易错点
易错点 | 解释 |
---|---|
着色器没编译成功 | 要检查 glGetShaderiv(shader, GL_COMPILE_STATUS) |
gl_Position 必须写在顶点着色器中 |
否则无法显示 |
视口没设置 | 默认可能不覆盖整个屏幕,记得加 glViewport |
float 数组大小要乘4 | OpenGL 默认用的是 byte buffer |
🏁 最后一图总结
[ 顶点数组 ] -> [ VBO ] -> [ VAO ] -> [ VertexShader ]
|
v
光栅化生成像素
|
v
[ FragmentShader ]
|
v
显示屏幕