本文是關(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效果:

在OpenGL ES 中物體透明技術(shù)通常被叫做混合(Blending)。透明是非純色而是混合色,這種顏色來自于不同濃度的自身顏色和它后面的物體顏色。一個(gè)物體的透明度,被定義為它的顏色的alpha值,alpha顏色值是一個(gè)顏色向量的第四個(gè)元素。
實(shí)現(xiàn)效果
接下來我們用OpenGL ES 實(shí)現(xiàn)類似的效果,效果圖如下所示:

基礎(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);
}
效果圖

- 兩張圖片進(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);
}
效果圖:

- 歌詞顯示效果測試。自己通過編寫著色器進(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);
}
效果圖:

常見問題
- 由于Xcode在打包的時(shí)候會對png圖片進(jìn)行優(yōu)化處理,因此我們在使用libpng讀取讀片的時(shí)候可能出現(xiàn)以下錯(cuò)誤:
libpng error: CgBI: unhandled critical chunk
解決辦法是關(guān)閉Xcode對png的優(yōu)化處理
