【问题标题】:Firemonkey semi-transparent Image3D is sometimes opaqueFiremonkey 半透明 Image3D 有时不透明
【发布时间】:2011-12-13 23:58:26
【问题描述】:

我创建了一个带有 3 个半透明 tImage3D 的 FireMonkey 应用程序。 这是代码和屏幕。一切似乎都很好。

procedure TForm1.Form3DCreate(Sender: TObject);

// create a new semi-transparent timage3d
// object with color and Z position.
procedure NewImage ( const nColor : tColor;
                     const nZ     : integer );
begin
  // create the image
  with tImage3D . Create ( self ) do
    begin
      // put it on the screen
      Parent := self;
      // set the size
      Width := 10;
      Height := 10;
      // set the image to a single pixel.
      Bitmap . Width := 1;
      Bitmap . Height := 1;
      // set the Alpha to $80 to make it
      // semi-transparent
      Bitmap . Pixels [ 0, 0 ] := $80000000 + nColor;
      // set the z position
      Position . Z := nZ;
    end;
end;

begin
  NewImage ( claRed,   +10 );
  NewImage ( claGreen,   0 );
  NewImage ( claBlue,  -10 );
end;

现在颠倒顺序。现在它们是不透明的。

begin
  NewImage ( claRed,   -10 );
  NewImage ( claGreen,   0 );
  NewImage ( claBlue,  +10 );
end;

我错过了什么?

【问题讨论】:

  • 显然,一个对象对于之前创建的对象只是半透明的。如果那有意义的话。红色框显示为粉红色,因为您可以通过它看到白色背景,但通过它看不到绿色或蓝色框。通过绿色框,你可以看到白色背景和红色框,但看不到蓝色框。 text3d 对象也发生了同样的事情。
  • 我认为@user 是对的。尝试重绘前面的对象。如果对象没有检测到它下面的任何东西,它可能会跳过混合功能,并且白色背景不算在内。
  • 重绘前面的对象是什么意思?这是一个非常简单的例子来说明问题。最初的应用程序有几个浮动的旋转图像和文本,每个对象的位置每 50 毫秒更改一次,因此会不断重绘。

标签: delphi firemonkey


【解决方案1】:

FireMonkey(截至目前)不支持在 3D 中渲染半透明对象。

FireMonkey 仅支持 混合 半透明对象(通过 Opacity 属性或由于它们的纹理,例如半透明 PNG 图像),但仅 blending 不足以使用Z-Buffer(这是 FMX 和大多数 3D 应用程序都使用的)在 3D 中正确处理。

有关技术解释,您可以阅读Transparency sorting,该文章是关于 OpenGL,但也适用于 DirectX。

因此,要获得正确的渲染,您需要将半透明对象从相机的角度从后到前排序

您可以在这篇文章中获得更多详细信息和一些代码来解决该问题:

Rendering semi-transparent object in FireMonkey

但请记住,这只是一种解决方法

理想情况下,这应该由 FireMonkey 场景图处理,因为它依赖于渲染,否则,您最终不得不更改场景图结构,这可能会产生各种其他副作用,而且问题更大如果您有多个摄像头在看同一个场景。

此外,排序方法仅适用于不相交且没有三重重叠的凸对象,如下所示:

不存在正确排序(没有一个元素在其他元素之前)。

【讨论】:

    【解决方案2】:

    正如您已经发现的那样,您必须从后向前绘制透明对象。

    绘制透明对象时,对象被绘制出来,并与它后面的像素混合。

    所以当您从后向前绘制时会发生这种情况:
    您绘制红色图像,它与白色背景混合。您可以通过“粉红色”而不是纯红色来判断它与白色背景混合。然后绘制绿色图像,它与已经绘制的白色背景和红色图像混合。最后绘制蓝色图像,它与已绘制的对象混合在一起。

    但现在我们从前向后绘制:
    我们先画红色平面。它与您可以看到的白色背景混合在一起,因为它是粉红色而不是红色。现在我们绘制绿色平面。它与白色背景融为一体,从颜色上可以看出,它不是纯的、深的、绿色的。但是,渲染器看到一个部分落在红色平面的后面,所以它不会绘制那个部分。但是,您认为:红色平面是透明的,渲染器应该在红色平面后面绘制!不,渲染器只跟踪 z 缓冲区/深度缓冲区中像素的深度/z 顺序,它不知道该像素是否透明。蓝色平面也是如此,只绘制未被其他物体遮挡的部分。

    你说的这个深度缓冲区是什么?
    在深度缓冲区中存储每个像素的深度。当您在 2,2 处绘制一个 z 为 1 的像素时,2,2 处的深度缓冲区将更新为值 1。现在,当您绘制一条从 1,2 到 3,2 且 z 为 3 的线时,渲染器只会绘制深度缓冲区值> = 3的像素。因此绘制像素1,2(并且1,2处的深度缓冲区设置为3)。未绘制像素 2,2,因为深度缓冲区表明该像素已经以较小的深度绘制(1 对 3)。像素 3,2 被绘制,3,2 处的深度缓冲区设置为 3。
    因此,深度缓冲区用于跟踪每个像素的 z 顺序,以防止用更远的像素覆盖该像素。

    如果您想以正确的方式绘制透明对象,请参阅this answer

    摘自该答案:

    • 首先绘制不透明的对象。
    • 禁用深度缓冲区写入(因此深度缓冲区不会更新),但保持启用深度缓冲区检查。
    • 绘制透明对象。因为深度缓冲区没有更新,所以没有透明对象相互遮挡的问题。由于启用了深度缓冲区检查,因此您不会在不透明对象后面绘制。

    不知道FireMonkey是否支持禁用depth-buffer writes,你得自己摸索。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-19
      • 2023-04-08
      • 2011-09-15
      • 1970-01-01
      相关资源
      最近更新 更多