在Android下图像渲染基本都用的是OpenGL ES,在使用opengl过程中需要注意几个关键点:
1、顶点着色器
2、色彩着色器
3、坐标
特别提到坐标,是因为opengl中的坐标与手机屏幕的坐标是不一致的,在使用opengl坐标时需要自己进行转换一下:
这里使用OpenGL来绘制一个三角形:
项目地址:https://github.com/tong123/OpenGLTriangle
1、使用opengl 2.0版本:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
2、MainActivity.java文件:
package com.example.tongjiangsong.opengltriangle;
import android.opengl.GLSurfaceView;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
private GLSurfaceView mGLView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mGLView = new MyGLSurfaceView(this);
setContentView(mGLView);
}
}
3、MyGLRenderer.java文件:
package com.example.tongjiangsong.opengltriangle;
import android.opengl.EGLConfig;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.opengles.GL10;
public class MyGLRenderer implements GLSurfaceView.Renderer {
private Triangle mTriangle;
@Override
public void onSurfaceCreated(GL10 gl, javax.microedition.khronos.egl.EGLConfig config) {
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);//白色不透明
mTriangle = new Triangle();
}
@Override
public void onSurfaceChanged(GL10 unused, int width, int height) {//当view的几何形状发生变化时调用,比如设备屏幕方向改变时
//绘制窗口
GLES20.glViewport(0, 0, width, height);
}
public void onDrawFrame(GL10 unused) {//每次重绘view时调用
//重绘背景色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
mTriangle.draw();
}
public static int loadShader(int type, String shaderCode){
//创建一个vertex shader类型(GLES20.GL_VERTEX_SHADER)
//或一个fragment shader类型(GLES20.GL_FRAGMENT_SHADER)
int shader = GLES20.glCreateShader(type);
// 将源码添加到shader并编译它
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}
4、MyGLSurfaceView.java文件:
package com.example.tongjiangsong.opengltriangle;
import android.content.Context;
import android.opengl.EGLConfig;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.opengles.GL10;
class MyGLSurfaceView extends GLSurfaceView {
private final MyGLRenderer mRenderer;
public MyGLSurfaceView(Context context){
super(context);
// 创建OpenGL ES 2.0的上下文
setEGLContextClientVersion(2);
mRenderer = new MyGLRenderer();
//设置Renderer用于绘图
setRenderer(mRenderer);
//只有在绘制数据改变时才绘制view,可以防止GLSurfaceView帧重绘
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}
5、Triangle.java文件:
package com.example.tongjiangsong.opengltriangle;
import android.opengl.GLES20;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
public class Triangle {
private final String vertexShaderCode =
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}";
private final String fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}";
private FloatBuffer vertexBuffer;
//设置每个顶点的坐标数
static final int COORDS_PER_VERTEX = 3;
//设置三角形顶点数组
static float triangleCoords[] = { //默认按逆时针方向绘制
0.0f, 1.0f, 0.0f, // 顶点
-1.0f, -0.5f, 0.0f, // 左下角
1.0f, -0.5f, 0.0f // 右下角
};
// 设置三角形颜色和透明度(r,g,b,a)
float color[] = {1.0f, 1.0f, 0f, 1.0f};//绿色不透明
private int mPositionHandle;
private int mColorHandle;
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex
private int mProgram;
public void draw() {
// 添加program到OpenGL ES环境中
GLES20.glUseProgram(mProgram);
// 获取指向vertex shader的成员vPosition的handle
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
// 启用一个指向三角形的顶点数组的handle
GLES20.glEnableVertexAttribArray(mPositionHandle);
//准备三角形的坐标数据
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false,
vertexStride, vertexBuffer);
// 获取指向fragment shader的成员vColor的handle
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
// 绘制三角形
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// Draw the triangle
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// 禁用指向三角形的顶点数组
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
public Triangle() {
// 初始化顶点字节缓冲区,用于存放形状的坐标
ByteBuffer bb = ByteBuffer.allocateDirect(//(每个浮点数占用4个字节
triangleCoords.length * 4);
//设置使用设备硬件的原生字节序
bb.order(ByteOrder.nativeOrder());
//从ByteBuffer中创建一个浮点缓冲区
vertexBuffer = bb.asFloatBuffer();
// 把坐标都添加到FloatBuffer中
vertexBuffer.put(triangleCoords);
//设置buffer从第一个坐标开始读
vertexBuffer.position(0);
// 编译shader代码
int vertexShader = MyGLRenderer.loadShader(GLES20.GL_VERTEX_SHADER,
vertexShaderCode);
int fragmentShader = MyGLRenderer.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentShaderCode);
// 创建空的OpenGL ES Program
mProgram = GLES20.glCreateProgram();
// 将vertex shader添加到program
GLES20.glAttachShader(mProgram, vertexShader);
// 将fragment shader添加到program
GLES20.glAttachShader(mProgram, fragmentShader);
// 创建可执行的 OpenGL ES program
GLES20.glLinkProgram(mProgram);
}
}
运行结果如下: