SMAA算法详解 - AreaTex

本文详细解析了AreaTex中锯齿面积的计算方法,包括正交锯齿和对角线锯齿的处理,通过数学原理和算法实现,为图像渲染提供精准的锯齿消除方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AreaTex

  • 以下均在无SubPixel的情况下,即offset = (0,0)。subpixel将单独讲解。

areaortho.area

# Calculates the area under the line p1->p2, for the pixel x..x+1:
    def area(p1, p2, x):

  1. 计算底边长
	d = p2[0] - p1[0], p2[1] - p1[1]
        x1 = float(x)
        x2 = x + 1.0
        y1 = p1[1] + d[1] * (x1 - p1[0]) / d[0]
        y2 = p1[1] + d[1] * (x2 - p1[0]) / d[0]

在这里插入图片描述
依据三角形相似原理:
Y1 / (x1 - p1[0]) = d[1] / d[0] => Y1 = d[1] * (x1 - p1[0]) / d[0]
p1 = [0, o2] = [0, 0.5 + offset - 1.0] => p1[1] < 0
所以:y1 = p1[1] + Y1 = p1[1] + d[1] * (x1 - p1[0]) / d[0]
同理:y2 = p1[1] + Y2 = p1[1] + d[1] * (x2 - p1[0]) / d[0]


  1. 判断是梯形还是三角形
inside = (x1 >= p1[0] and x1 < p2[0]) or (x2 > p1[0] and x2 <= p2[0])
        if inside:
            istrapezoid = (copysign(1.0, y1) == copysign(1.0, y2) or 
                           abs(y1) < 1e-4 or abs(y2) < 1e-4)

  1. 计算面积
    一个梯形:
    在这里插入图片描述
    area = (y1 + y2) * (x2 - x1) / 2
    其中:x2 - x1 = 1
    area = abs(y1 + y2) / 2
    a1 = area
    a2 = 0

两个三角形:
在这里插入图片描述
a1 = (x - x1) * y1 / 2
设 X1 = x - x1,要求面积,就要求出 X1。
从图中可以看出,X1 正好是 X 的小数部分。
只要求出X就能得出面积。
根据三角形相似,
X = (-1 * p1[1]) * d[0] / d[1]
x = X + p1[0] = -p1[1] * d[1] / d[1] + p1[0]
所以,a1 = y1 * modf(x) / 2
同理:a2 = y2 * (1 - modf(x)) / 2


areaortho

# Horizontal/Vertical Areas

# Calculates the area for a given pattern and distances to the left and to the
# right, biased by an offset:
def areaortho(pattern, left, right, offset):

计算横向 / 纵向锯齿类型的面积。
patterns
在这里插入图片描述
具体计算略,套公式即可


smootharea

# Smoothing function for small u-patterns:
def smootharea(d, a1, a2):
    b1 = (a1 * 2.0).sqrt() * 0.5
    b2 = (a2 * 2.0).sqrt() * 0.5
    p = saturate(d / float(SMOOTH_MAX_DISTANCE))
    return lerp(b1, a1, p), lerp(b2, a2, p)

在这里插入图片描述
短U型锯齿有重复部分。
中间两个三角形面积为a1, a2。
计算时,左边a1只计算像素左边部分, 右边a2只计算像素右边部分。
为了得到整个像素的值,融合a1, a2,得到一个平滑过渡值。
长U型锯齿不存在交集。
计算时a1, a2必定有一个为 0. 即使参与计算也不会改变值。

tex2dortho

# Creates a 2D orthogonal pattern subtexture:
def tex2dortho(args):
    pattern, path, offset = args
    size = (SIZE_ORTHO - 1)**2 + 1
    tex2d = Image.new("RGBA", (size, size))
    for y in range(size):
        for x in range(size):
            p = areaortho(pattern, x, y, offset)
            p = p[0], p[1], 0.0, 1.0
            tex2d.putpixel((x, y), bytes(p))

