如何在深度排序的半透明对象上避免这种损坏? [英] How can I avoid this corruption on depth-sorted semi-transparent objects?

查看:56
本文介绍了如何在深度排序的半透明对象上避免这种损坏?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

void setup() {大小(600、480、P3D);提示(ENABLE_DEPTH_SORT);}空抽(){背景(0);翻译(宽/2,高/2);填充(颜色(255,255,255),84);行程重量(0);翻译(-40,0,1);球体(80);翻译(2*40,0,0);球体(80);//因损坏而失败:http://i.imgur.com/GW2h7Qv.png}

注意:sphereDetail 不需要应用.sphereDetail(60) 导致失败,左重叠丢失:

解决方案

要了解为什么会发生这种情况,您必须了解幕后发生的事情.

三角形思维

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

size(500, 500, P3D);翻译(宽/2,高/2);球体(100);

在您的代码中,您已选择不绘制轮廓,但 Processing 仍在绘制一堆三角形来绘制您的球体.这是它使用 sphereDetail(10) 绘制的内容:

关键是,您需要停止看球体,而要看到构成这些球体的三角形.

深度缓冲

OpenGL(以及因此处理)使用深度缓冲来确定何时绘制位于其他内容后面的内容.

我只是要复制一部分 :

<块引用>

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

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

例如,如果我们在前面画一个透明的灰色球体,然后在它后面画一个红色球体,我们将看不到红色球体.

size(500, 500, P3D);翻译(宽/2,高/2);noStroke();//正面灰色球体填充(255、255、255、64);球体(100);//返回红色球体翻译(0, 0, -500);填充(255, 0, 0, 64);球体(100);

我在这里使用整个球体是因为我很懒,但是想象一下每个球体中的单个三角形都会发生这个确切的问题.这就是为什么您会在灰色球体中看到这些伪像的原因,与您看不到灰色球体后面的红色球体的原因相同.OpenGL 首先绘制灰色球体,然后跳过红色球体,因为它知道它在前面的球体后面.这是不透明对象的正确行为,但我们通过添加透明度使事情复杂化.

图纸订单事项

如果我们在绘制前面的灰色球体之前绘制后面的红色球体,那么我们可以通过灰色球体看到红色球体:

size(500, 500, P3D);翻译(宽/2,高/2);noStroke();//返回红色球体翻译(0, 0, -500);填充(255, 0, 0, 64);球体(100);//正面灰色球体翻译(0, 0, 500);填充(255、255、255、64);球体(100);

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

现在,要解决三角形的问题,绘制顺序很重要.就像您必须在绘制背面红色球体之后绘制正面灰色球体一样,您必须从背面到正面绘制三角形,这样 OpenGL 就不会跳过它确定的任何三角形在一个已经绘制的三角形后面.这是颈部的巨大疼痛.

处理救援

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

size(500, 500, P3D);提示(ENABLE_DEPTH_SORT);翻译(宽/2,高/2);noStroke();//正面灰色球体填充(255、255、255、64);球体(100);//返回红色球体翻译(0, 0, -500);填充(255, 0, 0, 64);球体(100);

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

交叉点的问题

请记住,三角形按其 3 个顶点的平均 Z 位置排序.这是相交三角形的问题.这是问题的症结所在.

来自,OpenGL 无法很好地处理它们.你能简单地移动你的形状,让它们不相交吗?如果您正在尝试构建复杂的形状,可以使用 3D 模型还是使用 vertex() 函数自行构建?​​

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

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

您也可以研究着色器.我对着色器一无所知,但是每当我看到 3D 渲染的问题时,总会有人站出来说答案是使用着色器.因此,可能值得研究(它也可能值得研究,老实说我不知道​​),尽管您自己处理那个.

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
}

Note: sphereDetail need not apply. sphereDetail(60) causes fail with loss of left overlap:

解决方案

To understand why this is happening, you have to understand what's going on under the hood.

Think in Triangles

When you call the sphere() function, Processing is actually drawing a bunch of triangles. You can see this if you run this simple program:

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

In your code, you have chosen to not draw the outline, but Processing is still drawing a bunch of triangles to draw your sphere. Here is what it draws with sphereDetail(10):

The point is, you need to stop seeing spheres, and see the triangles that make up those spheres.

Depth Buffering

OpenGL (and therefore Processing) uses depth buffering to figure out when not to draw something that's behind something else.

I'm just going to copy a section of this article, which explains it much better than I can:

Consider this example of drawing two triangles, A and B:

If we draw B first, then A, the depth buffer will see that the new pixels from A are closer than the ones previously drawn by B, so it will draw them over the top. If we draw in the opposite order (A followed by B) the depth buffer will see that the pixels coming in from B are further away than the ones already drawn by A, so it will discard them. In either case we get the correct result: A is on top, with B hidden behind it.

