diff --git a/GLMakie/assets/shader/surface.vert b/GLMakie/assets/shader/surface.vert index 4ce47665eab..e268131452a 100644 --- a/GLMakie/assets/shader/surface.vert +++ b/GLMakie/assets/shader/surface.vert @@ -42,14 +42,111 @@ vec2 grid_pos(Grid2D pos, vec2 uv); vec2 linear_index(ivec2 dims, int index); vec2 linear_index(ivec2 dims, int index, vec2 offset); vec4 linear_texture(sampler2D tex, int index, vec2 offset); -// vec3 getnormal_fast(sampler2D zvalues, ivec2 uv); -vec3 getnormal(Grid2D pos, Nothing xs, Nothing ys, sampler2D zs, vec2 uv); -vec3 getnormal(Nothing pos, sampler2D xs, sampler2D ys, sampler2D zs, vec2 uv); -vec3 getnormal(Nothing pos, sampler1D xs, sampler1D ys, sampler2D zs, vec2 uv); - -vec3 getnormal(Grid2D pos, Nothing xs, Nothing ys, sampler2D zs, ivec2 uv); -vec3 getnormal(Nothing pos, sampler2D xs, sampler2D ys, sampler2D zs, ivec2 uv); -vec3 getnormal(Nothing pos, sampler1D xs, sampler1D ys, sampler2D zs, ivec2 uv); + + +// Normal generation + +vec3 getnormal_fast(sampler2D zvalues, ivec2 uv) +{ + vec3 a = vec3(0, 0, 0); + vec3 b = vec3(1, 1, 0); + a.z = texelFetch(zvalues, uv, 0).r; + b.z = texelFetch(zvalues, uv + ivec2(1, 1), 0).r; + return normalize(a - b); +} + +bool isinbounds(ivec2 uv, ivec2 size) +{ + return (uv.x < size.x && uv.y < size.y && uv.x >= 0 && uv.y >= 0); +} + +/* +Computes normal at s0 based on four surrounding positions s1 ... s4 and the +respective uv coordinates uv, off1, ..., off4 + + s2 + s1 s0 s3 + s4 +*/ +vec3 normal_from_points( + vec3 s0, vec3 s1, vec3 s2, vec3 s3, vec3 s4, + ivec2 off1, ivec2 off2, ivec2 off3, ivec2 off4, ivec2 size + ){ + vec3 result = vec3(0,0,0); + // isnan checks should avoid darkening around NaN positions but may not + // work with all systems + if (!isnan(s0.z)) { + bool check1 = isinbounds(off1, size) && !isnan(s1.z); + bool check2 = isinbounds(off2, size) && !isnan(s2.z); + bool check3 = isinbounds(off3, size) && !isnan(s3.z); + bool check4 = isinbounds(off4, size) && !isnan(s4.z); + if (check1 && check2) result += cross(s2-s0, s1-s0); + if (check2 && check3) result += cross(s3-s0, s2-s0); + if (check3 && check4) result += cross(s4-s0, s3-s0); + if (check4 && check1) result += cross(s1-s0, s4-s0); + } + // normal should be zero, but needs to be here, because the dead-code + // elimanation of GLSL is overly enthusiastic + return normalize(result); +} + +// Overload for surface(Matrix, Matrix, Matrix) +vec3 getnormal(Nothing pos, sampler2D xs, sampler2D ys, sampler2D zs, ivec2 uv){ + vec3 s0, s1, s2, s3, s4; + ivec2 off1 = uv + ivec2(-1, 0); + ivec2 off2 = uv + ivec2(0, 1); + ivec2 off3 = uv + ivec2(1, 0); + ivec2 off4 = uv + ivec2(0, -1); + + s0 = vec3(texelFetch(xs, uv, 0).x, texelFetch(ys, uv, 0).x, texelFetch(zs, uv, 0).x); + s1 = vec3(texelFetch(xs, off1, 0).x, texelFetch(ys, off1, 0).x, texelFetch(zs, off1, 0).x); + s2 = vec3(texelFetch(xs, off2, 0).x, texelFetch(ys, off2, 0).x, texelFetch(zs, off2, 0).x); + s3 = vec3(texelFetch(xs, off3, 0).x, texelFetch(ys, off3, 0).x, texelFetch(zs, off3, 0).x); + s4 = vec3(texelFetch(xs, off4, 0).x, texelFetch(ys, off4, 0).x, texelFetch(zs, off4, 0).x); + + return normal_from_points(s0, s1, s2, s3, s4, off1, off2, off3, off4, textureSize(zs, 0)); +} + + +// Overload for (range, range, Matrix) surface plots +// Though this is only called by surface(Matrix) +vec2 grid_pos(Grid2D position, ivec2 uv, ivec2 size); + +vec3 getnormal(Grid2D pos, Nothing xs, Nothing ys, sampler2D zs, ivec2 uv){ + vec3 s0, s1, s2, s3, s4; + ivec2 off1 = uv + ivec2(-1, 0); + ivec2 off2 = uv + ivec2(0, 1); + ivec2 off3 = uv + ivec2(1, 0); + ivec2 off4 = uv + ivec2(0, -1); + ivec2 size = textureSize(zs, 0); + + s0 = vec3(grid_pos(pos, uv, size).xy, texelFetch(zs, uv, 0).x); + s1 = vec3(grid_pos(pos, off1, size).xy, texelFetch(zs, off1, 0).x); + s2 = vec3(grid_pos(pos, off2, size).xy, texelFetch(zs, off2, 0).x); + s3 = vec3(grid_pos(pos, off3, size).xy, texelFetch(zs, off3, 0).x); + s4 = vec3(grid_pos(pos, off4, size).xy, texelFetch(zs, off4, 0).x); + + return normal_from_points(s0, s1, s2, s3, s4, off1, off2, off3, off4, size); +} + + +// Overload for surface(Vector, Vector, Matrix) +// Makie converts almost everything to this +vec3 getnormal(Nothing pos, sampler1D xs, sampler1D ys, sampler2D zs, ivec2 uv){ + vec3 s0, s1, s2, s3, s4; + ivec2 off1 = uv + ivec2(-1, 0); + ivec2 off2 = uv + ivec2(0, 1); + ivec2 off3 = uv + ivec2(1, 0); + ivec2 off4 = uv + ivec2(0, -1); + + s0 = vec3(texelFetch(xs, uv.x, 0).x, texelFetch(ys, uv.y, 0).x, texelFetch(zs, uv, 0).x); + s1 = vec3(texelFetch(xs, off1.x, 0).x, texelFetch(ys, off1.y, 0).x, texelFetch(zs, off1, 0).x); + s2 = vec3(texelFetch(xs, off2.x, 0).x, texelFetch(ys, off2.y, 0).x, texelFetch(zs, off2, 0).x); + s3 = vec3(texelFetch(xs, off3.x, 0).x, texelFetch(ys, off3.y, 0).x, texelFetch(zs, off3, 0).x); + s4 = vec3(texelFetch(xs, off4.x, 0).x, texelFetch(ys, off4.y, 0).x, texelFetch(zs, off4, 0).x); + + return normal_from_points(s0, s1, s2, s3, s4, off1, off2, off3, off4, textureSize(zs, 0)); +} uniform uint objectid; uniform vec2 uv_scale; diff --git a/GLMakie/assets/shader/util.vert b/GLMakie/assets/shader/util.vert index ac3af127260..58c14bf81e0 100644 --- a/GLMakie/assets/shader/util.vert +++ b/GLMakie/assets/shader/util.vert @@ -31,8 +31,8 @@ vec2 grid_pos(Grid2D position, vec2 uv){ vec2 grid_pos(Grid2D position, ivec2 uv, ivec2 size){ return vec2( - (1 - uv.x / (size.x - 1)) * position.start[0] + uv.x / (size.x - 1) * position.stop[0], - (1 - uv.y / (size.y - 1)) * position.start[1] + uv.y / (size.y - 1) * position.stop[1] + (1.0 - (uv.x + 0.5) / size.x) * position.start[0] + (uv.x + 0.5) / size.x * position.stop[0], + (1.0 - (uv.y + 0.5) / size.y) * position.start[1] + (uv.y + 0.5) / size.y * position.stop[1] ); } @@ -243,118 +243,6 @@ void render(vec4 position_world, vec3 normal, mat4 view, mat4 projection, vec3 l o_view_pos = view_pos.xyz / view_pos.w; } -// -vec3 getnormal_fast(sampler2D zvalues, ivec2 uv) -{ - vec3 a = vec3(0, 0, 0); - vec3 b = vec3(1, 1, 0); - a.z = texelFetch(zvalues, uv, 0).r; - b.z = texelFetch(zvalues, uv + ivec2(1, 1), 0).r; - return normalize(a - b); -} - -bool isinbounds(vec2 uv) -{ - return (uv.x <= 1.0 && uv.y <= 1.0 && uv.x >= 0.0 && uv.y >= 0.0); -} - - -/* -Computes normal at s0 based on four surrounding positions s1 ... s4 and the -respective uv coordinates uv, off1, ..., off4 - - s2 - s1 s0 s3 - s4 -*/ -vec3 normal_from_points( - vec3 s0, vec3 s1, vec3 s2, vec3 s3, vec3 s4, - vec2 uv, vec2 off1, vec2 off2, vec2 off3, vec2 off4 - ){ - vec3 result = vec3(0,0,0); - // isnan checks should avoid darkening around NaN positions but may not - // work with all systems - if (!isnan(s0.z)) { - bool check1 = isinbounds(off1) && !isnan(s1.z); - bool check2 = isinbounds(off2) && !isnan(s2.z); - bool check3 = isinbounds(off3) && !isnan(s3.z); - bool check4 = isinbounds(off4) && !isnan(s4.z); - if (check1 && check2) result += cross(s2-s0, s1-s0); - if (check2 && check3) result += cross(s3-s0, s2-s0); - if (check3 && check4) result += cross(s4-s0, s3-s0); - if (check4 && check1) result += cross(s1-s0, s4-s0); - } - // normal should be zero, but needs to be here, because the dead-code - // elimanation of GLSL is overly enthusiastic - return normalize(result); -} - -// Overload for surface(Matrix, Matrix, Matrix) -vec3 getnormal(Nothing pos, sampler2D xs, sampler2D ys, sampler2D zs, vec2 uv){ - // The +1e-6 fixes precision errors at the edge - float du = 1.0 / textureSize(zs,0).x + 1e-6; - float dv = 1.0 / textureSize(zs,0).y + 1e-6; - - vec3 s0, s1, s2, s3, s4; - vec2 off1 = uv + vec2(-du, 0); - vec2 off2 = uv + vec2(0, dv); - vec2 off3 = uv + vec2(du, 0); - vec2 off4 = uv + vec2(0, -dv); - - s0 = vec3(texture(xs, uv).x, texture(ys, uv).x, texture(zs, uv).x); - s1 = vec3(texture(xs, off1).x, texture(ys, off1).x, texture(zs, off1).x); - s2 = vec3(texture(xs, off2).x, texture(ys, off2).x, texture(zs, off2).x); - s3 = vec3(texture(xs, off3).x, texture(ys, off3).x, texture(zs, off3).x); - s4 = vec3(texture(xs, off4).x, texture(ys, off4).x, texture(zs, off4).x); - - return normal_from_points(s0, s1, s2, s3, s4, uv, off1, off2, off3, off4); -} - - -// Overload for (range, range, Matrix) surface plots -// Though this is only called by surface(Matrix) -vec3 getnormal(Grid2D pos, Nothing xs, Nothing ys, sampler2D zs, vec2 uv){ - // The +1e-6 fixes precision errors at the edge - float du = 1.0 / textureSize(zs,0).x + 1e-6; - float dv = 1.0 / textureSize(zs,0).y + 1e-6; - - vec3 s0, s1, s2, s3, s4; - vec2 off1 = uv + vec2(-du, 0); - vec2 off2 = uv + vec2(0, dv); - vec2 off3 = uv + vec2(du, 0); - vec2 off4 = uv + vec2(0, -dv); - - s0 = vec3(grid_pos(pos, uv).xy, texture(zs, uv).x); - s1 = vec3(grid_pos(pos, off1).xy, texture(zs, off1).x); - s2 = vec3(grid_pos(pos, off2).xy, texture(zs, off2).x); - s3 = vec3(grid_pos(pos, off3).xy, texture(zs, off3).x); - s4 = vec3(grid_pos(pos, off4).xy, texture(zs, off4).x); - - return normal_from_points(s0, s1, s2, s3, s4, uv, off1, off2, off3, off4); -} - - -// Overload for surface(Vector, Vector, Matrix) -// Makie converts almost everything to this -vec3 getnormal(Nothing pos, sampler1D xs, sampler1D ys, sampler2D zs, vec2 uv){ - // The +1e-6 fixes precision errors at the edge - float du = 1.0 / textureSize(zs,0).x + 1e-6; - float dv = 1.0 / textureSize(zs,0).y + 1e-6; - - vec3 s0, s1, s2, s3, s4; - vec2 off1 = uv + vec2(-du, 0); - vec2 off2 = uv + vec2(0, dv); - vec2 off3 = uv + vec2(du, 0); - vec2 off4 = uv + vec2(0, -dv); - - s0 = vec3(texture(xs, uv.x).x, texture(ys, uv.y).x, texture(zs, uv).x); - s1 = vec3(texture(xs, off1.x).x, texture(ys, off1.y).x, texture(zs, off1).x); - s2 = vec3(texture(xs, off2.x).x, texture(ys, off2.y).x, texture(zs, off2).x); - s3 = vec3(texture(xs, off3.x).x, texture(ys, off3.y).x, texture(zs, off3).x); - s4 = vec3(texture(xs, off4.x).x, texture(ys, off4.y).x, texture(zs, off4).x); - - return normal_from_points(s0, s1, s2, s3, s4, uv, off1, off2, off3, off4); -} uniform vec4 highclip; uniform vec4 lowclip; diff --git a/GLMakie/src/glshaders/surface.jl b/GLMakie/src/glshaders/surface.jl index 714e174d772..9750cd458e4 100644 --- a/GLMakie/src/glshaders/surface.jl +++ b/GLMakie/src/glshaders/surface.jl @@ -6,7 +6,7 @@ end function normal_calc(x::Bool, invert_normals::Bool = false) i = invert_normals ? "-" : "" if x - return "$(i)getnormal(position, position_x, position_y, position_z, index01);" + return "$(i)getnormal(position, position_x, position_y, position_z, index2D);" else return "vec3(0, 0, $(i)1);" end