GameTech/URP 渲染实战

案例 1.4透视校正插值 vs 仿射插值入门

目标:复现 1.7 节说的“仿射插值”,看看没有透视校正的纹理是什么样子。

原理:HLSL 允许给 Varyings 的成员加插值修饰符(Interpolation Modifier)。noperspective 让这个值在屏幕空间里线性插值,跳过透视校正。同一份 UV 传两遍,一份默认,一份加 noperspective,片元里用开关选择。

Assets/Cookbook/Ch01/PerspectiveInterp.shader(节选:varyings)
struct Varyings
{
    float4 positionCS : SV_POSITION;
    float2 uv         : TEXCOORD0;   // 默认:透视校正插值
    noperspective float2 uvAffine : TEXCOORD1; // 在屏幕空间里线性插值(PS1 时代的效果)
};
VARYING vec2 uv;            // 默认:透视校正插值
VARYING vec2 uvAffineW;     // noperspective 的模拟:uv × w
VARYING float clipW;        //                      w
图 1-21只有两个三角形的大平面与一面斜墙。左:默认的透视校正插值;右:noperspective(仿射插值),纹理沿三角形对角线折断、扭曲

右图的地面上,纹理在两个三角形的公共对角线处明显“折断”,每个三角形内部的格子都朝同一方向歪斜。这正是 PS1 时代游戏的标志性瑕疵。注意右边那面斜墙:它几乎正对相机,深度变化很小,仿射插值的误差也就很小——误差大小取决于三角形内部 w(深度)的变化幅度。当年的游戏靠把大三角形细分成小三角形来缓解,就是这个原因。

noperspective 不只是用来怀旧。凡是本来就在屏幕空间里线性变化的量,用它插值才是“正确”的,下一个案例就是例子。其他修饰符还有 nointerpolation(不插值,整个三角形取第一个顶点的值,常用于传递整数 ID)、centroid(MSAA 下在覆盖区域的质心采样,防止采样点落在三角形外)等。