Android多媒体之GL-ES战记第一集--勇者集结

栏目: 后端 · 发布时间: 5年前

内容简介:传说,在这片代码大陆上,存在一个古老的种族,它们拥有无尽的力量,却罕有人能够驾驭多媒体王国中存在一个隐蔽的角落,是这个种族的栖息之地,很少有人敢冒犯那里Android多媒体领域有一处:被后人称为

传说,在这片代码大陆上,存在一个古老的种族,它们拥有无尽的力量,却罕有人能够驾驭

多媒体王国中存在一个隐蔽的角落,是这个种族的栖息之地,很少有人敢冒犯那里

Android多媒体领域有一处:被后人称为 黑龙洞穴--OpenGL ES ,其中埋藏着图形界的无限财富

勇士们,举起手中的剑,进发!

副本一: 黑龙洞口

NPC:黑龙洞口一片漆黑,其中隐藏着什么规律,勇士们,一起寻找吧!

1.第一关卡:绘制全屏的红色

Android多媒体之GL-ES战记第一集--勇者集结

1.1: GLSurfaceView的使用

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/9 0009:18:25<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:GL测试视图
 */
public class GLView extends GLSurfaceView {
    private GLRenderer mRenderer;

    public GLView(Context context) {
        this(context,null);
    }

    public GLView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        setEGLContextClientVersion(2);//设置OpenGL ES 2.0 context
        mRenderer = new GLRenderer();
        setRenderer(mRenderer);//设置渲染器
    }
}
复制代码

1.2: LSurfaceView.Renderer 的使用

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/9 0009:18:56<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:GL渲染类
 */
public class GLRenderer implements GLSurfaceView.Renderer {
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);//GL视口
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        //清除颜色缓存和深度缓存
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
    }
}
复制代码

1.3:Activity中

Android多媒体之GL-ES战记第一集--勇者集结
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new GLView(this));
    }
}
复制代码

2.第二关卡:三角形的绘制

Android多媒体之GL-ES战记第一集--勇者集结

2.1:三角形

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/9 0009:20:09<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:三角形
 */
public class Triangle {
    private FloatBuffer vertexBuffer;//顶点缓冲
    private final String vertexShaderCode =//顶点着色代码
            "attribute vec4 vPosition;" +
                    "void main() {" +
                    "  gl_Position = vPosition;" +
                    "}";
    private final String fragmentShaderCode =//片元着色代码
            "precision mediump float;" +
                    "uniform vec4 vColor;" +
                    "void main() {" +
                    "  gl_FragColor = vColor;" +
                    "}";
    private final int mProgram;
    private int mPositionHandle;//位置句柄
    private int mColorHandle;//颜色句柄
    private final int vertexCount = sCoo.length / COORDS_PER_VERTEX;//顶点个数
    private final int vertexStride = COORDS_PER_VERTEX * 4; // 3*4=12
    // 数组中每个顶点的坐标数
    static final int COORDS_PER_VERTEX = 3;
    static float sCoo[] = {   //以逆时针顺序
            0.0f, 0.0f, 0.0f, // 顶部
            -1.0f, -1.0f, 0.0f, // 左下
            1.0f, -1.0f, 0.0f  // 右下
    };
    // 颜色,rgba
    float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
    public Triangle() {
        //初始化顶点字节缓冲区
        ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每个浮点数:坐标个数* 4字节
        bb.order(ByteOrder.nativeOrder());//使用本机硬件设备的字节顺序
        vertexBuffer = bb.asFloatBuffer();// 从字节缓冲区创建浮点缓冲区
        vertexBuffer.put(sCoo);// 将坐标添加到FloatBuffer
        vertexBuffer.position(0);//设置缓冲区以读取第一个坐标
        int vertexShader = GLRenderer.loadShader(
                GLES20.GL_VERTEX_SHADER,//顶点着色
                vertexShaderCode);
        int fragmentShader = GLRenderer.loadShader
                (GLES20.GL_FRAGMENT_SHADER,//片元着色
                        fragmentShaderCode);
        mProgram = GLES20.glCreateProgram();//创建空的OpenGL ES 程序
        GLES20.glAttachShader(mProgram, vertexShader);//加入顶点着色器
        GLES20.glAttachShader(mProgram, fragmentShader);//加入片元着色器
        GLES20.glLinkProgram(mProgram);//创建可执行的OpenGL ES项目
    }
    public void draw() {
        // 将程序添加到OpenGL ES环境中
        GLES20.glUseProgram(mProgram);
        //获取顶点着色器的vPosition成员的句柄
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
        //启用三角形顶点的句柄
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        //准备三角坐标数据
        GLES20.glVertexAttribPointer(
                mPositionHandle, COORDS_PER_VERTEX,
                GLES20.GL_FLOAT, false,
                vertexStride, vertexBuffer);
        // 获取片元着色器的vColor成员的句柄
        mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
        //为三角形设置颜色
        GLES20.glUniform4fv(mColorHandle, 1, color, 0);
        //绘制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
        //禁用顶点数组
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }
}
复制代码

