Welcome to mirror list, hosted at ThFree Co, Russian Federation.

eevee_culling_lib.glsl « shaders « eevee « engines « draw « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f128b89e86478f15a49bf6ed74998b9eac8d03db (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179

#pragma BLENDER_REQUIRE(common_view_lib.glsl)
#pragma BLENDER_REQUIRE(common_math_lib.glsl)
#pragma BLENDER_REQUIRE(eevee_shader_shared.hh)

/* ---------------------------------------------------------------------- */
/** \name Intersection Tests
 * \{ */

struct Sphere {
  vec3 position;
  float radius;
};

struct Cone {
  vec3 direction;
  float angle_cos;
};

struct Cylinder {
  /* Assume Z axis as direction. */
  vec3 center;
  float radius;
};

struct Frustum {
  vec4 planes[4];
};

struct CullingTile {
  Frustum frustum;
  Cone cone;
};

bool culling_sphere_cone_isect(Sphere sphere, Cone cone)
{
  /**
   * 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.position);
  float sphere_sin = saturate(sphere.radius / sphere_distance);
  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.position / sphere_distance, 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. */
  return (cone_sphere_center_cos >= cone_sphere_angle_sum_cos);
}

bool culling_sphere_cylinder_isect(Sphere sphere, Cylinder cylinder)
{
  float distance_squared = len_squared(sphere.position.xy - cylinder.center.xy);
  return (distance_squared < sqr(cylinder.radius + sphere.radius));
}

bool culling_sphere_frustum_isect(Sphere sphere, Frustum frustum)
{
  if (dot(vec4(sphere.position, 1.0), frustum.planes[0]) > sphere.radius) {
    return false;
  }
  if (dot(vec4(sphere.position, 1.0), frustum.planes[1]) > sphere.radius) {
    return false;
  }
  if (dot(vec4(sphere.position, 1.0), frustum.planes[2]) > sphere.radius) {
    return false;
  }
  if (dot(vec4(sphere.position, 1.0), frustum.planes[3]) > sphere.radius) {
    return false;
  }
  return true;
}

bool culling_sphere_tile_isect(Sphere sphere, CullingTile tile)
{
  /* Culling in view space for precision and simplicity. */
  sphere.position = transform_point(ViewMatrix, sphere.position);
  bool isect;
  /* Test tile intersection using bounding cone or bounding cylinder.
   * This has less false positive cases when the sphere is large. */
  if (ProjectionMatrix[3][3] == 0.0) {
    isect = culling_sphere_cone_isect(sphere, tile.cone);
  }
  else {
    Cylinder cylinder = Cylinder(tile.cone.direction, tile.cone.angle_cos);
    isect = culling_sphere_cylinder_isect(sphere, cylinder);
  }
  /* Refine using frustum test. If the sphere is small it avoids intersection
   * with a neighbor tile. */
  if (isect) {
    isect = culling_sphere_frustum_isect(sphere, tile.frustum);
  }
  return isect;
}

/** \} */

/* ---------------------------------------------------------------------- */
/** \name Culling shapes extraction
 * \{ */

vec4 plane_from_quad(vec3 v0, vec3 v1, vec3 v2, vec3 v3)
{
  vec3 nor = normalize(cross(v2 - v1, v0 - v1) + cross(v0 - v3, v2 - v3));
  return vec4(nor, -dot(nor, v2));
}

/* Corners are expected to be in viewspace. */
Cone cone_from_quad(vec3 corners[8])
{
  for (int i = 0; i < 4; i++) {
    corners[i] = normalize(corners[i]);
  }
  vec3 center = normalize(corners[0] + corners[1] + corners[2] + corners[3]);

  vec4 corners_cos;
  for (int i = 0; i < 4; i++) {
    corners_cos[i] = dot(center, corners[i]);
  }
  return Cone(center, max_v4(corners_cos));
}

/* Corners are expected to be in viewspace. Returns Z-aligned bounding cylinder. */
Cone cylinder_from_quad(vec3 corners[8])
{
  vec3 center = (corners[0] + corners[1] + corners[2] + corners[3]) * 0.25;

  vec4 corners_dist;
  for (int i = 0; i < 4; i++) {
    corners_dist[i] = distance_squared(center, corners[i]);
  }
  /* Return a cone. Later converted to cylinder. */
  return Cone(center, sqrt(max_v4(corners_dist)));
}

vec2 tile_to_ndc(CullingData culling, vec2 tile_co, vec2 offset)
{
  /* Add a margin to prevent culling too much if the frustum becomes too much unstable. */
  tile_co += /* culling.tile_margin * */ offset;
  return tile_co * culling.tile_to_uv_fac * 2.0 - 1.0;
}

CullingTile culling_tile_get(CullingData culling)
{
  /* Culling frustum corners for this tile. */
  vec3 corners[8];
  corners[0].xy = corners[4].xy = tile_to_ndc(culling, gl_FragCoord.xy, vec2(0.5, 0.5));
  corners[1].xy = corners[5].xy = tile_to_ndc(culling, gl_FragCoord.xy, vec2(0.5, -0.5));
  corners[2].xy = corners[6].xy = tile_to_ndc(culling, gl_FragCoord.xy, vec2(-0.5, -0.5));
  corners[3].xy = corners[7].xy = tile_to_ndc(culling, gl_FragCoord.xy, vec2(-0.5, 0.5));
  /* The corners depth only matter for precision. Use a mix of not so close to clip plane to
   * avoid small float imprecision if near clip is low. */
  corners[0].z = corners[1].z = corners[2].z = corners[3].z = -0.5;
  corners[4].z = corners[5].z = corners[6].z = corners[7].z = 0.1;

  for (int i = 0; i < 8; i++) {
    /* Culling in view space for precision. */
    corners[i] = project_point(ProjectionMatrixInverse, corners[i]);
  }

  bool is_persp = ProjectionMatrix[3][3] == 0.0;
  CullingTile tile;
  tile.cone = (is_persp) ? cone_from_quad(corners) : cylinder_from_quad(corners);
  tile.frustum.planes[0] = plane_from_quad(corners[0], corners[1], corners[5], corners[4]);
  tile.frustum.planes[1] = plane_from_quad(corners[1], corners[2], corners[6], corners[5]);
  tile.frustum.planes[2] = plane_from_quad(corners[2], corners[3], corners[7], corners[6]);
  tile.frustum.planes[3] = plane_from_quad(corners[3], corners[0], corners[4], corners[7]);
  return tile;
}

/** \} */