本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.
讓我們從第12部分 Part 12繼續.使用上次我們工作的同一個playground,我們今天將學習光照和3D物體.記得前幾周我們做出的日食嗎?它又回來了! 好吧,這次我們將移除太陽,只關注行星.
首先,讓我們清理核心,使其只包含下面的程式碼:
int width = output.get_width();
int height = output.get_height();
float2 uv = float2(gid) / float2(width, height);
uv = uv * 2.0 - 1.0;
float radius = 0.5;
float distance = length(uv) - radius;
output.write(distance < 0 ? float4(1) : float4(0), gid);
複製程式碼
你一定認出幾周前的這些程式碼了.我們惟一改變的是將圓外面的顏色替換為黑色
,並將圓內部改為白色
.輸出的黑乎乎應該是這個樣子:
目前還不錯.行星看起來相當扁平,光照分佈的太均勻看起來不真實.讓我接下來修復它.幾何學告訴我們,為了找到球面上的點,我們需要球體公式:
在我們的例子中,x0, y0和z0都是0因為我們的球體在螢幕中間.算出z值就可以得到planet行星
顏色的值,所以讓我們用下面這幾行來替換核心中的最後一行:
float planet = float(sqrt(radius * radius - uv.x * uv.x - uv.y * uv.y));
planet /= radius;
output.write(distance < 0 ? float4(planet) : float4(0), gid);
複製程式碼
輸出影像應該像起來像這樣:
正如你期待的那樣,顏色從中間的純白色變為圓外面的純黑色.為此,我們必須用顏色除以radius半徑
,來使z
值規範化到 [0,1] 區間內,它能給我們全範圍的光照效果.我們實際偽造了一個燈光源放在 (0,0,1).讓引出了新話題:lighting燈光
.
lighting燈光讓我們的顏色真正活起來.為了在我們的場景中有一個燈光,我們需要計算每個座標的normal法線
.法向量是垂直於表面,告訴我們表面"指向"哪個座標.用下面幾行替換最後兩千:
float3 normal = normalize(float3(uv.x, uv.y, planet));
output.write(distance < 0 ? float4(float3(normal), 1) : float4(0), gid);
複製程式碼
注意,我們在planet行星
變數中已經有z
值了.輸出的圖片看起來應該這樣:
這可能不是我們想要看到的,但至少我們知道在每個規格化座標處計算顏色時法線看起來是什麼樣子了.下一步,讓我們建立一個光源放置在我們左側(負x
),後面(正z
)一點.用下面幾行替換最後一行:
float3 source = normalize(float3(-1, 0, 1));
float light = dot(normal, source);
output.write(distance < 0 ? float4(float3(light), 1) : float4(0), gid);
複製程式碼
我們採用了一種基本的光照模型叫做朗伯特 Lambertian(漫反射)光照,其中我們將法線乘以規格化光源.我們將在以後的文章中學習更多光照知識,但是如果你對學習光照模型很有興趣,可以參考這裡 here的眾多資源.輸出的圖片看起來應該這樣:
還記得上一次核心給我們了一個計時器uniform嗎?讓我們用起來玩玩!用這行替換source
行:
float3 source = normalize(float3(cos(timer), sin(timer), 1));
複製程式碼
通過使用cos
和sin
函式,我們給光源一個圓周運動.x
和y
都是按圓的引數方程從 -1到1.輸出的圖片看起來應該這樣:
我們來仔細看看,場景中的物體被照亮(天空中的行星),然而,物體仍然呈現出單一的表面.我們有兩種方法可以讓它看起來更真實:使用紋理,或者給plante
顏色加上一些噪點.
原始碼source code 已釋出在Github上.
下次見!