2.2:GL渲染类

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/9 0009:18:56<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:GL渲染类
 */
public class GLRenderer implements GLSurfaceView.Renderer {
    Triangle mTriangle;
    /**
     * 加载作色器
     * @param type  顶点着色 {@link GLES20.GL_VERTEX_SHADER}
     *              片元着色 {@link GLES20.GL_FRAGMENT_SHADER}
     * @param shaderCode 着色代码
     * @return 作色器
     */
    public static int loadShader(int type, String shaderCode){
        int shader = GLES20.glCreateShader(type);//创建着色器
        GLES20.glShaderSource(shader, shaderCode);//添加着色器源代码
        GLES20.glCompileShader(shader);//编译
        return shader;
    }
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f);//rgba
        mTriangle = new Triangle();
    }
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);//GL视口
    }
    @Override
    public void onDrawFrame(GL10 gl) {
        //清除颜色缓存和深度缓存
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        mTriangle.draw();
    }
}
复制代码

2.3:着色器

NPC:勇者,你阵亡了没...如果现在退出还来得及,这将是一篇宏伟的战斗史诗

如果你还想继续,举起你手中的剑,同我一起,进发!!!

Android多媒体之GL-ES战记第一集--勇者集结
/**
 * 加载作色器
 * @param type  顶点着色 {@link GLES20.GL_VERTEX_SHADER}
 *              片元着色 {@link GLES20.GL_FRAGMENT_SHADER}
 * @param shaderCode 着色代码
 * @return 作色器
 */
public static int loadShader(int type, String shaderCode){
    int shader = GLES20.glCreateShader(type);//创建着色器
    GLES20.glShaderSource(shader, shaderCode);//添加着色器源代码
    GLES20.glCompileShader(shader);//编译
    return shader;
}
复制代码

2.4:渲染器程序

Android多媒体之GL-ES战记第一集--勇者集结
private final String vertexShaderCode =//顶点着色代码
        "attribute vec4 vPosition;" +
                "void main() {" +
                "  gl_Position = vPosition;" +
                "}";
private final String fragmentShaderCode =//片元着色代码
        "precision mediump float;" +
                "uniform vec4 vColor;" +
                "void main() {" +
                "  gl_FragColor = vColor;" +
                "}";

int vertexShader = GLRenderer.loadShader(
        GLES20.GL_VERTEX_SHADER,//顶点着色
        vertexShaderCode);
int fragmentShader = GLRenderer.loadShader(
        GLES20.GL_FRAGMENT_SHADER,//片元着色
        fragmentShaderCode);
mProgram = GLES20.glCreateProgram();//创建空的OpenGL ES 程序
GLES20.glAttachShader(mProgram, vertexShader);//加入顶点着色器
GLES20.glAttachShader(mProgram, fragmentShader);//加入片元着色器
GLES20.glLinkProgram(mProgram);//创建可执行的OpenGL ES项目
复制代码

