GLSL 内置函数

fwidth()

返回一个值在相邻像素之间变化了多少。把它当作 smoothstep 的过渡宽度,无论缩放多少,边缘都正好柔和一个像素。

函数签名

float fwidth(float p)
vec2 fwidth(vec2 p)
vec3 fwidth(vec3 p)

参数

p
要测量的值,比如距离或坐标。

返回值

abs(dFdx(p)) + abs(dFdy(p)):p 在一个像素内的变化量。

在线示例

修改右侧代码,左侧预览会实时更新。

工作原理

GPU 以 2 x 2 的小块为单位着色像素,所以可以把一个值和相邻像素比较。fwidth(p) 返回 p 从一个像素到下一个像素变化了多少。

这正是抗锯齿需要的宽度。如果 d 是一个距离,smoothstep(0.0, fwidth(d), d) 就会在大约一个像素内完成过渡,不论图形缩放多少、画布多大。

示例中网格在不断缩放。左半边的线宽固定为 0.05 个网格单位,放大时线变粗变模糊,缩小时线变细甚至断开。右半边的线宽来自 fwidth,始终是清晰的一个像素。

常见用法

  • 抗锯齿图形和线条:任何尺寸下都清晰。
  • 网格和线框:线宽保持恒定像素数。
  • 选择细节层级:fwidth 很大说明图案细到看不清,可以逐渐淡出。

常见错误

  • WebGL 1 中漏了扩展声明。要在第一行(precision 之前)写 #extension GL_OES_standard_derivatives : enable。(WebGL 2 的 shader 不需要。)
  • 在相邻像素走不同分支的 if 里调用。只有 2 x 2 块中的四个像素都走同一个分支时导数才可靠,应在分支之前算好 fwidth。

相关函数