Android OpenGLES3.0 入门教程(三)——顶点数组和统一变量

上一章简单介绍了一下着色器的基础语法,复盘了一下第一章中讲到的两个着色器的代码。这一章主要讲述数据传递:Android 环境与顶点着色器的数据传递,顶点着色器与片元着色器的数据传递等。这篇文章还是以需求来驱动知识点的学习。

三个点

之前的demo,咱们是绘制了一个点。简单回顾一下这一个点的数据传递:

  1. 顶点着色器定义了一个顶点属性
  2. 通过顶点数组的赋值形式(glVertexAttribPointer)将点的坐标信息从Android环境传递到了顶点着色器
  3. 顶点着色器告诉OpenGL ES要绘制的点的位置
  4. 片元着色器告诉OpenGL ES绘制点的颜色(片元着色器中写死的色值)

通过上边四步,咱们就可以在手机屏幕上指定位置绘制一个指定颜色的点了。那么现在需求升级,咱们需要绘制三个点。

按着之前开发的思维惯性,很容易想到给顶点着色器定义三个顶点属性不就行了么。还真不行,在第一章中已经指出来了,顶点着色器只会负责一个顶点,也就是说不管你定义多少个顶点属性,用来控制OpenGL ES顶点位置的输出属性只有一个”gl_Position“

#version 300 es
layout (location = 0) in vec4 av_Position;
void main() {
    gl_Position  = av_Position;
    gl_PointSize = 10.0;
}

也就是你定义100个av_Position,但最终只会有一个赋值给gl_Position。除非你把100个值同时赋予gl_Position让gl_Position处于一个100个状态的叠加态(手动狗头🐶)。

确定三个顶点位置的一步其实是告诉OpenGL ES你要绘制三个顶点,这个时候根据之前开发的思维惯性是不是觉得OpenGL ES应该有个setVertexCount的Api,哈 并没有。那么OpenGL ES是怎么确认顶点数量的呢,看回第一章的顶点属性赋值代码:

private val POSITION_VERTEX = floatArrayOf(
  0.0f, 0.0f, 0.0f
)

init {
  vertexBuffer = ByteBuffer.allocateDirect(POSITION_VERTEX.size * 4)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
    .put(POSITION_VERTEX)
    vertexBuffer.position(0)
}

GLES30.glVertexAttribPointer(avPosition, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)

其实是把一个float数组按照指定规格赋值给了顶点属性avPosition,glVertexAttribPointer方法的参数中,avPosition代表了顶点属性的位置索引,第二参数”3"则是代表了赋值数组的长度,也就是把float数组POSITION_VERTEX中三个元素装成一个数组赋值给顶点属性四维向量avPosition。

这块其实就可以看出来一些问题的,我的POSITION_VERTEX数组就三个元素为什么还要指定传入的数组长度呢?盲生你发现了华点🤓🤓🤓🤓

其实这就是OpenGL ES指定顶点个数的方式。OpenGL ES的顶点数量=给顶点属性赋值的数组总长度/顶点属性需要的数组长度

上边代码的顶点数量就是POSITION_VERTEX.length/3 = 1,知道了这个三个点其实就很好画了,下边贴出关键代码

private val POSITION_VERTEX = floatArrayOf(
  0.0f, 0.5f, 0.0f,
  -0.25f, -0.25f, 0.0f,
  0.25f, -0.25f, 0.0f
)

init {
  vertexBuffer = ByteBuffer.allocateDirect(POSITION_VERTEX.size * 4)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
    .put(POSITION_VERTEX)
    vertexBuffer.position(0)
}

override fun onDrawFrame(gl: GL10?) {
  GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
  GLES30.glUseProgram(pointProgram)
  GLES30.glEnableVertexAttribArray(avPosition)
  GLES30.glVertexAttribPointer(avPosition, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)
  GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 3)
  GLES30.glDisableVertexAttribArray(avPosition)
}

补充一下,GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 3),这一句话并不是指明了OpenGL ES有三个顶点,而是指明了屏幕上要绘制三个点,这个三个点是OpenGL ES中前三个顶点。

看下手机绘制的效果

三个点.png

标题中的顶点数组指的就是POSITION_VERTEX这个用来指定顶点属性的关键数组。顶点数组的意义则是更加高效、快捷的指定顶点属性的值。

