-
-
Notifications
You must be signed in to change notification settings - Fork 312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement multiple lights and more Light types #3246
Conversation
0e81e35
to
f68ca35
Compare
I'm following the attenuation from learnopengl/ogre3d for now, which is effectively fig = Figure(backgroundcolor = :black)
sl = Slider(fig[1, 1], range = 0.05:0.05:5.0)
lights = Makie.AbstractLight[
AmbientLight(RGBf(0.1, 0.1, 0.1)),
PointLight(RGBf(1,0,0), Point3f(2, 2, 0), 1.0),
PointLight(RGBf(0,1,0), Point3f(-2, 2, 0), 10.0),
PointLight(RGBf(0,0,1), Point3f(-2, -2, 0), 100.0),
PointLight(RGBf(1,1,1), Point3f(2, -2, 0), 10.0),
]
scene = LScene(
fig[2, 1], show_axis = false,
scenekw=(camera = cam3d!, lights = lights, backgroundcolor = :black, center = false),
)
p = mesh!(scene, Rect3f(Point3f(-10, -10, 0.01), Vec3f(20, 20, 0.02)), color = :white)
map(z -> translate!(p, 0, 0, -z), sl.value)
update_cam!(scene.scene, Vec3f(0, 0, 10), Vec3f(0, 0, 0), Vec3f(0, 1, 0))
idxs = vcat(1:length(sl.range[]), length(sl.range[])-1:-1:2)
record(fig, "attenuation.webm", idxs, framerate = 30) do idx
sl.selected_index[] = idx
end attenuation.webmI also tried just lights = [
AmbientLight(RGBf(0.1, 0.1, 0.1)),
PointLight(RGBf(0,1,0), Point3f(0, -3, 1)),
PointLight(RGBf(1,0,0), Point3f(-3, 0, 1)),
PointLight(RGBf(0,1,0), Point3f(0, 3, 1)),
PointLight(RGBf(1,0,0), Point3f( 3, 0, 1)),
Makie.DirectionalLight(RGBf(0, 0, 0.5), Vec3f(0, 0, 1)),
]
scene = Scene(camera = cam3d!, lights = lights)
mesh!(scene, Rect3f(Point3f(-1), Vec3f(2)), color = :white)
r = range(-10, 10, length=101)
surface!(scene, r, r, [0.5 * (sin(x+y) * sin(2x) * sin(3y)) - 1 for x in r, y in r], colormap = [:white, :white])
ps = [light.position[] for light in lights if light isa PointLight]
scatter!(scene, ps, color = :yellow, strokewidth = 1, strokecolor = :black)
screen = display(scene) |
Compile Times benchmarkNote, that these numbers may fluctuate on the CI servers, so take them with a grain of salt. All benchmark results are based on the mean time and negative percent mean faster than the base branch. Note, that GLMakie + WGLMakie run on an emulated GPU, so the runtime benchmark is much slower. Results are from running: using_time = @ctime using Backend
# Compile time
create_time = @ctime fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @ctime Makie.colorbuffer(display(fig))
# Runtime
create_time = @benchmark fig = scatter(1:4; color=1:4, colormap=:turbo, markersize=20, visible=true)
display_time = @benchmark Makie.colorbuffer(display(fig))
|
GLMakie/src/drawing_primitives.jl
Outdated
updater = screen.render_tick | ||
MAX_PRIORITY = typemax(Int) | ||
|
||
attr[:lights_length] = map(updater, priority = -1000) do _ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updating on render_tick
doesn't work with record
.
Updating (many) lights is kind of complicated in general, because we want to react to updates of each field for each light and also when lights are added or deleted to the lights vector.
Listening to each field individually would require attaching and removing listeners when the lights vector changes. Detecting that would either require some kind of Vector wrapper or an update on draw.
Instead of bothering with all of that I thought it would be easier to just check the lights vector once per frame and update it then. I'm using render_tick
for that atm, but I guess we don't want that to update in record
to avoid triggering events?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I adjusted colorbuffer
to trigger render_tick
now. That seems to be fine for rendering beyond screen resolution and will be useful if we add an on_render_start
event in the future.
Moving, colored `PointLight`s with different attenuation + white SpotLight.begin
fig = Figure(backgroundcolor = :black)
sl = Slider(fig[1, 1], range = 0.05:0.05:5.0)
# Prepare lights
angle = Observable(0)
angle2pos(phi) = Point3f(3cosd(phi), 3sind(phi), 0)
lights = Makie.AbstractLight[
AmbientLight(RGBf(0.1, 0.1, 0.1)),
PointLight(RGBf(1,0,0), map(phi -> angle2pos(phi + 0), angle), 10.0),
PointLight(RGBf(0,1,0), map(phi -> angle2pos(phi + 120), angle), 100.0),
PointLight(RGBf(0,0,1), map(phi -> angle2pos(phi + 240), angle), 1000.0),
SpotLight(RGBf(1,1,1), Point3f(0, 0, 1), Vec3f(0, 0, -1), pi/6),
]
# Set scene
scene = LScene(
fig[2, 1], show_axis = false,
scenekw=(camera = cam3d!, lights = lights, backgroundcolor = :black, center = false),
)
# floor
p = mesh!(scene, Rect3f(Point3f(-10, -10, 0.01), Vec3f(20, 20, 0.02)), color = :white)
map(z -> translate!(p, 0, 0, -z), sl.value)
# Random balls
N_balls = 2_000
random_colors = !true
meshscatter!(
scene, [Point3f(20rand()-10, 20rand()-10, 10rand()-5) for _ in 1:N_balls],
color = random_colors ? rand(RGBf, N_balls) : (:white)
)
# Visualize light source positions
ps = Observable([light.position[] for light in lights if light isa Union{PointLight, SpotLight}])
cs = [light.color[] for light in lights if light isa Union{PointLight, SpotLight}]
scatter!(scene, ps, color = cs, strokewidth = 1, strokecolor = :white)
# Place camera above
# update_cam!(scene.scene, Vec3f(0, 0, 10), Vec3f(0, 0, 0), Vec3f(0, 1, 0))
# place camera at angle nearby
update_cam!(scene.scene, Vec3f(8, 1, 4), Vec3f(0, 0, 0), Vec3f(0, 0, 1))
# record
idxs = vcat(1:length(sl.range[]), length(sl.range[])-1:-1:2)
record(fig, "point_lights_and_spot_light.webm", idxs, framerate = 30) do idx
sl.selected_index[] = idx
angle[] = angle[] + 4
ps[] = [light.position[] for light in lights if light isa Union{PointLight, SpotLight}]
notify(fig.scene.current_screens[1].render_tick)
end
# interactive
# display(fig)
# @async while isopen(fig.scene)
# angle[] = angle[] + 2
# ps[] = [light.position[] for light in lights if light isa Union{PointLight, SpotLight}]
# sleep(1/60)
# end
end point_lights_and_spot_light.webm |
# polling may change window size, when its bigger than monitor! | ||
# we still need to poll though, to get all the newest events! | ||
# GLFW.PollEvents() | ||
pollevents(screen) | ||
# keep current buffer size to allows larger-than-window renders | ||
render_frame(screen, resize_buffers=false) # let it render |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added pollevents()
here so lighting updates from render_tick
can take effect before drawing the frame. Given the note I would assume this breaks output sizes with save
and record
, however I have no problems saving/recording at resolution = (4000, 4000)
.
GLMakie/assets/shader/util.vert
Outdated
// direction to light for :fast shading | ||
// (normalizing this will result in incorrect lighting) | ||
vec4 view_light_pos = view*vec4(lightposition, 1.0); | ||
o_lightdir = view_light_pos.xyz / view_light_pos.w - view_pos.xyz; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As noted normalizing here results in incorrect light directions/perceived positions. To illustrate this, consider a light source shining on a rect viewed from the side:
If we don't normalize o_lightdir
it will be a vector pointing from the light source to the vertex handled by the vertex shader. Since I placed the light source at (0, 0) here they'll match p1 and p2 in the example. On the way to the fragment shader o_lightdir
will then get interpolated between different vertices. For example, halfway between the two vertices the vector will point to the green "Vertex center". Normalizing in the fragment shader will then give correct light-to-vertex-center direction.
If we do normalize o_lightdir
in the vertex shader as before, we will be interpolating the normalized o_lightdir
vectors. In the sketch above those point to n1 (equal to p1) and n2. Doing the same half-way interpolation for vertices as above, we end up with a o_lightdir
halfway between n1 and n2, which is marked as "Normalized center". This is not a vector passing through the vertex center position and thus the wrong light direction.
I'm pretty sure the test failure in Order Independent transparency
is a result of this change. There are likely quite a few more failures that didn't get caught.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched to doing lighting in world space so this is not really relevant to lightdir anymore. Though it probably still applies to camdir. (World vs view/camera space shouldn't matter for lighting. It was using view/camera space because SSAO needed it and lighting could reuse it. Merging lighting between volume and the rest of Makie requires world space though.)
Volume with multiple lights: volume.webmVolume plots don't differentiate inwards facing vs outwards facing surfaces, so lighting is a bit weird here. I'm not going to try to change this here though. |
As far as I can tell the only issue left here that isn't also on #3113 is with "contour labels 3D". With Axis3 scaling being moved to |
Continues #2831 ! Still needs to check, if I rebased correctly and didn't incorrectly apply some of the changes! ## Merged PRs - #2598 - #2746 - #2346 - #2544 - #3082 - #2868 - #3062 - #3106 - #3281 - #3246 ## TODOS - [x] fix flaky test `@test GLMakie.window_size(screen.glscreen) == scaled(screen, (W, H))` - [x] Merge axis type inferences from #2220 - [x] Test on different resolution screens, IJulia, Pluto, VSCode, Windowed - [x] rebase to only have merge commits from the PRs - [x] investigate unexpected speed ups - [x] reset camera settings from tests - [ ] check doc image generation - [x] rethink default near/far in Camera3D (compatability with OIT) - [x] merge #3246 - [x] fix WGLMakie issues/tests: - [x] fix line depth issues (see tests: ~~hexbin colorrange~~ (not new), LaTeXStrings in Axis3, Axis3 axis reversal) - [x] fix lighting of surface with nan points (fixed in #3246) - ~~volume/3D contour artifacts (see 3D Contour with 2D contour slices)~~ not new - ~~artifacting in "colorscale (lines)"~~ not new - [x] GLMakie: - [x] slight outline in "scatter image markers" test - ~~clipping/z-fighting in "volume translated"~~ not new - [x] CairoMakie: - ~~Artfiacting in `colorscale (lines)"~~ not new - ~~markersize in "scatter rotations" changed?~~ not new - ~~color change in "colorscale (poly)"~~ not new - ~~transparency/render order of "OldAxis + Surface"~~ not new - ~~render order in "Merged color mesh"~~ not new - ~~render order of "Surface + wireframe + contour"~~ not new - [x] Check "SpecApi in convert_arguments" (colors swapped?) ## Fixes the following errors - fixes #2721 via #2746 - fixes #1600 via #2746 - fixes #1236 via #2746 - fixes MakieOrg/GeoMakie.jl#133 via #2598 - closes #2522 - closes #3239 via #3246 - fixes #3238 via #3246 - fixes #2985 via #3246 - fixes #3307 via #3281
Description
Change Summary
This pr adds/changes:
backlight
now affectsnormal
instead oflightdir
. This means specular reflections show up with backlightDirectionalLight
(closes Directional light as new default #3239)diff_coeff
around 0. This softens the edge where normals are perpendicular to the light direction)shading
attribute is now:none/:fast/:verbose
instead of true or falsePointLight
now has attenuation parameters (distance based intensity falloff, off by default)DirectionalLight
(parallel light rays)SpotLight
(light source illuminating a cone)PointLight
(and all others) is now connected (fixes Point light color is ignored in GLMakie #3238)colorbuffer()
now polls events to triggerrender_tick
so that it can cause lighting updateslights
vector (fix Issue with typing on LScene with lights #2985)Type of change
TODO
shading