Skip to content

Commit

Permalink
improve voxel clipping
Browse files Browse the repository at this point in the history
  • Loading branch information
ffreyer committed Jun 18, 2024
1 parent 9ca81a6 commit f3a9110
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 60 deletions.
23 changes: 23 additions & 0 deletions GLMakie/assets/shader/voxel.frag
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ flat in int plane_front;
uniform lowp usampler3D voxel_id;
uniform uint objectid;
uniform float gap;
uniform int _num_clip_planes;
uniform vec4 clip_planes[8];

{{uv_map_type}} uv_map;
{{color_map_type}} color_map;
Expand Down Expand Up @@ -81,6 +83,24 @@ vec4 get_color(sampler2D color, Nothing color_map, int id) {
return get_color_from_texture(color, id);
}

bool is_clipped()
{
float d1, d2;
ivec3 size = ivec3(textureSize(voxel_id, 0).xyz);
vec3 xyz = vec3(ivec3(o_uvw * size));
for (int i = 0; i < _num_clip_planes; i++) {
// distance from clip planes with negative clipped
d1 = dot(xyz, clip_planes[i].xyz) - clip_planes[i].w;
d2 = dot(xyz, clip_planes[i].xyz) - clip_planes[i].w;

// both outside - clip everything
if (d1 < 0.0 || d2 < 0.0) {
return true;
}
}

return false;
}

void write2framebuffer(vec4 color, uvec2 id);

Expand All @@ -90,6 +110,9 @@ vec3 illuminate(vec3 normal, vec3 base_color);

