Skip to content

Instantly share code, notes, and snippets.

@httpdigest
Last active July 20, 2020 04:10
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save httpdigest/15399efe2b60a2b31d1c2cbe414ce5cf to your computer and use it in GitHub Desktop.
Save httpdigest/15399efe2b60a2b31d1c2cbe414ce5cf to your computer and use it in GitHub Desktop.
Hierarchical GPU Frustum Culling and MultiDrawArraysIndirectCount Buffer Generation
#version 430 core
#ifdef GL_NV_gpu_shader5
#extension GL_NV_gpu_shader5 : enable
#elif GL_AMD_gpu_shader_int16
#extension GL_AMD_gpu_shader_int16 : enable
#endif
#ifdef GL_ARB_shader_atomic_counter_ops
#extension GL_ARB_shader_atomic_counter_ops : enable
#endif
struct nodegeom {
u8vec3 min;
u8vec3 max;
uint16_t left, right;
};
layout(binding = 0) uniform atomic_uint counter;
layout(std430, binding = 1) readonly restrict buffer NodeGeometries { nodegeom[] nodeGeomtries; };
layout(std430, binding = 2) readonly restrict buffer InputNodes { uint16_t[] inputNodes; };
layout(std430, binding = 3) writeonly restrict buffer OutputNodes { uint16_t[] outputNodes; };
layout(std430, binding = 4) readonly buffer Counts { uint[] counts; };
struct node {
u8vec3 min;
u8vec3 max;
uint16_t left, right;
uint firstVoxel;
uint numVoxels;
uint8_t splitAxis; uint8_t splitPos;
uint16_t ropes[6];
};
layout(std430, binding = 5) readonly restrict buffer Nodes { node[] nodes; };
struct DrawArraysIndirectCommand {
uint count;
uint instanceCount;
uint first;
uint baseInstance;
};
layout(std430, binding = 6) writeonly restrict buffer DrawCommands { DrawArraysIndirectCommand[] drawCommands; };
#define MODE_CULL 0u
#define MODE_NODES_TO_DRAWCALLS 1u
uniform mat4 vpt;
uniform uint mode = MODE_CULL;
uniform vec3 cameraPosition = vec3(0.0, 0.0, 0.0);
bool frustumCull(const in nodegeom n, const in vec4[6] p) {
#define CMP(N) (dot(p[N].xyz, mix(vec3(n.min), vec3(n.max)+1.0, greaterThan(p[N].xyz, vec3(0.0)))) >= -p[N].w)
return CMP(0) && CMP(1) && CMP(2) && CMP(3) && CMP(4) && CMP(5);
#undef CMP
}
bool largeEnough(const in nodegeom n) {
float diag = length(vec3(n.max) + vec3(1.0) - vec3(n.min));
vec3 cent = (vec3(n.max) + vec3(1.0) + vec3(n.min)) * 0.5;
float camDist = distance(cent, cameraPosition);
return diag / camDist > 0.5;
}
#define NO_NODE uint16_t(-1u)
layout (local_size_x = 8, local_size_y = 8) in;
void cull(void) {
const uint nindex = gl_GlobalInvocationID.y * gl_NumWorkGroups.x * gl_WorkGroupSize.x + gl_GlobalInvocationID.x;
if (nindex >= counts[0] || nindex >= inputNodes.length())
return;
const vec4 p[6] = vec4[6](vpt[3] + vpt[0], vpt[3] - vpt[0],
vpt[3] + vpt[1], vpt[3] - vpt[1],
vpt[3] + vpt[2], vpt[3] - vpt[2]);
const uint16_t nidx = inputNodes[nindex];
const nodegeom n = nodeGeomtries[nidx];
const bool pred = frustumCull(n, p);
if (pred) {
if (largeEnough(n) && n.left != NO_NODE) {
#ifdef GL_ARB_shader_atomic_counter_ops
uint idx = atomicCounterAddARB(counter, 2u);
outputNodes[idx] = n.left;
outputNodes[idx+1u] = n.right;
#else
outputNodes[atomicCounterIncrement(counter)] = n.left;
outputNodes[atomicCounterIncrement(counter)] = n.right;
#endif
} else {
outputNodes[atomicCounterIncrement(counter)] = nidx;
}
}
}
void producedraws(void) {
const uint nindex = gl_GlobalInvocationID.y * gl_NumWorkGroups.x * gl_WorkGroupSize.x + gl_GlobalInvocationID.x;
if (nindex >= counts[0] || nindex >= inputNodes.length())
return;
const uint16_t nidx = inputNodes[nindex];
const node n = nodes[nidx];
DrawArraysIndirectCommand cmd;
cmd.count = n.numVoxels;
cmd.instanceCount = 1u;
cmd.first = n.firstVoxel;
cmd.baseInstance = 0u;
drawCommands[atomicCounterIncrement(counter)] = cmd;
}
void main(void) {
switch (mode) {
case MODE_CULL:
cull();
break;
case MODE_NODES_TO_DRAWCALLS:
producedraws();
break;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment