光线类型
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大会上光线追踪展示材料请点击。
评论
查看更多