Texture mapping on a cylinder in C (building a raytracer) - c

This is my first question here so I hope I'm doing it right.
I'm trying to build a raytracer for a school project, and I'd like to add some texture mapping to my basic shapes. I already did it for the sphere and it worked perfectly, this is the algorithm I used (UV mapping):
d = vector3d_normalize(vector3d_sub(hit->point, object->position));
u = 0.5 + atan2(d.z, d.x) / M_PI * 0.5;
v = 0.5 - asin(d.y) / M_PI;
For the cylinder, I can't find any algorithm so I'm trying different things but I can't make it work. Actually, it works fine for a cylinder when it's fixed at (x: 0, y: 0, z: 0) but from the moment I'm moving it in space, the texture looks stretched.
For the code at this moment, it's the following:
d = vector3d_sub(hit->point, vector3d_mult(object->position, ray->direction));
u = 0.5 + atan2(d.z, d.x) / M_PI * 0.5;
v = d.y / M_PI;
v = v - floor(v);
Cylinder in (0, 0, 0):
With translation:
I'm stuck so if you have any idea, it could help me a lot!

Here's some pseudo-code from Jamis' Buck's The Ray Tracer Challenge (highly recommended):
// Based off of pseudocode from Jamis Buck: http://raytracerchallenge.com/bonus/texture-mapping.html
// Input: point on the cylinder. The cylinder is assumed to be of radius 1, centered at the origin and parallel with the y axis.
// Output: (u,v) with u and v both between 0 and 1.
// u will vary from 0 to 1 with the azimuthal angle, counter-clockwise.
// v will vary from 0 to 1 with whole units of y; note that the cylinder will have to be scaled by PI in the y axis to prevent stretching
map_cylinder_point_to_uv(point: (x: float, y: float, z: float)) -> (float, float) {
// compute the azimuthal angle, -PI < theta <= PI
float theta = arctan2(point.x, point.z);
// convert from radians to units
// -0.5 < rawU <= 0.5
float rawU = theta / (2 * Constants.PI)
// convert to correct scale, and flip it so that u increases with theta counter-clockwise
// 0 <= u < 1
float u = 1 - (rawU + 0.5);
// v will vary from 0 to 1 between whole units of y
// Use whatever modulus method or operator your language has to gauarantee a positive value for v.
// It's different for every programming language: https://en.wikipedia.org/wiki/Modulo_operation#In_programming_languages
float v = point.y % 1
return (u, v)
}
Your problem may have to do with the fact that you are placing a square texture on a cylinder, and the length of the texture applied to the side of a cylinder will be some multiple of 2 PI (the circumference of a circle). You may be able to fix your issue by simply scaling the cylinder in the y axis by PI or 2 PI.
If you don't want to be limited to cylinders of particular dimensions, and you have a texture that can be repeated like a checker pattern, then you can scale v to prevent stretching. Replace the v calculation above with this line, instead:
// let v go from 0 to 1 between 2*pi units of y
let v = point.y % (2 * Constants.PI) * 1 / (2 * Constants.PI);
I'll also note that these days there's also a separate stack exchange for computer graphics that might be of more help than SO: https://computergraphics.stackexchange.com/.

Related

How to render 3D axes given pitch, roll, and yaw?

I'm attempting to render a simple axes display like the following, except using simple 2D lines.
I have angle values for the pitch, yaw, and roll that the axes should display. (e.g. PI/2, PI/4, PI*3/2)
I have a function that can render 2D lines given start and end points in 2D space.
How can I properly render a rotated set of axes given the angles? I don't care about z-indexing (so if sometimes the lines incorrectly show up on top of each other, that is OK as long as they point the correct direction).
What I've tried
I know that the start points for all the lines will just be in the center of the axis, and we'll say the center is at (0, 0) and the length of the axes will be 100. That leaves me to calculate the endpoints for each of the 3 axes.
I have defined the X axis to be left-to-right, the Y axis to be up-and-down and the Z axis to be back-forward (i.e. out of the screen).
Pitch is rotation around the X axis, roll is rotation around Z axis, and yaw is rotation around Y axis.
To calculate the X-axis end point I've done:
x = cos(roll) * cos(yaw) * 100;
y = sin(-roll) * 100;
To calculate the Y-axis end point I've done:
x = cos(roll + PI/2) * 100;
y = sin(-roll - PI/2) * sin(PI/2 - pitch) * 100;
To calculate the Z-axis end point I've done:
x = cos(PI/2 - yaw) * 100;
y = sin(PI - pitch) * 100;
It's probably evident that I don't really know what I'm doing. I feel like I am taking a very naive approach when I should be using something more advanced like matrices. If it makes a difference, I'm using C, but psuedocode is fine. Any help would be appreciated.
First, you need to agree on an order of the rotations. In the following, I assume the order x, y, z (pitch, yaw, roll).
The end points are simply the column vectors of the according rotation matrix. Then, you need to project the 3d points onto the 2d screen. It seems as if you use a simple orthogonal projection (removing the z-coordinate). So here are the results:
x1 = 100 (cos yaw * cos roll)
y1 = 100 (cos pitch * sin roll + cos roll * sin pitch * sin yaw)
x2 = 100 (-cos yaw * sin roll)
y2 = 100 (cos pitch * cos roll - sin pitch * sin yaw * sin roll)
x3 = 100 (sin yaw)
y3 = 100 (-cos yaw * sin pitch)