顶点数组指定每个顶点的属性,是保存在应用程序地址空间(OpenGL ES称为客户空间)的缓冲区。它们作为顶点缓冲对象的基础,提供指定顶点属性数据的一种高效、灵活的手段。顶点数组用glVertexAttribPointer或 glVertexAttribIPointer函数指定。

自定义点的颜色

现在咱们升级需求,画的点除了可以在Android层定义位置,咱们还要定义颜色。

在现有的代码中可以发现点的颜色时在片元着色器写死的,再次根据开发的思维惯性,有顶点属性,是不是也有片元属性,然后用片元数组赋值不就完了嘛!

yougedidi.jpg

哈,并没有片元属性,因为片元着色器的个数是不确定的。比如你画一个三角形,顶点三个,但是片元是三条线占用的像素数,最终生成多少个片元完全由OpenGL ES控制的。顶点数组的控制逻辑是需要明确顶点数量的,片元着色器显然不适用。

片元着色器的变量传递一般用使用顶点着色器的输出变量。还记得第二章中介绍的in 和out 两个修饰符吗,也就是将顶点着色器的一个变量用out修饰,片元着色器中一个变量用in修饰,且两个变量同名,这个时候顶点着色器的值就能传递到片元着色器来了。咱们的需求也就变成了将顶点的坐标和位置传入顶点着色器。

直接上代码,顶点着色器:

#version 300 es
layout (location = 0) in vec4 av_Position;
layout (location = 1) in vec4 point_Color;
out vec4 frag_point_Color;
void main() {
    gl_Position  = av_Position;
    frag_point_Color = point_Color;
    gl_PointSize = 10.0;
}

片元着色器

#version 300 es
precision mediump float;
out vec4 fragColor;
in vec4 frag_point_Color;
void main() {
    fragColor = frag_point_Color;
}

可以看到顶点着色器其实是做了一个转发的操作,把通过顶点数组传下来的顶点属性用out修饰的frag_point_Color传到了片元着色器。这就是in 和 out 关键字的一个典型应用,只要记得in代表输入属性,out代表输出属性就可以了。然后再看kotlin代码

var pointProgram = -1
var vertexBuffer: FloatBuffer
var avPosition = -1
val POSITION_SIZE = 3
val COLOR_SIZE = 3
val VERTEX_ATTRIBUTES_SIZE = 4*(POSITION_SIZE+COLOR_SIZE)

private val POSITION_VERTEX = floatArrayOf(
  0.0f, 0.5f, 0.0f, 0.0f, 0.1f, 1.0f,
  -0.25f, -0.25f, 0.0f, 1.0f, 0.1f, 1.0f,
  0.25f, -0.25f, 0.0f, 0.0f, 1.1f, 1.0f
)

init {
  vertexBuffer = ByteBuffer.allocateDirect(POSITION_VERTEX.size * 4)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
    .put(POSITION_VERTEX)
    vertexBuffer.position(0)
}

override fun onDrawFrame(gl: GL10?) {
  GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
  GLES30.glUseProgram(pointProgram)
  GLES30.glEnableVertexAttribArray(avPosition)
  GLES30.glEnableVertexAttribArray(1)
  vertexBuffer.position(0)
  GLES30.glVertexAttribPointer(0, POSITION_SIZE, GLES30.GL_FLOAT, false,VERTEX_ATTRIBUTES_SIZE, vertexBuffer)
  vertexBuffer.position(3)
  GLES30.glVertexAttribPointer(1, COLOR_SIZE, GLES30.GL_FLOAT, false,          VERTEX_ATTRIBUTES_SIZE, vertexBuffer)
  GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 3)
  GLES30.glDisableVertexAttribArray(0)
  GLES30.glDisableVertexAttribArray(1)
}

override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
    GLES30.glViewport(0, 0, width, height)
}

override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
  pointProgram = ShaderUtil.loadProgramFromAssets(
  "vertex_point_1.glsl",
  "frag_point_1.glsl",
  context.resources)
    avPosition = GLES30.glGetAttribLocation(pointProgram, "av_Position")
}

这里的关注点主要在于怎样用一个数组给两个顶点属性赋值。