2.5:顶点缓冲

Android多媒体之GL-ES战记第一集--勇者集结
private FloatBuffer vertexBuffer;//顶点缓冲
private final int vertexCount = sCoo.length / COORDS_PER_VERTEX;//顶点个数
private final int vertexStride = COORDS_PER_VERTEX * 4; // 3*4=12
static final int COORDS_PER_VERTEX = 3;//数组中每个顶点的坐标数
static float sCoo[] = {   //以逆时针顺序
        0.0f, 0.0f, 0.0f, // 顶部
        -1.0f, -1.0f, 0.0f, // 左下
        1.0f, -1.0f, 0.0f  // 右下
};

//初始化顶点字节缓冲区
ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每个浮点数:坐标个数* 4字节
bb.order(ByteOrder.nativeOrder());//使用本机硬件设备的字节顺序
vertexBuffer = bb.asFloatBuffer();// 从字节缓冲区创建浮点缓冲区
vertexBuffer.put(sCoo);// 将坐标添加到FloatBuffer
vertexBuffer.position(0);//设置缓冲区以读取第一个坐标
复制代码

2.6: 绘制

Android多媒体之GL-ES战记第一集--勇者集结
public void draw() {
     // 将程序添加到OpenGL ES环境中
     GLES20.glUseProgram(mProgram);
     //获取顶点着色器的vPosition成员的句柄
     mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
     //启用三角形顶点的句柄
     GLES20.glEnableVertexAttribArray(mPositionHandle);
     //准备三角坐标数据
     GLES20.glVertexAttribPointer(
             mPositionHandle,//int indx, 索引
             COORDS_PER_VERTEX,//int size,大小
             GLES20.GL_FLOAT,//int type,类型
             false,//boolean normalized,//是否标准化
             vertexStride,// int stride,//跨度
             vertexBuffer);// java.nio.Buffer ptr//缓冲
     // 获取片元着色器的vColor成员的句柄
     mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor");
     //为三角形设置颜色
     GLES20.glUniform4fv(mColorHandle, 1, color, 0);
     //绘制三角形
     GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount);
     //禁用顶点数组:
     //禁用index指定的通用顶点属性数组。
     // 默认情况下,禁用所有客户端功能,包括所有通用顶点属性数组。
     // 如果启用,将访问通用顶点属性数组中的值,
     // 并在调用顶点数组命令(如glDrawArrays或glDrawElements)时用于呈现
     GLES20.glDisableVertexAttribArray(mPositionHandle);
 }
复制代码

副本二--- 龙之怒色

1.第一关卡:简单认识OpenGL ES 着色脚本语言

GLSL(OpenGL Shader Language)
1.一种面相过程的高级语言
2.基于C/C++的语法(子集)及流程控制
3.完美支持向量和矩阵的操作
4.通过类型限定符来管理输入与输出
复制代码

1.1:文件的格式

没有统一的拓展名,经过百度,感觉这种方式比较符合我的审美

而且AndroidStudio支持这些拓展名,你都叫 .glsl 也可以,能分清就像

Android多媒体之GL-ES战记第一集--勇者集结
.vert - 顶点着色器
.tesc - 曲面细分控制着色器
.tese - 曲面细分评估着色器
.geom - 几何着色器
.frag - 片元着色器
.comp - 计算着色器
复制代码

原生数据类型

标量:一维的数值操作

float   浮点型
bool    布尔型
int     整型
|--- 支持 8进制(0开头)  16进制(0x开头)
复制代码

向量:储存及操作 颜色、位置、纹理坐标等

vec2    二维向量型-浮点型
vec3    三维向量型-浮点型
vec4    四维向量型-浮点型

ivec2    二维向量型-整型
ivec3    三维向量型-整型
ivec4    四维向量型-整型

bvec2    二维向量型-布尔型
bvec3    三维向量型-布尔型
bvec4    四维向量型-布尔型
复制代码

矩阵:根据矩阵的运算进行变换操作