计算直角锯齿面积
SIZE_ORTHO = 16
16个像素能存放[0 - 15],也就是搜索的最大距离只有15。
对于纹理边界是不够用的。
为了能访问到更大的距离,size = (SIZE_ORTHO - 1)^2 + 1 = 226。

pattern7(226 * 226)
pattern7(226 * 226)


tex4dortho

把tex2dortho计算后的tex2d存入tex4d的左半部分。

# Calculate the orthogonal patterns 4D texture for a given offset:
def tex4dortho(tex4d, files, y, offset):

tex2dortho得出的tex2d
size = 226 * 226,但是每个pattern在tex4d中占的大小是 16 * 16,
这样就需要一个压缩算法,用16个像素存放226个值。
观察上面pattern 7纹理图,基本还是一个线性变化的值。
所以以坐标:x = x ^ 2, y = y ^ 2,取16个值。
通过tex4d双线性采样,得出误差很小的值。
距离越小,精度越大,很长的锯齿可以忽略误差。


areadiag.area1

# Calculates the area under the line p1->p2 for the pixel 'p' using brute
    # force sampling:
    # (quick and dirty solution, but it works)
    def area1(p1, p2, p):

在这里使用了暴力计算。
将一个像素分成 SAMPLES_DIAG * SAMPLES_DIAG = 30 * 30 = 900个小区块。
把在p1->p2这条射线的下方的小区块相加,除以900。
其实就是计算该像素点在p1->p2下方的面积。

为什么要暴力计算呢?
直接计算面积的话,就要分清各种锯齿形状,麻烦!
这种方式,只需要判断小区块是否在线的下方,相加即可。

但是,这种方式会产生2个问题:

第一:判断小区块是否在p1->p2下面时,与p1->p2相邻的小区块的计算会存在误差,导致错误。
临近的点最多也只有30个,不会对结果有实质性改变。

第二:如下图,分成了30 * 30 = 900个小块.
白色线条为p1->p。
p1->p2下方的小块数量是 :(900 - 30) / 2 = 435,面积是:435 / 900 = 0.4833333
p1->p2上方以及p1->p2上的数量是 (900 - 435) = 465,面积是:0.51666667
所以,计算后的权重会存在小范围误差。

在这里插入图片描述

areadiag.area

# Calculates the area under the line p1->p2:
    # (includes the pixel and its opposite)
    def area(p1, p2, left, offset):
        e1, e2 = edgesdiag[pattern]
        p1 = p1 + vec2(*offset) if e1 > 0 else p1
        p2 = p2 + vec2(*offset) if e2 > 0 else p2
        a1 = area1(p1, p2, vec2(1.0, 0.0) + vec2(left, left))
        a2 = area1(p1, p2, vec2(1.0, 1.0) + vec2(left, left))
        return vec2(1.0 - a1, a2)

图示:灰黑色部分表示所求部分
在这里插入图片描述


areadiag

计算对角线面积

# Diagonal Areas

# Calculates the area for a given pattern and distances to the left and to the
# right, biased by an offset:
def areadiag(pattern, left, right, offset):

pattern 类型.
在这里插入图片描述

大致计算目标图示:
在这里插入图片描述
具体计算略,套公式即可


tex2ddiag

# Creates a 2D diagonal pattern subtexture:
def tex2ddiag(args):
    pattern, path, offset = args
    tex2d = Image.new("RGBA", (SIZE_DIAG, SIZE_DIAG))
    for y in range(SIZE_DIAG):
        for x in range(SIZE_DIAG):
            p = areadiag(pattern, x, y, offset)
            p = p[0], p[1], 0.0, 0.0
            tex2d.putpixel((x, y), bytes(p))
    tex2d.save(path, "TGA")

对角线tex2d


tex4ddiag

把tex2ddiag装入tex4d的右半部分。

# Calculate the diagonal patterns 4D texture for a given offset:
def tex4ddiag(tex4d, files, y, offset):

assemble

# Assembles 2D pattern subtextures into a 4D texture:
def assemble(tex4d, files, edges, pos, size, compress):

把tex2d装入tex4d中


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值