现在咱们要处理的数据是分别给三个顶点着色器的两个属性传输一组值,两个属性分别是位置和颜色,位置需要用一组三个元素的float数组标识(x,y,z),颜色也是一样的(r,g,b)。所以现在咱们把一个顶点着色器中的两组数组糅合成一组数组,也就是用一个6个元素的数组同时来标识位置和颜色。


array.png

所以生成了新的数组,每一行是一个顶点的数据,前三位代表点的位置,后三位是点的颜色

private val POSITION_VERTEX = floatArrayOf(
  0.0f, 0.5f, 0.0f,        0.0f, 0.1f, 1.0f,
  -0.25f, -0.25f, 0.0f,    1.0f, 0.1f, 1.0f,
  0.25f, -0.25f, 0.0f,     0.0f, 1.1f, 1.0f
)

然后再看一下赋值的代码

vertexBuffer.position(0)
GLES30.glVertexAttribPointer(0, POSITION_SIZE, GLES30.GL_FLOAT, false, VERTEX_ATTRIBUTES_SIZE, vertexBuffer)
vertexBuffer.position(3)
GLES30.glVertexAttribPointer(1, COLOR_SIZE, GLES30.GL_FLOAT, false,             VERTEX_ATTRIBUTES_SIZE, vertexBuffer)

这种混合数组的赋值方式,关键就是告诉OpenGL ES指定每个顶点着色器所占用的数据总长度,以及这组数据中哪段数据对应着那个属性。

结合上文对glVertexAttribPointer api的介绍,glVertexAttribPointer api中的第四个参数,就是用来描述顶点属性的总长度,也就是(三位位置数据+三位颜色数据)*(folat类型数据长度),告诉了OpenGL ES每组数据有六个元素,glVertexAttribPointer api的第二个参数,则表示了该顶点属性使用的数据个数(这里不用计算数据占位长度 ),vertexBuffer.position(0)这表示了赋值时数组的游标定位。

好了介绍完基础知识在翻译上面的代码:

游标定位到0

给索引位置0的顶点属性(av_Position)赋值,需要数据长度为3,一个顶点着色器需要的完整数据总长度为24字节(6个float类型)

OpenGL ES 就会从零个元素读起,把读到的前三个赋值给av_Position属性,然后跳过三个值,循环三次。

游标定位到3

给索引位置0的顶点属性(point_Color)赋值,需要数据长度为3,一个顶点着色器需要的完整数据总长度为24字节(6个float类型)

OpenGL ES 就会从第四个元素开始读,把读到的前三个赋值给point_Color属性,然后跳过三个值,循环三次。

这样就完成使用一个数组,给两个顶点属性赋值的过程。咱们现在看一下效果:

Screenshot_2020-09-17-16-55-32-1656217187.png

ok,三个不同颜色的点就完成了。这个时候可能有的同学会问了,一个数组赋值有点麻烦我使用文章最上边的那种指定属性值的方式,分别用两个数组给两个属性赋值不可以吗?当然可以。用一组数组指定所有属性的方式叫做结构数组,多个数组指定多个属性的方式叫做数组结构........🤢🤢🤢🤢🤢🤢

我们已经描述了两种最常用的顶点属性存储方法∶结构数组和数组结构。问题是,对于OpenGLES3.0硬件实现,哪种分配方法最高效?在大部分情况下,答案是结构数组(一个数组方式)。原因是,每个顶点的属性数据可以顺序方式读取,这最有可能造成高效的内存访问模式。使用结构数组的缺点在应用程序需要修改特定属性时变得很明显。如果顶点属性数据的一个子集需要修改(例如,纹理坐标),这将造成顶点缓冲区的跨距更新。当顶点缓冲区以缓冲区对象的形式提供时,需要重新加载整个顶点属性缓冲区。可以通过将动态的顶点属性保存在单独的缓冲区来避免这种效率低下的情况。

顶点缓冲区对象(VBO)

上边描述了如何用glVertexAttribPointer指定这3个顶点属性。注意,我们在此介绍如何使用客户端顶点数组,以便解释逐顶点数据指定的概念。我们建议应用程序使用顶点缓冲区对象,避免使用客户端顶点数组,以实现最佳性能。在OpenGL ES 3.0 中支持客户端顶点数组只是为了与OpenGLES 2.0兼容。在OpenGLES3.0中,总是建议使用顶点缓冲区对象。

