广告竞价排名,响应式网站做seo怎么样,一件代发海外电商平台,广州网站排名优化费用OpenGL ES 绘制一个三角形(2)
简述
本节我们基于Android系统#xff0c;使用OpenGL ES来实现绘制一个三角形。在OpenGL ES里#xff0c;三角形是一个基础图形#xff0c;其他的图形都可以使用三角形拼接而成#xff0c;所以我们就的案例就基于这个开始。 在Android系统中…OpenGL ES 绘制一个三角形(2)
简述
本节我们基于Android系统使用OpenGL ES来实现绘制一个三角形。在OpenGL ES里三角形是一个基础图形其他的图形都可以使用三角形拼接而成所以我们就的案例就基于这个开始。 在Android系统中提供给上层应用的View都是通过Canvas的接口来绘制虽然底层最终也是通过OpenGL ES来实现的但是由于上层被封装了我们无法通过这个来实现我们想要实现的demo我们需要使用GLSurfaceView来实现。
GLSurfaceView继承自SurfaceView我们知道SurfaceView和一般的View不同会有自己的Surface而GLSurfaceView则在SurfaceView的基础上会初始化EGL的上下文环境。其实我们直接使用SurfaceView也是可以使用OpenGL ES的只不过GLSurfaceView给我们提供了一些生命周期管理的辅助在大多数场景使用起来更加方便。
GLSurfaceView提供的是EGL环境我们想要绘制一个三角形所需要做的事如下
创建一个GLSurfaceView配置EGL其实GLSurfaceView帮助我们做了大多数的事使用OpenGL ES接口绘制图像 配置顶点缓冲区实现顶点着色器和片段着色器调用drawCall 交换缓冲区呈现图像
本节主要是实现demo对OpenGL渲染大体流程有个感知一些api的细节可以不需要关注后续会对每个点会有更详细的介绍。
绘制一个三角形
配置OpenGL ES
在AndroidManifeast.xml里配置 主要就是配置一条 其中glEsVersion是版我们这里用OpenGL ES 3.0来写demo。
manifest xmlns:androidhttp://schemas.android.com/apk/res/android
xmlns:toolshttp://schemas.android.com/toolsuses-feature android:glEsVersion0x00030000 android:requiredtrue/application// .../activity
/application自定义GLSurfaceView
GLSurfaceView通过setRenderer暴露一个RendererRenderer有三个接口onSurfaceCreated/onSurfaceChanged/onDrawFrame。 GLSurfaceView处理了EGL环境相关的逻辑onDrawFrame则会控制VSync在需要渲染的时候调用。 onSurfaceChanged是Surface变化的情况下会调用而onSurfaceCreated则是Surface创建时回调onDrawFrame和我们自定义View时候的onDraw有一些类似。
public class DemoGLSurfaceView extends GLSurfaceView {public DemoGLSurfaceView(Context context) {super(context);init();}public DemoGLSurfaceView(Context context, AttributeSet attrs) {super(context, attrs);init();}public void init() {// 设置版本setEGLContextClientVersion(3);Renderer renderer new Renderer() {Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {// ...}Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {// 设定视口类似相机相机移动则渲染的图像相对位置变化。GLES30.glViewport(0, 0, width, height);}Overridepublic void onDrawFrame(GL10 gl) {// ...}};setRenderer(renderer);}
}配置顶点缓冲区数据
由于我们只是要画一个固定的三角形顶点缓冲区里的数据都是固定的所以我们在onSurfaceCreated填充只需要一次即可。 glGenBuffers是创建一个顶点缓冲区Buffer第二个参数是一个int数组创建的顶点缓冲区id会通过这个数组返回后续使用这个id来使用这个buffer。 我们需要先调用glBindBuffer绑定buffer然后再通过glBufferData将数据传到缓冲区中。 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)则是用来清除Buffer的绑定操作的OpenGL的接口设计像是一个状态机bind上一个Buffer才能对这个Buffer进行操作如果需要操作其他Buffer则需要bind其他Buffer。 vertexArray有三个节点是三个顶点的x,y,z坐标。OpenGL的坐标系是x,y,z都是(-1,1)。
private float[] vertexArray new float[] {-0.5f, -0.5f, 0.0f,0.5f, -0.5f, 0.0f,0.0f, 0.5f, 0.0f
};Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {// 清除背景颜色GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// 创建顶点缓冲区int[] idBuffer new int[1];GLES30.glGenBuffers(1, idBuffer, 0);vertexBufferId idBuffer[0];// 将数据转化成ByteBufferFloatBuffer vertexBuffer ByteBuffer.allocateDirect(vertexArray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();vertexBuffer.put(vertexArray);vertexBuffer.position(0);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);// 顶点缓冲区数据填充GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertexArray.length * 4,vertexBuffer,GLES30.GL_STATIC_DRAW);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);// 初始化shadershaderProgramId initShaderProgram(vertexShaderCode, fragmentShaderCode);
}配置着色器
着色器是一段给GPU执行的程序所以其实就是一段代码我们需要调用对应接口来编译链接。 GLES30.glCreateShader创建一个Shader参数表示着色器的类型GL_VERTEX_SHADER为顶点着色器GL_FRAGMENT_SHADER为片段着色器。 vertexShaderCode字符串是我们配置的顶点着色器gl_Position是出参这里是直接做了透传。 fragmentShaderCode是片段着色器gl_FragColor是出参是颜色而uniform vec4 vColor是统一变量我们设置统一变量直接作为片段着色器的出参。 通过api编译连接后我们会将它关联到一个Program上initShaderProgram返回到就是program到id。
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 int initShaderProgram(String vertexShaderCode, String fragmentShaderCode) {// 编译顶点着色器int vertexShader GLES30.glCreateShader(GLES30.GL_VERTEX_SHADER);GLES30.glShaderSource(vertexShader, vertexShaderCode);GLES30.glCompileShader(vertexShader);// 编译片段着色器int fragmentShader GLES30.glCreateShader(GLES30.GL_FRAGMENT_SHADER);GLES30.glShaderSource(fragmentShader, fragmentShaderCode);GLES30.glCompileShader(fragmentShader);// 链接着色器int program GLES30.glCreateProgram();GLES30.glAttachShader(program, vertexShader);GLES30.glAttachShader(program, fragmentShader);GLES30.glLinkProgram(program);return program;
}Override
public void onDrawFrame(GL10 gl) {// ...// 使用编译好的着色器GLES30.glUseProgram(shaderProgramId);// ...
}配置顶点布局/渲染
首先我们需要调用glClear清空屏幕glUseProgram配置着色器程序glBindBuffer绑定之前填充的Buffer。 属性需要通过glEnableVertexAttribArray使能才可使用我们这里需要使能vPosition属性。 后续会使用glVertexAttribPointer告诉GPU顶点缓冲区布局情况顶点缓冲区本质就是一段内存不过没有glVertexAttribPointerGPU并不知道怎么使用这个数据。 后面配置vColor作为颜色(1,1,1,1)分别为RGBA白色。 最后调用glDrawArrays来渲染三角形。
Override
public void onDrawFrame(GL10 gl) {// 清除屏幕GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);// 使能着色器程序GLES30.glUseProgram(shaderProgramId);// 绑定BufferGLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);// 获取vPosition属性int positionLocation GLES30.glGetAttribLocation(shaderProgramId, vPosition);// 属性需要使能才可使用GLES30.glEnableVertexAttribArray(positionLocation);// 告诉GPU顶点缓冲区的布局情况即那些数据的意义是什么。// 这是CPU向GPU传数据的一种方式我们这里是告诉GPU我们前面bind的顶点缓冲区是什么数据。// 第一个参数是attr的id第二个参数表示每一个顶点有几个数第三个参数为数据类型第四个是参数是否需要归一化// 第五个参数是步长表示每个顶点占用了多少字节0表示顶点都是紧凑的GPU会通过计算来计算步长最后一个参数表示offset。GLES30.glVertexAttribPointer(positionLocation, 3, GLES30.GL_FLOAT, false, 0, 0);// 配置统一变量用于CPU和GPU通信的int colorLocation GLES30.glGetUniformLocation(shaderProgramId, vColor);GLES30.glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f);// 调用DrawCall绘制三角形GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);// 清除配置GLES30.glDisableVertexAttribArray(positionLocation);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);GLES30.glUseProgram(0);
}效果
三角形之所以不是正三角形是因为屏幕是长方形的。
小结
本节通过OpenGL ES实现了一个三角形的渲染对于每个接口使用只做了一个简单的介绍想必首次学习OpenGL的同学会有很多疑问比如怎么渲染多个目标怎么实现渐变颜色等我们的后续会对每一个点做更细节的学习这一节主要是了解一下OpenGL的总体渲染流程大概知道OpenGL接口是怎么工作的即可后续的介绍也会基于本章的demo。