工序升級!在 Cocos Creator 裡畫一個完美的矩形

在認識了 WebGL 的基礎工作原理,以及如何利用 WebGL 繪製後,接下來的幾章就帶大家瞭解一下這套流程如何

應用到 Cocos Creator 3.x

之前我們利用 WebGL 繪製出了一個矩形,本章先來看看要如何在

Cocos Creator 3.x 繪製同樣的矩形

流程概述

Cocos Creator 3。x 為了方便使用者使用和定製,對內部功能做了多層封裝,使用者根據需求自行組裝即可。因此,我們只需要組裝相關的部分。根據矩形繪製流程,可以將內容很直接地拆分成以下幾個部分:

第一是資料準備部分,也就是提供頂點資料。

在之前的案例裡,我們是直接提供了固定資料。Cocos Creator 3。x 也有多個地方會提供頂點資料,例如:2D 的渲染元件(Sprite、Graphics 等),3D 的模型元件(MeshRenderer、SkinnedMeshRendere 等)等。當然,使用者也可以自定義頂點資料,由於這部分涉及到渲染管線以及引擎底層,超出本章介紹範圍,這裡就不再過多贅述。

第二是畫布清除階段,這部分跟相機有關。

因為遊戲場景往往是由很多物件構造而成,但是實際呈現的畫面只有其中一小部分,呈現的部分就是相機照射的部分。由於我們的螢幕畫布只有一塊,因此,由相機決定是否要擦除之前的內容重新繪製,或者在原有內容的基礎上繼續繪製。

第三是著色指令部分,這部分就類似於頂點/片元文字的編寫。

在 Cocos Creator 3。x 裡透過 Cocos Effect 來實現。

在這裡我會用最基礎繪圖元件 Graphics 進行繪製,帶大家瞭解一下這套流程。

首先,新建場景,在層級管理器上建立 Canvas 節點,並在 Canvas 節點下建立一個 Graphics 節點。建立指令碼 Draw 並掛載在 Graphics 節點身上並呼叫 Graphics 繪圖相關介面。在這裡,同樣繪製一個矩形:

import { _decorator, Component, Node, Graphics } from ‘cc’;

const { ccclass, property } = _decorator;

@ccclass(‘Draw’)

export class Draw extends Component {

start () {

const g = this。getComponent(Graphics);

g。fillRect(0, 0, 200, 150);

}

}

接著,執行預覽,就可以看到繪製了一個純白色的矩形。

工序升級!在 Cocos Creator 裡畫一個完美的矩形

在這個過程中一共經歷了以下幾個階段:

工序升級!在 Cocos Creator 裡畫一個完美的矩形

接下來說說開發者需要關心的幾個部分。所有之前關於 gl。xxx 的部分,都在底層直接處理完了,所以並不需要我們手動去執行,除非整個流程我們都需要使用自定義的。

頂點資料

在 Creator 中,頂點座標起源於模型空間,最終需要轉換到螢幕空間,這過程需要經歷以下幾個步驟:

工序升級!在 Cocos Creator 裡畫一個完美的矩形

Local Space 區域性座標,也可以稱之為模型座標。可以理解為就是相對於父節點的座標。

World Space 世界座標。世界座標是一個很大的空間範圍,相對於世界原點。透過模型座標結合模型矩陣得出。

View Space 觀察座標。可以理解為將世界座標轉換到相機空間的座標,轉換後的值是相對於相機原點。透過世界座標結合觀察矩陣得出。

Clip Space 裁剪座標。也就是將觀察座標處理到 -1。0 ~ 1。0 的範圍,也就是我們在 WebGL 裡提供的標準裝置化座標,最終剔除超出 -1 ~ 1 的座標。透過觀察座標結合投影矩陣得出。

Screen Space 螢幕座標。這個過程其實就是將 -1。0 ~ 1。0 範圍的座標轉換到 gl。viewport 所定義的座標範圍內。最後變換出來的座標會送到光柵器,轉換成片段。

因此,根據資料型別,最終都需要轉換成裁剪座標提供。

Graphics 提供的是模型座標。

喜歡探究的同學可以檢視引擎底層 graphics。ts 裡的 activeMode、_uploadData 和 graphics-assemler 部分,這裡就處理了頂點資料快取建立、繪製資料收集,繫結等步驟。

這裡順帶提一下

標準裝置化座標和螢幕座標之間的關係。

標準化裝置座標是 x 軸向右,y 軸向上,x 和 y 的取值都是從 -1~1,在這個範圍內的頂點可見,否則都不可見。螢幕座標是 x 軸向右,y 軸向下,x 和 y 的取值範圍都是從 0 對應到螢幕寬高,在矩陣變換的最後一步,就是將標準化裝置座標轉換到螢幕座標後上屏顯示。