使用顶点数组指定的顶点数据保存在客户内存中。在进行绘图调用时,这些数据必须从客户内存复制到图形内存。但是,如果我们没有必要在每次绘图调用时都复制顶点数据,而是在图形内存中缓存这些数据,那就好得多了。这种方法可以显著地改进渲染性能,也会降低内存带宽和电力消耗需求,对于移动设备相当重要。这是顶点缓冲区对象发挥作用的地方。顶点缓冲区对象使OpenGL ES 3.0应用程序可以在高性能的图形内存中分配和缓存顶点数据,并从这个内存进行渲染,从而避免在每次绘制图元的时候重新发送数据。不仅是顶点数据,描述图元顶点索引、作为 glDrawElements 参数传递的元素索引也可以缓存。
OpenGL ES 3.0支持两类缓冲区对象,用于指定顶点和图元数据∶数组缓冲区对象和元素数组缓冲区对象。GL_ARRAY_BUFFER标志指定的数组缓冲区对象用于创建保存顶点数据的缓冲区对象。GL_ELEMENT ARRAY BUFFER标志指定的元素数组缓冲区对象用于创建保存图元索引的缓冲区对象。

简单介绍一下数组缓冲区对象使用方式。

override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
  pointProgram = ShaderUtil.loadProgramFromAssets(
    "vertex_point_1.glsl",
    "frag_point_1.glsl",
    context.resources
  )
  GLES30.glGenBuffers(1, vboIds, 0);
  GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboIds[0])
  GLES30.glBufferData(
    GLES30.GL_ARRAY_BUFFER,
    POSITION_VERTEX.size * 4,
    vertexBuffer,
    GLES30.GL_STATIC_DRAW
  );
  GLES30.glEnableVertexAttribArray(0);
  GLES30.glVertexAttribPointer(0, POSITION_SIZE, GLES30.GL_FLOAT, false, LENGTH * 4, 0);
  GLES30.glEnableVertexAttribArray(1);
  GLES30.glVertexAttribPointer(1, POSITION_SIZE, GLES30.GL_FLOAT, false, LENGTH * 4, POSITION_SIZE*4);
}

override fun onDrawFrame(gl: GL10?) {
  GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
  GLES30.glUseProgram(pointProgram)
  GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 3);
  GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);
}


我们可以看到其实和顶点数组方式基本流程差不多,只不过是多了一个生成缓冲区对象,并绑定的流程。有一点需要注意的是glVertexAttribPointer api中最后一个参数变成了一个int型变量,他设置了缓冲区数据偏移量,作用和顶点数组的数组结构方式中的vertexBuffer.position(3) 效果是一样的。还有一个需要注意的是这个偏移量的单位是字节,也就是数据偏移量*数据类型所占字节数。

顶点数组对象(VAO)

除了顶点缓冲区对象,OPENGL ES还提供了一种性能更高的方式∶顶点数组对象(VAO)。

正如我们已经看到的,使用顶点缓冲区对象设置绘图操作可能需要多次调用glBindBuffr、glVertexAtribPointer和glEnableVertexAtribAray。为了更快地在顶点数组配置之间切换,OpenGL ES3.0推出了顶点数组对象。VAO提供包含在顶点数组/顶点缓冲区对象配置之间切换所需要的所有状态的单一对象。
实际上,OpenGL ES3.0中总是有一个活动的顶点数组对象。本章目前为止的所有例子都在默认的顶点数组对象上操作(默认VAO的ID为0)。要创建新的顶点数组对象,可以使用 glGen VertexArays 函数。

override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
  pointProgram = ShaderUtil.loadProgramFromAssets(
    "vertex_point_1.glsl",
    "frag_point_1.glsl",
    context.resources
  )
  GLES30.glGenBuffers(1, vaoIds, 0);
  GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vaoIds[0])
  GLES30.glBufferData(
    GLES30.GL_ARRAY_BUFFER,
    POSITION_VERTEX.size * 4,
    vertexBuffer,
    GLES30.GL_STATIC_DRAW
  );

  GLES30.glGenVertexArrays(1, vaoIds, 0)
  GLES30.glBindVertexArray(vaoIds[0])
  GLES30.glGenBuffers(1, vboIds, 0)
  GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboIds[0])
  GLES30.glBufferData(
    GLES30.GL_ARRAY_BUFFER,
    POSITION_VERTEX.size * 4,
    vertexBuffer,
    GLES30.GL_STATIC_DRAW
  )

  GLES30.glEnableVertexAttribArray(0);
  GLES30.glVertexAttribPointer(0, POSITION_SIZE, GLES30.GL_FLOAT, false, LENGTH * 4, 0);
  GLES30.glEnableVertexAttribArray(1);
  GLES30.glVertexAttribPointer(1, POSITION_SIZE, GLES30.GL_FLOAT, false, LENGTH * 4, POSITION_SIZE*4);
  GLES30.glBindVertexArray(0)

}

