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
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ce9d10b
skip second framebuffer & dynamic attachment ids
ffreyer Oct 28, 2021
af9a584
draft OIT implementation
ffreyer Oct 29, 2021
14185d8
combine shaders
ffreyer Oct 29, 2021
4670567
rename and simply colorbuffer attachment
ffreyer Oct 29, 2021
27e005c
reduce SSAO buffer size and reuse HDR_color
ffreyer Oct 29, 2021
4a837b0
add some convenience
ffreyer Oct 29, 2021
1b2ccb8
fix typos
ffreyer Oct 29, 2021
497d3b1
fix error for nonstandard prerenderers
ffreyer Oct 29, 2021
12f0005
cleanup
ffreyer Oct 29, 2021
e6eaa93
fix transparency with transparency = false
ffreyer Oct 30, 2021
dceac53
remove alpha from weight sum
ffreyer Oct 30, 2021
e44ead8
switch back to Float32 for position buffer
ffreyer Oct 31, 2021
47f7ba7
reuse HDR color buffer for FXAA
ffreyer Oct 31, 2021
18bac48
add note on buffer reuse
ffreyer Oct 31, 2021
c242b0a
Merge branch 'master' into ff/OIT
ffreyer Oct 31, 2021
05527a7
cleanup some comments
ffreyer Oct 31, 2021
787ce50
fix discoloration
ffreyer Oct 31, 2021
dcb96fa
remove warning
ffreyer Oct 31, 2021
3f6968c
use that opaque alpha = 0
ffreyer Oct 31, 2021
091b9ea
add OIT test
ffreyer Oct 31, 2021
cf2d511
add transparency
ffreyer Nov 1, 2021
7db7666
avoid reading and writing to the same colorbuffer
ffreyer Nov 1, 2021
1e67a48
reduce position buffer size
ffreyer Nov 2, 2021
a74212e
Merge branch 'master' into ff/OIT
ffreyer Nov 7, 2021
307f76d
fix 3d contour
ffreyer Nov 7, 2021
d81ef29
Merge branch 'master' into ff/OIT
SimonDanisch Dec 2, 2021
237264c
Update runtests.jl
SimonDanisch Dec 2, 2021
4fe8752
Update runtests.jl
SimonDanisch Dec 2, 2021
9aea42e
add docs for transparency and OIT
ffreyer Dec 5, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 27 additions & 0 deletions GLMakie/assets/shader/postprocessing/OIT_blend.frag
@@ -0,0 +1,27 @@
{{GLSL_VERSION}}

// See https://github.com/JuliaPlots/Makie.jl/issues/1390

in vec2 frag_uv;
// contains opaque color from previous main render steps
uniform sampler2D opaque_color;
// contains sum_i C_i * weight(depth_i, alpha_i)
uniform sampler2D sum_color;
// contains pod_i (1 - alpha_i)
uniform sampler2D prod_alpha;

out vec4 fragment_color;

void main(void)
{
vec4 color = texture(opaque_color, frag_uv);
vec4 summed_color_weight = texture(sum_color, frag_uv);
float transmittance = texture(prod_alpha, frag_uv).r;

vec3 weighted_transparent = summed_color_weight.rgb / max(summed_color_weight.a, 0.00001);
vec3 full_weighted_transparent = weighted_transparent * (1 - transmittance);
vec3 weighted_opaque = (color.rgb * color.a) * transmittance;

fragment_color.rgb = full_weighted_transparent + weighted_opaque;
fragment_color.a = 1;
}
22 changes: 22 additions & 0 deletions GLMakie/assets/shader/transparent_fragment_output.frag
@@ -0,0 +1,22 @@
{{GLSL_VERSION}}

layout(location=0) out vec4 fragment_color; // RGBAF16
layout(location=1) out uvec2 fragment_groupid; // keep this?
layout(location=2) out float coverage; // F16 or N0f8


in vec4 o_view_pos;
in vec3 o_normal;


void write2framebuffer(vec4 color, uvec2 id){
if(color.a <= 0.0)
discard;

// frag_Depth is in (-1, 1) right?
// summation/product via blend functions
float weight = color.a * max(0.01, 3000 * pow((1 - gl_FragCoord.z), 3));
fragment_color = weight * color;
coverage = color.a;
fragment_groupid = id;
}
33 changes: 30 additions & 3 deletions GLMakie/src/GLAbstraction/GLRenderObject.jl
Expand Up @@ -42,12 +42,39 @@ function (sp::StandardPrerender)()
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)
end
# Disable depth write for transparent objects
glDepthMask(sp.transparency[] ? GL_FALSE : GL_TRUE)

