遊戲圖形學與數學(Unity Shader篇)
前文我們提到了Unity Shader渲染的一個流程圖,大家還記得嘛。沒錯啦,就是這個東西。
那麽今天這篇文章,我們就帶大家來具體了解一下每一部份的含義和運用。
1️⃣頂點數據與頂點著色器
Unity 中的 Shader頂點數據 是指在 頂點著色器 中處理的頂點相關資訊,包括頂點的 位置 、 法線 、 顏色 、 UV座標 等。這些數據是用來描述三維模型表面的內容。
在 Unity 中, 頂點數據 是透過 頂點緩沖區(Vertex Buffer) 來傳遞的。在頂點著色器中,你可以定義輸入變量來接收這些頂點數據,並對其進行處理。一般情況下,你會用 頂點著色器 來進行頂點位置的變換、法線的計算以及其他頂點相關的操作。
以下是一個簡單的 Unity頂點著色器 範例,用來對頂點進行基本的變換:
Shader
"
Custom
/
Example
"
{
Properties
{
_MainTex
(
"
Texture
"
,
2
D
)
=
"
white
"
{}
}
SubShader
{
Tags
{
"
RenderType
"
=
"
Opaque
"
}
LOD
200
CGPROGRAM
#
pragma
surface
surf
Lambert
vertex
:
vert
sampler2D
_MainTex
;
struct
Input
{
float2
uv_MainTex
;
};
void
vert
(
inout
appdata_full
v
,
out
Input
o
)
{
// 頂點位置變換
v
.
vertex
.
xyz
+=
sin
(
_Time
.
y
+
v
.
vertex
.
y
)
*
0.1
;
// 將頂點位置傳遞給頂點著色器
o
.
uv_MainTex
=
v
.
texcoord
.
xy
;
}
void
surf
(
Input
IN
,
inout
SurfaceOutput
o
)
{
// 獲取紋理顏色
fixed4
c
=
tex2D
(
_MainTex
,
IN
.
uv_MainTex
);
// 輸出最終的顏色
o
.
Albedo
=
c
.
rgb
;
o
.
Alpha
=
c
.
a
;
}
ENDCG
}
FallBack
"
Diffuse
"
}
在這個例子中,
vert
函數接收了頂點位置資訊,並對其進行了簡單的變換。
surf
函數則接收了頂點著色器處理後的數據,並將紋理顏色輸出作為最終的結果。
具體演示步驟
1️⃣首先我們建立一個簡單的Unity工程,可以直接命名為Example之類的
2️⃣好了之後,我們直接建立專案。接著進入Unity,我們先在螢幕右鍵新建一個material的材質球和一個shader指令碼
3️⃣然後我們把剛才建立好的Shader指令碼拖拽給材質球
4️⃣雙擊開啟Shader指令碼,將我們上面的程式碼復制進來
5️⃣然後我們新建一個物體,並在材質球中加入一張圖片,再將得到的材質球賦給螢幕中的物體
6️⃣最後展示效果,我們就會得到一個這樣的物體
2️⃣曲面細分著色器
在 Unity 中, 曲面細分著色器 是一種特殊類別的著色器,它可以透過增加三角形數量來增加模型的細節和曲面的光滑度。這種著色器可以用來建立更加真實的模型表面,例如在角落、邊緣或者圓潤的表面。
Unity 中的曲面細分著色器是透過 Surface Shader 或者 Shader Graph(或者ASE) 來實作的。
使用Surface Shader實作曲面細分:
Shader
"
CCustom
/
Example
"
{
Properties
{
_MainTex
(
"
Texture
"
,
2
D
)
=
"
white
"
{}
_WireframeColor
(
"
Wireframe
Color
"
,
Color
)
=
(
0
,
0
,
0
)
_WireframeSmoothing
(
"
Wireframe
Smoothing
"
,
Range
(
0
,
10
))
=
1
_WireframeThickness
(
"
Wireframe
Thickness
"
,
Range
(
0
,
10
))
=
1
_TessellationUniform
(
"
Tessellation
Uniform
"
,
Range
(
1
,
64
))
=
1
}
SubShader
{
Tags
{
"
RenderType
"
=
"
Opaque
"
}
LOD
100
Pass
{
CGPROGRAM
#
pragma
vertex
tessVert
#
pragma
hull
hul
#
pragma
domain
dom
#
pragma
geometry
geo
#
pragma
fragment
frag
#
include
"
UnityCG
.
cginc
"
//頂點著色器數據
struct
vertexData
{
float4
vertex
:
POSITION
;
float4
tangent
:
TANGENT
;
float3
normal
:
NORMAL
;
float2
uv
:
TEXCOORD0
;
};
//細分著色器數據,一般與頂點著色器數據保持一致
struct
tessVertexData
{
float4
vertex
:
INTERNALTESSPOS
;
float4
tangent
:
TANGENT
;
float3
normal
:
NORMAL
;
float2
uv
:
TEXCOORD0
;
};
struct
TessellationFactors
{
float
edge
[
3
]
:
SV_TessFactor
;
float
inside
:
SV_InsideTessFactor
;
};
//幾何著色器數據
struct
v2g
{
float4
vertex
:
SV_POSITION
;
float4
tangent
:
TANGENT
;
float3
normal
:
NORMAL
;
float2
uv
:
TEXCOORD0
;
};
//片段著色器數據
struct
g2f
{
v2g
data
;
float3
barycentricCoordinates
:
TEXCOORD3
;
//三角形的重心座標
};
sampler2D
_MainTex
;
float4
_MainTex_ST
;
float3
_WireframeColor
;
float
_WireframeSmoothing
;
float
_WireframeThickness
;
float
_TessellationUniform
;
//將原始頂點數據傳遞到細分著色器中
tessVertexData
tessVert
(
vertexData
v
)
{
tessVertexData
o
;
o
.
vertex
=
v
.
vertex
;
o
.
tangent
=
v
.
tangent
;
o
.
normal
=
v
.
normal
;
o
.
uv
=
v
.
uv
;
return
o
;
}
//頂點著色器
v2g
vert
(
vertexData
v
)
{
v2g
o
;
o
.
vertex
=
UnityObjectToClipPos
(
v
.
vertex
);
o
.
uv
=
TRANSFORM_TEX
(
v
.
uv
,
_MainTex
);
return
o
;
}
//細分函數定義
TessellationFactors
hullFun
(
InputPatch
<
tessVertexData
,
3
>
v
)
{
TessellationFactors
o
;
o
.
edge
[
0
]
=
_TessellationUniform
;
o
.
edge
[
1
]
=
_TessellationUniform
;
o
.
edge
[
2
]
=
_TessellationUniform
;
o
.
inside
=
_TessellationUniform
;
return
o
;
}
//細分規則定義
[
UNITY_domain
(
"
tri
"
)]
//三角形
[
UNITY_outputcontrolpoints
(
3
)]
//三角形三個頂點
[
UNITY_outputtopology
(
"
triangle_cw
"
)]
//三角形生成順序(順時針/逆時針)
[
UNITY_partitioning
(
"
fractional_odd
"
)]
//細分只與整數有關,舍入規則
[
UNITY_patchconstantfunc
(
"
hullFun
"
)]
//細分函數
//hull著色器:定義細分規則
tessVertexData
hul
(
InputPatch
<
tessVertexData
,
3
>
v
,
uint
id
:
SV_OutputControlPointID
)
{
return
v
[
id
];
}
[
UNITY_domain
(
"
tri
"
)]
//domain著色器:計算細分後的頂點位置和數據,同時執行頂點著色器
v2g
dom
(
TessellationFactors
tessFactors
,
const
OutputPatch
<
tessVertexData
,
3
>
vi
,
float3
bary
:
SV_DomainLocation
)
{
vertexData
v
;
v
.
vertex
=
vi
[
0
].
vertex
*
bary
.
x
+
vi
[
1
].
vertex
*
bary
.
y
+
vi
[
2
].
vertex
*
bary
.
z
;
v
.
tangent
=
vi
[
0
].
tangent
*
bary
.
x
+
vi
[
1
].
tangent
*
bary
.
y
+
vi
[
2
].
tangent
*
bary
.
z
;
v
.
normal
=
vi
[
0
].
normal
*
bary
.
x
+
vi
[
1
].
normal
*
bary
.
y
+
vi
[
2
].
normal
*
bary
.
z
;
v
.
uv
=
vi
[
0
].
uv
*
bary
.
x
+
vi
[
1
].
uv
*
bary
.
y
+
vi
[
2
].
uv
*
bary
.
z
;
return
vert
(
v
);
}
[
maxvertexcount
(
3
)]
//幾何著色器
void
geo
(
triangle
v2g
v
[
3
],
inout
TriangleStream
<
g2f
>
tStream
)
{
float4
barycenter
=
(
v
[
0
].
vertex
+
v
[
1
].
vertex
+
v
[
2
].
vertex
)
/
3
;
float3
normal
=
(
v
[
0
].
normal
+
v
[
1
].
normal
+
v
[
2
].
normal
)
/
3
;
v
[
0
].
normal
=
normal
;
v
[
1
].
normal
=
normal
;
v
[
2
].
normal
=
normal
;
g2f
g0
,
g1
,
g2
;
g0
.
data
=
v
[
0
];
g1
.
data
=
v
[
1
];
g2
.
data
=
v
[
2
];
//
g0
.
barycentricCoordinates
=
float3
(
0
,
0
,
1
);
g1
.
barycentricCoordinates
=
float3
(
0
,
1
,
0
);
g2
.
barycentricCoordinates
=
float3
(
1
,
0
,
0
);
tStream
.
Append
(
g0
);
tStream
.
Append
(
g1
);
tStream
.
Append
(
g2
);
tStream
.
RestartStrip
();
}
fixed4
frag
(
g2f
i
)
:
SV_Target
{
fixed4
col
=
tex2D
(
_MainTex
,
i
.
data
.
uv
);
float3
barys
=
i
.
barycentricCoordinates
;
float3
deltas
=
fwidth
(
barys
);
float3
smoothing
=
deltas
*
_WireframeSmoothing
;
float3
thickness
=
deltas
*
_WireframeThickness
;
barys
=
smoothstep
(
thickness
,
thickness
+
smoothing
,
barys
);
float
minBary
=
min
(
barys
.
x
,
min
(
barys
.
y
,
barys
.
z
));
return
float4
(
lerp
(
_WireframeColor
,
col
,
minBary
),
1
);
//
return
col
;
}
ENDCG
}
}
}
這是一個使用Surface Shader實作曲面細分的範例。你可以將此程式碼貼上到Unity的Shader檔中,並將其套用到你的模型上。然後,透過調整
_DisplacementStrength
內容的值來控制曲面細分的強度。
把上面程式碼復制到我們剛的shader指令碼裏來替換掉,就能得到這樣的效果圖
使用Shader Graph(或者)實作曲面細分,關於這一部份,後面我們會再開個專題直接學習Shader工具的一些使用,因為使用工具總體比較簡單一點,這裏不做過多贅述,我們旨在了解內部程式碼原理。
3️⃣幾何著色器
我們在說到曲面細分著色器的時候,程式碼裏就運用了幾何著色器。那麽什麽事幾何著色器呢?
Unity 的 幾何著色器(Geometry Shader) 是一種用於在渲染管線中處理幾何圖元(例如點、線、三角形)的特殊類別的著色器。與 頂點著色器(Vertex Shader )和 片段著色器(Fragment Shader) 不同,幾何著色器在渲染管線的幾何階段中執行,可以對傳入的幾何數據進行操作和變換。
使用幾何著色器,你可以執行一系列的幾何操作,如建立新的幾何圖元、刪除幾何圖元、改變幾何圖元的形狀等。這為實作一些高級效果提供了便利,如幾何細分、法線擾動、霧效果等。
以下是一個簡單的範例,展示了如何在Unity中編寫和使用幾何著色器:
Shader
"
Custom
/
Example
"
{
Properties
{
_MainTex
(
"
Texture
"
,
2
D
)
=
"
white
"
{}
_WireframeColor
(
"
Wireframe
Color
"
,
Color
)
=
(
0
,
0
,
0
)
_WireframeSmoothing
(
"
Wireframe
Smoothing
"
,
Range
(
0
,
10
))
=
1
_WireframeThickness
(
"
Wireframe
Thickness
"
,
Range
(
0
,
10
))
=
1
}
SubShader
{
Tags
{
"
RenderType
"
=
"
Opaque
"
}
LOD
100
Pass
{
CGPROGRAM
#
pragma
vertex
vert
#
pragma
geometry
geo
#
pragma
fragment
frag
#
include
"
UnityCG
.
cginc
"
//頂點著色器數據
struct
appdata
{
float4
vertex
:
POSITION
;
float2
uv
:
TEXCOORD0
;
};
//幾何著色器數據
struct
v2g
{
float4
vertex
:
SV_POSITION
;
float2
uv
:
TEXCOORD0
;
float3
normal
:
TEXCOORD2
;
};
//片段著色器數據
struct
g2f
{
v2g
data
;
float3
barycentricCoordinates
:
TEXCOORD3
;
//三角形的重心座標
};
sampler2D
_MainTex
;
float4
_MainTex_ST
;
float3
_WireframeColor
;
float
_WireframeSmoothing
;
float
_WireframeThickness
;
v2g
vert
(
appdata
v
)
{
v2g
o
;
o
.
vertex
=
UnityObjectToClipPos
(
v
.
vertex
);
o
.
uv
=
TRANSFORM_TEX
(
v
.
uv
,
_MainTex
);
return
o
;
}
//參考:https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-geometry-shader
[
maxvertexcount
(
3
)]
void
geo
(
triangle
v2g
v
[
3
],
inout
TriangleStream
<
g2f
>
tStream
)
{
float3
normal
=
(
v
[
0
].
normal
+
v
[
1
].
normal
+
v
[
2
].
normal
)
/
3
;
v
[
0
].
normal
=
normal
;
v
[
1
].
normal
=
normal
;
v
[
2
].
normal
=
normal
;
g2f
g0
,
g1
,
g2
;
g0
.
data
=
v
[
0
];
g1
.
data
=
v
[
1
];
g2
.
data
=
v
[
2
];
//
g0
.
barycentricCoordinates
=
float3
(
0
,
0
,
1
);
g1
.
barycentricCoordinates
=
float3
(
0
,
1
,
0
);
g2
.
barycentricCoordinates
=
float3
(
1
,
0
,
0
);
tStream
.
Append
(
g0
);
tStream
.
Append
(
g1
);
tStream
.
Append
(
g2
);
tStream
.
RestartStrip
();
}
fixed4
frag
(
g2f
i
)
:
SV_Target
{
fixed4
col
=
tex2D
(
_MainTex
,
i
.
data
.
uv
);
float3
barys
=
i
.
barycentricCoordinates
;
float3
deltas
=
fwidth
(
barys
);
float3
smoothing
=
deltas
*
_WireframeSmoothing
;
float3
thickness
=
deltas
*
_WireframeThickness
;
barys
=
smoothstep
(
thickness
,
thickness
+
smoothing
,
barys
);
float
minBary
=
min
(
barys
.
x
,
min
(
barys
.
y
,
barys
.
z
));
return
float4
(
lerp
(
_WireframeColor
,
col
,
minBary
),
1
);
//
}
ENDCG
}
}
}
在這個範例中,我們定義了一個簡單的幾何著色器,它將傳入的點擴充套件成一個由三個點組成的三角形。在幾何著色器的
geo
函數中,我們透過叠代傳入的點,為每個點建立一個新的頂點,並將其添加到三角形流中。在片段著色器中,我們簡單地將頂點的顏色作為最終的顏色輸出。最終效果圖如下
4️⃣片元著色器
Unity
中的
片元著色器(Fragment Shader)
是一種用於計算每個螢幕像素最終顏色的著色器。它在頂點著色器處理完頂點數據之後,對每個像素進行處理,確定最終像素的顏色輸出。
通常, 片元著色器 用於執行諸如光照計算、紋理采樣、顏色混合等操作,以確定像素的最終顏色。在 Unity 中,你可以透過編寫片段著色器來實作各種各樣的視覺效果和渲染技術。
以下是一個簡單的 Unity片元著色器 範例:
Shader
"
Custom
/
Example
"
{
Properties
{
_MainTex
(
"
Texture
"
,
2
D
)
=
"
white
"
{}
_Color
(
"
Color
"
,
Color
)
=
(
1
,
1
,
1
,
1
)
}
SubShader
{
Tags
{
"
RenderType
"
=
"
Opaque
"
}
LOD
100
Pass
{
CGPROGRAM
#
pragma
vertex
vert
#
pragma
fragment
frag
#
include
"
UnityCG
.
cginc
"
struct
appdata
{
float4
vertex
:
POSITION
;
float2
uv
:
TEXCOORD0
;
};
struct
v2f
{
float2
uv
:
TEXCOORD0
;
float4
vertex
:
SV_POSITION
;
};
sampler2D
_MainTex
;
fixed4
_Color
;
v2f
vert
(
appdata
v
)
{
v2f
o
;
o
.
vertex
=
UnityObjectToClipPos
(
v
.
vertex
);
o
.
uv
=
v
.
uv
;
return
o
;
}
fixed4
frag
(
v2f
i
)
:
SV_TARGET
{
fixed4
texColor
=
tex2D
(
_MainTex
,
i
.
uv
);
return
texColor
*
_Color
;
}
ENDCG
}
}
}
在這個範例中,我們定義了一個簡單的
片元著色器
。它從輸入的紋理中采樣顏色,並與內容
_Color
相乘,輸出最終的像素顏色。
要使用這個
片元著色器
,只需將其保存為一個Shader檔,並將其套用到Unity中的物件上即可。你可以透過調整內容
_MainTex
和
_Color
的值來改變紋理和顏色效果。
上面主要講了幾個能透過編程來控制的渲染過程,接下來我們說一下,不可編程部份和GPU固定實作的部份。
5️⃣裁剪
在 Unity 中,渲染流程中的 裁剪 部份通常是指在圖形渲染管線的「 幾何處理 」階段,進行 視錐體裁剪 和 遮擋剔除 的過程。這個過程的目的是 最佳化渲染效能 ,確保只有在相機視野範圍內可見的物體才會被送入後續的渲染階段,從而避免不必要的繪制操作, 提高渲染效率 。
Unity中的渲染流程通常包含以下步驟:
- 幾何處理(Geometry Processing) :這是渲染管線的第一個階段,其中包括頂點著色器的執行、裁剪和三角形裝配等操作。在這個階段,視錐體裁剪是首先進行的操作。視錐體裁剪會將場景中不在相機視錐體內的頂點和三角形剔除,從而減少後續處理的物件數量。
- 光柵化(Rasterization) :在幾何處理後,剩余的可見三角形被轉換成螢幕上的像素,並且透過光柵化過程進行片段處理。
- 片段處理(Fragment Processing) :這個階段涉及像素著色器的執行,包括紋理采樣、光照計算等。在這個階段,可能會進行遮擋剔除操作,即根據深度緩沖區等資訊,確定哪些像素是不可見的,從而避免對它們進行著色和混合操作。
總的來說,裁剪部份主要發生在幾何處理階段,透過視錐體裁剪和遮擋剔除來提高渲染效率,確保只有可見的物體才會被送入後續的渲染階段,從而減少不必要的計算和繪制操作。
6️⃣逐片元操作
在 Unity 渲染過程中, 逐片元操作 指的是 渲染管線 中的 片段處理階段 。這個階段是在螢幕上的 每個像素 ( 或稱為片元 )上執行的操作,其主要目的是計算出每個 像素的最終顏色值 。逐片元操作包括以下主要步驟:
- 光柵化(Rasterization) :在幾何處理階段後,剩余的可見三角形會被轉換成螢幕上的像素,這個過程就是光柵化。每個三角形都會被分解成覆蓋它的像素。
- 片段著色器(Fragment Shader) :在光柵化後,針對每個像素會執行片段著色器。片段著色器是一段程式碼,用來計算每個像素最終的顏色。這裏進行了光照計算、紋理采樣、環境光遮蔽等操作,以確定片元最終的顏色。
- 深度測試(Depth Testing) :在計算了每個片元的顏色後,會將該片元的深度值與深度緩沖區中對應位置的值進行比較。如果當前片元的深度值比深度緩沖區中的值更接近相機視角,那麽這個片元就是最終的可見片元,其顏色值將被寫入到幀緩沖區中。否則,這個片元將被丟棄。
- 混合(Blending) :在深度測試之後,可能會進行混合操作。混合允許將新的片元顏色與幀緩沖區中已存在的顏色進行混合,這通常在透明物體的渲染中使用,以實作透明效果。
總的來說,逐片元操作是渲染管線中的一個重要階段,它負責計算每個螢幕像素的最終顏色值,是實作圖形渲染效果的關鍵之一。
7️⃣螢幕對映
在Unity渲染過程中,GPU的螢幕對映是指將場景中的3D物件投影到螢幕上的過程。這個過程是由GPU執行的,通常在渲染管線的幾何處理階段進行。以下是螢幕對映的一般步驟:
- 投影變換(Projection Transformation) :在場景中,攝影機會對3D物體進行投影。這個投影是由投影矩陣來實作的,通常包括透視投影(perspective projection)或正交投影(orthographic projection)。投影矩陣的作用是將3D場景中的點投影到攝影機的近裁剪面上。
- 視口變換(Viewport Transformation) :在投影後,得到的2D座標通常是以螢幕空間的方式表示的,其中座標原點位於螢幕的左下角,x和y軸的範圍通常是從0到螢幕的寬度和高度。視口變換將螢幕空間的座標對映到實際顯示器材上的像素座標,確保影像正確呈現在螢幕上。
- 裁剪(Clipping) :在視口變換後,可能會執行裁剪操作,將超出螢幕範圍的圖元剔除,從而提高渲染效率並防止渲染超出螢幕的不必要像素。
- 光柵化(Rasterization) :經過投影、視口變換和裁剪後,3D物件的頂點將被轉換成螢幕上的像素,這個過程稱為光柵化。GPU會根據這些像素的位置和內容來生成最終的影像。
總的來說,GPU的螢幕對映是渲染管線中的一個重要步驟,它負責將3D場景中的物件投影到螢幕上,從而實作圖形的顯示。
8️⃣三角形設定
在Unity渲染過程中,GPU的三角形設定是指在圖形渲染管線中的幾何處理階段,GPU如何處理和渲染場景中的三角形(或其他多邊形)的設定。這些設定通常由圖形編程人員在編寫頂點著色器和片段著色器時指定。
以下是一些GPU三角形設定的主要方面:
- 頂點數據傳輸(Vertex Data Transmission) :在幾何處理階段,場景中的三角形被分解成頂點,並且這些頂點的數據需要傳輸到GPU中進行處理。這包括頂點的位置、法線、紋理座標等資訊。
- 圖元組裝(Primitive Assembly) :GPU需要將頂點數據組裝成圖元(通常是三角形),以便進行後續的處理和渲染。圖元可以是三角形、線段或點等。
- 頂點著色器(Vertex Shader) :頂點著色器是在每個頂點上執行的程式,用於對頂點進行變換、光照計算和其他操作。在頂點著色器中,通常會進行一些GPU三角形設定,比如變換頂點位置、計算頂點法線等。
- 圖元剔除(Primitive Culling) :在進行光柵化之前,GPU可能會執行圖元剔除操作,根據特定的條件丟棄不需要的圖元,從而提高渲染效率。
- 三角形的渲染順序(Triangle Rendering Order) :在光柵化之後,GPU需要確定渲染三角形的順序。通常情況下,這由頂點數據的順序決定,但也可以透過調整頂點順序或者在幾何著色器中手動設定來改變渲染順序。
- 裁剪(Clipping) :在渲染過程中,GPU可能會執行裁剪操作,將超出螢幕範圍的圖元或片段剔除,以提高效能和減少不必要的渲染。
總的來說,GPU的三角形設定是指在圖形渲染管線的幾何處理階段中,GPU對場景中的三角形進行處理和渲染的設定和操作。這些設定包括頂點數據傳輸、圖元組裝、頂點著色器的操作、圖元剔除、三角形的渲染順序以及裁剪等。
9️⃣三角形遍歷
在Unity中,GPU的三角形遍歷是指在圖形渲染管線的幾何處理階段,GPU對場景中的三角形進行叠代處理的過程。這個過程通常包括以下步驟:
- 頂點著色器(Vertex Shader) :在頂點著色器中,每個頂點都經過一系列的變換和計算,以確定其在螢幕空間的位置、法線方向、紋理座標等資訊。頂點著色器是在GPU上執行的,可以並列地處理大量的頂點數據。
- 圖元組裝(Primitive Assembly) :一旦頂點經過了頂點著色器的處理,GPU會將頂點組裝成圖元,通常是三角形。這個階段也是在GPU上並列處理的,每個圖元的組裝都可以獨立進行。
- 三角形遍歷(Triangle Traversal) :在圖元組裝之後,GPU會對場景中的三角形進行遍歷。遍歷的過程實際上是將三角形投影到螢幕上,並確定每個像素(或稱為片元)的位置和內容。
- 光柵化(Rasterization) :在遍歷過程中,GPU會根據三角形的螢幕投影來確定其覆蓋的像素區域。這個過程稱為光柵化,它會將三角形分解為覆蓋它的像素,並將這些像素送入後續的片段處理階段。
- 片段處理(Fragment Processing) :在光柵化之後,GPU會對每個像素(片元)進行處理,包括執行片段著色器、深度測試、混合等操作,以計算最終的像素顏色值。
在整個渲染過程中,三角形遍歷是其中一個關鍵的步驟,它負責將場景中的三角形對映到螢幕上,並為後續的光柵化和片段處理階段提供輸入。這個過程需要高效地處理大量的三角形數據,以實作流暢的圖形渲染效果。
1️⃣0️⃣螢幕影像
在Unity中,GPU的螢幕影像是指最終呈現在螢幕上的影像,它是整個渲染過程的結果。GPU在渲染過程中負責計算每個像素的顏色值,並將這些顏色值寫入幀緩沖區(Frame Buffer)中,最終形成螢幕上的影像。
以下是Unity中GPU的螢幕影像生成的主要步驟:
- 頂點處理和光柵化 :在渲染管線的幾何處理階段,GPU會對場景中的頂點進行處理,將它們轉換為螢幕空間座標,並透過光柵化過程將三角形分解成覆蓋像素的片元。
- 片段處理 :對每個片元進行片段處理,包括執行片段著色器、深度測試、紋理采樣等操作。這些操作確定了每個像素的最終顏色值。
- 深度測試和混合 :在片段處理後,GPU執行深度測試來確定哪些片元是可見的,哪些是被遮擋的。然後,根據混合操作來合並不同片元的顏色值,以產生最終的像素顏色。
- 輸出到螢幕 :最終,GPU將幀緩沖區中的像素顏色值輸出到螢幕上,形成最終的螢幕影像。
整個過程是高度並列化的,GPU能夠同時處理多個像素和片元,以實作高效的圖形渲染。螢幕影像的質素和效能取決於許多因素,包括渲染管線的設定、光照效果、材質內容、渲染質素設定等。
總之,GPU的螢幕影像是渲染過程的最終輸出,它是使用者最終看到的遊戲畫面,包含了所有渲染效果和場景內容。
這篇文章我們熟悉了渲染的一個流程,了解了Unity一個物體是怎樣透過渲染最後呈現到我們眼中成為螢幕中我們所看到的遊戲物體那樣,最後也希望這篇文章對你們有所幫助。後面文章我會分享一些 Shader的制作效果以及如何去最佳化我們寫的Shader 。
享受遊戲,熱愛生活!我是皮皮,咱們下期見!
Unity常用發光效果和Shader程式碼