Opengl ES系列學習--glBlendFunc API使用

     本節我們來看一下glBlendFunc API的使用,對應的代碼是OpenGL\learn\src\main\java\com\opengl\learn\GlBlendFuncRender.java文件。

     所有實例均有提供源碼,下載地址:Opengl ES Source Code

     API中文說明:GLES2.0中文API-glBlendFunc

     glBlendFunc API是用來混合的,上面的API說明已經介紹了很多了,作爲入門學習,有幾個基本知識我們需要知道:1、混合時,要畫上去的爲源顏色,先畫上去的爲目標顏色,執行兩者的混合,比如調用glBlendFunc(GL_ONE, GL_ZERO),前面的GL_ONE表示取要畫上去的,而後面的GL_ZERO指原來已經畫上去的;2、必須在源顏色調用glDrawArrays、glDrawElements接口之前調用glEnable(GL_BLEND)開啓混合,在繪製完成的最後調用glDisable(GL_BLEND)關閉混合,否則可能會出現源顏色不顯示、透明背景黑色等等問題;3、使用混合繪製水印等透明效果時,需要特別注意,水印背景下面不能是白色,否則也會出現各種效果異常問題,本節最後我們有對此作實驗;4、既然是混合,就必須是兩層之間的混合,所以如果我們只調用一次glDrawArrays、glDrawElements接口繪製了一層,那根本不需要使用混合,這個我一開始也一直沒搞清楚。

     好了,本節來看一下我們要實現的效果,如下圖:

     大家看下,我們在最左下角繪製了一個水印。代碼實現中因爲繪製了兩層,所以爲了更好的解耦,我們把原來上一節GlActiveTextureRender中的內容封裝成Map類,本節新增了Watermark.java類。Watermark.java類的所有代碼如下:

package com.opengl.learn.blend;

import android.content.Context;
import android.util.Log;

import com.lime.common.ESShader;
import com.lime.common.TextureHelper;
import com.opengl.learn.R;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import javax.microedition.khronos.opengles.GL10;

import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_BLEND;
import static android.opengl.GLES20.GL_ELEMENT_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.GL_ONE;
import static android.opengl.GLES20.GL_STATIC_DRAW;
import static android.opengl.GLES20.GL_TEXTURE0;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TRIANGLES;
import static android.opengl.GLES20.GL_UNSIGNED_SHORT;
import static android.opengl.GLES20.GL_ZERO;
import static android.opengl.GLES20.glActiveTexture;
import static android.opengl.GLES20.glBindBuffer;
import static android.opengl.GLES20.glBindTexture;
import static android.opengl.GLES20.glBlendFunc;
import static android.opengl.GLES20.glBufferData;
import static android.opengl.GLES20.glClearColor;
import static android.opengl.GLES20.glDisable;
import static android.opengl.GLES20.glDisableVertexAttribArray;
import static android.opengl.GLES20.glDrawElements;
import static android.opengl.GLES20.glEnable;
import static android.opengl.GLES20.glEnableVertexAttribArray;
import static android.opengl.GLES20.glGenBuffers;
import static android.opengl.GLES20.glGetAttribLocation;
import static android.opengl.GLES20.glGetUniformLocation;
import static android.opengl.GLES20.glUniform1i;
import static android.opengl.GLES20.glUseProgram;
import static android.opengl.GLES20.glVertexAttribPointer;
import static android.opengl.GLES20.glViewport;

public class Watermark {
    private static final String TAG = Watermark.class.getSimpleName();
    private static final int BYTES_PER_FLOAT = 4;
    private static final int BYTES_PER_SHORT = 2;
    private static final int POSITION_COMPONENT_SIZE = 3;
    private final float[] mVerticesData =
            {
                    -1.0f, 0.5f, 0.0f, // v0
                    -1.0f, -0.5f, 0.0f, // v1
                    1.0f, -0.5f, 0.0f,  // v2
                    1.0f, 0.5f, 0.0f,  // v3
            };

    private final short[] mIndicesData =
            {
                    0, 1, 2,
                    0, 2, 3,
            };

    private final float[] mTexturePosiontData =
            {
                    0.0f, 0.0f,
                    0.0f, 1.0f,
                    1.0f, 1.0f,
                    1.0f, 0.0f
            };

    private Context mContext;
    private int mBlendProgram;
    private int mWidth, mHeight;
    private FloatBuffer mVertices;
    private FloatBuffer mTextureBuffer;
    private ShortBuffer mIndices;
    private int aBlendPosition, aBlendTexturePosition, uBlendTextureUnit;

    private int[] mVBOIds = new int[4];
    private int mBlendTexture;

