本文载自:http://blog.csdn.net/shulianghan/article/details/46680803
正交投影效果 :
透视投影效果 :
摄像机参数 :
摄像机位置 : 摄像机的 三维坐标位置 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 方向;
投影简介 :
视景体 : 管线会确定的一个可视空间区域, 由 上平面(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 参数 : 视口的宽度 与 高度
透视投影 : 与现实世界观察物体一样, 有 近大远小 的效果, 这种投影更加真实;
投影线介绍 : 透视投影的投影线不平行, 相交于视点
视景体 : 透视投影中视景体是锥台形区域
用处 : 所有的 3D 游戏都采用了透视投影的效果, 我们控制物体向前行走, 远处的物体不断变大就是这种效果
源码结构详解
源码组成 :
MatrixState : 矩阵相关的辅助类;
ProjectionActivity : 显示具体 OpenGL 图像的 Activity;
ProjectionSurfaceView : 自定义的 GLSurfaceView, 该 View 可以显示 OpenGL 图像内容;
ShaderUtil : 着色器工具类;
SixPointedStar : 具体的图形类, 如何生成该图形;
MatrixState 源码:
package com.opengl.app;
import android.opengl.Matrix;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
//存储系统矩阵状态的类
public class MatrixState {
private static float[] mProjMatrix = new float[16];//4x4矩阵 投影用
private static float[] mVMatrix = new float[16];//摄像机位置朝向9参数矩阵
private static float[] mMVPMatrix;//最后起作用的总变换矩阵
private static float[] currMatrix; //当前变换矩阵
static float[][] mStack = new float[10][16]; //用于保存变换矩阵的类
static int stackTop = -1; //标识栈顶的索引
public static float[] lightLocation=new float[]{0,0,0};//定位光光源位置
public static FloatBuffer lightPositionFB;
public static FloatBuffer cameraFB;
/**
* 初始化变换矩阵
*/
public static void setInitStack() {
currMatrix = new float[16];
Matrix.setRotateM(currMatrix, 0, 0, 1, 0, 0); //除初始化无变换内容的矩阵
}
/**
* 把变换矩阵保存到栈中
*/
public static void pushMatrix() {
stackTop++;
for (int i = 0; i < 16; i++) {
mStack[stackTop][i] = currMatrix[i];
}
}
/**
* 从栈中读取变换矩阵
*/
public static void popMatrix() {
for (int i = 0; i < 16; i++) {
currMatrix[i] = mStack[stackTop][i];
}
stackTop--;
}
/**
* 平移变换
*/
public static void translate(float x, float y, float z) {
Matrix.translateM(currMatrix, 0, x, y, z);
}
/**
* 旋转变换
*
* @param angle
* @param x
* @param y
*/
public static void rotate(float angle, float x, float y, float z) {
Matrix.rotateM(currMatrix, 0, angle, x, y, z);
}
/**
* 缩放变换
*/
public static void scale(float x, float y, float z) {
Matrix.scaleM(currMatrix, 0, x, y, z);
}
//设置摄像机
static ByteBuffer llbb= ByteBuffer.allocateDirect(3*4);
static float[] cameraLocation=new float[3];//摄像机位置
/**
* 设置摄像机
*
* @param cx 摄像机位置x
* @param cy 摄像机位置y
* @param cz 摄像机位置z
* @param tx 摄像机目标点x
* @param ty 摄像机目标点y
* @param tz 摄像机目标点z
* @param upx 摄像机UP向量X分量
* @param upy 摄像机UP向量Y分量
* @param upz 摄像机UP向量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);
cameraLocation[0]=cx;
cameraLocation[1]=cy;
cameraLocation[2]=cz;
llbb.clear();
llbb.order(ByteOrder.nativeOrder());//设置字节顺序
cameraFB=llbb.asFloatBuffer();
cameraFB.put(cameraLocation);
cameraFB.position(0);
}
/**
* 设置正交投影参数
*
* @param left near面的left
* @param right near面的right
* @param bottom near面的bottom
* @param top near面的top
* @param near near面距离
* @param far 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 left near面的left
* @param right near面的right
* @param bottom near面的bottom
* @param top near面的top
* @param near near面距离
* @param far 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 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;
}
/**
* 获取具体物体的变换之后的矩阵
*
* @return
*/
public static float[] getFinalMatrix() {
mMVPMatrix = new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, currMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
return mMVPMatrix;
}
//设置灯光位置的方法
static ByteBuffer llbbL = ByteBuffer.allocateDirect(3 * 4);
public static void setLightLocation(float x,float y,float z)
{
llbbL.clear();
lightLocation[0]=x;
lightLocation[1]=y;
lightLocation[2]=z;
llbbL.order(ByteOrder.nativeOrder());//设置字节顺序
lightPositionFB=llbbL.asFloatBuffer();
lightPositionFB.put(lightLocation);
lightPositionFB.position(0);
}
//获取具体物体的变换矩阵
public static float[] getMMatrix()
{
return currMatrix;
}
}
ShaderUtil 源码:
package com.opengl.app;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import android.content.res.Resources;
import android.opengl.GLES20;
import android.util.Log;
//加载顶点Shader与片元Shader的工具类
public class ShaderUtil {
//加载制定shader的方法
public static int loadShader
(
int shaderType, //shader的类型 GLES20.GL_VERTEX_SHADER GLES20.GL_FRAGMENT_SHADER
String source //shader的脚本字符串
) {
//创建一个新shader
int shader = GLES20.glCreateShader(shaderType);
//若创建成功则加载shader
if (shader != 0) {
//加载shader的源代码
GLES20.glShaderSource(shader, source);
//编译shader
GLES20.glCompileShader(shader);
//存放编译成功shader数量的数组
int[] compiled = new int[1];
//获取Shader的编译情况
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {//若编译失败则显示错误日志并删除此shader
Log.e("ES20_ERROR", "Could not compile shader " + shaderType + ":");
Log.e("ES20_ERROR", GLES20.glGetShaderInfoLog(shader));
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
//创建shader程序的方法
public static int createProgram(String vertexSource, String fragmentSource) {
//加载顶点着色器
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return 0;
}
//加载片元着色器
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0) {
return 0;
}
//创建程序
int program = GLES20.glCreateProgram();
//若程序创建成功则向程序中加入顶点着色器与片元着色器
if (program != 0) {
//向程序中加入顶点着色器
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
//向程序中加入片元着色器
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
//链接程序
GLES20.glLinkProgram(program);
//存放链接成功program数量的数组
int[] linkStatus = new int[1];
//获取program的链接情况
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
//若链接失败则报错并删除程序
if (linkStatus[0] != GLES20.GL_TRUE) {
Log.e("ES20_ERROR", "Could not link program: ");
Log.e("ES20_ERROR", GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
//检查每一步操作是否有错误的方法
public static void checkGlError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.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;
}
}
ProjectionSurfaceView 源码:
package com.opengl.app.projection;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.opengl.GLES20;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import com.opengl.app.Constant;
import com.opengl.app.MatrixState;
import com.opengl.app.view.SixPointedStar;
public class ProjectionSurfaceView extends GLSurfaceView {
public static final String TAG = "Projection";
private final float TOUCH_SCALE_FACTOR = 180.0f / 320;//角度缩放比例
private SceneRenderer mRenderer;//场景渲染器
private float mPreviousY;//上次的触控位置Y坐标
private float mPreviousX;//上次的触控位置X坐标
public ProjectionSurfaceView(Context context) {
super(context);
this.setEGLContextClientVersion(2); //设置使用OPENGL ES2.0
mRenderer = new SceneRenderer(); //创建场景渲染器
setRenderer(mRenderer); //设置渲染器
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染
}
public ProjectionSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
this.setEGLContextClientVersion(2); //设置使用OPENGL ES2.0
mRenderer = new SceneRenderer(); //创建场景渲染器
setRenderer(mRenderer); //设置渲染器
setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);//设置渲染模式为主动渲染
}
//触摸事件回调方法
@Override
public boolean onTouchEvent(MotionEvent e) {
float y = e.getY();
float x = e.getX();
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;//设置六角星数组中的各个六角星绕y轴旋转角度
h.xAngle+= dy * TOUCH_SCALE_FACTOR;//设置六角星数组中的各个六角星绕x轴旋转角度
}
}
mPreviousY = y;//记录触控笔位置
mPreviousX = x;//记录触控笔位置
return true;
}
public void setOrtho(boolean isOrtho) {
if (isOrtho) {
MatrixState.setProjectOrtho(-Constant.ratio, Constant.ratio, -1, 1, 1, 10);
} else {
//设置透视投影
MatrixState.setProjectFrustum(-Constant.ratio * 0.4f, Constant.ratio * 0.4f, -1 * 0.4f, 1 * 0.4f, 1, 50);
}
}
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();
}
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
Log.d(TAG, "onSurfaceChange");
//设置视窗大小及位置
GLES20.glViewport(0, 0, width, height);
//计算GLSurfaceView的宽高比
Constant.ratio = (float) width / height;
//设置平行投影
MatrixState.setProjectOrtho(-Constant.ratio, Constant.ratio, -1, 1, 1, 10);
//调用此方法产生摄像机9参数位置矩阵
MatrixState.setCamera(
0, 0, 3f,
0, 0, 0f,
0f, 1.0f, 0.0f
);
}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//设置屏幕背景色RGBA
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
//创建六角星数组中的各个对象
for (int i = 0; i < ha.length; i++) {
ha[i] = new SixPointedStar(ProjectionSurfaceView.this, 0.2f, 0.5f, -0.3f * i);
}
//打开深度检测
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
}
}
}
ProjectionActivity 源码:
package com.opengl.app.projection;
import android.content.pm.ActivityInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import com.opengl.app.R;
public class ProjectionActivity extends AppCompatActivity {
private ProjectionSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
//设置为全屏
// requestWindowFeature(Window.FEATURE_NO_TITLE);
//设置为横屏模式
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//初始化GLSurfaceView
//切换到主界面
setContentView(R.layout.activity_projection);
mGLSurfaceView = (ProjectionSurfaceView) findViewById(R.id.projection_sf);
mGLSurfaceView.requestFocus();//获取焦点
mGLSurfaceView.setFocusableInTouchMode(true);//设置为可触控
}
@Override
protected void onResume() {
super.onResume();
mGLSurfaceView.onResume();
}
@Override
protected void onPause() {
super.onPause();
mGLSurfaceView.onPause();
}
public void ortho(View v){
mGLSurfaceView.setOrtho(true);
}
public void frustum(View v){
mGLSurfaceView.setOrtho(false);
}
}
SixPointedStar 源码:
package com.opengl.app.view;
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;
import com.opengl.app.Constant;
import com.opengl.app.MatrixState;
import com.opengl.app.ShaderUtil;
import com.opengl.app.projection.ProjectionSurfaceView;
//六角星
public class SixPointedStar {
int mProgram;//自定义渲染管线着色器程序id
int muMVPMatrixHandle;//总变换矩阵引用
int maPositionHandle; //顶点位置属性引用
int maColorHandle; //顶点颜色属性引用
String mVertexShader; //顶点着色器代码脚本
String mFragmentShader; //片元着色器代码脚本
static float[] mMMatrix = new float[16]; //具体物体的3D变换矩阵,包括旋转、平移、缩放
FloatBuffer mVertexBuffer;//顶点坐标数据缓冲
FloatBuffer mColorBuffer;//顶点着色数据缓冲
int vCount = 0;
public float yAngle = 0;//绕y轴旋转的角度
public float xAngle = 0;//绕z轴旋转的角度
public SixPointedStar(ProjectionSurfaceView mv, float r, float R, float z) {
//调用初始化顶点数据的initVertexData方法
initVertexData(R, r, z);
//调用初始化着色器的intShader方法
initShader(mv);
}
//自定义初始化顶点数据的initVertexData方法
public void initVertexData(float R, float r, float z) {
List flist = new ArrayList();
float tempAngle = 360 / 6;
for (float angle = 0; angle < 360; angle += tempAngle) {
//第一个三角形
//第一个中心点
flist.add(0f);
flist.add(0f);
flist.add(z);
//第二个点
flist.add((float) (R * Constant.UNIT_SIZE * Math.cos(Math.toRadians(angle))));
flist.add((float) (R * Constant.UNIT_SIZE * Math.sin(Math.toRadians(angle))));
flist.add(z);
//第三个点
flist.add((float) (r * Constant.UNIT_SIZE * Math.cos(Math.toRadians(angle + tempAngle / 2))));
flist.add((float) (r * Constant.UNIT_SIZE * Math.sin(Math.toRadians(angle + tempAngle / 2))));
flist.add(z);
//第二个三角形
//第一个中心点
flist.add(0f);
flist.add(0f);
flist.add(z);
//第二个点
flist.add((float) (r * Constant.UNIT_SIZE * Math.cos(Math.toRadians(angle + tempAngle / 2))));
flist.add((float) (r * Constant.UNIT_SIZE * Math.sin(Math.toRadians(angle + tempAngle / 2))));
flist.add(z);
//第三个点
flist.add((float) (R * Constant.UNIT_SIZE * Math.cos(Math.toRadians(angle + tempAngle))));
flist.add((float) (R * Constant.UNIT_SIZE * Math.sin(Math.toRadians(angle + tempAngle))));
flist.add(z);
}
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);
}
ByteBuffer vbb = ByteBuffer.allocateDirect(vertexArray.length * 4);
vbb.order(ByteOrder.nativeOrder()); //设置字节顺序为本地操作系统顺序
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertexArray);
mVertexBuffer.position(0);
//顶点着色数据的初始化================begin============================
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()); //设置字节顺序为本地操作系统顺序
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(colorArray);
mColorBuffer.position(0);
//特别提示:由于不同平台字节顺序不同数据单元不是字节的一定要经过ByteBuffer
//转换,关键是要通过ByteOrder设置nativeOrder(),否则有可能会出问题
//顶点着色数据的初始化================end============================
}
//自定义初始化着色器的intShader方法
public void initShader(ProjectionSurfaceView mv) {
//加载顶点着色器的脚本内容
mVertexShader = ShaderUtil.loadFromAssetsFile("six_point_vertex.sh", mv.getResources());
//加载片元着色器的脚本内容
mFragmentShader = ShaderUtil.loadFromAssetsFile("six_point_frag.sh", mv.getResources());
//基于顶点着色器与片元着色器创建程序
mProgram = ShaderUtil.createProgram(mVertexShader, mFragmentShader);
//获取程序中顶点位置属性引用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,
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);
}
}
这里需要用到2个shell程序,用来加载片元着色器和顶点着色器:
six_point_vertex.sh:
uniform mat4 uMVPMatrix; //总变换矩阵
attribute vec3 aPosition; //顶点位置
attribute vec4 aColor; //顶点颜色
varying vec4 aaColor; //用于传递给片元着色器的变量
void main()
{
gl_Position = uMVPMatrix * vec4(aPosition,1); //根据总变换的矩阵计算绘制此顶点的位置
aaColor = aColor; //将接收的颜色传递给片元着色器
}
six_point_frag.sh
precision mediump float;
varying vec4 aaColor; //接收从顶点着色器过来的参数
void main()
{
gl_FragColor = aaColor; //给此片源颜色值
}