icosa-mirror / UnityGLTF

Runtime glTF 2.0 Loader for Unity3D
MIT License
0 stars 0 forks source link

How to handle additive transparency #1

Open andybak opened 7 months ago

andybak commented 7 months ago

The lack of support for additive blending has been discussed on the main glTF repo:

https://github.com/KhronosGroup/glTF/issues/1189

and in the broader context of blending modes here:

https://github.com/KhronosGroup/glTF/pull/1302

Unreal's glTF export plugins and their web viewer use an extension called EPIC_blend_modes but I can't find any documentation on it beyond the implementation here: https://github.com/ue4plugins/GLTFWebViewer

So we have two potential extensions - one documented with no implementation and the other with a single undocumented implementation.

I'm also wondering if regular PBR emissive could somehow be used to partially fake additive blending.

I'm surprised at the lack of interest in additive blending considering it's a fairly standard feature in most realtime and non-realtime engines and renderers and is a common technique to implement light beams, sparks, flames etc.

It is also useful as for avoiding issues with sorting of transparent objects. Additive blending is inherently order independent. This is one of the reasons that Open Brush uses is so extensively (z-sorting of brush strokes is especially problematic)

andybak commented 7 months ago

/cc @hybridherbst @donmccurdy

Just keeping a braindump here so I don't clutter up the issue tracker on UnityGLTF

I'm tempted to attempt to implement KHR_blend as documented as an import/export plugin for UnityGLTF and use the new Open Brush export implementation as a test case. I'm not sure how to move https://github.com/KhronosGroup/glTF/pull/1302 forward or whether it really matters. I'm more of a "rough consensus and running code" kind of guy and the concept of "unofficial but widely supported extensions" seems to be the way things are going with glTF.

hybridherbst commented 7 months ago

Wasn't aware of this proposal, thanks for the ping!

@pfcDorn can you take a look? I think with the new plugin infrastructure it totally makes sense to support this in UnityGLTF, and if someone doesn't want it they just turn the plugin off.

@andybak we may want to rename the extension to EXT_blend if there is no plan at Khronos to ratify KHR_blend.

(Regarding implementations, Needle Engine does indeed support additive blending in glTF as part of a bigger NEEDLE_* extension but not in a standardization-happy format.)

andybak commented 7 months ago

@andybak we may want to rename the extension to EXT_blend

Yep. If we're going back to the drawing board I would consider some other changes. Is the current proposal too specific to how glsl/hlsl define blending ops? Is it too flexible and therefore complex?

How would an importer for something like Blender work? I'm no Blender expert and I struggled to define an additive material at all using a Cycles material node graph.

I'd be happy with an extension that just handled additive alone. I think the "additive vs alpha/multiplicative vs stencil" distinction accounts for 99% of use cases I see in the real world and additive is the only one of the three that is currently absent from glTF.

andybak commented 7 months ago

I checked Shader Graph and it supports four transparent blending modes. They aren't documented but they are the same four that URP supports in the lit shader:

https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@14.0/manual/lit-shader.html

  1. Alpha
  2. Premultiply
  3. Additive
  4. Multiply

For an opaque shader you additionally get alpha clipping which is arguably a 5th mode. I'm still personally only really interested in additive but I wonder if adding Multiply and Premultiply would be a good baseline set (given that alpha and alpha clipping are already supported)

donmccurdy commented 7 months ago

In hindsight, I agree that my KHR_blend proposal was too heavily influenced by OpenGL and WebGL APIs. I don't feel strongly one way or the other about whether it was too flexible or complex. If full flexibility were preferred, the WebGPU API for blending might be good inspiration:

https://www.w3.org/TR/webgpu/#blend-state

Also, better to have string enums than integer GL constants.

andybak commented 7 months ago

In an ideal world this would simply be another potential value for alphaMode property: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#alpha-coverage

If we want to ease the path towards standardization Maybe the extension should simply be an "extended alphaMode" - one that follows the same syntax and is used in preference for any client that supports the extension.

In which case I'd be tempted to name the extension something like EXT_advanced_alphaMode

