- Home /
Having trouble to getting the height from an object being displaced by a shader
Hi, so I'm currently making a buoyancy script, but I'm having some trouble get the water height. The height is controlled through a shader graph which looks like this...
My current code for getting the height is the following...
public float HeightAtPosition(Vector3 position)
{
Vector2 uv = (new Vector2(ocean.position.x + position.x,
ocean.position.z + position.z) * waveDirection) * waveTiling;
Vector2 tiling = new Vector2(waveTiling, waveTiling);
Vector2 offset = Vector2.zero;
offset.x = (Time.time * waveDirection.x / 20.0f * waveSpeed);
offset.y = (Time.time * waveDirection.y / 20.0f * waveSpeed);
uv = TilingAndOffset(uv, tiling, offset);
float noise = SimpleNoise(uv, waveScale);
return ocean.position.y + noise * waveStrength;
}
And the code for the simple noise node is from this unity document. I've tried looking up how different people did it, but most of them use a height map texture, which I'm not doing. Also, I'm not the best at translating shader math over to C#, so if somebody's a wizard at that and have some good tips and tricks for how to think when approaching a problem like this.
Thanks beforehand :)
Pass your noise into your shader that way you have a reference on your cpu and gpu.
Answer by BastianUrbach · Mar 07 at 10:41 AM
This is how you can translate ShaderGraphs to C# systematically (this is more or less how ShaderGraph generates HLSL code):
For each node type that you use, make a function with the corresponding input and output parameters. For most nodes, you can look up how they work in the ShaderGraph documentation. Note that you sometimes need multiple overloads (e.g. for float and vector inputs).
Turn each node in the graph into a line like this:
NodeName(Input1, Input2, out var Output);
. When an output is connected to an input, use the same variable name for them.Sort these lines so that all outputs are created before they are used.
using UnityEngine;
public class WaveDisplacement {
public float WaveStrength;
public float WaveSpeed;
public float WaveScale;
public Vector2 WaveTiling;
public Vector2 WaveDirection;
public float GetHeight(Vector2 Position) {
Multiply(Time.time, WaveDirection, out var h);
Divide(h, 20, out var g);
Multiply(g, WaveSpeed, out var e);
Multiply(WaveDirection, Position, out var f);
Multiply(f, WaveTiling, out var d);
TilingAndOffset(d, WaveTiling, e, out var c);
SimpleNoise(c, WaveScale, out var b);
Multiply(WaveStrength, b, out var a);
return a;
}
void TilingAndOffset(Vector2 UV, Vector2 Tiling, Vector2 Offset, out Vector2 Out) {
Out = UV * Tiling + Offset;
}
void Multiply(float A, float B, out float Out) {
Out = A * B;
}
void Multiply(Vector2 A, float B, out Vector2 Out) {
Out = new Vector2(A.x * B, A.y * B);
}
void Multiply(float A, Vector2 B, out Vector2 Out) {
Out = new Vector2(A * B.x, A * B.y);
}
void Multiply(Vector2 A, Vector2 B, out Vector2 Out) {
Out = new Vector2(A.x * B.x, A.y * B.y);
}
void Divide(Vector2 A, float B, out Vector2 Out) {
Out = new Vector2(A.x / B, A.y / B);
}
void SimpleNoise(Vector2 UV, float Scale, out float Out) {
// Put SimpleNoise implementation here
}
}
First of all, thanks for the response, but sadly it still doesn't work. I tried to make a simple script that gets the water height at (0, 0) and it jumps all over the place... at one point it can give something like 0.03 and at the next Debug.log it says the height is 10, which doesn't seem right. I suspect it might be something with the noise generation, since the noise is random (as far as I know) it could be that there's no way make the buoyancy script and the shader have the same noise. Or maybe that's not the case since it's "simple" noise.
Yes it's probably something with the noise. It works fine for me. The randomness isn't a problem because even though it looks random, it's still deterministic. This is how I translated it to C#:
float Frac(float f) {
return f - Floor(f);
}
float unity_noise_randomValue(Vector2 uv) {
return Frac(Sin(Dot(uv, new Vector2(12.9898f, 78.233f))) * 43758.5453f);
}
float unity_valueNoise (Vector2 uv) {
Vector2 i = new Vector2(Floor(uv.x), Floor(uv.y));
Vector2 f = new Vector2(Frac(uv.x), Frac(uv.y));
f = f * f * (Vector2.one * 3f - 2f * f);
Vector2 c0 = i + new Vector2(0f, 0f);
Vector2 c1 = i + new Vector2(1f, 0f);
Vector2 c2 = i + new Vector2(0f, 1f);
Vector2 c3 = i + new Vector2(1f, 1f);
float r0 = unity_noise_randomValue(c0);
float r1 = unity_noise_randomValue(c1);
float r2 = unity_noise_randomValue(c2);
float r3 = unity_noise_randomValue(c3);
float bottomOfGrid = LerpUnclamped(r0, r1, f.x);
float topOfGrid = LerpUnclamped(r2, r3, f.x);
float t = LerpUnclamped(bottomOfGrid, topOfGrid, f.y);
return t;
}
void SimpleNoise(Vector2 UV, float Scale, out float Out) {
float t = 0f;
float freq = Pow(2f, 0f);
float amp = Pow(0.5f, 3f - 0f);
t += unity_valueNoise(UV * Scale / freq) * amp;
freq = Pow(2f, 1);
amp = Pow(0.5f, 3f - 1f);
t += unity_valueNoise(UV * Scale / freq) * amp;
freq = Pow(2f, 2f);
amp = Pow(0.5f, 3f - 2f);
t += unity_valueNoise(UV * Scale / freq) * amp;
Out = t;
}
As a side note, ShaderGraph's implementation is wild. I translated it faithfully to get the exact same result but I facepalmed hard a couple times.
They randomly made some functions inline explicitly. All HLSL functions are inline by default.
What on earth is this:
float2(UV.x*Scale/freq, UV.y*Scale/freq)
? Just writeUV * Scale / freq
. Should even be faster unless the compiler is smart enough to vectorize their version.Why implement their own linear interpolation? lerp is an intrinsic function and possibly faster on some hardware.
The decision to use sine for randomness is a bit odd to say the least. Computing twelve trigonometric functions for a little bit of noise seems excessive.
Matter of taste but imo just writing the results of those constant pow() calls would be much easier to read. Everyone knows what 1, 2, 4 is.
This line:
uv = abs(frac(uv) - 0.5);
is pointless. uv is never used again.
I see what you mean, after looking through the original noise code some it if definitely seemed weird at best, maybe I should have thought about it instead of just writing it xD.
Anyways thanks again for being so helpful... I've been struggling with this for weeks now, and now I can finally see an end to this problem. So, thank you so much.
Edit: Everything works as intended :)
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Bouyancy timing issue 0 Answers
How do I alter vertex displacement based on UV coordinates? 0 Answers