NavigationInfo { blendingSort "2D" }
Blending is the technique to render partially transparent objects with real-time graphics. Partially transparent objects mean that the algorithm accounts for transparency values between 0.0
and 1.0
, e.g. 0.25
or 0.5
.
If you have multiple partially transparent objects in your game, set the Viewport.Items.BlendingSort
to:
bs2D
for 2D games.
bs3D
for 3D games. Eventually try bs3DOrigin
or bs3DGround
.
You can set it in the editor.
If you leave it at default bsNone
value, you may experience rendering artifacts: When multiple partially transparent objects are visible at the same screen pixel, things that should be obscured could be rendered instead in front of others.
Read on to understand it better — how do we render partially transparent objects, what is sorting, what and why can go wrong.
Transparency is a value in 0..1 range, where 0 means that object is opaque, and 1 means that object is completely transparent (invisible).
The X3D material nodes have a Transparency
property, TPhysicalMaterialNode.Transparency
, TMaterialNode.Transparency
, TUnlitMaterialNode.Transparency
.
Alpha is "opaqueness", it is defined simply as "1.0 - transparency". So alpha = 0.0 is something "completely invisible", alpha = 1.0 is "something totally opaque".
When you provide an RGBA color (TCastleColor
, which is just equal to TVector4
) then the last (4th) component is alpha.
When you provide an RGBA texture, then it includes an alpha channel.
When you provide a texture without alpha channel (like RGB or grayscale) then it is always treated like alpha is 1.0 everywhere, i.e. it is fully opaque.
The engine can handle transparency at each Shape
in 3 ways:
No handling of transparency, i.e. the object is opaque.
Handle transparency using alpha-testing, which means that at each pixel, we test the shape RGBA color (which is a result of mixing material color, per-vertex colors, and texture colors). If the shape color’s alpha is > 0.5 then the pixel is rendered (as if the shape was opaque at this pixel). Otherwise the pixel is not rendered (as if the shape was completely invisible at this pixel).
While alpha-testing is a less capable algorithm than blending (as alpha-testing cannot account for partial transparency), it also doesn’t have various problems unavoidable with blending, and mentioned on this page. E.g. alpha-testing works without any problems with shadow maps.
Finally, engine can handle transparency using blending. This means that all partially-transparent shapes are rendered after all other shapes (that are opaque or use alpha-testing). Moreover,
The partially-transparent shapes are rendered with Z-testing but without Z-writing. This means that partially-transparent shapes that are behind opaque objects are correctly hidden, but partially-transparent shapes in front of all opaque objects are always considered visible (even if they are behind other transparent objects). This matches reality, as partially-transparent shapes never fully "obscure" stuff behind them, by definition.
When the partially-transparent pixel is rendered, it is mixed with the existing screen color using the "blending equation". By default is it screen_color.rgb = incoming_color.rgb * incoming_color.a + screen_color.rgb * (1 - incoming_color.a)
. This equation can be configured using Scene.RenderOptions.BlendingSourceFactor
, Scene.RenderOptions.BlendingDestinationFactor
and can be overridden per-shape using Appearance.blendMode.
The partially-transparent shapes are rendered in back-to-front order (by default, when Scene.RenderOptions.BlendingSort
is bs3D
and it is not overridden by TNavigationInfoNode.BlendingSort
in the loaded model). That is because the default blending equation (see above) assumes such order. Some other blending equations do not require sorting, and thus Scene.RenderOptions.BlendingSort
may be bsNone
, but they look less realistic.
The "ordering shapes" stage means that each shape is treated as a whole. We sort shape using "distance to the middle of the 3D bounding box" in case of BlendingSort = bs3D
. The possible problem: in some cases, shapes may be concave and intertwined in various crazy ways in 3D. It is not possible to strictly say "X is in front of Y" in general for two shapes, because they may be sometimes in front, sometimes behind each other, at each pixel of the screen. So using blending requires that partially-transparent shapes stay simple, preferably convex and not mixed with each other at the same distance from camera.
Engine makes the decision about blending per-shape.
By default (if TAppearanceNode.AlphaChannel
is acAuto
) the engine looks at material transparency
field, and the texture’s alpha channel. If transparency
is > 0, or if the texture has non-trivial alpha channel (this is detected by analyzing alpha contents, see TEncodedImage.AlphaChannel
description), then we use blending.
Note that this auto-detection cannot be perfect in all cases. For example, the alpha channel detection at the image (TEncodedImage.AlphaChannel
) is a heuristic, with some alpha tolerance. And what happens when multiple textures are used, with different alpha channel? Again, the engine assumes something reasonable, but it may not be what you want. Also, if you use GLSL shader code to set/modify alpha value, then the engine doesn’t know about it (in general, engine never parses your GLSL code).
You can explicitly override this auto-detection using TAppearanceNode.AlphaChannel
field. This makes sense when engine doesn’t do what you expect.
It can be set by Pascal code.
Or, when using X3D model, you can use Apperance.alphaChannel documentation).
Or when using glTF. In glTF, it is always explicitly set, glTF format requires it. So the auto-detection is not used for glTF, the imported shapes always have Appearance.alphaChannel <> acNone
. You can set the alpha treatment explicitly in Blender material.
TCastleScene
(MyScene.RenderOptions.BlendingSort
, default is to sort for 3D)As mentioned above, this is controlled by Scene.RenderOptions.BlendingSort
which is bs3D
by default, and can be overridden by TNavigationInfoNode.BlendingSort in the loaded model.
You can set Scene.RenderOptions.BlendingSort
to bs2D
for 2D models. This makes their sorting faster. When importing Spine models, they automatically have NavigationInfo.blendingSort = "2D"
so they automatically use this sorting.
You can set Scene.RenderOptions.BlendingSort
to bsNone
. This makes rendering faster, but assumes that you will never have more than one partially-transparent shapes visible in front of an opaque shape, or that your blending equation makes the order irrelevant.
You can also override sorting inside the X3D model. E.g. add this to force 2D sorting in X3D classic encoding:
NavigationInfo { blendingSort "2D" }
To request correct blending sorting in 2D, you should set MyScene.RenderOptions.BlendingSort := bs2D
(or call MyScene.Setup2D
which is just a shortcut for it). Note that you actually don’t need to do this when loading Spine (where we include proper NavigationInfo
inside model) or for sprite sheets and images (where there is only 1 layer, so blending sorting doesn’t matter). So actually you only need to care about this when you use "general (3D or 2D)" model format for 2D animation, e.g. you use glTF or X3D to define a 2D animation with layers.
MyViewport.Items.BlendingSort
, default none)By default the engine does not sort the scenes. You need to request the sorting explicitly.
It is easiest to just set Viewport.Items.BlendingSort
to something else than bsNone
. We have a few algorithms, some better for 2D some for 3D:
bs2D
- Sort along the Z axis.
bs3D
- Sort based on the distance from current camera, looking at bounding box.
bs3DOrigin
- Sort based on the distance from current camera, looking at origin point (point (0,0,0) in local coordinates). This may be better than bs3D
in some cases because the origin point is sometimes more "persistent", e.g. animations or billboard orientation may change bounding box, but not origin point.
bs3DGround
- Sort based on the distance from current camera, looking at origin point (point (0,0,0) in local coordinates) projected on Y=0 plane. This may be better than bs3DOrigin
in some cases because it ignores the object height for the sorting purposes.
Note
|
The current implementation of Viewport.Items.BlendingSort mechanism has some flaws. Namely, it only sorts whole scenes (not shapes) and it only sorts non-recursively the direct children of Viewport.Items.BlendingSort . It is planned to be improved, see roadmap.
|
An alternative to using Viewport.Items.BlendingSort
is to sort manually by an explicit call to a method Viewport.Items.SortBackToFront2D
or more general Viewport.Items.SortBackToFront
.
You can also do it at design-time, from the CGE editor, using the "Viewport → Sort Items For Correct 2D Blending" menu item.
The drawback is that you have to do it often enough.
In case of 2D, you should call it always after:
adding a partially-transparent object,
or moving partially-transparent object in the Z (depth) axis.
Luckily, in 2D, the camera position doesn’t affect the bs2D
sorting.
In 3D it is more difficult, and in general you should just sort every frame, which means that it will be easier to just rely on automatic Viewport.Items.BlendingSort
documented above.
If you draw using DrawPrimitive2D
, DrawRectangle
etc. — they automatically use blending when provided color has alpha < 1. They take blending factors (that determine the "blending equation" mentioned above) as explicit arguments.
If you draw using TDrawableImage
then it automatically determines alpha treatment looking at image contents and the TDrawableImage.Color
. You can override alpha treatment by TDrawableImage.Alpha
property, there are also properties to determine blending equation: TDrawableImage.BlendingSourceFactor
, TDrawableImage.BlendingDestinationFactor
.
The above routines are used by user interface rendering, e.g. by TCastleButton
or TCastleImageControl
rendering, so they follow the same alpha treatment. The TCastleImageControl.AlphaChannel
allows to control blending in case of TCastleImageControl
, underneath it uses TDrawabbleImage
to render.
To improve this documentation just edit the source of this page in AsciiDoctor (simple wiki-like syntax) and create a pull request to Castle Game Engine WWW (cge-www) repository.
Copyright Michalis Kamburelis and Castle Game Engine Contributors.
This webpage is also open-source and we welcome pull requests to improve it.
We use cookies for analytics. See our privacy policy.