我一直在尝试创建一个浮力脚本,该脚本对一个点的位置进行采样,测试它是否低于某个水平(“水位”),并根据深度在该位置上添加一个力。另外,我致力于在 Shadergraph 中创建一个漂亮的水着色器,并且有一个聪明的想法,即使用简单噪声节点 + 顶点置换来添加波浪。
但是,(我能想到的)使用这些位移值作为浮点数“水位”的唯一方法是用 C# 重写整个节点树,并用它来采样该位置的“水位”。
由于某种原因,最终置换的网格和计算出的位置不同,导致浮力脚本假设“水位”比实际高/低。差异不大,所以我假设 C# Node Graph 或 C# Simple Noise 转换中的某处存在错误。
对吗?如果是这样,我的误解在哪里和什么?如果不是,还有什么地方出了问题?
*如果您需要放大图片,请告诉我!
综合考虑,还是比较简单的。它:
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# 脚本中,发生了几件事。这是我的思考过程:
该脚本还包含并依赖于我从“显示生成的代码”翻译简单噪声节点的最佳尝试,看起来像这样。
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;
}
我和你有相同的前提,事实上使用你对简单噪声节点的翻译来让我的计算工作。
这里有一些我做的不同的事情可能会导致计算差异。
我使用了 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;
}
其余代码(用于简单的噪音功能等)与您的代码相同。