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

Examples: Add weighted, blended order-independent transparency pass / demo #24903

Open
wants to merge 9 commits into
base: dev
Choose a base branch
from

Conversation

stevinz
Copy link
Contributor

@stevinz stevinz commented Nov 2, 2022

Related issue: #9977

Description

This is an implementation of weighted, blended order independent transparency. It is designed as a Pass that can be used as a stand-alone renderer (similar to RenderPass), or in combination with other passes within an Effect Composer composition. It is based on the floating point buffer blending technique described in this paper and requested by this bounty.

This pull request is a result of the work in this GitHub Repo.

It was designed as a stand-alone renderer to allow room for other order-independent techniques to be implemented without complicating the renderer. And also to allow for it to be used in a larger rendering stack that may include pre-passes (i.e. background, skybox, etc.) and post-passes (outline, FXAA, etc.).

In keeping this as a stand-alone pass, this technique does not add shader code to the built-in materials. This pass includes a material (MeshWboitMaterial) that is functionally equivalent to MeshBasicMaterial, with the addition of a weight property. This is implemented in the style presented in MeshGouraudMaterial. The material is meant to serve as an example of the changes necessary to allow any material to be compatible with this pass. Users could easily add support to any material by monkey patching with onBeforeCompile.

The biggest disadvantage of not being in the renderer core is that the renderer will still sort transparent objects. This could be avoided with the addition of a way to overwrite / disable transparent object sorting (#24809, #19305). This pass keeps it's own list of opaque / transparent objects and renders them separately by implementing a visibility cache (similar to OutlinePass).

This pass doesn't need access to internal renderer framebuffers, and does not use a depth texture (instead it re-uses the depth buffer of the current render target). In doing so it remains WebGL 1 compatible and mobile friendly. Newer phones will fall back to gl.HALF_FLOAT with roughly the same visual quality of gl.FLOAT. Older phones that don't support the writing to gl.FLOAT framebuffers will fall back to gl.UNSIGNED_BYTE, which works but doesn't look as nice.

Live link:
https://raw.githack.com/stevinz/three.js/wboit/examples/index.html?q=wboit#webgl_transparency_wboit

@donmccurdy
Copy link
Collaborator

A few comments — but perhaps hold off on implementing my suggested changes until others comment, they may disagree!

  1. In three.js we typically use all-caps for abbreviations. See THREE.Mesh vs. Three.Mesh, GLTFLoader vs. GltfLoader, etc. I think MeshWboitMaterial should probably match that convention? I might suggest just MeshOITMaterial, as it seems unlikely we'd implement multiple OIT methods. Or perhaps MeshBasicOITMaterial.

  2. If you'll be maintaining the feature in a separate repository, perhaps it's best to follow the example of Examples: Add external three-gpu-pathtracer example #24803 and make an "external" example (linking to dependencies on unpkg) without pulling dependencies into this repository?

@donmccurdy
Copy link
Collaborator

donmccurdy commented Nov 3, 2022

Older phones that don't support the writing to gl.FLOAT framebuffers will fall back to gl.UNSIGNED_BYTE, which works but doesn't look as nice.

Unfortunately this problem is more than aesthetic, at least for PBR workflows — pixel values become clamped at [0,1], significantly limiting dynamic range. Most tonemapping techniques will be useless in this situation, and physically based lighting will be difficult. Not a problem that is specific to this OIT implementation, but to post-processing on devices without f16 or f32 framebuffer write support.

/* Stevinz, Adjustable Weight */
float scaleWeight = 0.7 + ( 0.3 * weight );
float w = clamp( pow( ( accum.a * 8.0 + 0.001 ) * ( - z * scaleWeight + 1.0 ), 3.0 ) * 1000.0, 0.001, 300.0 );
gl_FragColor = vec4( accum.rgb, accum.a ) * w;
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't vec4( accum.rgm, accum.a ) just accum?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, good catch. Leftover from before I accounted for the PREMULTIPLIED_ALPHA flag. I have simplified that section of the shader code.

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.

3 participants