Recalculating normal with curve (sine wave) - c++

I'm trying to make a water geometry shader that waves using a sine wave.
For each vertex I calculate a sine for x and y, and then offset the vertex to the result * normal.
Because I offset my vertex I have to recalculate my normals, but if I do this using their triangle I get hard edges, while it should be smooth waves.
I understand that somehow I should use that sine function and get a 3D normal from it, but I'm puzzled.
Could someone explain how you get the normal of a sin calculation in 3D space?

You don't give the exact function you use, but it sounds like it's something like:
z = a * sin(b * x) * sin(b * y)
I'll walk through the process, so you should be able to apply the recipe even if your function looks slightly differently. Also, if your wave is not relative to the xy-plane, you can still use the same calculation, and then apply the necessary transformation matrix to the resulting normal.
What we have here is a parametric surface, where the 3 coordinates of a point of the plane are calculated from two parameters. In this case, the parameters are x and y, and the vector describing each point is:
[ x ]
v(x, y) = [ y ]
[ a * sin(b * x) * sin(b * y) ]
The process described here works for any parametric surface, including common geometric shapes. For example for a torus, the two parameters would be two angles. The mathematical tools needed for calculating are basic analysis (derivatives) and some vector geometry (cross product).
As the first step, we calculate the gradient vector for each of the two parameters. These gradient vectors consist of the partial derivatives of each vector component with the corresponding parameter. In the example, the results are:
[ 1 ]
dv(x, y)/dx = [ 0 ]
[ a * b * cos(b * x) * sin(b * y) ]
[ 0 ]
dv(x, y)/dy = [ 1 ]
[ a * b * sin(b * x) * cos(b * y) ]
The normal vector is calculated as the cross product of these two gradient vectors:
[ - a * b * cos(b * x) * sin(b * y) ]
vn = [ - a * b * sin(b * x) * cos(b * y) ]
[ 1 ]
Then you normalize this vector, and vn / |vn| is your normal vector.

You need to get the derivative in x and y so you can construct 2 vectors 1,0,x' and 0,1,y' then you take the cross product and normalize. That will be the normal.

Related

Finding Perpendicular Points Given An Angle With Piece-wise Hermite Splines

I am given a Hermite spline from which I want to create another spline with every point on that spline being exactly x distance away.
Here's an example of what I want to do:
.
I can find every derivative and point on the original spline. I also know all the coefficients of each polynomial.
Here's the code that I've came up with that does this for every control point of the original spline. Where controlPath[i] is a vector of control points that makeup the spline, and Point is a struct representing a 2D point with its facing angle.
double x, y, a;
a = controlPath[i].Angle + 90;
x = x * cosf(a * (PI / 180)) + controlPath[i].X;
y = x * sinf(a * (PI / 180)) + controlPath[i].Y;
Point l(x, y, a - 90);
a = controlPath[i].Angle - 90;
x = x * cosf(a * (PI / 180)) + controlPath[i].X;
y = x * sinf(a * (PI / 180)) + controlPath[i].Y;
Point r(x, y, a + 90);
This method work to an extent, but its results are subpar.
Result of this method using input:
The inaccuracy is not good. How do I confront this issue?
If you build normals of given length in every point of Hermite spline and connect endpoint of these normals, resulting curve (so-called parallel curve) is not Hermit spline in general case. The same is true for Bezier curve and the most of pther curve (only circle arc generates self-similar curve and some exotic curves).
So to generate reliable result, it is worth to subdivide curve into small pieces, build normals in all intermediate points and generate smooth piecewise splines through "parallel points"
Also note doubtful using x in the right part of formulas - should be some distance.
Also you don't need to calculate sin/cos twice
double x, y, a, d, c, s;
a = controlPath[i].Angle + 90;
c = d * cosf(a * (PI / 180));
s = d * sinf(a * (PI / 180))
x = c + controlPath[i].X;
y = s + controlPath[i].Y;
Point l(x, y, controlPath[i].Angle);
x = -c + controlPath[i].X;
y = -s + controlPath[i].Y;
Point l(x, y, controlPath[i].Angle);

Drawing a circle with cos() sin(), no repeat pixel, no gaps?

