光照渲染 用canvas模擬光照效果

2021-09-14 03:17:30 字數 3596 閱讀 9429

我們能看到物體,是因為光照射在物體上然後反射到我們的眼睛當中。其中的影響因素非常多:觀察者的位置、光源的位置、光的顏色、物體表面的顏色、材質和粗糙程度等等。以後我們將會詳細**如何模擬物體的材質,在這篇文章中我們只討論光源。

太陽的尺度相對地球來說非常大,所以可以認為從太陽照射來的光線都是平行的,即太陽是乙個平行光源。

模擬平行光源的光照非常簡單,當光垂直照射到平面上,即光線方向和平面呈90度角時,這時光照是最強的。如果照射的角度不斷變大(或者說光線和平面的夾角不斷變小),光照也會隨之變弱,當光線方向完全和平面平行時,這時沒有光能照射到平面上,光強變成了0。

可以總結出,平行光的光照情況和兩個方向有關:光線的方向和受光照平面的朝向。

我們用乙個垂直於平面的向量去描述平面的朝向,在圖形學中,一般把這個向量稱為「法向量」。

我們可以用向量的「點乘」運算來計算光強變化。

點乘也叫數量積,是接受在實數r上的兩個向量並返回乙個實數值標量的二元運算。點乘運算規則非常簡單,將兩個向量對應座標的乘積求和就行了。

這裡我們計算的是三維向量,我們用陣列來表示向量,寫乙個簡單的方法來計算點乘:

/**

* 點乘運算

* @param v1 向量v1

* @param v2 向量v2

* @return 點乘結果

*/function dot( v1, v2 )

還有幾個重要的向量運算我們也會用到,在這裡我們提前定義好,為減小篇幅,這裡省略掉具體實現,**可以看最後的例項原始碼。

/**

* 將向量轉為單位向量

* @param v

* @return 單位向量

*/function normalize( v )

/** * 兩向量相減

* @param v1

* @param v2

* @return

*/function sub( v1, v2 )

/** * 計算乙個向量的反方向向量

* @param v

* @return

*/function negate( v )

我們假設頁面的左上角為原點o,右方向為x軸正方向,下方向為y軸正方向,垂直螢幕向外的方向為z軸正方向。我們可以這樣定義乙個寬高都為500的平面:

var plane =    // 顏色為紅色

}

對於平行光,只需要關心它的方向和顏色,我們可以這樣來定義乙個平行光源:

var directionallight =    // 顏色為純白色

}

平行光的光線都是平行的,所以它照射到平面上各個位置的效果都是一樣的,換言之,整個平面都應該是同乙個顏色。

根據上面的規則(光強等於光線反方向向量點乘平面法向量),我們可以計算出這個顏色:

// ...

var reverselightdirection = negate( directionallight.direction ); // 計算平行光的反方向向量

var intensity = dot( reverselightdirection, plane.normal ); // 計算兩向量點乘

// 計算有光照時的顏色

在日常生活中,點光源更加常見,白熾燈、檯燈等都可以認為是點光源。

首先,我們先定義乙個點光源,對於乙個點光源來說,我們只需要關心它的位置和顏色:

var pointlight =    // 顏色為純白色

}

光強的計算規則仍然不變:光強等於光線反方向向量點乘平面法向量。但是點光源的光是從乙個點發射出來,它們照射到平面上時,所有光線的方向都不一樣。所以,我們必須挨個計算平面上所有畫素的光強。

這裡需要用到canvas提供的putimagedata,這個方法可以直接填入乙個區域的畫素顏色值來繪圖。**如下:

// ...

var imagedata = ctx.createimagedata( 500, 500 ); // 建立乙個imagedata,用來儲存畫素資料

for ( var x = 0; x < imagedata.width; x++ )

}ctx.putimagedata( imagedata, 100, 100 );

這樣就可以看到結果了:

動態圖看起來有很多圈圈,實際上並沒有,可以自己玩一下

對於乙個500*500的平面,我們去計算它在點光源光照下的顏色,需要挨個計算平面上所有點,需要迴圈500*500=250000次,這其實是非常低效的。並且在做複雜場景的渲染時,不會只有乙個光源,而且還會有投影等計算,計算量將會非常大。

從更底層的角度來說,這是因為每次計算都是由cpu完成的,而cpu只能序列計算,它只能完成乙個計算以後才能開始下一次計算,所以非常緩慢。

這種複雜的渲染其實更適合用webgl來做,因為每一次計算其實前後無關,webgl可以利用gpu的平行計算能力,同時去計算所有點的光照強度。乙個500*500的平面,理論上只需要花一次計算的時間,這個提公升是非常大的。

這篇文章也是想通過這個簡單的光照計算來引出webgl,後面的文章我會用webgl來重新實現這個效果。

webgl渲染的光照效果

這篇文章到這裡就結束了。

我計畫寫一系列關於前端圖形渲染的文章,將會涵蓋常用的前端圖形繪製技術:canvas、svg和webgl。希望通過這一系列文章能讓讀者對前端的各種圖形繪製介面以及影象處理、圖形學的基礎知識有所了解。希望在分享的同時,也能鞏固和複習自己所學知識,和大家共同進步。

如果能幫助到你,歡迎star,這樣也能及時追蹤部落格的更新。

光照與渲染(二) 光照技術

廣義的來說,unity的全域性光照是 實時 或是 預先計算好 的,在某些情況下兩種方法可以結合使用,照出更逼真的場景。本節我們會針對兩種技術的差異優勢和使用時機做個簡單的描述。實時照明 realtime lighting 預設情況下,unity的燈源 直接光源,投射燈,點光源 都是實時的,代表這些燈...

Unity 光照和渲染

unity 文件 using real time lighting with realtime gi 完全理解unity中precompute realtime gi及其優化 一 完全理解unity預計算全域性光照及其優化 二 unity預計算全域性實時gi 九 lightmap parameter...

CG語言 基本光照渲染

本人新人一枚,想要將自己所學分享給大家,如有錯誤或不足請大家毫不猶豫的指出,謝謝大家的支援!那麼開始吧 乙個物體我們能看見是通過被光線照亮後經過反射進入我們的眼睛後大腦成像。計算機 gpu 就是計算進入眼睛之前的各個步驟得到的從而給我們反饋乙個結果。計算公式 su cecolor emissive ...