有了第一篇的基础,现在开始着手绘制一个三角形,
先总结一下 OpenGL es 绘制的基本流程
1. 编写 vertex Shader 和 Fragment shader 的代码
2. 编译 vertex Shader和Fragment shader
3. 创建 programs 对象,连接两个 shader
4. onDrawFrame 中,绘制 shader
我们按照上面的步骤一步步的来
第一步 编写shader
顶点着色器(vertex shader)
attribute vec4 vPosition;
void main() {
gl_Position = vPosition;
}
发现比上一篇的的多了一个东西,其实就是多定义了一个成员变量vPosition,其中 attribute表示成员变量,vec4表示类型为齐次坐标,也就是说gl_Position这个变量的内容取自vPosition这个变量,而这个变量在后面会被我们 Java 层赋值
片元着色器
uniform vec4 vColor;
void main() {
gl_FragColor = vColor;
}
原理和上面一致,定义变量让外界赋值
第二步 处理 shader
这一步在步骤十分固定 样板代码
- 设置背景
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
- 编译 链接 处理 shader 源码
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
VERTEX_SHADER)
val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,FRAGMENT_SHADER)
//创建一个空的OpenGLES程序
mProgram = GLES20.glCreateProgram()
//将顶点着色器加入到程序
GLES20.glAttachShader(mProgram, vertexShader)
//将片元着色器加入到程序中
GLES20.glAttachShader(mProgram, fragmentShader)
//连接到着色器程序
GLES20.glLinkProgram(mProgram)
fun loadShader(type: Int, shaderString: String): Int {
val shader = GLES20.glCreateShader(type)
GLES20.glShaderSource(shader, shaderString)
GLES20.glCompileShader(shader)
return shader
}
- 把图形坐标点数据转化成 openGL 可以识别的格式
//申请底层空间
val bb = ByteBuffer.allocateDirect(triangleCoords.size * 4)
//数据硬件对齐
bb.order(ByteOrder.nativeOrder())
//将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
vertexBuffer = bb.asFloatBuffer()
vertexBuffer.put(triangleCoords)
vertexBuffer.position(0)
- 最后 绘制三角形
设置 shader
GLES20.glUseProgram(mProgram)
更改 Vertex Shader的 里面 vPosition
的值,把三角形的顶点数值赋值上去
//获取顶点着色器的vPosition成员句柄
val mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition")
//启用三角形顶点的句柄
GLES20.glEnableVertexAttribArray(mPositionHandle)
//准备三角形的坐标数据
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer)
同理,设置 Fragment shader 里面 vColor
的值
//获取片元着色器的vColor成员的句柄
val mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor")
//设置绘制三角形的颜色
GLES20.glUniform4fv(mColorHandle, 1, color, 0)
绘制三角形
//绘制三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)
回收资源 回收顶点数组引用
//禁止顶点数组的句柄
GLES20.glDisableVertexAttribArray(mPositionHandle)
第一个参数表示绘制方式,定义如下
int GL_POINTS //将传入的顶点坐标作为单独的点绘制
int GL_LINES //将传入的坐标作为单独线条绘制,ABCDEFG六个顶点,绘制AB、CD、EF三条线
int GL_LINE_STRIP //将传入的顶点作为折线绘制,ABCD四个顶点,绘制AB、BC、CD三条线
int GL_LINE_LOOP //将传入的顶点作为闭合折线绘制,ABCD四个顶点,绘制AB、BC、CD、DA四条线。
int GL_TRIANGLES //将传入的顶点作为单独的三角形绘制,ABCDEF绘制ABC,DEF两个三角形
int GL_TRIANGLE_FAN //将传入的顶点作为扇面绘制,ABCDEF绘制ABC、ACD、ADE、AEF四个三角形
int GL_TRIANGLE_STRIP //将传入的顶点作为三角条带绘制,ABCDEF绘制ABC,BCD,CDE,DEF四个三角形
5.贴上完整代码
open class Triangle : Shape() {
private val VERTEX_SHADER =
"attribute vec4 vPosition;\n" +
" void main() {\n" +
" gl_Position = vPosition;\n" +
" }"
//gl_FragColor是fragment shader的内置变量,用于指定当前顶点的颜色,
// 四个分量(r, g, b, a)。这里是想指定为红色,不透明。
private val FRAGMENT_SHADER =
"precision mediump float;\n" +
" uniform vec4 vColor;\n" +
" void main() {\n" +
" gl_FragColor = vColor;\n" +
" }"
private var triangleCoords = floatArrayOf(0.5f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
)
private var color = floatArrayOf(1.0f, 1.0f, 1.0f, 1.0f) //白色
//顶点之间的偏移量
private val vertexStride = COORDS_PER_VERTEX * 4 // 每个顶点四个字节
//顶点个数
private val vertexCount = triangleCoords.size / COORDS_PER_VERTEX
private var mProgram: Int = 0
private lateinit var vertexBuffer:FloatBuffer
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
//将背景设置为灰色
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f)
//申请底层空间
val bb = ByteBuffer.allocateDirect(
triangleCoords.size * 4)
//数据硬件对齐
bb.order(ByteOrder.nativeOrder())
//将坐标数据转换为FloatBuffer,用以传入给OpenGL ES程序
vertexBuffer = bb.asFloatBuffer()
vertexBuffer.put(triangleCoords)
vertexBuffer.position(0)
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
VERTEX_SHADER)
val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
FRAGMENT_SHADER)
//创建一个空的OpenGLES程序
mProgram = GLES20.glCreateProgram()
//将顶点着色器加入到程序
GLES20.glAttachShader(mProgram, vertexShader)
//将片元着色器加入到程序中
GLES20.glAttachShader(mProgram, fragmentShader)
//连接到着色器程序
GLES20.glLinkProgram(mProgram)
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
}
override fun onDrawFrame(gl: GL10?) {
GLES20.glUseProgram(mProgram)
//获取顶点着色器的vPosition成员句柄
val mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition")
//启用三角形顶点的句柄
GLES20.glEnableVertexAttribArray(mPositionHandle)
//准备三角形的坐标数据
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer)
// 这里其实就是取出源码里面的 变量 进行赋值
//获取片元着色器的vColor成员的句柄
val mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor")
//设置绘制三角形的颜色
GLES20.glUniform4fv(mColorHandle, 1, color, 0)
//绘制三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount)
//禁止顶点数组的句柄
GLES20.glDisableVertexAttribArray(mPositionHandle)
}
}