OpenGL ES入门2-三角形绘制及纹理贴图

一 OpenGL中多边形

OpenGL中多边形都是由一个个三角形图元构成,三角形具有稳定性,三角形是OpenGLES提供的最复杂的图元单位。所以此处先以三角形的绘制作为OpenGL ES的入门。

二、三角形绘制

OpenGL图形的绘制流程基本差不多,先要定义顶点着色器和片元着色器。顶点着色器和片元着色器使用glsl语言编写。

顶点着色器:

#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec4 aColor;
uniform mat4 vMatrix;
out vec4 vColor;
void main() {
     gl_Position  = vMatrix * vPosition;
     gl_PointSize = 10.0;
     vColor = aColor;
}

mat4是4阶浮点方阵。vec4是4维浮点向量。in 代表输入,out代表输出。
uniform变量:uniform变量是外部application程序传递给(vertex和fragment)shader的变量。
attribute变量:只能在vertex shader中使用的变量。
varying变量:varying变量是vertex和fragment shader之间做数据传递用的。一般vertex shader修改varying变量的值,然后fragment shader使用该varying变量的值。因此varying变量在vertex和fragment shader二者之间的声明必须是一致的。

片元着色器:

#version 300 es
precision mediump float;
in vec4 vColor;
out vec4 fragColor;
void main() { 
     fragColor = vColor;
}

渲染器器代码如下:

/** 等腰直角三角形 + RGB 三个顶点红绿蓝三种颜色渲染。
 *
 * **/

public class TriangleRenderer implements GLSurfaceView.Renderer{
    private FloatBuffer vertexBuffer;
    private FloatBuffer colorBuffer;
    //渲染程序
    private int mProgram;

    //3个定点,等腰直角
    static float triangleCoords[] ={
            0.5f,  0.5f, 0.0f, // top
            -0.5f, -0.5f, 0.0f, // bottom left
            0.5f, -0.5f, 0.0f  // bottom right
    };

    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
    };

    //相机矩阵
    private final float[] mViewMatrix = new float[16];
    //投影矩阵
    private final float[] mProjectMatrix = new float[16];
    //最终变换矩阵
    private final float[] mMVPMatrix = new float[16];

//    //变换矩阵
//    private int uMatrixLocation;

    public TriangleRenderer() {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoords.length*4);
        byteBuffer.order(ByteOrder.nativeOrder());
        vertexBuffer = byteBuffer.asFloatBuffer();
        //把这门语法() 推送给GPU
        vertexBuffer.put(triangleCoords);
        vertexBuffer.position(0);

        colorBuffer = ByteBuffer.allocateDirect(color.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        //传入指定的数据
        colorBuffer.put(color);
        colorBuffer.position(0);

    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);
        //编译顶点着色程序
        String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_base_matrix_shader);
        int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
        //编译片段着色程序
        String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_base_common_shader);
        int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
        //连接程序
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES环境中使用程序
        GLES30.glUseProgram(mProgram);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES30.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,5.0f,//摄像机坐标
                0f,0f,0f,//目标物的中心坐标
                0f,1.0f,0.0f);//相机方向
        //接着是摄像机顶部的方向了,如下图,很显然相机旋转,up的方向就会改变,这样就会会影响到绘制图像的角度。
        //例如设置up方向为y轴正方向,upx = 0,upy = 1,upz = 0。这是相机正对着目标图像
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);

    }

    @Override
    public void onDrawFrame(GL10 gl) {

        //左乘矩阵
        int uMaxtrixLocation = GLES30.glGetUniformLocation(mProgram,"vMatrix");
        // 将前面计算得到的mMVPMatrix(frustumM setLookAtM 通过multiplyMM 相乘得到的矩阵) 传入vMatrix中,与顶点矩阵进行相乘
        GLES30.glUniformMatrix4fv(uMaxtrixLocation,1,false,mMVPMatrix,0);

        int aPositionLocation = GLES30.glGetAttribLocation(mProgram,"vPosition");
        GLES30.glEnableVertexAttribArray(aPositionLocation);
        //x y z 所以数据size 是3
        GLES30.glVertexAttribPointer(aPositionLocation,3,GLES30.GL_FLOAT,false,0,vertexBuffer);

        int aColorLocation = GLES20.glGetAttribLocation(mProgram,"aColor");
        //准备颜色数据 rgba 所以数据size是 4
        GLES30.glVertexAttribPointer(aColorLocation, 4, GLES30.GL_FLOAT, false, 0, colorBuffer);
        //启用顶点颜色句柄
        GLES30.glEnableVertexAttribArray(aColorLocation);

        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);

        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(aPositionLocation);
        GLES30.glDisableVertexAttribArray(aColorLocation);
    }
}

将顶点数据传递给vPosition变量,将投影、视图矩阵相乘的结果赋值给vMatrix,然后与顶点坐标矩阵左乘,从而作用到顶点坐标上。

绘制效果如下:

等腰三角形.png

三、三角形纹理贴图

纹理映射时只需要为物体的顶点指定纹理坐标即可,其余部分由片元着色器插值完成。所以核心工作就是将三角形的每个顶点坐标,找到对应的纹理坐标即可。
顶点着色器:

#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
uniform mat4 vMatrix;
out vec2 vTexCoord;
void main() {
     gl_Position  = vMatrix * vPosition;
     gl_PointSize = 10.0;
     vTexCoord = aTextureCoord;
}

片元着色器:
vertex_triangle_texture_shader.glsl

#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 vFragColor;
void main() {
     vFragColor = texture(uTextureUnit,vTexCoord);
}

