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。