官网镇楼
OpenGL 简介
openGL 与 OpenGL ES 区别
OpenGL 跨平台的高性能3D渲染API
openGL ES(OpenGL for Embedded Systems)是 OpenGL 的子集,针对移动设备及嵌入式设备设计的
OpenGL 版本
- 1.0旧版本,API1 支持 基本废弃
- 2.0 Android 2.2 (API level 8)支持,相对于1.0版本改动很大,不兼容OpenGL ES 1.x,易用性得到提升,常用的都是这个版本,
- 3.0 Android 4.3 (API level 18) 支持,向下兼容OpenGL ES 2.x
题外话:OpenGL2.0/3.0 中使用 openGL 都是static静态方法,比如
GLES20.glClearColor(0f, 0f, 0f, 0f)
也可以是 GLES10/11/30/31/32 其中的数字就代表 openGL 的版本,并且方法都是 gl 开头,看着设计,我只想说,碉堡了!!
基本概念
- Android 中OpenGL 承载为 GLSurfaceView,里面提供一个openGL 开发环境,上面说过openGL 方法调用都是static 静态的,但是只能在 Render 的 三个生命周期内调用
onSurfaceCreated
onSurfaceChanged
onDrawFrame
- 着色器(Shader):是在GPU上运行的小程序。从名称可以看出,可通过处理它们来处理顶点。此程序使用openGL ES Shading language 语言来编写。它是一个描述顶点或像素特性的简单程序,分为顶点着色器和片元着色器。
- 顶点着色器(vertex Shader):指定几何形状的顶点,比如三角形的三个点
- 片元着色器(fragment shader):指定每个顶点之间的着色
顶点着色器和片元着色器是一一对应的,一个 GL 程序必须至少有一个vertex Shader 和 Fragment shader
5.坐标轴,右手坐标系,跟 Android 的 Y 轴坐标刚好相反,这点要注意,后续的相关操作需要转换Y轴坐标
- 图形绘制:OpenGL只能绘制基本的点、线、三角形,其他图形都是由这三个基本元素组成的(大部分都是大量的三角形组成,所以衡量 GPU 性能常用单位时间绘制三角形数量表示)
实战,开始 OpenGL 绘制之旅
- 构建一个 GLSurfaceView
class GlActivity : AppCompatActivity() {
private lateinit var glSurfaceView: GLSurfaceView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
glSurfaceView = GLSurfaceView(applicationContext)
setContentView(glSurfaceView)
// 设置 openGl 的类型 OpenGL ES 2.0
glSurfaceView.setEGLContextClientVersion(2)
// 设置 render
glSurfaceView.setRenderer(getRender())
// 设置渲染模式 一直渲染
glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
}
private fun getRender(): GLSurfaceView.Renderer? {
return PointsRender
}
override fun onResume() {
super.onResume()
glSurfaceView.onResume()
}
override fun onPause() {
super.onPause()
glSurfaceView.onPause()
}
}
几个关键步骤:
- 创建一个 GLSurfaceView, 然后GLSurfaceView中 openGL 的版本
- 设置 Render, 这步骤是最重要的,所有的 openGL 相关代码都在 Render 生命周期内调用
- 设置渲染模式,两种
- GLSurfaceView主动刷新(continuously),不停的回调Renderer的onDrawFrame
- 被动刷新(when dirty),就是当请求刷新时才调一次onDrawFrame,调用方法为
glSurfaceView.requestRender()
glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
我们这里为了方便,采用主动刷新
Render 编写
这里也是最主要的内容
和 C 语言一样,分为 编写源码-> 编译(compile)->链接(link)->运行/绘制(draw)
1. 编写 Render 源码
基本上都是一个固定的步骤
创建一个vertex shader 和 fragment Shader,用的是openGL ES Shading language shader 描述语言,和 C 语言十分相似,能够直接操作矩阵和向量,直接运行在GPU 之上
//指定 绘制中心点为0,0,0 最后一个在2D 环境下永远为1.0, pointSize 表示点的大小为20
private const val VERTEX_SHADER =
"void main() {\n" +
"gl_Position = vec4(0, 0, 0.0, 1.0);\n" +
"gl_PointSize = 20.0;\n" +
"}\n"
//gl_FragColor是fragment shader的内置变量,用于指定当前顶点的颜色,
// 四个分量(r, g, b, a)。这里是想指定为红色,不透明。
private const val FRAGMENT_SHADER =
"void main() {\n" +
"gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" +
"}\n"
shader 语言永远都有一个 main() 方法,
vec4
为齐次坐标,vec4
和gl_PointSize
都是vertex shader
内置变量, 可以直接使用,FragmentShader同理
2. 编译 shder
这一步主要把 shader源码隐射到 openGL 对象上(vsh)
// 创建一个vertex shader程序 返回值是一个句柄/程序指针 JNI 知识
val vsh = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER)
// 设置 vertex shader 源码
GLES20.glShaderSource(vsh, VERTEX_SHADER)
// 编译 vertex shader
GLES20.glCompileShader(vsh)
val fsh = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER)
GLES20.glShaderSource(fsh, FRAGMENT_SHADER)
GLES20.glCompileShader(fsh)
3. 链接 shader
这一步主要是把vertex shader 和Fragment shader融合到 programs,生成最后的渲染对象,代码也是固定的
// 创建shader program句柄
programs = GLES20.glCreateProgram()
GLES20.glAttachShader(programs, vsh)// 把vertex shader添加到program
GLES20.glAttachShader(programs, fsh)// 把fragment shader添加到program
GLES20.glLinkProgram(programs)
4. 检测是否成功
这一步不是必须的,但是一般都会这么做,
status 为0表示正常,最后是打印出 program 的信息
GLES20.glValidateProgram(programs)
val status = IntArray(1)
// 获取验证的状态 0为正常
GLES20.glGetProgramiv(programs, GLES20.GL_VALIDATE_STATUS, status, 0)
Log.d(TAG, "validate shader program: " + GLES20.glGetProgramInfoLog(programs))
绘制 shader
代码也基本上是固定的
- 清除颜色缓冲区,清除上一帧的数据,防止脏数据
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
- 设置shader
把之前的编译链接好的shader传入
GLES20.glUseProgram(programs)
- 绘制 shader
因为只绘制一个点,所以传入1
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1)
最后 贴一下总的代码
shader
object PointsRender : GLSurfaceView.Renderer {
private const val VERTEX_SHADER =
"void main() {\n" +
"gl_Position = vec4(-0.5, 0.5, 0.0, 1.0);\n" +
"gl_PointSize = 20.0;\n" +
"}\n"
private const val FRAGMENT_SHADER =
"void main() {\n" +
"gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" +
"}\n"
private var programs: Int = -1
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
GLES20.glClearColor(0f, 0f, 0f, 0f)
val vsh = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER)
GLES20.glShaderSource(vsh, VERTEX_SHADER)
// 编译 vertex shader
GLES20.glCompileShader(vsh)
// 创建一个fragment shader程序
val fsh = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER)
GLES20.glShaderSource(fsh, FRAGMENT_SHADER)
GLES20.glCompileShader(fsh)
// 创建shader program句柄
programs = GLES20.glCreateProgram()
GLES20.glAttachShader(programs, vsh)// 把vertex shader添加到program
GLES20.glAttachShader(programs, fsh)// 把fragment shader添加到program
GLES20.glLinkProgram(programs)// 做链接,
GLES20.glValidateProgram(programs) // 让OpenGL来验证一下我们的shader program,并获取验证的状态
val status = IntArray(1)
GLES20.glGetProgramiv(programs, GLES20.GL_VALIDATE_STATUS, status, 0)
Log.d(TAG, "validate shader program: " + GLES20.glGetProgramInfoLog(programs))
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height) // 参数是left, top, width, height
}
override fun onDrawFrame(gl: GL10?) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)// 清除颜色缓冲区
GLES20.glUseProgram(programs)
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, 1)// 开始渲染,发送渲染点的指令
}
}
GLSurfaceView
class GlActivity : AppCompatActivity() {
private lateinit var glSurfaceView: GLSurfaceView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
glSurfaceView = GLSurfaceView(applicationContext)
setContentView(glSurfaceView)
// 设置 openGl 的类型 OpenGL ES 2.0
glSurfaceView.setEGLContextClientVersion(2)
// 设置 render
glSurfaceView.setRenderer(getRender())
// 设置渲染模式 一直渲染
glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
}
private fun getRender(): GLSurfaceView.Renderer? {
return PointsRender
}
override fun onResume() {
super.onResume()
glSurfaceView.onResume()
}
override fun onPause() {
super.onPause()
glSurfaceView.onPause()
}
}