diff --git a/GLMakie/assets/shader/dots.vert b/GLMakie/assets/shader/dots.vert index 0e7f4dd3021..a98fe359b77 100644 --- a/GLMakie/assets/shader/dots.vert +++ b/GLMakie/assets/shader/dots.vert @@ -32,11 +32,29 @@ void colorize(sampler1D color, float intensity, vec2 color_norm){ vec4 _position(vec3 p){return vec4(p,1);} vec4 _position(vec2 p){return vec4(p,0,1);} +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; +} + uniform mat4 projectionview, model; void main(){ colorize(color_map, color, color_norm); o_objectid = uvec2(objectid, gl_VertexID+1); - gl_Position = projectionview * model * _position(vertex); + vec4 world_pos = model * _position(vertex); + process_clip_planes(world_pos.xyz); + gl_Position = projectionview * world_pos; gl_Position.z += gl_Position.w * depth_shift; } diff --git a/GLMakie/assets/shader/heatmap.vert b/GLMakie/assets/shader/heatmap.vert index 380bf802328..2108f0c6111 100644 --- a/GLMakie/assets/shader/heatmap.vert +++ b/GLMakie/assets/shader/heatmap.vert @@ -19,6 +19,22 @@ ivec2 ind2sub(ivec2 dim, int linearindex){ return ivec2(linearindex % dim.x, linearindex / dim.x); } +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; +} + void main(){ //Outputs for ssao, which we don't use for 2d shaders like heatmap/image o_view_pos = vec3(0); @@ -38,6 +54,8 @@ void main(){ float x = texelFetch(position_x, index2D.x, 0).x; float y = texelFetch(position_y, index2D.y, 0).x; - gl_Position = projection * view * model * vec4(x, y, 0, 1); + vec4 world_pos = model * vec4(x, y, 0, 1); + process_clip_planes(world_pos.xyz); + gl_Position = projection * view * world_pos; gl_Position.z += gl_Position.w * depth_shift; } diff --git a/GLMakie/assets/shader/line_segment.geom b/GLMakie/assets/shader/line_segment.geom index fd32a3508bd..443cfb7f31d 100644 --- a/GLMakie/assets/shader/line_segment.geom +++ b/GLMakie/assets/shader/line_segment.geom @@ -6,6 +6,14 @@ struct Nothing{ //Nothing type, to encode if some variable doesn't contain any d }; layout(lines) in; + +// Need to set size of ClipDistance +in gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[8]; +} gl_in[]; + layout(triangle_strip, max_vertices = 4) out; uniform vec2 resolution; @@ -33,6 +41,7 @@ flat out float f_cumulative_length; flat out ivec2 f_capmode; flat out vec4 f_linepoints; flat out vec4 f_miter_vecs; +out float gl_ClipDistance[8]; const float AA_RADIUS = 0.8; const float AA_THICKNESS = 2.0 * AA_RADIUS; @@ -108,6 +117,9 @@ void main(void) f_linewidth = halfwidth; f_id = g_id[x]; + for (int i = 0; i < 8; i++) + gl_ClipDistance[i] = gl_in[x].gl_ClipDistance[i]; + for (int y = 0; y < 2; y++) { // Get offset in y direction & compute vertex position float n_offset = (2 * y - 1) * (halfwidth + AA_THICKNESS); diff --git a/GLMakie/assets/shader/line_segment.vert b/GLMakie/assets/shader/line_segment.vert index 3c4f4d14659..ffc61c35a15 100644 --- a/GLMakie/assets/shader/line_segment.vert +++ b/GLMakie/assets/shader/line_segment.vert @@ -22,11 +22,30 @@ out float g_thickness; vec4 to_vec4(vec3 v){return vec4(v, 1);} vec4 to_vec4(vec2 v){return vec4(v, 0, 1);} +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; +} + + void main() { g_id = uvec2(objectid, gl_VertexID + 1); g_color = color; g_thickness = px_per_unit * thickness; - gl_Position = projectionview * model * to_vec4(vertex); + vec4 world_pos = model * to_vec4(vertex); + process_clip_planes(world_pos.xyz); + gl_Position = projectionview * world_pos; gl_Position.z += gl_Position.w * depth_shift; } diff --git a/GLMakie/assets/shader/lines.geom b/GLMakie/assets/shader/lines.geom index f6c198f728d..f207eadd54b 100644 --- a/GLMakie/assets/shader/lines.geom +++ b/GLMakie/assets/shader/lines.geom @@ -9,6 +9,14 @@ struct Nothing{ //Nothing type, to encode if some variable doesn't contain any d {{define_fast_path}} layout(lines_adjacency) in; + +// Need to set size of ClipDistance +in gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[8]; +} gl_in[]; + layout(triangle_strip, max_vertices = 4) out; in {{stripped_color_type}} g_color[]; @@ -34,6 +42,7 @@ flat out float f_cumulative_length; flat out ivec2 f_capmode; flat out vec4 f_linepoints; flat out vec4 f_miter_vecs; +out float gl_ClipDistance[8]; out vec3 o_view_pos; out vec3 o_view_normal; @@ -79,6 +88,8 @@ void emit_vertex(LineVertex vertex) { f_linestart = vertex.linestart; f_linelength = vertex.linelength; f_id = g_id[vertex.index]; + for (int i = 0; i < 8; i++) + gl_ClipDistance[i] = gl_in[vertex.index].gl_ClipDistance[i]; EmitVertex(); } diff --git a/GLMakie/assets/shader/lines.vert b/GLMakie/assets/shader/lines.vert index 80a355b398f..1914884a7bb 100644 --- a/GLMakie/assets/shader/lines.vert +++ b/GLMakie/assets/shader/lines.vert @@ -34,6 +34,22 @@ int get_valid_vertex(Nothing se){return 1;} uniform float depth_shift; +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; +} + void main() { g_lastlen = lastlen; @@ -43,8 +59,10 @@ void main() g_thickness = px_per_unit * thickness; g_color = color; + vec4 world_pos = model * to_vec4(vertex); + process_clip_planes(world_pos.xyz); #ifdef FAST_PATH - gl_Position = projectionview * model * to_vec4(vertex); + gl_Position = projectionview * world_pos; #else gl_Position = to_vec4(vertex); #endif diff --git a/GLMakie/assets/shader/mesh.vert b/GLMakie/assets/shader/mesh.vert index 018248c0c5c..05ab913aa6d 100644 --- a/GLMakie/assets/shader/mesh.vert +++ b/GLMakie/assets/shader/mesh.vert @@ -16,6 +16,9 @@ in vec3 normals; uniform mat4 projection, view, model; +uniform int num_clip_planes; +uniform vec4 clip_planes[8]; + void render(vec4 position_world, vec3 normal, mat4 view, mat4 projection); vec4 get_color_from_cmap(float value, sampler1D color_map, vec2 colorrange); diff --git a/GLMakie/assets/shader/sprites.geom b/GLMakie/assets/shader/sprites.geom index fc771ef63b5..38bdd57a9de 100644 --- a/GLMakie/assets/shader/sprites.geom +++ b/GLMakie/assets/shader/sprites.geom @@ -7,6 +7,13 @@ struct Nothing{ bool _; }; layout(points) in; +// Need to set size of ClipDistance +in gl_PerVertex { + vec4 gl_Position; + float gl_PointSize; + float gl_ClipDistance[8]; +} gl_in[]; + layout(triangle_strip, max_vertices = 4) out; mat4 qmat(vec4 quat){ @@ -61,6 +68,7 @@ flat out uvec2 f_id; out vec2 f_uv; flat out vec4 f_uv_texture_bbox; flat out vec2 f_sprite_scale; +out float gl_ClipDistance[8]; uniform mat4 projection, view, model; @@ -89,6 +97,8 @@ void emit_vertex(vec4 vertex, vec2 uv) f_glow_color = g_glow_color[0]; f_id = g_id[0]; f_sprite_scale = g_offset_width[0].zw; + for (int i = 0; i < 8; i++) + gl_ClipDistance[i] = gl_in[0].gl_ClipDistance[i]; EmitVertex(); } diff --git a/GLMakie/assets/shader/sprites.vert b/GLMakie/assets/shader/sprites.vert index ceee24efe7f..3c1b5a84c07 100644 --- a/GLMakie/assets/shader/sprites.vert +++ b/GLMakie/assets/shader/sprites.vert @@ -90,13 +90,16 @@ out vec4 g_glow_color; vec4 to_vec4(vec3 x){return vec4(x, 1.0);} vec4 to_vec4(vec4 x){return x;} +void process_clip_planes(vec3 world_pos); void main(){ int index = gl_VertexID; g_primitive_index = index; vec3 pos; {{position_calc}} - vec4 p = preprojection * model * vec4(pos, 1); + vec4 world_pos = model * vec4(pos, 1); + process_clip_planes(world_pos.xyz); + vec4 p = preprojection * world_pos; if (scale_primitive) g_position = p.xyz / p.w + mat3(model) * marker_offset; else diff --git a/GLMakie/assets/shader/util.vert b/GLMakie/assets/shader/util.vert index afb2c379945..c6cbbf3ebb4 100644 --- a/GLMakie/assets/shader/util.vert +++ b/GLMakie/assets/shader/util.vert @@ -249,6 +249,23 @@ vec4 _color(Nothing color, float intensity, sampler1D color_map, vec2 color_norm } +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; +} + + uniform float depth_shift; // TODO maybe ifdef SSAO this stuff? @@ -271,6 +288,8 @@ out vec3 o_camdir; void render(vec4 position_world, vec3 normal, mat4 view, mat4 projection) { + process_clip_planes(position_world.xyz); + // position in view space (as seen from camera) vec4 view_pos = view * position_world; view_pos /= view_pos.w; diff --git a/GLMakie/assets/shader/voxel.vert b/GLMakie/assets/shader/voxel.vert index 9c84745a302..05bc6a00bee 100644 --- a/GLMakie/assets/shader/voxel.vert +++ b/GLMakie/assets/shader/voxel.vert @@ -31,6 +31,23 @@ 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) @@ -149,6 +166,7 @@ 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; diff --git a/GLMakie/src/GLAbstraction/GLRender.jl b/GLMakie/src/GLAbstraction/GLRender.jl index d6eb089f410..b1dbeb967d4 100644 --- a/GLMakie/src/GLAbstraction/GLRender.jl +++ b/GLMakie/src/GLAbstraction/GLRender.jl @@ -4,12 +4,26 @@ function render(list::Tuple) end return end + +function setup_clip_planes(robj) + N = to_value(get(robj.uniforms, :num_clip_planes, 0)) + for i in 0:min(7, N-1) + glEnable(GL_CLIP_DISTANCE0 + UInt32(i)) + end + # TODO: This crashes? + # for i in N:7 + # glDisable(GL_CLIP_DISTANCE0 + UInt32(i)) + # end +end + + """ When rendering a specialised list of Renderables, we can do some optimizations """ function render(list::Vector{RenderObject{Pre}}) where Pre isempty(list) && return nothing first(list).prerenderfunction() + setup_clip_planes(first(list)) vertexarray = first(list).vertexarray program = vertexarray.program glUseProgram(program.id) @@ -57,6 +71,7 @@ a lot of objects. function render(renderobject::RenderObject, vertexarray=renderobject.vertexarray) if renderobject.visible renderobject.prerenderfunction() + setup_clip_planes(renderobject) program = vertexarray.program glUseProgram(program.id) for (key, value) in program.uniformloc diff --git a/GLMakie/src/GLAbstraction/GLTypes.jl b/GLMakie/src/GLAbstraction/GLTypes.jl index 0c1a6a1ebc0..b9ce99f0167 100644 --- a/GLMakie/src/GLAbstraction/GLTypes.jl +++ b/GLMakie/src/GLAbstraction/GLTypes.jl @@ -392,7 +392,7 @@ function RenderObject( # Otherwise just let the value pass through # TODO: Is this ok/ever not filtered? else - # @debug "Passed on $k -> $(typeof(v)) without conversion." + @debug "Passed on $k -> $(typeof(v)) without conversion." end end end @@ -403,7 +403,7 @@ function RenderObject( # remove all uniforms not occuring in shader # ssao, instances transparency are special for rendering passes. TODO do this more cleanly - special = Set([:ssao, :transparency, :instances, :fxaa]) + special = Set([:ssao, :transparency, :instances, :fxaa, :num_clip_planes]) for k in setdiff(keys(data), keys(program.nametype)) if !(k in special) delete!(data, k) diff --git a/GLMakie/src/drawing_primitives.jl b/GLMakie/src/drawing_primitives.jl index ca667c5ca97..fb507ccfe32 100644 --- a/GLMakie/src/drawing_primitives.jl +++ b/GLMakie/src/drawing_primitives.jl @@ -226,7 +226,7 @@ const EXCLUDE_KEYS = Set([:transformation, :tickranges, :ticklabels, :raw, :SSAO :lightposition, :material, :axis_cycler, :inspector_label, :inspector_hover, :inspector_clear, :inspectable, :colorrange, :colormap, :colorscale, :highclip, :lowclip, :nan_color, - :calculated_colors, :space, :markerspace, :model, :dim_conversions, :material]) + :calculated_colors, :space, :markerspace, :model, :dim_conversions, :material]) function cached_robj!(robj_func, screen, scene, plot::AbstractPlot) @@ -237,6 +237,8 @@ function cached_robj!(robj_func, screen, scene, plot::AbstractPlot) filtered = filter(plot.attributes) do (k, v) return !in(k, EXCLUDE_KEYS) end + + # Handle update tracking for render on demand track_updates = screen.config.render_on_demand if track_updates for arg in plot.args @@ -251,6 +253,8 @@ function cached_robj!(robj_func, screen, scene, plot::AbstractPlot) screen.requires_update = true end end + + # Pass along attributes gl_attributes = Dict{Symbol, Any}(map(filtered) do key_value key, value = key_value gl_key = to_glvisualize_key(key) @@ -264,6 +268,27 @@ function cached_robj!(robj_func, screen, scene, plot::AbstractPlot) gl_attributes[:space] = plot.space gl_attributes[:px_per_unit] = screen.px_per_unit + # Handle clip planes + # Most GPUs support 8, some 6. We always pad the array to 8 so we don't + # need to recompile shaders when this changes + # https://opengl.gpuinfo.org/displaycapability.php?name=GL_MAX_CLIP_PLANES + clip_planes = pop!(gl_attributes, :clip_planes) + gl_attributes[:num_clip_planes] = map(x -> min(8, length(x)), plot, clip_planes) + gl_attributes[:clip_planes] = map(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) + end + return output + end + handle_intensities!(screen, gl_attributes, plot) connect_camera!(plot, gl_attributes, scene.camera, get_space(plot)) @@ -303,7 +328,7 @@ function cached_robj!(robj_func, screen, scene, plot::AbstractPlot) elseif shading == MultiLightShading handle_lights(gl_attributes, screen, scene.lights) end - robj = robj_func(gl_attributes) # <-- here + robj = robj_func(gl_attributes) get!(gl_attributes, :ssao, Observable(false)) screen.cache2plot[robj.id] = plot diff --git a/src/utilities/Plane.jl b/src/utilities/Plane.jl index 590f2d7e315..5217aa75479 100644 --- a/src/utilities/Plane.jl +++ b/src/utilities/Plane.jl @@ -15,4 +15,24 @@ const Plane3d = Plane{3, Float64} function distance(plane::Plane{N}, point::VecTypes{N}) where N return sqrt(dot(plane.point - point, plane.normal)) +end + +# Compressed format for shaders +# point = dist * normal for valid normals +function gl_plane_format(plane::Plane3) + dist = dot(plane.point, plane.normal) + return to_ndim(Vec4f, plane.normal, dist) +end + +function planes(rect::Rect3f) + mini = minimum(rect) + maxi = maximum(rect) + return [ + Plane3f(mini, Vec3f(1, 0, 0)), + Plane3f(mini, Vec3f(0, 1, 0)), + Plane3f(mini, Vec3f(0, 0, 1)), + Plane3f(maxi, Vec3f(-1, 0, 0)), + Plane3f(maxi, Vec3f(0, -1, 0)), + Plane3f(maxi, Vec3f(0, 0, -1)) + ] end \ No newline at end of file