第 10 章练习:波浪
对应教程:10 波浪的原理与实现
起始示例:波浪线与顶点位移,修改 10-waves.js 和需要时的 10-waves.html。
练习 1:读懂相位
**目标:**对 y = 0.2 * sin(2π * 2.0 * x - π * t),分别求振幅、x ∈ [0,1] 内的周期数、波长、一个固定 x 点的振动周期,以及波峰在 x 方向的移动速度和方向。
**完成标准:**区分“相位速度 π 弧度/秒”与“波峰移动多少 UV/秒”。
练习 2:双频波与分段数
**目标:**在现有波函数上加入一条振幅 0.04、周期数 7、向左传播的小波;片元模式与顶点模式都显示两条波叠加后的结果。
**要求:**大波保留原控件,小波使用固定参数;分别观察横向分段 8、28、84 的几何模式。说明哪一种分段数足以让小波看起来接近连续。
**完成标准:**片元和顶点模式的波形都出现小尺度起伏;低分段模式有明显折线或错误采样,高分段模式更平顺。记录你的屏幕观察结果,接受因显示大小不同而有差异。
<details><summary>提示</summary>
小波可写成 0.04 * sin(TAU * 7.0 * x + 2.3 * uTime);时间项为正使它向左传播。
</details>
练习 3:让线条厚度更稳定
**目标:**改进片元波浪线,让陡坡处的视觉厚度更接近平坦处。
**要求:**在片元模式中计算相位对 x 的导数,使用局部坡度修正距离。先保持现有平面与相机设置,不要求解决所有屏幕比例和透视情况。
**完成标准:**与原始竖直距离版本比较,在波峰附近与斜坡处,线条厚度变化减少;能解释该方法只是局部近似。
<a id=“reference-answers”></a>
第 10 章练习参考解答
对应练习:波浪。
练习 1
振幅为 0.2,整个 UV 宽度内有 2 个周期,波长 1 / 2 = 0.5 UV。固定 x 的时间振动周期为 2π / π = 2 秒。令相位 4πx - πt 恒定,得 dx/dt = π / (4π) = 0.25 UV/秒,方向为 +x。π 是相位速度,不是波峰的 UV 速度。
练习 2
在顶点着色器的位移处,把原来的波高替换为:
float largeWave = uAmplitude * sin( TAU * uCycles * uv.x - uSpeed * uTime );
float smallWave = 0.04 * sin( TAU * 7.0 * uv.x + 2.3 * uTime );
displaced.y += largeWave + smallWave;
在片元着色器的 centerY 处做相同修改,只把 uv.x 换成 vUv.x:
float largeWave = uAmplitude * sin( phase );
float smallWave = 0.04 * sin( TAU * 7.0 * vUv.x + 2.3 * uTime );
float centerY = 0.5 + largeWave + smallWave;
片元模式的 0.04 是 UV y 单位,几何模式的 0.04 是局部 y 单位,所以两者屏幕振幅不会完全相同。横向 8 段不足以呈现 7 个周期;28 段每周期约 4 段,已经能看见起伏但较粗;84 段每周期约 12 段,通常更接近连续曲线。实际观感还取决于屏幕尺寸与大波叠加。
练习 3
在片元模式中,保留现有 centerY,用下面代码代替原有的 distanceToLine:
float slope = uAmplitude * TAU * uCycles * cos( phase );
float physicalSlope = ( 1.2 / 2.0 ) * slope;
float distanceToLine = abs( vUv.y - centerY ) /
sqrt( 1.0 + physicalSlope * physicalSlope );
数学上,y - f(x) 除以 sqrt(1 + f'(x)^2) 是到曲线局部切线的近似距离。示例平面宽为 2.0、高为 1.2,所以 UV 中的坡度要先乘 1.2 / 2.0,换算为平面局部长度中的坡度。结果最后仍用 UV y 单位度量,因而可沿用原有 smoothstep(0.008, 0.014, distanceToLine)。对高频曲线,离曲线较远时局部切线近似会变差;若相机倾斜或发生透视缩放,还需进一步按屏幕导数处理。可增大周期数和振幅,比较最陡的区段。