Skip to content
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

Order independent transparency #1418

Merged
merged 29 commits into from Dec 5, 2021
Merged

Order independent transparency #1418

merged 29 commits into from Dec 5, 2021

Conversation

ffreyer
Copy link
Collaborator

@ffreyer ffreyer commented Oct 29, 2021

Using commit #dceac53

Screenshot from 2021-10-30 12-03-44

with

using GLMakie
scene = Scene()
r = Rect3f(Point3f(-1, -0.1, -1), Vec3f(2, 0.2, 2))

ps = [Point3f(0, y, 0) for y in (0, 0.2, 0.4)]
cs = [RGBAf(1, 0, 0, 0.3), RGBAf(0, 1, 0, 0.5), RGBAf(0, 0, 1, 0.7)]

meshscatter!(scene, ps .+ Point3f(0, 0, 0), marker = r, color = cs[[1, 2, 3]], transparency = true)
meshscatter!(scene, ps .+ Point3f(1, 0, 0), marker = r, color = cs[[2, 3, 1]], transparency = true)
meshscatter!(scene, ps .+ Point3f(0, 0, 1), marker = r, color = cs[[3, 1, 2]], transparency = true)
meshscatter!(scene, ps .+ Point3f(1, 0, 1), marker = r, color = cs[[1, 3, 2]], transparency = true)
meshscatter!(scene, ps .+ Point3f(1.1, 0.1, 1.1), marker = r, color = :gray, transparency = false)
cam3d!(scene)
center!(scene)
scene

See #1390

I'll try to summarize my changes a bit:

For OIT I added a RGBA{Float16} color buffer and a N0f8 buffer. The former holds the sum of transparent colors and weights. The latter holds the product of all (1-alpha_i). They are written to in fragment_output.frag via mustache replacements, so transparency is a static per plot just like shading. The summation and the product of colors, weights and alpha values work via blending functions. All transparency = true plots are rendered and then merged into the main color buffer (via OIT postprocessor) at the same stage, after SSAO and before FXAA.

I also made some general adjustments:

  • I removed the second framebuffer (which only had the color_luma in it)
  • I reduced the position buffer for SSAO to Float16 I went back on this because the positions are not normalized. I'm worried using Float16 might break SSAO in a large coordinate space. I'm not sure if it works in the first place though.
  • reduced position buffer from 4 floats to 3
  • I'm reusing the float16 colorbuffer from OIT as a normal_occlusion buffer for SSAO and as color_luma for FXAA.
  • I added a function to add colorbuffers with automatic id finding. This relies on GL_COLOR_ATTACHMENTs to be successive numbers.
  • I adjusted enabletransparency() to keep the output alpha at 1 for the opaque color buffer. I also adjusted the OIT blending shader to ignore other values. One of those two seems to be necessary.

After merging this pr, rendering will require

  • 4*8 color + 2*32 objectid + 24+8 depth & stencil + 4*16 transparent color & weight + 8 alpha = 200 Byte per pixel without SSAO active (8 more than before with FXAA active, 72 more with FXAA off)
  • 200 + 3*32 position = 296 Bytes per pixel with FXAA+SSAO active (152 less than before)

Comment on lines 12 to 19
mutable struct GLFramebuffer
resolution::Observable{NTuple{2, Int}}
id::NTuple{2, GLuint}
id::GLuint

buffer_ids::Dict{Symbol, GLuint}
buffers::Dict{Symbol, Texture}
render_buffer_ids::Vector{GLuint}
end
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before I end up forgetting this - I removed the second framebuffer because it doesn't seem necessary to me. Because of that there is only one id here. FXAA now uses a second texture/color buffer on the primary framebuffer instead.

Comment on lines 30 to 44
function attach_framebuffer!(fb::GLFramebuffer, t::Texture{T, 2}) where T
max_color_id = GL_COLOR_ATTACHMENT0
for id in values(fb.buffer_ids)
if GL_COLOR_ATTACHMENT0 <= id <= GL_COLOR_ATTACHMENT15 && id > max_color_id
max_color_id = id
end
end
next_color_id = max_color_id + 0x1
if next_color_id > GL_COLOR_ATTACHMENT15
error("Ran out of color buffers.")
end

