增加颜色和着色
1. 创建新的Activit和渲染器
- 创建新的 AirHockey2 代码和之前一样。
- 创建AirHockeyRender2
这一节主要为游戏桌面增加平滑着色。在前一部分,我们使用的单一颜色绘制物体。其实还可以绘制变化的颜色,也就是平滑着色。平滑着色其实是通过定点来完成的。比如三角形状,三个顶点各一个颜色,这样一个点到另外一个点之前颜色就是变化的。
之前我们只能绘画三角形,线,和点。这一节,要引入一个三角扇形。
三星扇形,就是以一个中心点。以两个相邻的点创建一个三角形状。然后每增加一个点。就创建出了一个三角形。最后以起始汇合,形成一个闭环。
AirHockeyRenderer2 类里面的坐标点变成这样:
float[] tableVerticesWithTriangles = {
//Triagle Fan
0,0,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,
// Line 1
-0.5f,0f,1f,0f,0f,
0.5f,0f,1f,0f,0f,
// Mallets
0f,-0.25f,0f,0f,1f,
0f,0.25f,1f,0f,0f
};
没一个点,包括x,y,r,g,b,构成一个顶点的位置和颜色。前面6个点就是定义三角扇形的。
接下来,重新创建了simple_vertex_shader2,simple_fragment_shader2作为渲染器
- 顶点着色器 simple_vertex_shader2:
attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main(){
v_Color = a_Color;
gl_Position = a_Position;
gl_PointSize = 10.0;
}
增加了a_Color 颜色属性。增加了一个特殊变量v_Color,varying类型,他会把给它的值进行混合,并把这些混合后的值发送给片段着色器。
- 片段着色器 simple_fragment_shader2:
precision mediump float;
varying vec4 v_Color;
void main(){
gl_FragColor = v_Color;
}
颜色换成了varying变量,如果片段属于一个三角形,那么OpengGL会用构成三角形的三个顶点计算器混合颜色。如果是一条直线,那么OpengGL会用构成直线的两个顶点计算器混合后的颜色。
- varying 颜色的计算是线性的。就是说不通位置,计算的混合颜色是不一样的。
使用和渲染
- 增加常量和位置,还要增加一个跨距。
private static final String A_COLOR = "a_Color";
private int aColorLocation;
private static final int COLOR_COMPONENT_COUNT = 3;
private static final int STRIDE = (POSITION_COMPONENT_COUNT+COLOR_COMPONENT_COUNT)*BYTES_PER_FLOAT;
去掉之前的u_Color相关的。
获取新颜色属性的位置:
aColorLocation = glGetAttribLocation(program,A_COLOR);
告诉OpenGL 顶点中数据与着色器中的a_Color 联系起来
vertexData.position(POSITION_COMPONENT_COUNT);
glVertexAttribPointer(aColorLocation,COLOR_COMPONENT_COUNT,GL_FLOAT,false,STRIDE,vertexData);
//告诉OpenGL 把定点数据与做色漆中的a_Color 关联起来
glEnableVertexAttribArray(aColorLocation);
最后绘画方法里是这样的:
glClear(GL_COLOR_BUFFER_BIT);
//绘制三角扇形
glDrawArrays(GL_TRIANGLE_FAN,0,6);
//绘制分割线
glDrawArrays(GL_LINES,6,2);
//绘制点
glDrawArrays(GL_POINTS,8,1);
glDrawArrays(GL_POINTS,9,1);
不需要给片段颜色,会使用顶点颜色。
调整屏幕宽高比
1. 创建新的Activit和渲染器
- 创建AirHockey3
- 创建渲染器 AirHockeyRenderer3
OpengGL 里面的Postion使用的是归一化坐标。不论屏幕尺寸是多少。OpengGL 里面的坐标都是-1到1,这样在不同分辨率下。就显示效果不是预期的,导致我们的桌面显示在横竖屏下,效果不一致,不是我们想要的。所以我们改成使用虚拟化坐标空间。然后就需要找到一种把我们用的虚拟化坐标转化为归一化坐标的方法。
我们想要的这种操作叫做正交投影。使用方法就是用orthoM方法创建一个正交投影矩阵,然后用这个矩阵乘以虚拟坐标。就可以得到归一化坐标了。
首先修改着色器程序。新的定点色器程序为 simple_vertex_shader3:
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;
}
增加了一个新的uniform 定义 u_Marix,mat4类型,也就是一个4*4的矩阵。最终gl_Position使用的就是矩阵和虚拟化坐标的乘积。
AirHockeyRenderer3 里面增加定义,矩阵属性,uniform位置,uniform名字
private static final String U_MATRIX = "u_Matrix";
private int uMatrixLocation;
private final float[] projectionMatrix = new float[16];
获得uniform位置。
uMatrixLocation = glGetUniformLocation(program,U_MATRIX);
在onSurfaceChanged里面我们创建正交矩阵
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
glViewport(0,0,width,height);
final float aspectRatio = width > height ? (float)width/(float)height: (float)height/(float)width;
//在不同屏幕尺寸下创建正交矩阵
if(width > height){
//LandScape
orthoM(projectionMatrix,0,-aspectRatio,aspectRatio,-1f,1f,-1f,1f);
}else{
orthoM(projectionMatrix,0,-1f,1f,-aspectRatio,aspectRatio,-1f,1f);
}
}
这段代码会创建一个正交投影矩阵,这个矩阵会把屏幕当前方向计算在内。它会建立一个虚拟坐标空间。
最后在绘画里面更新矩阵数据
glUniformMatrix4fv(uMatrixLocation,1,false,projectionMatrix,0);
参考书籍:OpenGL ES 应用开发实践指南(Kevin Brothaler)
github源码: https://github.com/SnailCoderGu/OpenGL