OpenGL 的概念
OpenGL API
OpenGL 专题
[Android OpenGL](https://www.jianshu.com/p/92d02ac80611)
一、什么是 OpenGL
图形领域的标准。它是一套描述了如何绘制2D和3D图像的API。它与硬件无关。是一个跨平台操作 GPU 的 API。可以想象成是一种CS架构,客户端通过调用 OpenGL 的 API 发起一个渲染的请求,请求传送给图形硬件厂商提供的 OpenGL 服务
在 OpenGl 中都是由三个顶点组成的平面
需要自己写顶点着色器和片元着色器的功能
二、GPU 概念
GPU 是是图形处理器,它无法单独工作,必须由 CPU 进行控制调用才能工作。它有许多的计算单元。
三、OpenGL 中的一些概念
着色器是使用 GLSL 所编写的一段小程序。
顶点着色器:它是运行在GPU上的小程序,用于渲染图形顶点的OpenGL ES 图形代码
片元着色器:负责呈现面的颜色
四、在 Android 中使用 OpenGL
android 中提供了 GLSurface 和 GLRender 。GLSurface 负责展示 GLRender 负责真正的渲染
[图片上传失败...(image-312d4c-1570195439422)]
五、示例代码
1.集成 GLSurfaceView
setEGLContextClientVersion 调用该方法设定使用的 OpenGL ES 的版本
给 GLSurfaceView 传递一个 Renderer 用来真正的渲染
public class GLView extends GLSurfaceView {
public GLView(Context context) {
super(context);
}
public GLView(Context context, AttributeSet attrs) {
super(context, attrs);
setEGLContextClientVersion(2);
setRenderer(new GLRender(this));
}
}
2.Render
API | 作用 |
---|---|
onSurfaceCreate | 当surface创建完毕 |
onSurfaceChange | 当surface改变时 |
onDrawFrame | 系统调用次方法重回Surface |
1.Render 是真正调用 OpenGL 做渲染的地方
2.Surface 创建的时候构造 OpenGL 的着色器
3.在onDrawFrame 之前要清空一下,并且调用 OpenGL 绘制
class GLRender implements GLSurfaceView.Renderer {
protected View mView;
private Triangle mGLObject;
public GLRender(GLView glView) {
this.mView = glView;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0, 0, 0, 0);
mGLObject = new Triangle();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
mGLObject.onSurfaceChanged(gl, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
mGLObject.onDrawFrame(gl);
}
}
3.Triangle
绘制一个三角形的核心代码
- 转换数据结构,因为 Java 使用的是大端字节序,而 OpenGL 使用的是小端字节序,需要通过 ByteBuffer转换
- 创建一个顶点着色器
- 创建一个片元着色器
- 将顶点着色器和片元着色器交给 OpenGL 生成可执行的程序。
- 绘制一个图形时,首先需要使用 OpenGL 的程序
- 获取顶点着色器某个字段的句柄,将我们的Java数据赋值给这个句柄。
- OpenGL 有相机和投影的概念,这里可以见参考文章。绘制的图像时需要调整相机的视角
/**
* 使用 OpenGL 绘制的图形
* 原理 http://www.twinklingstar.cn/2015/1532/introduce-to-opengl/
* https://blog.csdn.net/qq_32175491/article/details/79091647
* https://blog.csdn.net/ylbs110/article/details/52074826
* Matrix https://blog.csdn.net/kkae8643150/article/details/52805738
* 顶点着色器:把一个单独的顶点作为输入。顶点着色器的目的是把3D点转换成另一种3D点
* 片段着色器: 计算一个像素最终的颜色
*/
public class Triangle {
int mProgram;
// 绘制的数据 3d 坐标点
static float triangleCoords[] = {
0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};
private FloatBuffer vertexBuffer;
// 定点着色器的 gl 语言代码 我们输入的数据会替代这个 vPosition
// 顶点着色器是运行在 GPU 上的小代码
// 这里乘以一个矩阵得到一个等腰三角形
// https://www.jianshu.com/p/1c23ce604e4e vec4 包含4个浮点数的矢量 mat 代表 Matrix
// attribute 该值传递给顶点着色器
// uniform
private final String vertextShaderCode = "attribute vec4 vPosition;" +
"uniform mat4 vMatrix;\n" +
"void main(){" +
"gl_Position=vMatrix*vPosition;" +
"}";
// 片元着色器的代码
private final String fragmentShaderCode = "precision mediump float;\n" +
"uniform vec4 vColor;\n" +
"void main(){\n" +
"gl_FragColor=vColor;\t\n" +
"}";
private float[] mViewMatrix = new float[16];
private float[] mProjectMatrix = new float[16];
private float[] mMVPMatrix = new float[16];
// RGB Alpha
float color[] = {1.0f, 1.0f, 1.0f, 1.0f};
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 固定的写法
// //计算宽高比
float ratio = (float) width / height;
// 在哪个地方 最后将 mProjectMatrix 矩阵的值赋值给 mMVPMatrix
Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 120);
// 设置观察视角
Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 7,//摄像机的坐标
0f, 0f, 0f,//目标物的中心坐标
0f, 1f, 0f);//相机方向
//计算变换矩阵
Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mViewMatrix, 0);
}
/**
* 注意这里着色器的代码执行是很费时的,所以让它只执行一次。
*/
public Triangle() {
// 转换数据结构 因为 Java 端使用的是大端字节序,而 OpenGL 使用的小端字节序,所以需要通过 ByteBuffer 去转换。
// 公式就是 传入数据的长度*数据的字节数
// float 是 4 个字节
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoords.length * 4);
// 数据分配的顺序
byteBuffer.order(ByteOrder.nativeOrder());
vertexBuffer = byteBuffer.asFloatBuffer();
// 把数据交给这个native buffer
vertexBuffer.put(triangleCoords);
// 设置这块 Buffer 的位置
vertexBuffer.position(0);
// 创建定点着色器
int shader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
// 设置定点着色器的代码
GLES20.glShaderSource(shader, vertextShaderCode);
// 编译定点着色器
GLES20.glCompileShader(shader);
/**
* 创建片元着色器
*/
int fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
GLES20.glShaderSource(fragmentShader, fragmentShaderCode);
GLES20.glCompileShader(fragmentShader);
/**
* 将片元着色器和顶点着色器放到统一的OpenGL程序去管理
*/
mProgram = GLES20.glCreateProgram();
// 将顶点着色器和片元着色器交给 OpenGL 的程序
GLES20.glAttachShader(mProgram, shader);
GLES20.glAttachShader(mProgram, fragmentShader);
// 创建 OpenGL ES 的可执行程序
GLES20.glLinkProgram(mProgram);
}
public void onDrawFrame(GL10 gl) {
// 将程序添加到 OpenGL ES 的环境中
GLES20.glUseProgram(mProgram);
// 获取顶点着色器的位置的句柄
int matrixHandler = GLES20.glGetUniformLocation(mProgram, "vMatrix");
// 将 mMVPatrix 传递个 GPU 替代 vMatrix
GLES20.glUniformMatrix4fv(matrixHandler, 1, false, mMVPMatrix, 0);
int mPositionsHandler = GLES20.glGetAttribLocation(mProgram, "vPosition");
/**
* 必须调用这句话 数据才能被 GPU 访问
* 因为数据虽然传递到了 GPU 但是GPU仍然不能看到,这里是可以让 GPU 正常访问这块数据
*/
GLES20.glEnableVertexAttribArray(mPositionsHandler);
// 3*4 --- 3 表示一个顶点的3个坐标 4代表字节 传递数据
GLES20.glVertexAttribPointer(mPositionsHandler, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer);
// uniform location 代表声明为 uniform 的某个变量
int mColorHandler = GLES20.glGetUniformLocation(mProgram, "vColor");
// 将color数据传递给 GPU 替换 vColor字段 指定的是GPU渲染的形状的颜色
GLES20.glUniform4fv(mColorHandler, 1, color, 0);
// 真正的渲染,绘制一个三角形,
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3);
// 解绑数据,让GPU可以去处理其他工作
GLES20.glDisableVertexAttribArray(mPositionsHandler);
}
}