override fun onDrawFrame(gl: GL10?) {
  GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
  GLES30.glUseProgram(pointProgram)
  GLES30.glBindVertexArray(vaoIds[0])
  GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 3)
  GLES30.glBindVertexArray(0)
}

可以看到使用方式上和顶点缓冲区对象是十分相似的,只是又多了一个顶点数组对象。

常量顶点属性和统一变量

上文介绍了OPENGL ES中最常用的指定顶点数组属性的方式。现在呢咱们把上文的需求稍微再改变一下,现在不是绘制三个不同位置不同颜色的点了,而是三个不同位置相同颜色的点。是不是感觉需求变简单了,新的需求只是上文中需求的一种特殊情况。所以用上面说的任何一种方式都可以完成这个需求的。只不过是顶点数组的原始数据变化一下:

private val POSITION_VERTEX = floatArrayOf(
  0.0f, 0.5f, 0.0f,        0.1f, 0.1f, 1.0f,
  -0.25f, -0.25f, 0.0f,    0.1f, 0.1f, 1.0f,
  0.25f, -0.25f, 0.0f,     0.1f, 0.1f, 1.0f
)

代表颜色的数据全部变成了一种颜色。

现在咱们是绘制三个点,假如是三十个、三百个呢,相信大家都可以看出来这种方式会重复大量相同数值的数据。所以这个时候大家就需要一种新的赋值方式。也就是给所有着色器中统一值变量赋值。这种赋值方式有两种:常量顶点属性和统一变量。

常量顶点属性

常量顶点属性对于一个图元的所有顶点都相同,所以对一个图元的所有顶点只需指定一个值。可以用如下任何一个函数指定∶

void glvertexAttriblf (int index, float x);
void glVertexAttrib2f(int index, float x, float y);
void givertexattrib3f(int index, float x, float y, float z);
void g1Vertexλttrib4f(int index, float x, float y, float z, float w);
void glVertexttriblfv(int index, float[] values, int offset);
void glVertexAttrib2fv(int index, float[] values, int offset);
void glVertexAttrib3fv(int index, float[] values, int offset);
void g1Vertexttrib4fv(int index, float[] values, int offset);

gIVerexAtrib*命令用于加载index指定的通用顶点属性。glVertexAtriblf和glVertexAtriblfv 函数在通用顶点属性中加载(x,0.0,0.0,1.0)。glVerexAtrib2f和glVertexAtrib2v在通用顶点属性中加载(x,y,0.0,1.0)。glVertexAtri3f和glVertexAtrb3fv在通用顶点属性中加载(x,y,z,1.0)。glVertexAtrib4f和glVertexAtrib4fv在通用顶点属性中加载(x,y,z,w)。在实践中,常量顶点属性提供与使用标量/向量统一变量等价的功能,两者都是可以接受的选择。

统一变量

OpenGL ES着色语言中的变量类型限定符之一是统一变量。统一变量存储应用程序通过OpenGL ES3.0API传入着色器的只读值,对于保存着色器所需的所有数据类型(如变换矩阵、照明参数和颜色)都很有用。本质上,一个着色器的任何参数在所有顶点或者片段中都应该以统一变量的形式传入。在编译时已知值的变量应该是常量,而不是统一变量,这样可以提高效率。
统一变量在全局作用域中声明,只需要统一限定符。下面是统一变量的一些例子∶

uniform mat4 viewMatrix;
uniform vec3 lightPosition;

效果上统一变量和常量顶点属性基本上是差不多的,区别是同一变量是支持片元着色器。需要注意的是,统一变量的命名空间在顶点着色器和片段着色器中都是共享的。也就是说,如果顶点和片段着色器一起链接到一个程序对象,它们就会共享同一组统一变量。因此,如果在顶点着色器和片段着色器中都声明一个统一变量,那么两个声明必须匹配。应用程序通过API加载统一变量时,它的值在顶点和片段着色器中都可用。

