【Android应用开发】 OpenGL ES -- 透视投影 和 正交投影

时间:2023-03-09 15:37:41
【Android应用开发】 OpenGL ES  -- 透视投影 和 正交投影

博客地址 : http://blog.csdn.net/shulianghan/article/details/46680803

源代码下载 : http://download.csdn.net/detail/han1202012/8903437


正交投影效果 : 

【Android应用开发】 OpenGL ES  -- 透视投影 和 正交投影


透视投影效果 : 

【Android应用开发】 OpenGL ES  -- 透视投影 和 正交投影


一. 投影简单介绍

1. 摄像机位置

摄像机參数 :

-- 摄像机位置 : 摄像机的 三维坐标位置 x, y, z 坐标;

-- 观察方向 : 摄像机镜头的朝向, 是一个三维向量, 指向一个三维坐标方向;

-- up 方向 : 有了位置 和 朝向, 此时摄像机能够 360 度旋转, 这是我们须要一个 up 方向, 将摄像机固定在一个位置一个方向;

设置摄像机的方法 :

void android.opengl.Matrix.setLookAtM(float[] rm, int rmOffset, float eyeX, float eyeY, float eyeZ, float centerX, float centerY, float centerZ, float upX, float upY, float upZ)

-- float[] rm 參数 : 生成矩阵元素的 float[] 数组;

-- int rmOffset 參数 : 矩阵元素数组的起始偏移量;

-- float eyeX, float eyeY, float eyeZ 參数 : 摄像机位置的 x, y, z 三维坐标;

-- float centerX, float centerY, float centerZ 參数 : 摄像机镜头朝向的点, 该点与摄像机位置连线的方向就是摄像机方向;

-- float upX, float upY, float upZ 參数 : 摄像机 up 方向, 该点与摄像机连线的方向, 就是摄像机的 up 方向;

2. 正交投影简单介绍

投影简单介绍 :

-- 视景体 : 管线会确定的一个可视空间区域, 由 上平面(up), 下平面(down), 左平面(left), 右平面(right), 远平面(far), *面(near) 六个平面组成;

-- 视景体与投影 : 视景体内的物体会投影到*面, 视景体之外的内容会被裁减掉, 比如眼睛看不到的范围就是处于视景体外即被裁减掉的;

正交投影 : 正交投影属于平行投影, 投影线平行, 视景体是长方形的, 投影的内容不会出现近大远小的效果;

-- 投影线 : 物体顶点 与 *面的相应的物体顶点 投影的连线;

正交投影方法 : Matrix.orthoM() 方法设置正交投影;

void android.opengl.Matrix.orthoM(float[] m, int mOffset, float left, float right, float bottom, float top, float near, float far)

-- float[] m 參数 : 生成矩阵元素的 float[] 数组;

-- int mOffset 參数 : 矩阵数组的起始偏移量;

-- float left, float right, float bottom, float top 參数 : *面的 左, 右, 下, 上 的值;

-- float near 參数 : *面 与 视点之间的距离;

-- float far 參数 : 远平面 与 视点之间的距离;

视口 : 视景体中的物体投影到*面后, 终于会映射到显示屏的视口中, 视口就相当于眼睛 或者 手机屏幕的一部分;

-- 说明 : 视口并非占手机所有屏幕, 是显示投影的部分, 也能够是一个 View 组件;

视口设置方法 :

 void android.opengl.GLES20.glViewport(int x, int y, int width, int height)

-- int x, int y 參数 : x, y 是视口在手机屏幕左上角的坐标;

-- int width, int height 參数 : 视口的宽度 与 高度;

3. 透视投影简单介绍

透视投影 : 与现实世界观察物体一样, 有 近大远小 的效果, 这样的投影更加真实;

-- 投影线介绍 : 透视投影的投影线不平行, 相交于视点;

-- 视景体 : 透视投影中视景体是锥台形区域;

-- 用处 : 所有的 3D 游戏都採用了透视投影的效果, 我们控制物体向前行走, 远处的物体不断变大就是这样的效果;

二. 正交透视投影源代码具体解释

1. 源代码结构具体解释

源代码组成 :

-- MatrixState : 矩阵相关的辅助类;

-- OrthogonalProjectionActivity : 显示具体 OpenGL 图像的 Activity;

-- ProjectionGLSurfaceView : 自己定义的 GLSurfaceView, 该 View 能够显示 OpenGL 图像内容;

