mirror of
https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git
synced 2025-08-23 04:35:28 +08:00
校对 04/02
This commit is contained in:
@@ -20,11 +20,11 @@
|
||||
|
||||
为了理解什么是多采样,以及它是如何解决走样的问题的,我们先要更深入了解一个OpenGL像素器的工作方式。
|
||||
|
||||
像素器是是你的最终的经处理的顶点和像素着色器之间的所有算法和处理的集合。像素器将属于一个基本图形的所有顶点转化为一系列fragment。顶点坐标理论上可以含有任何坐标,但fragment却不是这样,这是因为它们与你的窗口的解析度有关。几乎永远都不会有顶点坐标和fragment的一对一映射,所以像素器必须以某种方式决定每个特定顶点最终结束于哪个fragment/屏幕坐标上。
|
||||
像素器是是你的最终的经处理的顶点和片段着色器之间的所有算法和处理的集合。像素器将属于一个基本图形的所有顶点转化为一系列fragment。顶点坐标理论上可以含有任何坐标,但fragment却不是这样,这是因为它们与你的窗口的解析度有关。几乎永远都不会有顶点坐标和fragment的一对一映射,所以像素器必须以某种方式决定每个特定顶点最终结束于哪个fragment/屏幕坐标上。
|
||||
|
||||

|
||||
|
||||
这里我们看到一个屏幕像素网格,每个像素中心包含一个采样点(sample point),它被用来决定一个像素是否被三角形所覆盖。红色的采样点如果被三角形覆盖,那么就会为这个被覆盖像(屏幕)素生成一个fragment。即使三角形覆盖了部分屏幕像素,但是采样点没被覆盖,这个像素仍然不会任何像素着色器影响到。
|
||||
这里我们看到一个屏幕像素网格,每个像素中心包含一个采样点(sample point),它被用来决定一个像素是否被三角形所覆盖。红色的采样点如果被三角形覆盖,那么就会为这个被覆盖像(屏幕)素生成一个fragment。即使三角形覆盖了部分屏幕像素,但是采样点没被覆盖,这个像素仍然不会任何片段着色器影响到。
|
||||
|
||||
你可能已经明白走样的原因来自何处了。三角形渲染后的版本最后在你的屏幕上是这样的:
|
||||
|
||||
@@ -36,21 +36,21 @@
|
||||
|
||||

|
||||
|
||||
左侧的图显示了我们普通决定一个三角形的覆盖范围的方式。这个像素并不会运行一个像素着色器(这就仍保持空白),因为它的采样点没有被三角形所覆盖。右边的图展示了多采样的版本,每个像素包含4个采样点。这里我们可以看到只有2个采样点被三角形覆盖。
|
||||
左侧的图显示了我们普通决定一个三角形的覆盖范围的方式。这个像素并不会运行一个片段着色器(这就仍保持空白),因为它的采样点没有被三角形所覆盖。右边的图展示了多采样的版本,每个像素包含4个采样点。这里我们可以看到只有2个采样点被三角形覆盖。
|
||||
|
||||
!!! Important
|
||||
|
||||
|
||||
采样点的数量是任意的,更多的采样点能带来更精确的覆盖率。
|
||||
|
||||
多采样开始变得有趣了。2个子样本被三角覆盖,下一步是决定这个像素的颜色。我们原来猜测,我们会为每个被覆盖的子样本运行像素着色器,然后对每个像素的子样本的颜色进行平均化。例子的那种情况,我们在插值的顶点数据的每个子样本上运行像素着色器,然后将这些采样点的最终颜色储存起来。幸好,它不是这么运作的,因为这等于说我们必须运行更多的像素着色器,会明显降低性能。
|
||||
多采样开始变得有趣了。2个子样本被三角覆盖,下一步是决定这个像素的颜色。我们原来猜测,我们会为每个被覆盖的子样本运行片段着色器,然后对每个像素的子样本的颜色进行平均化。例子的那种情况,我们在插值的顶点数据的每个子样本上运行片段着色器,然后将这些采样点的最终颜色储存起来。幸好,它不是这么运作的,因为这等于说我们必须运行更多的片段着色器,会明显降低性能。
|
||||
|
||||
MSAA的真正工作方式是,每个像素只运行一次像素着色器,无论多少子样本被三角形所覆盖。像素着色器运行着插值到像素中心的顶点数据,最后颜色被储存近每个被覆盖的子样本中,每个像素的所有颜色接着将平均化,每个像素最终有了一个唯一颜色。在前面的图片中4个样本中只有2个被覆盖,像素的颜色将以三角形的颜色进行平均化,颜色同时也被储存到其他2个采样点,最后生成的是一种浅蓝色。
|
||||
MSAA的真正工作方式是,每个像素只运行一次片段着色器,无论多少子样本被三角形所覆盖。片段着色器运行着插值到像素中心的顶点数据,最后颜色被储存近每个被覆盖的子样本中,每个像素的所有颜色接着将平均化,每个像素最终有了一个唯一颜色。在前面的图片中4个样本中只有2个被覆盖,像素的颜色将以三角形的颜色进行平均化,颜色同时也被储存到其他2个采样点,最后生成的是一种浅蓝色。
|
||||
|
||||
结果是,颜色缓冲中所有基本图形的边都生成了更加平滑的样式。让我们看看当再次决定前面的三角形覆盖范围时多样本看起来是这样的:
|
||||
|
||||

