美文网首页
OpenGL ES入门2-三角形绘制及纹理贴图

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

作者: 神迹12 | 来源:发表于2020-06-29 22:29 被阅读0次

    一 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-三角形绘制及纹理贴图

          本文链接:https://www.haomeiwen.com/subject/cqsbfktx.html