diff options
Diffstat (limited to 'source/blender/draw/intern/shaders/common_intersect_lib.glsl')
-rw-r--r-- | source/blender/draw/intern/shaders/common_intersect_lib.glsl | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/source/blender/draw/intern/shaders/common_intersect_lib.glsl b/source/blender/draw/intern/shaders/common_intersect_lib.glsl new file mode 100644 index 00000000000..33378588553 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_intersect_lib.glsl @@ -0,0 +1,398 @@ + +/** + * Intersection library used for culling. + * Results are meant to be conservative. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_shape_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Plane extraction functions. + * \{ */ + +/** \a v1 and \a v2 are vectors on the plane. \a p is a point on the plane. */ +vec4 isect_plane_setup(vec3 p, vec3 v1, vec3 v2) +{ + vec3 normal_to_plane = normalize(cross(v1, v2)); + return vec4(normal_to_plane, -dot(normal_to_plane, p)); +} + +struct IsectPyramid { + vec3 corners[5]; + vec4 planes[5]; +}; + +IsectPyramid isect_data_setup(Pyramid shape) +{ + vec3 A1 = shape.corners[1] - shape.corners[0]; + vec3 A2 = shape.corners[2] - shape.corners[0]; + vec3 A3 = shape.corners[3] - shape.corners[0]; + vec3 A4 = shape.corners[4] - shape.corners[0]; + vec3 S4 = shape.corners[4] - shape.corners[1]; + vec3 S2 = shape.corners[2] - shape.corners[1]; + + IsectPyramid data; + data.planes[0] = isect_plane_setup(shape.corners[0], A2, A1); + data.planes[1] = isect_plane_setup(shape.corners[0], A3, A2); + data.planes[2] = isect_plane_setup(shape.corners[0], A4, A3); + data.planes[3] = isect_plane_setup(shape.corners[0], A1, A4); + data.planes[4] = isect_plane_setup(shape.corners[1], S2, S4); + for (int i = 0; i < 5; i++) { + data.corners[i] = shape.corners[i]; + } + return data; +} + +struct IsectBox { + vec3 corners[8]; + vec4 planes[6]; +}; + +IsectBox isect_data_setup(Box shape) +{ + vec3 A1 = shape.corners[1] - shape.corners[0]; + vec3 A3 = shape.corners[3] - shape.corners[0]; + vec3 A4 = shape.corners[4] - shape.corners[0]; + + IsectBox data; + data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1); + data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3); + data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4); + /* Assumes that the box is actually a box! */ + data.planes[3] = vec4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, shape.corners[6])); + data.planes[4] = vec4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, shape.corners[6])); + data.planes[5] = vec4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, shape.corners[6])); + for (int i = 0; i < 8; i++) { + data.corners[i] = shape.corners[i]; + } + return data; +} + +struct IsectFrustum { + vec3 corners[8]; + vec4 planes[6]; +}; + +IsectFrustum isect_data_setup(Frustum shape) +{ + vec3 A1 = shape.corners[1] - shape.corners[0]; + vec3 A3 = shape.corners[3] - shape.corners[0]; + vec3 A4 = shape.corners[4] - shape.corners[0]; + vec3 B5 = shape.corners[5] - shape.corners[6]; + vec3 B7 = shape.corners[7] - shape.corners[6]; + vec3 B2 = shape.corners[2] - shape.corners[6]; + + IsectFrustum data; + data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1); + data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3); + data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4); + data.planes[3] = isect_plane_setup(shape.corners[6], B7, B5); + data.planes[4] = isect_plane_setup(shape.corners[6], B5, B2); + data.planes[5] = isect_plane_setup(shape.corners[6], B2, B7); + for (int i = 0; i < 8; i++) { + data.corners[i] = shape.corners[i]; + } + return data; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name View Intersection functions. + * \{ */ + +bool intersect_view(Pyramid pyramid) +{ + bool intersects = true; + + /* Do Pyramid vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 5; ++v) { + float test = dot(drw_view.frustum_planes[p], vec4(pyramid.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Frustum vertices vs Pyramid planes. */ + IsectPyramid i_pyramid = isect_data_setup(pyramid); + for (int p = 0; p < 5; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_pyramid.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + return intersects; +} + +bool intersect_view(Box box) +{ + bool intersects = true; + + /* Do Box vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(drw_view.frustum_planes[p], vec4(box.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Frustum vertices vs Box planes. */ + IsectBox i_box = isect_data_setup(box); + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_box.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + return intersects; +} + +bool intersect_view(Sphere sphere) +{ + bool intersects = true; + + for (int p = 0; p < 6 && intersects; ++p) { + float dist_to_plane = dot(drw_view.frustum_planes[p], vec4(sphere.center, 1.0)); + if (dist_to_plane < -sphere.radius) { + intersects = false; + } + } + /* TODO reject false positive. */ + return intersects; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Shape vs. Shape Intersection functions. + * \{ */ + +bool intersect(IsectPyramid i_pyramid, Box box) +{ + bool intersects = true; + + /* Do Box vertices vs Pyramid planes. */ + for (int p = 0; p < 5; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_pyramid.planes[p], vec4(box.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Pyramid vertices vs Box planes. */ + IsectBox i_box = isect_data_setup(box); + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 5; ++v) { + float test = dot(i_box.planes[p], vec4(i_pyramid.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + return intersects; +} + +bool intersect(IsectFrustum i_frustum, Pyramid pyramid) +{ + bool intersects = true; + + /* Do Pyramid vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 5; ++v) { + float test = dot(i_frustum.planes[p], vec4(pyramid.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Frustum vertices vs Pyramid planes. */ + IsectPyramid i_pyramid = isect_data_setup(pyramid); + for (int p = 0; p < 5; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_pyramid.planes[p], vec4(i_frustum.corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + return intersects; +} + +bool intersect(IsectFrustum i_frustum, Box box) +{ + bool intersects = true; + + /* Do Box vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_frustum.planes[p], vec4(box.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Frustum vertices vs Box planes. */ + IsectBox i_box = isect_data_setup(box); + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_box.planes[p], vec4(i_frustum.corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + return intersects; +} + +bool intersect(IsectFrustum i_frustum, Sphere sphere) +{ + bool intersects = true; + for (int p = 0; p < 6; ++p) { + float dist_to_plane = dot(i_frustum.planes[p], vec4(sphere.center, 1.0)); + if (dist_to_plane < -sphere.radius) { + intersects = false; + break; + } + } + return intersects; +} + +bool intersect(Cone cone, Sphere sphere) +{ + /** + * Following "Improve Tile-based Light Culling with Spherical-sliced Cone" + * by Eric Zhang + * https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html + */ + float sphere_distance = length(sphere.center); + float sphere_distance_rcp = safe_rcp(sphere_distance); + float sphere_sin = saturate(sphere.radius * sphere_distance_rcp); + float sphere_cos = sqrt(1.0 - sphere_sin * sphere_sin); + float cone_aperture_sin = sqrt(1.0 - cone.angle_cos * cone.angle_cos); + + float cone_sphere_center_cos = dot(sphere.center * sphere_distance_rcp, cone.direction); + /* cos(A+B) = cos(A) * cos(B) - sin(A) * sin(B). */ + float cone_sphere_angle_sum_cos = (sphere.radius > sphere_distance) ? + -1.0 : + (cone.angle_cos * sphere_cos - + cone_aperture_sin * sphere_sin); + /* Comparing cosines instead of angles since we are interested + * only in the monotonic region [0 .. M_PI / 2]. This saves costly acos() calls. */ + bool intersects = (cone_sphere_center_cos >= cone_sphere_angle_sum_cos); + + return intersects; +} + +bool intersect(Circle circle_a, Circle circle_b) +{ + return distance_squared(circle_a.center, circle_b.center) < + sqr(circle_a.radius + circle_b.radius); +} + +/** \} */ |