website articles
box functions

Intro



A sphere are simple geometry shape described by a simple equation and therefore it accepts analytic solutions to many problems involving projections, occlusion, shadows, motion blur, etc. Similary, boxes are simple shapes that also accept closed form expresions for many such problems. This page collects some of those that I have collected (more often than not, derived myself).



Functions




Box Intersection
// Calcs intersection and exit distances, and normal at intersection
//
// The box is axis aligned and at the origin, but has any size.
// For arbirarily oriented and located boxes, simply transform
// the ray accordingly and reverse the transformation for the
// returning normal(without translation)
//
vec2 boxIntersection( vec3 ro, vec3 rd, 
                      vec3 boxSize, out vec3 outNormal ) 
{
    vec3 m = 1.0/rd;
    vec3 n = m*ro;
    vec3 k = abs(m)*boxSize;
	
    vec3 t1 = -n - k;
    vec3 t2 = -n + k;

    float tN = max( max( t1.x, t1.y ), t1.z );
    float tF = min( min( t2.x, t2.y ), t2.z );
	
    if( tN > tF || tF < 0.0) return vec2(-1.0); // no intersection
    
    outNormal = -sign(rdd)*step(t1.yzx,t1.xyz)*step(t1.zxy,t1.xyz);

    return vec2( tN, tF );
}
Example / Code

Box Density
float boxDensity( vec3 wro, vec3 wrd,       // ray origin and direction
                  mat4 boxTx, vec3 boxSize, // box rot+pos, and size
                  float dbuffer )
{
    vec3 d = (boxTx*vec4(wrd,0.0)).xyz;
    vec3 o = (boxTx*vec4(wro,1.0)).xyz;

    // ray-box intersection in box space
    vec3 m = 1.0/d;
    vec3 n = m*o;
    vec3 k = abs(m)*boxSize;
    vec3 ta = -n - k;
    vec3 tb = -n + k;
    float tN = max( max( ta.x, ta.y ), ta.z );
    float tF = min( min( tb.x, tb.y ), tb.z );
    if( tN > tF || tF < 0.0) return 0.0;

    // not visible (behind camera or behind dbuffer)
    if( tF<0.0 || tN>dbuffer ) return 0.0;

    // clip integration segment from camera to dbuffer
    tN = max( tN, 0.0 );
    tF = min( tF, dbuffer );
    
    // move ray to the intersection point
    o += tN*d; tF=tF-tN; tN=0.0;

    // density d(x,y,z) = [1-(x/rx)^2]*[1-(y/ry)^2]*[1-(z/rz)^2]
    // is analytically integrable:
    
    float ir2 = 1.0/(boxSize*boxSize);
    vec3 a = 1.0 -     (o*o)*ir2;
    vec3 b =     - 2.0*(o*d)*ir2;
    vec3 c =     -     (d*d)*ir2;
    
    float t1 = tF;
    float t2 = t1*t1;
    float t3 = t2*t1;
    float t4 = t2*t2;
    float t5 = t2*t3;
    float t6 = t3*t3;
    float t7 = t3*t4;

    return (t1/1.0)*(a.x*a.y*a.z) + 
           (t2/2.0)*(a.x*a.y*b.z + a.x*b.y*a.z + b.x*a.y*a.z) + 
           (t3/3.0)*(a.x*a.y*c.z + a.x*b.y*b.z + a.x*c.y*a.z + 
                     b.x*a.y*b.z + b.x*b.y*a.z + c.x*a.y*a.z) +
           (t4/4.0)*(a.x*b.y*c.z + a.x*c.y*b.z + b.x*a.y*c.z + 
                     b.x*b.y*b.z + b.x*c.y*a.z + c.x*a.y*b.z + 
                     c.x*b.y*a.z) + 
           (t5/5.0)*(a.x*c.y*c.z + b.x*b.y*c.z + b.x*c.y*b.z + 
                     c.x*a.y*c.z + c.x*b.y*b.z + c.x*c.y*a.z) + 
           (t6/6.0)*(b.x*c.y*c.z + c.x*b.y*c.z + c.x*c.y*b.z) + 
           (t7/7.0)*(c.x*c.y*c.z);
}
Example / Code

Box Ambient Occlusion
float boxOcclusion( vec3 pos, vec3 nor, 
                    mat4 boxTx,    // box rotation+position
                    vec3 boxSize ) // box size
{
    vec3 p = (boxTx*vec4(pos,1.0)).xyz;
    vec3 n = (boxTx*vec4(nor,0.0)).xyz;
    
    // 8 verts
    vec3 v0 = normalize( vec3(-1.0,-1.0,-1.0)*boxSize - p);
    vec3 v1 = normalize( vec3( 1.0,-1.0,-1.0)*boxSize - p);
    vec3 v2 = normalize( vec3(-1.0, 1.0,-1.0)*boxSize - p);
    vec3 v3 = normalize( vec3( 1.0, 1.0,-1.0)*boxSize - p);
    vec3 v4 = normalize( vec3(-1.0,-1.0, 1.0)*boxSize - p);
    vec3 v5 = normalize( vec3( 1.0,-1.0, 1.0)*boxSize - p);
    vec3 v6 = normalize( vec3(-1.0, 1.0, 1.0)*boxSize - p);
    vec3 v7 = normalize( vec3( 1.0, 1.0, 1.0)*boxSize - p);
    
    // 12 edges
    float k02 = dot(n,normalize(cross(v2,v0)))*acos(dot(v0,v2));
    float k23 = dot(n,normalize(cross(v3,v2)))*acos(dot(v2,v3));
    float k31 = dot(n,normalize(cross(v1,v3)))*acos(dot(v3,v1));
    float k10 = dot(n,normalize(cross(v0,v1)))*acos(dot(v1,v0));
    float k45 = dot(n,normalize(cross(v5,v4)))*acos(dot(v4,v5));
    float k57 = dot(n,normalize(cross(v7,v5)))*acos(dot(v5,v7));
    float k76 = dot(n,normalize(cross(v6,v7)))*acos(dot(v7,v6));
    float k37 = dot(n,normalize(cross(v7,v3)))*acos(dot(v3,v7));
    float k64 = dot(n,normalize(cross(v4,v6)))*acos(dot(v6,v4));
    float k51 = dot(n,normalize(cross(v1,v5)))*acos(dot(v5,v1));
    float k04 = dot(n,normalize(cross(v4,v0)))*acos(dot(v0,v4));
    float k62 = dot(n,normalize(cross(v2,v6)))*acos(dot(v6,v2));
    
    // 6 faces    
    float occ = 0.0;
    occ += ( k02 + k23 + k31 + k10) * step( 0.0,  v0.z );
    occ += ( k45 + k57 + k76 + k64) * step( 0.0, -v4.z );
    occ += ( k51 - k31 + k37 - k57) * step( 0.0, -v5.x );
    occ += ( k04 - k64 + k62 - k02) * step( 0.0,  v0.x );
    occ += (-k76 - k37 - k23 - k62) * step( 0.0, -v6.y );
    occ += (-k10 - k51 - k45 - k04) * step( 0.0,  v0.y );
        
    return occ / 6.283185;
}
This code will work only when the box is fully visible from the shading point.
For boxes that are partially below the visibility horizon, use this code that does
clipping in the polygons instead: https://www.shadertoy.com/view/4sSXDV
Maths / Article

Example / Code