如何在深度排序的半透明对象上避免这种损坏?

时间:2016-06-22 13:05:37

标签: processing

 
void setup() {
  size(600, 480, P3D);hint(ENABLE_DEPTH_SORT);
}

void draw()
{
  background(0); 
  translate(width/2, height/2);  fill(color(255,255,255),84);
  strokeWeight(0);
  translate(-40,0,1);sphere(80);
  translate(2*40,0,0);sphere(80);
  // Fails with corruption: http://i.imgur.com/GW2h7Qv.png
}

enter image description here

注意:sphereDetail不需要申请。 sphereDetail(60)导致失败而左侧重叠丢失:

enter image description here

1 个答案:

答案 0 :(得分:2)

要理解为什么会这样,你必须了解幕后发生的事情。

在三角形中思考

当你调用sphere()函数时,Processing实际上是在绘制一堆三角形。如果你运行这个简单的程序,你可以看到这个:

 
size(500, 500, P3D);
translate(width/2, height/2);
sphere(100);

在你的代码中,你选择不绘制轮廓,但Processing仍在绘制一堆三角形来绘制球体。以下是sphereDetail(10)

的内容

重点是,你需要停止看球体,并看到组成这些球体的三角形。

深度缓冲

OpenGL(以及因此处理)使用深度缓冲来确定何时来绘制其他内容。

我只是要复制this article的一部分,这比我能解释得更好:

  

考虑这个绘制两个三角形A和B的例子:

     

     

如果我们首先绘制B,然后是A,深度缓冲区将看到新的   来自A的像素比先前由B绘制的像素更接近,所以它   将它们画在顶部。如果我们以相反的顺序绘制(A   接下来是B)深度缓冲区将看到像素进来   来自B的距离远远超过已经由A绘制的那些,所以它会   丢弃它们。在任何一种情况下,我们都会得到正确的结果:A位于顶部,   B隐藏在它背后。

当你画一个不透明的球体时,你真的画了一堆不透明的三角形。处理将以球对三角算法生成它们的任何顺序绘制这些三角形。这适用于不透明对象,因为Processing将检查Z缓冲区,然后跳过已经绘制的三角形后面的三角形。这基本上和OpenGL一样聪明,开箱即用。

透明度问题

再次引用the article

  

但是,如果这个几何体是α混合的,那么B是部分可见的   通过半透明的三角形?如果我们绘制B,这仍然有效   首先,然后A超过顶部,但如果我们绘制A然后是B.   在这种情况下,深度缓冲区将从B获得一个像素,并注意到   它已经从A绘制了一个更接近的像素,但它没有办法处理   有了这种情况。它的唯一选择是绘制B像素(哪个   会给出错误的结果,因为它会混合更多   远处的B越过A的顶部,而alpha混合则没有   交换)或它可以完全丢弃B.不好!<​​/ p>

问题是,这对透明三角形不起作用。请记住,Processing正在以算法生成它们的顺序绘制三角形,并且它使用Z缓冲区来决定何时跳过已经绘制的三角形后面的三角形。但是我们不希望它跳过任何三角形!我们想绘制每个三角形,因为我们希望能够通过前三角形看到后三角形。

例如,如果我们在前面绘制一个透明的灰色球体,然后我们在它后面绘制一个红色球体,我们就不会看到红色球体。

size(500, 500, P3D);
translate(width/2, height/2);
noStroke();

//front gray sphere
fill(255, 255, 255, 64);
sphere(100);

//back red sphere
translate(0, 0, -500);
fill(255, 0, 0, 64);
sphere(100);

我在这里使用整个球体,因为我很懒,但想象一下每个球体中各个三角形的确切问题。这就是为什么你在灰色球体中看到这些瑕疵的原因,原因同样你看不到灰色球体后面的红色球体。 OpenGL首先绘制灰色球体,然后它跳过红色球体,因为它知道它在前球体后面。这是不透明对象的正确行为,但我们通过添加透明度使事情变得复杂。

图纸订单事项

如果我们在之前绘制背面红色球体,我们绘制前灰色球体,然后我们通过灰色球体看到红色球体:

size(500, 500, P3D);
translate(width/2, height/2);
noStroke();

//back red sphere
translate(0, 0, -500);
fill(255, 0, 0, 64);
sphere(100);

