Last active
April 3, 2025 14:19
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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