Rendering semi-transparent objects in FireMonkey

The question has (predictably) popped up several times now, so here is a recapitulative post with workaround.

FireMonkey (as of now) doesn’t support rendering semi-transparent objects in 3D.

FireMonkey only supports blending of semi-transparent objects (either through the Opacity property or because of their texture, for instance a semi-transparent PNG image), but blending alone is not enough to get it right in 3D with a Z-Buffer (which is what FMX, and most 3D apps are using).

For a technical explanation, you can read about Transparency sorting, the article is about OpenGL, but applies to DirectX too.

A solution (not always quick or simple) is to manually sort the objects back to front, ie. have the objects farthest from the camera be rendered first, using an approach similar to that posted by Peter Söderman:

You should be able to sort them in distance from camera order with something like this (a bit sloppy and you may have to tweak the sorting but it’s a start): [...]

To get access to the children list I do an override of the TViewport3D class.

type
  TMyViewPort = class(TViewport3D)
  end;

procedure TForm1.Button2Click(Sender: TObject);
var
  myv: TMyViewPort;
begin
  myv :=  TMyViewPort( Viewport3D1);

  myv.FChildren.SortList(
  function (i1,i2: Pointer): Integer
  var
    o1,o2: TControl3D;
  begin
    if TfmxObject(i1) is TControl3D then o1 := TControl3D(i1);
    if TfmxObject(i2) is TControl3D then o2 := TControl3D(i2);
    if (o1 <> nil) and (o2 <> nil) then
    begin
      Result := trunc(  VectorDistance2(myv.Camera.Position.Vector,o1.Position.Vector)
                      - VectorDistance2(myv.Camera.Position.Vector,o2.Position.Vector));
    end else
      Result := 0;
  end
  );
end;

You’ll have to call the above sorting every time the objects or camera position changes.

This is just a workaround

Ideally this should be handled by the scene-graph, as it is rendering-dependent, otherwise, as in the code above, you end up having to change the scene-graph structure, which can have various other side-effects, and is even more problematic if you have more than one camera looking at the same scene.

Another downside is that this approach will work with convex objects that don’t intersect, and for which you don’t have triple-overlap.

When intersection or triple overlap happens, there is no object that is fully closer or farther from the camera, and the simple sorting approach fails.

The sorting approach also won’t solve transparency issues that happen for a mesh with itself, for that and the overlap case, you need to involve more advanced techniques that FireMonkey currently doesn’t support, like sorting mesh-sub-element, using tessellation, depth peeling, BSP, etc.

To implement the more advanced techniques mentioned above in a reusable and user-friendly fashion, it would involve standardizing materials an textures, standardizing mesh structures, and generalizing the FMX scene graph, thus hitting the top three architectural weaknesses mentioned previously.