How to convert X and Y screen coordinates to -1.0, 1.0 float? (in C)

I'm trying to convert X and Y screen coordinates to a float range of -1.0, to 1.0.
(-1,-1 being 0,0 and if the resolution was 640x480, 1,1 would be 640,480. 0,0 would be 320,240... the center.)
How would I approach this? I made several futile attempts, and I'm not exactly mathematically inclined.
Here is some C code
void convert(int X, int Y)
{
float newx = 2*(X-320.0f)/640.0f;
float newy = 2*(Y-240.0f)/480.0f;
printf("New x = %f, New y = %f", newx, newy);
}
EDIT: Added the f suffix to ensure we do not do integer math !
In the X direction:
Screen coordinate 0 corresponds to -1.0.
Screen coordinate 640 corresponds to 1.0.
You can convert that to an equation:
Given fX in floating point coordinates, the screen coordinate sX is:
sX = 640*(fX + 1.0)/2.0
or
sX = 320*(fX + 1.0)
Similarly, given fY in floating point coordinates, the screen coordinate sY is:
sY = 480*(fY + 1.0)/2.0
or
sY = 240*(fY + 1.0)
The inverse of that:
Given sX in screen coordinates, fX in real coordinates is:
fX = (sX/320 - 1.0)
Given sY in screen coordinates, fY in real coordinates is:
fY = (sY/240 - 1.0)
When you convert that to code, make sure the last two equations have a 1.0. Otherwise, you'll lose accuracy due to integer division.
fX = (1.0*sX/320 - 1.0)
fY = (1.0*sY/240 - 1.0)

C program, getting cartesian coordinate from degrees of rotation

