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: 尊敬的勇者-张风捷特烈,祝您一路平安,成功斩杀黑龙...


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

查看所有标签

猜你喜欢:

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

编写可维护的JavaScript

编写可维护的JavaScript

扎卡斯 / 李晶、郭凯、张散集 / 人民邮电出版社 / 2013-4 / 55.00元

《编写可维护的JavaScript》向开发人员阐述了如何在团队开发中编写具备高可维护性的JavaScript代码,书中详细说明了作为团队一分子,应该怎么写JavaScript。《编写可维护的JavaScript》内容涵盖了编码风格、编程技巧、自动化、测试等几方面,既包括具体风格和原则的介绍,也包括示例和技巧说明,最后还介绍了如何通过自动化的工具和方法来实现一致的编程风格。 《编写可维护的Ja......一起来看看 《编写可维护的JavaScript》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试