上边简单介绍了一下两种全局统一变量的赋值方式,下边是实际代码应用。

顶点着色器:

#version 300 es
layout (location = 0) in vec4 av_Position;
layout (location = 1) in vec4 point_Size;
void main() {
    gl_Position  = av_Position;
    gl_PointSize = 10.0;
}

片元着色器

#version 300 es
precision mediump float;
out vec4 fragColor;
uniform vec4 frag_point_Color;
void main() {
    fragColor = frag_point_Color;
}

可以看到在之前的demo中固定大小的画笔这次变成了一个顶点属性,片元着色器的颜色值则变成了一个统一变量。同一变量基本使用形式类似顶点属性,有一套相应的Api,唯一不同的是同一变量不能用layout修饰符修饰,也就是只能用api来获取统一变量的index值。

kotlin代码:

class ThreePointColorUniformRenderer(var context: Context) : GLSurfaceView.Renderer {
  var pointProgram = -1
  var vertexBuffer: FloatBuffer
  var fragPointColorIndex = -1
  val POSITION_SIZE = 3
  val COLOR_SIZE = 3
  val VERTEX_ATTRIBUTES_SIZE = 4 * (POSITION_SIZE + COLOR_SIZE)

  private val POSITION_VERTEX = floatArrayOf(
    0.0f, 0.5f, 0.0f,
    -0.25f, -0.25f, 0.0f,
    0.25f, -0.25f, 0.0f
  )

  init {
    vertexBuffer = ByteBuffer.allocateDirect(POSITION_VERTEX.size * 4)
    .order(ByteOrder.nativeOrder())
    .asFloatBuffer()
    .put(POSITION_VERTEX)
    vertexBuffer.position(0)
  }

  override fun onDrawFrame(gl: GL10?) {
    GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
    GLES30.glUseProgram(pointProgram)
    GLES30.glEnableVertexAttribArray(0)
    GLES30.glVertexAttribPointer(0, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)
    GLES30.glDrawArrays(GLES30.GL_POINTS, 0, 3)
    GLES30.glDisableVertexAttribArray(0)
  }

  override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
    GLES30.glViewport(0, 0, width, height)
  }

  override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
    pointProgram = ShaderUtil.loadProgramFromAssets(
      "vertex_point_2.glsl",
      "frag_point_2.glsl",
      context.resources
    )
    GLES30.glUseProgram(pointProgram)
    fragPointColorIndex = GLES30.glGetUniformLocation(pointProgram, "frag_point_Color")
    GLES30.glUniform4fv(fragPointColorIndex, 1, floatArrayOf(1.0f, 0.5f, 0.1f, 1.0f), 0)
    GLES30.glVertexAttrib1f(1, 20.0f)
  }
}

总结

这一章主要是详细讲述了从应用层把参数值传递到OPENGL ES工作环境的几种方式。我在刚学这一块的时候书本上直接把这几种方式罗列了出来,也没有系统的讲解这几种方式的应用场景,我当时是看的一脸懵逼。在本章教程中从需求作为出发点展示了几种传值方式的异同。希望可以更好地帮到想学习OPENGL ES的同学们。然后就是OPENGL ES的api的设计方式有浓烈的C语言风格,写习惯了java代码的同学肯定是适应不过来的。java代码可能调用一个对象的一个方法就能方法完成的工作,在OPENGL ES中一般来说都是需要调用gen系列的api生成一个id,然后使用bind api绑定id,也就是确定调用对象,然后才是调用相关的方法api,最后还得解绑这个id。这个还是需要多多练习来适应。

swdt.png

一般来讲,每个顶点着色器都不一样的顶点属性,比如点位置可以使用顶点数组赋值,当点的位置恒定时就可以使用顶点缓冲区或者顶点数组对象方式来优化性能。当处理每个着色器都是统一值的属性时,比如说当所有点的颜色都一样时则需要考虑使用常量顶点属性或者统一变量的形式来进行赋值,这两个在使用上的区别则是是否支持片元着色器。

本章内容到这基本就结束了,下章会开始纹理部分的相关知识。

代码已经同步更新到了https://github.com/fengao1004/OpenGlES3Demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335