获取顶点数据
假设我们有三角形的三个点的数据,每个顶点由 (x, y, z)
三个基向量表示,每个纹理由 (u, v)
两个基向量表示。
private float[] mTriangleVerticeData = {
-1.0f, -0.5f, 0.0f, -0.5f, 0.0f,
1.0f, -0.5f, 0.0f, 1.5f, -0.0f,
0.0f, 1.1180339f, 0.0f, 0.5f, 1.6183399f
};
OpenGL 中处理数据没有像面向语言一样那么结构化的存取,只能使用数组的方式进行存取(开发者需要自己知道数组中每个数据表示什么意思)。
private static final int FLOAT_SIZE_BYTES = 4; // 一个 float 数据占4字节
private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES; // 每5个元素表示一个顶点
private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0; // 第一个顶点坐标数据的偏移量
private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3; // 第一个顶点 U,V 数据的偏移量
private FloatBuffer mTriangleVertices; // 用于将 jvm 堆栈中的属性,存储到计算机的直接内存中
mTriangleVertices = ByteBuffer.allocateDirect(mTriangleVerticesData.length * FLOAT_SIZE_BYTES)
.order(ByteOrder.nativeOrder()).asFloatBuffer(); // 分配直接内存空间
mTriangleVertices.put(mTriangleVerticesData); // 将堆栈中的顶点数据存到直接内存中
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); // 将读数据指针指向索引 0 处,准备开始读数据
glVertexAttribPointer(aPositionHandle, 3, GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); // 从 0 开始取 3 个字节数据,再间隔 20 字节,再取 3 个,直到数组尾部。这是取块状数据的关键
GlUtil.checkGlError("glVertexAttribPointer aPositionHandle");
上面的程序块首先将 JVM 中的数据数组 mTriangleVerticeData
转移到直接内存 mTriangleVertices
中,然后再从直接内存中按块读出数据,拷贝到 GPU 存储空间的 aPositionHandle
地址。
画一个点
我们先来看看绘制的 Shader 程序:
- 顶点 Shader 程序:写了一个点的位置和大小。其中
gl_Position
表示一个存储 4 维向量数组的地址,但是其实我们在下面的应用中,点坐标只有两个值(x, y)
。
为什么只有两个值的坐标却要用 4 维向量来存储?
点坐标其实是(x, y, z, 1.0f)
,表示向量时为(x, y, z, 0.0f)
,不论是点表示还是向量表示,最终都会与一个 4x4 的矩阵相乘进行变换。因此必须是一个 4 维向量。
我们在 onDrawFrame()
绘画时也要注意取值使用 GL_POINTS
,这样就会两个 float
一组地取。gl_PointSize
单位为像素。
attribute vec4 a_Position;
void main()
{
gl_Position = a_Position;
gl_PointSize = 30.0;
}
- 片元 Shader 程序:表明顶点图形中应该填充什么颜色。这里的
gl_FragColor
接受一个四维向量,它们依次表示R, G, B, A
。我们在下面onDrawFrame()
调用绘制时,会将颜色向量传到该地址。
precision mediump float;
uniform vec4 u_Color;
void main() {
gl_FragColor = u_Color;
}
假设我们需要画的点为 (0, 0)
:
float[] pointVertex = {
0f, 0f
};
按照上面的步骤,我们首先将 JVM 数据拷贝到直接内存中:
private FloatBuffer vertexData;
...
vertexData = ByteBuffer.allocateDirect(pointVertex.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
vertexData.put(pointVertex);
然后在 onSurfaceCreated(GL10 gl10, EGLConfig eglConfig)
方法中通过 glGetUniformLocation
或 glGetAttribLocation
来获取点坐标存储在 GPU 中的位置值,并将直接内存中的点坐标数据拷贝到该地址指针中。最后调用 glEnableVertexAttribArray(aPositionLocation)
来使能 aPositionLocation
。
通过
glGetUniformLocation
或glGetAttribLocation
获取到的位置值一般是0, 1...
这样增加的,不过aColorLocation
究竟是 0 还是 1,是不可知的。也可以在glLinkProgram
之前调用glBindAttribLocation(program, 0, "a_Position")
来指定aPosotionLocation
值就是 0。
如果顶点着色器中使用了attribute
属性的变量(在本例中是a_Position
),那么在 GPU 执行着色器代码之前,一定要通过glEnableVertexAttribArray()
使能这个变量,否则在 GPU执行着色器代码时(通过调用glDraw**()
),是无法访问到该attribute
属性的。
@Override
public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
// 前面是 shader 程序编译和初始化
...
aColorLocation = glGetUniformLocation(program, U_COLOR); // 获取颜色信息在 GPU 存储空间的地址
aPositionLocation = glGetAttribLocation(program, A_POSITION); // 获取点在 GPU 存储空间的地址
vertexData.position(0);
Timber.d("enable vertex attribute");
glVertexAttribPointer(aPositionLocation, POSITION_COMPONENT_COUNT, GL_FLOAT, false, 0, vertexData); // 拷贝点坐标到 GPU 存储空间
glEnableVertexAttribArray(aPositionLocation);
}
然后在 onDrawFrame(GL10)
时,指明每一帧的绘制的数据颜色和位置。
@Override
public void onDrawFrame(GL10 gl10) {
glClear(GL_COLOR_BUFFER_BIT);
glUniform4f(aColorLocation, 0.0f, 1.0f, 0.5f, 1.0f);
glDrawArrays(GL_POINTS, 0, 1);
}
其中 glUniform4f
中指明的颜色是 R, G, B, A
排列。上面的 (0.0f, 1.0f, 0.5f, 1.0f)
表示绿色,每个元素的取值范围是 0.0f ~ 1.0f
之间,表示 0~255
的归一化数据。
画一条线
- 线的连个点坐标:
float[] lineVertex = {
-0.5f, 0.5f,
0.5f, -0.5f
};
- 绘制时使用
GL_LINES
:
glDrawArrays(GL_LINES, 0, 2);
画三角形
- 三角形的 3 个顶点,注意是逆时针的 3 个点:
float[] triangleVertex = {
-1f, 1f,
-1f, -1f,
1f, -1f
};
在 OpenGL 中所有形状的定义都最好按照逆时针来定义,这是因为 OpenGL 中所有形状都有正面和反面,逆时针定义的面是正面,顺时针定义的面是反面。当你开启 face culling 时,OpenGL 将不会画反面,而只会画正面。因此逆时针定义形状是约定俗成。https://developer.android.com/guide/topics/graphics/opengl.html#faces-winding
- 绘制时使用
GL_TRIANGLES
:
glDrawArrays(GL_TRIANGLES, 0, 3);
https://github.com/fightyz/OpenGLPlayground
checkout 到 a650a852fe91d2ad89ca9deab22306979e8a2c7e
网友评论