I have been working for too long on the business applications it seems... And graduated too long ago perhaps :) Recently i have been tasked writing a small simulation of robotics using C (although this question is more math/algorithm than C) where i have two units (tank-bots) starting at an X and Y coordinate on a playfield.
Now there are keys on a panel to rotate them, and a key to move them forwards. I am now facing a minor brain meltdown on the translation from degrees of rotation to the next X,Y coord to move to in the cartesian playfield.
Due to limitations in HW only fixed point is available for the actual movement, but calculations can be done by float values.
I wrote the following code just from memory just now:
/* Recalculate to radians */
int radians;
/* Use sin and cos to get a vector (new x and y coords). Translate from polar to
cartesian coordinates */
radians = (int) _tanks[0].rotationAngle * (M_PI / 180);
_tanks[0].x += _tanks[0].speed * cos(radians);
_tanks[0].y += _tanks[0].speed * sin(radians);
radians = (int) _tanks[1].rotationAngle * (M_PI / 180);
_tanks[1].x += _tanks[1].speed * cos(radians);
_tanks[1].y += _tanks[1].speed * sin(radians);
Unfortunately it seems my brain is not really refreshed on polar coordinate math and geometry after all these years on writing pure biz software, so it seems to not work as intended.
For instance if rotationAngle is 180, instead the next x/y is to the left, causing the bot to topple over :)
What i want is a movement scheme similiar to the old Micro Machines games if you remember where the next point would be in front of where the object is facing, so it moves (speed) number of steps there.
Can someone suggest where i'm going wrong here...
Also, if there is a smoother way of doing this in C than the pure mathematical attempt that i just wrote (badly at that), give me a hint.
EDIT:
Tried to add :
float radians;
radians = (45 - _tanks[0].rotationAngle) * (M_PI / 180);
_tanks[0].x += (int) (_tanks[0].speed * cos(radians));
_tanks[0].y += (int) (_tanks[0].speed * sin(radians));
according to answer below as 0 degrees is indeed the positive Y-axis. But this also gives incorrect results. Now movement for 180 degrees starting point is upwards to the left. At 180 degrees should be movement along the negative Y axis.
Some more code:
Init of _tank struct -
tanks[0].acc = 0;
tanks[0].dec = 0;
tanks[0].rotationAngle = 180;
tanks[0].speed = 0;
tanks[0].x = 400;
tanks[0].y = 150;
tanks[0].turretRotationAngle = 180;
The rotation degree is just a number (fixed integer), and i wrap it around as according to the circle # 360 degrees, like so -
switch(direction) {
case 0:
tank->rotationAngle -= degrees;
if(tank->rotationAngle < 1) {
tank->rotationAngle = 360;
}
break;
case 1:
tank->rotationAngle += degrees;
if(tank->rotationAngle > 360) {
tank->rotationAngle = 0;
}
break;
}
One key for rotating clockwise, one for counter clockwise.
The rotation works, but the movement does not, as described...
Results of debug run:
Initial state (no movement due to 0 speed) -
radians = -2.3561945
x = 400
y = 150
speed = 0
After movement (speed > 0) -
radians = -2.3561945 (the same since i only press the move button)
x = 399
y = 149
speed = 2
This seems odd. The X coord should not change at all if the rotation is 180 degrees from the initial origin right? Only the Y should change, and in the opposite direction. I would translate the change instead to if speed is 2, vector length should be 2 so change would be 2 steps in the direction the object is facing, so y = y + 2 and x = x + 0 for 180 degree rotation on the object?
I feel like i'm getting there :)
Further EDIT:
Seems to be ALMOST correct along the lines of what i need for the playfield if i do this:
radians = (_tanks[0].rotationAngle - 90) * (M_PI / 180);
Note -90...
Still when speed is lowered it seems to glitch but at least it moves in the right direction.
For instance if rotationAngle is 180, instead the next x/y is to the left, causing the bot to topple over :)
Yes, that is what your code does: your code is correct, aside from the int radians issue that user3386109 mentions above, provided that 0° is the positive x-axis, 90° is the positive y-axis, 180° is the negative x-axis, 270° (or -90°) is the negative y-axis.
I'm guessing that instead, you want 0° to be the positive y-axis? And — do you want 90° to be the positive x-axis (so your angles proceed clockwise around the circle), or the negative x-axis (so they proceed counterclockwise)? For the former (clockwise) case, just change _tanks[...].rotationAngle to (90 - _tanks[...].rotationAngle) (to "flip" around the 45° line); for the latter (counterclockwise) case, just change it to (_tanks[...].rotationAngle + 90) (to "rotate" it 90° about the origin).
#ruakh and #user3386109 well discuss the issues about angle units and phase.
In addtion, for a "smoother way of doing this in C" also consider:
Use round(), else code will introduce a bias. (Assuming _tanks[1].x is some integer)
double radians = _tanks[0].rotationAngle * (M_PI / 180);
_tanks[0].x += (int) round(_tanks[0].speed * cos(radians));
Use float rather than double as the extra precision with its longer calculation time are not needed.
float radians = _tanks[0].rotationAngle * (float)((M_PI / 180));
_tanks[0].x += (int) roundf(_tanks[0].speed * cosf(radians)); // note function names
If processing time is limited, an integer look-up-table could be used with 360 int scaled sine and cosine values rather than all the floating point math.
_tanks[0].x += (_tanks[0].speed * LUT_cos[_tanks[0].rotationAngle])/scale;

Animate a sine wave from the center

