@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
170 lines (129 loc) • 6.9 kB
JavaScript
import { GLSL3, ShaderMaterial, Vector2 } from "three";
export function makeQueryShader() {
return new ShaderMaterial({
vertexShader: `
void main() {
gl_Position = vec4( position, 1.0 );
}
`,
fragmentShader: `
uniform sampler2D utDepth;
uniform sampler2D utInput;
uniform vec2 uDepthMipLimits;
out highp vec4 out_value;
vec3 read_input_v3(ivec2 start){
float x = texelFetch(utInput, start, 0).x;
float y = texelFetch(utInput, start + ivec2(1,0), 0).x;
float z = texelFetch(utInput, start + ivec2(2,0), 0).x;
return vec3(x,y,z);
}
vec4 read_input_v4(ivec2 start){
return vec4(
read_input_v3(start),
texelFetch(utInput, start + ivec2(3,0) , 0).x
);
}
mat4 read_input_m4(ivec2 start){
return mat4(
read_input_v4(start),
read_input_v4(start + ivec2(4,0)),
read_input_v4(start + ivec2(8,0)),
read_input_v4(start + ivec2(12,0))
);
}
vec3 project_vec3(vec3 value, mat4 transform){
vec4 v1 = transform * vec4( value, 1.0 );
v1.xyz /= abs(v1.w);
return v1.xyz;
}
void project_aabb(out vec3 pb_min, out vec3 pb_max, in vec3 bb_min, in vec3 bb_max, in mat4 transform){
vec3 p0 = project_vec3( bb_min, transform );
vec3 p1 = project_vec3( vec3(bb_min.xy, bb_max.z), transform );
pb_min = min( p0, p1 );
pb_max = max( p0, p1 );
vec3 p2 = project_vec3( vec3(bb_min.x, bb_max.y, bb_min.z), transform );
pb_min = min( pb_min, p2 );
pb_max = max( pb_max, p2 );
vec3 p3 = project_vec3( vec3( bb_min.x, bb_max.y, bb_max.z ), transform );
pb_min = min( pb_min, p3 );
pb_max = max( pb_max, p3 );
vec3 p4 = project_vec3( vec3( bb_max.x, bb_min.y, bb_min.z), transform );
pb_min = min( pb_min, p4 );
pb_max = max( pb_max, p4 );
vec3 p5 = project_vec3( vec3( bb_max.x, bb_min.y, bb_max.z ), transform );
pb_min = min( pb_min, p5 );
pb_max = max( pb_max, p5 );
vec3 p6 = project_vec3( vec3( bb_max.x, bb_max.y, bb_min.z ), transform );
pb_min = min( pb_min, p6 );
pb_max = max( pb_max, p6 );
vec3 p7 = project_vec3( vec3( bb_max.x, bb_max.y, bb_max.z ), transform );
pb_min = min( pb_min, p7 );
pb_max = max( pb_max, p7 );
}
int compute_mip_level_for_dimension(float mip_limit, float ndc_extents){
return max(0, int( floor( mip_limit - log2( 1.0 / ndc_extents) ) ) );
}
float query_depth_from_screen_space_bb(in vec3 pb_min, in vec3 pb_max){
vec3 n_min = clamp( (pb_min+1.0)*0.5, 0.0, 1.0);
vec3 n_max = clamp( (pb_max+1.0)*0.5, 0.0, 1.0);
float bb_width = ( n_max.x - n_min.x ) * 0.5;
float bb_height = ( n_max.y - n_min.y ) * 0.5 ;
// pick coarsest z-buffer mip to test 4x4 pixels of the bounding box against
int mip_level_x = compute_mip_level_for_dimension( uDepthMipLimits.x, bb_width );
int mip_level_y = compute_mip_level_for_dimension( uDepthMipLimits.y, bb_height );
// figure out which mip level to use
int mip_to_use = max(mip_level_x, mip_level_y);
// sample 4 pixels that cover AABB
float n0 = textureLod(utDepth, n_min.xy, float(mip_to_use)).x;
float n1 = textureLod(utDepth, vec2(n_min.x, n_max.y), float(mip_to_use)).x;
float n2 = textureLod(utDepth, vec2(n_max.y, n_min.y), float(mip_to_use)).x;
float n3 = textureLod(utDepth, n_max.xy, float(mip_to_use)).x;
float n = max(n0, max(n1, max(n2,n3)));
float depth_delta = n - n_min.z;
return depth_delta;
// return float( uDepthMipLimits.x );
// return float( depth_delta*255.0 );
}
uint query_bounds(vec3 bb_min, vec3 bb_max, mat4 transform){
vec3 pb_min;
vec3 pb_max;
// project bounds to screen space
project_aabb(pb_min, pb_max, bb_min, bb_max, transform);
if(
pb_max.x < -1.0 || pb_max.y < -1.0 || pb_max.z < -1.0
|| pb_min.x > 1.0 || pb_min.y > 1.0 || pb_min.z > 1.0
){
// outside of view frustum
return 0u;
}
float n = query_depth_from_screen_space_bb( pb_min, pb_max );
if(n < 0.0){
return 0u;
}else{
return 255u;
}
}
void main() {
ivec2 input_offset = ivec2( int(gl_FragCoord.x) * ITEM_SIZE, int(gl_FragCoord.y) );
vec3 bb_min = read_input_v3(input_offset);
vec3 bb_max = read_input_v3(input_offset + ivec2(3,0));
mat4 transform = read_input_m4(input_offset + ivec2(6,0));
out_value = vec4( float( query_bounds(bb_min, bb_max, transform) ) / 255.0, 0.0, 0.0, 1.0 );
}
`,
depthTest: false,
depthWrite: false,
uniforms: {
utDepth: {
value: null
},
utInput: {
value: null
},
uDepthMipLimits: {
value: new Vector2(1, 1)
}
},
glslVersion: GLSL3
});
}