一、开始
现在我们要做的内容是让我们的桌面看起来更真实一些,我们基本上来说有两个事情要做,一个是让桌面的颜色不要那么的单调,一个就是需要适配屏幕的比例。
最终我们完成的效果如下图:
二、让桌面有变化的颜色
好的,现在就让我们学习如何让桌面的颜色有变化,我们看到我们最终完成的效果图其实是四边比较暗,中间比较亮,同时我们也知道,在上篇文章中,我们是用两个三角形去绘制的,所以这种效果是达不到的,这个时候我们就可以去学一个新东西了,叫做TRIANGLE FAN
,它的绘制原理可以看下图:
就是定义一个中心点,然后其他不论定义多少点,都是按照图中的顺序去绘制三角形的,按照这张图我们就可以解决四边暗,中间亮的问题了。
我们需要把三角型定义的那块数据修改下
// Triangle Fan
0, 0,
-0.5f, -0.5f,
0.5f, -0.5f,
0.5f, 0.5f,
-0.5f, -0.5f,
然后绘制的方法也需要修改下,如下:
glDrawArrays(GL_TRIANGLE_FAN, 0, 6);
接下来就要解决我们的重点问题了,如何让颜色产生变化呢?
OpenGL提供了一个叫做Smooth Shading东西去做这件事,他让我们可以平稳的改变线和三角形的颜色,原文是这样说的:
OpenGL gives us a way to smoothly blend the colors at each vertex across a line or across the surface of a triangle.
要让颜色变化,就需要告诉OpenGL要用什么颜色去绘制,所以我们的数据需要修改成这个样子:
private final float[] mData = new float[]{
// Order of coordinates: X, Y, R, G, B
// Triangle Fan
0f, 0f, 1f, 1f, 1f,
-0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
0.5f, 0.5f, 0.7f, 0.7f, 0.7f,
-0.5f, 0.5f, 0.7f, 0.7f, 0.7f,
-0.5f, -0.5f, 0.7f, 0.7f, 0.7f,
// 线
-0.5f, 0f, 1f, 0f, 0f,
0.5f, 0f, 1f, 0f, 0f,
// 点
0f, -0.25f, 0f, 0f, 1f,
0f, 0.25f, 1f, 0f, 0f
};
前面两个参数还是点的坐标,后面三个参数是表示RGB颜色的,然后大家可能对一个三角形的颜色是怎么根据三个点的颜色来变化的不太清楚,现在解释下就清楚了,看下图:
其实三角形内的每个点的颜色都是根据三个点的颜色,然后乘以比例得到的,大家如果想了解的话,可以自己去看书,或者查资料。
现在我们定义了颜色的数据,我们要让OpenGL去使用我们定义的颜色数据,我们先修改下
simple_vertex_shader.glsl
的代码:
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
gl_PointSize = 10.0;
v_Color = a_Color;
}
我们定义了一个attribute叫做a_Color,这个不用解释了,大家还没见过varying,书上对varying是这样解释的:
A varying is a special type of variable that blends the values given to it and sends these values to the fragment shader.
就是一种特殊的变量混合给到他的值然后传递给fragment shader进行绘制,在这里就是混合三个顶点的颜色值,然后计算出该点的颜色,传递给fragment shader进行绘制,我们已经计算出来颜色了,接下来就是在fragment shader中使用它了,修改simple_fragment_shader.glsl
代码如下:
precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}
这样我们的OpenGL就可以用计算出来的颜色去绘制每个点了,u_Color已经没用了,注意删掉相关代码。
接下来我们就需要修改我们的java代码了,首先我们添加这些定义:
private static final String A_COLOR = "a_Color";
private static final int COLOR_COMPONENT_COUNT = 3;
private static final int STRIDE = (POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT) * BYTE_PRE_FLOAT;
private int mAColorLocation;
第一行和最后一行看过上篇文章的人应该都知道是干嘛用的了,COLOR_COMPONENT_COUNT
表示的是颜色参数的个数,RGB三个值,然后最后STRIDE上篇文章有提到,这里解释下,原文是这样说的:
Did you notice that we added a special constant, called STRIDE? As we now have both a position and a color attribute in the same data array, OpenGL can no longer assume that the next position follows immediately after the previous position. Once OpenGL has read the position for a vertex, it will have to skip over the color for the current vertex if it wants to read the position for the next vertex. We’ll use the stride to tell OpenGL how many bytes are between each position so that it knows how far it has to skip.
意思就是当我们的数据有两种属性的时候,我们需要这个值去告诉OpenGL取其中一种属性的时候,需要跳过多少值。
然后我们需要获取a_Color的位置信息:
mAColorLocation = glGetAttribLocation(mProgram , A_COLOR);
然后修改告诉OpenGL如何取值的那块代码需要修改成下面的样子:
mVertexData.position(0);
glVertexAttribPointer(mAPositionLocation , POSITION_COMPONENT_COUNT, GL_FLOAT , false , STRIDE , mVertexData);
glEnableVertexAttribArray(mAPositionLocation);
mVertexData.position(POSITION_COMPONENT_COUNT);
glVertexAttribPointer(mAColorLocation , COLOR_COMPONENT_COUNT , GL_FLOAT , false , STRIDE , mVertexData);
glEnableVertexAttribArray(mAColorLocation);
然后运行就可以看到效果了,运行之后你会发现我们好像已经完成了效果图的效果了,但是你可以试着旋转屏幕看看,会出现什么情况,你会发现,桌面变窄了,为了防止这种情况的发生,我们就需要对屏幕比例进行适配,就是接下来我们要学习的内容。
三、适配屏幕的比例
原文是这样说的:
To adjust the coordinate space so that we can take screen orientation into account, we need to stop working directly in normalized device coordinates and start working in a virtual coordinate space.
正常的设备坐标系是不考虑屏幕比例的,所以会导致上面的情况发生。所以我们需要在虚拟坐标系中绘制。所以我们要找到一个办法把虚拟坐标系中的点转换成正常的设备坐标系的点的坐标,要做到这点,我们就需要正投影了,用正投影需要用到矩阵的知识,书中有讲解,大家可以自己看书,也可以查资料,看这个只是了解一个方法的实现原理,在这里就不详细讲述了,只告诉大家怎么去用这个方法好了。
我们需要用到的方法是在android.opengl
包下的
orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)
具体的参数解释在原文中这这样的:
具体每个值代表什么看注释应该了解一些了,我们在使用中再去深入了解,用这个矩阵我们就可以获得虚拟坐标系中的点在正常的设备坐标系中的点的坐标了,然后我们现在就要去使用这个矩阵了,先修改
simple_vertex_shader.glsl
中的代码如下:
uniform mat4 u_Matrix;
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
v_Color = a_Color;
gl_Position = u_Matrix * a_Position;
gl_PointSize = 10.0;
}
我们定义了一个矩阵u_Matrix,mat4
表示我们定义的变量是一个4×4的矩阵,OpenGL部分的代码修改完了,然后我们就需要在Java代码中为u_Matrix赋值了,首先我们在Renderer中做如下定义:
private static final String U_MATRIX = "u_Matrix";
private int mUMatrixLocation;
private float[] mProjectionMatrix = new float[16];
mProjectionMatrix是定义来储存投影矩阵的,然后我们需要去获取u_Matrix的位置:
mUMatrixLocation = glGetUniformLocation(mProgram , U_MATRIX);
然后在onSurfaceChanged()
方法中添加如下代码:
final float aspectRatio = width > height ?
(float)width / (float) height : (float) height/ (float) width;
if (width > height){
//横屏
orthoM(mProjectionMatrix , 0 , -aspectRatio , aspectRatio , -1 , 1 , -1 , 1);
}else {
//竖屏
orthoM(mProjectionMatrix , 0 , -1 , 1 , -aspectRatio , aspectRatio , -1 , 1);
}
这样我们就计算除了正投影矩阵,并赋值给了mProjectionMatrix。
最后我们在onDrawFrame()
中绘制之前加上如下代码:
glUniformMatrix4fv(mUMatrixLocation , 1 , false , mProjectionMatrix , 0);
运行下就能看到效果了,这下旋转的时候就没有什么变化了,但是有没有觉得不太好看,我们调整下我们的数据,就会好了,调整数据成下面的样子:
private final float[] mData = new float[]{
// Order of coordinates: X, Y, R, G, B
// Triangle Fan
0f, 0f, 1f, 1f, 1f,
-0.5f, -0.8f, 0.7f, 0.7f, 0.7f,
0.5f, -0.8f, 0.7f, 0.7f, 0.7f,
0.5f, 0.8f, 0.7f, 0.7f, 0.7f,
-0.5f, 0.8f, 0.7f, 0.7f, 0.7f,
-0.5f, -0.8f, 0.7f, 0.7f, 0.7f,
//线
-0.5f, 0f, 1f, 0f, 0f,
0.5f, 0f, 1f, 0f, 0f,
//点
0f, -0.4f, 0f, 0f, 1f,
0f, 0.4f, 1f, 0f, 0f
};
最后再运行一遍,是不是感觉好多了。
项目代码在这里:https://github.com/KevinKmoo/AirHockeyWithVaryColor
能力有限,自己读书的学习所得,有错误请指导,轻虐!
转载请注明出处。----by kmoo