unity3d游戲引擎立體渲染教程
547 2017-05-26
完美教室教程之立體渲染(VolumetricRendering)。這些技術能突破現(xiàn)代3D引擎只能渲染物體外殼的最大限制。立體渲染可以實現(xiàn)逼真的材質與燈光進行復雜的交互,例如霧、煙、水和玻璃。查看NMZ的PlasmaGlobe效果了解立體渲染的基本概念,如下圖:
誠然,這些技術本身并不復雜,但實現(xiàn)上圖效果需要非常多的步驟。本教程將為大家全面介紹以下概念:
第一篇:立體渲染。介紹立體渲染的概念以及在Unity中如何實現(xiàn)立體渲染。
第二篇:光線追蹤。文章著重說明如何實現(xiàn)距離輔助的光線追蹤,這是是渲染立體的事實性標準技術。
第三篇:表面著色。全面指引如何逼真地進行立體著色。
第四篇:有向距離常一篇對于數(shù)學工具更深入的討論,讓我們能制作和組合任意幾何體。
本文將介紹立體渲染的基本概念,并以一個簡單的著色器收尾,后續(xù)的文章將以此為基礎進行迭代:
介紹
第一部分立體渲染
第二部分立體射線投射
第三部分固定步長的立體光線追蹤
結論
介紹
在3D游戲引擎中,球體、立方體以及所有其它復雜的集合體都是由三角面片組成的。Unity采用的實時光照系統(tǒng)只能渲染平面。例如渲染球體,Unity僅繪制球體表面的三角形。盡管一些材質是半透明的,也只繪制表面并將其顏色與后面的物體進行混合。光照系統(tǒng)無法探測到材質的幾何體的內部。對于GPU來說,整個世界就是由各種空殼構成的。
為了突破這種強大的限制,大量技術順勢而出。盡管傳統(tǒng)著色器最終都會止于物體表面,但這并不意味著無法更深入。立體渲染技術可以在材質內部模擬光線的傳送,從而實現(xiàn)更震撼也更真實的視覺效果。
立體渲染
無光照紋理的片段著色器如下:
fixed4frag(v2fi):SV_Target
{
fixed4col=tex2D(_MainTex,i.texcoord);
returncol;
}
不嚴格地說,上述代碼會為最終渲染圖像每一個可能的像素(片段)調用。GPU會在有三角形與相機視錐體相交時,調用該片段著色器。換句話說,就是相機“看見了”該對象。Unity需要知道該對象的實際顏色,以便將其分配到渲染圖像的各個像素。
片段著色器最后返回的對象,是從特定角度看過去特定位置的顏色。這種方式計算的顏色是完全隨意的,因此返回的內容可以不必匹配幾何體的真實渲染情況。下圖展示了一個3D立方體的例子。當片段著色器檢測到立方體表面的顏色時,我們獲得的顏色如同我們在一個球體上所看到的。這個幾何體是個立方體,但是從相機的角度來看,它的外觀和感覺其實“酷似”一個球體。
這就是立體渲染(VolumetricRendering)的基本概念:模擬光線在物體內部的傳送。
如果想模擬前面的效果,就要更精確地進行描述。假設主物體是一個立方體,要在其內部立體渲染一個球體,實際上并不存在這個球體,因為我們將完全通過著色器代碼來渲染。球體中心點位于_Centre,半徑是_Radius,均為世界坐標。移動立方體不會影響球體位置,因為它是完全以世界坐標系來表述的。外部的幾何體也不會對該球體造成任何影響。立方體表面的三角形就是通向幾何體內部的窗口。雖然可以使用四邊形(Quad)減少三角形數(shù)量,但要能從立方體的任意角度觀看該球體。
立體射線投射
第一種立體渲染的方式完全適用于實現(xiàn)前文所述的效果。片段著色器接收要渲染的點(wolrdPosition)以及視線方向(viewDirection),然后使用raycastHit函數(shù)檢測是否投射到紅色球體。這種技術叫做立體射線投射(VolumetricRaycasting),它將射線從相機投射到幾何體內部。
在片段著色器函數(shù)中添加剩下的代碼:
float3_Centre;
float_Radius;
fixed4frag(v2fi):SV_Target
{
float3worldPosition=...
float3viewDirection=...
if(raycastHit(worldPosition,viewDirection))
returnfixed4(1,0,0,1);//Redifhittheball
else
returnfixed4(1,1,1,1);//Whiteotherwise
}
下面來解釋代碼中的其它變量。
世界坐標
首先,片段的世界坐標就是從相機生成的射線投射到幾何體上的點。在片段著色器中獲取世界坐標的代碼如下:
structv2f{
float4pos:SV_POSITION;//Clipspace
float3wPos:TEXCOORD1;//Worldposition
};
v2fvert(appdata_fullv)
{
v2fo;
o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
o.wPos=mul(_Object2World,v.vertex).xyz;
returno;
}
視線方向
其次,視線方向就是射線從相機投射到幾何體上被渲染的點的方向。這里需要知道相機坐標,Unity已內置了該變量_WorldSpaceCameraPos。計算通過兩點的射線方向可使用如下代碼:
float3viewDirection=normalize(i.wPos-_WorldSpaceCameraPos);
RaycastHit函數(shù)
當我們知道了渲染點的坐標和方向后,現(xiàn)在需要使用raycastHit函數(shù)來決定射線是否投射到了虛擬的紅色球體上。這就是球體與線段相交的問題,這種問題已有慣用解決方案,但通常效率不高。如果需要更具分析性的方法,就需要自行解決線段與自定義幾何體相交的問題。這種方案極大限制了可以創(chuàng)建的模型,所以很少被應用。
固定步長的立體光線追蹤
上面提到純分析式的立體射線投射,其實不適合解決這里的問題。如果希望模擬任意幾何體,就要找到不依賴于相交方程的更為靈活的技術。常見的解決方案叫做立體光線追蹤(VolumetricRaymarching),是基于迭代的解決方案。
立體光線追蹤會緩慢地將射線投射到立方體內,每一步都會檢測當前是否已投射到紅色球體。
每條射線均從片段坐標worldPosition開始,然后迭代沿著viewDirection的方向投射STEP_SIZE單位長度。這可以通過每次迭代為worldPosition加上STEP_SIZE*viewDirection來實現(xiàn)。
用下面的raymarchHit函數(shù)代替之前的raycastHit:
#defineSTEPS64
#defineSTEP_SIZE0.01
boolraymarchHit(float3position,float3direction)
{
for(inti=0;i { if(sphereHit(position)) returntrue; position+=direction*STEP_SIZE; } returnfalse; } 下面的函數(shù)用于檢測點p是否位于球體內: boolsphereHit(float3p) { returndistance(p,_Centre)<_Radius; } 線段與球體相交很難,但迭代檢測點是否位于球體內就很簡單了。結果見下圖,別看它看起來就是個圓形,實際上這就是個無光照的球體:
結論
本文介紹了基本的立體渲染概念。盡管傳統(tǒng)著色器只能渲染材質的外殼,但還是有辦法讓光線穿透到材質內部的幾何體,創(chuàng)造畫面的深度。光線追蹤就是最常用的技術,本文用該技術在立方體內繪制了一個紅色球體。后面的教程將分享如何逼真地著色(第三篇表面著色)以及如何做出一些有趣的形狀(第四篇有向距離場)。最終將能夠使用簡單幾行代碼和一個立體渲染著色器實現(xiàn)如下效果:
請聯(lián)系網(wǎng)站客服,了解詳細的優(yōu)惠課程信息~
優(yōu)質、權威、便捷、省心
掃一掃
獲取更多福利
獵學網(wǎng)企業(yè)微信
獵學網(wǎng)訂閱號
獵學網(wǎng)服務號