//front gray sphere
translate(0, 0, 500);
fill(255, 255, 255, 64);
sphere(100);

请注意,由于三角形顺序问题,我们仍然会看到每个球体内的工件。发生这种情况的原因与我们在灰色球体之后绘制时看不到红色球体的原因相同。

现在,要解决三角形的问题,绘图顺序很重要。正如您必须绘制前灰色球之后绘制后红色球体,您必须从后面到前面绘制三角形,这样OpenGL不会跳过任何那个它决定了已经绘制的三角形背后。这是一个巨大的痛苦。

处理救援

幸运的是,Processing可以选择为您完成此操作。这是hint(ENABLE_DEPTH_SORT)功能。启用此功能后,Processing会将每个三角形按其三个顶点的平均Z位置排序,然后按此顺序绘制它们。启用此功能后,我们可以在前灰色球体之后绘制后红色球体,然后Processing将为我们排序三角形并在到达OpenGL之前以正确的顺序绘制它们:

size(500, 500, P3D);
hint(ENABLE_DEPTH_SORT);

translate(width/2, height/2);
noStroke();

//front gray sphere
fill(255, 255, 255, 64);
sphere(100);

//back red sphere
translate(0, 0, -500);
fill(255, 0, 0, 64);
sphere(100);

请注意,我们可以看到后面的红色球体,即使我们正在前灰色球体之后绘制它。处理代表我们进行干预,并在实际在OpenGL中绘制三角形之前对三角形进行排序。这也修复了每个球体内的瑕疵,因为三角形在每个球体中排序,而不仅仅在球体之间。

交叉问题

请记住,三角形按其3个顶点的平均Z位置排序。 对于相交的三角形来说,这是一个问题。这是您问题的症结所在。

the article开始,假设您有两个像这样相交的三角形AB

  

     

没有办法对这些三角形进行排序,因为我们需要这样做   将B的顶部绘制在A上,但A的底部部分覆盖B.   唯一的解决方案是检测何时发生这种情况并拆分三角形   它们交叉的地方,但这将是非常昂贵的。

应该首先绘制哪个三角形?没有一个答案,但Processing会尽力而为,并根据其3个顶点的平均Z位置对它们进行排序。 这会导致您看到的文物。

我们可以在一个简单的交互式草图中看到这一点:

void setup() {
  size(500, 500, P3D);
  hint(ENABLE_DEPTH_SORT);
  noStroke();
}

void draw() {
  background(128);
  translate(width/2, height/2);

  //gray sphere
  fill(255, 255, 255, 64);
  sphere(100);

  //red sphere
  translate(0, 0, mouseY-width/2);
  fill(255, 0, 0, 64);
  sphere(100);
}

上下移动鼠标以向前或向后移动红色球体。当它与灰色球体相交时,您会得到伪影,因为三角形是相交的。

(忽略鼠标周围的文物,这是创建gif的结果。)

这是你所看到的文物的原因。

可能的解决方案

你能做些什么来修复它?你有几个选择:

选项1:停止使用透明度。您的许多问题都是由使用透明度引起的,这使事情变得非常复杂。最快和最简单的方法是停止使用透明度,因为OpenGL最适合使用不透明对象。

选项2:停止使用相交的形状。您可以将交叉球体视为impossible objects,OpenGL无法很好地处理。你能简单地移动你的形状,使它们不相交吗?如果您正在尝试构建复杂的形状,可以使用3D模型或使用vertex()函数自行构建吗?

选项3:提出自己的排序算法。处理使用平均Z位置,当您有相交的形状时,该位置不能保证正确。您可以自己完成,而不是依靠Processing来对三角形进行排序。你如何做到这一点超出了这个问题,这听起来很烦人,但这就是3D渲染的本质。

选项4:如果你真的,真的,真的需要有相交的透明形状,那么唯一正确的做法就是检测交叉点,然后将它们分成你随后绘制的子形状正确的订单。这非常非常烦人,但是你正走在一条烦人的道路上。

您也可以调查着色器。我对着色器一无所知,但是当我看到3D渲染问题时,有人不可避免地会出现并说答案是使用着色器。所以可能值得研究(它也可能值得研究,老实说我也不知道),尽管你自己也是如此那个。