在我的 3D 游戲和設計中,我經(jīng)常選擇可愛的低多邊形卡通風格。我一直想給我的模型一個真正的卡通輪廓,所以這就是我們今天要做的工作。在以后的文章中,我們將著眼于為三角形著色以使其看起來也很卡通。
本文是「我正在進行的中等難度 ThreeJS 教程系列」的一部分。我一直想要在介紹“如何繪制立方體”和“讓我們用瘋狂的著色器填充屏幕”關(guān)卡之間找到一些東西。所以就在這里。
如果你在網(wǎng)上搜索“OpenGL 輪廓效果”,你會遇到很多相互矛盾的信息。經(jīng)過大量研究,我確定有兩種常用方法可以使用現(xiàn)代 3D 圖形 API 創(chuàng)建輪廓。
- 繪制一個對象兩次;一次是輪廓顏色,一次是正常。
- 通過在像素級別檢測邊緣的后處理效果運行整個場景。
第二種選擇是當今 Unity 等現(xiàn)代游戲引擎中最常用的。但是我不想使用它,因為它涉及多個后處理步驟,這在移動 GPU 上可能很慢并且消耗更多內(nèi)存。此外,后處理和 WebVR 目前還沒有很好地融合,所以我暫時避免使用它。(當我介紹發(fā)光效果時,我們會重新討論這個)。讓我們關(guān)注第一種方法,兩次繪制同一個對象。
兩次渲染一個對象可能看起來很浪費,但請記住,大多數(shù) GPU 都是帶寬有限的。一旦你把幾何圖形傳到 GPU 上,它就可以一遍又一遍地渲染同樣的東西,幾乎沒有成本。在大多數(shù)情況下,我想概述的東西是靜態(tài)幾何。
讓我們從兩次渲染圓環(huán)結(jié)開始,一次是黑色,一次是黃色。完成這項工作的這個技巧是縮放輪廓,使其比主要對象略大。
//create a cubeobj = new THREE.Group()const c1 = new THREE.Mesh( new THREE.TorusKnotBufferGeometry(0.6,0.1), new THREE.MeshLambertMaterial({ color:’black’, }))const s = 1.03c1.scale.set(s,s,s)obj.add(c1)obj.add(new THREE.Mesh( new THREE.TorusKnotBufferGeometry(0.6,0.1), new THREE.MeshPhongMaterial({ color:’yellow’, })))
運行它,看看會發(fā)生什么。唔。根據(jù)您使用的幾何形狀,您將看到全黑或雙色調(diào)、部分黑色和部分黃色的東西。還注意到一些從黃色中突出的黑色三角形嗎?這就是所謂的z戰(zhàn)斗。那么問題是什么。
實際上,這有點道理。黑結(jié)略微擴大,因此在看不到黃色的地方,黑夜就在它的前面。那么我們該如何解決呢?
15 秒內(nèi)解釋剔除
我們將利用一個小技巧。
當 GPU 渲染三角形時,它通常只渲染正面的三角形。這些是面向相機的三角形。根據(jù)定義,任何背對相機的三角形都是不可見的,因此無需費心繪制它們。如果我們有一個球體,那么實際上只會繪制前半球。GPU 已「剔除」構(gòu)成背面半球的三角形。
對于輪廓效果,我們希望僅使用正面幾何圖形繪制常規(guī)對象。這已經(jīng)在發(fā)生。但是,對于輪廓,我們只希望繪制背面的三角形。然后它們將位于規(guī)則形狀的后面,僅在邊緣可見。
碰巧的是,ThreeJS 已經(jīng)知道如何繪制正面和背面。我們只需要告訴它我們想要什么。下面的代碼和上面一樣,只是side正確設置了兩種材質(zhì)的屬性。
obj = new THREE.Group()const c1 = new THREE.Mesh( new THREE.TorusKnotBufferGeometry(0.6,0.1), new THREE.MeshLambertMaterial({ color:’black’, side: THREE.BackSide }))const s = 1.03c1.scale.set(s,s,s)obj.add(c1)obj.add(new THREE.Mesh( new THREE.TorusKnotBufferGeometry(0.6,0.1), new THREE.MeshPhongMaterial({ color:’yellow’, side: THREE.FrontSide })))
現(xiàn)在看起來像這樣:
image.png
完美的!
修復法線
實際上不,它不是很完美。如果你仔細觀察,你會發(fā)現(xiàn)物體背面的輪廓比正面的輪廓要細。那是因為我們只是在放大整個對象。這種幼稚的方法只適用于像球體這樣的完美凸面物體。對于結(jié)或任何真實模型,我們需要正確地加厚幾何圖形。
碰巧大多數(shù)幾何圖形上已經(jīng)有法線。這些法線垂直于幾何體的表面。如果我們在法線方向上擴展點,那么一切都應該正常工作。我們可以通過稍微修改的頂點著色器來做到這一點。有關(guān)自定義頂點著色器的說明,請參閱其他文章。
//create a cubeconst mat = new THREE.MeshLambertMaterial({ color:’black’, side:THREE.BackSide })mat.onBeforeCompile = (shader) => { const token = ‘#include <begin_vertex>’ const customTransform = ` vec3 transformed = position objectNormal*0.02; ` shader.vertexShader = shader.vertexShader.replace(token,customTransform)}
上面的代碼為輪廓對象創(chuàng)建了一個自定義材質(zhì)。其余與之前相同。在著色器內(nèi)部,它objectNormal向每個頂點的位置添加一小部分,將其向外擴展。將 更改0.02為更大的值以獲得更粗的輪廓。
現(xiàn)在看起來像這樣:
image.png
壯麗的。您已經(jīng)創(chuàng)建了一個類似于卡通的輪廓。
版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶自發(fā)貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請發(fā)送郵件至 舉報,一經(jīng)查實,本站將立刻刪除。