模板缓冲
介绍 (Introduction)
The trendy thing in real-time rendering these days is ray-tracing. However, traditional rasterization hasn’t disappeared, and it won’t in the near future. I recommend this blog post on the subject: A hybrid rendering pipeline for realtime rendering: (When) is raytracing worth it?
如今,实时渲染中的流行趋势是光线追踪 。 但是,传统的栅格化还没有消失,并且在不久的将来也不会消失。 我推荐有关此主题的博客文章: 用于实时渲染的混合渲染管道:(何时)光线跟踪值得吗?
I find that one of the most neglected elements in the rasterization pipeline is the Stencil Buffer. To get an idea of how neglected it is, I’ve checked the number of appearances of the stencil buffer in the approximately 1000 pages of “Real-Time Rendering”[1]: it appears just 5 times, and there are no more than 4 paragraphs dedicated to it. At least for me, it’s hard to get my head around the stencil buffer because it’s not fully programmable, so I tend to avoid using it. You can only configure it, and to do so you have to think of Boolean algebra, but in 3D.
我发现栅格化管道中最被忽略的元素之一是模板缓冲区 。 为了了解它是如何被忽略的,我检查了大约1000页“实时渲染” [1]中模版缓冲区的出现次数:它仅出现5次,并且不超过专门针对它的4个段落。 至少对我来说,很难完全绕过模板缓冲区,因为它不是完全可编程的,因此我倾向于避免使用它。 您只能对其进行配置,并且必须这样做,但必须考虑布尔代数,但要使用3D。
This blog post is an attempt to demystify the stencil buffer. I will briefly review the rendering pipeline, to see where the stencil sits, and then explain how the stencil works. I will use an example application in WebGL that we use to detect volume intersections, and explain the steps to convert the algorithm in my head to a tabular format that can be used to configure the stencil.
这篇博客文章试图揭开模板缓冲区的神秘面纱。 我将简要回顾渲染管线,以查看模板位于何处,然后解释模板的工作方式。 我将在WebGL中使用一个示例应用程序,该示例程序用于检测体积相交,并说明将我头脑中的算法转换为可用于配置模具的表格格式的步骤。
栅格化渲染管道 (The Rasterization Rendering Pipeline)
Virtually every GPU implements a rendering pipeline like the one above. In the middle row I tried to illustrate the transformations that we apply to our models until they become an image on the screen. In the vertex shader, we receive the triangles that make up the surface of our 3D model. Then, our vertex shader will apply a series of matrix multiplications to those triangles to convert from the model space (origin of coordinates centered around the model), to world space (origin of coordinates in the world origin), and then to camera space (origin of coordinates in the camera). Then, we apply a projection transform (perspective or orthographic), so the camera frustum becomes a unit cube. Whatever is outside that unit cube gets clipped, and mapped to screen coordinates. Then the rasterizer converts those triangles into pixels, interpolating color values between vertices. Then we can apply operations per pixel in our pixel shader, and blend the result into the frame buffer that we see on screen, in the merger stage.
实际上,每个GPU都像上面那样实现渲染管线。 在中间的一行中,我试图说明我们应用于模型的转换,直到它们成为屏幕上的图像为止。 在顶点着色器中,我们接收到构成3D模型表面的三角形。 然后,我们的顶点着色器将对这些三角形应用一系列矩阵乘法,以从模型空间(以模型为中心的坐标原点),世界空间(世界原点坐标的原点),然后到相机空间进行转换(相机中的坐标原点)。 然后,我们应用投影变换(透视图或正交投影),使摄像机视锥变成一个单位立方体。 该单元多维数据集之外的任何内容都会被裁剪,并映射到屏幕坐标。 然后,光栅化器将这些三角形转换为像素,在顶点之间插入颜色值。 然后,在合并阶段,我们可以在像素着色器中对每个像素应用操作,并将结果混合到屏幕上看到的帧缓冲区中。
合并阶段:混合,Z缓冲区和模具 (The merger stage: blending, Z-buffer, and stencil)
That merger stage does mainly 2 types of operations: blending and discarding pixels. The blending, or Alpha Blending, blends pixel colors of our object with the colors already in the frame buffer based on the alpha value of the texture of the object. The alpha value is typically 8-bit, so there are only 256 possible values. We can also use the alpha value to discard pixels as well, based on a threshold. Pixels with a value smaller than the threshold will be discarded. That’s referred to as alpha masking.
合并阶段主要执行两种类型的操作: 混合和丢弃像素。 blending或Alpha Blending基于对象纹理的alpha值将对象的像素颜色与帧缓冲区中已经存在的颜色进行混合 。 alpha值通常为8位,因此只有256个可能的值。 我们还可以根据阈值使用alpha值丢弃像素。 值小于阈值的像素将被丢弃。 这就是所谓的Alpha遮罩。
Pixels can also be discarded thanks to the Z-buffer. The Z-buffer contains the distance (Z) from the camera to the objects in the scene. Say we have rendered the mountain from the illustration above, and now we try to render a tree that’s behind the mountain. The Z-buffer contains the distance to the camera for every pixel of the mountain. We can compare the Z values of the tree, and discard them if the new Z value is greater than the Z we have already. The tree won’t render. Notice that if we change the rendering order and render the tree first, it will get rendered. However, once we draw the mountain the Z-test won’t fail, so the mountain will be rendered on top. So some pixels will be drawn over several times. That’s what we call the overdraw, which can be used to measure efficiency. Sorting the scene is a way of reducing the overdraw.
借助Z缓冲区,像素也可以被丢弃。 Z缓冲区包含从摄像机到场景中的对象的距离(Z)。 假设我们已根据上图绘制了山峰,现在我们尝试绘制山峰后面的一棵树。 Z缓冲区包含山的每个像素到相机的距离。 我们可以比较树的Z值,如果新的Z值大于我们已有的Z,则将其丢弃。 树不会渲染。 注意,如果我们更改渲染顺序并首先渲染树,则它将被渲染。 但是,一旦绘制了山峰,Z检验就不会失败,因此山峰将呈现在顶部。 因此一些像素将被绘制多次。 这就是我们所说的透支 ,可以用来衡量效率。 对场景进行排序是减少透支的一种方法。
Lastly, we can use the stencil buffer to discard pixels as well. The stencil buffer is typically an 8-bit buffer, so 256 distinct values are possible. In its simplest form, it can be used as an alpha mask. Say that we are seeing the mountain through a window, and we want to hide everything else. We can mark the pixels that belong to the window with an arbitrary number in the stencil buffer, e.g. a 1 signifies a pixel from the window, and then we configure the stencil buffer to discard everything that it’s not labeled as “window”. When combined with the Z-buffer, the stencil buffer can be used as a powerful tool to create volumetric effects, as we will see in the example later on.
最后,我们也可以使用模板缓冲区来丢弃像素。 模板缓冲区通常是8位缓冲区,因此可以有256个不同的值。 以其最简单的形式,它可用作alpha蒙版。 假设我们正在透过窗户看到这座山,而我们想隐藏其他所有东西。 我们可以在模板缓冲区中使用任意数字标记属于窗口的像素,例如1表示窗口中的像素,然后配置模板缓冲区以丢弃所有未标记为“窗口”的内容。 当与Z缓冲区结合使用时,模版缓冲区可以用作创建体积效果的强大工具,我们将在后面的示例中看到。
模板缓冲区配置 (Stencil buffer configuration)
To configure the stencil buffer we have 3 types of settings:
要配置模板缓冲区,我们有3种类型的设置:
Comparison functions. This is the function used to decide whether to discard a pixel or not. For instance, “greater than”, or “less than”. See: available stencil functions in WebGL.
比较功能 。 这是用于决定是否丢弃像素的功能。 例如,“大于”或“小于”。 请参阅: WebGL中可用的模具功能 。
Mask values. These are 8-bit binary masks. There are 3 types of masks: reference, read mask, and write mask. In WebGL, the reference and read mask are set with the stencil function, whereas the write mask is set with the stencil mask. The reference and read mask are used in conjunction with the comparison function. For instance, if the comparison is set to “greater than”, the stencil test will pass if
(refMask & readMask) > (stencil & readMask)
, where "&" is a bitwise binary AND operation. The write mask gets applied to what we write to the stencil buffer if the test passes and we decide to update it.掩码值 。 这些是8位二进制掩码。 屏蔽有3种类型:参考屏蔽,读取屏蔽和写入屏蔽。 在WebGL中,参考和读取掩码是通过模板功能设置的,而写掩码是通过模板掩码设置的。 参考和读取掩码与比较功能结合使用。 例如,如果将比较设置为“大于”,则如果
(refMask & readMask) > (stencil & readMask)
(其中“&”是按位二进制AND运算(refMask & readMask) > (stencil & readMask)
,则模板测试将通过。 如果测试通过并且我们决定更新它,则将写掩码应用于我们写入模板缓冲区的内容。Stencil operations. These are actions that can be configured in case of a successful or a failed test. You can do things like keep the current stencil value, replace it, or increment it. See: available stencil ops in WebGL. The actions can be configured for the 3 following conditions:
模具操作 。 这些是可以在测试成功或失败的情况下配置的操作。 您可以执行诸如保留当前模板值,替换它或增加它的操作。 请参阅: WebGL中可用的模具操作 。 可以针对以下三种情况配置操作:
- fail: the stencil test fails 失败:模板测试失败
- z-fail: the z-test fails (see Z-buffer in previous section) z-fail:z测试失败(请参阅上一节中的Z-buffer)
- z-pass: both the stencil and the z-test pass. z-pass:模具和z-test通过。
Writing it down as one big logical operation, for each pixel, the new value of the stencil buffer can be computed as follows:
将其记为一项大的逻辑运算,对于每个像素,可以如下计算模板缓冲区的新值:
if (refMask & readMask) Comparison (stencil & readMask):
It does sound very abstract, doesn’t it? How do all these logical operations become something useful? I hope with the example in the next section you learn how to configure the stencil.
听起来很抽象,不是吗? 所有这些逻辑运算如何变得有用呢? 我希望通过下一节中的示例学习如何配置模板。
使用模板缓冲区可视化体积相交 (Visualizing volume intersections with the stencil buffer)
问题定义 (Problem definition)
Let start with the problem definition. We want to visualize the volume intersections in a mesh, and any open areas of the mesh. This is a quick way of visually detecting if a mesh is watertight, i.e. the mesh contains no holes and it’s clearly defined inside. Holes are easy to visualize if we render the object in 2 passes. A vertex has 2 sides, front and back. Whether a side is front or back is decided by an arbitrary vertex winding order (it can be configured). When rendering, back faces are usually not rendered, but this culling is one of those things that can be configured in the render pipeline. So we can do a first pass where we render only the back faces in a bright green color, and then a normal pass where we render the rest. If we see green on screen that means that the mesh has a hole in there.
让我们从问题定义开始。 我们要可视化网格中的体积相交以及网格的任何开放区域。 这是一种直观地检测网格是否防水的快速方法,即该网格不包含任何Kong,并且在内部明确定义。 如果我们通过2次渲染对象,则Kong很容易可视化。 一个顶点有2个面,正面和背面。 侧面是正面还是背面取决于任意顶点缠绕顺序(可以配置)。 渲染时,通常不渲染背面,但是这种剔除是可以在渲染管道中配置的那些东西之一。 因此,我们可以进行第一遍,仅以明亮的绿色渲染背面,然后进行普通遍,以渲染其余部分。 如果我们在屏幕上看到绿色,则表示网格中有一个Kong。
体交的配置 (Configuration for volume intersection)
For the volume intersection things get a bit more complicated. I know that the stencil should be useful in this, but how do we set it up? I always start writing down on the white board all the examples of triangle layerings that I can think of. Then, I know that in the end I want a stencil mask that marks exactly the intersection area in the given example. What operations can take me there? There are multiple. The challenge is to find one that works for all the examples you’ve written down. There must be a better way to draw this, but this is what I got:
对于体积相交,情况变得更加复杂。 我知道模版应该在其中有用,但是我们如何设置它呢? 我总是开始在白板上写下我能想到的所有三角形分层示例。 然后,我知道最后我需要一个模版遮罩,以精确标记给定示例中的相交区域。 什么手术可以带我到那里? 有多个。 面临的挑战是找到一个适用于您编写的所有示例的示例。 必须有更好的方法来绘制此图形,但这就是我得到的:
Then, once I think I have all the cases I need, I try to fill in a table with all the stencil configuration per render pass. From the picture above, you can see that the way I designed it, I’m going to need at least 3 passes:
然后,一旦我认为我有所有需要的情况,便尝试使用每个渲染遍历在表格中填写所有模具配置。 从上面的图片中,您可以看到我的设计方式,我至少需要3次通过:
- one to render the back faces, where I count the number of back-facing polygons; 一个用于渲染背面,我在其中计算背面多边形的数量;
- a second pass to render the front faces and decrease the counter if the z-test fails. We will avoid writing onto the Z-buffer so we can distinguish those 2 circled cases (where front-face B is rendered before front-face A). Because during the back pass we update the Z-buffer, the Z-buffer before starting the 2nd pass contains the z value of the back face closer to the camera. In the non-intersecting example, the order doesn’t matter, because whether the Z-buffer contains the z of the back-face or the z of B, we can detect a z-test failure when trying to draw A and decrease the counter. But in the intersecting example, if we draw face B first and update the Z-buffer, when trying to draw face A the z-test will fail and we will wrongly decrease the counter. To solve this without having to sort the geometry, we will stop all Z-buffer updates (Z-write off) during this pass. 如果z测试失败,则进行第二次渲染以渲染正面并减少计数器。 我们将避免写入Z缓冲区,因此我们可以区分这2个带圆圈的情况(正面B在正面A之前渲染)。 因为在后退过程中我们更新了Z缓冲区,所以在开始第二遍之前的Z缓冲区包含了更靠近相机的背面的z值。 在非相交的示例中,顺序无关紧要,因为Z缓冲区是包含背面的z还是B的z,我们在尝试绘制A并减小A时可以检测到z检验失败。计数器。 但是在相交的示例中,如果我们首先绘制面B并更新Z缓冲区,则在尝试绘制面A时z测试将失败并且我们将错误地减少计数器。 要解决此问题而不必对几何进行排序,我们将在此过程中停止所有Z缓冲区更新(取消Z写入)。
- a third pass to create a binary mask with the intersection area. 第三遍创建具有交叉区域的二进制蒙版。
- I can add an optional 4th pass to render the lighting of the non-intersecting volumes. 我可以添加可选的第4遍以渲染非相交体积的照明。
Here’s my final stencil table:
这是我最终的模具表:
If you want to check how that translates into code, check this pull request in GitHub: self-intersections for WebGL Model Viewer.
如果要检查如何将其转换为代码,请在GitHub中检查此拉取请求: WebGL Model Viewer的自相交 。
可视化结果 (Visualization results)
Here’s a video of the WebGL Model Viewer in action:
这是运行中的WebGL Model Viewer的视频:
The green areas are back faces, so holes in the mesh, whereas the red areas are the volume intersections. One application of this is to help us spot issues in poses in our avatars. If part of the arm intersects with the chest, we will have problems when trying to dress the avatar with a shirt, because the sleeve will also try to enter the chest and the cloth simulation will struggle. See example below:
绿色区域是背面,因此网格中有Kong,而红色区域是体积相交。 一种应用是帮助我们发现化身中姿势中的问题。 如果手臂的一部分与胸部相交,则在尝试使用衬衫为化身打扮时会遇到问题,因为袖子还会尝试进入胸部,而布料模拟会很困难。 请参见下面的示例:
结论 (Conclusion)
Rasterization is still the most used rendering pipeline in real time graphics. Inside the rasterizer, the Stencil Buffer seems to be the ugly duckling no one wants to hang around with, perhaps only reserved to big graphic gurus. I have showed you with a practical example that we can use the stencil buffer to visualize volume intersections in real time, and that the stencil is not as scary if we describe the problem with examples and in tabular form.
栅格化仍然是实时图形中最常用的渲染管道。 在光栅化器内部,“模板缓冲区”似乎是一个丑陋的小鸭,没人愿意与它混在一起,也许只保留给大型图形大师使用。 我已经通过一个实际示例向您展示了我们可以使用模板缓冲区实时显示体积相交,并且如果我们以示例和表格形式描述问题,模板并不那么令人恐惧。
Visualizing volume intersections in real time has a practical application for us. When we author poses for our avatars, we can immediately see if a pose will end up having cloth simulation problems, and correct the limb position accordingly.
实时可视化体积相交对我们来说具有实际应用。 当我们为化身创作姿势时,我们可以立即查看姿势是否最终会遇到布料模拟问题,并相应地校正肢体位置。
For more applications of the stencil buffer, check the “Real-Time Rendering” book [1], and the Wikipedia article.
有关模板缓冲区的更多应用,请查看“实时渲染”一书[1]和Wikipedia文章 。
Originally published at https://tech.metail.com on July 3, 2020.
最初于 2020年7月3日 在 https://tech.metail.com 上 发布 。
翻译自: https://medium.com/real-time-rendering/the-stencil-buffer-and-how-to-use-it-to-visualize-volume-intersections-34dc1d205ebc
模板缓冲
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/274502.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!