# Disable cullface for now, untill all rendering code is corrected!
glDisable(GL_CULL_FACE)
# glCullFace(GL_BACK)
enabletransparency()

# # Disable depth write for transparent objects
# glDepthMask(sp.transparency[] ? GL_FALSE : GL_TRUE)
# enabletransparency()

if sp.transparency[]
sp.overdraw[] && @warn("overdraw may break transparency?")

glDepthMask(GL_FALSE)

# Blending
glEnable(GL_BLEND)
glBlendEquation(GL_FUNC_ADD)

# 0 contains weight * color.rgba, should do sum
# destination <- 1 * source + 1 * destination
glBlendFunci(0, GL_ONE, GL_ONE)

# 1 is objectid, do nothing
glDisablei(1, GL_BLEND)

# 2 is color.a, should do product
# destination <- 0 * source + (1 - source) * destination
glBlendFunci(2, GL_ZERO, GL_ONE_MINUS_SRC_COLOR)

else
glDepthMask(GL_TRUE)
glDisable(GL_BLEND)
end
end

struct StandardPostrender
Expand Down
5 changes: 4 additions & 1 deletion GLMakie/src/GLVisualize/visualize/mesh.jl
Expand Up @@ -12,8 +12,11 @@ function _default(mesh::TOrSignal{M}, s::Style, data::Dict) where M <: GeometryB
color_norm = nothing
fetch_pixel = false
uv_scale = Vec2f(1)
transparency = false
shader = GLVisualizeShader(
"fragment_output.frag", "util.vert", "standard.vert", "standard.frag",
# transparency ? "transparent_fragment_output.frag" : "fragment_output.frag",
"transparent_fragment_output.frag",
"util.vert", "standard.vert", "standard.frag",
view = Dict("light_calc" => light_calc(shading))
)
end
Expand Down
4 changes: 3 additions & 1 deletion GLMakie/src/GLVisualize/visualize/particles.jl
Expand Up @@ -105,8 +105,10 @@ function meshparticle(p, s, data)
instances = const_lift(length, position)
shading = true
backlight = 0f0
transparency = false
shader = GLVisualizeShader(
"util.vert", "particles.vert", "fragment_output.frag", "standard.frag",
transparency[] ? "transparent_fragment_output.frag" : "fragment_output.frag",
"util.vert", "particles.vert", "standard.frag",
view = Dict(
"position_calc" => position_calc(position, position_x, position_y, position_z, TextureBuffer),
"light_calc" => light_calc(shading)
Expand Down
81 changes: 60 additions & 21 deletions GLMakie/src/glwindow.jl
Expand Up @@ -11,8 +11,9 @@ end

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
Comment on lines 12 to 19
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.

Expand All @@ -25,50 +26,88 @@ function attach_framebuffer(t::Texture{T, 2}, attachment) where T
glFramebufferTexture2D(GL_FRAMEBUFFER, attachment, GL_TEXTURE_2D, t.id, 0)
end

function GLFramebuffer(fb_size::NTuple{2, Int})
# First Framebuffer
render_framebuffer = glGenFramebuffers()
glBindFramebuffer(GL_FRAMEBUFFER, render_framebuffer)
# attach texture as color attachment with automatic id picking
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.


function GLFramebuffer(fb_size::NTuple{2, Int})
# Create framebuffer
frambuffer_id = glGenFramebuffers()
glBindFramebuffer(GL_FRAMEBUFFER, frambuffer_id)

# Buffers we always need
# Holds the image that eventually gets displayed
color_buffer = Texture(
RGBA{N0f8}, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge
)
# Holds a (plot id, element id) for point picking
objectid_buffer = Texture(
Vec{2, GLuint}, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge
)
# holds depth and stencil values
depth_buffer = Texture(
Ptr{GLAbstraction.DepthStencil_24_8}(C_NULL), fb_size,
minfilter = :nearest, x_repeat = :clamp_to_edge,
internalformat = GL_DEPTH24_STENCIL8,
format = GL_DEPTH_STENCIL
)
# Order Independent Transparency
HDR_color_buffer = Texture(
RGBA{Float16}, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge
)
OIT_weight_buffer = Texture(
N0f8, fb_size, minfilter = :nearest, x_repeat = :clamp_to_edge
)
# opaque is color, depth is depth, tonemapped_luma is luma?

attach_framebuffer(color_buffer, GL_COLOR_ATTACHMENT0)
attach_framebuffer(objectid_buffer, GL_COLOR_ATTACHMENT1)
attach_framebuffer(HDR_color_buffer, GL_COLOR_ATTACHMENT2)
attach_framebuffer(OIT_weight_buffer, GL_COLOR_ATTACHMENT3)
attach_framebuffer(depth_buffer, GL_DEPTH_ATTACHMENT)
attach_framebuffer(depth_buffer, GL_STENCIL_ATTACHMENT)

status = glCheckFramebufferStatus(GL_FRAMEBUFFER)
@assert status == GL_FRAMEBUFFER_COMPLETE


# Second Framebuffer
# postprocessor adds buffers here
color_luma_framebuffer = glGenFramebuffers()
glBindFramebuffer(GL_FRAMEBUFFER, color_luma_framebuffer)

@assert status == GL_FRAMEBUFFER_COMPLETE

glBindFramebuffer(GL_FRAMEBUFFER, 0)
fb_size_node = Observable(fb_size)

# To allow adding postprocessors in various combinations we need to keep
# track of the buffer ids that are already in use. We may also want to reuse
# buffers so we give them names for easy fetching.
buffer_ids = Dict(
:color => GL_COLOR_ATTACHMENT0,
:objectid => GL_COLOR_ATTACHMENT1,
:HDR_color => GL_COLOR_ATTACHMENT2,
:OIT_weight => GL_COLOR_ATTACHMENT3,
:depth => GL_DEPTH_ATTACHMENT,
:stencil => GL_STENCIL_ATTACHMENT,
)
buffers = Dict(
:color => color_buffer,
:color => color_buffer,
:objectid => objectid_buffer,
:depth => depth_buffer
:HDR_color => HDR_color_buffer,
:OIT_weight => OIT_weight_buffer,
:depth => depth_buffer,
:stencil => depth_buffer
)

return GLFramebuffer(
fb_size_node,
(render_framebuffer, color_luma_framebuffer),
buffers,
fb_size_node, frambuffer_id,
buffer_ids, buffers,
[GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1]
)
end
Expand Down
69 changes: 54 additions & 15 deletions GLMakie/src/postprocessing.jl
Expand Up @@ -30,6 +30,37 @@ function empty_postprocessor(args...; kwargs...)
end


function OIT_postprocessor(framebuffer)
# OIT setup
shader = LazyShader(
loadshader("postprocessing/fullscreen.vert"),
loadshader("postprocessing/OIT_blend.frag")
)
data = Dict{Symbol, Any}(
:opaque_color => framebuffer.buffers[:color],
:sum_color => framebuffer.buffers[:HDR_color],
:prod_alpha => framebuffer.buffers[:OIT_weight],
)
pass = RenderObject(data, shader, PostprocessPrerender(), nothing)
pass.postrenderfunction = () -> draw_fullscreen(pass.vertexarray.id)

color_id = framebuffer.buffer_ids[:color]
full_render = screen -> begin
fb = screen.framebuffer
w, h = size(fb)

# Blend transparent onto opaque
glDrawBuffer(color_id)
glViewport(0, 0, w, h)
glDisable(GL_STENCIL_TEST)
GLAbstraction.render(pass)
end

PostProcessor([pass], full_render)
end




function ssao_postprocessor(framebuffer)
# Add missing buffers
Expand All @@ -38,24 +69,26 @@ function ssao_postprocessor(framebuffer)
position_buffer = Texture(
Vec4f, size(framebuffer), minfilter = :nearest, x_repeat = :clamp_to_edge
)
attach_framebuffer(position_buffer, GL_COLOR_ATTACHMENT2)
pos_id = attach_framebuffer!(framebuffer, position_buffer)
push!(framebuffer.buffer_ids, :position => pos_id)
push!(framebuffer.buffers, :position => position_buffer)
end
if !haskey(framebuffer.buffers, :normal)
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[1])
normal_occlusion_buffer = Texture(
Vec4f, size(framebuffer), minfilter = :nearest, x_repeat = :clamp_to_edge
)
attach_framebuffer(normal_occlusion_buffer, GL_COLOR_ATTACHMENT3)
normal_occ_id = attach_framebuffer!(framebuffer, normal_occlusion_buffer)
push!(framebuffer.buffer_ids, :normal_occlusion => normal_occ_id)
push!(framebuffer.buffers, :normal_occlusion => normal_occlusion_buffer)
end

# Add buffers written in primary render (before postprocessing)
if !(GL_COLOR_ATTACHMENT2 in framebuffer.render_buffer_ids)
push!(framebuffer.render_buffer_ids, GL_COLOR_ATTACHMENT2)
if !(pos_id in framebuffer.render_buffer_ids)
push!(framebuffer.render_buffer_ids, pos_id)
end
if !(GL_COLOR_ATTACHMENT3 in framebuffer.render_buffer_ids)
push!(framebuffer.render_buffer_ids, GL_COLOR_ATTACHMENT3)
if !(normal_occ_id in framebuffer.render_buffer_ids)
push!(framebuffer.render_buffer_ids, normal_occ_id)
end

# SSAO setup
Expand Down Expand Up @@ -111,14 +144,14 @@ function ssao_postprocessor(framebuffer)
pass2.postrenderfunction = () -> draw_fullscreen(pass2.vertexarray.id)



color_id = framebuffer.buffer_ids[:color]
full_render = screen -> begin
fb = screen.framebuffer
w, h = size(fb)

# Setup rendering
# SSAO - calculate occlusion
glDrawBuffer(GL_COLOR_ATTACHMENT3) # occlusion buffer
glDrawBuffer(normal_occ_id) # occlusion buffer
glViewport(0, 0, w, h)
# glClearColor(1, 1, 1, 1) # 1 means no darkening
# glClear(GL_COLOR_BUFFER_BIT)
Expand All @@ -143,7 +176,7 @@ function ssao_postprocessor(framebuffer)


# SSAO - blur occlusion and apply to color
glDrawBuffer(GL_COLOR_ATTACHMENT0) # color buffer
glDrawBuffer(color_id) # color buffer
for (screenid, scene) in screen.screens
# Select the area of one leaf scene
isempty(scene.children) || continue
Expand All @@ -170,11 +203,15 @@ Returns a PostProcessor that handles fxaa.
function fxaa_postprocessor(framebuffer)
# Add missing buffers
if !haskey(framebuffer.buffers, :color_luma)
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[2])
# glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[2])
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[1])
color_luma_buffer = Texture(
RGBA{N0f8}, size(framebuffer), minfilter=:linear, x_repeat=:clamp_to_edge
)
attach_framebuffer(color_luma_buffer, GL_COLOR_ATTACHMENT0)
# attach_framebuffer(color_luma_buffer, GL_COLOR_ATTACHMENT0)
# push!(framebuffer.buffers, :color_luma => (GL_COLOR_ATTACHMENT0, color_luma_buffer))
luma_id = attach_framebuffer!(framebuffer, color_luma_buffer)
push!(framebuffer.buffer_ids, :color_luma => luma_id)
push!(framebuffer.buffers, :color_luma => color_luma_buffer)
end

Expand All @@ -201,22 +238,24 @@ function fxaa_postprocessor(framebuffer)
pass2 = RenderObject(data2, shader2, PostprocessPrerender(), nothing)
pass2.postrenderfunction = () -> draw_fullscreen(pass2.vertexarray.id)

color_id = framebuffer.buffer_ids[:color]
full_render = screen -> begin
fb = screen.framebuffer
w, h = size(fb)

# FXAA - calculate LUMA
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[2])
glDrawBuffer(GL_COLOR_ATTACHMENT0) # color_luma buffer
# glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[2])
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[1])
glDrawBuffer(luma_id) # color_luma buffer
glViewport(0, 0, w, h)
# necessary with negative SSAO bias...
glClearColor(1, 1, 1, 1)
glClear(GL_COLOR_BUFFER_BIT)
GLAbstraction.render(pass1)

# FXAA - perform anti-aliasing
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[1])
glDrawBuffer(GL_COLOR_ATTACHMENT0) # color buffer
# glBindFramebuffer(GL_FRAMEBUFFER, framebuffer.id[1])
glDrawBuffer(color_id) # color buffer
# glViewport(0, 0, w, h) # not necessary
GLAbstraction.render(pass2)
end
Expand Down