void main()
{
if (is_clipped())
discard;

vec2 voxel_uv = mod(o_tex_uv, 1.0);
if (voxel_uv.x < 0.5 * gap || voxel_uv.x > 1.0 - 0.5 * gap ||
voxel_uv.y < 0.5 * gap || voxel_uv.y > 1.0 - 0.5 * gap)
Expand Down
18 changes: 0 additions & 18 deletions GLMakie/assets/shader/voxel.vert
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,6 @@ uniform float depth_shift;
uniform bool depthsorting;
uniform float gap;

uniform int num_clip_planes;
uniform vec4 clip_planes[8];
out float gl_ClipDistance[8];

void process_clip_planes(vec3 world_pos)
{
// distance = dot(world_pos - plane.point, plane.normal)
// precalculated: dot(plane.point, plane.normal) -> plane.w
for (int i = 0; i < num_clip_planes; i++)
gl_ClipDistance[i] = dot(world_pos, clip_planes[i].xyz) - clip_planes[i].w;

// TODO: can be skipped?
for (int i = num_clip_planes; i < 8; i++)
gl_ClipDistance[i] = 1.0;
}


const vec3 unit_vecs[3] = vec3[]( vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1) );
const mat2x3 orientations[3] = mat2x3[](
mat2x3(0, 1, 0, 0, 0, 1), // xy -> _yz (x normal)
Expand Down Expand Up @@ -166,7 +149,6 @@ void main() {
vec3 plane_vertex = size * (orientations[dim] * vertices) + displacement;
vec4 world_pos = model * vec4(plane_vertex, 1.0f);
o_world_pos = world_pos.xyz;
process_clip_planes(world_pos.xyz);
gl_Position = projectionview * world_pos;
gl_Position.z += gl_Position.w * depth_shift;

Expand Down
26 changes: 26 additions & 0 deletions GLMakie/src/drawing_primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,32 @@ function draw_atomic(screen::Screen, scene::Scene, plot::Voxels)
)
end

# Handled manually without using OpenGL clipping
gl_attributes[:_num_clip_planes] = pop!(gl_attributes, :num_clip_planes)
gl_attributes[:num_clip_planes] = Observable(0)
pop!(gl_attributes, :clip_planes)
gl_attributes[:clip_planes] = map(plot, gl_attributes[:model], plot.clip_planes) do model, planes
# model/modelinv has no perspective projection so we should be fine
# with just applying it to the plane origin and transpose(inv(modelinv))
# to plane.normal
modelinv = inv(model)
@assert modelinv[4, 4] == 1

output = Vector{Vec4f}(undef, 8)
for i in 1:min(length(planes), 8)
origin = modelinv * to_ndim(Point4f, planes[i].distance * planes[i].normal, 1)
normal = transpose(model) * to_ndim(Vec4f, planes[i].normal, 0)
distance = dot(Vec3f(origin[1], origin[2], origin[3]) / origin[4],
Vec3f(normal[1], normal[2], normal[3]))
output[i] = Vec4f(normal[1], normal[2], normal[3], distance)
end
for i in min(length(planes), 8)+1:8
output[i] = Vec4f(0, 0, 0, -1e9)
end

return output
end

# color attribute adjustments
pop!(gl_attributes, :lowclip, nothing)
pop!(gl_attributes, :highclip, nothing)
Expand Down
26 changes: 23 additions & 3 deletions WGLMakie/assets/voxel.frag
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ flat in int plane_front;
#endif

uniform int num_clip_planes;
uniform vec4 clip_planes[8];

vec4 debug_color(uint id) {
return vec4(
Expand Down Expand Up @@ -82,6 +83,25 @@ vec3 blinnphong(vec3 N, vec3 V, vec3 L, vec3 color){
);
}

bool is_clipped()
{
float d1, d2;
vec3 size = vec3(textureSize(voxel_id, 0).xyz);
vec3 xyz = vec3(ivec3(o_uvw * size));
for (int i = 0; i < num_clip_planes; i++) {
// distance from clip planes with negative clipped
d1 = dot(xyz, clip_planes[i].xyz) - clip_planes[i].w;
d2 = dot(xyz, clip_planes[i].xyz) - clip_planes[i].w;

// both outside - clip everything
if (d1 < 0.0 || d2 < 0.0) {
return true;
}
}

return false;
}

flat in uint frag_instance_id;
vec4 pack_int(uint id, uint index) {
vec4 unpack;
Expand All @@ -91,11 +111,11 @@ vec4 pack_int(uint id, uint index) {
unpack.w = float((index & uint(0x00ff)) >> 0) / 255.0;
return unpack;
}

void main()
{
for (int i = 0; i < num_clip_planes; i++)
if (o_clip_distance[i] < 0.0)
discard;
if (is_clipped())
discard;

vec2 voxel_uv = mod(o_tex_uv, 1.0);
if (voxel_uv.x < 0.5 * gap || voxel_uv.x > 1.0 - 0.5 * gap ||
Expand Down
8 changes: 0 additions & 8 deletions WGLMakie/assets/voxel.vert
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ flat out int plane_front;
out vec3 o_camdir;

uniform mat4 projection, view;
uniform vec4 clip_planes[8];
uniform int num_clip_planes;

void process_clip_planes(vec3 world_pos) {
for (int i = 0; i < num_clip_planes; i++)
o_clip_distance[i] = dot(world_pos, clip_planes[i].xyz) - clip_planes[i].w;
}

const vec3 unit_vecs[3] = vec3[]( vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1) );
const mat2x3 orientations[3] = mat2x3[](
Expand Down Expand Up @@ -144,7 +137,6 @@ void main() {
// place plane vertices
vec3 plane_vertex = vec3(size) * (orientations[dim] * get_position()) + displacement;
vec4 world_pos = get_model() * vec4(plane_vertex, 1.0f);
process_clip_planes(world_pos.xyz);
gl_Position = projection * view * world_pos;
gl_Position.z += gl_Position.w * get_depth_shift();

Expand Down
81 changes: 50 additions & 31 deletions WGLMakie/src/serialization.jl
Original file line number Diff line number Diff line change
Expand Up @@ -370,37 +370,38 @@ function serialize_three(scene::Scene, @nospecialize(plot::AbstractPlot))
mesh[:cam_space] = to_value(get(plot, key, :data))

# Handle clip planes
if !(plot isa Volume)

clip_planes = map(plot, plot.clip_planes) do planes
if length(planes) > 8
@warn("Only up to 8 clip planes are supported. The rest are ignored!", maxlog = 1)
end
if plot isa Voxels

clip_planes = map(
plot, plot.converted..., plot.model, plot.clip_planes
) do xs, ys, zs, chunk, model, planes
# model/modelinv has no perspective projection so we should be fine
# with just applying it to the plane origin and transpose(inv(modelinv))
# to plane.normal
mini = minimum.((xs, ys, zs))
width = maximum.((xs, ys, zs)) .- mini
_model = Mat4f(model) *
Makie.scalematrix(Vec3f(width ./ size(chunk))) *
Makie.translationmatrix(Vec3f(mini))
modelinv = inv(_model)
@assert modelinv[4, 4] == 1

output = Vector{Vec4f}(undef, 8)
for i in 1:min(length(planes), 8)
output[i] = Makie.gl_plane_format(planes[i])
origin = modelinv * to_ndim(Point4f, planes[i].distance * planes[i].normal, 1)
normal = transpose(_model) * to_ndim(Vec4f, planes[i].normal, 0)
distance = dot(Vec3f(origin[1], origin[2], origin[3]) / origin[4],
Vec3f(normal[1], normal[2], normal[3]))
output[i] = Vec4f(normal[1], normal[2], normal[3], distance)
end
for i in min(length(planes), 8)+1:8
output[i] = Vec4f(0, 0, 0, -1e10)
output[i] = Vec4f(0, 0, 0, -1e9)
end

return output
end

uniforms[:clip_planes] = serialize_three(clip_planes[])
on(plot, clip_planes) do value
updater[] = [:clip_planes, serialize_three(value)]
return
end

uniforms[:num_clip_planes] = serialize_three(length(plot.clip_planes[]))
on(plot, plot.clip_planes) do planes
updater[] = [:num_clip_planes, serialize_three(length(planes))]
return
end

else
elseif plot isa Volume

# TODO: better solution (ShaderAbstractions doesn't like Vector uniforms)
model2 = lift(plot, plot.model, plot[1], plot[2], plot[3]) do m, xyz...
Expand Down Expand Up @@ -433,20 +434,38 @@ function serialize_three(scene::Scene, @nospecialize(plot::AbstractPlot))
return output
end

uniforms[:clip_planes] = serialize_three(clip_planes[])
on(plot, clip_planes) do value
updater[] = [:clip_planes, serialize_three(value)]
return
end

uniforms[:num_clip_planes] = serialize_three(length(plot.clip_planes[]))
on(plot, plot.clip_planes) do planes
updater[] = [:num_clip_planes, serialize_three(length(planes))]
return
else

clip_planes = map(plot, plot.clip_planes) do planes
if length(planes) > 8
@warn("Only up to 8 clip planes are supported. The rest are ignored!", maxlog = 1)
end

output = Vector{Vec4f}(undef, 8)
for i in 1:min(length(planes), 8)
output[i] = Makie.gl_plane_format(planes[i])
end
for i in min(length(planes), 8)+1:8
output[i] = Vec4f(0, 0, 0, -1e10)
end

return output
end

end

uniforms[:clip_planes] = serialize_three(clip_planes[])
on(plot, clip_planes) do value
updater[] = [:clip_planes, serialize_three(value)]
return
end

uniforms[:num_clip_planes] = serialize_three(length(plot.clip_planes[]))
on(plot, plot.clip_planes) do planes
updater[] = [:num_clip_planes, serialize_three(length(planes))]
return
end

return mesh
end

Expand Down

0 comments on commit f3a9110

Please sign in to comment.