天空著色器

天空著色器是一種特殊類型的著色器,用於繪製天空背景及更新用於基於影像照明(IBL)的輻射立方體貼圖。天空著色器僅有一個處理函式,即 sky() 函式。

天空著色器會在三個地方被使用。

  • 首先,當你在場景中選擇使用天空作為背景時,天空著色器會用來繪製天空。

  • 其次,當使用天空作為環境光或反射來源時,天空著色器會用於更新輻射立方體貼圖。

  • 第三,天空著色器會用於算繪較低解析度的子通道,這些結果可用於高解析度背景或立方體貼圖通道。

總結來說,天空著色器每幀最多可執行六次。但實際上通常會少很多,因為輻射立方體貼圖不需每幀更新,而且不是所有子通道都會被用到。你可以透過檢查 AT_*_PASS 布林值來根據呼叫位置調整著色器行為。例如:

shader_type sky;

void sky() {
    if (AT_CUBEMAP_PASS) {
        // Sets the radiance cubemap to a nice shade of blue instead of doing
        // expensive sky calculations
        COLOR = vec3(0.2, 0.6, 1.0);
    } else {
        // Do expensive sky calculations for background sky only
        COLOR = get_sky_color(EYEDIR);
    }
}

當用天空著色器繪製背景時,會對螢幕上所有未被遮蔽的片段呼叫著色器。但對於背景的子通道,會對該子通道每個像素呼叫一次著色器。

當使用天空著色器來更新輻射立方體貼圖時,會針對立方體貼圖內每個像素執行一次著色器。但只有當輻射立方體貼圖需要更新時才會呼叫著色器。只要著色器的任何參數有更新時,立方體貼圖就需要重新計算。例如,如果著色器中有使用 TIME,那麼輻射立方體貼圖將每幀都重新計算。以下情況會強制觸發輻射立方體貼圖更新:

  • 使用了 TIME

  • 使用了 POSITION 並且相機位置發生變化。

  • 如果有使用任何 LIGHTX_* 屬性,且任何 DirectionalLight3D 發生變化。

  • 如果著色器中的任何 uniform 參數有更動。

  • 若螢幕尺寸被調整,且有使用到任何子通道。

盡量避免不必要地更新輻射立方體貼圖。如果確實需要每幀更新輻射立方體貼圖,請確認你的 Sky 處理模式 已設為 PROCESS_MODE_REALTIME

請注意,處理模式 僅影響輻射立方體貼圖的算繪。可見的天空仍會針對每個像素呼叫片段著色器。如果你的片段著色器很複雜,這會造成顯著的算繪負擔。如果天空是靜態的(如前述條件),或變化速度很慢,其實無需每幀都執行完整片段著色器。你可以將完整天空算繪到輻射立方體貼圖,再在顯示可見天空時直接讀取此貼圖。若天空完全靜態,只需要算繪一次即可。

以下程式碼會將完整天空算繪到輻射立方體貼圖,並在顯示可見天空時從該貼圖讀取:

shader_type sky;

void sky() {
    if (AT_CUBEMAP_PASS) {
        vec3 dir = EYEDIR;

        vec4 col = vec4(0.0);

        // Complex color calculation

        COLOR = col.xyz;
        ALPHA = 1.0;
    } else {
        COLOR = texture(RADIANCE, EYEDIR).rgb;
    }
}

這樣一來,複雜的計算只會發生在立方體貼圖階段。你可以調整 天空處理模式輻射貼圖尺寸,在效能與視覺品質間取得理想平衡。

算繪模式

子通道可讓你以較低解析度進行較昂貴的計算,加速著色器運算。例如,以下程式碼會以比天空其餘部分更低的解析度算繪雲層:

shader_type sky;
render_mode use_half_res_pass;

void sky() {
    if (AT_HALF_RES_PASS) {
        // Run cloud calculation for 1/4 of the pixels
        vec4 color = generate_clouds(EYEDIR);
        COLOR = color.rgb;
        ALPHA = color.a;
    } else {
        // At full resolution pass, blend sky and clouds together
        vec3 color = generate_sky(EYEDIR);
        COLOR = color + HALF_RES_COLOR.rgb * HALF_RES_COLOR.a;
    }
}

算繪模式

說明

use_half_res_pass

允許著色器寫入及存取半解析度通道。

use_quarter_res_pass

允許著色器寫入及存取四分之一解析度通道。

disable_fog

啟用後,霧效將不會影響天空。

內建變數

標記為 in 的值為唯讀。標記為 out 的值可寫入,但不一定有合理初始值。取樣器(sampler)無法寫入,因此未標記。

全域內建變數

全域內建變數可於任何地方使用,包括自訂函式中。

有 4 個 LIGHTX 燈光,可分別以 LIGHT0LIGHT1LIGHT2LIGHT3 存取。

內建變數

說明

in float TIME

自引擎啟動以來的全域時間(秒)。每 3,600 秒會重設(可透過 rollover 設定變更)。該值會受到 time_scale 影響,但不受暫停影響。如果你需要不受 time_scale 影響的 TIME,請自訂 全域著色器 uniform 並於每幀更新。

in vec3 POSITION

相機在世界座標中的位置。

samplerCube RADIANCE

輻射度立方體貼圖。僅能於背景通道時讀取。使用前請檢查 !AT_CUBEMAP_PASS 條件。

in bool AT_HALF_RES_PASS

若目前算繪到半解析度通道時,為 true

in bool AT_QUARTER_RES_PASS

若目前算繪到四分之一解析度通道時,為 true

in bool AT_CUBEMAP_PASS

若目前算繪到輻射度立方體貼圖時,為 true

in bool LIGHTX_ENABLED

LIGHTX 在場景中且可見則為 true。若為 false,其他燈光屬性可能是無效值。

in float LIGHTX_ENERGY

LIGHTX 的能量乘數。

in vec3 LIGHTX_DIRECTION

LIGHTX 面向的方向。

in vec3 LIGHTX_COLOR

LIGHTX 的顏色。

in float LIGHTX_SIZE

天空中 LIGHTX 的角直徑,以弧度表示。作為參考,太陽在地球上看起來約為 0.0087 弧度(0.5 度)。

in float PI

常數 PI(3.141592),為圓周長與直徑的比值,也是一半圈的弧度數。

in float TAU

常數 TAU(6.283185),等於 PI * 2,也是一整圈的弧度數。

in float E

常數 E(2.718281),歐拉數,也是自然對數的底數。

天空內建變數

內建變數

說明

in vec3 EYEDIR

目前像素的正規化方向。可作為程序效果的基本方向參考。

in vec2 SCREEN_UV

目前像素在螢幕上的 UV 座標。可用於將紋理貼圖到全螢幕。

in vec2 SKY_COORDS

球面 UV。用於將全景紋理貼圖到天空。

in vec4 HALF_RES_COLOR

來自半解析度通道對應像素的顏色值。使用線性過濾。

in vec4 QUARTER_RES_COLOR

來自四分之一解析度通道對應像素的顏色值。使用線性過濾。

out vec3 COLOR

輸出顏色。

out float ALPHA

輸出的 alpha 值,只能在子通道中使用。

out vec4 FOG