I want to animate a sine wave as if it's generated from the center and moves outwards (to the left and right).
I started with this library: SISinusWaveView and made some adjustments. Currently I have this naive code for calculating the Y position of the curve depending on the X, phase and frequency:
float width = view.width; float mid = width / 2;
float adjustedX = x < mid ? x : width - x;
float y = maxAmplitude * sinf(2 * M_PI *(adjustedX / mid) * frequency + phase);
// phase increases every frame
Quite obviously, this causes a sharp angle at the middle of the sine wave, as seen below:
I would like to make it so the horizontal center of the animation is a smooth curve rather than a sharp angle, while keeping the animation horizontally symmetric. How would I approach this? Any mathematical insight for achieving this is appreciated.
EDIT
I tried to implement #TheBigH suggestion, but the parabola section is not seamlessly continuing the sine curve. Here's what I tried (implemented on Mathematica for quick visualization):
amp = 10;
freq = 1.5;
phase = 0.5;
Z = 1;
plotSine = Plot[amp*Sin[freq*x + phase], {x, Z, 2 Pi}];
aPara = amp*freq*Cos[phase]/(2 Z);
bPara = 0;
cPara = amp*Sin[c] - aPara*Z^2;
plotPara =
Plot[aPara*x^2 + bPara*x + cPara, {x, -Z, Z },
PlotRange -> {{-Z, Z}, {-20, 20}}];
Show[plotPara, plotSine, PlotRange -> {{-2 Pi, 2 Pi}, {-20, 20}}
Which results in this:
Changing the sign of the parabola didn't quite work either:
EDIT 2
I see now that the problem was assuming s(0) = p(Z) and s'(0) = p'(Z); instead of s(z) = p(Z) and s'(Z) = p'(Z). Moving the sine wave so it would start exactly at the end of the parabole would fix the problem, but it's more convenient solving the parabola such as s(z) = p(Z) and s'(Z) = p'(Z) as that would simplify the implementation. How to do this?
EDIT 3
See this math.stackexchange.com answer for the final solution.
Since OP has asked me to elaborate, here's my take as an answer.
Most generally, you're plotting the function s(x) = a sin(bx + c), where a, b and c come from the original problem. Later we will shift the sine curve by some offset Z but I'll leave it out of the sine curve for now as it will complicate the mathematics.
The new parabolic section will have equation p(x) = Ax^2 + Bx + C (A, B and C are different variables than a,b and c).
You want the two equations to join up cleanly, which means s(0) = p(Z). You also want the slopes to join up nicely so that there are no corners. That means you also need s'(0) = p'(Z). Also, since the parabola is centered about the origin, B = 0.
Thus you have two simultaneous equations for A, C given that you already know a, b, c and Z
a sin( c ) = A Z^2 + C
ab cos( c ) = 2AZ
or
A = ab cos( c ) / (2Z)
C = a sin (c) - A Z^2
This gives you the equation of the parabola, which you plot between -Z and Z. Then all you have to do is plot the sine curves, now adding in that offset. Let me know if any of this is unclear.
EDIT: I see there is a vertical shift of the sine wave as well. This does not pose any problems; just leave it out to begin with and add it to the parabola and sine waves at the end.

Need help deciphering a formula for Projectile Motion

I need to implement a bit of AI to figure out how to hit a target with projectile motion.
I found this over at wikipedia:
Angle required to hit coordinate
Which looks like just the thing I need, especially since I have the added problem launching the projectile from above zero height. However, my maths skills aren't great so it all looks like complete nonsense to me and I have no idea how to translate any of it into code.
If anyone can break this down into something I can understand with basic operators (+ - * %) and functions (sin, cos, sqrt etc.), I'd really appreciate it.
If xTarget/yTarget is the position of the target, xProj/yProj the initial position of the projectile and v the initial velocity of the projectile (in meters per second), then the formula would translate to the following pseudo code:
x = xTarget - xProj;
y = yTarget - yProj;
g = 9.8;
tmp = pow(v, 4) - g * (g * pow(x, 2) + 2 * y * pow(v, 2));
if tmp < 0
// no solution
else if x == 0
angle1 = pi/2;
if y < 0
angle2 = -pi/2;
else
angle2 = pi/2;
end
else
angle1 = atan((pow(v, 2) + sqrt(tmp)) / (g * x));
angle2 = atan((pow(v, 2) - sqrt(tmp)) / (g * x));
end
g is the graviational constant (~9.8 m/s^2), atan is the arcus tangent function and pow is the power function. The if-statement is necessary, because the formula can have no solution (if the target is not reachable with the initial velocity), one solution (then angle1 == angle2) or two solutions (as can be seen in this animation; this is also why you have the +/- sign in the formula).
In most programming languages you will also find atan2, in which case you should be able to replace some code with
if tmp < 0
// no solution
else
angle1 = atan2(pow(v, 2) + sqrt(tmp), g * x);
angle2 = atan2(pow(v, 2) - sqrt(tmp), g * x);
end
The formula is quite simple, don't worry about the derivation.
x is the horizontal distance away of the target you're trying to hit
y is the vertical distance away of the target you're trying to hit
v is the initial velocity of the launch
g is the acceleration due to gravity (9.81 m/s on earth)
and the formula on that link will give you the angle you need to launch the projectile in order to hit the target on the coordinate (x,y)

Resources