Last active
May 10, 2019 21:32
-
-
Save BeRo1985/80fbcdf16a9809c84c7b to your computer and use it in GitHub Desktop.
Hybrid atomic loop weighted blended order independent transparency implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Hybrid atomic loop weighted blended order independent transparency implementation (work in progress) | |
// Copyright (C) 2014 by Benjamin 'BeRo' Rosseaux | |
// Licensed under the CC0 license, since in the German legislation exists no public | |
// domain. | |
// This implementation needs at least OpenGL 4.3 as minimum OpenGL version, due to my usage of shader storage buffer objects here | |
// Supports also additive blending for emission color portions | |
uniform sampler2D uTexTailWeightedBlendedOrderIndependentTransparencyColor; | |
uniform sampler2D uTexTailWeightedBlendedOrderIndependentTransparencyAlpha; | |
uniform int uOrderIndependentTransparencyWidth; | |
uniform int uOrderIndependentTransparencyLayers; | |
layout(std430) coherent buffer orderIndependentTransparencyDepthBufferData { | |
uint orderIndependentTransparencyDepthBuffer[]; | |
}; | |
layout(std430) buffer orderIndependentTransparencyColorBufferData { | |
uvec4 orderIndependentTransparencyColorBuffer[]; // Two packed RGBA16F values: xy = alpha blended color and zw = emission color | |
}; | |
int oitGetCoordBaseBufferIndex(){ | |
return ((int(gl_FragCoord.y) * uOrderIndependentTransparencyWidth) + int(gl_FragCoord.x)) * uOrderIndependentTransparencyLayers; | |
} | |
// For one-way clearing at after depth buffer creation, when it shouldn't happen for whatever reason on the CPU itself | |
void oitClear(){ | |
int lBufferIndex = oitGetCoordBaseBufferIndex(); | |
for(int lLayerIndex = 0; lLayerIndex < uOrderIndependentTransparencyLayers; lLayerIndex++){ | |
orderIndependentTransparencyDepthBuffer[lBufferIndex++] = 0xffffffffu; | |
} | |
} | |
// Optional: Pass "zero" with alpha test against alpha value 1.0 for better weighted blended order independent transparency portion results | |
// First pass for finding K closest depth values | |
void oitPassOne(){ | |
uint lFragDepth = floatBitsToUint(gl_FragCoord.z); | |
int lBufferIndex = oitGetCoordBaseBufferIndex(); | |
for(int lLayerIndex = 0; lLayerIndex < uOrderIndependentTransparencyLayers; lLayerIndex++){ | |
uint lOldDepth = atomicMin(orderIndependentTransparencyDepthBuffer[lBufferIndex++], lFragDepth); | |
if((lOldDepth == 0xffffffffu) || (lOldDepth == lFragDepth)){ | |
break; | |
} | |
lFragDepth = max(lOldDepth, lFragDepth); | |
} | |
} | |
// Second pass with fragment color recording and tail weighted blended order independent transparency blending | |
void oitPassTwo(const in vec4 pAlphaBlendingColor, const in vec4 pEmissionColor, const in float pViewSpaceZ, out vec4 pTailWeightedBlendedOrderIndependentTransparencyColor, out float pTailWeightedBlendedOrderIndependentTransparencyAlpha){ | |
uint lFragDepth = floatBitsToUint(gl_FragCoord.z); | |
int lBufferIndex = oitGetCoordBaseBufferIndex(); | |
// Binary search (the followed code could be more better optimized, when it is needed) | |
int lLow = 0; | |
int lHigh = uOrderIndependentTransparencyLayers - 1; | |
while(lLow < lHigh){ | |
int lMiddle = lLow + ((lHigh - lLow) >> 1); | |
uint lTestDepth = orderIndependentTransparencyDepthBuffer[lBufferIndex + lMiddle]; | |
if(lFragDepth > lTestDepth){ | |
lLow = lMiddle + 1; | |
}else if(lFragDepth < lTestDepth){ | |
lHigh = lMiddle - 1; | |
}else{ | |
lLow = -1; | |
orderIndependentTransparencyColorBuffer[lBufferIndex + lMiddle] = uvec4(uvec2(packHalf2x16(pAlphaBlendingColor.rg), packHalf2x16(pAlphaBlendingColor.ba)), uvec2(packHalf2x16(pEmissionColor.rg), packHalf2x16(pEmissionColor.ba))); | |
break; | |
} | |
} | |
// Tail weighted blended order independent transparency blending | |
if(lLow < 0){ | |
// Fragment was recorded, so we must do no tail weighted blended order independent transparency blending here then | |
pTailWeightedBlendedOrderIndependentTransparencyColor = vec4(0.0); | |
pTailWeightedBlendedOrderIndependentTransparencyAlpha = 0.0; | |
}else{ | |
// Fragment was not recorded, because it is behind the recorded last layer depth, so we must do tail weighted blended order independent transparency blending here then | |
if(dot(pAlphaBlendingColor, pAlphaBlendingColor) > 0.0){ | |
// Alpha blending if alpha value is greater or equal zero (positive) | |
float pTailWeightedBlendedOrderIndependentTransparencyWeight = max(min(1.0, max(max(pAlphaBlendingColor.r, pAlphaBlendingColor.g), pAlphaBlendingColor.b) * pAlphaBlendingColor.a), pAlphaBlendingColor.a) * clamp(0.03 / (1e-5 + pow(pViewSpaceZ / 4096.0, 4.0)), 1e-2, 3e3); | |
pTailWeightedBlendedOrderIndependentTransparencyColor = vec4(pAlphaBlendingColor.rgb * pAlphaBlendingColor.a, pAlphaBlendingColor.a) * pTailWeightedBlendedOrderIndependentTransparencyWeight; | |
pTailWeightedBlendedOrderIndependentTransparencyAlpha = pAlphaBlendingColor.a; | |
} | |
if(dot(pEmissionColor, pEmissionColor) > 0.0){ | |
// Fake incorrect but in the most cases still acceptable additive-like blending with weighted blended order independent transparency, if alpha value is less than zero (negative) | |
const float pTailWeightedBlendedOrderIndependentTransparencyAdditiveBlendingRescaleWeight = 1e-3; // This value must not be zero, because the additive blending like effect is disappearing then, | |
// but also not too high, since it is used also as the alpha value for the blending itself. | |
// The base resolve step will do the following then: | |
// (firstTexel.rgb / firstTexel.a) * secondTexel.r | |
// where firstTexel.a and secondTexel.r are this one rescale weight value | |
pTailWeightedBlendedOrderIndependentTransparencyColor = vec4(pEmissionColor.rgb, pTailWeightedBlendedOrderIndependentTransparencyAdditiveBlendingRescaleWeight); | |
pTailWeightedBlendedOrderIndependentTransparencyAlpha = pTailWeightedBlendedOrderIndependentTransparencyAdditiveBlendingRescaleWeight; | |
} | |
} | |
} | |
// Resolve the recorded and collected stuff | |
// Needs active blending with GL_ONE, GL_ONE_MINUS_SRC_ALPHA => dstcolor = srccolor + (dstcolor * (1.0 - srcalpha)) | |
// since the front-to-back layer has rendered over black, so it is pre-multiplied by its alpha value | |
vec4 oitResolve(){ | |
ivec2 lFragCoord = ivec2(gl_FragCoord.xy); | |
// 1. Clear to translucent black (0.0, 0.0, 0.0, 0.0) | |
// The black color meaning nothing to add color-wise, and the transparent alpha value of 0.0 meaning that the background won't be occluded | |
vec4 lOutputColor = vec4(0.0); | |
// 2. Resolve recorded fragments per front-to-back blending with the UNDER blending operator | |
int lBufferIndex = oitGetCoordBaseBufferIndex(); | |
for(int lLayerIndex = 0; lLayerIndex < uOrderIndependentTransparencyLayers; lLayerIndex++){ | |
// Get array cell value and clear array cell at the same time | |
uint lDepth = atomicExchange(orderIndependentTransparencyDepthBuffer[lBufferIndex], 0xffffffffu); | |
if(lDepth == 0xffffffffu){ | |
break; | |
} | |
uvec4 lBufferPackedColor = orderIndependentTransparencyColorBuffer[lBufferIndex++]; | |
vec4 lBufferAlphaBlendingColor = vec4(unpackHalf2x16(lBufferPackedColor.x), unpackHalf2x16(lBufferPackedColor.y)); | |
vec4 lBufferEmissionColor = vec4(unpackHalf2x16(lBufferPackedColor.z), unpackHalf2x16(lBufferPackedColor.w)); | |
// Emulating GL_ONE_MINUS_DST_ALPHA, GL_ONE => dstcolor += srccolor * (1.0 - dstalpha) | |
lOutputColor += (vec4(lBufferAlphaBlendingColor.rgb * lBufferAlphaBlendingColor.a, lBufferAlphaBlendingColor.a) + lBufferEmissionColor) * (1.0 - lOutputColor.a); | |
} | |
// 3. Blend the weighted blended order independent transparency portion top on the opaque color layer | |
{ | |
vec4 lTailWeightedBlendedOrderIndependentTransparencyColor = texelFetch(uTexTailWeightedBlendedOrderIndependentTransparencyColor, lFragCoord, 0); | |
float lTailWeightedBlendedOrderIndependentTransparencyAlpha = texelFetch(uTexTailWeightedBlendedOrderIndependentTransparencyAlpha, lFragCoord, 0).x; | |
lTailWeightedBlendedOrderIndependentTransparencyColor = vec4((lTailWeightedBlendedOrderIndependentTransparencyColor.rgb / max(lTailWeightedBlendedOrderIndependentTransparencyColor.a, 1e-5)), lTailWeightedBlendedOrderIndependentTransparencyAlpha); | |
// Compositing by emulating GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA => dstcolor = (srccolor * (1.0 - srcalpha)) + (dstcolor * srcalpha) | |
// inverted for front-to-back compositing GL_ONE_MINUS_SRC_ALPHA * GL_ONE_MINUS_DST_ALPHA, GL_ONE => dstcolor += (srccolor * (1.0 - srcalpha)) * (1.0 - dstalpha) | |
// TO-DO: Check if it is correct in this way | |
lOutputColor += (lTailWeightedBlendedOrderIndependentTransparencyColor * (1.0 - lTailWeightedBlendedOrderIndependentTransparencyColor.a)) * (1.0 - lOutputColor.a); | |
} | |
// Return the final color | |
return lOutputColor; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment