RealTime-Rendering18-dFdx/dFdy/fwidth

dFdx/dFdy/fwidth

Basis

01.png

$fwidth/dFdx/dFdy$是GLSL中的函数方法,$ddx/ddy$是HLSL中的方法,他们都是用于计算变量基于屏幕空间像素变化率的方法。

  • dFdx(v) = 该像素点右边的v值 - 该像素点的v值
  • dFdy(v) = 该像素点下面的v值 - 该像素点的v值
  • fwidth(v) = abs( dFdx(v) + dFdy(v))
  • fwidth、dFdx/dFdy 只可以在片元着色器中使用
  • fwidth、dFdx/dFdy 计算的是逐像素的变量差值,而不是逐片元
  • fwidth、dFdx/dFdy 接收什么类型的参数,返回值就是什么类型的参数
  • fwidth、dFdx/dFdy 参数可以是任意参数,但应该是从顶点着色器中传递的参数(varying)
  • 假如传入的参数为一个固定的值,那么像素差值为0,因为所有像素输出的值都一样,导数自然为0

02.png

2×2Quad并行处理机制, GPU以2×2的片段块(Quad)为单位处理片段, 也就是说导数数据是每两个像素变化一次,而不是每一次!

samples

纹理 LOD 选择 / 各向异性过滤

1
2
3
4
5
vec2 texCoord = ...; //屏幕空间,uv需要乘以纹理宽高
vec2 dx = dFdx(texCoord);
vec2 dy = dFdy(texCoord);
float deltaMaxS = max(dot(dx, dx), dot(dy, dy)); // 估算 mip 级别
float mipLevel = log2(deltaMaxS);

片元着色器中片元计算法线

1
2
3
4
5
6
7
8
in vec3 vWorldFragPos;

void main()
{
vec3 dpDx = dFdx(vWorldFragPos);
vec3 dpDy = dFdy(vWorldFragPos);
vec3 normal = normalize(cross(dpDx, dpDy));
}

线框着色

1
2
3
4
5
barys.z = 1 - barys.x - barys.y;
vec3 deltas = fwidth(barys);
barys = smoothstep(deltas, deltas * 2.0, barys);
float minBary = min(barys.x, min(barys.y, barys.z));
return albedo * minBary;

References:

flatShading & wireFrame