hybridherbst commented 7 months ago

If it's just another allowed value on that then some existing materials wouldn't translate over, for example "mesh has alpha cutout at 0.5 and is additively blended".

andybak commented 7 months ago

If it's just another allowed value on that then some existing materials wouldn't translate over, for example "mesh has alpha cutout at 0.5 and is additively blended".

My first thought is that setting any values on EXT_advanced_alphaMode would indicate that the "legacy" alphaMode, and alphaCutoff should be ignored.

Is this consistent with how other extensions interact with existing properties?

donmccurdy commented 7 months ago

I tend to view this as a two-part problem:

  1. alphaMode = "BLEND" and alphaMode = "MASK" should not be mutually exclusive. This could be fixed with a new enum value like alphaMode = "BLEND_AND_MASK" for backward compatibility. Perhaps not required for this extension. Other enum values could be added someday, like alphaMode = "HASHED"
  2. New blend operations should added by some additional property, like alphaBlendOperation: "add", which has a default value equivalent to the current alpha blending.
andybak commented 7 months ago

The existing alphaMode supports:

EPIC_blend_modes has the following valid values:

https://docs.unrealengine.com/5.1/en-US/how-the-gltf-exporter-handles-unreal-engine-content/#blendmodesupport

and their behavior is described here: https://docs.unrealengine.com/5.1/en-US/material-blend-modes-in-unreal-engine/

As mentioned previously Unity Shader Graph has:

This matches the Unreal list perfectly from my brief reading. (I listed them in the same order above so you can see the matches)

three.js is a bit different and closer to @donmccurdy 's suggestion in that alpha clipping and other blending modes are not mutually exclusive. I don't know unreal very well but this is doable in Unity. Alpha clipping would have to be done manually in non-opaque shaders.

three.js doesn't have premultiplied blend but it does have subtractive blend. I'm personally in favour of leaving both out of this extension and going with the common subset.

I'm still not clear how Blender handles this. It used to have additive as a simple setting but it was removed. I also suspect Cycles and Eevee might behave differently - I only looked at Cycles.

I know Blender isn't the only non-realtime 3d app to consider but it's the one I have handy so I'm using it as a proxy for others.

andybak commented 7 months ago

This seems to be the best Blender equivalent: https://developer.blender.org/docs/images/Eevee2.81_transparent_bsdf2.png

The emission strength needs to probably be higher than 1.0 to match typical the look of typical glsl additive.

EDIT fix link because Blender website doesn't update url when viewing an image. Grumble...

andybak commented 4 months ago

Thinking about how interactions with the existing alphaMode would work:

Opaque
Mask
Blend

The "common subset" discussed above reduces to:

Opaque
Opaque / alpha clipping    
Transparent / Alpha
Transparent / Additive Blending
Transparent / Multiplicative Blending

Three of these map perfectly to existing alphaModes:

Opaque (Opaque)
Opaque / alpha clipping (Mask)
Transparent / Alpha (Blend)

Which leaves:

Transparent / Additive Blending
Transparent / Multiplicative Blending

Simply adding a new alphaMode property that has possible values of add/multiply would give us these two.

@donmccurdy said:

If it's just another allowed value on that then some existing materials wouldn't translate over, for example "mesh has alpha cutout at 0.5 and is additively blended".

Not sure what "existing materials" you're referring to? I'm assuming we'd doing "SrcAlpha One" in GL terms so the existing alpha is taking into account. You can't specify a hard cutoff but you can do alpha-masking. Is "alpha clip plus additive" used much?

I just did a survey of Open Brush shaders (the ones used in brushes which is the reason we want additive blending) and it's actually fairly complex.

On the whole we're doing Blend One One but there's a few cases of Blend SrcAlpha One - I need to take a look to see how much difference this makes (we can probably modify the textures on export to remove the distinction between these two cases).

However we're also using an even mix of BlendOp Add, Min with the default (BlendOp Add, Add) and just for fun the mobile variants of two shaders use BlendOp Max, Min which I presume is a performance optimisation...