如何在Shader Graph中获取Gerstner Shader的波浪高度?

4
我已经按照这个Gerstner波纹着色器图教程这里进行了跟随。
现在,我正在尝试通过将以下脚本附加到游戏对象上来实现浮力(取自这个教程)。
using UnityEngine;

public class Floater : MonoBehaviour
{
    public Rigidbody rigidBody;
    public float depthBeforeSubmerged = 1f;
    public float displacementAmount = 3f;
    public int floaterCount = 1;
    public float waterDrag = 0.99f;
    public float waterAngularDrag = 0.5f;

    private void FixedUpdate()
    {
        rigidBody.AddForceAtPosition(Physics.gravity / floaterCount, transform.position, ForceMode.Acceleration);

        float waveHeight = WaveManager.instance.GetWaveHeight();

        if (transform.position.y < waveHeight)
        {
            float displacementMultiplier = Mathf.Clamp01((waveHeight - transform.position.y) / depthBeforeSubmerged) * displacementAmount;
            rigidBody.AddForceAtPosition(new Vector3(0f, Mathf.Abs(Physics.gravity.y) * displacementMultiplier, 0f), transform.position, ForceMode.Acceleration);
            rigidBody.AddForce(displacementMultiplier * -rigidBody.velocity * waterDrag * Time.fixedDeltaTime, ForceMode.VelocityChange);
            rigidBody.AddTorque(displacementMultiplier * -rigidBody.angularVelocity * waterAngularDrag * Time.fixedDeltaTime, ForceMode.VelocityChange);
        }
    }
}

在这段代码中,你使用float waveHeight = WaveManager.instance.GetWaveHeight();来从下面的WaveManager脚本中获取波浪高度,该脚本附加在Gerstner Wave游戏对象上(只是一个应用了着色器图形材质的细分平面)。
public class WaveManager : MonoBehaviour
{
    public static WaveManager instance;

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
        }
        else if (instance != this)
        {
            Debug.Log("Instance already exists, destroying object!");
            Destroy(this);
        }
    }

    public float GetWaveHeight()
    {
       // The formula will go here.
    }
}
你应该在GetWaveHeight()函数中使用与着色器图中相同的公式。 问题是我不知道如何实现这一点。
以下截图展示了我目前使用的Gerstner波着色器图设置。
要澄清的是,有4个单独的Gerstner波叠加在一起,以使其更加逼真,因此需要将其纳入公式中。 频率子图

Frequency Subgraph

Theta子图

Theta Subgraph

位移子图

Displacement Subgraph

GerstnerWave子图

GerstnerWave Subgraph

新常态子图

NewNormal Subgraph

Gerstner Shader Graph(分为3个部分:顶部、中部和底部,因为它太大了)

Top

Middle

Bottom

欢迎任何建议。我知道这可能是一个复杂的问题,如果需要澄清任何事情,请告诉我。

项目在GitHub上这里可用。

我正在使用一艘船模型来测试浮力,它有4个附加了Floater脚本的游戏对象。

谢谢!


我认为没有直接将ShaderGraph转换为GetHeight(Vector2)函数的方法,因为着色器通过在网格中的x、z位置和y方向上移动点来工作,所以很难从给定的(x,z)位置"查找"高度。要么你需要一个更简单的波浪系统,只进行垂直位移,或者可以将波浪渲染到表示波浪高度的RenderTexture中,并读取纹理值来获取波浪高度。 - John
我刚刚尝试了使用“RenderTexture”的方法,但是延迟太大了,以至于我无法确定是否做对了。我查阅了一些资料,得知这是一个不断从GPU读取到CPU的密集过程。我真的很想使用这个分层的格斯纳系统,因为对于我希望创建的游戏类型来说,正弦波并不足够有趣。 - Magnality
使用渲染纹理并不便宜,但是添加一个渲染纹理不应该引起任何重大性能问题。你只需要添加一个分辨率适中的渲染纹理,使用正交相机从上方渲染水网格,并读取深度值。提醒一下,我建议的RenderTexture方法比较复杂,所以尝试在C#中重新实现你的Gerstner算法,并通过插值附近的值来解决(x,z)问题可能会更容易。如果你在shadergraph上点击“查看生成的着色器”,这将为你转换为C#提供一个良好的起点。 - John
2个回答

0
实际上,你有波的方向、振幅和速度。实际上,你可以模拟。在C#端创建4个波形结构,并将每个波的值传递给它们中的每一个,在理论上找到尖锐边缘的高度。但这只是我的猜测。

1
我尝试过几次,但是我觉得自己的知识不足以正确地在C#中实现shadergraph公式。我似乎无法找到正确的公式。 - Magnality

0

好的,让我给你一点帮助

public float HyperbolicTangent(float x)
{
    float exp2x = Mathf.Exp(2 * x);
    return (exp2x - 1) / (exp2x + 1);
}

private float Frequency(Vector3 direction)
{
    float length = direction.magnitude;
    float gLength = gravity * length;

    float dLength = depth * length;
    float  hyperbolicTang = HyperbolicTangent(dLength);

    return Mathf.Sqrt(hyperbolicTang * gLength);
}

private float Theta(Vector3 position, float timing, Vector3 direction)
{
    float positionDirectionX = position.x * direction.x;
    float positionDirectionZ = position.z * direction.z;

    float sum = positionDirectionX + positionDirectionZ;

    float multFrequency = Frequency(direction) * timing;

    return sum - multFrequency - phase;
}

private Vector3 GerstnerWave(Vector3 position, Vector3 direction, float amlitude, float timing)
{
    float theta = Theta(position, timing, direction);

    float GetCoordinate(float axisCoordinate)
    {
        float amplitudePerHyperbolicLengthX = amlitude / HyperbolicTangent(direction.magnitude * depth);

        float directionXToLength = axisCoordinate / direction.magnitude;

        return Mathf.Sin(theta) * directionXToLength * amplitudePerHyperbolicLengthX;
    }

    float GetY()
    {
        return Mathf.Cos(theta) * amlitude;
    }

    return new Vector3(-GetCoordinate(direction.x), GetY(), -GetCoordinate(direction.z));
}

private Vector3 Displacement(Vector3 position, float timing)
{
    Vector3 gerstnerWave1 = GerstnerWave(position, direction1, amlitude1, timing);
    Vector3 gerstnerWave2 = GerstnerWave(position, direction2, amlitude2, timing);
    Vector3 gerstnerWave3 = GerstnerWave(position, direction3, amlitude3, timing);
    Vector3 gerstnerWave4 = GerstnerWave(position, direction4, amlitude4, timing);

    return gerstnerWave1 + gerstnerWave2 + gerstnerWave3 + gerstnerWave4;
}

public Vector3 GetResultVector(Vector3 position, float timing)
{
    Vector3 result = Displacement(position, timing);
    return result;
}

根据目前的写法,你的回答不够清晰。请编辑以添加更多细节,帮助其他人理解这如何回答所提出的问题。你可以在帮助中心找到关于如何撰写好回答的更多信息。 - Community
这对我来说很有用,因为它似乎准确地模拟了着色器图公式!我会更新GitHub链接,分享可工作的项目给其他人。 - Magnality

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接