Closed edljk closed 6 years ago
In link with lighting questions. The following example show an illumination different for spheres and cylinders whereas the source light seems to be the same. From one side, the spheres are brighting whereas the cylinder are dark and reversely on the other side of the grid. I suspect an orientation choice but this should only affect interior/exterior lighting differences of cylinders, no ?
using GeometryTypes, Makie
scene = Scene(resolution = (500, 500))
δx = 0.1; h1 = []; h2= []
for x in 0:δx:1
for y in 0:δx:1
h1 = mesh(Sphere(Point3f0(x, y, 0), Float32(δx * 0.35)))
h2 = mesh(GLNormalMesh(Cylinder(Point3f0(x, y, 0),
Point3f0(x + δx, y, 0),
Float32(δx * 0.3)), 10))
end
end
println(h1[:light])
println(h2[:light])
The lighting of the cylinders, pyramids, cubes and cats seems to be compatible and coherent with a light coming from Float32[20.0, 20.0, 20.0]
.
using GeometryTypes, Makie, GLVisualize
scene = Scene(resolution = (500, 500))
δx = 0.1; nbc = 5; it = 0; h = []
for x in 0:δx:1
for y in 0:δx:1
h = if mod(it, nbc) == 0
objG = Sphere(Point3f0(x, y, 0), Float32(δx * 0.35))
mesh(GLNormalMesh(objG, 30))
elseif mod(it, nbc) == 1
objG = Cylinder(Point3f0(x - δx / 2, y, 0), Point3f0(x + δx / 2, y, 0),
Float32(δx * 0.3))
mesh(GLNormalMesh(objG, 30))
elseif mod(it, nbc) == 2
objG = GLVisualize.loadasset("cat.obj")
meshscatter([Vec3f0(x, y, 0),], marker = GLNormalMesh(objG), markersize = 1.5 * δx)
elseif mod(it, nbc) == 3
objG = GeometryTypes.HyperRectangle(Vec3f0(- 1 / 2), Vec3f0(1 / 2))
meshscatter([Vec3f0(x, y, 0),], marker = GLNormalMesh(objG), markersize = 1. * δx)
elseif mod(it, nbc) == 4
objG = GeometryTypes.Pyramid(Point3f0(0), 1f0, 1f0)
meshscatter([Vec3f0(x, y, 0),], marker = GLNormalMesh(objG), markersize = 1. * δx)
end
it += 1
end
end
println(h[:light])
Makie.axis(linspace(0, 1, 4), linspace(0, 1, 4), linspace(0, 1, 4))
Makie.center!(scene)
@SimonDanisch I am interested in looking at the possibilities to extend lighting options in Makie. Where should I start? I guess reflectance properties have first to be accessible in GLVisualize?
You can start by changing this shader to use uniforms instead of hard coded values:
https://github.com/JuliaGL/GLVisualize.jl/blob/master/assets/shader/standard.frag#L47
You can first do something like changing the hard coded vec3(0.1)
to :
uniform vec3 diffuse_amount = vec3(0.1);
And then this should already work in a non breaking fashion:
_view(visualize(mesh, diffuse_amount = Vec3f0(0.5)))
Then you can start putting the defaults in: https://github.com/JuliaGL/GLVisualize.jl/blob/master/src/visualize_interface.jl#L8
And that's pretty much it :) The bigger change will be to have something that is global to a scene. But I think this will come pretty naturally in Makie!
Thank you for the precise road map! Nice, I will try this.
By the way other constant vectors vec3(0.3), vec3(0.9)
and vec(0.3)
in the formula
return vec3(
vec3(0.1) * vec3(0.3) +
vec3(0.9) * color * diff_coeff +
vec3(0.3) * spec_coeff
);
have to be hard coded or they correspond to classical parameters? If it is the case what could be their name? I tried to identify them but their is plenty of different formula related to blinnphong..
Finally, I noticed that the light
attribute in Makie
is made of 4 vectors. The last one corresponds to the position. Do the others 3 should correspond to some of these vec3
?
Thanks.
I just have a more careful look to your last pointer and understood that diffuse_amount = vec3(0.1)
should correspond to the second component of
_default_light = Vec3f0[Vec3f0(1.0,1.0,1.0), Vec3f0(0.1,0.1,0.1), Vec3f0(0.9,0.9,0.9), Vec3f0(20,20,20)]
I see the vec3(0.9)
but I do not know to which characteristic it corresponds.
I do not know neither to which vector should correspond the first one Vec3f0(1.0,1.0,1.0)
.
Sorry for the noise.
in this order: ambient
, diffuse
, specular
.
Do the others 3 should correspond to some of these vec3?
Yes :) I think I used the same order for the light... Although ambient of 1.0 would be terrible... Just play around with it, and either swap them, or use the second slot as ambient.... Or maybe those values are all super bad :D I don't remember anymore why I took them out, and what they corresponded to last :(
Have a look at https://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model to get an idea
OK, I will have a look when I find some time and let you know. Thanks for the details.
Some conclusions and (too much) questions. Based on wikipedia Blinn-Phong as suggested.
I first tried to establish a correspondence between our three terms and the ones of the wikipedia's page
first term vec3(0.1) * vec3(0.3) <-> ambientColor
Proposal for a new description ambientcolor
vec3(0.9) * diff_coeff * color <-> diffuseColor * lambertian * lightColor * lightPower / distance
Proposal for a new description diffusecolor * diff_coeff * color
third term vec3(0.3) * spec_coeff <-> specColor * specular * lightColor * lightPower / distance
Proposal for a new description specularcolor * spec_coeff * color
Fact that should be checked can I effectively conclude that color <-> lightColor * lightPower / distance
uniform vec3 ambientcolor = vec3(0.03); uniform vec3 diffusecolor = vec3(0.9); uniform vec3 specularcolor = vec3(0.3); uniform float shininess = 8.0;
and implement the new descriptions.
My last question is how to define default values and relate them together. As suggested I can adapt the default values of the _default_light
array but how to connect the ambientcolor
(for instance) and the first vector of the light array??
I checked that the new options do have some effects :-) with the small example
using GLVisualize, GeometryTypes, GLAbstraction, Colors, FileIO
window = GLVisualize.glscreen()
lightd = Vec3f0[Vec3f0(1.0,1.0,1.0), Vec3f0(0.1,0.1,0.1), Vec3f0(0.9,0.9,0.9), Vec3f0(0,0,200)]
for x = 0:2
for y = 0:2
dc = linspace(0, 2, 3)[x + 1]
sc = linspace(0, 2, 3)[y + 1]
sphere_mesh = GLNormalMesh(Sphere{Float32}(Point3f0(x, y, 0), 0.5f0), 50)
GLVisualize._view(GLVisualize.visualize(sphere_mesh, color = RGBA{Float32}(1, 0, 0, 1.),
ambientcolor = Vec3f0(0.01),
diffusioncolor = Vec3f0(dc),
specularcolor = Vec3f0(sc),
light = lightd, shininess = Float32(3.)),
window)
end
end
GLVisualize.renderloop(window)
Proposal to be checked
Perhaps adding 3 last (?) intensity parameters ambientintensity
, diffuseintensity
and specularintensity
. The last two could be by default equal to ~ 4
and this would be a simple way to have the possibility to force effects preserving colors in [0, 1] ^ 3
, no? Thus we would have the final relation
ambientintensity * ambientcolor
+ diffuseintensity * diffusecolor * diff_coeff * color
+ specularintensity * specularcolor * spec_coeff * color
and the 4 new parameters ( ambientintensity
, diffuseintensity
, specularintensity
, shininess
) could be collected in a 5th vector of the light
array?
At least this seems to be compatible with parameters illustrated in matlab help.
It would remain to define default parameters :-( and to connect the light fileds with these parameter.
With the above formulation, I tried to reproduce the two illustrations of matlab's help with the default parameters
uniform vec3 ambientcolor = vec3(0.03);
uniform vec3 diffusecolor = vec3(0.9);
uniform vec3 specularcolor = vec3(0.3);
uniform float ambientintensity = 1.;
uniform float diffuseintensity = 1.;
uniform float specularintensity = 1.;
uniform float shininess = 8.0;
The first tests seem to correspond whereas the results of the second one seem to me different but more intuitive..
window = GLVisualize.glscreen()
lightd = Vec3f0[Vec3f0(1.0,1.0,1.0), Vec3f0(0.1,0.1,0.1), Vec3f0(0.9,0.9,0.9), Vec3f0(0,0,200)]
for x = 0:2
for y = 0:2
dc = linspace(0, 1, 3)[x + 1]
sc = linspace(0, 1, 3)[2 - y + 1]
sphere_mesh = GLNormalMesh(Sphere{Float32}(Point3f0(x, y, 0), 0.45f0), 50)
GLVisualize._view(GLVisualize.visualize(sphere_mesh, color = RGBA{Float32}(1, 0, 0, 1.),
ambientcolor = Vec3f0(0.03),
diffusecolor = Vec3f0(0.9),
specularcolor = Vec3f0(0.3),
light = lightd, shininess = Float32(8.),
ambientintensity = Float32(1),
diffuseintensity = Float32(dc),
specularintensity = Float32(sc)),
window)#, camera = :orthographic_pixel)
end
end
for x = 0:2
for y = 0:2
ac = if x == 0
[0, 1, 0]
elseif x == 1
[0.5, 0, 1]
elseif x == 2
ones(3)
end
as = linspace(0, 1 / 2, 3)[y + 1]
sphere_mesh = GLNormalMesh(Sphere{Float32}(Point3f0(x , 2 - y - 4, 0), 0.45f0), 50)
GLVisualize._view(GLVisualize.visualize(sphere_mesh, color = RGBA{Float32}(1, 0, 0, 1.),
ambientcolor = Vec3f0(ac...),
diffusecolor = Vec3f0(0.9),
specularcolor = Vec3f0(0.3),
light = lightd, shininess = Float32(8.),
ambientintensity = Float32(as),
diffuseintensity = Float32(1.),
specularintensity = Float32(1.)),
window)#, camera = :orthographic_pixel)
end
end
GLVisualize.renderloop(window)
Hm, I would have thought that the ambient light should change the color :D https://cglearn.codelight.eu/pub/computer-graphics/shading-and-lighting
It affects definitely the color (second test on the right) but it only changes dramatically the color if its intensity is sufficient..
Le 07/12/2017 à 11:33, Simon a écrit :
Hm, I would have thought that the ambient light should change the color :D https://cglearn.codelight.eu/pub/computer-graphics/shading-and-lighting
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/SimonDanisch/Makie.jl/issues/27#issuecomment-349928150, or mute the thread https://github.com/notifications/unsubscribe-auth/AOTEe-UGgU_JZU3XHoaU1kQJz3pW82PQks5s978LgaJpZM4Qx8ce.
-- Edouard Oudet : http://www-ljk.imag.fr/membres/Edouard.Oudet/ IMAG - Bureau 164 700 avenue Centrale 38400 Saint Martin d'Hères +33 (0)4 57 42 17 71 (office LJK) +33 (0)4 79 68 82 06 (home)
It affects definitely the color
I mean compared to matlab, your results seem more likely ;)
ah ok ;-), yes in fact !
They probably developed a plotting friendly shading model, that doesn't become super white and doesn't change the color too much ;)
Still some remarks on the lighting
we could implement previous options but I do not know how to attach uniform
data to light
vectors/parameters. It seems to me that this question has to be addressed keeping in ming that we could have several lights with several attributes..
I am surprised that blinnphong
function is defined in three different files (with the hard coded parameters): assets/shader/standard.frag, assets/shader/uv_normal.frag and assets/shader/volume.frag. Should we keep only the standard.frag version?
As noticed previously, lights are expected to have a global effect to every element of a scene (or a subscene). I understood that today in Makie, lights are associated to Nodes
. Should we link every light Nodes
of a scene together? Again, how to implement several lights?
@SimonDanisch I guess points (1) above requires a global point of view of the design of Makie.jl
and how it interacts with the GL*
packages, while (3) seems to me more related to the shader. Unfortunately, I can not help directly to improve (1) & (3) but if you have some idea of intermediate tasks, I would be happy to contribute.
Point (2) looks more straightforward(?)
we could implement previous options but I do not know how to attach uniform data to light vectors/parameters
What is exactly the problem? just pass the data as a keyword argument to GLVisualize and it should pick it up automatically. For Makie, that's not always true since I do some filtering, but we could include it ;)
2) Yeah, they should all be the same and controlled by the same uniforms. I think the only exception was the volume shader, where performance is a bit more crucial so a simpler version might be appropriate - but it should still use the same uniforms!
3) In Makie, missing attributes will be inherited from the parent scene, so if you don't pass a light attribute to a plotting function, they should all end up with the same light uniforms!
(1) & (3) Ah, I see, when you write uniform you mean a single light. "The problem" was for me precisely related to several lights: I do not know how to attach the (global/uniform) parameters to a specific light. Actually, I understood that this possibility does not exist for the moment but it seems to me that it is perhaps more efficient to define the strategy to have several lights before to give the possibility to adjust parameters, no?
(2) You mean only keep the definition of blinnphong
function only in standard.frag ("extended" version), and volume.frag (actual version)?
1) You could do something like:
struct Light{
vec3 color;
vec3 position;
...
};
uniform Light[5] lights; // 5 lights
But sadly the infrastructure in GLAbstraction isn't ready for that quite yet. GLSL does it best to make arrays of structs awkward. I've some prototypes for this, but was never 100% sure how to integrate it into the current design. Let me think about this, maybe there is an easy adhoc solution!
2) not sure what you mean by actual
version... But I'd be happy with any solution that unifies things, without drastically reducing performance of the iso surface rendering ;)
I just found this
which looks very close to what we are looking for. Also the fields of a light are a little bit different but seem more generic! Additionally, it also implements the material properties which could be very convenient in some specific situations.
I could try to follow very closely (~ copy / paste) this code or do you see source of troubles in GLVisualize
context? This would define a new type light
and a new type material
.
Last naive question: Is the const
type in const int numberOfLights = 2;
mandatory..?
Well, that's very close to what I've suggested, and comes exactly with the problems I mentioned... Although the posted code doesn't actually have the problems, since it hard codes the light sources so they don't need to get set from the julia side, but I'm sure that's not what you're aiming for ;)
Yes, I think the const
is needed, you can't have the size of the array be non const
.
Ah ok sorry, this is the GLAbstraction infrastructure point you mentioned related to GLSL structures.
For me this pointer was a progress since I was not sure how to define/tune all these parameters and this piece of code seems to me generic enough for most situations. I guess you already had a look to it in the past ;-)
Yes I did ;) Yeah passing arrays of struct to OpenGL is no fun... Well if you want, play around with it with hardcoded values as in the example, and hopefully I can backport some code from Visualize.jl, where I have the struct stuff figured all out...
Actually, we could also use the convenient structure at julia level and pass an array of array if it is simpler. The crucial point would be the non fixed size of the array (== nb of lights). I guess it would have the same complexity..? Perfect plan for me, I will first try to get familiar with the model using hardcoded values.
I tried to have a look and it compiles..but the current experimental version gives me only black object :-). Regarding the GLSL code, (at least) two points are not clear to me
They use a vector position for spot lights (only)
varying vec4 position; // position of the vertex (and fragment) in world space
Do we have access to the position in our context? I also read that varying
is deprecated..
In your original version of blinnphong, you use the input color
only for the diffuse term. In the GLSL code, there is no color keyword but material fields . My hypothesis is that material.diffuse
plays the role of the color
term you used but I am not sure. Other material fileds/colors would be additional parameters..
yes, material.diffuse == color
OK cool! Thanks. Let's forget spotlights for the moment ;-)
OK, I managed to make it work in a very pedestrian way. Sorry for the naivety of the code!! Finally
onoff
bools which add only five equality tests with respect to original version. I do not know if it could have a severe impact on intensive computations..uniform vec3 ambientcolor;
const int numberOfLights = 5;
struct lightSource
{
vec3 color;
vec3 position;
int onoff; // == 0 or 1
};
uniform vec3 position1;
uniform vec3 position2;
uniform vec3 position3;
uniform vec3 position4;
uniform vec3 position5;
uniform vec3 colorlight1;
uniform vec3 colorlight2;
uniform vec3 colorlight3;
uniform vec3 colorlight4;
uniform vec3 colorlight5;
uniform int onoff1; uniform int onoff2;
uniform int onoff3; uniform int onoff4; uniform int onoff5;
lightSource light0 = lightSource(
colorlight1, // color
position1, // position
onoff1
);
lightSource light1 = lightSource(
colorlight2, // color
position2, // position
onoff2
);
lightSource light2 = lightSource(
colorlight3, // color
position3, // position
onoff3
);
lightSource light3 = lightSource(
colorlight4, // color
position4, // position
onoff4
);
lightSource light4 = lightSource(
colorlight5, // color
position5, // position
onoff5
);
lightSource lights[numberOfLights];
uniform vec4 material; //vec4(1., 0.4, 0.5, 1.);
/*
(ambient, diffuse, specular, specularcolorcoeff) ∈ [0, 1]
default vec4(1., 0.4, 0.5, 1.);
*/
uniform float shininess;
///////////////////////////////////////////////////////////////////////////////
struct Nothing{ //Nothing type, to encode if some variable doesn't contain any data
bool _; //empty structs are not allowed
};
in vec3 o_normal;
in vec3 o_lightdir;
in vec3 o_vertex;
in vec4 o_color;
in vec2 o_uv;
flat in uvec2 o_id;
{{color_type}} color;
vec4 get_color(vec3 color, vec2 uv){
return vec4(color, 1.0 + (0.0 * uv)); // we must prohibit uv from getting into dead variable removal
}
vec4 get_color(vec4 color, vec2 uv){
return color + uv.x * 0.0; // we must prohibit uv from getting into dead variable removal
}
vec4 get_color(Nothing color, vec2 uv){
return o_color + uv.x * 0.0;
}
vec4 get_color(samplerBuffer color, vec2 uv){
return o_color + uv.x * 0.0;
}
vec4 get_color(sampler2D color, vec2 uv){
return texture(color, uv);
}
/*
1) L light direction still needed as input parameters?
2) Term pow(max(0.0, dot(reflect(-L, N), V)), shininess) different
from first implementation??
*/
vec3 blinnphong(vec3 N, vec3 V, vec3 L, vec3 color){
// define lights
lights[0] = light0;
lights[1] = light1;
lights[2] = light2;
lights[3] = light3;
lights[4] = light4;
// initialize total lighting with ambient lighting
vec3 totalLighting = material[0] * vec3(ambientcolor);
for (int index = 0; index < numberOfLights; index++) // for all light sources
{
if (lights[index].onoff == 1)
{
//??? L
L = normalize(vec3(lights[index].position));
vec3 diffuseReflection = material[1] * vec3(lights[index].color) *
max(0.0, dot(N, L)) * color;
vec3 specularReflection;
if (dot(N, L) < 0.0) // light source on the wrong side?
{
specularReflection = vec3(0.0, 0.0, 0.0); // no specular reflection
}
else // light source on the right side
{
vec3 specularcolor = (1 - material[3]) * vec3(lights[index].color) +
material[3] *vec3(1);
specularReflection = material[2] * vec3(specularcolor) *
pow(max(dot(L, N), 0.0), shininess);
}
totalLighting = totalLighting + diffuseReflection + specularReflection;
}
}
return totalLighting;
}
and the piece of code in vizualize_interface.jl is simply
@gen_defaults! data begin # make sure every object has these!
model = eye(Mat4f0)
light = _default_light
preferred_camera = :perspective
is_transparent_pass = Cint(false)
# default lights attributes of a scene
material = Vec4f0(1., 0.4, 0.5, 1.) # (ambient, diffuse, specular, specularcolorcoeff) ∈ [0, 1]
shininess = Float32(8.); ambientcolor = Vec3f0(0.01)
onoff1 = 1; position1 = Vec3f0(10.0, 100.0, 200.0); colorlight1 = Vec3f0(1.)
onoff2 = 0; position2 = Vec3f0(10.0, 100.0, -200.0); colorlight2 = Vec3f0(1.)
onoff3 = 0; position3 = Vec3f0(10.0, 100.0, 200.0); colorlight3 = Vec3f0(1.)
onoff4 = 0; position4 = Vec3f0(10.0, 100.0, 200.0); colorlight4 = Vec3f0(1.)
onoff5 = 0; position5 = Vec3f0(10.0, 100.0, 200.0); colorlight5 = Vec3f0(1.)
end
Illustration of two lights in a single scene and other material properties side 1/ side 2
and the example code
window = GLVisualize.glscreen(); δ = 0.45
lightd = Vec3f0[Vec3f0(1.0, 1.0, 1.0), Vec3f0(0.1, 0.1, 0.1),
Vec3f0(0.9, 0.9, 0.9), Vec3f0(10, 50, 200)]
markerm = :o
#markerm = :□
for x = 0:2
for y = 0:2
dc = linspace(0, 0.5, 3)[x + 1]
sc = linspace(0, 0.5, 3)[2 - y + 1]
lmesh = if markerm == :o
GLNormalMesh(Sphere{Float32}(Point3f0(x, y, 0), Float32(δ)), 50)
elseif markerm == :□
GLNormalMesh(GeometryTypes.HyperRectangle(Vec3f0(x - δ, y - δ , - δ),
Vec3f0(3 * δ / 2, 3 * δ / 2 , 3 * δ / 2)))
end
material = Vec4f0(1, dc, sc, 1.)
GLVisualize._view(GLVisualize.visualize(lmesh, color = RGBA{Float32}(1, 0, 0, 1.),
light = lightd, shininess = Float32(8.),
material = material,
position1 = Vec3f0(100), onoff2 = 1, colorlight1 = Vec3f0(0.8),
ambientcolor = Vec3f0(0.01)), window)
end
end
cd = 1.
for x = 0:2
for y = 0:2
ac = if x == 0
[0, 1, 0]
elseif x == 1
[0.5, 0, 1]
elseif x == 2
ones(3)
end
ac /= cd
as = linspace(0, 0.5, 3)[y + 1]
lmesh = if markerm == :o
GLNormalMesh(Sphere{Float32}(Point3f0(x , 2 - y - 4, 0), Float32(δ)), 50)
elseif markerm == :□
GLNormalMesh(GeometryTypes.HyperRectangle(Vec3f0(x - δ, 2 - y - δ - 4 , - δ),
Vec3f0(3 * δ / 2, 3 * δ / 2 , 3 * δ / 2)))
end
material = Vec4f0(as, 0.4, 0.8, 1.)
GLVisualize._view(GLVisualize.visualize(lmesh, color = RGBA{Float32}(1, 0, 0, 1.),
ambientcolor = Vec3f0(ac...),
light = lightd, shininess = Float32(8.),
material = material), window)
end
end
GLVisualize.renderloop(window)
Since the modifications described below,
scene = Makie.Scene(resolution = (500, 500))
Makie.mesh(GLNormalMesh(Sphere{Float32}(Point3f0(0), Float32(1)), 50))
center!(scene)
produces a colored shining sphere whereas
scene = Makie.Scene(resolution = (500, 500))
Makie.meshscatter([Vec3f0(0),], maker = GLNormalMesh(Sphere{Float32}(Point3f0(0), Float32(1)), 50))
center!(scene)
produces a black sphere (no light, no colors).. Any idea of what could be at the origin of the problem?
You haven't updated particles.vert?
No, should I?
I guess, it also uses standard.frag, and I'm guessing it's not receiving the correct inputs? Or did you actually not touch any vertex shader at all?
git diff master lightattributes
only gives me differences between the assets/shader/standard.frag
, assets/shader/uv_normal.frag
and src/visualize_interface.jl
files.
ah maybe, meshscatter doesn't pass through the attributes or something...
can you open a PR so that I can have a look at it?
Here it is lights attributes. Thanks.
Changing the two lines
into
viz = visualize(main, Style(:default), gl_data).children[]
seems to fix the problem
I try to identify what could be a more general but still efficient and generic way of handling lighting. Let us first focalize on GLVisualize and the shader:
lights
positions and colors = array (of variable size) of struct or something equivalentmaterial
= Vec4f0(ambient, diffuse, specular, specularcolorcoeff) ∈ [0, 1]shininess
= Float32(8.)ambientcolor
= Vec3f0(0.01)
backlighting
= Float32(0.4) (used in light_calc
function defined in surface.jl)backlighting
can be simulated by an additional light with low intensityIf we take that direction we need to:
src/vizualize_interface.jl
blinnphong
function. utils.jl
, in the function assemble_shader
seems to be a reasonable place.assets/shader/standard.frag
and assets/shader/uv_normal.frag
with the new blinnphong
function.light_calc
to cancle backlighting but enables backlighting by default when using a single light.volume.frag
is ok with previous changes: is it still possible to have an efficient and simple lighting ;-)?
Can we access/modify the lighting/reflectance properties of an object/mesh in Makie?