D3DBook:Ray-traced Shadows

From GDWiki
Jump to: navigation, search

Ray-traced shadows

Image:Pvgps3_11_sphere_raytrace.png

The pixel processing power of the modern graphics processors is beginning to enable real-time ray-tracing applications, even though the fill rate limits and relatively rigid data structure requirements of even the top-of-the-line graphics processors dictate that full ray-tracing isn’t a viable option for entire scenes designed for real-world usage just yet.

Ray-tracing is a method of brute force light evaluation, in which for each pixel in the frame buffer, a ray is collided with the scene objects to determine the reflected color of the object. The ray can be traced further than the first collision with the scene, to produce effects like mirror or glass.

Ray-traced shadows are evaluated in a similar way, but instead of evaluating the collision of rays from the frame buffer to the scene, the collision check is performed on rays from the scene to the light source. Should the ray from the current pixel collide with some object before reaching the light source, the current pixel would be in shadow and thus its color could be modulated darker from its basic material color. A severe limitation for using ray-tracing in Direct3D 10 is that you can’t evaluate arbitrary mesh data in the context of a pixel shader due to inherent rasterizer-oriented architecture. However, if you can model your shadow casting geometry mathematically (using relatively optimized formulas for performance), you can evaluate the collision steps relatively easily for each rendered pixel and get fantastic-looking results.

An intriguing aspect of using per-pixel ray-traced shadows is that by virtue of evaluating each and every result pixel independently, the shadow need not be uniform of nature. Consider a solid glass ball, for example; the simulated photons (rays) refract with more intensity when traced near through the center of the ball rather than near the edge of the same ball due to Fresnel refraction coefficient. The rays could even be bent by the ball to create a caustic distribution of the shadow and the light that would actually amplify the light in some points of the shadow receiver instead of making them appear like in shadow.

Following is a small code sample which implements a ray-traced shadow evaluator in pixel shader. The occluding geometry is a simple ball, but any object that can be expressed as a mathematical formula in pixel shader’s terms could be used.


//this function returns the distance of a point to a line:
float dist_p2l(float3 pnt, float3 l0, float3 l1)
{
    float3 v = l1 - l0;
    float3 w = pnt - l0;
 
    //determine the relations of the dot products of a triangle
    //formed by the vectors
    float c1 = dot(w,v);
    float c2 = dot(v,v);
    float b = c1/c2;
 
    //and use them to calculate the distance.
    float3 pb = l0 + b * v;
    return distance (pnt, pb);
}
 
float getSphereIntersect(float3 lightPos, float3 pixelPos, float3 ballPos, float ballRadius, out bool hit)
{
    hit = true;
    float dist = dist_p2l(ballPos, pixelPos, lightPos);
    if (dist > ballRadius) {hit=false; return 0;}
    else
        //prepare the shadow intensity here.
        return 1-cos(1.0-(dist/ballRadius));
}
 
//this is the ray tracing shadow pixel shader.
//it uses the above function to determine the shadow intensity.
 
float4 RaytraceShadowPS(PS_In input) : SV_Target
{
    bool hit;
    float shadowIntensity;
 
    shadowIntensity = getSphereIntersect(g_lightPos,
        input.wsPosition, g_ballPos, g_ballRadius, hit);
 
    float4 ret = input.color;
    if (hit)
        ret.rgb *= shadowIntensity; 
 
    return ret;
}
Personal tools