Android OpenGL ES开发(四)等腰直角三角形和彩色三角形

上篇文章,我们已经绘制出了一个直角三角形,但是我们的坐标明明设置是等腰直角的三角形,但实际上不是,这是为什么呢?原因是,手机屏幕宽高不等,没有做坐标变换,所以导致不是等腰直角三角形。那要如何坐标变换呢?那要涉及到变换矩阵、相机和投影了。

矩阵

在数学中,矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合 ,最早来自于方程组的系数及常数所构成的方阵。这一概念由19世纪英国数学家凯利首先提出。

相机

根据现实生活中的经历,对一个场景,随着相机的位置、方向的不同,拍摄出来的画面也是不相同。将相机对应于OpenGL世界,决定相机拍摄的结果(也就是最后屏幕上展示的结果),包括相机位置、相机观察方向以及相机的UP方向、

相机位置:可以理解为相机在3D空间里面的坐标点
相机观察方向:表示镜头的朝向
相机UP方向:可以理解为相机顶端指向的方向

在Android OpenGLES 程序中,我们可以通过以下方法来进行相机设置:

Matrix.setLookAtM ( float[] rm, //接收相机变换矩阵
int rmOffset, //变换矩阵的起始位置(偏移量)
float eyeX,float eyeY, float eyeZ, //相机位置
float centerX,float centerY,float centerZ, //观测点位置
float upX,float upY,float upZ) //up向量在xyz上的分量

投影

用相机看到的3D世界,最后还需要呈现到一个2D平面上,这就是投影。投影有两种:正交投影、透视投影
正交投影
使用正交投影,物体呈现出来的大小不会随着居里点的远近而发生变化,在Android OpenGLES 程序中,我们可以通过以下方法来进行设置正交投影:

Matrix.orthoM ( float[] m, //接收正交投影的变换矩阵
int mOffset, //变换矩阵的起始位置(偏移量)
float left, //相对观察点近面的左边距
float right, //相对观察点近面的右边距
float bottom, //相对观察点近面的下边距
float top, //相对观察点近面的上边距
float near, //相对观察点近面距离
float far) //相对观察点远面距离

透视投影
使用透视投影,物体离视点越远,呈现出来的越小。离视点越近,呈现出来的越大。在Android OpenGLES 程序中,我们可以通过以下方法来进行设置透视投影:

Matrix.frustumM ( float[] m, //接收透视投影的变换矩阵
int mOffset, //变换矩阵的起始位置(偏移量)
float left, //相对观察点近面的左边距
float right, //相对观察点近面的右边距
float bottom, //相对观察点近面的下边距
float top, //相对观察点近面的上边距
float near, //相对观察点近面距离
float far) //相对观察点远面距离

使用变换矩阵

实际上相机设置和投影设置并不是真正的设置,而是通过设置参数来得到一个使用相机后顶点坐标的变换矩阵,和投影下的顶点坐标变换矩阵,我们还需把矩阵传入给顶点着色器,在顶点着色器中传入矩阵乘以坐标的向量,得到实际展示的坐标向量。注意,是矩阵乘以向量,不是坐标向量乘以矩阵,矩阵乘法是不满足交换律的。
而通过上面的相机设置和投影设置,我们得到的是两个矩阵,为了方便,我们还需要将相机矩阵和投影矩阵相乘,得到一个实际的变换矩阵,再传给顶点着色器。矩阵相乘:

Matrix.multiplyMM ( float[] result, //接收相乘结果
int resultOffset, //接收矩阵的起始位置(偏移量)
float[] lhs, //左矩阵
int lhsOffset, //左矩阵的起始位置(偏移量)
float[] rhs, //右矩阵
int rhsOffset) //右矩阵的起始位置(偏移量)

等腰直角三角形的实现

在上篇文章基础上,我们需要做以下步骤即可实现绘制一个等腰直角三角形:
1、修改顶点着色器,增加矩阵变换:

    private final String vertextShaderCode =
            "attribute vec4 vPosition;"+
            "uniform mat4 vMatrix;"+
            "void main() {"+
            "   gl_Position = vMatrix * vPosition;"+
            "}";

2、设置相机和投影,获取相机矩阵和投影矩阵,然后通过相机矩阵与投影矩阵相乘,得到实际变换矩阵:

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        GLES20.glViewport(0,0,width,height);

        float ratio = (float) width/height;

        //设置透视矩阵
        Matrix.frustumM(mProjectMatrix,0,-ratio,ratio,-1,1,3,7);
        //设置相机位置
        Matrix.setLookAtM(mViewMatrix,0,0,0,7.0f,0,0,0,0,1.0f,0);
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
    }