-- ShaderUtil : 着色器工具类;

-- SixPointedStar : 具体的图形类, 怎样生成该图形;

【Android应用开发】 OpenGL ES  -- 透视投影 和 正交投影

2. MatrixState 具体解释

(1) 设置摄像机參数

设置摄像机參数 :

-- 相关内容 :

	/**
* 设置摄像机的參数
*
* @param cx
* 摄像机位置的 x 坐标
* @param cy
* 摄像机位置的 y 坐标
* @param cz
* 摄像机位置的 z 坐标
* @param tx
* 摄像机朝向 x 坐标
* @param ty
* 摄像机朝向 y 坐标
* @param tz
* 摄像机朝向 z 坐标
* @param upx
* 摄像机上方朝向 x 坐标
* @param upy
* 摄像机上方朝向 y 坐标
* @param upz
* 摄像机上方朝向 z 坐标
*/
public static void setCamera(float cx, float cy, float cz, float tx,
float ty, float tz, float upx, float upy, float upz) {
Matrix.setLookAtM(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);
}

-- 方法作用 : 设置了摄像机的相关參数;

(2) 设置正交投影參数

代码解析 :

-- 作用 : 设置正交投影的*面相关信息, *面与远平面距离;

-- 代码相关内容 :

	/**
* 设置正交投影的參数
*
* @param left
* *面的 left
* @param right
* *面的 right
* @param bottom
* *面的 bottom
* @param top
* *面的 top
* @param near
* *面的距离
* @param far
* 远平面的距离
*/
public static void setProjectOrtho(float left, float right, float bottom,
float top, float near, float far) {
Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);
}

(3) 设置透视投影參数

代码具体解释 :

-- 作用 : 设置透视投影 *面 以及*面 远平面与视点间的距离;

-- 代码内容 :

	/**
* 设置正交投影的參数
*
* @param left
* *面的 left
* @param right
* *面的 right
* @param bottom
* *面的 bottom
* @param top
* *面的 top
* @param near
* *面的距离
* @param far
* 远平面的距离
*/
public static void setProjectOrtho(float left, float right, float bottom,
float top, float near, float far) {
Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);
}

(4) 获取物体的总变换矩阵

代码具体解释 :

-- 作用 : 将摄像机矩阵, 投影矩阵, 着色矩阵相乘, 就是终于矩阵;

-- 代码内容 :

	/**
* 获取物体的总变换矩阵
*
* @param spec
* @return
*/
public static float[] getFinalMatrix(float[] spec) {
mMVPMatrix = new float[16];
/*
* 矩阵乘法计算, 将两个矩阵相乘, 并存入到第三个矩阵中
* 六个參数 :
* ①② 參数 : 结果矩阵, 结果矩阵起始位移
* ③④ 參数 : 左矩阵, 结果矩阵起始位移
* ⑤⑥ 參数 : 右矩阵, 结果矩阵起始位移
*/
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}

(5) MatrixState 源代码

源代码内容 :

package cn.org.octopus.opengl.projection;

import android.opengl.Matrix;

/**
* 存储矩阵状态的类
*
* @author octopus
*
*/
public class MatrixState { private static float[] mProjMatrix = new float[16]; // 4x4矩阵 投影用
private static float[] mVMatrix = new float[16]; // 摄像机位置朝向9參数矩阵
private static float[] mMVPMatrix; // 最后起作用的总变换矩阵 /**
* 设置摄像机的參数
*
* @param cx
* 摄像机位置的 x 坐标
* @param cy
* 摄像机位置的 y 坐标
* @param cz
* 摄像机位置的 z 坐标
* @param tx
* 摄像机朝向 x 坐标
* @param ty
* 摄像机朝向 y 坐标
* @param tz
* 摄像机朝向 z 坐标
* @param upx
* 摄像机上方朝向 x 坐标
* @param upy
* 摄像机上方朝向 y 坐标
* @param upz
* 摄像机上方朝向 z 坐标
*/
public static void setCamera(float cx, float cy, float cz, float tx,
float ty, float tz, float upx, float upy, float upz) {
Matrix.setLookAtM(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);
} /**
* 设置透视投影參数
*
* @param left
* *面的 left
* @param right
* *面的 right
* @param bottom
* *面的 bottom
* @param top
* *面的 top
* @param near
* *面与视点的距离
* @param far
* 远平面与视点的距离
*/
public static void setProjectFrustum(float left, float right, float bottom,
float top, float near, float far) {
Matrix.frustumM(mProjMatrix, 0, left, right, bottom, top, near, far);
} /**
* 设置正交投影的參数
*
* @param left
* *面的 left
* @param right
* *面的 right
* @param bottom
* *面的 bottom
* @param top
* *面的 top
* @param near
* *面的距离
* @param far
* 远平面的距离
*/
public static void setProjectOrtho(float left, float right, float bottom,
float top, float near, float far) {
Matrix.orthoM(mProjMatrix, 0, left, right, bottom, top, near, far);
} /**
* 获取物体的总变换矩阵
*
* @param spec
* @return
*/
public static float[] getFinalMatrix(float[] spec) {
mMVPMatrix = new float[16];
/*
* 矩阵乘法计算, 将两个矩阵相乘, 并存入到第三个矩阵中
* 六个參数 :
* ①② 參数 : 结果矩阵, 结果矩阵起始位移
* ③④ 參数 : 左矩阵, 结果矩阵起始位移
* ⑤⑥ 參数 : 右矩阵, 结果矩阵起始位移
*/
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, spec, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}
}