渲染器代码:

/** 等腰直角三角形+纹理贴图
 *
 * **/
public class TriangleTextureRenderer implements GLSurfaceView.Renderer{
    private static final String TAG = "TriangleTextureRenderer";
    private FloatBuffer vertexBuffer;
    private FloatBuffer textureBuffer;
    //渲染程序
    private int mProgram;

    //3个定点,等腰直角
    static float triangleCoords[] ={
            0.5f,  0.5f, 0.0f, // top
            -0.5f, -0.5f, 0.0f, // bottom left
            0.5f, -0.5f, 0.0f  // bottom right
    };

    //纹理坐标1
//    private float textureVertex[] = {
//            0.75f, 0.25f,
//            0.25f, 0.75f,
//            0.75f, 0.75f,
//    };

    //纹理坐标2
    // 三角形3个定点对应在纹理坐标系中的坐标
    private float textureVertex[] = {
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
    };

    //相机矩阵
    private final float[] mViewMatrix = new float[16];
    //投影矩阵
    private final float[] mProjectMatrix = new float[16];
    //最终变换矩阵
    private final float[] mMVPMatrix = new float[16];


    //纹理id
    private int textureId;

//    //变换矩阵
//    private int uMatrixLocation;

    public TriangleTextureRenderer() {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(triangleCoords.length*4);
        byteBuffer.order(ByteOrder.nativeOrder());
        vertexBuffer = byteBuffer.asFloatBuffer();
        //把这门语法() 推送给GPU
        vertexBuffer.put(triangleCoords);
        vertexBuffer.position(0);

        textureBuffer = ByteBuffer.allocateDirect(textureVertex.length * 4)
                .order(ByteOrder.nativeOrder())
                .asFloatBuffer();
        //传入指定的数据
        textureBuffer.put(textureVertex);
        textureBuffer.position(0);

    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        GLES30.glClearColor(0.5f,0.5f,0.5f,1.0f);
        //编译顶点着色程序
        String vertexShaderStr = ResReadUtils.readResource(R.raw.vertex_triangle_texture_shader);
        int vertexShaderId = ShaderUtils.compileVertexShader(vertexShaderStr);
        //编译片段着色程序
        String fragmentShaderStr = ResReadUtils.readResource(R.raw.fragment_triangle_texture_shader);
        int fragmentShaderId = ShaderUtils.compileFragmentShader(fragmentShaderStr);
        //连接程序
        mProgram = ShaderUtils.linkProgram(vertexShaderId, fragmentShaderId);
        //在OpenGLES环境中使用程序
        GLES30.glUseProgram(mProgram);
        //加载纹理
        textureId = TextureUtils.loadTexture(AppCore.getInstance().getContext(),R.drawable.world_map);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES30.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,3.6f,//摄像机坐标
                0f,0f,0f,//目标物的中心坐标
                0f,1.0f,0.0f);//相机方向
        //接着是摄像机顶部的方向了,如下图,很显然相机旋转,up的方向就会改变,这样就会会影响到绘制图像的角度。
        //例如设置up方向为y轴正方向,upx = 0,upy = 1,upz = 0。这是相机正对着目标图像
        //计算变换矩阵
        Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);

    }

    @Override
    public void onDrawFrame(GL10 gl) {

        //左乘矩阵
        int uMaxtrixLocation = GLES30.glGetUniformLocation(mProgram,"vMatrix");
        // 将前面计算得到的mMVPMatrix(frustumM setLookAtM 通过multiplyMM 相乘得到的矩阵) 传入vMatrix中,与顶点矩阵进行相乘
        GLES30.glUniformMatrix4fv(uMaxtrixLocation,1,false,mMVPMatrix,0);

        int aPositionLocation = GLES30.glGetAttribLocation(mProgram,"vPosition");
        GLES30.glEnableVertexAttribArray(aPositionLocation);
        //x y z 所以数据size 是3
        GLES30.glVertexAttribPointer(aPositionLocation,3,GLES30.GL_FLOAT,false,0,vertexBuffer);

        int aTextureLocation = GLES20.glGetAttribLocation(mProgram,"aTextureCoord");
        Log.e(TAG, "onDrawFrame: textureLocation="+aTextureLocation);
        //纹理坐标数据 x、y,所以数据size是 2
        GLES30.glVertexAttribPointer(aTextureLocation, 2, GLES30.GL_FLOAT, false, 0, textureBuffer);
        //启用顶点颜色句柄
        GLES30.glEnableVertexAttribArray(aTextureLocation);
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0);
        //绑定纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D,textureId);
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 3);

        //禁止顶点数组的句柄
        GLES30.glDisableVertexAttribArray(aPositionLocation);
        GLES30.glDisableVertexAttribArray(aTextureLocation);
    }
}

绘制效果如下:
完整贴图如下:

世界地图.PNG

使用纹理坐标2:


三角形纹理贴图.png

使用纹理坐标1:


三角形纹理贴图-坐标1.png

可以看到,对于同一个纹理,使用不同纹理坐标进行贴图时,实际就能显示贴图的不同部位。

代码:
https://github.com/godtrace12/DOpenglTest.git

主要参考:
https://blog.csdn.net/byhook/article/details/83747146
https://blog.csdn.net/gongxiaoou/article/details/89463784
https://blog.csdn.net/byhook/article/details/83990792
https://blog.csdn.net/jklwan/article/details/104010383
https://blog.csdn.net/z444_579/article/details/51967037

你可能感兴趣的:(OpenGL ES入门2-三角形绘制及纹理贴图)