3D图形都是一个个从三角形搭过来的
Triangle类
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.annotation.SuppressLint;
import android.opengl.GLES30;
import android.opengl.Matrix;
public class Triangle
{
public static float[] mProjMatrix = new float[16];//4x4 投影矩阵
public static float[] mVMatrix = new float[16];//摄像机位置朝向的参数矩阵
public static float[] mMVPMatrix;//最后起作用的总变换矩阵
int mProgram;//自定义渲染管线程序id
int muMVPMatrixHandle;//总变换矩阵引用
int maPositionHandle; //顶点位置属性引用
int maColorHandle; //顶点颜色属性引用
String mVertexShader;//顶点着色器代码脚本
String mFragmentShader;//片元着色器代码脚本
static float[] mMMatrix = new float[16];//具体物体的移动旋转矩阵,包括旋转、平移、缩放
FloatBuffer mVertexBuffer;//顶点坐标数据缓冲
FloatBuffer mColorBuffer;//顶点着色数据缓冲
//ByteBuffer mIndexBuffer;
int vCount=0;
float xAngle=0;//绕x轴旋转的角度
public Triangle(MyTDView mv)
{
//调用初始化顶点数据的initVertexData方法
initVertexData();
//调用初始化着色器的intShader方法
initShader(mv);
}
public void initVertexData()//初始化顶点数据的方法
{
//顶点坐标数据的初始化
vCount=36;
final float UNIT_SIZE=0.15f;
float vertices[]=new float[]//顶点坐标数组
{
/*-4*UNIT_SIZE,0,0,
0,-4*UNIT_SIZE,0,
4*UNIT_SIZE,0,0,*/
/* 0,0,0,
4*UNIT_SIZE,0,4*UNIT_SIZE,
4*UNIT_SIZE,0,0,
0,0,0,
0,0,4*UNIT_SIZE,
4*UNIT_SIZE, 0,4*UNIT_SIZE,*/
/*0,0,0,
4*UNIT_SIZE,4*UNIT_SIZE,0,
4*UNIT_SIZE,0,0,
0,0,0,
0,4*UNIT_SIZE,0,
4*UNIT_SIZE, 4*UNIT_SIZE,0,*/
/*-4*UNIT_SIZE,0,0, //1
0,-4*UNIT_SIZE,0, //2
4*UNIT_SIZE,0,0, //3
-4*UNIT_SIZE,0,0, //4
4*UNIT_SIZE,0,0, //5
0,4*UNIT_SIZE,0, //6
-4*UNIT_SIZE,0,0, //7
0,-4*UNIT_SIZE,0, //8
-4*UNIT_SIZE,0,4*UNIT_SIZE, //9
-4*UNIT_SIZE,0,4*UNIT_SIZE, //10
0,-4*UNIT_SIZE,0 ,//11
0,-4*UNIT_SIZE,4*UNIT_SIZE, //12
0,-4*UNIT_SIZE,4*UNIT_SIZE,//13
0,-4*UNIT_SIZE,0, //14
4*UNIT_SIZE,0,0, //15
0,-4*UNIT_SIZE,4*UNIT_SIZE,//16
4*UNIT_SIZE,0,0, //17
4*UNIT_SIZE,0,4*UNIT_SIZE, //18
0,4*UNIT_SIZE,4*UNIT_SIZE, //19
0,4*UNIT_SIZE,0, //20
4*UNIT_SIZE,0,0, //21
0,4*UNIT_SIZE,4*UNIT_SIZE, //22
4*UNIT_SIZE,0,0, //23
4*UNIT_SIZE,0,4*UNIT_SIZE, //24
-4*UNIT_SIZE,0,4*UNIT_SIZE, //25
-4*UNIT_SIZE,0,0, //26
0,4*UNIT_SIZE,0, //27
-4*UNIT_SIZE,0,4*UNIT_SIZE, //28
0,4*UNIT_SIZE,0, //29
0,4*UNIT_SIZE,4*UNIT_SIZE, //30
-4*UNIT_SIZE,0,4*UNIT_SIZE, //31
0,-4*UNIT_SIZE,4*UNIT_SIZE, //32
4*UNIT_SIZE,0,4*UNIT_SIZE, //33
-4*UNIT_SIZE,0,4*UNIT_SIZE, //34
4*UNIT_SIZE,0,4*UNIT_SIZE, //35
0,4*UNIT_SIZE,4*UNIT_SIZE, //36*/
-4*UNIT_SIZE,0,0-2*UNIT_SIZE, //1
0,-4*UNIT_SIZE,0-2*UNIT_SIZE, //2
4*UNIT_SIZE,0,0-2*UNIT_SIZE, //3
-4*UNIT_SIZE,0,0-2*UNIT_SIZE, //4
4*UNIT_SIZE,0,0-2*UNIT_SIZE, //5
0,4*UNIT_SIZE,0-2*UNIT_SIZE, //6
-4*UNIT_SIZE,0,0-2*UNIT_SIZE, //7
0,-4*UNIT_SIZE,0-2*UNIT_SIZE, //8
-4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //9
-4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //10
0,-4*UNIT_SIZE,0-2*UNIT_SIZE,//11
0,-4*UNIT_SIZE,4*UNIT_SIZE-2*UNIT_SIZE, //12
0,-4*UNIT_SIZE,4*UNIT_SIZE-2*UNIT_SIZE,//13
0,-4*UNIT_SIZE,0-2*UNIT_SIZE, //14
4*UNIT_SIZE,0,0-2*UNIT_SIZE, //15
0,-4*UNIT_SIZE,4*UNIT_SIZE-2*UNIT_SIZE,//16
4*UNIT_SIZE,0,0-2*UNIT_SIZE, //17
4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //18
0,4*UNIT_SIZE,4*UNIT_SIZE-2*UNIT_SIZE, //19
0,4*UNIT_SIZE,0-2*UNIT_SIZE, //20
4*UNIT_SIZE,0,0-2*UNIT_SIZE, //21
0,4*UNIT_SIZE,4*UNIT_SIZE-2*UNIT_SIZE, //22
4*UNIT_SIZE,0,0-2*UNIT_SIZE, //23
4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //24
-4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //25
-4*UNIT_SIZE,0,0-2*UNIT_SIZE, //26
0,4*UNIT_SIZE,0-2*UNIT_SIZE, //27
-4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //28
0,4*UNIT_SIZE,0-2*UNIT_SIZE, //29
0,4*UNIT_SIZE,4*UNIT_SIZE-2*UNIT_SIZE, //30
-4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //31
0,-4*UNIT_SIZE,4*UNIT_SIZE-2*UNIT_SIZE, //32
4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //33
-4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //34
4*UNIT_SIZE,0,4*UNIT_SIZE-2*UNIT_SIZE, //35
0,4*UNIT_SIZE,4*UNIT_SIZE-2*UNIT_SIZE, //36
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
vbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
mVertexBuffer = vbb.asFloatBuffer();//转换为浮点(Float)型缓冲
mVertexBuffer.put(vertices);//在缓冲区内写入数据
mVertexBuffer.position(0);//设置缓冲区起始位置
float colors[]=new float[]//顶点颜色数组
{
1,1,1,0,
1,1,1,0,
1,1,1,0,
1,1,1,0,
1,1,1,0,
1,1,1,0,
1,1,0,0,
1,1,0,0,
1,1,0,0,
1,1,0,0,
1,1,0,0,
1,1,0,0,
1,0,1,0,
1,0,1,0,
1,0,1,0,
1,0,1,0,
1,0,1,0,
1,0,1,0,
1,0,0,0,
1,0,0,0,
1,0,0,0,
1,0,0,0,
1,0,0,0,
1,0,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,1,0,0,
0,0,1,0,
0,0,1,0,
0,0,1,0,
0,0,1,0,
0,0,1,0,
0,0,1,0,
/*1,1,1,0,//白色 1
0,0,1,0,//蓝 2
0,1,0,0,//绿 3
1,1,1,0, //4
0,1,0,0, // 5
0,1,1,0, //6
1,1,1,0, //7
0,0,1,0, //8
1,0,0,0, //9
1,0,0,0, //10
0,0,1,0, //11
1,1,0,0, //12
1,1,0,0, //13
0,0,1,0,//蓝 14
0,1,0,0,//绿 15
1,1,0,0, //16
0,1,0,0,//绿 //17
0.5f,0,0.5f,0, //18
0.5f,0.5f,0.5f,0, //19
0,1,1,0, //20
0,1,0,0,//绿 21
0.5f,0.5f,0.5f,0, //22
0,1,0,0,//绿 23
0.5f,0,0.5f,0, //24
1,0,0,0, //25
1,1,1,0,//白色 26
0,1,1,0, //27
1,0,0,0, //28
0,1,1,0, //29
0.5f,0.5f,0.5f,0, //30
1,0,0,0, //31
1,1,0,0, //32
0.5f,0,0.5f,0, //33
1,0,0,0, //34
0.5f,0,0.5f,0, //35
0.5f,0.5f,0.5f,0, //36*/
};
ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
cbb.order(ByteOrder.nativeOrder());//设置字节顺序为本地操作系统顺序
mColorBuffer = cbb.asFloatBuffer();//转换为浮点(Float)型缓冲
mColorBuffer.put(colors);//在缓冲区内写入数据
mColorBuffer.position(0);//设置缓冲区起始位置
}
//初始化着色器的方法
@SuppressLint("NewApi")
public void initShader(MyTDView mv)
{
//加载顶点着色器的脚本内容
mVertexShader=ShaderUtil.loadFromAssetsFile("vertex.sh", mv.getResources());
//加载片元着色器的脚本内容
mFragmentShader=ShaderUtil.loadFromAssetsFile("frag.sh", mv.getResources());
//基于顶点着色器与片元着色器创建程序
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
//获取程序中顶点位置属性引用
maPositionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition");
//获取程序中顶点颜色属性引用
maColorHandle= GLES30.glGetAttribLocation(mProgram, "aColor");
//获取程序中总变换矩阵引用
muMVPMatrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");
}
@SuppressLint("NewApi")
public void drawSelf()
{
//指定使用某套shader程序
GLES30.glUseProgram(mProgram);
//初始化变换矩阵
Matrix.setRotateM(mMMatrix,0,0,0,1,0);
//设置沿Z轴正向位移1
Matrix.translateM(mMMatrix,0,0,0,1);
//设置绕x轴旋转
Matrix.rotateM(mMMatrix,0,xAngle,1,0,0);
//将变换矩阵传入渲染管线
GLES30.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, Triangle.getFianlMatrix(mMMatrix), 0);
//将顶点位置数据传送进渲染管线
GLES30.glVertexAttribPointer(
maPositionHandle,
3,
GLES30.GL_FLOAT,
false,
3*4,
mVertexBuffer
);
//将顶点颜色数据传送进渲染管线
GLES30.glVertexAttribPointer
(
maColorHandle,
4,
GLES30.GL_FLOAT,
false,
4*4,
mColorBuffer
);
GLES30.glEnableVertexAttribArray(maPositionHandle);//启用顶点位置数据
GLES30.glEnableVertexAttribArray(maColorHandle);//启用顶点着色数据
//绘制三角形
GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, vCount);
}
public static float[] getFianlMatrix(float[] spec)
{
mMVPMatrix=new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}
}
MyTDView类
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
public class MyTDView extends GLSurfaceView
{
final float ANGLE_SPAN = 0.375f;
RotateThread rthread;
SceneRenderer mRenderer;//自定义渲染器的引用
public MyTDView(Context context)
{
super(context);
this.setEGLContextClientVersion(3);
mRenderer=new SceneRenderer();
this.setRenderer(mRenderer);
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
private class SceneRenderer implements GLSurfaceView.Renderer
{
Triangle tle;
public void onDrawFrame(GL10 gl)
{
//清除深度缓冲与颜色缓冲
GLES30.glClear( GLES30.GL_DEPTH_BUFFER_BIT | GLES30.GL_COLOR_BUFFER_BIT);
//绘制三角形对象
tle.drawSelf();
}
public void onSurfaceChanged(GL10 gl, int width, int height)
{
//设置视窗大小及位置
GLES30.glViewport(0, 0, width, height);
//计算GLSurfaceView的宽高比
float ratio = (float) width / height;
//调用此方法计算产生透视投影矩阵
Matrix.frustumM(Triangle.mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
//调用此方法产生摄像机9参数位置矩阵
Matrix.setLookAtM(Triangle.mVMatrix, 0, 0,0,3,0f,0f,0f,0f,1.0f,0.0f);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
//设置屏幕背景色RGBA
GLES30.glClearColor(0,0,0,1.0f);
//创建三角形对对象
tle=new Triangle(MyTDView.this);
//打开深度检测
GLES30.glEnable(GLES30.GL_DEPTH_TEST);
rthread=new RotateThread();
rthread.start();
}
}
public class RotateThread extends Thread//自定义的内部类线程
{
public boolean flag=true;
@Override
public void run()//重写的run方法
{
while(flag)
{
mRenderer.tle.xAngle=mRenderer.tle.xAngle+ANGLE_SPAN;
try
{
Thread.sleep(20);
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
}
}
ShaderUtil 类
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.opengl.GLES30;
import android.util.Log;
//加载顶点Shader与片元Shader的工具类
public class ShaderUtil
{
//加载制定shader的方法
public static int loadShader
(
int shaderType, //shader的类型 GLES30.GL_VERTEX_SHADER(顶点) GLES30.GL_FRAGMENT_SHADER(片元)
String source //shader的脚本字符串
)
{
//创建一个新shader
int shader = GLES30.glCreateShader(shaderType);
//若创建成功则加载shader
if (shader != 0)
{
//加载shader的源代码
GLES30.glShaderSource(shader, source);
//编译shader
GLES30.glCompileShader(shader);
//存放编译成功shader数量的数组
int[] compiled = new int[1];
//获取Shader的编译情况
GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0)
{//若编译失败则显示错误日志并删除此shader
Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");
Log.e("ES20_ERROR", GLES30.glGetShaderInfoLog(shader));
GLES30.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
//创建shader程序的方法
public static int createProgram(String vertexSource, String fragmentSource)
{
//加载顶点着色器
int vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0)
{
return 0;
}
//加载片元着色器
int pixelShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0)
{
return 0;
}
//创建程序
int program = GLES30.glCreateProgram();
//若程序创建成功则向程序中加入顶点着色器与片元着色器
if (program != 0)
{
//向程序中加入顶点着色器
GLES30.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
//向程序中加入片元着色器
GLES30.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
//链接程序
GLES30.glLinkProgram(program);
//存放链接成功program数量的数组
int[] linkStatus = new int[1];
//获取program的链接情况
GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0);
//若链接失败则报错并删除程序
if (linkStatus[0] != GLES30.GL_TRUE)
{
Log.e("ES20_ERROR", "Could not link program: ");
Log.e("ES20_ERROR", GLES30.glGetProgramInfoLog(program));
GLES30.glDeleteProgram(program);
program = 0;
}
}
return program;
}
//检查每一步操作是否有错误的方法
@SuppressLint("NewApi")
public static void checkGlError(String op)
{
int error;
while ((error = GLES30.glGetError()) != GLES30.GL_NO_ERROR)
{
Log.e("ES20_ERROR", op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
//从sh脚本中加载shader内容的方法
public static String loadFromAssetsFile(String fname,Resources r)
{
String result=null;
try
{
InputStream in=r.getAssets().open(fname);
int ch=0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while((ch=in.read())!=-1)
{
baos.write(ch);
}
byte[] buff=baos.toByteArray();
baos.close();
in.close();
result=new String(buff,"UTF-8");
result=result.replaceAll("\\r\\n","\n");
}
catch(Exception e)
{
e.printStackTrace();
}
return result;
}
}
Activity类
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
public class Sample3_1Activity extends Activity//创建继承Activity的主控制类
{
MyTDView mview;//声明MyTDView类的引用
@Override
public void onCreate(Bundle savedInstanceState)//继承Activity后重写的方法
{
super.onCreate(savedInstanceState);//调用父类
//设置为竖屏模式
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mview=new MyTDView(this);//创建MyTDView类的对象
mview.requestFocus();//获取焦点
mview.setFocusableInTouchMode(true);//设置为可触控
setContentView(mview);
}
@Override
public void onResume()//继承Activity后重写的onResume方法
{
super.onResume();
mview.onResume();//通过MyTDView类的对象调用onResume方法
}
@Override
public void onPause()//继承Activity后重写的onPause方法
{
super.onPause();
mview.onPause();//通过MyTDView类的对象调用onPause方法
}
}