本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.html
讓咱們從第12部分 Part 12繼續.使用上次咱們工做的同一個playground,咱們今天將學習光照和3D物體.記得前幾周咱們作出的日食嗎?它又回來了! 好吧,此次咱們將移除太陽,只關注行星.git
首先,讓咱們清理內核,使其只包含下面的代碼:github
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);
複製代碼
你必定認出幾周前的這些代碼了.咱們唯一改變的是將圓外面的顏色替換爲黑色
,並將圓內部改成白色
.輸出的黑乎乎應該是這個樣子: 函數
目前還不錯.行星看起來至關扁平,光照分佈的太均勻看起來不真實.讓我接下來修復它.幾何學告訴咱們,爲了找到球面上的點,咱們須要球體公式: post
在咱們的例子中,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);
複製代碼
輸出圖像應該像起來像這樣:ui
正如你期待的那樣,顏色從中間的純白色變爲圓外面的純黑色.爲此,咱們必須用顏色除以radius半徑
,來使z
值規範化到 [0,1] 區間內,它能給咱們全範圍的光照效果.咱們實際僞造了一個燈光源放在 (0,0,1).讓引出了新話題:lighting燈光
.spa
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上.
下次見!