Up to date

This page is up to date for Godot 4.2. If you still find outdated information, please open an issue.

第一個遊戲

前言

著色器是在 GPU 上運作,用來渲染圖像的一種特殊程式。現代渲染都是通過著色器實作的。若想瞭解關於著色器更詳細的說明,請查看:ref:著色器是什麼 <doc_introduction_to_shaders>

本教學將重點介紹實際編寫著色器程式的各個方面, 引導您走過使用頂點和片段函式編寫著色器的整個流程. 本教學面向著色器.

備註

如果你在著色器方面有一定的經驗,只是想知道著色器在 Godot 中是如何運作的,請參閱:ref:著色器參考 <toc-shading-reference>

設定

對象,而 Spatial 著色器則用於繪製所有 3D 物件。

要使用著色器,它必須要被附加到一個 Material。若要使用同一材質繪製多個物件,該材質必須附加到每個物件上。

所有繼承自 CanvasItem 的物件都有一個材質屬性, 這包含所有的 GUI 元素, 精靈, 方塊地圖, 2D 網格實例 等等. 它們同時也可以選擇性地繼承其父類的材質. 如果你有要使用同一材質的很多節點, 這個功能就可以派上用場.

首先,建立一個 Sprite2D 節點。 您可以使用任何 CanvasItem,只要它繪製到畫布上即可,因此在本教學中我們將使用 Sprite2D,因為它是最容易開始繪製的 CanvasItem。

在“屬性面板”中,點擊“Texture”旁邊寫著“[空]”的地方然後選擇“載入”,再選擇“Icon.png”。對於新的專案,這個就是 Godot 的圖示。你現在就會在視口中看到這個圖示。

接下來,在“屬性面板”下的 CanvasItem 部分中,在“Material”旁點擊並選擇“新建 ShaderMaterial”。這會建立一個新的材質資源。然後點擊新出現的球體。Godot 目前還不知道你是要寫 CanvasItem 著色器還是 Spatial 著色器,它顯示 Spatial 著色器的輸出預覽,所以你看到的是預設的 Spatial 著色器的輸出。

點擊“Shader”旁邊的位置並選擇“新建 Shader”。最後,點擊新建立的著色器資源,就會打開著色器編輯器。你現在就已經準備好開始寫你的第一個著色器了。

你的第一個 CanvasItem 著色器

在Godot中, 所有的著色器第一行都是指定著色器型別的, 格式如下:

shader_type canvas_item;

因為我們正在編寫CanvasItem著色器, 所以我們在第一行中指定了 canvas_item. 我們所有的程式碼都會在這個宣告下面.

這一行告訴遊戲引擎要提供你哪些內建變數以及函式.

在Godot中, 你可以重寫三個函式來控制著色器的運作, 它們是 vertex (頂點函式), fragment (片段函式)和 light (光照函式). 本教學會引導你寫出一個包含頂點和片段函式的著色器. 因為光照函式比另外兩個函式要複雜非常多, 所以在這裡不會進行講解.

你的第一個片段函式

片段函式對精靈中的每個像素進行操作, 並且決定這個像素應該是什麼顏色的.

它們限制在精靈覆蓋的那些像素中, 這也就意味著, 你無法用片段函式來實作例如在精靈周圍加邊框的事情.

最基礎的片段函式僅僅給每個像素賦予一個顏色.

我們向內建變數 COLOR 中寫入一個 vec4 來做到這點. vec4 是建立一個四維向量的簡寫形式. 若想進一步瞭解向量, 請參閱 向量數學教學. COLOR 變數既是片段函式的一個輸入, 同時也是它的最終輸出.

void fragment(){
  COLOR = vec4(0.4, 0.6, 0.9, 1.0);
}
../../../_images/blue-box.png

恭喜你!你成功在 Godot 中寫出了你的第一個著色器。

接著, 我們來討論更複雜的事情.

你可以使用片段函式中的很多輸入來計算 COLOR, UV 就是其中的一個. 你的精靈指定了UV座標(在你不知情的情況下!), 而它們告訴著色器, 對於網格的每個部分從紋理的何處讀取資訊.

在片段函式中你只能從 UV 中讀取, 但是你可以在其他函式中使用, 或者直接對 COLOR 賦值.

UV 取值在0-1之間, 從左到右, 由上到下.

../../../_images/iconuv.png
void fragment() {
  COLOR = vec4(UV, 0.5, 1.0);
}
../../../_images/UV.png

使用內建變數 TEXTURE

當你想調整 Sprite 中的顏色時,你不能像下面的程式碼那樣手動修改紋理中的顏色。

void fragment(){
  //this shader will result in an all white rectangle
  COLOR.b = 1.0;
}

預設的片段函式從紋理中讀取並顯示它。當你覆蓋了預設的片段函式,你就失去了這個功能,所以你必須自己實作它。你使用 texture 函式從紋理中讀取。Sprite 等節點有一個專用的紋理變數,可以在著色器中使用 TEXTURE 存取。可以用它與 UVtexture 一起繪製 Sprite。

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = 1.0; //set blue channel to 1.0
}
../../../_images/blue-tex.png

Uniform 輸入

Uniform 輸入是用來向著色器傳遞資料的,這些資料在整個著色器中都是一致的。

你可以像這樣通過在著色器頂部定義來使用 Uniform 值:

uniform float size;

用法的更多詳情請參見:ref:著色語言文件 <doc_shading_language>

新增一個 Uniform 值來改變 Sprite 中藍色量。

uniform float blue = 1.0; // you can assign a default value to uniforms

void fragment(){
  COLOR = texture(TEXTURE, UV); //read from texture
  COLOR.b = blue;
}

現在你可以在編輯器中改變Sprite的藍色量. 回頭看看你建立著色器的地方的屬性面板. 你應該看到一個叫做 "Shader Param" 的部分. 展開這個部分, 你會看到你剛剛宣告的Uniform. 如果你在編輯器中改變這個值, 它將覆蓋你在著色器中提供的預設值.

程式碼與著色器的互動

你可以使用在節點的材質資源上呼叫的函式 set_shader_param() , 從程式碼中改變Uniform. 對於一個Sprite節點, 可以使用下面的程式碼來設定 blue Uniform.

var blue_value = 1.0
material.set_shader_parameter("blue", blue_value)

注意,uniform值的名稱是一個字串. 字串必須與它在著色器中的書寫方式完全配對, 包括拼寫和大小寫.

你的第一個頂點函式

現在我們有了一個片段函式, 我們再寫一個頂點函式.

使用頂點函式計算螢幕上每個頂點的結束位置.

頂點函式中最重要的變數是 VERTEX。它最初指定的是模型中的頂點座標,但你也會通過往裡面寫值來決定把這些頂點畫到哪裡。VERTEX 是一個 vec2,最初使用的是局部空間(即與相機、視口、父節點無關)。

您可以通過直接調整 VERTEX 來偏移頂點。

void vertex() {
  VERTEX += vec2(10.0, 0.0);
}

與內建變數 TIME 相結合,可用於簡單的動畫。

void vertex() {
  // Animate Sprite2D moving in big circle around its location
  VERTEX += vec2(cos(TIME)*100.0, sin(TIME)*100.0);
}

結論

著色器的核心, 如你所見, 是計算 VERTEXCOLOR. 你可以制定更複雜的數學策略來給這些變數賦值.

一些更高級的著色器教學可以給你啟發, 如 Shadertoy著色器之書 .