绘制线
线段的绘制很简单,在之前点绘制的基础上,首先需要多增加几个顶点的数据:
1 2 3 4 5 6
| private final float[] VERTEX = { 0, 0, 0, 1, 0, 0, 1, 1, 0 };
|
修改glVertexAttribPointer方法对应数据的数量:
1 2
| GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 12, mBuffer);
|
最后在绘制时指定绘制方式为线段:
1 2 3 4 5 6 7 8
| @Override public void onDrawFrame(GL10 gl) { GLES20.glClear(GL10.GL_COLOR_BUFFER_BIT);
GLES20.glUniformMatrix4fv(mMatrixHandle, 1, false, mMVPMatrix, 0); GLES20.glDrawArrays(GLES20.GL_LINE_STRIP, 0, 3); }
|
GL_LINES:独立的线段,1和2连,3和4连等;
GL_LINE_STRIP:连续的线段,1和2连,2和3连等;
GL_LINE_LOOP:收尾相连,1和2连,2和3连,3和1连;
绘制矩形
利用三角形我们可以“拼出”矩形。
创建顶点索引数据:
1 2 3 4 5 6 7 8 9 10
| private static final short[] VERTEX_INDEX = { 0, 1, 2, 0, 2, 3 };
private final ShortBuffer mVertexIndexBuffer; ...
mVertexIndexBuffer = ByteBuffer.allocateDirect(VERTEX_INDEX.length * 2) .order(ByteOrder.nativeOrder()) .asShortBuffer() .put(VERTEX_INDEX); mVertexIndexBuffer.position(0);
|
在onDrawFrame中绘制:
1
| GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTEX_INDEX.length, GLES20.GL_UNSIGNED_SHORT, mVertexIndexBuffer);
|
glDrawElements:它使用来自启用数组的计数顺序元素,从索引开始构造几何图元序列。mode指定构造什么类型的图元以及数组元素如何构造这些图元。
mode:指定要渲染的图元类型。 接受符号常量GL_POINTS,GL_LINE_STRIP,GL_LINE_LOOP,GL_LINES,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN和GL_TRIANGLES。
count:指定要渲染的元素数。
type:指定indices中值的类型。 必须是GL_UNSIGNED_BYTE或GL_UNSIGNED_SHORT。
indices:指定指向存储索引的位置的指针。
绘制纹理
图片的纹理
在原先绘制矩形的基础上,先添加图片的纹理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| int[] texNames = new int[1]; GLES20.glGenTextures(1, texNames, 0); int mTexName = texNames[0]; Bitmap bitmap = BitmapFactory.decodeResource(OpenGlTextureActivity.this.getResources(), R.drawable.bg2); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTexName); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
|
glGenTextures:创建纹理,产生n个纹理ID存储在textures数组中。
glActiveTexture:激活纹理单元。texture必须是GL_TEXTUREi之一,其中i的范围从0到(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS - 1)。初始值是GL_TEXTURE0
glBindTexture:将一个指定的纹理ID绑定到一个纹理目标上。
glTexParameteri:确定如何把纹理象素映射成像素。
glTexParameteri(GLenum target,GLenum pname,GLint param)
target:指定之前激活了的纹理要绑定到的一个目标。必须是GL_TEXTURE_2D
或GL_TEXTURE_CUBE_MAP。
pname:指定一个单值纹理参数的符号名,pname可以是下列值之一:GL_TEXTURE_MIN_FILTER(放大过滤) GL_TEXTURE_MAG_FILTER(缩小过滤)GL_TEXTURE_WRAP_S (S方向)GL_TEXTURE_WRAP_T(T方向)。
param:指定pname的值。
params提供的缩小采样功能,可选参数如下:
GL_NEAREST
临近采样,返回与纹理像素的中心最接近(在曼哈顿距离内)的纹理元素的值。
GL_LINEAR
线性采样,返回最接近被纹理像素中心的四个纹理元素的加权平均值。
GL_NEAREST_MIPMAP_NEAREST
选择最接近匹配纹理像素大小的mipmap,并使用GL_NEAREST标准(最接近像素中心的纹理元素)来生成纹理值。
GL_LINEAR_MIPMAP_NEAREST
选择最接近匹配纹理像素大小的mipmap,并使用GL_LINEAR标准(最接近像素中心的四个纹理元素的加权平均值)来生成纹理值。
GL_NEAREST_MIPMAP_LINEAR
选择与纹理像素大小最匹配的两个mipmap,并使用GL_NEAREST标准(最接近像素中心的纹理元素)从每个mipmap生成纹理值。 最终纹理值是这两个值的加权平均值。
GL_LINEAR_MIPMAP_LINEAR
选择与纹理像素大小最匹配的两个mipmap,并使用GL_LINEAR标准(最接近像素中心的四个纹理元素的加权平均值)从每个mipmap生成纹理值。 最终纹理值是这两个值的加权平均值。
shader 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| private static final String VERTEX_SHADER = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "attribute vec2 a_texCoord;" + "varying vec2 v_texCoord;" + "void main() {" + " gl_Position = uMVPMatrix * vPosition;" + " v_texCoord = a_texCoord;" + "}"; private static final String FRAGMENT_SHADER = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "void main() {" + " gl_FragColor = texture2D(s_texture, v_texCoord);" + "}";
|
uniform
由外部程序传递给 shader,就像是C语言里面的常量,shader 只能用,不能改;attribute
是只能在 vertex shader 中使用的变量;varying
变量是 vertex 和 fragment shader 之间做数据传递用的。
绘制
首先我们需要指定截取纹理的哪一部分绘制到图形上:
1 2 3 4 5 6 7 8
| private static final float[] TEX_VERTEX = { 1, 0, 0, 0, 0, 1, 1, 1, };
mTexVertexBuffer = Util.getFloatBuffer(TEX_VERTEX);
|
修改初始化和绘制的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| @Override public void onSurfaceCreated(GL10 unused, EGLConfig config) {
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); mTexCoordHandle = GLES20.glGetAttribLocation(mProgram, "a_texCoord"); mMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix"); mTexSamplerHandle = GLES20.glGetUniformLocation(mProgram, "s_texture");
GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 12, mVertexBuffer);
GLES20.glEnableVertexAttribArray(mTexCoordHandle); GLES20.glVertexAttribPointer(mTexCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mTexVertexBuffer);
}
@Override public void onDrawFrame(GL10 unused) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUniformMatrix4fv(mMatrixHandle, 1, false, mMVPMatrix, 0); GLES20.glUniform1i(mTexSamplerHandle, 0);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, VERTEX_INDEX.length, GLES20.GL_UNSIGNED_SHORT, mVertexIndexBuffer); }
|
在 activity 销毁时,我们需要销毁 OpenGL 纹理:
1
| GLES20.glDeleteTextures(1, new int[] { mTexName }, 0);
|
读取显存
在 onDrawFrame
方法执行完毕之后(实际上是 glDrawElements
执行完毕之后),我们就可以从显存中读取帧数据了。这里我们利用 glReadPixels
方法读取数据:
1 2 3 4 5 6 7 8 9 10
| captureBitmap = getImage(surfaceView.getWidth(), surfaceView.getHeight());
private Bitmap getImage(int width, int height) { ByteBuffer rgbaBuf = ByteBuffer.allocateDirect(width * height * 4); rgbaBuf.position(0); GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,rgbaBuf); Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); bmp.copyPixelsFromBuffer(rgbaBuf); return bmp; }
|
注意:glReadPixels方法必须在onDrawFrame中,在glDrawElements之后,在onDrawFrame以外调用可能会拿到空的bitmap。