工序升級!在 Cocos Creator 裡畫一個完美的矩形

Cocos Effect

有了頂點資料之後,對應的也需要編寫 Shader 文字,在 3。x 裡則對應 Cocos Effect。Cocos Effect 是一種基於 YAML 和 GLSL 的單原始碼嵌入式領域特定語言,YAML 部分宣告流程控制清單,GLSL 部分宣告實際的著色片段,這兩部分內容上相互補充,共同構成了一個完整的渲染流程描述。引擎會根據這份描述執行相對應的渲染程式。

Cocos Effect 無法單獨使用,需要搭配材質使用。

> 注意:如果使用 VSCode 編輯自定義 Effect。推薦在 VSCode 上搜索安裝 Cocos Effect 外掛,以便獲得程式碼高亮提示。

我們可以透過在編輯器的資源管理器面板處右鍵,選擇 Effect 建立 。effect 檔案即可。

YAML101

YAML 是一種序列化語言,也可以理解為是一種專注於寫配置檔案的語言。Cocos Creator 3。x 完全支援 YAML 1。2 標準的解析器。YAML 完全相容 Json 語法,所以 Json 也可以看做是 YAML 的子集。

YAML 是由 : 和空格分隔的鍵值組合。

所有的引號和逗號都可以省略

key1: 1

key2: unquoted string

// 注意:冒號後的空格不可省略

行首的空格縮排數量代表資料的層級

object1:

key1: false

object2:

key2: 3。14

key3: 0xdeadbeef

nestedObject:

key4: ‘quoted string’

以連字元 + 空格開頭,表示陣列元素

- 42

- “double-quoted string”

// 最終解析效果如下:

{[42, “double-quoted string”]}

YAML 中可以透過 & 定錨點,* 來引用

object1: &o1

key1: value1

object2:

key2: value2

key3: *o1

// 最終解析出來效果如下:

{

“object1”: {

“key1”: “value1”

},

“object2”: {

“key2”: “value2”,

“key3”: {

“key1”: “value1”

}

}

}

object1: &o1

key1: value1

key2: value2

object2:

key3: value3

// 最終解析效果如下:

{

“object1”: {

“key1”: “value1”,

“key2”: “value2”

},

“object2”: {

“key1”: “value1”,

“key2”: “value2”,

“key3”: “value3”

}

}

以上部分僅羅列出 Cocos Effect 開發中常見的寫法,更多寫法請參考 YAML 官網。

Cocos Effect 寫法

Cocos Effect 主要由兩部分構成:

一個是由 CCEffect 包裹的用 YAML 格式編輯的渲染流程清單。這裡羅列的內容主要涉及到與編輯器互動(供開發者在編輯器中進行資料調整)以及與 CCProgram 的資料互動。CCEffect 的核心是 Technique 渲染技術。

◇ Technique 渲染技術代表完成一個最終效果的方案。一個方案可以由一個或者多個 Pass 融合完成。

◇ 一個  Pass 就是一次 GPU 繪製,一般包括一次頂點著色器和片元著色器。

◇ 每個頂點/片元著色器都要申明各自的入口函式並提供返回值,此處入口函式的返回值會提供給執行平臺的入口函式。

另一個是由 CCProgram 包裹的基於 GLSL 300es 格式的著色器(shader)片段。

如果要繪製在文章開頭的目標矩形,內容可以如下:

CCEffect %{

techniques:

- name: opaque

passes:

- vert: unlit-vs:vert #此處的 vert 對應 CCProgram 的 vert,指向渲染平臺的入口函式。例如:WebGL 就是 main 函式。

frag: unlit-fs:frag

}%

CCProgram unlit-vs %{

precision highp float;

in vec4 a_position;

in vec4 a_color;

out vec4 v_color;

vec4 vert () {

v_color = a_color;

return a_position;

}

}%

CCProgram unlit-fs %{

precision highp float;

in vec4 v_color;

vec4 frag () {

return v_color;

}

}%

>注意:Cocos Creator 採用的是 GLSL es300 格式來編寫 Shader 片段。因此,後續所有的輸入輸出都使用 “in”、“out” 關鍵字而非舊版的 “attribute” 和 “varing”。當然,如果想繼續使用,仍然相容。

Graphics 元件在繪圖的時候,也採用了為自己量身定做的 Shader,這個在 Graphics。ts 內有跡可循。採用的是內建的 builtin-graphics。可以在

資源管理器面板->internal->effects