mat2    2X2矩阵-浮点型
mat3    3X3矩阵-浮点型
mat4    4X4矩阵-浮点型
复制代码

采样器

sampler2D   二维纹理
sampler3D   三维纹理
samplerCube 立方贴图纹理
复制代码

结构体:例如

struct ball{
    vec3 color;
    vec3 position;
}
复制代码

数组

vec3 pos[]; //声明不定大小的三维向量数组
vec3 pos[6];//声明6个三维向量数组
复制代码

限定符

attribute 顶点的变量,如顶点位置,颜色
uniform 
varying 用于从定点着色器传递到片元作色器的变量
const 
precision 精度
|---lowp
|---mediump
|---highp
复制代码

2.第二关卡:资源文件的读取

加载着色脚本的代码差不多,封装一下,写个GLUtils吧:

/**
 * 作者:张风捷特烈<br/>
 * 时间:2019/1/10 0010:10:58<br/>
 * 邮箱:1981462002@qq.com<br/>
 * 说明:OpenGL ES 辅助工具
 */
public class GLUtils {

    //从脚本中加载shader内容的方法
    public static int loadShaderAssets(Context ctx, int type, String name) {
        String result = null;
        try {
            InputStream in = ctx.getAssets().open(name);
            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 loadShader(type, result);
    }

    /**
     * 加载作色器
     *
     * @param type       着色器类型    顶点着色 {@link GLES20.GL_VERTEX_SHADER}
     *                   片元着色 {@link GLES20.GL_FRAGMENT_SHADER}
     * @param shaderCode 着色代码
     * @return 作色器
     */
    public static int loadShader(int type, String shaderCode) {
        int shader = GLES20.glCreateShader(type);//创建着色器
        if (shader == 0) {//加载失败直接返回
            return 0;
        }
        GLES20.glShaderSource(shader, shaderCode);//加载着色器源代码
        GLES20.glCompileShader(shader);//编译
        return checkCompile(type, shader);
    }

    /**
     * 检查shader代码是否编译成功
     *
     * @param type   着色器类型
     * @param shader 着色器
     * @return 着色器
     */
    private static int checkCompile(int type, int shader) {
        int[] compiled = new int[1];//存放编译成功shader数量的数组
        //获取Shader的编译情况
        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {//若编译失败则显示错误日志并
            Log.e("ES20_COMPILE_ERROR",
                    "Could not compile shader " + type + ":" + GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);//删除此shader
            shader = 0;
        }
        return shader;
    }
}
复制代码

3.第三关卡: tri.fragtri.vert 的分析

3.1:先看片元: tri.frag

第一句是声明片元的精度

第二句是声明片元的颜色:一个vec4的变量--vColor

gl_FragColor = vColor; gl_FragColor是gl内定名,将vColor值赋给它

precision mediump float;
uniform vec4 vColor;
void main() {
  gl_FragColor = vColor;
}
复制代码

单看一下着色的操作流程:

Android多媒体之GL-ES战记第一集--勇者集结

所以从 Java 代码来看,重点在color,它是一个四值数组,每个值0~1

分别对应 r,g,b,a 四值,即 红,绿,蓝,透明 四个颜色维度

// 颜色,rgba
float color[] = {0.63671875f, 0.76953125f, 0.22265625f, 1.0f};
复制代码

更换颜色:

rgba 132,197,240,255---->0.5176471f, 0.77254903f, 0.9411765f, 1.0f

Android多媒体之GL-ES战记第一集--勇者集结

3.2:再看定点:tri.vert

定义了一个四维的向量给gl_Position

attribute vec4 vPosition;
void main() {
  gl_Position = vPosition;
}
复制代码

关于顶点的缓冲 初始化阶段将顶点数据经过基本处理

static float sCoo[] = {   //以逆时针顺序
        0.0f, 0.0f, 0.0f, // 顶部
        -1.0f, -1.0f, 0.0f, // 左下
        1.0f, -1.0f, 0.0f  // 右下
};

/**
 * 缓冲数据
 */
private void bufferData() {
    ByteBuffer bb = ByteBuffer.allocateDirect(sCoo.length * 4);//每个浮点数:坐标个数* 4字节
    bb.order(ByteOrder.nativeOrder());//使用本机硬件设备的字节顺序
    vertexBuffer = bb.asFloatBuffer();// 从字节缓冲区创建浮点缓冲区
    vertexBuffer.put(sCoo);// 将坐标添加到FloatBuffer
    vertexBuffer.position(0);//设置缓冲区以读取第一个坐标
}
复制代码

每三个数是一个顶点,分别代表(x,y,z),先卡z=0,也就是二维坐标系

经过三个点的测试,可以发现是一个中心在原点,左右跨度为1的坐标系

Android多媒体之GL-ES战记第一集--勇者集结

变动坐标

Android多媒体之GL-ES战记第一集--勇者集结

4.第三关卡: 顶点着色

刚才是给片元进行着色的,现在看看怎么给顶点着色,肯定要有顶点变量

前面关于修饰关键字: varying 用于从定点着色器传递到片元作色器的变量

4.1:顶点代码: tri.vert

attribute vec3 vPosition;//顶点坐标
uniform mat4 uMVPMatrix; //总变换矩阵
attribute vec4 aColor;//顶点颜色
varying  vec4 vColor;//片元颜色

void main() {
  gl_Position = uMVPMatrix*vec4(vPosition,1);
  vColor = aColor;//将顶点颜色传给片元
}
复制代码

4.2:片元代码: tri.frag

precision mediump float;
varying vec4 vColor;
void main() {
  gl_FragColor = vColor;
}
复制代码

4.3:使用: Triangle.java

三个点,第三个颜色,顶点+缓冲,跟顶点坐标一个套路,取 黄、蓝、绿 三色

//成员变量
private FloatBuffer mColorBuffer;//颜色缓冲
static final int COLOR_PER_VERTEX = 4;//向量维度
private final int vertexColorStride = COLOR_PER_VERTEX * 4; // 4*4=16
float colors[] = new float[]{
        1f, 1f, 0.0f, 1.0f,//黄
        0.05882353f, 0.09411765f, 0.9372549f, 1.0f,//蓝
        0.19607843f, 1.0f, 0.02745098f, 1.0f//绿
};

//注意颜色句柄不是uniform了,获取片元着色器的vColor成员的句柄
mColorHandle = GLES20.glGetAttribLocation(mProgram, "aColor");

//启用三角形顶点颜色的句柄
GLES20.glEnableVertexAttribArray(mColorHandle);

//准备三角顶点颜色数据
GLES20.glVertexAttribPointer(
                mColorHandle,
                COLOR_PER_VERTEX,
                GLES20.GL_FLOAT,
                false,
                vertexColorStride,
                mColorBuffer);  
复制代码
Android多媒体之GL-ES战记第一集--勇者集结

副本三--- 龙之赤瞳

先看这个图,按这样来画个人脸,岂不是会扁掉?这怎么能忍

Android多媒体之GL-ES战记第一集--勇者集结

1.第一关卡:相机-- Matrix.setLookAtM

一共11个参数,吓得我一抖,经过百度,再加上我 神级的Ps技能 ,绘图如下

主要有三个点eye(相机/眼睛位置),center(观察物的位置),up(抬头的感觉,意会一下...)

Android多媒体之GL-ES战记第一集--勇者集结
public static void setLookAtM(float[] rm, int rmOffset,
            float eyeX, float eyeY, float eyeZ,
            float centerX, float centerY, float centerZ, 
            float upX, float upY,float upZ) {
复制代码

2.第二关卡:透视投影-- Matrix.frustumM

八个参数,还好还好,也不是太多...

Android多媒体之GL-ES战记第一集--勇者集结
Matrix.frustumM(float[] m, int offset,
            float left, float right, float bottom, float top,
            float near, float far)
复制代码

3.第三关卡:修正视野,让x,y看起来一致

Android多媒体之GL-ES战记第一集--勇者集结

3.1.GLRenderer中:

//Model View Projection Matrix--模型视图投影矩阵
private final float[] mMVPMatrix = new float[16];
//投影矩阵 mProjectionMatrix
private final float[] mProjectionMatrix = new float[16];
//视图矩阵 mViewMatrix
private final float[] mViewMatrix = new float[16];

---->[GLRenderer#onSurfaceChanged]-------
float ratio = (float) width / height;
//透视投影矩阵--截锥
Matrix.frustumM(mProjectionMatrix, 0,
        -ratio, ratio, -1, 1, 
        3, 7);
// 设置相机位置(视图矩阵)
Matrix.setLookAtM(mViewMatrix, 0,
        0, 0, -3,
        0f, 0f, 0f,
        0f, 1.0f, 0.0f);
        
---->[GLRenderer#onDrawFrame]-------  

 // 计算投影和视图转换
 Matrix.multiplyMM(
         mMVPMatrix, 0,
         mProjectionMatrix, 0,
         mViewMatrix, 0);
 mTriangle.draw(mMVPMatrix);
复制代码

3.2: tri.vert :为顶点添加矩阵变换

attribute vec3 vPosition;//顶点坐标
uniform mat4 uMVPMatrix; //总变换矩阵
void main() {
  gl_Position = uMVPMatrix*vec4(vPosition,1);
}
复制代码

3.3:获取句柄,修正顶点: Triangle.java

//获取程序中总变换矩阵uMVPMatrix成员的句柄
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");

---->[Triangle#draw]-------------
//对顶点进行矩阵变换
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mvpMatrix, 0);
复制代码
Android多媒体之GL-ES战记第一集--勇者集结

副本四-- 龙之振翼

1.第一关卡:旋转30°

mMVPMatrix 再进行矩阵变换就行了

Android多媒体之GL-ES战记第一集--勇者集结
//变换矩阵
private float[] mOpMatrix = new float[16];

---->[GLRenderer#onDrawFrame]-------  
//mOpMatrix旋转变换
Matrix.setRotateM(mOpMatrix, 0, 30, 0, 0, -1);

//使用mOpMatrix对mMVPMatrix进行变换
Matrix.multiplyMM(
        mMVPMatrix, 0,
        mViewMatrix, 0,
        mOpMatrix, 0);
        
Matrix.multiplyMM(
        mMVPMatrix, 0,
        mProjectionMatrix, 0,
        mMVPMatrix, 0);
复制代码

隐藏关卡-- Matrix.multiplyMM

我知道你看得一脸懵X,现在看看multiplyMM是个什么东西

怎么看?当然先看源码啦,这是目前OpenGl ES 里我见过注释最多的...

将两个4x4矩阵相乘,并将结果存储在第三个4x4矩阵中。其中:result = lhs x rhs。
由于矩阵相乘的工作方式,结果矩阵的效果相当于先被右边的矩阵乘,再被左边的矩阵乘。
这跟你期望的情况是相反的。

result  保存结果的浮点数组
lhs     保存左侧矩阵的浮点数组。
rhs     保存右侧矩阵的浮点数组。

三个对应的offset--偏移量

public static native void multiplyMM(float[] result, int resultOffset,
        float[] lhs, int lhsOffset, float[] rhs, int rhsOffset);
复制代码

这里都是用16个float的数组成的矩阵,写个方法打印出来再说

Android多媒体之GL-ES战记第一集--勇者集结
public static void logM(float[] matrix) {
    logM(matrix, "Matrix");
}
/**
 * 打印方阵数组
 *
 * @param matrix 
 * @param name
 */
public static void logM(float[] matrix, String name) {
    int wei = (int) Math.sqrt(matrix.length);
    StringBuffer sb = new StringBuffer("\n[");
    for (int i = 0; i < matrix.length; i++) {
        sb.append(matrix[i]);
        if ((i + 1) % wei == 0) {
            if (i == matrix.length - 1) {
                sb.append("]");
                continue;
            }
            sb.append("\n");
            continue;
        }
        sb.append(" , ");
    }
    Log.e("Matrix_TAG", name + ": " + sb.toString());
}
复制代码
Android多媒体之GL-ES战记第一集--勇者集结
Android多媒体之GL-ES战记第一集--勇者集结

现在回头再来看看:

mOpMatrix本来全是0,经过setRotateM之后变成图中第一个矩阵

第一个 Matrix.multiplyMMmOpMatrix 矩阵作用于 mViewMatrix 上,获得结果矩阵: mMVPMatrix
第二个 Matrix.multiplyMMmMVPMatrix 矩阵作用于 mProjectionMatrix 上,获得结果矩阵: mMVPMatrix

最后根据顶点变换矩阵的句柄,将mMVPMatrix在tri.vert中作用在顶点上

//变换矩阵
private float[] mOpMatrix = new float[16];

//mOpMatrix旋转变换
Matrix.setRotateM(mOpMatrix, 0, 30, 0, 0, -1);

//使用mOpMatrix对mMVPMatrix进行变换
Matrix.multiplyMM(
        mMVPMatrix, 0,
        mViewMatrix, 0,
        mOpMatrix, 0);
        
Matrix.multiplyMM(
        mMVPMatrix, 0,
        mProjectionMatrix, 0,
        mMVPMatrix, 0);
复制代码

2.第二关卡:不停旋转

当GLSurfaceView的 setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY) ;时

RendereronDrawFrame(GL10 gl) { 会不断执行,更新的时间间隔和手机有关

我的真机在 13~19ms 之间,模拟器在 16~48ms 之间,看了一下,转一圈用6s,

即6000ms,一共360°,每次+1°,使用平均每度(每次刷新)用了16.667ms,好吧,完美的 60fps

Android多媒体之GL-ES战记第一集--勇者集结
Android多媒体之GL-ES战记第一集--勇者集结
private int currDeg = 0;

---->[GLRenderer#onDrawFrame]-------  
//初始化变换矩阵
Matrix.setRotateM(mOpMatrix, 0, currDeg, 0, 0, -1);
Matrix.multiplyMM(mMVPMatrix, 0,
        mViewMatrix, 0,
        mOpMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0,
        mProjectionMatrix, 0,
        mMVPMatrix, 0);
mTriangle.draw(mMVPMatrix);

currDeg++;
if (currDeg == 360) {
    currDeg = 0;
}

复制代码

3.第二关卡:不停旋转着缩小

你拍照的时候怎么让成像缩小?----往后退呗!

根据后退为正,可以推测出坐标系是一个右手系,也就是z轴朝向我们

执行很简单: Matrix.translateM 就可以将mOpMatrix进行平移操作

以我们的视角(参考系):你可以想象成图形(观察物)一边旋转一边原离我们,也可以反过来想想

引擎推动的不是飞船而是宇宙。飞船压根就没动过。 --如果对矩阵有兴趣,建议把这篇看十遍

//设置沿Z轴位移
Matrix.translateM(mOpMatrix, 0, 0, 0, currDeg/90.f);
复制代码
Android多媒体之GL-ES战记第一集--勇者集结

NPC: 恭喜您,完成第四副本,现在您获得OpenGL-ES 新手战士的称号,请留下名号:

我(输入):张风捷特烈

NPC: 张风捷特烈,是否继续前行,下面的关卡将更加艰难

我:(点击确定) 执剑向前

NPC: 尊敬的勇者-张风捷特烈,祝您一路平安,成功斩杀黑龙...


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Sass and Compass in Action

Sass and Compass in Action

Wynn Netherland、Nathan Weizenbaum、Chris Eppstein、Brandon Mathis / Manning Publications / 2013-8-2 / USD 44.99

Written by Sass and Compass creators * Complete Sass language reference * Covers prominent Compass community plug-ins * Innovative approach to creating stylesheets Cascading Style Sheets paint the we......一起来看看 《Sass and Compass in Action》 这本书的介绍吧!

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具