Files
test2/source/blender/draw/intern/shaders/draw_intersect_lib.glsl
Clément Foucault 7e5bc58649 GPU: Change GLSL include directive
This changes the include directive to use the standard C preprocessor
`#include` directive.

The regex to applied to all glsl sources is:
`pragma BLENDER_REQUIRE\((\w+\.glsl)\)`
`include "$1"`

This allow C++ linter to parse the code and allow easier codebase
traversal.

However there is a small catch. While it does work like a standard
include directive when the code is treated as C++, it doesn't when
compiled by our shader backends. In this case, we still use our
dependency concatenation approach instead of file injection.

This means that included files will always be prepended when compiled
to GLSL and a file cannot be appended more than once.

This is why all GLSL lib file should have the `#pragma once` directive
and always be included at the start of the file.

These requirements are actually already enforced by our code-style
in practice.

On the implementation, the source needed to be mutated to comment
the `#pragma once` and `#include`. This is needed to avoid GLSL
compiler error out as this is an extension that not all vendor
supports.

Rel #127983
Pull Request: https://projects.blender.org/blender/blender/pulls/128076
2024-10-04 15:48:22 +02:00

523 lines
15 KiB
GLSL

/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/**
* Intersection library used for culling.
* Results are meant to be conservative.
*/
#include "common_shape_lib.glsl"
#include "draw_math_geom_lib.glsl"
#include "gpu_shader_utildefines_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_pyramid_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_box_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;
}
/* Construct box from 1 corner point + 3 side vectors. */
IsectBox isect_box_setup(vec3 origin, vec3 side_x, vec3 side_y, vec3 side_z)
{
IsectBox data;
data.corners[0] = origin;
data.corners[1] = origin + side_x;
data.corners[2] = origin + side_y + side_x;
data.corners[3] = origin + side_y;
data.corners[4] = data.corners[0] + side_z;
data.corners[5] = data.corners[1] + side_z;
data.corners[6] = data.corners[2] + side_z;
data.corners[7] = data.corners[3] + side_z;
data.planes[0] = isect_plane_setup(data.corners[0], side_y, side_z);
data.planes[1] = isect_plane_setup(data.corners[0], side_x, side_y);
data.planes[2] = isect_plane_setup(data.corners[0], side_z, side_x);
/* Assumes that the box is actually a box! */
data.planes[3] = vec4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, data.corners[6]));
data.planes[4] = vec4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, data.corners[6]));
data.planes[5] = vec4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, data.corners[6]));
return data;
}
struct IsectFrustum {
vec3 corners[8];
vec4 planes[6];
};
IsectFrustum isect_frustum_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.
* \{ */
#ifdef DRW_VIEW_CULLING_INFO
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_culling.frustum_planes.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_pyramid_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_culling.frustum_corners.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_culling.frustum_planes.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_box_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_culling.frustum_corners.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(IsectBox i_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_culling.frustum_planes.planes[p], vec4(i_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;
}
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_culling.frustum_corners.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_culling.frustum_planes.planes[p], vec4(sphere.center, 1.0));
if (dist_to_plane < -sphere.radius) {
intersects = false;
}
}
/* TODO reject false positive. */
return intersects;
}
#endif
/** \} */
/* ---------------------------------------------------------------------- */
/** \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_box_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(IsectPyramid i_pyramid, IsectBox i_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(i_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. */
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_pyramid_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_box_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) <
square(circle_a.radius + circle_b.radius);
}
/** \} */