當前的陰影技術
在過去十年中,實時渲染陰影的流行方法是使用陰影貼圖。這是場景從光源視角再次渲染到離屏深度緩沖區(稱為陰影貼圖)的地方,然后在著色過程中對陰影貼圖進行采樣,以使用深度比較計算可見性。雖然這種方法已經成功地應用于許多應用中,但它也有一些缺點。
最常見的問題是陰影鋸齒——這是在陰影貼圖的分辨率過低的地方,導致出現塊狀陰影。雖然可以通過使用更高分辨率的陰影貼圖來解決,但會增加內存占用和帶寬利用率,可能會對性能產生負面影響,尤其是在移動設備上。即使使用更高分辨率的陰影貼圖,某些微觀細節也很難保留,這要后續的屏幕空間陰影通道來細化。但是,在使用光線追蹤時,可以在屏幕上為每個像素分配一條光線,這將產生像素完美的硬陰影。
光線追蹤管線
光線生成
當在命令緩沖區調用 vkCmdTraceRaysKHR ,將為當前綁定的光線追蹤流水線調用用戶定義的光線生成著色器。追蹤光線的命令允許開發人員為調度的線程設置各種參數。我們的演示是完全光線追蹤的,這意味著最好為屏幕上的每個像素分配一個光線生成著色器線程。
每次調用光線生成著色器都必須指定將主光線發射到場景中所需的變量。光線需要有原點(視點)和行進方向。可以通過將逆視圖矩陣應用于(0,0,0,1)來計算原點。要計算方向,需要當前像素的屏幕空間位置。可以使用 gl_LaunchIDEXT 從光線生成著色器查詢分派坐標。使用此內置擴展,屏幕空間坐標和光線方向可以按如下方式計算:
const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy);
vec2 screenspace = inUV * 2.0 - 1.0;
vec4 target = mInvProjectionMatrix * vec4(screenspace.xy, 1, 1);
vec4 direction = mInvViewMatrix * vec4(normalize(target.xyz), 0);
從這里,我們可以使用 traceRayEXT 函數將主光線發射到場景中。然后,它將遍歷加速結構,在該結構中,它將命中或錯過場景中的幾何體,并調用相應著色器組。執行的著色器組將取決于其命中內容,并將像素顏色存儲在有效負載結構中。“未命中”著色器只是將光線的顏色設置為硬編碼的清除顏色。
命中組著色器
一旦光線與場景中的對象發生碰撞,就會執行命中著色器。頂點緩沖區、索引緩沖區和材質等模型數據都附著到“命中組”著色器。光線追蹤擴展允許我們獲取命中對象的實例ID。在本演示中,每個模型都是唯一的,因此實例ID直接對應于模型ID。模型ID可用于查找上述緩沖區。
// Since each object is unique in this scene, instance ID is enough to identify which buffers to look up
uint objID = gl_InstanceID;
// indices of the triangle we hit
ivec3 ind = ivec3(indices[nonuniformEXT(objID)].i[3 * gl_PrimitiveID + 0], //
indices[nonuniformEXT(objID)].i[3 * gl_PrimitiveID + 1], //
indices[nonuniformEXT(objID)].i[3 * gl_PrimitiveID + 2]); //
// Vertices of the hit triangle
Vertex v0 = vertices[nonuniformEXT(objID)].v[ind.x];
Vertex v1 = vertices[nonuniformEXT(objID)].v[ind.y];
Vertex v2 = vertices[nonuniformEXT(objID)].v[ind.z];
gl_PrimitiveID可以用來告訴我們使用哪些索引來查找命中的頂點,然后使用重心插值系數在它們之間進行插值,該插值系數來自命中著色器中聲明為hitAttributeEXT類型的全局變量。然后,我們使用世界矩陣將插值頂點值轉換到世界空間,并旋轉法線值。
// Get the interpolation coefficients
const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
// Interpolate the position and normal vector for this ray
vec4 modelNormal = vec4(v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + v2.nrm * barycentrics.z, 1.0);
vec4 modelPos = vec4(v0.pos * barycentrics.x + v1.pos * barycentrics.y + v2.pos * barycentrics.z, 1.0);
// Transform the position and normal vectors from model space to world space
mat4 worldTransform = transforms[nonuniformEXT(objID)];
vec3 worldPos = (worldTransform * modelPos).xyz;
// Don't translate the normal vector, only rotate and scale
mat3 worldRotate = mat3(worldTransform[0].xyz, worldTransform[1].xyz, worldTransform[2].xyz);
vec3 worldNormal = worldRotate * modelNormal.xyz;
使用擊中點的法線和世界位置,我們可以計算光線相對于場景中靜態光源的Phong照明分量。然后,我們使用一個新的命中和未命中組從該點向光源發射另一條光線。我們可以將次光線的最大長度設置為擊中點和光源之間的距離。如果光線在這個距離內與加速度結構中的任何物體碰撞,我們可以得出結論,在碰撞點和光源之間有一個物體,因此該點處于陰影中。如果次光線未擊中距離集內的任何對象,則會執行“未命中”著色器,我們可以假定該點不在陰影中。
在這個圖中,我們可以看到兩個例子。光線從視口(1)發射,第一條光線擊中點3,第二條光線發射,在到達光源的途中不會擊中任何幾何體。視點的第二條光線在點4處照射,但在到達光源的過程中,在點5處再次發生碰撞。因此,我們可以得出結論,點4在陰影中,但點3不在陰影中。
優化
雖然光線追蹤陰影提供了比傳統方法更高的逼真度,但它們仍然不完全完美。因此,就實時計算而言,光線追蹤算法需要更多的硬件,這是一個明顯的缺點。除此之外,還可以采取一些不同的優化措施來改進本文概述的技術。
陰影檢查
我們可以減少第一組用來檢查硬陰影的二次光線的數量;這是通過首先檢查已計算的Phong光照分量來完成的。如果由于曲面背向光源,光照分量已為0,則檢查硬陰影沒有意義,因為該點已處于黑暗中。
這將光線預算從屏幕上像素數的大約1.8倍減少到大約1.5倍。這顯然取決于場景和其中的對象,因為它隨未命中場景的主光線的比例以及通過或未通過陰影檢查的主光線的比例而變化。
混合渲染一般來說,光線追蹤核心將比傳統的光柵化流水線慢(至少目前是這樣)。有幾個可能的原因,但主要的原因是光線追蹤硬件仍然相對較新,因此與光柵化相比,GPU仍然沒有為其投入更多的空間。這意味著可以計算一個標準的G緩沖區,并使用位置附件來定位從哪個位置發射陰影檢查光線。G緩沖區在《Vulkan中的環境遮擋》中有介紹,所以如果你還沒有看到,一定要看一看。簡而言之,G緩沖區可以替代主光線,從而產生更好的任務重疊和更小的光線預算。
結束
雖然完全光線追蹤的硬陰影在寫這篇博文的時候可能不是最佳解決方案,但它們仍然提供了傳統流水線難以模擬的細節和準確性。一如既往,我們強烈建議大家看看PowerVR SDK及其代碼示例,以了解我們如何實現這些技術和實現這些算法的確切機制。我們也總是通過支持門戶或論壇發送電子郵件。
如果您有興趣了解更多關于各種圖形技術的信息,請查看我們的文檔網站,或者在SDK Github中探索我們的其他代碼示例。
原文標題:Vulkan完全光線追蹤硬陰影
文章出處:【微信公眾號:Imagination Tech】歡迎添加關注!文章轉載請注明出處。
審核編輯:湯梓紅
-
代碼
+關注
關注
30文章
4801瀏覽量
68735 -
Vulkan
+關注
關注
0文章
28瀏覽量
5724 -
光線追蹤
+關注
關注
0文章
183瀏覽量
21495
原文標題:Vulkan完全光線追蹤硬陰影
文章出處:【微信號:Imgtec,微信公眾號:Imagination Tech】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論