3、将变换矩阵传入顶点着色器:

    @Override
    public void onDrawFrame(GL10 gl)
    {
        if(program == 0)
            return;
        //获取变换矩阵vMatrix成员句柄
        int vMatrix = GLES20.glGetUniformLocation(program,"vMatrix");
        //设置vMatrix的值
        GLES20.glUniformMatrix4fv(vMatrix,1,false,mMVPMatrix,0);
        //获取顶点着色器的vPosition成员句柄
        int vPosition = GLES20.glGetAttribLocation(program, "vPosition");
        //启用vPosition句柄
        GLES20.glEnableVertexAttribArray(vPosition);
        //传入但侥幸的坐标数据
        GLES20.glVertexAttribPointer(vPosition,3,GLES20.GL_FLOAT,false,3*4, vertexBuffer);
        //获取片元着色器的vColor成员句柄
        int vColor = GLES20.glGetUniformLocation(program, "vColor");
        //设置绘制三角形的颜色
        GLES20.glUniform4fv(vColor,1,color,0);
        //绘制三角形
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,3);
        //禁止顶点数组的句柄
        GLES20.glDisableVertexAttribArray(vPosition);
    }

运行即可得到一个等腰直角三角形:


2.jpg

彩色三角形

老是显示一个白色的三角形实在太单调了,我们需要让这个三角形变成彩色,该怎么做?
片元着色是针对片元颜色的,针对片元执行一次。而在我们的片元着色器中,我们是直接给片元着色器赋值的,外部只传入一个颜色值,要使三角形呈现出彩色,我们需要在不同的片元赋值不同的颜色。为了简单处理,我们在上个等腰直角三角形的实例中修改顶点着色器跟片元着色器,达到三角形呈现为彩色:
顶点着色器

    private final String vertextShaderCode =
            "attribute vec4 vPosition;"+
            "uniform mat4 vMatrix;"+
            "attribute vec4 aColor;"+
            "varying  vec4 vColor;"+
            "void main() {"+
            "   gl_Position = vMatrix * vPosition;"+
            "   vColor = aColor;"+
            "}";

可以看到增加已一个aColor(顶点颜色)作为输入量,传递给vColor。vColor的前面有个varying 。像attribute、uniform、varying都是在OpenGL的着色器中表示限定符,
attribute一般用于每个顶点都各不相同的量。
只能在顶点着色器中使用
应用场景:传递顶点的位置、法线、颜色、纹理坐标。
uniform一般用于对同一组顶点组成的3D物理中的各个顶点都相同的量。
在 顶点着色器 和 片元着色器中是共享的,相当于一个全局的变量。
应用场景:传递 MVP 矩阵、光照位置、颜色等全局参数。
varying一般用于从顶点着色器传入片元着色器的量。
应用场景:纹理坐标、光照计算信息、法线等。

还有个const表示常量。

片元着色器

    private final String fragmentShaderCode =
            "precision mediump float;"+
            "varying vec4 vColor;"+
            "void main() {"+
            "   gl_FragColor = vColor;"+
            "}";

vColor前面的限定符改为varying,接收顶点着色器传过来的量

然后,我们需要传入三个不同的顶点颜色到顶点着色器中:

    //颜色
    private float color[] = {
        0.0f, 1.0f, 0.0f, 1.0f ,
        1.0f, 0.0f, 0.0f, 1.0f,
        0.0f, 0.0f, 1.0f, 1.0f
    };
        //申请底层空间
        ByteBuffer colorBuffer = ByteBuffer.allocateDirect(color.length * 4);
        colorBuffer.order(ByteOrder.nativeOrder());
        colorFloatBuffer = colorBuffer.asFloatBuffer();
        colorFloatBuffer.put(color);
        colorFloatBuffer.position(0);

        //获取顶点着色器的vColor成员句柄
        int aColor = GLES20.glGetAttribLocation(program, "aColor");
        //启用aColor句柄
        GLES20.glEnableVertexAttribArray(aColor);
        //设置绘制三角形的颜色
        GLES20.glVertexAttribPointer(aColor,4,GLES20.GL_FLOAT,false,4*4,colorFloatBuffer);

运行得到一个彩色的等腰直角三角形:


3.jpg

你可能感兴趣的:(Android OpenGL ES开发(四)等腰直角三角形和彩色三角形)