多重采样和片段着色器

4
多重采样似乎对片元着色器生成的片元无效。在下面的示例中,片元着色器用于生成棋盘式程序纹理。正方形的外边缘被正确地抗锯齿处理,但程序纹理的内部边缘却没有被处理。
片元着色器是每像素评估还是每个给定像素的片元的纹理坐标相同?
以下是代码,image 显示了其输出(请注意,程序边缘——白色和灰色正方形之间的边缘——没有进行抗锯齿处理,而几何边缘——黑色和白色/灰色之间的边缘——则有)。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-


# imports ####################################################################

import sys

from OpenGL.GLUT import *
from OpenGL.GL import *


# display ####################################################################

def reshape(width, height):
    """window reshape callback."""
    glViewport(0, 0, width, height)

    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    r = float(min(width, height))
    w, h = 2*width/r, 2*height/r
    glOrtho(-w, w, -h, h, -1, 1)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()
    glRotate(45, 0, 0, 1)

def display():
    """window redisplay callback."""
    glClear(GL_COLOR_BUFFER_BIT)
    glBegin(GL_TRIANGLE_STRIP)
    for x in [-1, 1]:
        for y in [-1, 1]:
            glTexCoord(x, y)
            glVertex(x, y)
    glEnd()
    glutSwapBuffers()


# setup ######################################################################

glutInit(sys.argv)
glutInitDisplayString(b"rgba double samples=4")
glutInitWindowSize(100, 100)
glutCreateWindow(sys.argv[0].encode())

glutReshapeFunc(reshape)
glutDisplayFunc(display)

glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

shader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(shader, """
    void main() {
        vec2 c = gl_TexCoord[0].xy;
        vec4 color = gl_Color;
        if(c.x*c.y < 0.) color.a *= .5;
        gl_FragColor = color;
    }
""")
glCompileShader(shader)
program = glCreateProgram()
glAttachShader(program, shader)
glLinkProgram(program)
glUseProgram(program)

glutMainLoop()  

那是哪种语言? - vallentin
使用Python/Python3和PyOpenGL,但问题并不依赖于编程语言。如果有帮助的话,我可以添加一个C版本。 - rndblnch
好的,看起来我需要sample_shading扩展: 在标准多采样渲染中,实现允许将相同的颜色和纹理坐标值分配给每个样本,然后只评估着色器一次,然后将其分发到已确定被当前正在光栅化的原语覆盖的样本。 - rndblnch
2个回答

12

这是MSAA(多重采样抗锯齿)的基本思想。片段着色器仅对每个片段执行一次。然后使用样本掩码来控制将结果片段写入哪些样本中。

假设您使用4x MSAA,其中每个片段包含2x2个样本。如果三角形的边通过一个片段,只有边内的样本会用新颜色更新。因此,如果片段仅部分位于三角形内,可能会更新1、2或3个4个样本。但所更新的样本都使用相同的颜色进行更新。因此,片段着色器仅需评估一次。

这种方法的最大优点是非常高效。与不使用MSAA渲染相比,它通常只会增加很小的开销。正如已经确定的那样,着色器执行的数量不变。渲染目标理论上变为4倍大,这可能会大幅增加内存使用量。但是,该内存通常可以有效压缩,因此并没有听起来那么糟糕。显然,在渲染帧结束时进行下采样步骤会增加开销。

缺点是MSAA仅有助于平滑三角形边界和交叉点。如果三角形内有明显的转换,例如由于纹理内容或由于程序纹理,则根本无法帮助。Mipmap可以减少文理造成的锯齿状边缘,而程序纹理可以通过考虑梯度来减少锐利的转换。但MSAA无法帮助。

如果您想要针对所有锯齿问题进行抗锯齿,您可以全力以赴并使用超采样。这意味着您以更高的分辨率渲染整个场景。例如,如果最终渲染目标的大小为w × h,则渲染到大小为2×w × 2×h的表面上,并在最后进行下采样。这会极大地增加成本,因为现在必须将片段着色器执行4次,并且在大表面上进行压缩也不像在MSAA表面上那么有效。

我没有使用您找到的ARB_sample_shading扩展。但是根据我对它的了解,它试图接近超采样的视觉质量,同时保持MSAA至少部分性能优势。


如果一个片段属于多个三角形怎么办?例如,如果样本1和2属于三角形A,而样本3和4属于三角形B,这是否意味着片段着色器必须被调用两次,分别为每个三角形调用一次? - Tony Beta Lambda

0
需要 sample_shading 扩展:"在标准的多采样渲染中,实现允许将相同的颜色和纹理坐标值分配给每个采样,这使得着色器仅被评估一次,然后分配给已确定由当前正在光栅化的原语覆盖的采样。"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接