color_buffer = Texture(RGBA{N0f8}, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge)
objectid_buffer = Texture(Vec{2, GLuint}, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge)
glFramebufferTexture2D(GL_FRAMEBUFFER, next_color_id, GL_TEXTURE_2D, t.id, 0)
return next_color_id
end
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also added a new function to attach color buffers. It looks through the already attached ids and figures out the next one automatically.

@ffreyer
Copy link
Collaborator Author

ffreyer commented Oct 29, 2021

One problem with OIT is that colors can get overexposed in the transparency pass because its color buffer isn't fixed to a (0, 1) range. The current output for overlapping panes (lit front, unlit back) looks like this:

Screenshot from 2021-10-29 22-15-25
Screenshot from 2021-10-29 22-16-39

We could explicitly clamp(color, 0, 1) before applying weights to combat this effect. The result looks like this:

Screenshot from 2021-10-29 22-15-45
Screenshot from 2021-10-29 22-16-06

Or a more extreme clamp(color.rgb, 0, color.a):

Screenshot from 2021-10-29 22-15-05
Screenshot from 2021-10-29 22-16-24

All the images use panes with colors RGBAf(1, 0, 0, 0.5), RGBAf(0, 1, 0, 0.5) and RGBAf(0, 0, 1, 0.5). I'm not sure if clamping is the right choice here, especially the last case since it may distort colors. But we probably want some kind of color/brightness adjustments here, right?

@ffreyer
Copy link
Collaborator Author

ffreyer commented Oct 29, 2021

Looks like enable_depth in volume messes with the depth_shift test. Also scenekw = (show_axis = false,)) in LScene doesn't work anymore.

@SimonDanisch
Copy link
Member

SimonDanisch commented Oct 29, 2021

Yeah the axis moved out of scene into lscene, so you need to directly pass show_axis to the lscene

@ffreyer
Copy link
Collaborator Author

ffreyer commented Oct 29, 2021

Also is the upload of the test images gone or am I just blind?

@SimonDanisch
Copy link
Member

Yeah they changed the UI it seems... The only way I found them was navigating to summary:
image
And on that page you'll find the artifacts

@ffreyer
Copy link
Collaborator Author

ffreyer commented Oct 30, 2021

I changed \sum_i alpha_i * w(z_i, alpha_i) from the paper to just \sum_i w(z_i, alpha_i). That fixes the issue of overexposing and it fixes an issue with transparent grays getting mapped to white (i.e. rgb >= 1).

Example with alpha = (1, 0.75, 0.5, 0.25):

Screenshot from 2021-10-30 11-39-04

Example with mixed alpha (blue/magenta with alpha = 0.25, green/cyan = 0.5, red/yellow = 0.75):
Screenshot from 2021-10-30 11-47-37

@lazarusA
Copy link
Contributor

this is looking great!

@ffreyer
Copy link
Collaborator Author

ffreyer commented Oct 31, 2021

The current implementation is reading from and writing to the same color buffer. Is this something I should avoid?

I switched to doing it with blend functions.

@ffreyer ffreyer marked this pull request as ready for review October 31, 2021 15:07
@ffreyer
Copy link
Collaborator Author

ffreyer commented Nov 7, 2021

This breaks contour(rand(10, 10, 10), levels=[0.5]) for some reason...

Fixed it

@ffreyer
Copy link
Collaborator Author

ffreyer commented Dec 5, 2021

I added a documentation page on transparency. Do I need to include it in some list somewhere or is it automatically included?

@SimonDanisch
Copy link
Member

Awesome! :)
It's here:
https://makie.juliaplots.org/previews/PR1418/documentation/transparency/
under documentation!
You want to do the honor of merging?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants