1、概述
下面我通过使用OpenGL绘制三角形,来解析OpenGL的绘制原理。
我们先来回顾下OpenGL的渲染管道流程:
顶点数据 -> 顶点着色器 -> 图元装配 -> 几何着色器 -> 光栅化 -> 片断着色器 -> 逐片断处理/测试与混合 -> 帧缓冲
简化掉不可操作的部分,渲染管道流程为:
顶点数据 -> 顶点着色器 -> 片断着色器 -> 帧缓冲
所以,使用OpenGL进行绘制,需提供用于输入的顶点数据和提供用于输出的帧缓存区。
通过调用下面的方法,就触发了OpenGL的这个渲染管道流程:
glDrawArrays(GL_TRIANGLES, 0, 3);
GL_TRIANGLES:图元装配阶段使用,表示这些顶点是用来组成三角形的
第三个参数:一个有3个顶点
思考:
glDrawArrays 第一个参数表示图元构造的形状,有GL_TRIANGLES(三角形)、GL_POINTS(点)、GL_LINES(线)。但就是没有四边形或多边形,为什么?
三个点一定只能画出一个面,超过三个点,绘制的图形就不一定只有一个面了。比如四个点,根据不同的连接方式,可以组成不同的图形,不能确定唯一的图形,所以没有多边形的绘制方式。要构造四边形,可以通过绘制两个三角形拼接成四边形。
2、输入部分
顶点数据:顶点坐标,顶点颜色,顶点法向量等。绘制三角形的话,只需要顶点坐标和顶点颜色。
顶点坐标:
OpenGL接收的x,y坐标都是介于[-1,1]之间。
本案例的顶点为:
const GLfloat vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
顶点着色器:
attribute vec4 Position; // position of vertex
attribute vec4 SourceColor; // color of vertex
varying vec4 DestinationColor;
void main(void) {
DestinationColor = SourceColor;
gl_Position = Position;
}
是不是很简单,就那么几句,就构成了顶点着色器。这里可以对顶点数据进行加工处理,比如进行光照计算处理。本案例,我们不需要计算光照,保持上面代码就可以。
为顶点着色器传值:
顶点着色器定义两个作为输入变量,我们外部只需要把顶点值赋值给这两个变量就完成了传值:
attribute vec4 Position:接收顶点坐标值
attribute vec4 SourceColor:接收顶点颜色值
通过以下代码,得到上面两个变量:
_colorSlot = glGetAttribLocation(_glProgram, "SourceColor");
_positionSlot = glGetAttribLocation(_glProgram, "Position");
通过以下代码,给两个变量分别传入顶点坐标值和顶点颜色值
//顶点坐标
const GLfloat vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
};
// 给_positionSlot传递顶点坐标数据
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glEnableVertexAttribArray(_positionSlot);
// 顶点颜色值
const GLfloat Colors[] = {
0,0,0,1, // 左下,黑色
1,0,0,1, // 右下,红色
0,0,1,1, // 左上,蓝色
};
// 取出Colors数组中的每个坐标点的颜色值,赋给_colorSlot
glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, 0, Colors);
glEnableVertexAttribArray(_colorSlot);
片元着色器:
varying lowp vec4 DestinationColor;
void main(void) {
// gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // must set gl_FragColor for fragment shader
gl_FragColor = DestinationColor;
}
比顶点着色器还简单。在这里,我们可以改变顶点的颜色。本案例不需要改变定义颜色,保存默认就好。
片元着色器的输入变量为DestinationColor,这个值是顶点着色器传过来的:
varying vec4 DestinationColor;
void main(void) {
//把顶点颜色值传给片元着色器
DestinationColor = SourceColor;
gl_Position = Position;
}
3、输出部分
帧缓冲区:
GLuint _frameBuffer; // 帧缓冲区
glGenFramebuffers(1, &_frameBuffer);
//设置为当前framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
上面定义了一个帧缓冲区,在glDrawArrays调用前,使用glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer)指定输出的帧缓冲区,那么OpenGL管道流程执行完成,就会把绘制的内容输出到指定的帧缓冲区。
渲染缓冲区:
这个可以不算OpenGL的内容了。对于OpenGL,把内容输出到帧缓冲区,就算完成了。加入渲染缓冲区,只是为了在界面把图形展示出来。
glGenRenderbuffers(1, &_colorRenderBuffer);
// 设置为当前renderBuffer
glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
//为color renderbuffer 分配存储空间
[_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
// FBO用于管理colorRenderBuffer,离屏渲染
glGenFramebuffers(1, &_frameBuffer);
//设置为当前framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
// 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);
上面会有把渲染缓冲区与具体的CALayer关联起来。更多的上下文代码,
请参考demo:https://github.com/wulang150/MyProject/tree/master/MyProject/Controller/TmpTestController/OpenGL
运行结果为:
如上图,我只设置了三个点的颜色,OpenGL自动帮我们填充了三角形内部的颜色,并且是通过渐变的方式填充。这是光栅化颜色插值。
4、更多OpenGL知识
OpenGL ES 2.0 (iOS)[01]: 一步从一个小三角开始
OpenGL ES 2.0 Programming Guide
网友评论