下找到。它的內容如下:

CCEffect %{

techniques:

- passes:

# 確定頂點和片元著色器。指向的是 CCProgram 定義的著色器。

- vert: vs:vert

frag: fs:frag

# blendState、rasterizerState 和 depthStencilState 是與測試與混合有關,可以暫時忽略

# 此處設定的原因是因為引擎內提供一套預設的測試與混合配置,但是在 2D 上由於當前設計暫時不需要深度,因此,需要手動修改相關配置

blendState:

targets:

- blend: true

blendSrc: one

blendDst: one_minus_src_alpha

blendSrcAlpha: one

blendDstAlpha: one_minus_src_alpha

rasterizerState:

cullMode: none

depthStencilState:

depthTest: false

depthWrite: false

}%

CCProgram vs %{

// 頂點著色器內所有浮點數精度定義

precision highp float;

// 引入 Creator 3。x 提供的程式碼片段

// cc-global 提供了投影矩陣和觀察矩陣

#include

// cc-local 提供了模型矩陣

#include

// 定義需要輸入的三個頂點屬性資料 a_position,a_color 和 a_dist。其中,a_dist 是為了抗鋸齒功能所需提供,可不用關心。

in vec3 a_position;

in vec4 a_color;

out vec4 v_color;

in float a_dist;

out float v_dist;

// 提供最終需要傳給頂點著色器 main 函式的資料值

vec4 vert () {

vec4 pos = vec4(a_position, 1);

// 將模型座標轉換成裁剪座標

pos = cc_matViewProj * cc_matWorld * pos;

v_color = a_color;

v_dist = a_dist;

return pos;

}

}%

CCProgram fs %{

// 低版本處理方案,可不用關心。

#pragma extension([GL_OES_standard_derivatives, __VERSION__

precision highp float;

in vec4 v_color;

in float v_dist;

// 提供最終需要傳給片元著色器 main 函式的資料值

vec4 frag () {

vec4 o = v_color;

// 此處也可不用關心

#if __VERSION__

#ifdef GL_OES_standard_derivatives

float aa = fwidth(v_dist);

#else

float aa = 0。05;

#endif

#else

float aa = fwidth(v_dist);

#endif

float alpha = 1。 - smoothstep(-aa, 0。, abs(v_dist) - 1。0);

o。rgb *= o。a;

o *= alpha;

return o;

}

}%

上下一對比,我們所需要的部分跟上方繪製矩形所需內容就差不多重合了。只是多了一層模型座標到裁剪座標的轉換。

最後,這裡還差最後一部分內容,就是關於相機。當我們建立 Canvas 節點的時候,可以看到預設會創建出一個 Camera 節點,Camera 節點上的 Camera 元件持有 ClearFlags,ClearColor 以及 Rect 這三個屬性,在 WebGL 就分別控制了 gl。viewport、gl。clear 和 gl。clearColor 部分。其中:

ClearFlags 的 SOLID_COLOR 模式,要求每幀清除螢幕內容

ClearColor 要求清除螢幕內容後預設填充什麼顏色

Rect 定義螢幕空間視口,xy 值限制在 -1~1,wh 值限制在 0~1

到此為止,我們大致瞭解了一個基礎繪圖元件 Graphics 的頂點資料的獲取途徑以及它的 Shader 內容。

下一章,我們來分析一下在基礎繪圖 Shader 基礎上添加了紋理貼圖處理的 2D 渲染元件 Sprite,並對它進行一些改造。

擴充套件知識

所有場景裡的物件都必須在相機的可視區域內,才能被最終渲染出來。相機的可視條件分為兩部分:

條件一:相機的 Visibility 包含節點的 Layer 值。比如:2D 相機的 Visibility 包含 UI_3D 和 UI_2D,節點的 layer 為 DEFAULT,那麼此節點就無法被 2D 相機渲染。

條件二:在條件一滿足的情況下,物體需要在相機照射的視距框內,物體才可被渲染。

最終,所有的內容經過渲染管線的處理成為一個“拍扁”後的 2D 畫素。此時不代表最終呈現的就是這樣的 2D 畫素,最後一個階段是 viewport。假設,此時將相機 Rect 的 w 分量改為 0。5,可以看看前後渲染內容的對比:

工序升級!在 Cocos Creator 裡畫一個完美的矩形

可以清楚的看到由於視口的調整,只有左半邊螢幕能夠呈現內容,因此,只有以相機原點為中心所照射的一半內容被呈現了出來。

Cocos Shader 基礎入門系列

···更新中···

該系列教程

影片版

已更新至 EP05

TAG: 座標Cocosvec4color頂點