利用OpenGL对应api在屏幕上绘制一个平面三角形。
vertex_shader.glsl 顶点着色器
attribute vec4 vPosition;
void main() {
# gl_Position是固定表达
gl_Position = vPosition;
}
fragment_shader.glsl
precision mediump float;
uniform vec4 vColor;
void main() {
# gl_FragColor是固定表达
gl_FragColor = vColor;
}
将脚本放入res/raw/ 文件夹下。
可参考链接:
《OpenGL shader GLSL 语法和函数详解》
Triangle.java
/**
* 三角形
*
* @version 1.0
* @since 2019/3/8
*/
public class Triangle implements Shape {
// 坐标本地内存地址
private FloatBuffer vertexBuffer;
// 取几个点
private static final int COORDS_PER_VERTEX = 3;
// 三角形坐标
private static final float triangleCoords[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.3f, 0.0f
};
// 设置颜色 red, green, blue 和 alpha (可选) values
private static final float color[] = {0.0f, 1.0f, 0f, 1.0f};
private final int mProgram;
private int mPositionHandle;
private int mColorHandle;
// 顶点数量
private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX;
// 每个顶点4byte
private final int vertexStride = COORDS_PER_VERTEX * 4;
public Triangle(Context context) {
vertexBuffer = ByteBuffer.allocateDirect(triangleCoords.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(triangleCoords);
vertexBuffer.put(0);
//根据shader代码和fragment代码 获取到一个渲染程序
mProgram = ShaderUtil.createProgram(ShaderUtil.readRawTxt(context, R.raw.vertex_shader),
ShaderUtil.readRawTxt(context, R.raw.fragment_shader));
if (mProgram > 0) {
//获取vertex shader的属性vPosition 的地址
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
//获取fragment shader的属性vColor 的地址
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
}
}
@Override
public void draw() {
// 使用渲染程序
GLES20.glUseProgram(mProgram);
// 使顶点属性数组有效
GLES20.glEnableVertexAttribArray(mPositionHandle);
// 为顶点属性赋值
GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
// 设置颜色
GLES20.glUniform4fv(mColorHandle, 1, color, 0);
// 绘制图形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
// 禁用顶点数组
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}
工具类ShaderUtil.java代码
public class ShaderUtil {
private static final String TAG = "ShaderUtil";
public static String readRawTxt(Context context, int rawId) {
InputStream inputStream = context.getResources().openRawResource(rawId);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer sb = new StringBuffer();
String line;
try {
while ((line = reader.readLine()) != null) {
sb.append(line).append("\n");
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
public static int loadShader(int shaderType, String source) {
// 创建一个顶点或者片段shader
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0) {
// 添加代码到shader
GLES20.glShaderSource(shader, source);
// 编译shader
GLES20.glCompileShader(shader);
int[] compile = new int[1];
// 检查编译结果
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compile, 0);
int err = GLES20.glGetError();
if (compile[0] != GLES20.GL_TRUE) {
Log.e(TAG, "shader compile error:" + err);
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
public static int createProgram(String vertexSource, String fragmentSource) {
// 获取片段shader
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (fragmentShader == 0) {
return 0;
}
// 获取顶点shader
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
// 创建一个空的渲染程序
int program = GLES20.glCreateProgram();
if (program != 0) {
//添加vertexShader到渲染程序
GLES20.glAttachShader(program, vertexShader);
//添加fragmentShader到渲染程序
GLES20.glAttachShader(program, fragmentShader);
// 关联可执行渲染程序
GLES20.glLinkProgram(program);
// 检查是否关联成功
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
int err = GLES20.glGetError();
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e(TAG, "link program error:" + err);
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
}
插播一条Shader的创建流程图
MyRender.java
public class MyRender implements GLSurfaceView.Renderer {
private Context context;
private Shape shape;
public MyRender(Context context) {
this.context = context;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
shape = new Triangle(context);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
// 清空颜色
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// 绘制三角形
shape.draw();
}
}
MyGLSurfaceView.java
public class MyGLSView extends GLSurfaceView {
public MyGLSView(Context context) {
this(context, null);
}
public MyGLSView(Context context, AttributeSet attrs) {
super(context, attrs);
setEGLContextClientVersion(2);
// 应用渲染器
setRenderer(new MyRender(context));
}
}
只要将MyGLSurfaceView应用到Activity就好了。。。
OpenGL是一套流程,而非特定API或者类。如下所示:应用层主要业务是:①,②,⑤。
①读取顶点数据
->②执行顶点着色器
->③组装图元
->④光栅化图元
->⑤执行片段着色器
->⑥写入帧缓冲区
->⑦显示在屏幕上