OpenglES2.0 for Android:来做个地球吧
前言
Ok , 现在回到我们前面绘制球的项目(http://blog.csdn.net/cassiepython/article/details/51620114),前面我们已经完成了球的绘制,而且使用了棋盘纹理,
这次我们需要使用上面的图片来做纹理贴图,所以我们先删除掉原来的关于棋盘纹理的代码。然后将纹理工具类copy到工具类目录下,准备工作完成。
(最后会给出整个项目的代码,如果你不想从原来的开始改的话可以直接看这个)
看下我们的步骤 :
1 修改着色器代码加入纹理
这个在上两节已经说过,直接看更改后的代码:
<span style="font-size:12px;">//vertex_shader_ball.glsl
uniform mat4 u_Matrix;//最终的变换矩阵
attribute vec4 a_Position;//顶点位置
attribute vec2 a_TextureCoordinates;
varying vec2 v_TextureCoordinates;
void main()
{
gl_Position = u_Matrix * a_Position;
v_TextureCoordinates = a_TextureCoordinates;
} </span>
<span style="font-size:12px;">//fragment_shader_ball.glsl
precision mediump float;
uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;
void main()
{
gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
} </span>
2 纹理坐标计算
前面我们说过做纹理贴图最关键的地方是确定我们的顶点坐标与纹理坐标的对应关系。对于二维的图像以及比较规则的三维图像比较容易确定,但是对于球该如何处理呢?看我们原先是如何计算球的顶点坐标的。我们通过半径r (确定量)和两个角度 θ (范围0到180)和φ(范围0到360)来计算绘制球时的一个小方格的左上角顶点的坐标,同时知道分割的间距singleSpan ,由此可以确定这个小方格的其余三个顶点坐标。同理我们如何能确定左上角顶点坐标的对应的纹理坐标,其余的也就容易求了。于是问题转化成这样:要求一个球的纹理坐标只需要确定绘制球的某个小方格的四个顶点对应的纹理坐标,进一步讲如何确定小方格的左上角顶点与相应纹理坐标的关系。
现在我们以及清楚了问题,下面考虑具体的。我们绘制球时是一个个的小方格(矩形),纹理应该也是分成一个个的小方格(矩形),然后与球的一个个的小方格一一对应。绘制球的确定小方格位置的两个变量(θ 和φ)的范围分别为 0 到180 ,0到360 ,对应纹理的T (1到0 ,注意为什么不是0到1 见上一节最后)和 S ( 0到1), 于是通过比例g关系我们有如下:
当前角度 θ / 180 = 1– 纹理 t坐标 / 1
当前角度φ / 360 = 纹理 s坐标 / 1
通过这两个角度我们确定了顶点坐标也确定了相应的纹理坐标,由此我们就知道了顶点对应的纹理坐标。下面我们就可以具体编码实现了。
此时Ball.java代码如下 :
package com.cumt.shape;
import static android.opengl.GLES20.GL_TEXTURE0;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.glActiveTexture;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glUniform1i;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import android.content.Context;
import android.opengl.GLES20;
import com.cumt.openglesearth.R;
import com.cumt.utils.MatrixState;
import com.cumt.utils.ShaderHelper;
import com.cumt.utils.TextResourceReader;
import com.cumt.utils.TextureHelper;
public class Ball {
private Context context;
private static final float UNIT_SIZE = 1.0f;// 单位尺寸
private float r = 0.6f; // 球的半径
final int angleSpan = 10;// 将球进行单位切分的角度
private FloatBuffer vertexBuffer;// 顶点坐标
int vCount = 0;// 顶点个数,先初始化为0
// float类型的字节数
private static final int BYTES_PER_FLOAT = 4;
// 数组中每个顶点的坐标数
private static final int COORDS_PER_VERTEX = 3;
// **********************************************************************
private static final int TEXTURE_COORDIANTES_COMPONENT_COUNT = 2; // 每个纹理坐标为 S T两个
private static final String A_TEXTURE_COORDINATES = "a_TextureCoordinates";//纹理
private static final String U_TEXTURE_UNIT = "u_TextureUnit";//纹理
private FloatBuffer textureBuffer;// 纹理坐标
private int uTextureUnitLocation;
private int aTextureCoordinates;
private int texture;
// ***********************************************************************
private int program;
private static final String A_POSITION = "a_Position";
private static final String U_MATRIX = "u_Matrix";
private int uMatrixLocation;
private int aPositionLocation;
public Ball(Context context){
this.context = context;
initVertexData();
getProgram();
aPositionLocation = GLES20.glGetAttribLocation(program, A_POSITION);
uMatrixLocation = GLES20.glGetUniformLocation(program, U_MATRIX);
// ***********************************************************************
initTexture();
// **********************************************************************
//---------传入顶点数据数据
GLES20.glVertexAttribPointer(aPositionLocation, COORDS_PER_VERTEX,
GLES20.GL_FLOAT, false, 0, vertexBuffer);
GLES20.glEnableVertexAttribArray(aPositionLocation);
// ***********************************************************************
GLES20.glVertexAttribPointer(aTextureCoordinates, TEXTURE_COORDIANTES_COMPONENT_COUNT,
GLES20.GL_FLOAT, false, 0, textureBuffer);
GLES20.glEnableVertexAttribArray(aTextureCoordinates);
// **********************************************************************
}
public void initVertexData() {
ArrayList<Float> alVertix = new ArrayList<Float>();// 存放顶点坐标的ArrayList
// ***************************************
ArrayList<Float> textureVertix = new ArrayList<Float>();// 存放纹理坐标的ArrayList
// ***************************************
for (int vAngle = 0; vAngle < 180; vAngle = vAngle + angleSpan)// 垂直方向angleSpan度一份
{
for (int hAngle = 0; hAngle <= 360; hAngle = hAngle + angleSpan)// 水平方向angleSpan度一份
{
// 纵向横向各到一个角度后计算对应的此点在球面上的坐标
float x0 = (float) (r * UNIT_SIZE
* Math.sin(Math.toRadians(vAngle)) * Math.cos(Math
.toRadians(hAngle)));
float y0 = (float) (r * UNIT_SIZE
* Math.sin(Math.toRadians(vAngle)) * Math.sin(Math
.toRadians(hAngle)));
float z0 = (float) (r * UNIT_SIZE * Math.cos(Math
.toRadians(vAngle)));
// Log.w("x0 y0 z0","" + x0 + " "+y0+ " " +z0);
float x1 = (float) (r * UNIT_SIZE
* Math.sin(Math.toRadians(vAngle)) * Math.cos(Math
.toRadians(hAngle + angleSpan)));
float y1 = (float) (r * UNIT_SIZE
* Math.sin(Math.toRadians(vAngle)) * Math.sin(Math
.toRadians(hAngle + angleSpan)));
float z1 = (float) (r * UNIT_SIZE * Math.cos(Math
.toRadians(vAngle)));
float x2 = (float) (r * UNIT_SIZE
* Math.sin(Math.toRadians(vAngle + angleSpan)) * Math
.cos(Math.toRadians(hAngle + angleSpan)));
float y2 = (float) (r * UNIT_SIZE
* Math.sin(Math.toRadians(vAngle + angleSpan)) * Math
.sin(Math.toRadians(hAngle + angleSpan)));
float z2 = (float) (r * UNIT_SIZE * Math.cos(Math
.toRadians(vAngle + angleSpan)));
// Log.w("x2 y2 z2","" + x2 + " "+y2+ " " +z2);
float x3 = (float) (r * UNIT_SIZE
* Math.sin(Math.toRadians(vAngle + angleSpan)) * Math
.cos(Math.toRadians(hAngle)));
float y3 = (float) (r * UNIT_SIZE
* Math.sin(Math.toRadians(vAngle + angleSpan)) * Math
.sin(Math.toRadians(hAngle)));
float z3 = (float) (r * UNIT_SIZE * Math.cos(Math
.toRadians(vAngle + angleSpan)));
// Log.w("x3 y3 z3","" + x3 + " "+y3+ " " +z3);
// 将计算出来的XYZ坐标加入存放顶点坐标的ArrayList
alVertix.add(x1);
alVertix.add(y1);
alVertix.add(z1);
alVertix.add(x3);
alVertix.add(y3);
alVertix.add(z3);
alVertix.add(x0);
alVertix.add(y0);
alVertix.add(z0);
// *****************************************************************
float s0 = hAngle / 360.0f;
float s1 = (hAngle + angleSpan)/360.0f ;
float t0 = 1 - vAngle / 180.0f;
float t1 = 1 - (vAngle + angleSpan) / 180.0f;
textureVertix.add(s1);// x1 y1对应纹理坐标
textureVertix.add(t0);
textureVertix.add(s0);// x3 y3对应纹理坐标
textureVertix.add(t1);
textureVertix.add(s0);// x0 y0对应纹理坐标
textureVertix.add(t0);
// *****************************************************************
alVertix.add(x1);
alVertix.add(y1);
alVertix.add(z1);
alVertix.add(x2);
alVertix.add(y2);
alVertix.add(z2);
alVertix.add(x3);
alVertix.add(y3);
alVertix.add(z3);
// *****************************************************************
textureVertix.add(s1);// x1 y1对应纹理坐标
textureVertix.add(t0);
textureVertix.add(s1);// x2 y3对应纹理坐标
textureVertix.add(t1);
textureVertix.add(s0);// x3 y3对应纹理坐标
textureVertix.add(t1);
// *****************************************************************
}
}
vCount = alVertix.size() / COORDS_PER_VERTEX;// 顶点的数量
// 将alVertix中的坐标值转存到一个float数组中
float vertices[] = new float[vCount * COORDS_PER_VERTEX];
for (int i = 0; i < alVertix.size(); i++) {
vertices[i] = alVertix.get(i);
}
vertexBuffer = ByteBuffer
.allocateDirect(vertices.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
// 把坐标们加入FloatBuffer中
vertexBuffer.put(vertices);
// 设置buffer,从第一个坐标开始读
vertexBuffer.position(0);
// *****************************************************************
float textures[] = new float[textureVertix.size()];
for(int i=0;i<textureVertix.size();i++){
textures[i] = textureVertix.get(i);
}
textureBuffer = ByteBuffer
.allocateDirect(textures.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
// 把坐标们加入FloatBuffer中
textureBuffer.put(textures);
// 设置buffer,从第一个坐标开始读
textureBuffer.position(0);
// *****************************************************************
}
//获取program
private void getProgram(){
//获取顶点着色器文本
String vertexShaderSource = TextResourceReader
.readTextFileFromResource(context, R.raw.vertex_shader_ball);
//获取片段着色器文本
String fragmentShaderSource = TextResourceReader
.readTextFileFromResource(context, R.raw.fragment_shader_ball);
//获取program的id
program = ShaderHelper.buildProgram(vertexShaderSource, fragmentShaderSource);
GLES20.glUseProgram(program);
}
// *******************************************************
//初始化加载纹理
private void initTexture(){
aTextureCoordinates = GLES20.glGetAttribLocation(program, A_TEXTURE_COORDINATES);
uTextureUnitLocation = GLES20.glGetAttribLocation(program, U_TEXTURE_UNIT);
texture = TextureHelper.loadTexture(context, R.drawable.logo,false);
// Set the active texture unit to texture unit 0.
glActiveTexture(GL_TEXTURE0);
// Bind the texture to this unit.
glBindTexture(GL_TEXTURE_2D, texture);
// Tell the texture uniform sampler to use this texture in the shader by
// telling it to read from texture unit 0.
glUniform1i(uTextureUnitLocation, 0);
}
// *******************************************************
public void draw(){
//将最终变换矩阵写入
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, MatrixState.getFinalMatrix(),0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
}
}
其中 //****************************与 //**********************************之间的内容是我们相比原来绘制球新增加的内容。
相比前面的知识并没有新增什么,只是要注意纹理坐标的求法。
运行结果就如我们开头处展示的那样,这样使用原来会只球的代码,点击屏幕会绕y轴旋转,大家可以自由发挥
增添一些额外的变换。
3 源码下载
最后给出源码下载
http://download.csdn.net/detail/cassiepython/9551962