Skip to content

Instantly share code, notes, and snippets.

@zeux
Last active July 30, 2023 04:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zeux/19b1ba2ce3121e9933da18802fed09d6 to your computer and use it in GitHub Desktop.
Save zeux/19b1ba2ce3121e9933da18802fed09d6 to your computer and use it in GitHub Desktop.
Shader code used in "Approximate projected bounds" article, used for profiling with offline cycle estimation tools.
#version 450
// 2D Polyhedral Bounds of a Clipped, Perspective-Projected 3D Sphere. Michael Mara, Morgan McGuire. 2013
bool projectSphereView(vec3 c, float r, float znear, float P00, float P11, out vec4 aabb)
{
if (c.z < r + znear) return false;
vec3 cr = c * r;
float czr2 = c.z * c.z - r * r;
float vx = sqrt(c.x * c.x + czr2);
float minx = (vx * c.x - cr.z) / (vx * c.z + cr.x);
float maxx = (vx * c.x + cr.z) / (vx * c.z - cr.x);
float vy = sqrt(c.y * c.y + czr2);
float miny = (vy * c.y - cr.z) / (vy * c.z + cr.y);
float maxy = (vy * c.y + cr.z) / (vy * c.z - cr.y);
aabb = vec4(minx * P00, miny * P11, maxx * P00, maxy * P11);
// clip space -> uv space
aabb = aabb.xwzy * vec4(0.5f, -0.5f, 0.5f, -0.5f) + vec4(0.5f);
return true;
}
bool projectBox(vec3 bmin, vec3 bmax, float znear, mat4 viewProjection, out vec4 aabb)
{
vec4 SX = vec4(bmax.x - bmin.x, 0.0, 0.0, 0.0) * viewProjection;
vec4 SY = vec4(0.0, bmax.y - bmin.y, 0.0, 0.0) * viewProjection;
vec4 SZ = vec4(0.0, 0.0, bmax.z - bmin.z, 0.0) * viewProjection;
vec4 P0 = vec4(bmin.x, bmin.y, bmin.z, 1.0) * viewProjection;
vec4 P1 = P0 + SZ;
vec4 P2 = P0 + SY;
vec4 P3 = P2 + SZ;
vec4 P4 = P0 + SX;
vec4 P5 = P4 + SZ;
vec4 P6 = P4 + SY;
vec4 P7 = P6 + SZ;
if (min(min(min(P0.w, P1.w), min(P2.w, P3.w)), min(min(P4.w, P5.w), min(P6.w, P7.w))) < znear) return false;
aabb.xy = min(
min(min(P0.xy / P0.w, P1.xy / P1.w), min(P2.xy / P2.w, P3.xy / P3.w)),
min(min(P4.xy / P4.w, P5.xy / P5.w), min(P6.xy / P6.w, P7.xy / P7.w)));
aabb.zw = max(
max(max(P0.xy / P0.w, P1.xy / P1.w), max(P2.xy / P2.w, P3.xy / P3.w)),
max(max(P4.xy / P4.w, P5.xy / P5.w), max(P6.xy / P6.w, P7.xy / P7.w)));
// clip space -> uv space
aabb = aabb.xwzy * vec4(0.5f, -0.5f, 0.5f, -0.5f) + vec4(0.5f);
return true;
}
bool projectBoxView(vec3 c, vec3 r, float znear, float P00, float P11, out vec4 aabb)
{
if (c.z - r.z < znear) return false;
// when we're computing the extremum of projection along an axis, the maximum
// is reached by front face for positive and by back face for negative values
float rminz = 1 / (c.z - r.z);
float rmaxz = 1 / (c.z + r.z);
float minx = (c.x - r.x) * (c.x - r.x >= 0 ? rmaxz : rminz);
float maxx = (c.x + r.x) * (c.x + r.x >= 0 ? rminz : rmaxz);
float miny = (c.y - r.y) * (c.y - r.y >= 0 ? rmaxz : rminz);
float maxy = (c.y + r.y) * (c.y + r.y >= 0 ? rminz : rmaxz);
aabb = vec4(minx * P00, miny * P11, maxx * P00, maxy * P11);
// clip space -> uv space
aabb = aabb.xwzy * vec4(0.5f, -0.5f, 0.5f, -0.5f) + vec4(0.5f);
return true;
}
bool projectSphere(vec3 c, float r, mat4 view, float znear, float P00, float P11, out vec4 aabb)
{
vec4 cv = vec4(c, 1.0) * view;
return projectSphereView(cv.xyz, r, znear, P00, P11, aabb);
}
bool projectBoxApprox(vec3 min, vec3 max, mat4 view, float znear, float P00, float P11, out vec4 aabb)
{
vec4 c = vec4((min + max) * 0.5, 1.0) * view;
vec3 s = (max - min) * 0.5;
vec3 r = s * mat3(abs(view[0].xyz), abs(view[1].xyz), abs(view[2].xyz));
return projectBoxView(c.xyz, r, znear, P00, P11, aabb);
}
bool projectSphereApprox(vec3 c, float r, mat4 view, float znear, float P00, float P11, out vec4 aabb)
{
vec4 cv = vec4(c, 1.0) * view;
return projectBoxView(cv.xyz, vec3(r), znear, P00, P11, aabb);
}
layout(binding=0)
uniform Foo {
mat4 World;
mat4 Projection;
float ZNear;
float P00;
float P11;
};
layout(location = 0) in vec4 sphere;
layout(location = 1) in vec4 bmin;
layout(location = 2) in vec4 bmax;
layout(location = 0) out vec4 outColor;
void main()
{
vec4 aabb = vec4(0.0);
// Uncomment one of these:
// projectSphere(sphere.xyz, sphere.w, World, ZNear, P00, P11, aabb);
// projectSphereApprox(sphere.xyz, sphere.w, World, ZNear, P00, P11, aabb);
// projectBoxApprox(bmin.xyz, bmax.xyz, World, ZNear, P00, P11, aabb);
// projectBox(bmin.xyz, bmax.xyz, ZNear, Projection, aabb);
// Note: RGA miscompiles projectBoxApprox call for some reason, but using sphere instead of bmin/bmax allows to estimate cycles
outColor = aabb;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment