光線類(lèi)型
GLSL中的默認(rèn)光線有以下隱式的內(nèi)置變量:
raytypeIMG {
highp vec3 gl_OriginIMG;
highp vec3 gl_DirectionIMG;
highp rayprogramIMG gl_PrefixRayProgramIMG;
lowp uint gl_SceneIMG;
highp float gl_MaxDistanceIMG;
mediump ivec2 gl_PixelIMG;
mediump uint gl_BounceCountIMG;
bool gl_IsOutgoingIMG;
bool gl_FlipFacingIMG;
bool gl_RunPrefixProgramIMG;
};
通過(guò)添加用戶自定義變量界定默認(rèn)光線的類(lèi)型。例如,陰影光線如下所示:
layout(binding = 0, occlusion_test_always) raytypeIMG ShadowRay {
vec3 colour;
};
調(diào)用glSceneArrayRayBlockSizeIMG,可以界定使用的每個(gè)光線類(lèi)型。調(diào)用glGetComponentProgramHandleIMG,可以界定每個(gè)組件集應(yīng)該執(zhí)行的頂點(diǎn)和光線著色器。創(chuàng)建一個(gè)像標(biāo)準(zhǔn)頂點(diǎn)或片段著色器一樣的光線著色器。
幀著色器
流程的第一部分便是幀著色器。幀著色器是glsl著色器,根據(jù)glDispatchRaysIMG中參數(shù)請(qǐng)求的寬度x高度,可以發(fā)送零條或多條光線到場(chǎng)景中。注意,不需要通過(guò)當(dāng)前幀著色器中的坐標(biāo)來(lái)積累光線位置,即光線與像素位置不耦合。
layout (rgba8, binding = 0) uniform accumulateonly highp image2D rayTraceDiffuseImage;
layout(max_rays = 1) out;
out ShadowRay shadowRay;
uniform rayprogramIMG defaultRayProgram;
void emitShadowRay(highp vec3 p, highp vec3 normal, highp vec3 dir, highp float maxDistance, vec3 colour) {
shadowRay.gl_OriginIMG = p + depthModifier*normal;
shadowRay.gl_DirectionIMG = dir;
shadowRay.gl_PrefixRayProgramIMG = gl_NullRayProgramIMG;
shadowRay.gl_SceneIMG = uint(gl_DispatchRaysIDIMG);
shadowRay.gl_MaxDistanceIMG = maxDistance;
shadowRay.gl_PixelIMG = gl_FrameCoordIMG;
shadowRay.gl_BounceCountIMG = 0u;
shadowRay.gl_IsOutgoingIMG = true;
shadowRay.gl_FlipFacingIMG = false;
shadowRay.gl_RunPrefixProgramIMG = false;
shadowRay.colour = colour;
emitRayIMG(shadowRay, defaultRayProgram);
}
void main() {
emitShadowRay(vPosition, unpackedNormal, vNormalisedDirectionToLight, length(vDirectionToLight), vec3(1.0,0.0,0.0));
imageAddIMG(rayTraceDiffuseImage, gl_FrameCoordIMG, vec4(0.0,0.0,1.0,0.0));
}
可以看到,幀著色器有一些額外的添加至GLSL中。內(nèi)置的命令是:
gl_DispatchRaysIDIMG 是輸入glDispatchRaysIMG的第一個(gè)參數(shù),用于多緩沖。
gl_FrameCoordIMG是目前幀著色器中的坐標(biāo)。
gl_NullRayProgramIMG是無(wú)操作程序,用于比較rayprogramIMGs。
陰影光線具有可用的隱式光線類(lèi)變量,如上所示,且每個(gè)變量在流程中執(zhí)行某個(gè)函數(shù)。在幀著色器中,這些變量通常是可編輯而非可讀取。
gl_OriginIMG是發(fā)送光線的源頭。
gl_DirectionIMG是光線發(fā)送的方向。
gl_PrefixRayProgramIMG是運(yùn)行交叉光線著色器之前可運(yùn)行的前綴光線方案。
gl_SceneIMG是發(fā)送光線的場(chǎng)景id.(如上述glBindSceneArrayComponentGroupIMG中指定的參數(shù))
gl_MaxDistanceIMG是光線所能追蹤的最大距離,這里不考慮交叉,且運(yùn)行defaultRayProgram。
gl_PixelIMG是光線發(fā)送的原始像素。
gl_BounceCountIMG是光線當(dāng)前的反射數(shù)。(在幀著色器中通常為0)
gl_IsOutgoingIMG即光線向外延伸——詳細(xì)信息在下一篇文章中討論。
gl_FlipFacingIMG即是否在下次交叉中翻轉(zhuǎn)表面以再次測(cè)試光線。
gl_RunPrefixProgramIMG即是否運(yùn)行上述的前綴方案。
emitRayIMG是發(fā)送光線且將光線傳輸至交叉測(cè)試硬件的GLSL函數(shù),而imageAddIMG是下文即將討論的累積函數(shù)。
shadeRayIMG也可用。該函數(shù)可以在給定的光線方案內(nèi)對(duì)光線著色,且不需要進(jìn)行交叉測(cè)試。
光線著色器
當(dāng)光線與三角形交叉、光線到達(dá)最大距離或想要運(yùn)行前綴方案時(shí),則啟動(dòng)光線著色器。在幀著色器中,我們僅編寫(xiě)光線變量;而在光線著色器中,我們可以讀取光線變量,且發(fā)送更多光線時(shí)還可以編寫(xiě)光線變量。
layout(binding=0, occlusion_test_always) raytypeIMG ShadowRay {
highp vec3 diffuseObjectColor;
highp vec3 ambientObjectColor;
};
layout(binding=1, occlusion_test_never) raytypeIMG ReflectiveRay {
highp vec3 reflectiveColor;
};
layout(rgba8, binding=2) uniform accumulateonly highp image2D reflectionOutput;
in perVertexData {
highp vec3 vertexNormal;
highp vec2 vertexTexCoord;
} vertexData[];
rayInputHandlerIMG(ShadowRay inputRay) {
void main() {
imageAddIMG(reflectionOutput, inputRay.gl_PixelIMG, vec4(inputRay.ambientObjectColor, 0.0));
}
}
rayInputHandlerIMG(ReflectiveRay inputRay) {
layout(max_rays=2) out;
out ShadowRay reflectedShadowRay;
out ReflectiveRay reflectionRay;
void main() {
// We interpolate the varyings ourselves
highp vec3 intersectionPoint = interpolateAtRayHitIMG(gl_in[0].gl_Position.xyz, gl_in[1].gl_Position.xyz, gl_in[2].gl_Position.xyz);
highp vec2 intersectionTextureCoord = interpolateAtRayHitIMG(vertexData[0].vertexTexCoord.xy, vertexData[1].vertexTexCoord.xy, vertexData[2].vertexTexCoord.xy);
highp vec3 vDirectionToLight = lightData.vLightPosition.xyz - intersectionPoint;
highp vec3 intersectionNormal = interpolateAtRayHitIMG(vertexData[0].vertexNormal.xyz, vertexData[1].vertexNormal.xyz, vertexData[2].vertexNormal.xyz);
highp vec3 vNormalisedNormal = normalize(intersectionNormal);
highp vec4 reflectionTexture = texture(sTexture, intersectionTextureCoord);
if (aboveReflectionThreshold && inputRay.gl_BounceCountIMG < NUMBER_OF_REFLECTION_RAYS) {
reflectionRay.gl_DirectionIMG = reflectionDirection;
reflectionRay.gl_OriginIMG = intersectionPoint + reflectionDirectionOffset * reflectionRay.gl_DirectionIMG;
reflectionRay.gl_MaxDistanceIMG = inputRay.gl_MaxDistanceIMG;
reflectionRay.gl_SceneIMG = inputRay.gl_SceneIMG;
reflectionRay.gl_PixelIMG = inputRay.gl_PixelIMG;
reflectionRay.gl_BounceCountIMG = inputRay.gl_BounceCountIMG + 1u;
reflectionRay.gl_FlipFacingIMG = // ... set the rest of the ray values
reflectionRay.reflectiveColor = reflectedObjectColor;
emitRayIMG(reflectionRay, environmentRayProgram);
} else {
// ...
imageAddIMG(reflectionOutput, inputRay.gl_PixelIMG, vec4(environmentAccumulationColor, 0.0));
}
}
}
光線著色器與通常的OpenGL ES著色器略有差別。首先,其有多個(gè)main()入口點(diǎn)。這是因?yàn)槲覀冇卸囝?lèi)光線。當(dāng)光線與一些幾何圖形交叉時(shí),入口點(diǎn)便會(huì)執(zhí)行相應(yīng)的光線。例如,當(dāng)陰影光線與附帶該光線著色器的幾何圖形交叉時(shí),將運(yùn)行第一個(gè)main()。而當(dāng)與反射光線交叉時(shí)則運(yùn)行第二個(gè)main()。光線類(lèi)型通常通過(guò)raytypeIMG進(jìn)行分配。在本例中,有兩類(lèi)光線:陰影光線和反射光線。可以看到,在第二個(gè)main(),可以發(fā)送光線:“l(fā)ayout(max_rays=2) out”。在下一行中還可以看到發(fā)送的光線類(lèi)型。所以main()可以發(fā)送兩種類(lèi)型的光線。而對(duì)于第一個(gè)main(),可以看到,如果該幾何圖形與陰影光線交叉,則不會(huì)發(fā)送更多的光線。
接下來(lái)便是perVertexData。這是我們?cè)陧旤c(diǎn)著色器上編寫(xiě)的變量數(shù)據(jù)。它被存儲(chǔ)在主存中,當(dāng)運(yùn)行光線著色器時(shí)可以對(duì)其進(jìn)行檢索。在三角形上的每個(gè)點(diǎn)都具有變量,可以使用interpolateAtRayHitIMG函數(shù)在變量數(shù)據(jù)上執(zhí)行重心插值。這與柵格化不同,柵格化主要依靠一些不受我們控制的因素來(lái)進(jìn)行插值。使用該API,我們可以控制插值,這樣便可以根據(jù)需要來(lái)執(zhí)行不同類(lèi)型的插值。
在光線發(fā)送前先手動(dòng)增加光線的反射數(shù)。這是為了確保我們不會(huì)進(jìn)入一個(gè)無(wú)限循環(huán)中。
在光線著色器中調(diào)用imageAddIMG。該像素的累積由第二個(gè)參數(shù)指定。從輸入光線中獲取像素地址,但這不是必須的,因?yàn)楣饩€的來(lái)源有很多。
光線限制器
當(dāng)我們將光線類(lèi)型設(shè)置為occlusion_test_always時(shí),此光線將與其他光線完全不同。這是交叉測(cè)試光線的優(yōu)化。如果光線與任何幾何圖形相交(即光線限制器,見(jiàn)glComponentOccluderIMG),則刪除光線且不做進(jìn)一步的著色。若光線達(dá)到它的最大距離,則仍然可以運(yùn)行光線著色器。但若光線被任何幾何圖形遮擋,則這樣做有利于測(cè)試陰影。在硬件中光線限制器有一個(gè)快速路徑,這對(duì)于開(kāi)發(fā)人員而言是有用的機(jī)制。
前綴程序
前綴程序指的是在執(zhí)行交叉光線著色器之前執(zhí)行光線著色器。我們可以附加前綴程序至各光線著色器中。假設(shè)其使用用于存在距離因素且基于效果的光線,如云層或水的渲染。我們需要了解光線行駛的距離。例如,下圖中,我們了解云層與另一交叉對(duì)象之間的距離。這時(shí)便可以在運(yùn)行交叉對(duì)象著色器之前運(yùn)行前綴著色器來(lái)計(jì)算該云層的著色量。
關(guān)于這個(gè)特征還可以舉出更多例子,在有關(guān)前綴程序及光線距離選擇的文章中我們將做進(jìn)一步闡述。
混合渲染
光線追蹤與基于延遲渲染的PowerVR拼貼硬件非常匹配。有了像素的本地存儲(chǔ)擴(kuò)展(PLS)我們可以使用光柵化來(lái)渲染場(chǎng)景,向G緩沖區(qū)編寫(xiě)信息并使之保存在本地,再隨后在G緩沖區(qū)中發(fā)出光線追蹤命令。目前為止這已經(jīng)應(yīng)用到許多技術(shù)中,包括軟陰影及照明。使用本地內(nèi)存意味著讀寫(xiě)G緩沖區(qū)時(shí)可以節(jié)省內(nèi)存帶寬。更多資訊敬請(qǐng)期待。
SDK演示
SDK團(tuán)隊(duì)正在使用源代碼以及輔助函數(shù)做演示示例,以使光線追蹤應(yīng)用程序的創(chuàng)建更加簡(jiǎn)單。
光線追蹤SDK演示軟陰影
性能
未來(lái)將貼出更多有關(guān)光線追蹤性能的文章。我們的PowerVR SDK性能分析工具PVRTune支持從光線追蹤硬件中讀取性能計(jì)數(shù)器。兩個(gè)新的硬件模塊如下圖所示。
PowerVR SDK PVRTune中的光線追蹤計(jì)數(shù)器
更多資訊
目前您可以通過(guò)NDA訪問(wèn)擴(kuò)展規(guī)范。預(yù)計(jì)未來(lái)我們將公布這一規(guī)范。我們的硬件目前用作PCIe的測(cè)試芯片,其在GDC會(huì)議上進(jìn)行過(guò)展示。關(guān)于GDC大會(huì)上光線追蹤展示材料請(qǐng)點(diǎn)擊。
評(píng)論