I'm interested in drawing a circle of a vary radius using sin() and cos() functions.
Is there a golden rule to increment the radians so that there isn't multiple plots to the same location and no gaps in the circle drawn on a pixel based display?
x = cos(r) * radius
y = sin(r) * radius
r = r + s
My guess would be that s is something to do with dividing 2 × PI by the a number derived from the radius?
I'm sure this is either really simple or impossible due to the limitations of floating point calculations.
Thanks for your time
Anthony
The length of arc is simply s = r * delta_fi where r is the radius of the circle, fi is the angle and delta_fi is the change of the angle.
The projection of this arc to x-axis is delta_x = s * sin(fi) and to y-axis it is delta_y = s * cos(fi)
You want such delta_fi that either delta_x or delta_y is 1.
Obviously, the problem is symmetrical and we can solve it for fi from -45° to 45° and for delta y and then apply the same solution in other quadrants. We have:
r * delta_fi * cos(fi) = 1
Hence:
delta_fi = 1/cos(fi)/r
The coordinates of a circle can indeed be completely defined using the trigonometric functions sine and cosine:
x = cos(angle)
y = sin(angle)
If the radius is any other value than 1 (which happens to define the unit circle), the underlying principles of trigonometric functions still apply and, therefore, the following equations can be derived:
x = cos(angle) * radius
y = sin(angle) * radius
To implement this in Python (with the kind help of Numpy) all that is necessary in addition to what we have already defined is a suitable vector (or 1-dimensional array) for the angle, which will be evaluated by the function x and y.
import numpy as np
r = 2 # An arbitrary value for the radius
angle = np.linspace(0, 2*np.pi, 1000) # A vector covering all angles from
# 0 to 2*pi (the full circle in radians)
# with an arbitrary number of
# elements, 1000 in this example
x = np.cos(angle)*r
y = np.sin(angle)*r
On plotting this circle don't forget to adjust the size of the figure to a square, otherwise the circle will be distorted.
import matplotlib.pyplot as plt
plt.figure(figsize=(3, 3))
plt.plot(x, y)

Polar Rose 2D offset

I'm having some trouble trying to plot a polar rose with a offset C of the equation r(theta) = cos(k*theta) + C.
I'm trying to plot this polar rose: http://en.wikipedia.org/wiki/Polar_coordinate_system#/media/File:Cartesian_to_polar.gif
The polar equation can be:
r(theta) = cos(k * theta)
or
r(theta) = sin(k * theta)
The equation of the polar rose I want to draw is:
r(theta) = 2 + sin(6 * theta)
Ok, and the parametric equations will be:
x = C + sin(k * theta) * cos(theta)
y = C + sin(k * theta) * sin(theta)
In my Canvas(drawing area), my origin is not at the center of the screen, so I need to translate the rose to it. Ok, no big deal. Another point is that I need to scale the rose for it to be visible or it will be too small, but still no problem, this explains the: 100*. Here is my code, it is on C++ btw:
for ( float t = 0; t < PI_2; t+= 0.01 )
{
r = Origin.get_x() + 100*(2+(sin(6*t) * cos(t)));
h = Origin.get_y() + 100*(2+(sin(6*t) * sin(t)));
point(r,h);
}
I know that I'm doing it wrong, because when I add the +2 which should be the C constant is not working the way I want to, It's just translating more and drawing a polar rose without an offset. How do I prevent the "extra translation" and draw it properly?
x = r cos(theta), y = r sin(theta) so your parametric equations should be x(theta) = C * cos(theta) + sin(k*theta) * cos(theta) and y(theta) = C * sin(theta) + sin(k*theta) * sin(theta). You just forgot to multiply C by cos(theta) and by sin(theta) respectively.

Finding Y given X on a Cubic Bezier Curve?

