- Home /
Calculate ripple normals
Hey I'm generating wave ripples with this function
public Vector3 GetSurfacePosition(Vector3 point) {
Vector3 ripplePosition = (initPosition * -1) * 2;
float offset = ((point.x * point.x) + (point.z * point.z));
offset += ((point.x * ripplePosition.x) + (point.z * ripplePosition.z));
var value = amplitude * Mathf.Sin(timer * speed + offset * frequency);
if(Vector3.Distance(point, initPosition) < rippleDistance)
return new Vector3(0, (value * amplitude), 0);
return Vector3.zero;
}
It works well for getting the positions of the ripples, but now i would like to also get the normals. I have tried with this function, but it doesn't seem to return the correct values.
public Vector3 GetSurfaceNormal(Vector3 point) {
Vector3 ripplePosition = (initPosition * -1) * 2;
float offset = ((point.x * point.x) + (point.z * point.z));
offset += ((point.x * ripplePosition.x) + (point.z * ripplePosition.z));
float x = -1 * speed * amplitude * Mathf.Cos(-1 * timer * speed + offset * frequency);
float y = 1;
float z = speed * amplitude * Mathf.Cos(-1 * timer * speed + offset * frequency);
if(Vector3.Distance(point, initPosition) < rippleDistance)
return new Vector3(x,y,z).normalized * 2;
return Vector3.zero;
}
Here's a screenshot of what I have right now.
i think you could take two approaches.
1) calculate the normal based on the geometry. this has the advantage of working on more complex surfaces, for example if you had multiple ripples all overlapping. the typical approach for a vertex normal with smooth-shading is to calculate two vectors: VSN = vector from south neighbor vertex to north neighbor vertex, VWE = vector from west neighbor vertex to east neighbor, then your normal is the normalized cross-product of VSN and VW$$anonymous$$ the disadvantage with this approach is that it requires neighbors all the way around, so have to fudge the normals for the vertices on the edges of your mesh.
2) find the Radius and Theta of your input point. use $$anonymous$$athf.atan2() to find theta. then calculate the normal as cos (your radius-based formula above), pretending you're only worrying about normals along the X-axis. Then rotate that normal around the Up Vector (Z or Y) by Theta. the advantage is that it's mathematically capital-C Correct, and works for vertices on the edge as well.
all in all i would suggest taking approach 1.
thanks for your reply. If I where to go with approach 2, how would you do the actual formula? I feel like i'm a little in over my head here, mathematically speaking.
Answer by syltefar · Jan 31, 2017 at 02:43 PM
Does this work, Asger?
public Vector3 GetSurfaceNormal(Vector3 point)
{
Vector3 ripplePosition = (initPosition * -1) * 2;
float a = amplitude * amplitude;
float f = frequency;
float x = point.x;
float z = point.z;
float p = timer * speed;
float rx = ripplePosition.x;
float rz = ripplePosition.z;
Vector3 slopeX = new Vector3(1f, a * Mathf.Cos((x*x + z*z + x*rx + z*rz)*f+p) * (f*2*x + f*rx), 0f);
Vector3 slopeZ = new Vector3(0f, a * Mathf.Cos((x*x + z*z + x*rx + z*rz)*f+p) * (f*2*z + f*rz), 1f);
if (Vector3.Distance(point, initPosition) < rippleDistance)
return Vector3.Cross(slopeX, slopeZ).normalized;
else
return Vector3.right;
}
Elenzils numerical way of computing the normals is probably the best solution for this type of problem, but I like trying to do it formally as well. Here is the derivation:
Given the following input:
a = amplitude^2
f = frequency
p = timer * speed
x, z = point.x, point.z
rx, rz = ripplePosition.x, ripplePosition.z
offset = x^2 + z^2 + x*rx + z*rz
The surface is given by this function:
y(x,z) = a * sin((x^2 + z^2 + x*rx + z*rz) * f + p)
We assume that the following are constant for a given frame:
initPosition, timer, speed, frequency, ripplePosition rippleDistance, amplitude
For a surface y(x,z), the normal is the cross product of the tangent vectors in x and z directions:
normal(x,z) = tangent_x x tangent_z
tangent_x = (1, dy/dx(x,z), 0)
where dy/dx is the partial derivative of y with respect to x. Similarly, we have:
tangent_z = (0, dy/dz(x,z), 1)
So we need to find the partial derivaties of y with respect to x.
We need a few key derivatives:
sin'(x) = cos(x)
f(g(x))' = f'(g(x))g'(x) [chain rule]
First, let's decompose the function y(x) into the composite f(g(x)):
f(x) = a * sin(x)
g(x) = (x^2 + z^2 + x*rx + z*rz) * f + p
The derivative of f(x):
f'(x) = a * cos(x)
For g(x), we remember that everything but x is a constant. We can rewrite g(x) as the polynomial:
g(x) = f*x^2 + f*rx*x + f*z^2 + f*z*rz + p
In the derived function g'(x), all added constants go away:
g'(x) = f*2x + f*rx
We can then use the chain rule to combine the two:
dy/dx = f(g(x))'
= f'(g(x))g'(x)
= a * cos(f*x^2 + f*rx*x + f*z^2 + f*z*rz + p) * (f*2x + f*rx)
= a * cos((x^2 + z^2 + x*rx + z*rz) * f + p) * (f*2x + f*rx)
Similarly, y derived with respect to z:
dy/dz = a * cos((x^2 + z^2 + x*rx + z*rz) * f + p) * (f*2z + f*rz)
nice! i realized my 'use cos() to get the normal' was oversimplifying a bit.
Thanks for that very thorough answer! It works like a charm. You are indeed a mathematical wizzard!
Your answer
Follow this Question
Related Questions
How to import an fbx in Unity correctly? 1 Answer
Change Camera Clipping/Culling Mode 1 Answer
Normals incorrect in view 0 Answers
3D. How to get 1 pixel to equal 1 degree camera FOV 0 Answers