|
||||
|
||||
这里每个像素包含着4个子样本(不相关的已被隐藏)蓝色的子样本是被三角形覆盖了的,灰色的没有被覆盖。三角形内部区域中的所有像素都会运行一次像素着色器,它输出的颜色被储存到所有4个子样本中。三角形的边缘并不是所有的子样本都会被覆盖,所以像素着色器的结果仅储存在部分子样本中。根据被覆盖子样本的数量,最终的像素颜色由三角形颜色和其他子样本所储存的颜色所决定。
|
||||
这里每个像素包含着4个子样本(不相关的已被隐藏)蓝色的子样本是被三角形覆盖了的,灰色的没有被覆盖。三角形内部区域中的所有像素都会运行一次片段着色器,它输出的颜色被储存到所有4个子样本中。三角形的边缘并不是所有的子样本都会被覆盖,所以片段着色器的结果仅储存在部分子样本中。根据被覆盖子样本的数量,最终的像素颜色由三角形颜色和其他子样本所储存的颜色所决定。
|
||||
|
||||
大致上来说,如果更多的采样点被覆盖,那么像素的颜色就会更接近于三角形。如果我们用早期使用的三角形的颜色填充像素,我们会获得这样的结果:
|
||||
|
||||
@@ -142,7 +142,7 @@ glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
|
||||
|
||||
你可以[在这里找到源代码](http://learnopengl.com/code_viewer.php?code=advanced/anti_aliasing_framebuffers)。
|
||||
|
||||
但是如果我们打算使用一个多采样帧缓冲的纹理结果来做这件事,就像后处理一样会怎样?我们不能在像素着色器中直接使用多采样纹理。我们可以做的事情是把多缓冲位块传送(Blit)到另一个带有非多采样纹理附件的FBO中。之后我们使用这个普通的颜色附件纹理进行后处理,通过多采样来对一个图像渲染进行后处理效率很高。这意味着我们必须生成一个新的FBO,它仅作为一个将多采样缓冲还原为一个我们可以在像素着色器中使用的普通2D纹理中介。伪代码是这样的:
|
||||
但是如果我们打算使用一个多采样帧缓冲的纹理结果来做这件事,就像后处理一样会怎样?我们不能在片段着色器中直接使用多采样纹理。我们可以做的事情是把多缓冲位块传送(Blit)到另一个带有非多采样纹理附件的FBO中。之后我们使用这个普通的颜色附件纹理进行后处理,通过多采样来对一个图像渲染进行后处理效率很高。这意味着我们必须生成一个新的FBO,它仅作为一个将多采样缓冲还原为一个我们可以在片段着色器中使用的普通2D纹理中介。伪代码是这样的:
|
||||
|
||||
```c++
|
||||
GLuint msFBO = CreateFBOWithMultiSampledAttachments();
|
||||
@@ -153,7 +153,7 @@ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, scre
|
||||
while(!glfwWindowShouldClose(window))
|
||||
{
|
||||
...
|
||||
|
||||
|
||||
glBindFramebuffer(msFBO);
|
||||
ClearFrameBuffer();
|
||||
DrawScene();
|
||||
@@ -166,8 +166,8 @@ while(!glfwWindowShouldClose(window))
|
||||
ClearFramebuffer();
|
||||
glBindTexture(GL_TEXTURE_2D, screenTexture);
|
||||
DrawPostProcessingQuad();
|
||||
|
||||
...
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
@@ -178,9 +178,9 @@ while(!glfwWindowShouldClose(window))
|
||||
你可以[在这里找到所有MSAA版本的后处理源码](http://learnopengl.com/code_viewer.php?code=advanced/anti_aliasing_post_processing)。
|
||||
|
||||
!!! Important
|
||||
|
||||
|
||||
因为屏幕纹理重新变回了只有一个采样点的普通纹理,有些后处理过滤器,比如边检测(edge-detection)将会再次导致锯齿边问题。为了修正此问题,之后你应该对纹理进行模糊处理,或者创建你自己的反走样算法。
|
||||
|
||||
|
||||
当我们希望将多采样和离屏渲染结合起来时,我们需要自己负责一些细节。所有细节都是值得付出这些额外努力的,因为多采样可以明显提升场景视频输出的质量。要注意,开启多采样会明显降低性能,样本越多越明显。本文写作时,MSAA4样本很常用。
|
||||
|
||||
#### 自定义反走样算法
|
||||
@@ -199,4 +199,4 @@ uniform sampler2DMS screenTextureMS;
|
||||
vec4 colorSample = texelFetch(screenTextureMS, TexCoords, 3); // 4th subsample
|
||||
```
|
||||
|
||||
我们不会深究自定义反走样技术的创建细节,但是会给你自己去实现它提供一些提示。
|
||||
我们不会深究自定义反走样技术的创建细节,但是会给你自己去实现它提供一些提示。
|
||||
|
Reference in New Issue
Block a user