OpenGL基础篇之渲染过程

  • 背景

OpenGL是一种跨平台的图像渲染方式,这边主要介绍一下OpenGL在android上的应用,即OpenGLES,之所以用它来渲染是因为以往的c++渲染方式比较慢而且占用较大内存,使用OpenGL可以实现实时渲染,而且可以充分利用GPU的内存。

 

  • OpenGL数据传递

要使用OpenGL进行渲染,第一步就是怎样把数据放到GPU里面了,利用IPC这样的数据传递是不现实的,因为数据量太大了,所以图像数据的传递是通过Androdi匿名共享内存实现的,在驱动层分配一块共享内存,把数据传送到这块区域,然后通知GPU进程来取就行了。

 

 1、数据构造

OpenGL只能绘制点、线、三角形,根据不同形状需要的顶点个数也不同,这里以三角形为例,要绘制一个三角形,首先需要三个顶点坐标,OpenGL本身就支持3D,所以顶点坐标是三维的,但是如果只绘制2D图像,那么z轴可以写死在脚本里面,java层传递的顶点坐标是二维的就行了,下面看个例子:

public float[] VERTEX_DATA = new float[]{
        0.0f, 0.0f, 0.5f, 0.5f,
        -1.0f, -1.0f, 0f, 1.0f,
        1.0f, -1.0f, 1f, 1f,
        1.0f, 1.0f, 1f, 0.0f,
        -1.0f, 1.0f, 0f, 0.0f,
        -1.0f, -1.0f, 0f, 1f
};

上面就是绘制一个矩形包含的包含顶点坐标和纹理坐标,首先是每一行由顶点坐标和纹理坐标构成,分别解释一下顶点坐标和纹理坐标:

1)顶点坐标

顶点坐标是我们要绘制的具体位置,Opengl中顶点坐标的范围是[-1,1]的,屏幕左下角是[-1,-1],右上角是[1,1]

 

2)纹理坐标

纹理坐标是我们要采样的图片上面的坐标,即要往顶点坐标指定的位置绘制的图片区域,纹理坐标和顶点坐标不同,范围是[0,1],屏幕左上角是[0,0],右下角是[1,1]

介绍完坐标后我们再来重新看一下上面每一行的数据,以第一行为例

0.0f ,  0.0f,  0.5f ,  0.5f

前两个是顶点坐标,根据上面的坐标系可以知道是屏幕中心点坐标,后面两个是纹理坐标,可以看出是图片区域的中心点,一个顶点坐标必须对应一个纹理坐标,形象的说顶点坐标相当于钉子一样,而纹理坐标相当于把一块布的指定位置固定在钉子处,这样钉子所围成的区域里面看到的布就是我们屏幕上看到的图像

 

我们这里以绘制矩形为例,一个矩形由两个三角形或者四个三角形组成,这里我们是以四个三角形为例

我们来看一下绘制矩形的过程,O->A->D,  O->D->C,  O->C->B,  O->B->A,像这种绘制方法在opengl中是GL_TRIANGLE_FAN类型,就像一个风扇一样旋转,第一个点始终不变,后两个点每次顺移一位,最终闭合形成一个矩形。

还有一种绘制方式是GL_TRIANGLE_TRIPS,这种方式是A->D->C, D->C->B, C->B->A,这种方式用到的点比较少,它不需要中心点,每次三个点都顺移一位。

上面两种方式是常见的绘制三角形方式,这两种方式优点都是重复利用点,这样空间就节省了不少,当然最普通的就是一个三角形三个点,画几个就乘于几,不过除非只画一个三角形,不然不会这样浪费空间的

 

经过上面的介绍,我们再次回顾最开始展示的数据

        0.0f, 0.0f, 0.5f, 0.5f,
        -1.0f, -1.0f, 0f, 1.0f,
        1.0f, -1.0f, 1f, 1f,
        1.0f, 1.0f, 1f, 0.0f,
        -1.0f, 1.0f, 0f, 0.0f,
        -1.0f, -1.0f, 0f, 1f
是不是看懂了,这个就是采用我们上面介绍的GL_TRIANGLE_FAN方式绘制一个矩形,纹理坐标和顶点坐标对应就行,比如顶点坐标左下角是[-1,-1],那么对应纹理的左下角就是[0,1],这就是我们绘制一个矩形需要的数据。

 

2、数据传递

经过上面的步骤,我们有了绘制一个矩形的数据,接下来就是把数据传递给共享内存,然后通知GPU来取了

a) 传递数据到共享内存