I need a method that allows me to find the Y-coordinate on a Cubic Bezier Curve, given an x-coordinate.
I've come across lots of places telling me to treat it as a cubic function then attempt to find the roots, which I understand. HOWEVER the equation for a Cubic Bezier curve is (for x-coords):
X(t) = (1-t)^3 * X0 + 3*(1-t)^2 * t * X1 + 3*(1-t) * t^2 * X2 + t^3 * X3
What confuses me is the addition of the (1-t) values. For instance, if I fill in the X values with some random numbers:
400 = (1-t)^3 * 100 + 3*(1-t)^2 * t * 600 + 3*(1-t) * t^2 * 800 + t^3 * 800
then rearrange it:
800t^3 + 3*(1-t)*800t^2 + 3*(1-t)^2*600t + (1-t)^3*100 -400 = 0
I still don't know the value of the (1-t) coefficients. How I am I supposed to solve the equation when (1-t) is still unknown?
There are three common ways of expressing a cubic bezier curve.
First x as a function of t
x(t) = sum( f_i(t) x_i )
= (1-t)^3 * x0 + 3*(1-t)^2 * t * x1 + 3*(1-t) * t^2 * x2 + t^3 * x3
Secondly y as a function of x
y(x) = sum( f_i(x) a_i )
= (1-x)^3 * y0 + 3*(1-x)^2 * x * y1 + 3*(1-x) * x^2 * y2 + x^3 * y3
These first two are mathematically the same, just using different names for the variables.
Judging by your description "find the Y-coordinate on a Cubic Bezier Curve, given an x-coordinate on it." I'm guessing that you've got a question using the second equation are are trying to rearrange the first equation to help you solve it, where as you should be using the second equation. If thats the case, then no rearranging or solving is required - just plug your x value in and you have the solution.
Its possible that you have an equation of the third kind case, which is the ugly and hard case.
This is both the x and y parameters are cubic Beziers of a third variable t.
x(t) = sum( f_i(t) x_i )
y(t) = sum( f_i(t) y_i )
If this is your case. Let me know and I can detail what you need to do to solve it.
I think this is a fair CS question, so I'm going to attempt to show how I solved this. Note that a given x may have more than 1 y value associated with it. In the case where I needed this, that was guaranteed not to be the case, so you'll have to figure out how to determine which one you want.
I iterated over t generating an array of x and y values. I did it at a fairly high resolution for my purposes. (I was looking to generate an 8-bit look-up table, so I used ~1000 points.) I just plugged t into the bezier equation for the next x and the next y coordinates to store in the array. Once I had the entire thing generated, I scanned through the array to find the 2 nearest x values. (Or if there was an exact match, used that.) I then did a linear interpolation on that very small line segment to get the y-value I needed.
Developing the expression further should get you rid of the (1 - t) factors
If you run:
expand(800*t^3 + 3*(1-t)*800*t^2 + 3*(1-t)^2*600*t + (1-t)^3*100 -400 = 0);
In either wxMaxima or Maple (you have to add the parameter t in this one though), you get:
100*t^3 - 900*t^2 + 1500*t - 300 = 0
Solve the new cubic equation for t (you can use the cubic equation formula for that), after you got t, you can find x doing:
x = (x4 - x0) * t (asuming x4 > x0)
Equation for Bezier curve (getting x value):
Bx = (-t^3 + 3*t^2 - 3*t + 1) * P0x +
(3*t^3 - 6*t^2 + 3*t) * P1x +
(-3*t^3 + 3*t^2) * P2x +
(t^3) * P3x
Rearrange in the form of a cubic of t
0 = (-P0x + 3*P1x - 3*P2x + P3x) * t^3+
(3*P0x - 6*P1x + 3*P2x) * t^2 +
(-3*P0x + 3*P1x) * t +
(P0x) * P3x - Bx
Solve this using the cubic formula to find values for t. There may be multiple real values of t (if your curve crosses the same x point twice). In my case I was dealing with a situation where there was only ever a single y value for any value of x. So I was able to just take the only real root as the value of t.
a = -P0x + 3.0 * P1x - 3.0 * P2x + P3x;
b = 3.0 * P0x - 6.0 * P1x + 3.0 * P2x;
c = -3.0 * P0x + 3.0 * P1x;
d = P0x;
t = CubicFormula(a, b, c, d);
Next put the value of t back into the Bezier curve for y
By = (1-t)^3 * P0x +
3t(1-t)^2 * P1x +
3t^2(1-t) * P2x +
t^3 * P3x
So I've been looking around for some sort of method to allow me to find the Y-coordinate on a Cubic Bezier Curve, given an x-coordinate on it.
Consider a cubic bezier curve between points (0, 0) and (0, 100), with control points at (0, 33) and (0, 66). There are an infinite number of Y's there for a given X. So there's no equation that's going to solve Y given X for an arbitrary cubic bezier.
For a robust solution, you'll likely want to start with De Casteljau's algorithm
Split the curve recursively until individual segments approximate a straight line. You can then detect whether and where these various line segments intercept your x or whether they are vertical line segments whose x corresponds to the x you're looking for (my example above).

Using camera rotation to make quad always appear in front of the camera (c++/opengl)

I'm trying to make a quad appear always in front of the camera, I'm trying to start by aligning it with the camera on the x-z plane and making sure it always faces the camera. I used this code...
float ry = cameraRY+PI_2;
float dis = 12;
float sz = 4;
float x = cameraX-dis*cosf(ry);
float y = cameraY;
float z = cameraZ-dis*sinf(ry)+cosf(ry)*sz;
float x2 = x + sinf(ry)*sz;
float y2 = y + sz;
float z2 = z - cosf(ry)*sz;
glVertex3f(x,y,z);
glVertex3f(x2,y,z2);
glVertex3f(x2,y2,z2);
glVertex3f(x,y2,z);
But it didn't quite look right, it seemed that the quad was rotating around a invisible point that was rotating correctly around the camera. I don't really know how to change it or how to go about doing this, any help appreciated!
Edit: Forgot to mention,
cameraX,cameraY,cameraZ are the camera's x,y,z positions
cameraRX and cameraRY are the camera's x and y rotations (Z rotation is always zero)
Check out this old tutorial on Lighthouse3D. It describes several "billboarding" techniques, which I believe are what you want.
Let P be your model view projection matrix, and c be the center of the quad you are trying to draw. You want to find a pair of vectors u, v that determine the edges of your quad,
Q = [ c-u-v, c-u+v, c-u-v, c+u-v ]
Such that u is pointing directly down in clip coordinates, while v is pointing to the right:
P(u) = (0, s, 0, 0)
P(v) = (s, 0, 0, 0)
Where s is the desired scale of your quad. Suppose that P is written in block diagonal form,
[ M | t ]
P = [-----------]
[ 0 0 1 | 0 ]
Then let m0, m1 be the first two rows of M. Now consider the equation we got for P(u), substituting and simplifying, we get:
[ 0 ]
P(u) ~> M u = [ s ]
[ 0 ]
Which leads to the following solution for u, v:
u = s * m1 / |m1|^2
v = s * m0 / |m0|^2