Skip to content

Commit

Permalink
add gap attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
ffreyer committed Jan 11, 2024
1 parent 6d6cb6b commit 2885ae8
Show file tree
Hide file tree
Showing 11 changed files with 130 additions and 58 deletions.
2 changes: 1 addition & 1 deletion CairoMakie/src/primitives.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1142,7 +1142,7 @@ function draw_atomic(scene::Scene, screen::Screen, @nospecialize(primitive::Maki
pos = Makie.voxel_positions(primitive)
scale = Makie.voxel_size(primitive)
colors = Makie.voxel_colors(primitive)
marker = normal_mesh(Rect3f(Point3f(0), Vec3f(1)))
marker = normal_mesh(Rect3f(Point3f(-0.5), Vec3f(1)))

# For correct z-ordering we need to be in view/camera or screen space
model = copy(primitive.model[])
Expand Down
12 changes: 10 additions & 2 deletions GLMakie/assets/shader/voxel.frag
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// {{GLSL_EXTENSIONS}}

// debug FLAGS
// #define DEBUG_RENDER_ORDER 2 // (0, 1, 2) - dimensions
// #define DEBUG_RENDER_ORDER 0 // (0, 1, 2) - dimensions

struct Nothing{ //Nothing type, to encode if some variable doesn't contain any data
bool _; //empty structs are not allowed
Expand All @@ -19,10 +19,13 @@ in vec2 o_tex_uv;

#ifdef DEBUG_RENDER_ORDER
flat in float plane_render_idx; // debug
flat in int plane_dim;
flat in int plane_front;
#endif

uniform lowp usampler3D voxel_id;
uniform uint objectid;
uniform float gap;

{{uv_map_type}} uv_map;
{{color_map_type}} color_map;
Expand Down Expand Up @@ -87,6 +90,11 @@ vec3 illuminate(vec3 normal, vec3 base_color);

void main()
{
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)
discard;

// grab voxel id
int id = int(texture(voxel_id, o_uvw).x);

Expand All @@ -99,7 +107,7 @@ void main()
vec4 voxel_color = get_color(color, color_map, id);

#ifdef DEBUG_RENDER_ORDER
if (mod(o_side, 3) != DEBUG_RENDER_ORDER)
if (plane_dim != DEBUG_RENDER_ORDER)
discard;
voxel_color = vec4(plane_render_idx, 0, 0, id == 0 ? 0.01 : 1.0);
#endif
Expand Down
47 changes: 35 additions & 12 deletions GLMakie/assets/shader/voxel.vert
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ out vec2 o_tex_uv;

#ifdef DEBUG_RENDER_ORDER
flat out float plane_render_idx;
flat out int plane_dim;
flat out int plane_front;
#endif

out vec3 o_camdir;
Expand All @@ -27,6 +29,7 @@ uniform vec3 view_direction;
uniform lowp usampler3D voxel_id;
uniform float depth_shift;
uniform bool depthsorting;
uniform float gap;

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 @@ -70,17 +73,37 @@ void main() {
// TODO: might be better for transparent rendering to alternate xyz?
// How do we do this for non-cubic chunks?
ivec3 size = textureSize(voxel_id, 0);
int dim = 2, id = gl_InstanceID;
if (gl_InstanceID > size.z + size.y + 1) {
dim = 0;
id = gl_InstanceID - (size.z + size.y + 2);
} else if (gl_InstanceID > size.z) {
dim = 1;
id = gl_InstanceID - (size.z + 1);
int dim, id = gl_InstanceID, front = -1;
if (gap > 0.01) {
front = 1 - 2 * int(gl_InstanceID & 1);
int temp_id = (id + 1) >> 1;
if (id < 2 * size.z) {
dim = 2;
id = temp_id;
} else if (id < 2 * (size.z + size.y)) {
dim = 1;
id = temp_id - size.z;
} else { // if (id > 2 * (size.z + size.y)) {
dim = 0;
id = temp_id - (size.z + size.y);
}
} else {
if (id < size.z) {
dim = 2;
id = id;
} else if (id < size.z + size.y) {
dim = 1;
id = id - size.z;
} else { // if (id > 2 * (size.z + size.y)) {
dim = 0;
id = id - (size.z + size.y);
}
}

#ifdef DEBUG_RENDER_ORDER
plane_render_idx = float(id) / float(size[dim]-1);
plane_dim = dim;
plane_front = front;
#endif

// plane placement
Expand All @@ -90,15 +113,15 @@ void main() {
vec3 displacement;
if (depthsorting) {
// depthsorted should start far away from viewer so every plane draws
displacement = ((0.5 + 0.5 * dir) * size - dir * id) * unit_vecs[dim];
displacement = ((0.5 + 0.5 * dir) * size - dir * (id - 0.5 * gap * front)) * unit_vecs[dim];
} else {
// no sorting should start at viewer and expand in view direction so
// that depth test can quickly eliminate unnecessary fragments
vec4 origin = model * vec4(0, 0, 0, 1);
float dist = dot(eyeposition - origin.xyz / origin.w, normal) / dot(normal, normal);
int start = clamp(int(dist), 0, size[dim]);
// this should work better with integer modulo...
displacement = mod(start + dir * id, size[dim] + 0.001) * unit_vecs[dim];
displacement = mod(start + dir * (id - 0.5 * gap * front), size[dim] + 0.001) * unit_vecs[dim];
}

// place plane vertices
Expand All @@ -114,8 +137,8 @@ void main() {
// If we assume the viewer to be outside of a voxel, the normal direction
// should always be facing them. Thus:
o_camdir = eyeposition - world_pos.xyz / world_pos.w;
float normal_dir = sign(dot(o_camdir, normal));
o_normal = normalize(normal_dir * normal);
float normal_dir = -front * sign(dot(o_camdir, normal));
o_normal = normal_dir * normalize(normal);

// The texture coordinate can also be derived. `voxel_pos` effectively gives
// an integer index into the chunk, shifted to be centered. We can convert
Expand All @@ -129,7 +152,7 @@ void main() {
// | 1 | 2 | 0 | v
// If we shift in +normal direction (towards viewer) the planes would sample
// from the id closer to the viewer, drawing a backface.
o_uvw = (voxel_pos - 0.5 * o_normal) / size;
o_uvw = (voxel_pos - 0.5 * (1 - gap) * o_normal) / size;

// normal in: -x -y -z +x +y +z direction
o_side = dim + 3 * int(0.5 + 0.5 * normal_dir);
Expand Down
3 changes: 2 additions & 1 deletion GLMakie/src/glshaders/voxel.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ function draw_voxels(screen, main::VolumeTypes, data::Dict)
shading = pop!(data, :shading, FastShading)
@gen_defaults! data begin
voxel_id = main => Texture
instances = const_lift(t -> sum(size(t)) + 3, voxel_id)
instances = const_lift(t -> 2 * sum(size(t)), voxel_id)
model = Mat4f(I)
transparency = false
backlight = 0f0
color = nothing => Texture
color_map = nothing => Texture
uv_map = nothing => Texture
gap = 0f0
shader = GLVisualizeShader(
screen,
"voxel.vert",
Expand Down
2 changes: 2 additions & 0 deletions MakieCore/src/basic_plots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,7 @@ representation and may behave a bit differently than usual.
- `uvmap = nothing` defines a map from voxel ids (and optionally sides) to uv coordinates. These uv coordinates are then
used to sample a 2D texture passed through `color` for texture mapping.
- `interpolate = false` controls whether the texture map is sampled with interpolation (i.e. smoothly) or not (i.e. pixelated).
- `gap = 0.0` sets the gap between voxels.
- `_limits`: Internal attribute for keeping track of `extrema(chunk)`.
- `_local_update`: Internal attribute for communicating updates to the backend.
Expand Down Expand Up @@ -553,6 +554,7 @@ $(Base.Docs.doc(MakieCore.generic_plot_attributes!))
uvmap = nothing,
interpolate = false, # texture
depthsorting = false, # false = fast, true = correct transparency
gap = 0.0,
# Internal:
_limits = (0.0, 1.0),
_local_update = (0:0, 0:0, 0:0),
Expand Down
2 changes: 1 addition & 1 deletion RPRMakie/src/meshes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ end
function to_rpr_object(context, matsys, scene, plot::Makie.Voxels)
# Potentially per instance attributes
positions = Makie.voxel_positions(plot)
m_mesh = normal_mesh(Rect3f(Point3f(0), Vec3f(1)))
m_mesh = normal_mesh(Rect3f(Point3f(-0.5), Vec3f(1)))
marker = RPR.Shape(context, m_mesh)
instances = [marker]
n_instances = length(positions)
Expand Down
9 changes: 8 additions & 1 deletion WGLMakie/assets/voxel.frag
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ in vec3 o_camdir;

#ifdef DEBUG_RENDER_ORDER
flat in float plane_render_idx; // debug
flat in int plane_dim;
flat in int plane_front;
#endif

vec4 debug_color(uint id) {
Expand Down Expand Up @@ -88,6 +90,11 @@ vec4 pack_int(uint id, uint index) {
}
void main()
{
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)
discard;

// grab voxel id
int id = int(texture(voxel_id, o_uvw).x);

Expand All @@ -100,7 +107,7 @@ void main()
vec4 voxel_color = get_color(color, color_map, uv_map, id);

#ifdef DEBUG_RENDER_ORDER
if (mod(o_side, 3) != DEBUG_RENDER_ORDER)
if (plane_dim != DEBUG_RENDER_ORDER)
discard;
voxel_color = vec4(plane_render_idx, 0, 0, id == 0 ? 0.01 : 1.0);
#endif
Expand Down
52 changes: 41 additions & 11 deletions WGLMakie/assets/voxel.vert
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ out vec2 o_tex_uv;

#ifdef DEBUG_RENDER_ORDER
flat out float plane_render_idx;
flat out int plane_dim;
flat out int plane_front;
#endif

out vec3 o_camdir;
Expand Down Expand Up @@ -58,17 +60,39 @@ void main() {
// TODO: might be better for transparent rendering to alternate xyz?
// How do we do this for non-cubic chunks?
ivec3 size = textureSize(voxel_id, 0);
int dim = 2, id = gl_InstanceID;
if (gl_InstanceID > size.z + size.y + 1) {
dim = 0;
id = gl_InstanceID - (size.z + size.y + 2);
} else if (gl_InstanceID > size.z) {
dim = 1;
id = gl_InstanceID - (size.z + 1);
int dim, id = gl_InstanceID;
float front = -1.0;
float gap = get_gap();
if (gap > 0.01) {
front = float(1 - 2 * int(gl_InstanceID & 1));
int temp_id = (id + 1) >> 1;
if (id < 2 * size.z) {
dim = 2;
id = temp_id;
} else if (id < 2 * (size.z + size.y)) {
dim = 1;
id = temp_id - size.z;
} else { // if (id > 2 * (size.z + size.y)) {
dim = 0;
id = temp_id - (size.z + size.y);
}
} else {
if (id < size.z) {
dim = 2;
id = id;
} else if (id < size.z + size.y) {
dim = 1;
id = id - size.z;
} else { // if (id > 2 * (size.z + size.y)) {
dim = 0;
id = id - (size.z + size.y);
}
}

#ifdef DEBUG_RENDER_ORDER
plane_render_idx = float(id) / float(size[dim]-1);
plane_dim = dim;
plane_front = front;
#endif

// plane placement
Expand All @@ -78,15 +102,21 @@ void main() {
vec3 displacement;
if (depthsorting) {
// depthsorted should start far away from viewer so every plane draws
displacement = ((0.5 + 0.5 * dir) * float(size[dim]) - dir * float(id)) * unit_vecs[dim];
displacement = (
(0.5 + 0.5 * dir) * float(size[dim]) -
dir * (float(id) - 0.5 * gap * front)
) * unit_vecs[dim];
} else {
// no sorting should start at viewer and expand in view direction so
// that depth test can quickly eliminate unnecessary fragments
vec4 origin = get_model() * vec4(0, 0, 0, 1);
float dist = dot(get_eyeposition() - origin.xyz / origin.w, normal) / dot(normal, normal);
float start = clamp(float(int(dist)), 0.0, float(size[dim]));
// this should work better with integer modulo...
displacement = mod(start + dir * float(id), float(size[dim]) + 0.001) * unit_vecs[dim];
displacement = mod(
start + dir * (float(id) - 0.5 * gap * front),
float(size[dim]) + 0.001
) * unit_vecs[dim];
}

// place plane vertices
Expand All @@ -101,7 +131,7 @@ void main() {
// If we assume the viewer to be outside of a voxel, the normal direction
// should always be facing them. Thus:
o_camdir = get_eyeposition() - world_pos.xyz / world_pos.w;
float normal_dir = sign(dot(o_camdir, normal));
float normal_dir = -front * sign(dot(o_camdir, normal));
o_normal = normalize(normal_dir * normal);

// The texture coordinate can also be derived. `voxel_pos` effectively gives
Expand All @@ -116,7 +146,7 @@ void main() {
// | 1 | 2 | 0 | v
// If we shift in +normal direction (towards viewer) the planes would sample
// from the id closer to the viewer, drawing a backface.
o_uvw = (voxel_pos - 0.5 * o_normal) / vec3(size);
o_uvw = (voxel_pos - 0.5 * (1.0 - gap) * o_normal) / vec3(size);

// normal in: -x -y -z +x +y +z direction
o_side = dim + 3 * int(0.5 + 0.5 * normal_dir);
Expand Down
54 changes: 27 additions & 27 deletions WGLMakie/src/voxel.jl
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
function create_shader(scene::Scene, plot::Makie.Voxels)
uniform_dict = Dict{Symbol, Any}(
:voxel_id => Sampler(plot.converted[end], minfilter = :nearest),
# for plane sorting
:depthsorting => plot.depthsorting,
:eyeposition => Vec3f(1),
:view_direction => camera(scene).view_direction,
# lighting
:diffuse => lift(x -> convert_attribute(x, Key{:diffuse}()), plot, plot.diffuse),
:specular => lift(x -> convert_attribute(x, Key{:specular}()), plot, plot.specular),
:shininess => lift(x -> convert_attribute(x, Key{:shininess}()), plot, plot.shininess),
:depth_shift => get(plot, :depth_shift, Observable(0.0f0)),
:light_direction => Vec3f(1),
:light_color => Vec3f(1),
:ambient => Vec3f(1),
# picking
:picking => false,
:object_id => UInt32(0),
# other
:normalmatrix => map(plot.model) do m
# should be fine to ignore placement matrix here because
# translation is ignored and scale shouldn't matter
i = Vec(1, 2, 3)
return transpose(inv(m[i, i]))
end,
:shading => to_value(get(plot, :shading, NoShading)) != NoShading,
)
uniform_dict = Dict{Symbol, Any}()
uniform_dict[:voxel_id] = Sampler(plot.converted[end], minfilter = :nearest)
# for plane sorting
uniform_dict[:depthsorting] = plot.depthsorting
uniform_dict[:eyeposition] = Vec3f(1)
uniform_dict[:view_direction] = camera(scene).view_direction
# lighting
uniform_dict[:diffuse] = lift(x -> convert_attribute(x, Key{:diffuse}()), plot, plot.diffuse)
uniform_dict[:specular] = lift(x -> convert_attribute(x, Key{:specular}()), plot, plot.specular)
uniform_dict[:shininess] = lift(x -> convert_attribute(x, Key{:shininess}()), plot, plot.shininess)
uniform_dict[:depth_shift] = get(plot, :depth_shift, Observable(0.0f0))
uniform_dict[:light_direction] = Vec3f(1)
uniform_dict[:light_color] = Vec3f(1)
uniform_dict[:ambient] = Vec3f(1)
# picking
uniform_dict[:picking] = false
uniform_dict[:object_id] = UInt32(0)
# other
uniform_dict[:normalmatrix] = map(plot.model) do m
# should be fine to ignore placement matrix here because
# translation is ignored and scale shouldn't matter
i = Vec(1, 2, 3)
return transpose(inv(m[i, i]))
end
uniform_dict[:shading] = to_value(get(plot, :shading, NoShading)) != NoShading
uniform_dict[:gap] = lift(x -> convert_attribute(x, Key{:gap}(), Key{:voxels}()), plot.gap)

# TODO: localized update
# buffer = Vector{UInt8}(undef, 1)
Expand Down Expand Up @@ -85,7 +85,7 @@ function create_shader(scene::Scene, plot::Makie.Voxels)
end

# TODO: this is a waste
N_instances = sum(size(plot.converted[end][])) + 3
N_instances = 2 * sum(size(plot.converted[end][]))
dummy_data = [0f0 for _ in 1:N_instances]

instance = GeometryBasics.mesh(Rect2(0f0, 0f0, 1f0, 1f0))
Expand Down
Loading

0 comments on commit 2885ae8

Please sign in to comment.