OpenGL ES 绘制基础图形
OpenGL ES基本概念
OpenGL ES (Embedded-System) 是专为嵌入式设备(如手机、平板、VR 设备)设计的图形 API,是 OpenGL 的轻量级版本。
|下面是一个Android使用 OpenGL ES的基本框架
- MainActivity 设置一个 GLSurfaceView 直接显示
class MainActivity : ComponentActivity() {
private lateinit var glSurfaceView: GLSurfaceView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
glSurfaceView = GLSurfaceView(this)
// 设置渲染器
glSurfaceView.setRenderer(GLRender())
setContentView(glSurfaceView)
}
override fun onResume() {
super.onResume()
glSurfaceView.onResume()
}
override fun onPause() {
super.onPause()
glSurfaceView.onPause()
}
}
- GLRender 着色器代码已经其内部函数含义,由于没有绘制样式,运行后显示黑色屏幕,因为onSurfaceCreated时将颜色重制为黑色。
class GLRender : Renderer {
/**
* 应用程序窗口创建时调用的函数
* 一般处理一些全局的设置
*/
override fun onSurfaceCreated(gl: GL10, config: EGLConfig?) {
// 设置需要对透视进行修正
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST)
// 设置清理屏幕颜色 RGBA 取值范围为 0-1之间的float数 0 0 0 就是黑色
gl.glClearColor(0F, 0F, 0F, 1F)
// 启动深度缓存 如果不启动深度缓存效果,后绘制的东西可能会覆盖先绘制的东西。
// 启动深度缓存后,按照实际Z值来处理远近效果,更符合3D效果。
gl.glEnable(GL10.GL_DEPTH_TEST)
}
/**
* 应用程序窗口改变时调用的函数
*/
override fun onSurfaceChanged(gl: GL10, width: Int, height: Int) {
val ratio: Float = width / height.toFloat()
// 设置GL场景的大小
gl.glViewport(0, 0, width, height)
// 设置投影矩阵为投影矩阵
// 定义视锥体(View Frustum)和投影方式(透视或正交),决定物体如何从 3D 空间投影到 2D 屏幕上。
// 其他矩阵类型还有模型矩阵,纹理矩阵等
gl.glMatrixMode(GL10.GL_PROJECTION)
// 重制投影矩阵
// 作用是将当前矩阵重置为单位矩阵(Identity Matrix)。
// 若不调用该函数,矩阵会保留之前的变换状态,可能导致后续变换叠加在已有变换上,产生非预期的结果
gl.glLoadIdentity()
// 创建一个透视投影矩阵,设置窗口大小
gl.glFrustumf(-ratio, ratio, -1F, 1F, 1F, 10F)
}
/**
* Android在图形绘制时调用的方法,除非设置手动刷新,否则会一直调用。
*/
override fun onDrawFrame(gl: GL10) {
// 清理缓存 颜色和深度
gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)
// 设置模型视图矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW)
// 重制矩阵
gl.glLoadIdentity()
// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向
GLU.gluLookAt(gl,0F,0F,3F,0F,0F,0F,0F,1F,0F)
}
}
通过 OpenGL ES 完成 2D图形的绘制。
- OpenGL的坐标向线如下:
绘制三角形
private val one: Int = 0x10000
// 正确创建直接缓冲区
private val triggerBuffer: IntBuffer by lazy {
// 创建直接缓冲区并设置本地字节序
val byteBuffer = ByteBuffer.allocateDirect(3 * 3 * 4) // 3个顶点,每个3个分量,每个int占4字节
byteBuffer.order(ByteOrder.nativeOrder())
// 获取 IntBuffer 视图
val intBuffer = byteBuffer.asIntBuffer()
// 填充顶点数据
intBuffer.put(
intArrayOf(
0, one, 0, // 顶点1
-one, -one, 0, // 顶点2
one, -one, 0 // 顶点3
)
)
// 重置位置到起始点
intBuffer.position(0)
intBuffer
}
override fun onDrawFrame(gl: GL10) {
// 清理缓存 颜色和深度
gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)
// 设置模型视图矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW)
// 重制矩阵
gl.glLoadIdentity()
// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向
GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)
// 允许设置顶点
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)
// 给三角形设置顶点数组
// size 含义代表每个点有 xyz三个方向数据所以size为3;
// GL10.GL_FIXED 为整形数据 还可以设置 GL_FLOAT 浮点型,以及其他类型,甚至是无符号类型
// 第三个参数代表相邻顶点之间的偏移量,0 表示紧密排列。即下一个顶点数据紧跟当前顶点数据。非0的话代表交替存储顶点数据额外便宜的字节。
gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)
// 绘制三角形 GL_TRIANGLES 代表三角形,开始0 绘制3个点。
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)
// 关闭顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)
}
上述代码绘制了一个三角形如下:
需要注意的是创建三角形顶点使用的buffer必须使用直接缓冲区,提高数据读取的速度。
- 绘制正方形 , 三角形右侧增加绘制正方形。
// 创建正方形直接缓冲区
private val squareBuffer: IntBuffer by lazy {
// 创建直接缓冲区并设置本地字节序
val byteBuffer = ByteBuffer.allocateDirect(4 * 3 * 4) // 4个顶点,每个3个分量,每个int占4字节
byteBuffer.order(ByteOrder.nativeOrder())
// 获取 IntBuffer 视图
val intBuffer = byteBuffer.asIntBuffer()
// 填充顶点数据
intBuffer.put(
intArrayOf(
one, one, 0, // 顶点1
-one, one, 0, // 顶点2
one, -one, 0, // 顶点3
-one, -one, 0 // 顶点4
)
)
// 重置位置到起始点
intBuffer.position(0)
intBuffer
}
/**
* Android在图形绘制时调用的方法,除非设置手动刷新,否则会一直调用。
*/
override fun onDrawFrame(gl: GL10) {
// 清理缓存 颜色和深度
gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)
// 设置模型视图矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW)
// 重制矩阵
gl.glLoadIdentity()
// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向
GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)
// 设置模型位置
gl.glTranslatef(-2.0F, 0.0F, -4.0F)
// 允许设置顶点
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)
// 给三角形设置顶点数组
// size 含义代表每个点有 xyz三个方向数据所以size为3;
// GL10.GL_FIXED 为整形数据 还可以设置 GL_FLOAT 浮点型,以及其他类型,甚至是无符号类型
// 第三个参数代表相邻顶点之间的偏移量,0 表示紧密排列。即下一个顶点数据紧跟当前顶点数据。非0的话代表交替存储顶点数据额外便宜的字节。
gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)
// 绘制三角形 GL_TRIANGLES 代表三角形,开始0 绘制3个点。
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)
// 重制模型矩阵
gl.glLoadIdentity()
// 绘制正方形 两个相同的三角形组成正方形,可以采用三角形的带
// 设置模型位置 为了显示效果更小所以 Z 周弄远了。Z越大看起来离我们越近显示效果越大(也可以通过坐标修改)
gl.glTranslatef(1F, 0.0F, -6.0F)
// 设置顶点数据
gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)
// 绘制正方形
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP,0,4)
// 关闭顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)
}
绘制三角形以后,重制模型矩阵gl.glLoadIdentity() ,设置模型位置 gl.glTranslatef,设置顶点数据gl.glVertexPointer,绘制正方形。gl.glDrawArrays。
效果如下
在0penGL中绘制2D多边形常用的函数以及常量:
- glEnableClientState/glDisableClientState:状态开关
- glVertexPointer:设置顶点数据
- glDrawArrays:绘制函数
- GL_VERTEX_ARRAY:顶点数组
- GL_BYTE/GL_SHORT/GL_FIXED/GL_FLOAT:顶点数据的类理
- GL_LINES:线
- GL_TRIANGLES:三角形
- GL_TRIANGLE_STRIP:三角形带
如何绘制颜色
// 创建颜色buffer 分为 RGBA
private val colorBuffer: IntBuffer by lazy {
// 创建直接缓冲区并设置本地字节序
val byteBuffer = ByteBuffer.allocateDirect(3 * 4 * 4)
byteBuffer.order(ByteOrder.nativeOrder())
// 获取 IntBuffer 视图
val intBuffer = byteBuffer.asIntBuffer()
// 填充顶点数据
intBuffer.put(
intArrayOf(
one, 0, 0, one, // 顶点1
0, one, 0, one, // 顶点2
0, 0, one, one // 顶点3
)
)
// 重置位置到起始点
intBuffer.position(0)
intBuffer
}
override fun onDrawFrame(gl: GL10) {
// 清理缓存 颜色和深度
gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)
// 设置模型视图矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW)
// 重制矩阵
gl.glLoadIdentity()
// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向
GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)
// 设置模型位置
gl.glTranslatef(-2.0F, 0.0F, -4.0F)
// 允许设置顶点
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)
// 允许设置颜色数组
gl.glEnableClientState(GL10.GL_COLOR_ARRAY)
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer)
// 给三角形设置顶点数组
// size 含义代表每个点有 xyz三个方向数据所以size为3;
// GL10.GL_FIXED 为整形数据 还可以设置 GL_FLOAT 浮点型,以及其他类型,甚至是无符号类型
// 第三个参数代表相邻顶点之间的偏移量,0 表示紧密排列。即下一个顶点数据紧跟当前顶点数据。非0的话代表交替存储顶点数据额外便宜的字节。
gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)
// 绘制三角形 GL_TRIANGLES 代表三角形,开始0 绘制3个点。
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)
// 绘制完成禁用颜色数组绘制
gl.glDisableClientState(GL10.GL_COLOR_ARRAY)
// 重制模型矩阵
gl.glLoadIdentity()
// 绘制正方形 两个相同的三角形组成正方形,可以采用三角形的带
// 设置模型位置 为了显示效果更小所以 Z 周弄远了。Z越大看起来离我们越近显示效果越大
gl.glTranslatef(1F, 0.0F, -6.0F)
// 设置顶点数据
gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)
// 设置颜色,R G B A 的色值
gl.glColor4f(1F, 0F, 0F, 1F)
// 绘制正方形
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4)
// 关闭顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)
}
// 设置颜色,R G B A 的色值
gl.glColor4f(1F, 0F, 0F, 1F) 这里只给红色和透明度设置成1 显示效果为红色
给正方形上色,直接采用设置RGBA的方式,单一的颜色
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer) 给三角形绘制平滑过渡的颜色,需要开启设置颜色数组,使用结束后关闭。
效果如下:
- 颜色所需要的常用函数
常用函数及常量
- glColor4f:设置单一颜色
- glColorPointer:设置颜色数组
- GL_COLOR_ARRAY:颜色数组(通过状态开关函数
- glDisableClientState 来操作)
- GL_COLOR_BUFFER_BIT:颜色缓存
图形的变换
OpenGLES有三种不同类型的变换,它们分别是:
转移(Translate):在3D空间中移动物体
旋转(Rotate):绕X,Y,或者 Z 轴进行旋转
缩放(Scale):改变物体的大小
- 为了防止上述三角形和正方形的位置重叠,进行了 gl.glTranslatef(1F, 0.0F, -6.0F) 这就是平移操作。
gl.glTranslatef(1F, 0.0F, -6.0F) 代表分别在 x y z上分别平移多少距离。
- 旋转操作
/**
* 定义旋转角度
*/
var rotate1: Float = 0F
var rotate2: Float = 0F
/**
* Android在图形绘制时调用的方法,除非设置手动刷新,否则会一直调用。
*/
override fun onDrawFrame(gl: GL10) {
// 清理缓存 颜色和深度
gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)
// 设置模型视图矩阵
gl.glMatrixMode(GL10.GL_MODELVIEW)
// 重制矩阵
gl.glLoadIdentity()
// 视角变换,就是观察的方向,眼睛的位置,中心点的位置 和 视线的朝向
GLU.gluLookAt(gl, 0F, 0F, 3F, 0F, 0F, 0F, 0F, 1F, 0F)
// 设置模型位置
gl.glTranslatef(-2.0F, 0.0F, -4.0F)
// 设置旋转角度 设置成了绕Y轴旋转
gl.glRotatef(rotate1, 0F, 1F, 0F)
// 允许设置顶点
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY)
// 允许设置颜色数组
gl.glEnableClientState(GL10.GL_COLOR_ARRAY)
gl.glColorPointer(4, GL10.GL_FIXED, 0, colorBuffer)
// 给三角形设置顶点数组
// size 含义代表每个点有 xyz三个方向数据所以size为3;
// GL10.GL_FIXED 为整形数据 还可以设置 GL_FLOAT 浮点型,以及其他类型,甚至是无符号类型
// 第三个参数代表相邻顶点之间的偏移量,0 表示紧密排列。即下一个顶点数据紧跟当前顶点数据。非0的话代表交替存储顶点数据额外便宜的字节。
gl.glVertexPointer(3, GL10.GL_FIXED, 0, triggerBuffer)
// 绘制三角形 GL_TRIANGLES 代表三角形,开始0 绘制3个点。
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3)
// 绘制完成禁用颜色数组绘制
gl.glDisableClientState(GL10.GL_COLOR_ARRAY)
// 重制模型矩阵
gl.glLoadIdentity()
// 绘制正方形 两个相同的三角形组成正方形,可以采用三角形的带
// 设置模型位置 为了显示效果更小所以 Z 周弄远了。Z越大看起来离我们越近显示效果越大
gl.glTranslatef(1F, 0.0F, -6.0F)
// 设置绕X轴旋转
gl.glRotatef(rotate2, 1F, 0F, 0F)
// 设置顶点数据
gl.glVertexPointer(3, GL10.GL_FIXED, 0, squareBuffer)
// 设置颜色,R G B A 的色值
gl.glColor4f(1F, 0F, 0F, 1F)
// 绘制正方形
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4)
// 关闭顶点设置
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY)
// 改变旋转角度
rotate1 += 0.5F
rotate2 += 0.5F
}
上端代码实际上增加了 gl.glRotatef(rotate2, 1F, 0F, 0F) ,参数依次是 角度,x轴 y轴 z轴旋转。看上图效果实际上是动旋转,三角Y轴旋转,正方形一直绕x轴旋转。然后通过改变第一个参数的角度,一直旋转。
- 放大
// 增加如下代码即可 x y z 放大2倍
gl.glScalef(2.0F,2.0F,2.0F)
总结
2D基本图形的绘制;坐标系;着色方式包括单一着色,平滑着色;图形的平移、旋转、缩放。