OpenGL ES入門13-Blend混合

本文是關(guān)于OpenGL ES的系統(tǒng)性學(xué)習(xí)過程,記錄了自己在學(xué)習(xí)OpenGL ES時(shí)的收獲。
這篇文章的作用是學(xué)習(xí)的OpenGL ES 中的混合技術(shù)。物體透明技術(shù)通常被叫做混合(Blending)。
環(huán)境是Xcode8.1+OpenGL ES 2.0
目前代碼已經(jīng)放到github上面,OpenGL ES入門13-混合

歡迎關(guān)注我的 OpenGL ES入門專題

概述

混合技術(shù)在某些情況下特別有用。比如在iOS中做音樂播放器的時(shí)候,我們希望歌詞當(dāng)前的播放進(jìn)度與整句歌詞具有明顯的顏色區(qū)分。我們可以這樣做:

@interface MVMusicView : UILabel
@property (nonatomic, assign) CGFloat progress;
@end

@implementation MVMusicView

- (void)setProgress:(CGFloat)progress
{
    _progress = progress;
    [self setNeedsDisplay];
}

- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    
    [[UIColor greenColor] setFill];
    
    CGRect lyricRect = CGRectMake(0, 0, self.frame.size.width * _progress, self.frame.size.height);
    UIRectFillUsingBlendMode(lyricRect, kCGBlendModeSourceIn);
}

@end

Blend效果:

效果.png

在OpenGL ES 中物體透明技術(shù)通常被叫做混合(Blending)。透明是非純色而是混合色,這種顏色來自于不同濃度的自身顏色和它后面的物體顏色。一個(gè)物體的透明度,被定義為它的顏色的alpha值,alpha顏色值是一個(gè)顏色向量的第四個(gè)元素。

實(shí)現(xiàn)效果

接下來我們用OpenGL ES 實(shí)現(xiàn)類似的效果,效果圖如下所示:

效果圖.png

基礎(chǔ)知識

片段著色器運(yùn)行完成并且所有的測試都通過以后,混合方程才能自由執(zhí)行片段的顏色輸出,當(dāng)前它在顏色緩沖中(前面片段的顏色在當(dāng)前片段之前儲存)。源和目標(biāo)顏色會自動被OpenGL設(shè)置,而源和目標(biāo)因子可以讓我們自由設(shè)置。

  • 開啟混合
glEnable(GL_BLEND);
  • 關(guān)閉混合
glDisable(GL_BLEND);
  • 混合因子計(jì)算公式
result = source * Fsource + destination * Fdestination                                                                                           

參數(shù) source:源顏色向量。這是來自紋理的本來的顏色向量。
參數(shù) destination:目標(biāo)顏色向量。這是儲存在顏色緩沖中當(dāng)前位置的顏色向量。
參數(shù) Fsource:源因子。設(shè)置了對源顏色的alpha值影響。
參數(shù) Fdestination:目標(biāo)因子。設(shè)置了對目標(biāo)顏色的alpha影響。

  • 混合因子設(shè)置函數(shù)
void glBlendFunc (GLenum sfactor, GLenum dfactor);
  • 源(source)和目標(biāo)(destination)因子相關(guān)設(shè)置選項(xiàng):
選項(xiàng)
GL_ZERO 0
GL_ONE 1
GL_SRC_COLOR 源顏色向量
GL_ONE_MINUS_SRC_COLOR 1 ? 源顏色向量
GL_SRC_ALPHA 源的alpha值
GL_ONE_MINUS_SRC_ALPHA 1 ? 源的alpha值
GL_DST_ALPHA 目標(biāo)的alpha值
GL_ONE_MINUS_DST_ALPHA 1 ? 目標(biāo)的alpha值

實(shí)現(xiàn)過程

  • 加載png圖片。在這里主要是用到了兩張png的紋理,加載的時(shí)候分別加載,并生成紋理對象。
- (void)setupTexure
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"text" ofType:@"png"];
    
    pic_data pngData;
    read_png_file(path.UTF8String, &pngData);
    // 創(chuàng)建紋理
    if (pngData.flag > 0) {
        _texture = createTexture2D(GL_RGBA, pngData.width, pngData.height, pngData.rgba);
    }else {
        _texture = createTexture2D(GL_RGB, pngData.width, pngData.height, pngData.rgba);
    }
   
    if (pngData.rgba) {
        free(pngData.rgba);
        pngData.rgba = NULL;
    }
}

- (void)setupTexure1
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"wood" ofType:@"png"];
    
    pic_data pngData;
    read_png_file(path.UTF8String, &pngData);
    // 創(chuàng)建紋理
    if (pngData.flag > 0) {
        _texture1 = createTexture2D(GL_RGBA, pngData.width, pngData.height, pngData.rgba);
    }else {
        _texture1 = createTexture2D(GL_RGB, pngData.width, pngData.height, pngData.rgba);
    }
    
    if (pngData.rgba) {
        free(pngData.rgba);
        pngData.rgba = NULL;
    }
}
  • 開啟混合測試。這個(gè)測試主要是測試開啟混合以及相應(yīng)的混合因子對結(jié)果的影響。
