void sreFrustum::CalculateShadowCasterVolume(const Vector4D& lightpos, int nu_frustum_planes) { nu_frustum_planes = maxf(nu_frustum_planes, SRE_NU_FRUSTUM_PLANES); // Note: for beam lights, this might be inaccurate. if (lightpos.w == 1.0f && Intersects(lightpos.GetPoint3D(), frustum_world)) { // If the point light position is inside the view frustum, we only need to consider the // set of visible objects. shadow_caster_volume.nu_planes = nu_frustum_planes; for (int i = 0; i < nu_frustum_planes; i++) shadow_caster_volume.plane[i] = frustum_world.plane[i]; goto end; } // Calculate the convex hull enclosing the view frustum and the light source. shadow_caster_volume.nu_planes = 0; // Calculate the dot products between the frustum planes and the light source. float dot[6] DST_ALIGNED(16); // The SIMD-accelerated version check for 16-bytes aligned arrays, which is hard to // guarantee in this case; otherwise, no SIMD will be used. #if 1 dstCalculateDotProductsNx1(nu_frustum_planes, &frustum_world.plane[0], lightpos, &dot[0]); #else for (int i = 0; i < nu_frustum_planes; i++) dot[i] = Dot(frustum_world.plane[i], lightpos); #endif // For each frustum plane, add it if it is part of the convex hull. for (int i = 0; i < nu_frustum_planes; i++) if (dot[i] > 0) { shadow_caster_volume.plane[shadow_caster_volume.nu_planes] = frustum_world.plane[i]; shadow_caster_volume.nu_planes++; } // printf("Shadow caster volume planes: from frustum: %d\n", shadow_caster_volume.nu_planes); if (shadow_caster_volume.nu_planes == 0 && lightpos.w == 1.0f) { // Special case: the point light source is behind the camera and there is no far plane. // As a result, there is no plane from the frustum with a positive dot product. In // this case, construct a volume consisting of the lightsource and four planes parallel // to the frustum side planes but starting at the lightsource. for (int i = 0; i < 4; i++) { // Copy the normal. shadow_caster_volume.plane[i] = frustum_world.plane[i]; // Calculate the distance such that the lightsource is in the plane. shadow_caster_volume.plane[i].w = - Dot(frustum_world.plane[i].GetVector3D(), lightpos.GetPoint3D()); } shadow_caster_volume.nu_planes = 4; goto end; } // For each pair of adjacent frustum planes, if one has a positive dot product and the other // does not, calculate a new plane defined by the edge between those two frustum planes and // the position of the light source, making sure the plane's normal direction faces inward. // For directional lights, the plane runs parallel to the direction of the light. nu_shadow_caster_edges = 0; int n; if (nu_frustum_planes == 5) n = 8; else n = 12; for (int i = 0; i < n; i++) if ((dot[adjacent_plane[i].plane0] > 0 && dot[adjacent_plane[i].plane1] <= 0) || (dot[adjacent_plane[i].plane0] <= 0 && dot[adjacent_plane[i].plane1] > 0)) { // printf("Setting plane from adjacent planes %d and %d using frustum vertices %d and %d\n", // adjacent_plane[i].plane0, adjacent_plane[i].plane1, adjacent_plane[i].vertex0, // adjacent_plane[i].vertex1); if (lightpos.w == 1.0f) shadow_caster_volume.plane[shadow_caster_volume.nu_planes] = dstPlaneFromPoints( frustum_world.hull.vertex[adjacent_plane[i].vertex0], frustum_world.hull.vertex[adjacent_plane[i].vertex1], lightpos.GetPoint3D() ); else { shadow_caster_volume.plane[shadow_caster_volume.nu_planes] = dstPlaneFromPoints( frustum_world.hull.vertex[adjacent_plane[i].vertex0], frustum_world.hull.vertex[adjacent_plane[i].vertex1], frustum_world.hull.vertex[adjacent_plane[i].vertex0] + lightpos.GetVector3D() ); shadow_caster_edge[nu_shadow_caster_edges][0] = adjacent_plane[i].vertex0; shadow_caster_edge[nu_shadow_caster_edges][1] = adjacent_plane[i].vertex1; nu_shadow_caster_edges++; } // Make sure the normal is pointed inward, check by taking the dot product with the frustum // "centroid". shadow_caster_volume.plane[shadow_caster_volume.nu_planes].OrientPlaneTowardsPoint( frustum_world.sphere.center); shadow_caster_volume.nu_planes++; } end: ; #if 0 printf("Light (%lf, %lf, %lf, %lf), %d planes in shadow caster volume.\n", lightpos.x, lightpos.y, lightpos.z, lightpos.w, shadow_caster_volume.nu_planes); for (int i = 0; i < shadow_caster_volume.nu_planes; i++) printf("Plane %d: (%lf, %lf, %lf, %lf) ", i, shadow_caster_volume.plane[i].x, shadow_caster_volume.plane[i].y, shadow_caster_volume.plane[i].y, shadow_caster_volume.plane[i].w); printf("\n"); #endif }
void sreFrustum::CalculateNearClipVolume(const Vector4D& lightpos) { // Calculate the occlusion pyramid with the tip at the lightsource and the base // consisting of the viewport on the near clipping plane. // Note: for beam lights, this might be inaccurate. // Transform the light position to eye space. Vector4D lightpos_eye = sre_internal_view_matrix * lightpos; // Calculate the distance of the lightsource to the near plane. float d = Dot(lightpos_eye, Vector4D(0, 0, - 1, - nearD)); if (d < - 0.001) { // Light source lies behind the near plane. light_position_type = SRE_LIGHT_POSITION_BEHIND_NEAR_PLANE; } else if (d > 0.001) { // Light source lies in front of the near plane. light_position_type = SRE_LIGHT_POSITION_IN_FRONT_OF_NEAR_PLANE; } else { // Light source lies "in" the near plane. light_position_type = SRE_LIGHT_POSITION_IN_NEAR_PLANE; } near_clip_volume.nu_planes = 0; if (light_position_type != SRE_LIGHT_POSITION_IN_NEAR_PLANE) { // The light source lies in front of or behind the near plane. // Calculate the four planes Ki. for (int i = 0; i < 4; i++) { // Use the vertices of the near plane in world space (frustum_world.hull.vertex[i]). // Calculate the normal. Vector3D N = Cross(frustum_world.hull.vertex[i] - frustum_world.hull.vertex[(i - 1) & 3], lightpos.GetVector3D() - lightpos.w * frustum_world.hull.vertex[i]); if (light_position_type == SRE_LIGHT_POSITION_BEHIND_NEAR_PLANE) N = - N; // Calculate the parametric plane. Vector4D K = 1 / Magnitude(N) * Vector4D(N.x, N.y, N.z, - Dot(N, frustum_world.hull.vertex[i])); near_clip_volume.plane[i] = K; near_clip_volume.nu_planes = 4; } } Matrix4D transpose_view_matrix = Transpose(sre_internal_view_matrix); if (light_position_type != SRE_LIGHT_POSITION_IN_NEAR_PLANE) { // Calculate the fifth plane that is coindicent with the near plane and has a normal pointing // towards the light source. Vector4D K4 = transpose_view_matrix * Vector4D(0, 0, - 1, - nearD); if (light_position_type == SRE_LIGHT_POSITION_BEHIND_NEAR_PLANE) K4 = -K4; near_clip_volume.plane[4] = K4; near_clip_volume.nu_planes = 5; } if (lightpos.w == 1.0f) { // For point lights only. // Calculate the sixth plane that contains the light position and has a normal direction that // points towards the center of the near rectangle. // Vector3D N5 = (transpose_view_matrix * Vector4D(0, 0, - nearD, 1)).GetVector3D() - // lightpos.GetVector3D(); Matrix4D inverse_view_matrix = Inverse(sre_internal_view_matrix); Vector3D N5 = (inverse_view_matrix * Vector4D(0, 0, - nearD, 1)).GetVector3D() - lightpos.GetVector3D(); Vector4D K5 = 1 / Magnitude(N5) * Vector4D(N5.x, N5.y, N5.z, - Dot(N5, lightpos)); near_clip_volume.plane[near_clip_volume.nu_planes] = K5; near_clip_volume.nu_planes++; light_position_type |= SRE_LIGHT_POSITION_POINT_LIGHT; } }