光線類型
GLSL中的默認光線有以下隱式的內置變量:
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;
};
通過添加用戶自定義變量界定默認光線的類型。例如,陰影光線如下所示:
layout(binding = 0, occlusion_test_always) raytypeIMG ShadowRay {
vec3 colour;
};
調用glSceneArrayRayBlockSizeIMG,可以界定使用的每個光線類型。調用glGetComponentProgramHandleIMG,可以界定每個組件集應該執行的頂點和光線著色器。創建一個像標準頂點或片段著色器一樣的光線著色器。
幀著色器
流程的第一部分便是幀著色器。幀著色器是glsl著色器,根據glDispatchRaysIMG中參數請求的寬度x高度,可以發送零條或多條光線到場景中。注意,不需要通過當前幀著色器中的坐標來積累光線位置,即光線與像素位置不耦合。
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中。內置的命令是:
gl_DispatchRaysIDIMG 是輸入glDispatchRaysIMG的第一個參數,用于多緩沖。
gl_FrameCoordIMG是目前幀著色器中的坐標。
gl_NullRayProgramIMG是無操作程序,用于比較rayprogramIMGs。
陰影光線具有可用的隱式光線類變量,如上所示,且每個變量在流程中執行某個函數。在幀著色器中,這些變量通常是可編輯而非可讀取。
gl_OriginIMG是發送光線的源頭。
gl_DirectionIMG是光線發送的方向。
gl_PrefixRayProgramIMG是運行交叉光線著色器之前可運行的前綴光線方案。
gl_SceneIMG是發送光線的場景id.(如上述glBindSceneArrayComponentGroupIMG中指定的參數)
gl_MaxDistanceIMG是光線所能追蹤的最大距離,這里不考慮交叉,且運行defaultRayProgram。
gl_PixelIMG是光線發送的原始像素。
gl_BounceCountIMG是光線當前的反射數。(在幀著色器中通常為0)
gl_IsOutgoingIMG即光線向外延伸——詳細信息在下一篇文章中討論。
gl_FlipFacingIMG即是否在下次交叉中翻轉表面以再次測試光線。
gl_RunPrefixProgramIMG即是否運行上述的前綴方案。
emitRayIMG是發送光線且將光線傳輸至交叉測試硬件的GLSL函數,而imageAddIMG是下文即將討論的累積函數。
shadeRayIMG也可用。該函數可以在給定的光線方案內對光線著色,且不需要進行交叉測試。
光線著色器
當光線與三角形交叉、光線到達最大距離或想要運行前綴方案時,則啟動光線著色器。在幀著色器中,我們僅編寫光線變量;而在光線著色器中,我們可以讀取光線變量,且發送更多光線時還可以編寫光線變量。
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著色器略有差別。首先,其有多個main()入口點。這是因為我們有多類光線。當光線與一些幾何圖形交叉時,入口點便會執行相應的光線。例如,當陰影光線與附帶該光線著色器的幾何圖形交叉時,將運行第一個main()。而當與反射光線交叉時則運行第二個main()。光線類型通常通過raytypeIMG進行分配。在本例中,有兩類光線:陰影光線和反射光線。可以看到,在第二個main(),可以發送光線:“layout(max_rays=2) out”。在下一行中還可以看到發送的光線類型。所以main()可以發送兩種類型的光線。而對于第一個main(),可以看到,如果該幾何圖形與陰影光線交叉,則不會發送更多的光線。
接下來便是perVertexData。這是我們在頂點著色器上編寫的變量數據。它被存儲在主存中,當運行光線著色器時可以對其進行檢索。在三角形上的每個點都具有變量,可以使用interpolateAtRayHitIMG函數在變量數據上執行重心插值。這與柵格化不同,柵格化主要依靠一些不受我們控制的因素來進行插值。使用該API,我們可以控制插值,這樣便可以根據需要來執行不同類型的插值。
在光線發送前先手動增加光線的反射數。這是為了確保我們不會進入一個無限循環中。
在光線著色器中調用imageAddIMG。該像素的累積由第二個參數指定。從輸入光線中獲取像素地址,但這不是必須的,因為光線的來源有很多。
光線限制器
當我們將光線類型設置為occlusion_test_always時,此光線將與其他光線完全不同。這是交叉測試光線的優化。如果光線與任何幾何圖形相交(即光線限制器,見glComponentOccluderIMG),則刪除光線且不做進一步的著色。若光線達到它的最大距離,則仍然可以運行光線著色器。但若光線被任何幾何圖形遮擋,則這樣做有利于測試陰影。在硬件中光線限制器有一個快速路徑,這對于開發人員而言是有用的機制。
前綴程序
前綴程序指的是在執行交叉光線著色器之前執行光線著色器。我們可以附加前綴程序至各光線著色器中。假設其使用用于存在距離因素且基于效果的光線,如云層或水的渲染。我們需要了解光線行駛的距離。例如,下圖中,我們了解云層與另一交叉對象之間的距離。這時便可以在運行交叉對象著色器之前運行前綴著色器來計算該云層的著色量。
關于這個特征還可以舉出更多例子,在有關前綴程序及光線距離選擇的文章中我們將做進一步闡述。
混合渲染
光線追蹤與基于延遲渲染的PowerVR拼貼硬件非常匹配。有了像素的本地存儲擴展(PLS)我們可以使用光柵化來渲染場景,向G緩沖區編寫信息并使之保存在本地,再隨后在G緩沖區中發出光線追蹤命令。目前為止這已經應用到許多技術中,包括軟陰影及照明。使用本地內存意味著讀寫G緩沖區時可以節省內存帶寬。更多資訊敬請期待。
SDK演示
SDK團隊正在使用源代碼以及輔助函數做演示示例,以使光線追蹤應用程序的創建更加簡單。
光線追蹤SDK演示軟陰影
性能
未來將貼出更多有關光線追蹤性能的文章。我們的PowerVR SDK性能分析工具PVRTune支持從光線追蹤硬件中讀取性能計數器。兩個新的硬件模塊如下圖所示。
PowerVR SDK PVRTune中的光線追蹤計數器
更多資訊
目前您可以通過NDA訪問擴展規范。預計未來我們將公布這一規范。我們的硬件目前用作PCIe的測試芯片,其在GDC會議上進行過展示。關于GDC大會上光線追蹤展示材料請點擊。
評論
查看更多