转换+解释Unity Shadergraph到C#的问题和不准确

问题描述 投票:0回答:1

上下文

我一直在尝试创建一个浮力脚本,该脚本对一个点的位置进行采样,测试它是否低于某个水平(“水位”),并根据深度在该位置上添加一个力。另外,我致力于在 Shadergraph 中创建一个漂亮的水着色器,并且有一个聪明的想法,即使用简单噪声节点 + 顶点置换来添加波浪。

但是,(我能想到的)使用这些位移值作为浮点数“水位”的唯一方法是用 C# 重写整个节点树,并用它来采样该位置的“水位”。

问题

由于某种原因,最终置换的网格和计算出的位置不同,导致浮力脚本假设“水位”比实际高/低。差异不大,所以我假设 C# Node Graph 或 C# Simple Noise 转换中的某处存在错误。

对吗?如果是这样,我的误解在哪里和什么?如果不是,还有什么地方出了问题?

方法

节点图

Image of the node graph for the wave vector displacement

*如果您需要放大图片,请告诉我!

综合考虑,还是比较简单的。它:

  1. 将世界位置作为 UV,并对其进行偏移和平铺。
  2. 将 UV 馈送到简单噪声节点,并将噪声乘以强度。
  3. 钳位输出。
  4. 再次重复 1-3 并将两者相加以获得更多细节。
  5. 用组合波值替换顶点位置的Y值。