    public Watermark(Context context) {
        mContext = context;
        mVertices = ByteBuffer.allocateDirect(mVerticesData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mVertices.put(mVerticesData).position(0);

        mTextureBuffer = ByteBuffer.allocateDirect(mTexturePosiontData.length * BYTES_PER_FLOAT)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mTextureBuffer.put(mTexturePosiontData).position(0);

        mIndices = ByteBuffer.allocateDirect(mIndicesData.length * BYTES_PER_SHORT)
                .order(ByteOrder.nativeOrder()).asShortBuffer();
        mIndices.put(mIndicesData).position(0);
    }

    public void onSurfaceCreated() {
        loadWatermark();
    }

    private void loadWatermark() {
        String vShaderStr = ESShader.readShader(mContext, "blendfunc_vertexShader.glsl");
        String fShaderStr = ESShader.readShader(mContext, "blendfunc_fragmentShader.glsl");

        // Load the shaders and get a linked program object
        mBlendProgram = ESShader.loadProgram(vShaderStr, fShaderStr);

        aBlendPosition = glGetAttribLocation(mBlendProgram, "aBlendPosition");
        aBlendTexturePosition = glGetAttribLocation(mBlendProgram, "aBlendTexturePosition");
        uBlendTextureUnit = glGetUniformLocation(mBlendProgram, "uBlendTextureUnit");

        glGenBuffers(3, mVBOIds, 0);
        Log.e(TAG, "0: " + mVBOIds[0] + ", 1: " + mVBOIds[1] + ", 2: " + mVBOIds[2]);

        // mVBOIds[0] - used to store vertex position
        mVertices.position(0);
        glBindBuffer(GL_ARRAY_BUFFER, mVBOIds[0]);
        glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mVerticesData.length,
                mVertices, GL_STATIC_DRAW);

        mTextureBuffer.position(0);
        glBindBuffer(GL_ARRAY_BUFFER, mVBOIds[1]);
        glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mTexturePosiontData.length,
                mTextureBuffer, GL_STATIC_DRAW);

        // mVBOIds[2] - used to store element indices
        mIndices.position(0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBOIds[2]);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, BYTES_PER_SHORT * mIndicesData.length, mIndices, GL_STATIC_DRAW);

        mBlendTexture = TextureHelper.loadTexture(mContext, R.mipmap.watermark);
    }

    public void onSurfaceChanged(GL10 gl, int width, int height) {
        mWidth = width;
        mHeight = height;
    }

    public void onDrawFrame() {
        glViewport(0, 0, 288, 144);
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE, GL_ZERO);
        drawWatermark();
        glDisable(GL_BLEND);
    }

    private void drawWatermark() {
        glUseProgram(mBlendProgram);
        glBindBuffer(GL_ARRAY_BUFFER, mVBOIds[0]);
        glEnableVertexAttribArray(aBlendPosition);
        glVertexAttribPointer(aBlendPosition, POSITION_COMPONENT_SIZE,
                GL_FLOAT, false, 0, 0);

        glBindBuffer(GL_ARRAY_BUFFER, mVBOIds[1]);
        glEnableVertexAttribArray(aBlendTexturePosition);
        glVertexAttribPointer(aBlendTexturePosition, 2,
                GL_FLOAT, false, 0, 0);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, mBlendTexture);
        glUniform1i(mBlendTexture, 0);

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVBOIds[2]);

        glDrawElements(GL_TRIANGLES, mIndicesData.length, GL_UNSIGNED_SHORT, 0);

        glDisableVertexAttribArray(aBlendPosition);
        glDisableVertexAttribArray(aBlendTexturePosition);

        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
    }
}

     其中大部分都是和Map基本相同的,因爲我們不需要color顏色了,所以把顏色相關的都刪除掉,我們需要的是水印的頂點座標、水印的紋理座標、水印的索引屬性,所以VBO的數組長度爲3,drawWatermark方法中的邏輯和Map中也基本相同,都是使能頂點屬性,然後賦值,綁定紋理uniform,最後繪製。

     有一個不同的,就是在onDrawFrame方法中的第一行調用glViewport(0, 0, 288, 144)。我們最開始時候已經講過,glViewport API可以指定我們當前繪製的目標窗口位置和大小,所以我們可以使用它來控制水印繪製的位置。另外,因爲我們是兩個對象,所以兩個頂點着色器、兩個片段着色器,分別組合生成兩個Program對象,所以它們倆是不相干的,就是說完全可以取相同名字、相同類型的變量,也都可以正常使用,而且glActiveTexture(GL_TEXTURE0)激活紋理時,都是激活0號紋理,因爲它們不在同一個Program中,所以不會衝突,也不會出錯。

     最後,我們把Map中頂點數組的值改爲如下來看看效果:

    private final float[] mVerticesData =
            {
                    -1.0f, 0.5f, 0.0f, // v0
                    -1.0f, -0.5f, 0.0f, // v1
                    1.0f, -0.5f, 0.0f,  // v2
                    1.0f, 0.5f, 0.0f,  // v3
            };

     對應的效果如下:

     可以看到,左下角的水印沒了,看不到了。

     我們把頂點數組還原回去,把glBlendFunc混合接口註釋掉:

    @Override
    public void onDrawFrame(GL10 gl) {
        glClear(GL_COLOR_BUFFER_BIT);
        mMap.onDrawFrame();
//        glEnable(GL_BLEND);
//        glBlendFunc(GL_ONE, GL_ONE);
        mWatermark.onDrawFrame();
//        glDisable(GL_BLEND);
    }

     效果如下:

     左下角的水印背景是黑色的,反正就是各種不對,所以我們必須要掌握要點,才能保證水印能正確的畫出來。好了,本節的內容就講這麼多,大家可以下載代碼自己去試。

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