您當前的位置:首頁 > 繪畫

一個關於渲染管線中座標變換的簡單例項

作者:由 Sean是肖恩 發表于 繪畫時間:2017-12-28

前言

關於渲染管線大家一定都不陌生,那麼關於渲染管線中的座標變換大家到底是陌生還是不陌生呢?我曾經有段時間覺得:emmm,我好像懂了。但是多問幾遍自己,就開始懷疑人生了!!所以我決定寫這麼一篇東西來解決一些朋友們的遇到的和我曾經相似的疑惑。

這篇文章結合了Unity,所有的東西是在Unity中實現的。

注意,下面的例子我只做了位移變換

。在開始之前,我先給出兩個Unity的API:

Camera。WorldToScreenPoint

Camera。WorldToViewportPoint

在這裡我就有一個問題了:這兩個API中到底執行了什麼呢?怎麼就從world到viewport或screen了呢?那麼帶著這兩個問題開始吧!!

無論是Camera。WorldToScreenPoint還是Camera。WorldToViewportPoint,它們的背後就是我們經常提到的座標變換。

座標變換的根源就是 — 矩陣

!!這裡我不會涉及到任何關於數學的講解,這講下去估計我自己都能把自己說糊塗!

先給出一張珍藏多年的圖(原來買了個老外的教程):

一個關於渲染管線中座標變換的簡單例項

可以看到,座標空間的變化大致會經過

Object Space - World Space

World Space - View Space

View Space - Projection Space

Projection Space - NDC

NDC - Texture Space

Texture Space - Screen Space

所有靠後的空間,可以看作是之前一個空間的父空間。對於頂點在座標空間的變換就是用父空間的矩陣來對子空間做變換,從而得到在父空間的位置。所以重點就出來了:

如何構建父空間的矩陣

Object Space - World Space

我們知道,對於一個模型而言,他的每個頂點位置是基於自身座標空間的 — 通常我們叫做模型空間(Object Space)。

將模型丟到世界座標中之後,對於整個模型而言,它有一個相對於世界空間的座標值。我們這時候能說這個模型是位於世界空間的。

因此要讓模型中的頂點變換到對應的世界空間下,就是讓這些頂點的自身座標變換到模型對應的世界座標下。我們就可以利用模型在世界空間下的座標值構建矩陣。

// 模型空間到世界空間

Vector4

O2W

(){

Vector3

parentPoint

=

Parent

position

Vector3

childPoint

=

Child

localPosition

Matrix4x4

worldMat

=

new

Matrix4x4

();

worldMat

SetRow

0

new

Vector4

1

0

0

parentPoint

x

));

worldMat

SetRow

1

new

Vector4

0

1

0

parentPoint

y

));

worldMat

SetRow

2

new

Vector4

0

0

1

parentPoint

z

));

worldMat

SetRow

3

new

Vector4

0

0

0

1

));

Vector4

worldPos

=

worldMat

*

new

Vector4

childPoint

x

childPoint

y

childPoint

z

1

);

Debug

Log

“WorldPosition: ”

+

worldPos

);

return

worldPos

}

World Space - View Space

經過了O2W的變換之後,下一步就是W2V的變換。其實就是將世界空間下的頂點變換到相機空間下。這個變換兩點比較特殊的:

1、因為相機和世界座標原點可能不重合,我們要做的是讓他們重合。這裡的做法是移動相機的位置讓他與世界座標重合。

2、因為在Unity中相機空間使用的是右手座標系,那麼它的正方向是朝向螢幕外面的,所以在矩陣中要對Z值取反。

//世界空間到檢視空間(相機空間)

Vector4

W2V

(){

Vector3

cameraPoint

=

Camera

main

transform

position

// 讓相機座標和世界座標對齊,

Vector3

c2m

=

-

cameraPoint

Matrix4x4

viewMat

=

new

Matrix4x4

();

viewMat

SetRow

0

new

Vector4

1

0

0

c2m

x

));

viewMat

SetRow

1

new

Vector4

0

1

0

c2m

y

));

viewMat

SetRow

2

new

Vector4

0

0

1

c2m

z

));

viewMat

SetRow

3

new

Vector4

0

0

0

1

));

//因為觀察空間的Z軸是反的

viewMat

SetRow

2

new

Vector4

0

0

1

-

c2m

z

));

Vector4

viewPos

=

viewMat

*

worldPoint

Debug

Log

“viewPosition: ”

+

viewPos

);

return

viewPos

}

View Space - Projection Space

經過了W2V的變換後,再接下里一步是V2P的變換。投影(Projection)變換有正交投影和透視投影的區別,它們的投影矩陣是有區別的,這裡以透視投影為例:

//檢視空間到投影空間

Vector4

V2P

(){

float

fov

=

Camera

main

fieldOfView

*

Mathf

Deg2Rad

float

aspect

=

Camera

main

aspect

float

far

=

Camera

main

farClipPlane

float

near

=

Camera

main

nearClipPlane

float

cot

=

1

/

Mathf

Tan

fov

/

2

);

cot

=

Mathf

Abs

cot

);

float

factor1

=

cot

/

aspect

float

factor2

=

-((

far

+

near

/

far

-

near

));

float

factor3

=

-((

2

*

far

*

near

/

far

-

near

));

Matrix4x4

clipMat

=

new

Matrix4x4

();

clipMat

SetRow

0

new

Vector4

factor1

0

0

0

));

clipMat

SetRow

1

new

Vector4

0

cot

0

0

));

clipMat

SetRow

2

new

Vector4

0

0

factor2

factor3

));

clipMat

SetRow

3

new

Vector4

0

0

-

1

0

));

Vector4

clipPos

=

clipMat

*

viewPoint

Debug

Log

“ClipPosition: ”

+

clipPos

);

return

clipPos

}

Projection Space - NDC

NDC是一個歸一化的空間,座標空間範圍為[-1, 1]。從Projection空間到NDC空間的做法就是做了一個齊次除法!

//clip到NDC

Vector4

P2NDC

(){

Vector4

ndcPos

=

clipPoint

/

clipPoint

w

Debug

Log

“NDCPosition: ”

+

ndcPos

);

return

ndcPos

}

NDC - Texture Space

這個過程是將[-1, 1]對映到[0, 1]之間。

//從NDC到Texture Space

Vector4

NDC2Texture

(){

Vector4

texturePos

=

ndcPoint

+

Vector4

one

/

2

Debug

Log

“TexturePosition: ”

+

texturePos

);

return

texturePoint

}

Texture Space - Screen Space

這個過程就是得到頂點最終在螢幕上的座標,其實就是利用Texture Space的座標乘上螢幕的寬高。

//從Texture Space到Screen

Vector4

Texture2Screen

(){

Vector4

screenPos

=

new

Vector4

texturePoint

x

*

Screen

width

texturePoint

y

*

Screen

height

0

0

);

Debug

Log

“ScreenPosition: ”

+

screenPos

);

return

screenPoint

}

上面的過程中,真正涉及到矩陣的就是三個階段:

Object Space - World Space

World Space - View Space

View Space - Projection Space

其他的空間變換都是一些對映關係。

因為我所說的非常簡短,沒有涉及到任何關於座標空間的意義和內容。但是這個過程可以證明一件事情,回到最初的問題:

Camera。WorldToScreenPoint

Camera。WorldToViewportPoint

這兩個API最終得到的值對應的是什麼階段,事實證明:

Camera。WorldToViewportPoint — Texture Space

Camera。WorldToScreenPoint — Screen Space

我只能說,如果不寫這一個過程的話,但從API的字面上,是真的會誤導人的!!

標簽: space  newVector4  空間  SetRow  變換