跳至內容

GLSL程式設計/GLUT/透明度

來自Wikibooks,開放世界的開放書籍

本教程涵蓋了使用Blender中的GLSL著色器對片段進行混合(即合成)。它假設您熟悉前面和背面概念,如剖面教程中所述。

更具體地說,本教程是關於渲染透明物體,例如透明玻璃、塑膠、織物等。(更嚴格地說,這些實際上是半透明物體,因為它們不需要完全透明。)透明物體允許我們透過它們看到;因此,它們的顏色與它們後面任何物體的顏色“混合”。

“OpenGL ES 2.0 管線”的描述中所述,片段著色器為每個片段計算一個RGBA顏色(即gl_FragColor中的紅色、綠色、藍色和alpha分量)(除非片段被丟棄)。然後在“逐片段操作”中處理這些片段。其中一個階段是混合階段,它將片段的顏色(如gl_FragColor中指定的那樣),稱為“源顏色”,與幀緩衝區中已有的對應畫素的顏色,稱為“目標顏色”(因為結果混合顏色的“目標”是幀緩衝區)結合起來。

混合是一個固定功能階段,即您可以自定義它,但不能對其進行程式設計。自定義的方式是指定一個混合方程。您可以將混合方程視為對結果RGBA顏色的定義

vec4 result = SrcFactor * gl_FragColor + DstFactor * pixel_color;

其中pixel_color是當前幀緩衝區中的RGBA顏色,result是混合結果,即混合階段的輸出。SrcFactorDstFactor是可自定義的RGBA顏色(型別為vec4),它們與片段顏色和畫素顏色按元件相乘。SrcFactorDstFactor的值在Blender的Python API中使用此函式指定

bge.types.KX_BlenderMaterial.setBlending({SrcFactor的程式碼},{DstFactor的程式碼})