C# 脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WaveHeightCalculator : MonoBehaviour
{
    // Step 1
    [SerializeField] Material _waterMaterial;

    [Header("Waves")]
    [SerializeField] float _waveTiling;
    [SerializeField] float _waveOffset;
    [SerializeField] float _waveMin;
    [SerializeField] float _waveMax;
    [Header("Small Waves")]
    [SerializeField] float _wavesSmallScale;
    [SerializeField] float _wavesSmallStrength;
    [SerializeField] Vector2 _wavesSmallVelocity;
    [Header("Large Waves")]
    [SerializeField] float _wavesLargeScale;
    [SerializeField] float _wavesLargeStrength;
    [SerializeField] Vector2 _wavesLargeVelocity;
    
    // Step 2
    private void OnValidate()
    {
        _waterMaterial = GetComponent<Renderer>().sharedMaterial;
        SetVariables();
    }
    void SetVariables()
    {
        _waveTiling = _waterMaterial.GetFloat("_Wave_Tiling");
        _waveOffset = _waterMaterial.GetFloat("_Wave_Offset");
        _waveMin = _waterMaterial.GetFloat("_Wave_Min");
        _waveMax = _waterMaterial.GetFloat("_Wave_Max");
        _wavesSmallScale = _waterMaterial.GetFloat("_Waves_Small_Scale");
        _wavesSmallStrength = _waterMaterial.GetFloat("_Waves_Small_Strength");
        _wavesSmallVelocity = _waterMaterial.GetVector("_Waves_Small_Velocity");
        _wavesLargeScale = _waterMaterial.GetFloat("_Waves_Large_Scale");
        _wavesLargeStrength = _waterMaterial.GetFloat("_Waves_Large_Strength");
        _wavesLargeVelocity = _waterMaterial.GetVector("_Waves_Large_Velocity");
    }

    // Step 3
    public float GetWaveHeightAtPosition(Vector3 position)
    {
        Vector2 noiseMapUV;
        noiseMapUV = new Vector2(position.x, position.z) * _waveTiling;
        // Calculate Small Waves
        Vector2 wavesSmallUVOffset = (Time.time / 20) * _wavesSmallVelocity;
        float noiseValueAtUVPlusOffset = UnitySimpleNoiseAtUV(noiseMapUV + wavesSmallUVOffset, _wavesSmallScale);
        float wavesSmall = noiseValueAtUVPlusOffset * _wavesSmallStrength;
        // Calculate Large Waves
        Vector2 wavesLargeUVOffset = (Time.time / 20) * _wavesLargeVelocity;
        noiseValueAtUVPlusOffset = UnitySimpleNoiseAtUV(noiseMapUV + wavesLargeUVOffset, _wavesLargeScale);
        float wavesLarge = noiseValueAtUVPlusOffset * _wavesLargeStrength;
        // Combine
        float waveHeight = wavesSmall + wavesLarge;
        // Clamp
        waveHeight = Mathf.Clamp(waveHeight, _waveMin, _waveMax);
        // Offset
        waveHeight += _waveOffset;
        return waveHeight;
    }

在 C# 脚本中,发生了几件事。这是我的思考过程:

  1. 它将相关的材料属性赋值给成员变量。
  2. 它在 OnValidate() 函数中设置这些变量。
  3. 它使用这些变量来计算波值;相当于“水位”。

该脚本还包含并依赖于我从“显示生成的代码”翻译简单噪声节点的最佳尝试,看起来像这样。

生成代码

inline float Unity_SimpleNoise_RandomValue_float (float2 uv)
{
    float angle = dot(uv, float2(12.9898, 78.233));
    #if defined(SHADER_API_MOBILE) && (defined(SHADER_API_GLES) || defined(SHADER_API_GLES3) || defined(SHADER_API_VULKAN))
        // 'sin()' has bad precision on Mali GPUs for inputs > 10000
        angle = fmod(angle, TWO_PI); // Avoid large inputs to sin()
    #endif
    return frac(sin(angle)*43758.5453);
}

inline float Unity_SimpleNnoise_Interpolate_float (float a, float b, float t)
{
    return (1.0-t)*a + (t*b);
}


inline float Unity_SimpleNoise_ValueNoise_float (float2 uv)
{
    float2 i = floor(uv);
    float2 f = frac(uv);
    f = f * f * (3.0 - 2.0 * f);

    uv = abs(frac(uv) - 0.5);
    float2 c0 = i + float2(0.0, 0.0);
    float2 c1 = i + float2(1.0, 0.0);
    float2 c2 = i + float2(0.0, 1.0);
    float2 c3 = i + float2(1.0, 1.0);
    float r0 = Unity_SimpleNoise_RandomValue_float(c0);
    float r1 = Unity_SimpleNoise_RandomValue_float(c1);
    float r2 = Unity_SimpleNoise_RandomValue_float(c2);
    float r3 = Unity_SimpleNoise_RandomValue_float(c3);

    float bottomOfGrid = Unity_SimpleNnoise_Interpolate_float(r0, r1, f.x);
    float topOfGrid = Unity_SimpleNnoise_Interpolate_float(r2, r3, f.x);
    float t = Unity_SimpleNnoise_Interpolate_float(bottomOfGrid, topOfGrid, f.y);
    return t;
}
void Unity_SimpleNoise_float(float2 UV, float Scale, out float Out)
{
    float t = 0.0;

    float freq = pow(2.0, float(0));
    float amp = pow(0.5, float(3-0));
    t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

    freq = pow(2.0, float(1));
    amp = pow(0.5, float(3-1));
    t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

    freq = pow(2.0, float(2));
    amp = pow(0.5, float(3-2));
    t += Unity_SimpleNoise_ValueNoise_float(float2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

    Out = t;
}

    /* WARNING: $splice Could not find named fragment 'CustomInterpolatorPreVertex' */

    // Graph Vertex
    // GraphVertex: <None>

    /* WARNING: $splice Could not find named fragment 'CustomInterpolatorPreSurface' */

    // Graph Pixel
    struct SurfaceDescription
{
float4 Out;
};

翻译代码

    float float_frac(float x) { return x - Mathf.Floor(x);}
    Vector2 frac(Vector2 x) { return x - new Vector2(Mathf.Floor(x.x), Mathf.Floor(x.y));}
    float sin(float x) { return Mathf.Sin(x);}
    float dot(Vector2 a, Vector2 b) { return a.x * b.x + a.y * b.y;}
    float float_floor(float x) { return Mathf.Floor(x);}
    Vector2 floor(Vector2 x) { return new Vector2(Mathf.Floor(x.x), Mathf.Floor(x.y));}
    float float_abs(float x) { return Mathf.Abs(x);}
    Vector2 abs(Vector2 x) { return new Vector2(Mathf.Abs(x.x), Mathf.Abs(x.y));}
    float pow (float x, float y) { return Mathf.Pow(x, y);}

    float Unity_SimpleNoise_RandomValue_float (Vector2 uv)
    {
        float angle = dot(uv, new Vector2(12.9898f, 78.233f));
        return float_frac(sin(angle) * 43758.5453f);
    }
    float Unity_SimpleNnoise_Interpolate_float (float a, float b, float t)
    {
        return (1.0f - t) * a + (t * b);
    }
    float Unity_SimpleNoise_ValueNoise_float (Vector2 uv)
    {
        Vector2 i = floor(uv);
        Vector2 f = frac(uv);
        f = (f * f) * (new Vector2 (3.0f, 3.0f) - new Vector2(2.0f, 2.0f) * f);

        uv = abs(frac(uv) - new Vector2 (0.5f, 0.5f));
        Vector2 c0 = i + new Vector2(0.0f, 0.0f);
        Vector2 c1 = i + new Vector2(1.0f, 0.0f);
        Vector2 c2 = i + new Vector2(0.0f, 1.0f);
        Vector2 c3 = i + new Vector2(1.0f, 1.0f);
        float r0 = Unity_SimpleNoise_RandomValue_float(c0);
        float r1 = Unity_SimpleNoise_RandomValue_float(c1);
        float r2 = Unity_SimpleNoise_RandomValue_float(c2);
        float r3 = Unity_SimpleNoise_RandomValue_float(c3);

        float bottomOfGrid = Unity_SimpleNnoise_Interpolate_float(r0, r1, f.x);
        float topOfGrid = Unity_SimpleNnoise_Interpolate_float(r2, r3, f.x);
        float t = Unity_SimpleNnoise_Interpolate_float(bottomOfGrid, topOfGrid, f.y);
        return t;
    }
    float UnitySimpleNoiseAtUV(Vector2 UV, float Scale)
    {
        float t = 0.0f;

        float freq = pow(2.0f, 0);
        float amp = pow(0.5f, 3-0);
        t += Unity_SimpleNoise_ValueNoise_float(new Vector2(UV.x*Scale/freq, UV.y*Scale/freq))*amp;

        freq = pow(2.0f, 1);
        amp = pow(0.5f, 3-1);
        t += Unity_SimpleNoise_ValueNoise_float(new Vector2(UV.x * Scale / freq, UV.y * Scale / freq)) * amp;

        freq = pow(2.0f, 2);
        amp = pow(0.5f, 3-2);
        t += Unity_SimpleNoise_ValueNoise_float(new Vector2(UV.x * Scale / freq, UV.y * Scale / freq)) * amp;

        return t;
    }
unity3d noise code-translation shader-graph shaderlab
1个回答
0
投票

我和你有相同的前提,事实上使用你对简单噪声节点的翻译来让我的计算工作。

这里有一些我做的不同的事情可能会导致计算差异。

我使用了 Time.timeSinceLevelLoad 而不是 Time.time,因为这是着色器图显然也使用的那个(参见 here)。使用 Time.time 可能会导致帧之间的某些值不同,因此可能会导致某些差异。

我还在我得到的最终 Y 坐标上使用了 TransformPoint,以便使用我的水平面顶点的世界坐标而不是它的局部坐标。当我的平面位于 (0,0,0) 时,着色器导致平面中波浪的局部 y 值约为 y = 0.6,但在世界空间中,y 值实际上约为 y=5.9。所以我需要这种转变,否则它会给我水面的局部高度。

这是我的水高函数,使用来自here的着色器。

public float GetWaterHeight(Vector3 position)
{
    //need to do the shader's y position calculation to get the height of the vertex at this position
    var uvInput = waveTile * (waveTileFactor * (new Vector2(position.x, position.z)));
    Debug.Log("Uv Input: " + uvInput);
    var offsetInput = waveSpeed * ((Time.timeSinceLevelLoad * new Vector2(0, 1)) / 20);
    Debug.Log("Offset Input: " + offsetInput + " with time as " + Time.timeSinceLevelLoad);
    var tilingOutput = uvInput * new Vector2(1, 1) + offsetInput;
    var noiseOutput = UnitySimpleNoiseAtUV(tilingOutput, waveScale);
    Debug.Log("Noise gen: " + noiseOutput + " from tilingOutput " + tilingOutput);
    var finalY = waveStrength * noiseOutput;

    //var pixelValue = oceanTex.GetPixelBilinear(transform.position.y, transform.position.z).b;
    return ocean.position.y + ocean.TransformPoint(new Vector3(0, finalY, 0)).y;
}

其余代码(用于简单的噪音功能等)与您的代码相同。

© www.soinside.com 2019 - 2024. All rights reserved.