上一章Android OpenGL ES(二)-正交投影 的学习,我们已经能够画正常的图片图形了,这章我们会继续来绘制正方形和圆的这样的平面图形和绘制纹理。
前两章,其实我们已经明白了绘制平面图形的套路了。
接下来我们按照套路继续画其他的图形。
因为OpenGL只提供给我们画三角形的方式,所以想要正方形的话,其实就是画两个三角形拼在一起。
GL_TRIANGLE_STRIP
的方式GL_TRIANGLE_STRIP.png
如上图。使用GLES20.GL_TRIANGLE_STRIP
可以在定义3个点的确定三角形的情况下,每多一个点,就多绘制一个三角形。这种方式需要注意数组中点的顺序。
1. 修改矩阵的数组。将其改成正方形
按照上图中的顺序定制我们的矩阵数组。
private static float SQUARE_COLOR_COORDS[] = {
//Order of coordinates: X, Y, Z, R,G,B,
-0.5f, 0.5f, 0.0f, 1.f, 0f, 0f, // 0.top left RED
-0.5f, -0.5f, 0.0f, 0.f, 0f, 1f, // 1.bottom left Blue
0.5f, 0.5f, 0.0f, 1f, 1f, 1f, // 3.top right WHITE
0.5f, -0.5f, 0.0f, 0.f, 1f, 0f, // 2.bottom right GREEN
};
2. 调用**onDrawFrame
**内调用我们的代码
//在OnDrawFrame中进行绘制
@Override
public void onDrawFrame(GL10 gl) {
super.onDrawFrame(gl);
//传递给着色器
GLES20.glUniformMatrix4fv(uMatrix, 1, false, mProjectionMatrix, 0);
//1.使用三角形带的方式
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0, VERTEX_COUNT);
}
3. 结果
正方形.png
GL_TRIANGLES_FAN
的方式将传入的顶点作为扇面绘制,ABCDEF绘制ABC、ACD、ADE、AEF四个三角形.
1. 更新正方形的矩阵数组
//0,1,2 是一个三角形 //0,2,3 是一个三角形
private static float SQUARE_COLOR_COORDS[] = {
//Order of coordinates: X, Y, Z, R,G,B,
-0.5f, 0.5f, 0.0f, 1.f, 0f, 0f, // 0.top left RED
0.5f, 0.5f, 0.0f, 1f, 1f, 1f, // 1.top right WHITE
0.5f, -0.5f, 0.0f, 0.f, 1f, 0f, // 2.bottom right GREEN
-0.5f, -0.5f, 0.0f, 0.f, 0f, 1f, // 3.bottom left Blue
};
2. 更新绘制的方法
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, VERTEX_COUNT);
3. 结果
正方形2.png
GL_TRIANGLES
和顶点矩阵数组加位置矩阵数组的方式这种方法就是根据我们的数组,自己来定义绘制的顺序来,完成绘制两个三角形完成正方形的任务。
1. 更新数组的数据
//正方形的点1
private static float SQUARE_COLOR_COORDS[] = {
//Order of coordinates: X, Y, Z, R,G,B,
-0.5f, 0.5f, 0.0f, 1.f, 0f, 0f, // 0.top left RED
-0.5f, -0.5f, 0.0f, 0.f, 0f, 1f, // 1.bottom right Blue
0.5f, 0.5f, 0.0f, 1f, 1f, 1f, // 3.top right WHITE
0.5f, -0.5f, 0.0f, 0.f, 1f, 0f, // 2.bottom left GREEN
};
/*
创建一个遍历的点的顺序.
1,0,2,1 一个三角形
1,2,3,1 另一个三角
*/
private static short SQUARE_INDEX[] = {
1 , 0, 2, 1, 2, 3
};
2. 添加**indexBuffer
**
我们同样需要为我们新添加的位置数组的分配内存,让OpenGL来读取。
// /*
// 新增-为位置添加内存空间
// */
mIndexBuffer = ByteBuffer
.allocateDirect(SQUARE_INDEX.length * Constant.BYTES_PER_SHORT)
.order(ByteOrder.nativeOrder())
.asShortBuffer()
.put(SQUARE_INDEX);
mIndexBuffer.position(0);
3. 修改绘制的方法
//使用indexBuffer的方式
GLES20.glDrawElements(GLES20.GL_TRIANGLES, SQUARE_INDEX.length, GLES20.GL_UNSIGNED_SHORT, mIndexBuffer);
4. 结果
indexBuffer正方形.png
这里我们一共使用三种方式进行绘制
GL_TRIANGLES
将传入的顶点作为单独的三角形绘制,ABCDEF绘制ABC,DEF两个三角形GL_TRIANGLE_FAN
将传入的顶点作为扇面绘制,ABCDEF绘制ABC、ACD、ADE、AEF四个三角形GL_TRIANGLE_STRIP
将传入的顶点作为三角条带绘制,ABCDEF绘制ABC,BCD,CDE,DEF四个三角形绘制圆形。是通过绘制切分的三角形来形成的。三角形切分的越细,越接近圆。
1.更新代表圆形的矩阵数组
GL_TRIANGLE_FAN
绘制扇形的顺序。所以我们需要
a. 先传入一个圆形。
b. 然后按照我们的切分点开始绘制若干个三角形。最后一个三角形闭合,
c. 还需要重复一次起点和终点。image.png
按照上图和我们的切分点,计算每一个点的坐标,放到数组里面。构造出来的数组属性是X,Y,Z,R,G,B
。
private float[] createCircleCoords(Circle circle, int numbersRoundCircle) {
//先计算总共需要多少个点
int needNumber = getCircleVertexNum(numbersRoundCircle);
//创建数组
float[] circleColorCoord = new float[needNumber * TOTAL_COMPONENT_COUNT];
//接下来给每个点分配数据
//对每一组点进行赋值
for (int numberIndex = 0; numberIndex < needNumber; numberIndex++) {
int indexOffset = numberIndex * TOTAL_COMPONENT_COUNT;
if (numberIndex == 0) { //第一个点。就是圆心
//位置
circleColorCoord[indexOffset] = circle.center.x;
circleColorCoord[indexOffset + 1] = circle.center.y;
circleColorCoord[indexOffset + 2] = circle.center.z;
//下面是颜色。给一个白色
circleColorCoord[indexOffset + 3] = 1.f;
circleColorCoord[indexOffset + 4] = 1.f;
circleColorCoord[indexOffset + 5] = 1.f;
} else if (numberIndex < needNumber - 1) { //切分圆的点
//需要根据半径。中心点。来结算
int angleIndex = numberIndex - 1;
float angleRadius = (float) (((float) angleIndex / (float) numbersRoundCircle) * Math.PI * 2f);
float centerX = circle.center.x;
float centerY = circle.center.y;
float centerZ = circle.center.z;
float radius = circle.radius;
float tempX = (float) (centerX + radius * Math.cos(angleRadius));
float tempY = (float) (centerY + radius * Math.sin(angleRadius));
float temp = centerZ + 0;
//位置
circleColorCoord[indexOffset] = tempX;
circleColorCoord[indexOffset + 1] = tempY;
circleColorCoord[indexOffset + 2] = temp;
//下面是颜色。给一个白色
circleColorCoord[indexOffset + 3] = (float) (1.f* Math.cos(angleRadius));
circleColorCoord[indexOffset + 4] = (float) (1.f* Math.sin(angleRadius));
circleColorCoord[indexOffset + 5] = 1.f;
} else { //最后一个点了。重复数据中的二组的位置
//位置.index为1的点
int copyTargetIndex = 1;
//复制点
circleColorCoord[indexOffset] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT];
circleColorCoord[indexOffset + 1] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 1];
circleColorCoord[indexOffset + 2] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 2];
circleColorCoord[indexOffset + 3] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 3];
circleColorCoord[indexOffset + 4] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 4];
circleColorCoord[indexOffset + 5] = circleColorCoord[copyTargetIndex * TOTAL_COMPONENT_COUNT + 5];
}
}
return circleColorCoord;
}
这样就更新好我们的矩阵数组了。
2. 绘制
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, VERTEX_COUNT);
3. 结果
circle.png
画圆就是熟练的应用了正方形的经验。
除了平面图形。我们还能绘制我们自己的2D纹理。
OpenGL中的纹理可以用来表示图像。照片甚至数学算法生成的分形数据。 每个二维的纹理都由许多小的纹理元素(text1)组成。它们是小块的数据。
每个二维的纹理都有自己的坐标空间。其范围是从一个拐角(0,0)到另外一个拐角(1,1)。一个纬度叫做S,而另一个拐角叫做T.
二维纹理坐标.png
对比Android系统的Y轴
android系统中的y轴也是向下的。但是纹理坐标是向上的。
纹理的大小
在标准的OpenGL ES 2.0中,纹理不必是正方形。但是每个纬度都应该是2的幂。POT纹理适用于各种情况。
纹理也有一个最大值,但是会根据不同的实现而变化。
当我们渲染表面上绘制一个纹理时,那个纹理的纹理元素可能无法精确的映射到OpenGL生成的片段上。由两种情况:缩小或者放大。通过纹理过滤(texture filtering),来控制产生的效果。
最近邻过滤.png
双线性过滤会进行插值。
双线性过滤.png
可以生成一组优化过的不同大小的纹理。当生成这组纹理的时候。OpenGL会使用所有的纹理元素生成每个级别的纹理,当过滤纹理时,还要确保所有的纹理元素能被使用。在渲染时,会更具每个片段的纹理元素数量为每个片段选择最合适的级别。
如果OpenGL在不同的MIP贴图级别中来回切换。当我们用双线性过滤使用MIP贴图时,再起渲染的场景中,在不同级别的切换时,就会看到明显的跳跃。我们可以切换到三线性过滤。告诉OpenGL 两个最邻近的MIP贴图级别之间也要插值。这样每个片段总共要使用8个纹理元素插值。有助于消除每个MIP贴图级别中间的过渡。得到一个更平滑的图像。
过滤模式.png
attribute
的location a_TextureCoordinates
(纹理的坐标)属性和varying
型的变量v_TextureCoordinates
attribute vec4 a_Position; //添加了一个 a_TextureCoordinates ,因为他有两个分量。S坐标和T 坐标,所以定义为vec2. attribute vec2 a_TextureCoordinates; uniform mat4 u_Matrix; //然后把坐标传递给被插值的varying varying vec2 v_TextureCoordinates; void main(){ gl_Position=u_Matrix*a_Position; v_TextureCoordinates=a_TextureCoordinates; }sampler2D
采样器u_TextureUnit
,并应用v_TextureCoordinates
纹理坐标系 precision mediump float; //在片元着色器这里添加这个 sampler2D 表示我们要添加2D贴图 uniform sampler2D u_TextureUnit; varying vec2 v_TextureCoordinates; void main(){ //渲染2D纹理,交给fragColor gl_FragColor=texture2D(u_TextureUnit,v_TextureCoordinates); }Y坐标
**是向下的和而纹理中的**T坐标
**是向上的,所以表达同一个点的**Y坐标
**和**T坐标
**是相反的!
//顶点的坐标系 private static float TEXTURE_COORDS[] = { //Order of coordinates: X, Y,S,T -1.0f, 1.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, //bottom left 1.0f, 1.0f, 1.0f, 0.0f, // top right 1.0f, -1.0f, 1.0f, 1.0f, // bottom right };onDrawFrame
方法中,重新激活和绑定。然后调用画出定义的矩阵就可以了
//绑定和激活纹理 //因为我们生成了MIP,放到了GL_TEXTURE0 中,所以重新激活纹理 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); //重新去半丁纹理 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); //设置纹理的坐标 GLES20.glUniform1i(uTexture, 0); //绘制三角形. //draw arrays的几种方式 GL_TRIANGLES三角形 GL_TRIANGLE_STRIP三角形带的方式(开始的3个点描述一个三角形,后面每多一个点,多一个三角形) GL_TRIANGLE_FAN扇形(可以描述圆形) GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, VERTEX_COUNT);image.png
bitmap
总结一下,我们从这第一章节的内容了解到了下面这些使用的知识点:
下一章开始,我们会进入Android的相机和OpenGL的结合。
相机部分结束之后,才会到三维图形的部分。