When you're drawing an opaque sphere, you're really drawing a bunch of opaque triangles. Processing will draw these triangles in whatever order the sphere-to-triangle algorithm generated them. This works for opaque objects, since Processing will check the Z-buffer and then skip over triangles that are behind triangles that were already drawn. That's basically as smart as OpenGL gets out of the box.

The Problem with Transparency

Again, quoting the article:

But what if this geometry is alpha blended, so B is partially visible through the translucent A triangle? This still works if we draw B first, then A over the top, but not if we draw A followed by B. In that case, the depth buffer will get a pixel from B, and notice that it has already drawn a closer pixel from A, but it has no way to deal with this situation. It’s only choices are to draw the B pixel (which will give the wrong result, because it would be blending the more distant B over the top of the closer A, and alpha blending is not commutative) or it could discard B entirely. Not good!

The problem is, this doesn't work for transparent triangles. Remember that Processing is drawing the triangles in whatever order the algorithm generated them, and it's using the Z-buffer to decide when to skip over triangles that are behind already-drawn triangles. But we don't want it to skip over any triangles! We want to draw every triangle, because we want to be able to see the back triangles through the front triangles.

For example, if we draw a transparent gray sphere in front, and then we draw a red sphere behind it, we won't see the red sphere.

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);

I'm using entire spheres here because I'm lazy, but imagine this exact problem happening for the individual triangles in each sphere. This is why you see those artifacts in the gray sphere, for the same reason you can't see the red sphere behind the gray sphere. OpenGL draws the gray sphere first, but then it skips over the red sphere because it knows that it's behind the front sphere. This is the correct behavior for opaque objects, but we're complicating things by adding transparency.

Drawing Order Matters

If instead we draw the back red sphere before we draw the front gray sphere, then we see the red sphere through the gray sphere:

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);

Note that we still see artifacts within each sphere because of the triangle order problem. This is happening for the same reason we couldn't see the red sphere when we draw it after the gray sphere.

Now, to fix the issue with the triangles, drawing order matters. Exactly like you have to draw the front gray sphere after you draw the back red sphere, you have to draw the triangles from the back to the front, that way OpenGL doesn't skip over any that it determines are behind an already-drawn triangle. This is a huge pain in the neck.

Processing to the Rescue

Luckily, Processing has an option to do this for you. That's the hint(ENABLE_DEPTH_SORT) function. With this enabled, Processing will sort every triangle by the average Z position of its three vertices, and then draw them in that order. With this enabled, we can draw the back red sphere after the front gray sphere, and Processing will sort the triangles for us and draw them in the correct order before it gets to 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);

Notice that we can see the back red sphere, even though we're drawing it after the front gray sphere. Processing is intervening on our behalf and sorting the triangles before actually drawing them in OpenGL. This also fixes the artifacts within each sphere, since the triangles are sorted in each sphere, not just between spheres.

The Problem with Intersection

Remember that the triangles are sorted by the average Z position of their 3 vertices. This is a problem for triangles that intersect. This is the crux of your issue.

From the article, imagine that you have two triangles A and B that intersect like this:

There is no possible way to sort these triangles, because we need to draw the top part of B over A, but the bottom part of A over B. The only solution is to detect when this happens and split the triangles where they intersect, but that would be prohibitively expensive.

Which triangle should be drawn first? There isn't a single answer, but Processing is going to do its best and sort them according to the average Z position of their 3 vertices. This is causing the artifacts you're seeing.

We can see this in a simple interactive sketch:

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);
}

Move your mouse up and down to move the red sphere front or back. As it intersects with the gray sphere, you get the artifacts because the triangles are intersecting.

(Ignore the artifacts around the mouse, that's a result of creating a gif.)

This is the cause of the artifacts you're seeing.

Possible Solutions

What can you do to fix it? You've got a few options:

Option 1: Stop using transparency. Many of your issues are caused by using transparency, which makes things very complicated. The quickest and easiest thing to do would be to stop using transparency, as OpenGL works best with opaque objects.

Option 2: Stop using shapes that intersect. You can think of your intersecting spheres as impossible objects, which OpenGL doesn't handle very well. Could you simply move your shapes so they aren't intersecting? If you're trying to build a complex shape, maybe use a 3D model or construct it yourself using the vertex() function?

Option 3: Come up with your own sorting algorithm. Processing uses the average Z position, which isn't guaranteed to be correct when you have intersecting shapes. Instead of relying on Processing to sort the triangles, you could do it yourself. How you'd do that goes beyond this question, and it sounds super annoying, but that's the nature of 3D rendering.

Option 4: If you really, really, really need to have intersecting transparent shapes, then the only correct thing to do is to detect intersections and then split them up into subshapes that you then draw in the correct order. This is very very very annoying, but you're going down an annoying path.

You might also investigate shaders. I don't really know anything about shaders, but anytime I see an issue with 3D rendering, somebody inevitably comes along and says the answer is to use a shader. So that might be worth looking into (it also might not be worth looking into, I honestly don't know), although you're on your own with that one.

这篇关于如何在深度排序的半透明对象上避免这种损坏?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