着色技巧

2022-02-25 17:30:09

(我今天发了一封邮件,内容是关于我们在着色器系统中使用的一个简单技巧,公开分享这封邮件似乎很容易,也很有用,所以,给你!)

那里';这是另一个雾霾突然袭来的地方,这是及时的——这可能很容易修复,尽管解释可能有点出乎意料,所以我把它作为一般知识传授的事情交给了团队。

一般来说,对于浮点数来说,当你进行大数加小数的计算时,会出现精度损失的问题;小的数字往往会下降并趋于零。这是关于计算机的一个普遍现象,而不仅仅是图形(这是我们为假装可以在少量内存中存储实数而付出的代价)。大多数时候是';这不是一个问题,程序员不会';I don’我想不起来,但有时它会成为一个问题。

在着色器中,我们通常希望有一个由CPU控制的时间变量,可以用来帮助生成效果。这个时间变量向上计数,可能是从关卡开始的时间,也可能是游戏开始的时间。如果这个数字是以秒为单位的,那么10分钟后它将是600左右,2小时后它将是7200,等等。如果有人让游戏运行3天(这是控制台上的认证要求!),价格将超过25万美元。

这些数字没有';看起来不是超级大,但同时,我们也处理小的数字。如果游戏以每秒60帧的速度运行(现在在PC上被认为是一种慢帧速!),那';每帧0.017秒,我们可能在做数学运算,包括这个dt和当前时间。此时,当前时间与帧时间的比率为250000/(1/60)=1500万。这真的非常高,而且是你不知道的各种数值问题的秘方';t expect--如果将增量时间加在当前时间上,在32位浮点中,增量时间几乎完全被破坏,因为数字精度不够。很久以前,你';d开始看到动画中的紧张,事情与你的';我希望他们会这样,等等。

解决这个问题的一种方法是不要让当前时间无限增长——在某个时刻,你可以将其重置为0。你可以选择固定的时间,就像我不知道的那样';I don’我不知道,每一分钟,最后时间回到0,你继续前进。但是如果你想让你的效果天衣无缝,它们必须在1分钟的边界上完全匹配,这使得调整变得困难和恼人。例如,如果你想制作一个无缝跨越时间的余弦波,你可以制作周期为1分钟、半分钟或1/3分钟的余弦波,等等,首先这很烦人,因为当设计师想要调整东西时,他们必须知道这一点,而且它';第二,这些数字也不能用浮点数精确表示,所以你';这样做会增加不精确性。

但有一个古老的图形编程技巧可以非常干净地解决这个问题,我认为这已经存在很长时间了(我第一次从Ignacio那里听说它是在我们处理证人的时候)。

对于上面提到的余弦波例子,我们将评估一些函数,比如cos(freq*time*2*PI),其中';时间';只是游戏中的挂钟时间,而且';频率';是一些控制余弦波频率的参数,我们希望它非常容易为设计师调整,而不引入问题。

当计时器重置时,为了让余弦波匹配,你必须满足的约束条件是,无论最长时间是什么,它们都必须计算为1——因为当你重置为time=0时,你会得到cos(freq*0*2*PI),也就是1。所以cos(freq*time_max*2*PI)也必须是1,换句话说,余弦必须绕圆整数次。因为我们这里有一个2*PI的因子,这意味着我们的波在任何时候都是完美的,freq*time_max是一个整数。

我们如何以人们不知道的方式轻松地确保这一点';你不需要考虑吗?为time_max选择10到某个整数幂的秒数,例如1000。然后,在调整文件中键入的任何数字(小数点后的数字不超过3位)都将在圆周围产生整数次。例如,如果freq=9.876,我们得到cos(9.876*1000*2*PI)==cos(9876*2*PI)==1。这适用于小数点后有3位数字的任何数字。如果你需要4位数字,那么把时间设为10000,以此类推。如果我们正在从调整文件中读取值,我们可以在将其发送到着色器之前将其四舍五入到最接近的3位数字(如果四舍五入的数字与输入的数字明显不同,可能会输出一条警告,说明这些数字未被使用,以提醒人们注意这个系统)。

这适用于任何周期函数,我们只是以余弦为例。对于线性移位,比如滚动uv坐标或诸如此类,它';s类似——如果uv坐标从0到1,则只需确保shift_rate*time_max是一个整数,以便在时间重置时完美滚动。类似地,时间参数用于索引动画书动画。

因此,播放一段时间后雾会捕捉的原因是,发送到着色器系统的时间每1000秒重置为0,但雾不会';我不知道这件事。重置的原因是确保着色器中基于时间的效果保持良好的精度。