3. ShaderUtil 着色工具具体解释

该代码在 http://blog.csdn.net/shulianghan/article/details/17020359 中具体的解说;

(1) 源代码

ShaderUtil 源代码 :

package cn.org.octopus.opengl.projection;

import java.io.ByteArrayOutputStream;
import java.io.InputStream; import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log; /*
* 这个工具类用来载入定点着色器与片元着色器
*/
public class ShaderUtil { /**
* 载入着色器方法
*
* 流程 :
*
* ① 创建着色器
* ② 载入着色器脚本
* ③ 编译着色器
* ④ 获取着色器编译结果
*
* @param shaderType 着色器类型,顶点着色器(GLES20.GL_FRAGMENT_SHADER), 片元着色器(GLES20.GL_FRAGMENT_SHADER)
* @param source 着色脚本字符串
* @return 返回的是着色器的引用, 返回值能够代表载入的着色器
*/
public static int loadShader(int shaderType , String source){
//1.创建一个着色器, 并记录所创建的着色器的id, 假设id==0, 那么创建失败
int shader = GLES20.glCreateShader(shaderType);
if(shader != 0){
//2.假设着色器创建成功, 为创建的着色器载入脚本代码
GLES20.glShaderSource(shader, source);
//3.编译已经载入脚本代码的着色器
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
//4.获取着色器的编译情况, 假设结果为0, 说明编译失败
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if(compiled[0] == 0){
Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");
Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
//编译失败的话, 删除着色器, 并显示log
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
} /**
* 检查每一步的操作是否正确
*
* 使用GLES20.glGetError()方法能够获取错误代码, 假设错误代码为0, 那么就没有错误
*
* @param op 具体运行的方法名, 比方运行向着色程序中增加着色器,
* 使glAttachShader()方法, 那么这个參数就是"glAttachShader"
*/
public static void checkGLError(String op){
int error;
//错误代码不为0, 就打印错误日志, 并抛出异常
while( (error = GLES20.glGetError()) != GLES20.GL_NO_ERROR ){
Log.e("ES20_ERROR", op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
} /**
* 创建着色程序
*
* ① 载入顶点着色器
* ② 载入片元着色器
* ③ 创建着色程序
* ④ 向着色程序中增加顶点着色器
* ⑤ 向着色程序中增加片元着色器
* ⑥ 链接程序
* ⑦ 获取链接程序结果
*
* @param vertexSource 定点着色器脚本字符串
* @param fragmentSource 片元着色器脚本字符串
* @return
*/
public static int createProgram(String vertexSource , String fragmentSource){
//1. 载入顶点着色器, 返回0说明载入失败
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if(vertexShader == 0)
return 0;
//2. 载入片元着色器, 返回0说明载入失败
int fragShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if(fragShader == 0)
return 0;
//3. 创建着色程序, 返回0说明创建失败
int program = GLES20.glCreateProgram();
if(program != 0){
//4. 向着色程序中增加顶点着色器
GLES20.glAttachShader(program, vertexShader);
checkGLError("glAttachShader");
//5. 向着色程序中增加片元着色器
GLES20.glAttachShader(program, fragShader);
checkGLError("glAttachShader"); //6. 链接程序
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
//获取链接程序结果
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if(linkStatus[0] != GLES20.GL_TRUE){
Log.e("ES20.ERROR", "链接程序失败 : ");
Log.e("ES20.ERROR", GLES20.glGetProgramInfoLog(program));
//假设链接程序失败删除程序
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
} /**
* 从assets中载入着色脚本, 终于获得一个着色器脚本字符串
*
* ① 打开assets文件夹中的文件输入流
* ② 创建带缓冲区的输出流
* ③ 逐个字节读取文件数据, 放入缓冲区
* ④ 将缓冲区中的数据转为字符串
*
* @param fileName assets文件夹中的着色脚本文件名称
* @param resources 应用的资源
* @return
*/
public static String loadFromAssetsFile(String fileName, Resources resources){
String result = null;
try {
//1. 打开assets文件夹中读取文件的输入流, 相当于创建了一个文件的字节输入流
InputStream is = resources.getAssets().open(fileName);
int ch = 0;
//2. 创建一个带缓冲区的输出流, 每次读取一个字节, 注意这里字节读取用的是int类型
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//3. 逐个字节读取数据, 并将读取的数据放入缓冲器中
while((ch = is.read()) != -1){
baos.write(ch);
}
//4. 将缓冲区中的数据转为字节数组, 并将字节数组转换为字符串
byte[] buffer = baos.toByteArray();
baos.close();
is.close();
result = new String(buffer, "UTF-8");
result = result.replaceAll("\\r\\n", "\n");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}

4. SixPointedStar 六角形形成类

(1) 源代码

package cn.org.octopus.opengl.projection;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List; import android.opengl.GLES20;
import android.opengl.Matrix; /**
* 单个六角星元素
*
* @author octopus
*
*/
public class SixPointedStar {
int mProgram; // 自己定义渲染管线着色器程序id static float[] mMMatrix = new float[16]; // 具体物体的3D变换矩阵,包含旋转、平移、缩放
int muMVPMatrixHandle; // 总变换矩阵引用 int maPositionHandle; // 顶点位置属性引用
int maColorHandle; // 顶点颜色属性引用 String mVertexShader; // 顶点着色器代码脚本
String mFragmentShader; // 片元着色器代码脚本 FloatBuffer mVertexBuffer; // 顶点坐标数据缓冲
FloatBuffer mColorBuffer; // 顶点着色数据缓冲 int vCount = 0; // 顶点个数
public float yAngle = 0; // 绕y轴旋转的角度
public float xAngle = 0; // 绕z轴旋转的角度
final float UNIT_SIZE = 1; public SixPointedStar(ProjectionGLSurfaceView mv, float r, float R, float z) {
// 调用初始化顶点数据的initVertexData方法
initVertexData(R, r, z);
// 调用初始化着色器的intShader方法
initShader(mv);
} /**
* 自己定义初始化顶点数据的initVertexData方法
* @param R 外圆半径, 最外面6个点组成的圆
* @param r 内圆半径, 最里面6个点组成的圆, 6个凹槽处的点
* @param z 深度
*/
public void initVertexData(float R, float r, float z) {
List<Float> flist = new ArrayList<Float>();
float tempAngle = 360 / 6;
// 每 60 度绘制一个四边形, 每一个四边形由 2 个三角形组成, 箭头形的平行四边形
for (float angle = 0; angle < 360; angle += tempAngle) {
// 第一个三角形, (angle = 60度时, 这是处于 60 ~ 90度的三角形)
// 第一个中心点, 正中心的点
flist.add(0f); //屏幕中心
flist.add(0f); //屏幕中心
flist.add(z); //深度, z轴, 垂直于屏幕
// 第二个点, (angle = 60度时 第一象限 60度 右上的点)
flist.add((float) (R * UNIT_SIZE * Math.cos(Math.toRadians(angle)))); // 公式 : R / x = cos60, x = R * cos60
flist.add((float) (R * UNIT_SIZE * Math.sin(Math.toRadians(angle)))); // 公式 : R / y = cos60, y = R * sin60
flist.add(z); //深度
// 第三个点, 顺时针方向的三角形的还有一个点
flist.add((float) (r * UNIT_SIZE * Math.cos(Math.toRadians(angle
+ tempAngle / 2))));
flist.add((float) (r * UNIT_SIZE * Math.sin(Math.toRadians(angle
+ tempAngle / 2))));
flist.add(z); // 第二个三角形
// 第一个中心点, 最中心的点
flist.add(0f);
flist.add(0f);
flist.add(z);
// 第二个点, (angle = 60度时, 这是处于 90 ~ 120 的三角形)
flist.add((float) (r * UNIT_SIZE * Math.cos(Math.toRadians(angle
+ tempAngle / 2))));
flist.add((float) (r * UNIT_SIZE * Math.sin(Math.toRadians(angle
+ tempAngle / 2))));
flist.add(z);
// 第三个点
flist.add((float) (R * UNIT_SIZE * Math.cos(Math.toRadians(angle
+ tempAngle))));
flist.add((float) (R * UNIT_SIZE * Math.sin(Math.toRadians(angle
+ tempAngle))));
flist.add(z);
} //顶点个数, 集合个数 / 3
vCount = flist.size() / 3;
//创建一个顶点数组, 大小为顶点集合的大小, 将顶点数组的元素复制到顶点集合中
float[] vertexArray = new float[flist.size()];
for (int i = 0; i < vCount; i++) {
vertexArray[i * 3] = flist.get(i * 3);
vertexArray[i * 3 + 1] = flist.get(i * 3 + 1);
vertexArray[i * 3 + 2] = flist.get(i * 3 + 2);
} //创建一个字节数组缓冲, 大小为 顶点个数 * 4
ByteBuffer vbb = ByteBuffer.allocateDirect(vertexArray.length * 4);
// 设置字节顺序为本地操作系统顺序
vbb.order(ByteOrder.nativeOrder());
//将 byte 缓冲 转为 float 缓冲, 赋值给 顶点数据缓冲
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertexArray);
//设置缓冲区的起始位置
mVertexBuffer.position(0); /*
* 以下是初始化顶点颜色数据
*/ //共同拥有 vCount 个顶点, 每一个顶点颜色值是 4个各自是 RGBA
float[] colorArray = new float[vCount * 4];
//中心点设置一个颜色, 其他点设置一个颜色
for (int i = 0; i < vCount; i++) {
if (i % 3 == 0) {// 中心点为白色
colorArray[i * 4] = 1;
colorArray[i * 4 + 1] = 1;
colorArray[i * 4 + 2] = 1;
colorArray[i * 4 + 3] = 0;
} else {// 边上的点为淡蓝色
colorArray[i * 4] = 0.45f;
colorArray[i * 4 + 1] = 0.75f;
colorArray[i * 4 + 2] = 0.75f;
colorArray[i * 4 + 3] = 0;
}
} ByteBuffer cbb = ByteBuffer.allocateDirect(colorArray.length * 4);
cbb.order(ByteOrder.nativeOrder()); // 设置字节顺序为本地操作系统顺序
//将颜色Byte缓冲转为 Float缓冲
mColorBuffer = cbb.asFloatBuffer();
//将颜色缓冲数据放入 颜色数据缓冲成员变量中
mColorBuffer.put(colorArray);
mColorBuffer.position(0);
// 特别提示:因为不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
// 转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题 } /**
* 初始化着色器
* ① 载入顶点着色器与片元着色器脚本
* ② 基于载入的着色器创建着色程序
* ③ 依据着色程序获取 顶点属性引用 顶点颜色引用 总变换矩阵引用
* @param mv
*/
public void initShader(ProjectionGLSurfaceView mv) {
/*
* mVertextShader是顶点着色器脚本代码
* 调用工具类方法获取着色器脚本代码, 着色器脚本代码放在assets文件夹中
* 传入的两个參数是 脚本名称 和 应用的资源
* 应用资源Resources就是res文件夹下的那写文件
*/ //① 载入顶点着色器的脚本内容
mVertexShader = ShaderUtil.loadFromAssetsFile("vertex_projection.sh",
mv.getResources());
//② 载入片元着色器的脚本内容
mFragmentShader = ShaderUtil.loadFromAssetsFile("frag_projection.sh",
mv.getResources());
//③ 基于顶点着色器与片元着色器创建程序, 传入顶点着色器脚本 和 片元着色器脚本 注意顺序不要错
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader); /*
* 从着色程序中获取 属性变量 顶点坐标(颜色)数据的引用
* 当中的"aPosition"是顶点着色器中的顶点位置信息
* 当中的"aColor"是顶点着色器的颜色信息
*/ //④ 获取程序中顶点位置属性引用id
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
//⑤ 获取程序中顶点颜色属性引用id
maColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");
//⑥ 获取程序中总变换矩阵引用id
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
} /**
* 六角星绘制自身方法
*
* ① 设置绘制使用的着色程序
* ② 初始化总变换矩阵
* ③ 设置位移
* ④ 设置旋转
* ⑤ 应用终于变换矩阵
* ⑥ 指定顶点与颜色位置缓冲数据
* ⑦ 開始绘制
*/
public void drawSelf() {
// 制定使用某套shader程序
GLES20.glUseProgram(mProgram);
// 初始化变换矩阵, 第二參数是矩阵起始位, 第三參数 旋转的角度, 四五六參数 旋转的轴
Matrix.setRotateM(mMMatrix, 0, 0, 0, 1, 0);
// 设置沿Z轴正向位移1
Matrix.translateM(mMMatrix, 0, 0, 0, 1);
// 设置绕y轴旋转
Matrix.rotateM(mMMatrix, 0, yAngle, 0, 1, 0);
// 设置绕z轴旋转
Matrix.rotateM(mMMatrix, 0, xAngle, 1, 0, 0);
// 将终于变换矩阵传入shader程序
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false,
MatrixState.getFinalMatrix(mMMatrix), 0);
// 为画笔指定顶点位置数据
GLES20.glVertexAttribPointer(maPositionHandle, // 顶点位置数据引用
3, // 每 3 个元素代表一个坐标
GLES20.GL_FLOAT, // 坐标的单位是浮点型
false, //
3 * 4, // 每组数据有多少字节
mVertexBuffer); // 顶点数据缓冲区
// 为画笔指定顶点着色数据
GLES20.glVertexAttribPointer(maColorHandle, 4, GLES20.GL_FLOAT, false,
4 * 4, mColorBuffer);
// 同意顶点位置数据数组
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glEnableVertexAttribArray(maColorHandle);
// 绘制六角星
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
}
}

5. ProjectionGLSurfaceView 自己定义View显示类

(1) 正交透视投影设置

关键成员变量 :

public static boolean isOrth

-- 正交投影 : 设置为 true, 时为正交投影;

-- 透视投影 : 设置为 false 时, 为透视投影;

(3) 源代码

源代码 :

package cn.org.octopus.opengl.projection;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10; import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent; /**
* 自己定义显示 OpenGL 图形的 SurfaceView
*
* ① 初始化 SurfaceView
* a. 设置 OpenGL ES 版本号
* b. 创建场景渲染器
* c. 设置场景渲染器
* d. 设置场景渲染器模式
* ② 自己定义场景渲染器
* a. 创建时 设置背景 -> 创建绘制元素 -> 打开深度检測
* b. 场景改变时 设置视口參数 -> 设置投影參数 -> 设置摄像机參数
* c. 绘制时 清楚颜色,深度缓冲 -> 绘制元素
* @author octopus
*
*/
public class ProjectionGLSurfaceView extends GLSurfaceView {
public static boolean isOrth = true; private final float TOUCH_SCALE_FACTOR = 180.0f / 320; // 角度缩放比例
private SceneRenderer mRenderer; // 场景渲染器 private float mPreviousY; //上次触摸位置的Y坐标
private float mPreviousX; //上次触摸位置的X坐标 /**
* 初始化 GLSurfaceView
* ① 设置 OpenGL ES 的版本号
* ② 创建场景渲染器
* ③ 设置场景渲染器
* ④ 设置场景渲染模式
* @param context
*/
public ProjectionGLSurfaceView(Context context) {
super(context);
this.setEGLContextClientVersion(2); // 设置OpenGL ES 版本号为 2.0
mRenderer = new SceneRenderer(); // 创建场景渲染器
setRenderer(mRenderer); // 设置场景渲染器
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); // 设置场景渲染模式
} // 触摸方法
@Override
public boolean onTouchEvent(MotionEvent e) {
float y = e.getY(); //获取当前触摸的 y 坐标
float x = e.getX(); //获取当前触摸的 x 坐标
switch (e.getAction()) { //获取触摸类型
case MotionEvent.ACTION_MOVE:
float dy = y - mPreviousY;// 计算 y 方向的位移
float dx = x - mPreviousX;// 计算 x 方向的位移
for (SixPointedStar h : mRenderer.ha) {
h.yAngle += dx * TOUCH_SCALE_FACTOR;// 设置六角星绕 x 轴旋转角度
h.xAngle += dy * TOUCH_SCALE_FACTOR;// 设置六角星绕 y 轴旋转角度
}
}
mPreviousY = y;// 将本次触摸的 y 坐标记录为历史坐标
mPreviousX = x;// 将本次触摸的 x 坐标记录为历史坐标
return true;
} /**
* 场景渲染器
* 创建六角星数组中得六角星对象, 将六角星显示在屏幕中
* @author octopus
*
*/
private class SceneRenderer implements GLSurfaceView.Renderer {
SixPointedStar[] ha = new SixPointedStar[6];// 六角星数组 /**
* ① 清楚深度缓冲 与 颜色缓冲
* ② 又一次绘制各个元素
*/
public void onDrawFrame(GL10 gl) {
// 清除深度缓冲与颜色缓冲
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT
| GLES20.GL_COLOR_BUFFER_BIT);
// 循环绘制各个六角星
for (SixPointedStar h : ha) {
h.drawSelf();
}
} /**
* Surface 改变时
* ① 设置视口參数
* ② 设置投影參数
* ③ 设置摄像机參数
*/
public void onSurfaceChanged(GL10 gl, int width, int height) {
// 设置视口的大小及位置
GLES20.glViewport(0, 0, width, height);
// 设置视口的宽高比, 注意视口的长宽比与*面的长宽比须要同样, 否则显示内容会变形
float ratio = (float) width / height;
// 设置正交投影, 假设是透视投影, 就在这里使用透视投影
if(isOrth){
//设置正交投影
MatrixState.setProjectOrtho(-ratio, ratio, -1, 1, 1, 10);
}else{
//设置透视投影
MatrixState.setProjectFrustum(-ratio*0.4f, ratio*0.4f, -1*0.4f, 1*0.4f, 1, 50);
} // 设置摄像机位置
MatrixState.setCamera(0, 0, 3f, 0, 0, 0f, 0f, 1.0f, 0.0f);
} /**
* 创建时回调
* ① 设置北京颜色
* ② 创建绘制元素
* ③ 打开深度检測
*/
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 设置屏幕的背景颜色
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); float distance = 0f;
if(isOrth){
distance = -1.0f;
}else{
distance = -1.0f;
} // 创建六角星数组中得各个六角星
for (int i = 0; i < ha.length; i++) {
ha[i] = new SixPointedStar(ProjectionGLSurfaceView.this, 0.2f, 0.5f,
distance * i);
}
// 打开深度检測
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
}
}
}

6. OrthogonalProjectionActivity 类

源代码 :

package cn.org.octopus.opengl.projection;
import cn.org.octopus.opengl.R;
import cn.org.octopus.opengl.utils.DLog;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout; /**
* Activity 显示 OpenGL 流程
* ① 设置屏幕參数
* ② 初始化 GLSurfaceView
* ③ 设置显示 GLSurface
*
* 在onResume 和 onPause 中分别调用 GLSurfaceView 的 onResume 和 onPause 方法
* @author octopus
*
*/
public class OrthogonalProjectionActivity extends Activity { public static final String TAG = "octopus.OrthogonalProjectionActivity"; private ProjectionGLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//① 设置屏幕參数
requestWindowFeature(Window.FEATURE_NO_TITLE); //设置无标题
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN , //设置全屏充满
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); //设置屏幕为竖屏 //② 初始化GLSurfaceView
mGLSurfaceView = new ProjectionGLSurfaceView(this); //③ 设置显示 GLSurfaceView
setContentView(mGLSurfaceView); //设置界面显示该 GLSurfaceView
mGLSurfaceView.requestFocus(); //获取焦点
mGLSurfaceView.setFocusableInTouchMode(true); //设置为可触控 } public void onClick(View view) {
DLog.i(TAG, "点击了button");
int id = view.getId();
switch (id) {
case R.id.bt_switch_orth:
ProjectionGLSurfaceView.isOrth = true;
break;
case R.id.bt_switch_flu:
ProjectionGLSurfaceView.isOrth = false;
break; default:
break;
}
} @Override
protected void onResume() {
super.onResume();
mGLSurfaceView.onResume(); // GLSurfaceView 依据 Acivity 周期变化
} @Override
protected void onPause() {
super.onPause();
mGLSurfaceView.onPause(); // GLSurfaceView 依据 Acivity 周期变化
}
}

.

博客地址 : http://blog.csdn.net/shulianghan/article/details/46680803

源代码下载 : http://download.csdn.net/detail/han1202012/8903437