android-opengles3.0開發【5】繪製紋理

簡介

本文在上一篇文章的基礎上完成紋理的繪製。

繪製紋理流程也不復雜:

  • 一張作爲紋理的圖片
  • 編寫相應的着色器
  • 準備圖形和紋理的座標
  • 將圖片轉換成紋理
  • 將紋理綁定到着色器指定屬性的位置
  • 繪製圖形和紋理

編寫着色器

頂點着色器

  • a_Position:圖形定點座標
  • a_texCoord:對應的紋理座標,其他位置的座標 opengles 通過插值進行計算
  • v_texCoord:輸出到到片段着色器的紋理座標

在 main() 方法中進行賦值。

#version 300 es

layout (location = 0) in vec4 a_Position;
layout (location = 1) in vec2 a_texCoord;

out vec2 v_texCoord;

void main()
{
    gl_Position = a_Position;
    v_texCoord = a_texCoord;
}

片段着色器

s_texture:紋理的採樣

通過 texture() 方法將傳進來的紋理和座標進行差值採樣,輸出到顏色緩衝區。

#version 300 es
precision mediump float;

in vec2 v_texCoord;
layout (location = 0) out vec4 outColor;
uniform sampler2D s_texture;

void main(){
    outColor = texture(s_texture, v_texCoord);
}

準備頂點、紋理座標

opengl 的座標系是歸一化座標系,原點在屏幕中心,橫向是橫座標,縱向是縱座標,範圍都是[-1,1]

一般設備的屏幕座標系遠點在屏幕左上角,橫向是橫座標,縱向是縱座標,但是縱座標的正方向向下

紋理的座標系是左下角是原點,右上方是正方向,也是歸一化座標系,範圍是[0,1]

所以紋理顯示到屏幕上是,紋理座標與定點座標的 y 座標方向是反着的。

    //頂點,按逆時針順序排列
    public static final float[] VERTEX = {
            0.0f, 0.5f, 0.0f,
            -0.5f, -0.5f, 0.0f,
            0.5f, -0.5f, 0.0f};

    //紋理座標,(s,t),t座標方向和頂點y座標反着
    public static final float[] TEXTURE_COORD = {
            0.5f,0.0f,
            0.0f,1.0f,
            1.0f,1.0f
    };

圖片轉換成紋理

主要就是通過 glGenTextures 方法創建紋理對象

然後將圖片加載進來生成位圖,綁定到紋理對象上,別忘了設置放大和縮小的過濾模式

再使用 glBindTexture 方法綁定紋理到 opengl

通過 texImage2D 將位圖綁定到紋理上,glGenerateMipmap 創建 mip 貼圖

最後位圖的資源已經綁定並複製到紋理對象上了,所以 bitmap 對象就可以釋放了,並且紋理也可以從 opengl 解綁。

    public static int loadTexture(int resId){
        //創建紋理對象
        int[] textureObjIds = new int[1];
        //生成紋理:紋理數量、保存紋理的數組,數組偏移量
        glGenTextures(1, textureObjIds,0);
        if(textureObjIds[0] == 0){
            throw new RuntimeException("創建紋理對象失敗");
        }
        //原尺寸加載位圖資源(禁止縮放)
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;
        Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId, options);
        if (bitmap == null){
            //刪除紋理對象
            glDeleteTextures(1, textureObjIds, 0);
            throw new RuntimeException("加載位圖失敗");
        }
        //綁定紋理到opengl
        glBindTexture(GL_TEXTURE_2D, textureObjIds[0]);
        //設置放大、縮小時的紋理過濾方式,必須設定,否則紋理全黑
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        //將位圖加載到opengl中,並複製到當前綁定的紋理對象上
        texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
        //創建 mip 貼圖
        glGenerateMipmap(GL_TEXTURE_2D);
        //釋放bitmap資源(上面已經把bitmap的數據複製到紋理上了)
        bitmap.recycle();
        //解綁當前紋理,防止其他地方以外改變該紋理
        glBindTexture(GL_TEXTURE_2D, 0);
        //返回紋理對象
        return textureObjIds[0];
    }

渲染器繪製

流程基本和之前文章的繪製流程相同,具體的細節下面代碼中都有很詳細的註釋。

    public void render(Surface surface, int width, int height){
        //創建屏幕上渲染區域:EGL窗口
        int[] surfaceAttribList = {EGL_NONE};
        EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, surface, surfaceAttribList, 0);
        //指定當前上下文
        eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
        //獲取着色器
        int texVertexShader = loadShader(GL_VERTEX_SHADER, loadShaderSource(R.raw.texture_vertex_shader));
        int texFragmentShader = loadShader(GL_FRAGMENT_SHADER, loadShaderSource(R.raw.texture_fragtment_shader));
        //創建並連接程序
        int program = createAndLinkProgram(texVertexShader, texFragmentShader);
        //設置清除渲染時的顏色
        glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
        //設置視口
        glViewport(0, 0, width, height);
        //獲取頂點、紋理座標數據
        FloatBuffer vertexBuffer = getVertextBuffer();
        FloatBuffer texCoordBuffer = getTextureCoordBuffer();
        //擦除屏幕
        glClear(GL_COLOR_BUFFER_BIT);
        //使用程序
        glUseProgram(program);

        //綁定頂點、紋理座標到指定屬性位置
        int aPosition = glGetAttribLocation(program, "a_Position");
        int aTexCoord = glGetAttribLocation(program, "a_texCoord");
        glVertexAttribPointer(aPosition,3,GL_FLOAT,false,0,vertexBuffer);
        glVertexAttribPointer(aTexCoord, 2, GL_FLOAT, false, 0, texCoordBuffer);
        glEnableVertexAttribArray(aPosition);
        glEnableVertexAttribArray(aTexCoord);
        //綁定紋理
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, loadTexture(R.drawable.texture));
        //Set the sampler texture unit to 0
        glUniform1i(glGetUniformLocation(program, "s_texture"),0);
        //繪製
        glDrawArrays(GL_TRIANGLES,0,3);
        //交換 surface 和顯示器緩存
        eglSwapBuffers(eglDisplay, eglSurface);
        //釋放
        eglDestroySurface(eglDisplay, eglSurface);
    }

總結

本文在前文的基礎上,梳理了具體的紋理操作方法,給繪製的圖形貼上一張圖片紋理,整個的流程和前文差不多。

需要注意就是紋理到頂點的座標匹配時的 y 方向,以正常顯示紋理。

項目地址

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章