這兩個因子的可能程式碼在下面的表格中進行了總結(另請參見OpenGL文件

程式碼 結果因子(SrcFactorDstFactor
GL_ONE vec4(1.0)
GL_ZERO vec4(0.0)
GL_SRC_COLOR gl_FragColor
GL_SRC_ALPHA vec4(gl_FragColor.a)
GL_DST_COLOR pixel_color
GL_DST_ALPHA vec4(pixel_color.a)
GL_ONE_MINUS_SRC_COLOR vec4(1.0) - gl_FragColor
GL_ONE_MINUS_SRC_ALPHA vec4(1.0 - gl_FragColor.a)
GL_ONE_MINUS_DST_COLOR vec4(1.0) - pixel_color
GL_ONE_MINUS_DST_ALPHA vec4(1.0 - pixel_color.a)

“向量和矩陣運算”中所述,vec4(1.0)只是vec4(1.0, 1.0, 1.0, 1.0)的簡寫方式。還要注意,混合方程中所有顏色和因子的所有分量都限制在0到1之間。

Alpha混合

[編輯 | 編輯原始碼]

混合方程的一個具體示例稱為“alpha混合”。在OpenGL中,它是這樣指定的

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

這將SrcFactor設定為vec4(gl_FragColor.a),將DstFactor設定為vec4(1.0 - gl_FragColor.a)。因此混合方程變為

vec4 result = vec4(gl_FragColor.a) * gl_FragColor + vec4(1.0 - gl_FragColor.a) * pixel_color;

這使用gl_FragColor的alpha分量作為不透明度。即,片段顏色越不透明,其不透明度和alpha分量越大,因此混合到結果中的片段顏色越多,幀緩衝區中畫素的顏色越少。完全不透明的片段顏色(即alpha分量為1)將完全替換畫素顏色。

此混合方程有時被稱為“over”運算,即“gl_FragColor over pixel_color”,因為它對應於在畫素顏色頂部放置具有特定不透明度的片段顏色的半透明層。(想象一下,在另一種顏色的東西上面放一層彩色玻璃或彩色半透明塑膠。)

由於alpha混合的流行,顏色的alpha分量通常被稱為不透明度,即使沒有使用alpha混合。此外,請注意,在計算機圖形學中,顏色透明度的常見正式定義是該顏色的1 - 不透明度

預乘Alpha混合

[編輯 | 編輯原始碼]

alpha混合有一個重要的變體:有時片段的顏色已經將其alpha分量預乘到顏色分量中。(您可以將其視為已包含增值稅的價格。)在這種情況下,alpha不應該再次相乘(增值稅不應該再次新增),正確的混合是

glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)

對應於

vec4 result = vec4(1.0) * gl_FragColor + vec4(1.0 - gl_FragColor.a) * pixel_color;

疊加混合

[編輯 | 編輯原始碼]

混合方程的另一個示例是

glBlendFunc(GL_ONE, GL_ONE)

這對應於

vec4 result = vec4(1.0) * gl_FragColor + vec4(1.0) * pixel_color;

它只是將片段顏色新增到幀緩衝區中的顏色。請注意,alpha分量根本沒有使用;儘管如此,此混合方程對於許多型別的透明效果非常有用;例如,當粒子系統表示火焰或其他透明併發出光的物體時,它通常用於粒子系統。在與次序無關的透明度教程中更詳細地討論了疊加混合。

著色器程式碼

[編輯 | 編輯原始碼]

這是一個簡單的著色器,它使用alpha混合和不透明度為0.3的綠色。

attribute vec3 v_coord;
uniform mat4 m, v, p;

void main()
{
  vec4 v_coord4 = vec4(v_coord, 1.0);
  mat4 mvp = p*v*m;
  gl_Position = mvp * v_coord4;
}
void main()
{
  gl_FragColor = vec4(0.0, 1.0, 0.0, 0.3); 
  // fourth component (alpha) is important: 
  // this is semitransparent green
}
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

通常,在所有不透明網格渲染完成後,應該使用停用寫入深度緩衝區的設定渲染半透明網格。這樣,任何不透明物體(應該透過半透明物體發光)的片段都不會因為前面的半透明物體而深度測試失敗。(深度測試在“逐片段操作”部分進行了討論。)

您可能需要從後到前對三角形進行渲染。這是必要的,因為使用停用寫入深度緩衝區的設定渲染透明網格並不總是能解決所有問題。如果片段混合的順序無關緊要,它可以完美地工作;例如,如果片段顏色只是使用疊加混合新增到幀緩衝區中的畫素顏色,則片段混合的順序並不重要;請參見與次序無關的透明度教程。但是,對於其他混合方程,例如alpha混合,結果將根據片段混合的順序而有所不同。(如果您透過幾乎不透明的綠色玻璃觀察幾乎不透明的紅色玻璃,您將主要看到綠色,而如果您透過幾乎不透明的紅色玻璃觀察幾乎不透明的綠色玻璃,您將主要看到紅色。同樣,將幾乎不透明的綠色混合到幾乎不透明的紅色上將與將幾乎不透明的紅色混合到幾乎不透明的綠色上不同。)為了避免偽影,Z透明度從後到前渲染三角形。另一種方法是使用疊加混合或(預乘)alpha混合和較小的不透明度(在這種情況下,目標因子DstFactor接近1,因此alpha混合接近疊加混合)。

包含背面

[編輯 | 編輯原始碼]

前面的著色器沒有渲染物體的“內部”。但是,由於我們可以透過透明物體的外部看到,因此我們也應該渲染內部。如剖面教程中所述,可以透過停用背面剔除來渲染內部。但是,如果我們只是停用剔除,我們可能會遇到麻煩:如上所述,透明片段渲染的順序通常很重要,但在沒有任何剔除的情況下,來自內部和外部的重疊三角形可能會以隨機順序渲染,這會導致惱人的渲染偽影。因此,如上一節所述,啟用三角形排序非常重要。

恭喜你完成了本教程!關於渲染透明物體的一個有趣之處在於,它不僅僅是混合的問題,還需要了解背面剔除和深度緩衝區。具體來說,我們已經瞭解了

  • 什麼是混合以及如何在OpenGL中指定它。
  • 如何渲染包含透明和不透明物體的場景。

進一步閱讀

[編輯 | 編輯原始碼]

如果你還想了解更多



< GLSL程式設計/GLUT

除非另有說明,否則本頁面上的所有示例原始碼均歸入公有領域。
返回OpenGL程式設計 - 光照章節 返回GLSL程式設計 - GLUT章節
華夏公益教科書