floatBuffer = ByteBuffer.allocateDirect(vertexData.length * BYTE_PER_FLOAT)
        .order(ByteOrder.nativeOrder())
        .asFloatBuffer()
        .put(vertexData);

//最后记得一定要put进去,否则数据是没有放到native memory中的

可以看到传递数据还是比较方便的,直接调用接口分配一块共享内存,然后把我们构建的数据放进去

b) 通知GPU取数据

把数据放进去之后还没结束,因为GPU也是一脸懵逼,不知道你这数据干嘛用的,所以接下来我们要告诉它怎么用这些数据,先来看一段shader脚本:

attribute vec2 posCoord;
attribute vec2 texCoord;
varying vec2 texcoordOut;
uniform mat4 u_Matrix;

void main()
{
   texcoordOut = texCoord;
   gl_Position = u_Matrix * vec4(posCoord.x, posCoord.y, 0.0, 1.0);
}

上面的脚本就是传递给GPU使用教程,只要GPU按照这个脚本获取数据就能达到我们的目的了,简单介绍一下几个变量:

1)attribute变量是只能在vertex  shader中使用的变量。(不能在fragment shader中声明attribute变量,也不能在fragment shader中使用),一般用attribute变量来表示一些顶点的数据,如:顶点坐标,法线,纹理坐标,顶点颜色等。它是一种可变变量

2)uniform变量是一种常量,vertext shader和fragment shader都能使用,类似java里面的final变量

3)varying变量是传递变量,只能在vertex shander中赋值,fragment shander中使用,它在两者中的声明必须一致

针对上面的介绍,我们知道顶点坐标和纹理坐标是一种可变变量,即类似于for循环一样每次取指定位置的值放入脚本里面执行,所以上面的posCoord就是代表顶点坐标,texCoord就是代表纹理坐标,接下来就是怎样给这两个变量赋值的问题了。

a) 首先肯定是要找到这个变量的引用了,如下:

private int positionCoordinates;
private int textureCoordinates;

private static final String POSITION_COORDINATES = "posCoord";
private static final String TEXTURE_COORDINATES = "texCoord";

mVertexShader = MTGLUtil.compileShader(GLES20.GL_VERTEX_SHADER, getVertexShaderResource());
mFragmentShader = MTGLUtil.compileShader(GLES20.GL_FRAGMENT_SHADER, getFragmentShaderResource());
mProgram = MTGLUtil.buildProgram(mVertexShader, mFragmentShader);
GLES20.glDeleteShader(mVertexShader);
GLES20.glDeleteShader(mFragmentShader);

positionCoordinates = glGetAttribLocation(mProgram, POSITION_COORDINATES);
textureCoordinates = glGetAttribLocation(mProgram, TEXTURE_COORDINATES);

可以看到并不是很复杂,首先把我们的vertex shader和fragment shander加载进GPU,然后把这两个脚本拿去构建出一个整体,即program结构,返回这个结构的引用,这个引用就是最终包含脚本所有内容的地址了,接下来获取所有脚本相关的内容都是通过这个引用实现。

因为顶点坐标和纹理坐标在脚本里的类型是attribute,所以这边通过特定的api获取到变量的引用,获取方式很简单,把变量名作为key传进去就行了。

 

b) 拿到变量的引用后就开始赋值了

floatBuffer.position(dataOffset);
glVertexAttribPointer(attributeLocation, componentCount, GL_FLOAT, false, stride, floatBuffer);
glEnableVertexAttribArray(attributeLocation);
floatBuffer.position(0);

在我们前期的准备工作下,现在我们的数据已经放在共享内存里了,这里通过floatBuffer变量进行引用,我们通过glVertexAttribPointer来将变量和数据绑定起来,先介绍一下函数的参数,glVertexAttribPointer(变量引用,变量维度,变量每个维度数据类型,是否归一化,采样间隔,数据源),简单来说就是从一个数组中每隔stride个位置读取componentCount个数据,每个数据作为GPU循环调用脚本时传入的变量值。

到这一步,GPU总算不懵逼了,首先它拿到了脚本,知道自己该做什么了,然后根据我们绑定的变量和数据去共享内存里取到数据,再渲染到屏幕上,最终我们就看到一个矩形了

 

  • 总结

经过上面的介绍,我们知道了GPU展示图像的过程,也了解了openGL在android上的基本使用,但是这里我们只绘制了一个矩形,还没介绍矩形里面的图像是怎么来的,这个后面继续介绍。

上面所有的代码都封装成了基本工具类,有兴趣的可以查看

https://github.com/yjp123456/CommonLib

 

 

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值