You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在旗帜飘动顶点Shader中正确计算法线(替代临时方案)

问题:旗帜飘动顶点Shader的法线正确计算方法

我正在开发一个实现旗帜飘动效果的顶点Shader,目前已正确处理顶点位置,但在调整旗帜飘动对应的法线时遇到困难。我采用了一种临时解决方案,但该方案仅在部分视角下有效。

相关顶点Shader代码:

vec4 aPos=input_XYZ;
float aTime=(uniform_timer/75.)*7.; 
aPos.y+=sin(aTime)*.15f*input.XYZ.x; // The wave
output_XYZ=aPos*uniform_ComboMatrix; // Into scene space

vec4 aWorkNormal=input_Normal;
aWorkNormal.y+=sin(aTime)*.25f; // <-- Here's the kludge to inexpensively tip the normal into the "wave" pattern
output_Normal=aWorkNormal*uniform_NormalizedWorldMatrix; // Put into same space as the light's vector

我希望移除该临时方案,得到旗帜飘动时的正确法线。请问在通过sin修改y位置后,正确变换/旋转旗帜法线的方法是什么?


解决方案

你的临时方案直接修改法线y分量,本质是强行偏移法线方向,没有考虑曲面的实际几何变化,所以仅在特定视角下有效。正确的做法是根据曲面的几何导数计算法线:

原理

你的旗帜飘动是沿X轴的正弦波动,位置扰动公式为:
y = 原始y + 0.15 * 原始x * sin(aTime)
这个曲面的法线需要通过计算两个切线向量的叉乘得到——法线始终垂直于曲面的切线方向。

修改后的代码

vec4 aPos = input_XYZ;
float aTime = (uniform_timer / 75.0) * 7.0;
float waveAmplitude = 0.15;

// 处理顶点位置波动
aPos.y += waveAmplitude * input_XYZ.x * sin(aTime);
output_XYZ = aPos * uniform_ComboMatrix;

// 计算正确的法线
// 1. 求y对x的偏导数:描述曲面沿X方向的斜率
float dy_dx = waveAmplitude * sin(aTime);
// 2. 构造模型空间的两个切线向量
vec3 tangentX = vec3(1.0, dy_dx, 0.0); // 沿X轴的切线(随波动变化)
vec3 tangentZ = vec3(0.0, 0.0, 1.0);   // 沿Z轴的切线(无波动,保持原始方向)
// 3. 叉乘切线得到法线(注意顺序,确保法线方向与原始一致)
vec3 perturbedNormal = cross(tangentZ, tangentX);
// 4. 归一化法线,保证光照计算正确
perturbedNormal = normalize(perturbedNormal);
// 5. 转换到世界空间
output_Normal = vec4(perturbedNormal, 0.0) * uniform_NormalizedWorldMatrix;

说明

  • 这种方法基于曲面的实际几何变形计算法线,无论从哪个视角观察,法线都能准确反映旗帜当前的曲面朝向,彻底解决临时方案的视角局限性。
  • 如果你的旗帜模型本身带有原始法线(比如非平面的褶皱),可以将原始法线的切线空间与波动的切线结合,不过对于平面旗帜,上述代码完全适用。

内容的提问来源于stack exchange,提问作者KiraHoneybee

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 03:01:08