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

关于顶点受cos(x)波上下偏移的平面表面法向量计算及代码中dx、dy分量推导的技术问询

咱们先把你的顶点着色器代码整理成更易读的格式,方便后续分析:

public:
    void SetTime(float time) { t = time; }

    typename BaseVertexShader::Output operator()(const Vertex& v) const {
        // calculate some triggy bois
        const auto angle = wrap_angle(v.pos.x * freq + t * wavelength);
        const auto cosx = std::cos(angle);
        const auto sinx = std::sin(angle);

        // sine wave amplitude from position w/ time variant phase animation
        const auto dz = amplitude * cosx;
        const auto pos = Vec4{ v.pos.x, v.pos.y, v.pos.z + dz, 1.0f };

        // normal derived base on cross product of partial dx x dy
        auto n = Vec4{ -freq * amplitude * sinx, 0.0f, -1.0f, 0.0f };
        n.Normalize();

        return { pos * worldViewProj, n * worldView, pos * worldView, v.t };
    }

private:
    static constexpr float wavelength = PI;
    static constexpr float freq = 45.0f;
    static constexpr float amplitude = 0.02f;
    float t = 0.0f;
};

接下来逐个解答你的技术疑问:


1. 如何计算dx和dy分量?

这里的dx和dy指的是参数化曲面的偏导数向量——我们把这个波纹平面看作是由局部坐标系的x和y(对应顶点的v.pos.x和v.pos.y)作为参数的曲面,时间t是动画控制参数。

先明确曲面的位置函数:
平面上任意一点的空间位置可以表示为:
P(x, y, t) = (x, y, z0 + amplitude * cos(freq*x + wavelength*t), 1)
其中z0是平面的初始z坐标(对应代码里的v.pos.z)。

基于这个函数,两个偏导数向量的计算如下:

  • dx分量(对x的偏导数):固定y和t,对x求导。x自身的导数为1,y的导数为0,z分量对x的导数是 -amplitude * freq * sin(freq*x + wavelength*t)(复合函数求导:cos(u)的导数是-sin(u)u’,这里u=freqx + ...,u’=freq)。最终dx向量为:
    dx = (1, 0, -amplitude*freq*sin(angle), 0)(angle就是代码中计算的角度)
  • dy分量(对y的偏导数):固定x和t,对y求导。x和z分量对y的导数都是0,y自身的导数为1,所以dy向量为:
    dy = (0, 1, 0, 0)

2. 代码中法向量n的x分量为-freq * amplitude * sinx,该值是如何通过dx与dy的叉乘推导而来的?

参数曲面的法向量由两个偏导数向量的叉乘得到,不过要注意叉乘的顺序和坐标系方向(你的场景采用右手坐标系)。

先回忆右手坐标系下的叉乘公式:对于向量a=(a_x,a_y,a_z)和b=(b_x,b_y,b_z),叉乘a × b的结果为:
(a_y*b_z - a_z*b_y, a_z*b_x - a_x*b_z, a_x*b_y - a_y*b_x)

如果我们取dy × dx(而非dx × dy)来计算叉乘,代入dx=(1,0,-Afreqsinθ)、dy=(0,1,0)(这里用θ代替angle,A代替amplitude):

  • x分量:1*0 - (-A*freq*sinθ)*1 = A*freq*sinθ?不对,等下,叉乘顺序是dy×dx的话,应该把dy作为第一个向量,dx作为第二个:
    dy=(0,1,0),dx=(1,0,-Afreqsinθ)
    x分量:1*(-A*freq*sinθ) - 0*0 = -A*freq*sinθ
    y分量:0*1 - 0*(-A*freq*sinθ) = 0
    z分量:0*0 - 1*1 = -1

这个结果正好和代码里的初始法向量(-freq * amplitude * sinx, 0.0f, -1.0f)完全一致。

之所以选择dy × dx的顺序,是为了适配你的坐标系设定:平面局部z轴向下、世界坐标系z轴指向屏幕内部,这个叉乘顺序能得到符合光照计算预期的法向量方向(正确区分正面/背面)。最后代码对向量做归一化,得到单位法向量用于后续渲染。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 22:57:37