- (void)render
{
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    // 激活紋理
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, _texture);
    glUniform1i(glGetUniformLocation(_program, "image"), 0);
    
    glDrawArrays(GL_TRIANGLES, 0, _vertCount);
    
    //將指定 renderbuffer 呈現(xiàn)在屏幕上,在這里我們指定的是前面已經(jīng)綁定為當(dāng)前 renderbuffer 的那個(gè),在 renderbuffer 可以被呈現(xiàn)之前,必須調(diào)用renderbufferStorage:fromDrawable: 為之分配存儲空間。
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

頂點(diǎn)著色器

attribute vec3 position;
attribute vec2 texcoord;

varying vec2 vTexcoord;

void main()
{
    gl_Position = vec4(position, 1.0);
    vTexcoord = texcoord;
}

片源著色器

precision mediump float;

uniform sampler2D image;
uniform sampler2D image1;

varying vec2 vTexcoord;

void main()
{
    gl_FragColor = texture2D(image, vTexcoord);
}

效果圖

開啟混合.png
  • 兩張圖片進(jìn)行混合測試。一張圖片先渲染到幀緩存中,然后再渲染另一張,并與幀緩存中的圖像混合。
- (void)render
{
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    
    // 第一個(gè)紋理關(guān)閉混合
    glDisable(GL_BLEND);
    
    // 激活紋理
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, _texture1);
    glUniform1i(glGetUniformLocation(_program, "image"), 1);
    
    glDrawArrays(GL_TRIANGLES, 0, _vertCount);
    
    // 渲染的時(shí)候開啟混合
    glEnable(GL_BLEND);
    glBlendFunc(GL_ONE, GL_ONE);
    
    // 激活紋理
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, _texture);
    glUniform1i(glGetUniformLocation(_program, "image"), 0);
    
    glDrawArrays(GL_TRIANGLES, 0, _vertCount);
    
    //將指定 renderbuffer 呈現(xiàn)在屏幕上,在這里我們指定的是前面已經(jīng)綁定為當(dāng)前 renderbuffer 的那個(gè),在 renderbuffer 可以被呈現(xiàn)之前,必須調(diào)用renderbufferStorage:fromDrawable: 為之分配存儲空間。
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

頂點(diǎn)著色器

attribute vec3 position;
attribute vec2 texcoord;

varying vec2 vTexcoord;

void main()
{
    gl_Position = vec4(position, 1.0);
    vTexcoord = texcoord;
}

片源著色器

precision mediump float;

uniform sampler2D image;
uniform sampler2D image1;

varying vec2 vTexcoord;

void main()
{
    gl_FragColor = texture2D(image, vTexcoord);
}

效果圖:

兩張紋理混合.png
  • 歌詞顯示效果測試。自己通過編寫著色器進(jìn)行相關(guān)混合。在這里由于文字的渲染比較復(fù)雜(如果想渲染文字可以使用FreeType2,它是一個(gè)免費(fèi)、開源、可移植且高質(zhì)量的字體引擎,被廣泛使用著。),因此用透明的png字體圖代替文字的渲染,即工程中的 text.png。
- (void)render
{
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
    
    // 激活紋理
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, _texture1);
    glUniform1i(glGetUniformLocation(_program, "image"), 1);
    
    // 激活紋理
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, _texture);
    glUniform1i(glGetUniformLocation(_program, "image1"), 0);
    
    glDrawArrays(GL_TRIANGLES, 0, _vertCount);
    
    //將指定 renderbuffer 呈現(xiàn)在屏幕上,在這里我們指定的是前面已經(jīng)綁定為當(dāng)前 renderbuffer 的那個(gè),在 renderbuffer 可以被呈現(xiàn)之前,必須調(diào)用renderbufferStorage:fromDrawable: 為之分配存儲空間。
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

頂點(diǎn)著色器

attribute vec3 position;
attribute vec2 texcoord;

varying vec2 vTexcoord;

void main()
{
    gl_Position = vec4(position, 1.0);
    vTexcoord = texcoord;
}

片源著色器。利用紋理坐標(biāo),使紋理坐標(biāo)s軸0.43左邊的為紅色字體,右邊的保持原來的顏色。

precision mediump float;

uniform sampler2D image;
uniform sampler2D image1;

varying vec2 vTexcoord;

void main()
{
    vec4 des = vec4(1.0, 0.0, 0.0, 1.0);
    vec4 src = texture2D(image1, vTexcoord);
    vec4 bld;
    if (vTexcoord.x <= 0.43) {
        bld = vec4(vec3(des), src.a);
    }else {
        bld = src;
    }
    
    gl_FragColor = bld * bld.a + vec4(1.0) * (1.0 - bld.a);
}

效果圖:

效果圖.png

常見問題

  • 由于Xcode在打包的時(shí)候會對png圖片進(jìn)行優(yōu)化處理,因此我們在使用libpng讀取讀片的時(shí)候可能出現(xiàn)以下錯(cuò)誤:
libpng error: CgBI: unhandled critical chunk

解決辦法是關(guān)閉Xcode對png的優(yōu)化處理

解決辦法.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容