Programming Sine and Cosine in C more efficently - c

Im writing a C code programm that calcultates sine and cosine of a given angle without using the Sine and Cosine Functions of the Math.h library.
But the problem I am facing right now is that i can only calculate the sine and cosine of the Angles between -90° - 90° (so the angles in the first and fourth quadrant). The Cosine(100) = Cosine(80) with a negative operator. So my way of thinking would be to just write code that whenever it gets an angle that is greater than 90 and smaller than 270, it should just substract the additional value from 90; so in the case of Cos(240) that would be the same as Cos(90-150) with an inverted operator infront.
How should one go about this, without having to write 180-if statements?
#include <stdio.h>
#include <math.h>
int main() {
double alpha[29];
alpha[0] = 45.00000000;
alpha[1] = 26.56505118;
alpha[2] = 14.03624347;
alpha[3] = 7.12501635;
alpha[4] = 3.57633437;
alpha[5] = 1.78991061;
alpha[6] = 0.89517371;
alpha[7] = 0.44761417;
alpha[8] = 0.22381050;
alpha[9] = 0.11190568;
alpha[10] = 0.05595289;
alpha[11] = 0.02797645;
alpha[12] = 0.01398823;
alpha[13] = 0.00699411;
alpha[14] = 0.00349706;
alpha[15] = 0.00174853;
alpha[16] = 0.00087426;
alpha[17] = 0.00043713;
alpha[18] = 0.00021857;
alpha[19] = 0.00010928;
alpha[20] = 0.00005464;
alpha[21] = 0.00002732;
alpha[22] = 0.00001366;
alpha[23] = 0.00000683;
alpha[24] = 0.00000342;
alpha[25] = 0.00000171;
alpha[26] = 0.00000085;
alpha[27] = 0.00000043;
alpha[28] = 0.00000021;
double x = 0.60725294;
double y = 0;
double winkel = -150;
double theta = winkel;
double xs;
double ys;
int i = 0;
}
while ( i < 29 ){
printf("This is run number %d with theta = %lf \n", i, theta);
xs = y / pow(2, i);
ys = x / pow(2, i);
if (theta <= 0){
x = x + xs;
y = y - ys;
theta = theta + alpha[i];
} else {
x = x - xs;
y = y + ys;
theta = theta - alpha[i];
};
printf("x = %lf and y = %lf \n \n",x,y);
i++;
}
printf("cosine = %lf\n", x);
printf("sine = %lf\n", y);
return 0;
}

cos(x) = cos(-x)
cos(x) = cos(x%360) if x is in degrees and x is positive
those identities should be sufficient to understand what to do, right?
likewise sin(-x) = -sin(x)
sin(x) = sin(x%360) if x is in degrees and x is positive

Related

How to estimate multiplicity of the polynomial root?

I want to estimate multiplicity of polynomial roots.
I have found some info about it, choosed the test example and made c program
Here should be 4 roots. One simple root and one with multiplicity 3.
#include <complex.h>
#include <math.h>
#include <stdio.h>
complex long double z0 = +1.5; // exact period = 1 stability = 3.000000000000000000 multiplicity = ?
complex long double z1 = -0.5; // exact period = 2 stability = 0.999999999999900080 multiplicity = ?
complex long double c = -0.75; // parameter of the f function
/*
https://en.wikibooks.org/wiki/Fractals/Mathematics/Newton_method
*/
int GiveMultiplicity(const complex long double c, const complex long double z0 , const int pMax){
complex long double z = z0;
complex long double d = 1.0; /* d = first derivative with respect to z */
complex long double e = 0.0; // second derivative with respect to z
complex long double m;
int multiplicity;
int p;
for (p=0; p < pMax; p++){
d = 2*z*d; // f' = first derivative with respect to z */
e = 2*(d*d +z*e); // f'' = second derivative with respect to z
z = z*z +c ; // f = complex quadratic polynomial
}
m = (d*d)/(d*d -z*e);
multiplicity = (int) round(cabs(m));
return multiplicity;
}
int main(){
int m;
m = GiveMultiplicity(c, z0, 1);
printf("m = %d \n", m);
m = GiveMultiplicity(c, z1, 1);
printf("m = %d \n", m);
m = GiveMultiplicity(c, z1, 2);
printf("m = %d \n", m);
return 0;
}
The result is :
m=1
m=1
m=1
Is it good ? Maybe I should simply add the results ?
Good results using symbolic computations are roots: [ 3/2, -1/2] and its multiplicities : [1,3]
Here is a graph of the function f(z)= (z^2-0.75)^2-z-0.75 = z^4-1.5*z^2-z-3/16
Is it possibly to compute the similar values numerically ?
You do this with contour integration, see here. Software is available.
Summary of changes:
evaluate e before evaluating d inside the loop;
when subtracting z0 from z after the loop, you also need to subtract 1 from d to match;
perturb input a small amount from true root location to avoid 0/0 = NaN result: h must be small enough, but not too small...
Complete program:
#include <complex.h>
#include <math.h>
#include <stdio.h>
complex long double h = 1.0e-6; // perturb a little; not too big, not too small
complex long double z0 = +1.5; // exact period = 1 stability = 3.000000000000000000 multiplicity = ?
complex long double z1 = -0.5; // exact period = 2 stability = 0.999999999999900080 multiplicity = ?
complex long double c = -0.75; // parameter of the f function
/*
https://en.wikibooks.org/wiki/Fractals/Mathematics/Newton_method
*/
int GiveMultiplicity(const complex long double c, const complex long double z0, const int pMax){
complex long double z = z0;
complex long double d = 1.0; /* d = first derivative with respect to z */
complex long double e = 0.0; // second derivative with respect to z
complex long double m;
int multiplicity;
int p;
for (p=0; p < pMax; p++){
e = 2*(d*d +z*e); // f'' = second derivative with respect to z
d = 2*z*d; // f' = first derivative with respect to z */
z = z*z +c ; // f = complex quadratic polynomial
}
d = d - 1;
z = z - z0;
m = (d*d)/(d*d -z*e);
multiplicity = (int) round(cabs(m));
return multiplicity;
}
int main(){
int m;
m = GiveMultiplicity(c, z0 + h, 1);
printf("m = %d\n", m);
m = GiveMultiplicity(c, z1 + h, 1);
printf("m = %d\n", m);
m = GiveMultiplicity(c, z1 + h, 2);
printf("m = %d\n", m);
return 0;
}
Output:
m = 1
m = 1
m = 3
I have found one error im my initial program. Function for finding periodic points should be
f^n(z) - z
so
for (p=0; p < pMax; p++){
d = 2*z*d; // f' = first derivative with respect to z */
e = 2*(d*d +z*e); // f'' = second derivative with respect to z
z = z*z +c ; // f = complex quadratic polynomial
}
z = z - z0; // new line
I have choosed the method based on the geometrical notation of the root
It is described in The Fundamental Theorem of Algebra: A Visual Approach by Daniel J. Velleman
I count how many times color chages along a circle around root.
I use carg function which returns the phase angle of z in the interval [−π; π]. So count the sign change of the argument and divide it by 2. This estimates the multiplicity of the root.
It is probly the same method as above, but easier to understand and implement for me.
Here is the image of dynamical plane
before transformation:
and after f(z):
and the code:
// gcc p.c -Wall -lm
// ./a.out
#include <complex.h>
#include <math.h>
#include <stdio.h>
// parameter c of the function fc(z) = z^2+c is c = -0.7500000000000000 ; 0.0000000000000000
const long double pi = 3.1415926535897932384626433832795029L;
long double EPS2 = 1e-18L*1e-18L; //
complex double c = -0.75;
complex double z = 1.5; //-0.5;
//https://stackoverflow.com/questions/1903954/is-there-a-standard-sign-function-signum-sgn-in-c-c
int sign(long double x){
if (x > 0.0) return 1;
if (x < 0.0) return -1;
return 0;
}
int DifferentSign(long double x, long double y){
if (sign(x)!=sign(y)) return 1;
return 0;
}
long double complex Give_z0(long double InternalAngleInTurns, long double radius )
{
//0 <= InternalAngleInTurns <=1
long double a = InternalAngleInTurns *2.0*pi; // from turns to radians
long double Cx, Cy; /* C = Cx+Cy*i */
Cx = radius*cosl(a);
Cy = radius*sinl(a);
return Cx + Cy*I;
}
int GiveMultiplicity(complex long double zr, int pMax){
int s; // number of starting point z0
int sMax = 5*pMax; // it should be greater then 2*pMax
long double t= 0.0; // angle of circle around zr, measured in turns
long double dt = 1.0 / sMax; // t step
long double radius = 0.001; // radius should be smaller then minimal distance between roots
int p;
long double arg_old = 0.0;
long double arg_new = 0.0;
int change = 0;
complex long double z;
complex long double z0;
//complex long double zp;
//
for (s=0; s<sMax; ++s){
z0 = zr + Give_z0(t, radius); // z = point on the circle around root zr
// compute zp = f^p(z)
z = z0;
for (p=0; p < pMax; ++p){z = z*z + c ;} /* complex quadratic polynomial */
// turn (zp-z0)
z = z - z0; // equation for periodic_points of f for period p
arg_new = carg(z);
if (DifferentSign(arg_new, arg_old)) {change+=1;}
arg_old = arg_new;
//printf("z0 = %.16f %.16f zp = %.16f %.16f\n", creal(z0), cimag(z0), creal(zp), cimag(zp));
t += dt; // next angle using globl variable dt
}
return change/2;
}
int main(){
printf("multiplicity = %d\n", GiveMultiplicity(z,2));
return 0;
}
And here is the image of argument of z around root ( it uses carg )

How to display the true range of my parameters on a contour plot in MATLAB?

I want to display the values of a function Z = Z(x,y) in the range (x_min, x_max) and (y_min, y_max) using the contourf function in Matlab 2015a. Here is my code:
N = 20;
x_min = 20;
x_max = 40;
y_min = 40;
y_max = 80;
x = linspace(x_min, x_max, N);
y = linspace(y_min, y_max, N);
[X,Y] = meshgrid(y,x);
Z = X.*Y;
for i = 1:N
for j = 1:N
Z(i, j) = 10*i+j;
end
end
contourf(Z);
colorbar
And this is the plot I get:
How can I show the true range of x and y (20<=x=<40 and 40<=y=<80)?
A look at the fantastic MATLAB documentation reveals that you can supply three arguments to contourf, namely the X, Y, and Z values.
N = 20;
x_min = 20;
x_max = 40;
y_min = 40;
y_max = 80;
x = linspace(x_min, x_max, N);
y = linspace(y_min, y_max, N);
[X,Y] = meshgrid(y,x);
Z = X.*Y;
for i = 1:N
for j = 1:N
Z(i, j) = 10*i+j;
end
end
contourf(X,Y,Z);
colorbar
This will give you properly labelled tick marks:

C program - taylor series_long formula

This formula is from a friend of mine --- and I fixed it up for him. But I can't seem to figure out of how to get the right sine calculations per angle. Can someone please help me in getting the right commands in the sin part?
Code:
#include<stdio.h>
#define PI 3.141592653589
#define NUMBER_OF_TERMS 10
double factorial(double x)
{
double counter, total;
counter=x;
total=x;
while(counter>1)
{
counter--;
total = total * counter;
}
return total;
}
double power(double x, double y)
{
double counter, j;
counter=0;
j = x;
while (counter<(y-1))
{
counter++;
x = x * j;
}
return x;
}
double cosine_func(double radians)
{
int counter, x;
double cosine;
x=0;
counter=0;
cosine = 0;
while(counter<NUMBER_OF_TERMS-1)
{
counter++;
x=x+2;
if(counter%2 == 0)
{
cosine = cosine - (power(radians, x)/factorial(x));
}
else if(counter%2 != 0)
{
cosine = cosine + (power(radians, x)/factorial(x));
}
}
return cosine;
}
double sine_func(double radians)
{
int counter, x;
double sin;
x=0;
counter=0;
sin = 0;
while(counter<NUMBER_OF_TERMS-2)
{
counter++;
x=x+3;
if(counter%2 != 0)
{
sin= sin -(power(radians, x)/factorial(x));
}
else if(counter%2 != 0)
{
sin= sin + (power(radians, x)/factorial(x));
}
}
return sin;
}
main()
{
double counter;
double x_angles;
double x_radians;
double cosine;
double sin;
printf("11526769\n\n");
printf("x(degrees)\t x(radians)\t\t cos x\t\t sin x\t\t");
counter = 0;
x_angles = -185;
while (x_angles<180)
{
counter++;
x_angles+=5;
x_radians=x_angles*PI/180.0;
cosine=1-cosine_func(x_radians);
sin=x_radians-sine_func(x_radians);
printf("%.2lf\t\t%.12lf\t\t%.12lf\t\t%.12lf\n", x_angles, x_radians, cosine, sin);
}
return 0;
}
for(f2=3;f2==1;f2--){
fac2*=f2;
}
f2 is initialized with 3 and then in condition portion it is checked that if f2 is equaled to 1 which returns false.
This loop doesn't run for even a single time. Condition is false for the first time. So
fac2*=f2;
is never executed.
And you did same error for every next loop.
Most of your for loops never run. Check the correct syntax for them. Example:
for(f2=3;f2==1;f2--)
Is never executed because f2 = 3 and 3 != 1
#include<stdio.h>
#define PI 3.141592653589
#define NUMBER_OF_TERMS 10
int
main()
{
double angle_degrees, angle_radians;
double sin, i, i2, i3, i4 , i5, i6, i7, i8, i9, i10;
double fac1, fac2, fac3, fac4, fac5, fac6=1, fac7, fac8, fac9, fac10;
double f1, f2, f3, f4, f5, f6, f7, f8, f9, f10;
double p1, p2, p3, p4, p5, p6, p7, p8, p9, p10;
for(angle_degrees = -180; angle_degrees <= 180; angle_degrees += 5){
p1 = angle_radians;
p2 = angle_radians;
p3 = angle_radians;
p4 = angle_radians;
p5 = angle_radians;
p6 = angle_radians;
p7 = angle_radians;
p8 = angle_radians;
p9 = angle_radians;
p10 = angle_radians;
angle_radians = angle_degrees*PI/180;
for(f1=3;f1>=1;f1--){
fac1*=f1;
}
for(f2=5;f2>=1;f2--){
fac1*=f2;
}
for(f3=7;f3>=1;f3--){
fac1*=f3;
}
for(f4=9;f4>=1;f4--){
fac1*=f4;
}
for(f5=11;f5>=1;f5--){
fac1*=f5;
}
for(f6=13;f6>=1;f6--){
fac1*=f6;
}
for(f7=15;f7>=1;f7--){
fac1*=f7;
}
for(f8=17;f8>=1;f8--){
fac1*=f8;
}
for(f9=19;f9>=1;f9--){
fac1*=f9;
}
for(f10=21;f10>=1;f10--){
fac1*=f10;
}
sin = angle_radians - (pow(p1, 3)/fac1) + (pow(p2, 5)/fac2) - (pow(p3, 7)/fac3) + (pow(p4, 9)/fac4) - (pow(p5, 11)/fac5) + (pow(p6, 13)/fac6) - (pow(p7, 15)/fac7) + (pow(p8, 17)/fac8) -(pow(p9, 19)/fac9) - (pow(p10, 21)/fac10);
printf ("%.2lf\t\t%.12lf\t\t%.12lf\n",
angle_degrees, angle_radians, sin);
}
}
Here's a brand new one --- also I forgot these restrictions involved with it:
1)NO other function aside from main().
2)NO inputs involved.
3) Angles = double data.
4)Output = angles (both degrees and radians), cosine (will work on that once done solving for sine), and sine.
The Taylor series of sin(x) is x-x^3/3!+x^5/5!-... . In your code I assume that p1 should mean x^1=x, p2=x^3, .. and f1=1! f2=3!, .. . Now in the result line you write
sin = angle_radians - (p1/fac1) + (p2/fac2) + //...
which has the series with wrong sign + the first term again.
sin = (p1/fac1) - (p2/fac2) + //...
Also the code is overly complicated. You can collect the result in a single loop:
double p=x;
double f=1;
double res=0;
int k=1;
for (int i=0;i<n;i++){
res+=p/f;
p*=-x*x;
k+=2;
f*=(k-1)*k;
}
Here x is the angle in radians p is the current power with right sign and f the factorial of the term.
As commented by LutzL this would overflow at some point because of the factorial. This version is safer against overflows
double res=0;
double term=x;
int k=1;
for (int i=0;i<n;i++){
res+=term;
k+=2;
term*=-x*x/k/(k-1);
}
Code allowing for arbitrary number of terms
Copying myself from https://stackoverflow.com/a/28227419/3088138
Avoiding the recomputation of recursive functions like powers of the same argument and factorials is always a good idea. Thus a reduction to (a minimal amount of) elementary arithmetic operations looks like this:
public static double sine(int terms, double x) {
double result = 1;
double mxx = -x*x;
double addens = 1;
double temp = 2;
for(int n = 2; n <= terms; n++) {
addens *= mxx/temp++/temp++;
result += addens;
}
return x*result;
}
To study a production quality implementation, you can visit the math library libmath of the basic calculator bc, the gnu version can be read at http://code.metager.de/source/xref/gnu/bc/1.06/bc/libmath.b
It normalizes the argument wrt. pi and then uses the Taylor series of sin.
Horner-like schema for fixed number of terms
If you are restricted to a fixed number of terms in the power series, you can use a Horner like code for its evaluation. As example, take 4 terms, then
x-x^3/3!+x^5/5!-x^7/7! = x*(1-x²/6*(1-x²/20*(1-x²/42)))
This can be put into a loop
res = 1
for( k=num_terms; k-->1;) {
res = 1-x*x*res/(2*k*(2*k+1))
}
res = res*x;
or you can unroll the loop
res = 1-x*x/42;
res = 1-x*x/20*res;
res = 1-x*x/6*res;
res = x*res;
You are free to combine x2=x*x or to rename x=angle_radians.
Code can do better than sin(D2R(angle_degrees))
Step 1: Reduce angle to the range 0 to 90 degrees before converting to radians. Take advantage of the usual trig identities. Then we get exact answers for 90*n degrees.
Step 2: Calculate power series from last term up to the first as summing small terms first is more accurate.
#include<stdio.h>
#define PI 3.1415926535897932384626433832795
#define D2R(a) ((a)*PI/180)
#define NUMBER_OF_TERMS 10
int main(void) {
double angle_degrees;
for (angle_degrees = -360; angle_degrees <= 360; angle_degrees += 30) {
double angle_radians = D2R(angle_degrees);
int sign = 1;
double angle_reduced_degrees = fmod(angle_degrees, 360.0);
if (angle_reduced_degrees < 0) {
angle_reduced_degrees = -angle_reduced_degrees;
sign = -1;
}
int quadrant = (int) (angle_reduced_degrees / 90.0);
angle_reduced_degrees -= quadrant * 90;
switch (quadrant) {
case 0:
break;
case 1:
angle_reduced_degrees = 90 - angle_reduced_degrees;
break;
case 2:
angle_reduced_degrees = -angle_reduced_degrees;
sign = -sign;
break;
case 3:
angle_reduced_degrees = 90 - angle_reduced_degrees;
sign = -sign;
break;
}
double angle_reduced_radians = D2R(angle_reduced_degrees);
long long denom = 1;
for (int i = 1; i < NUMBER_OF_TERMS; i++) {
denom *= -(2 * i + 0) * (2 * i + 1);
}
double aa = angle_reduced_radians * angle_reduced_radians;
double sum = 0;
for (int i = NUMBER_OF_TERMS; --i >= 0;) {
double term = 1.0 / (double) denom;
sum = term + sum * aa;
if (i) denom /= (-(2 * i) * (2 * i + 1));
}
sum *= angle_reduced_radians * sign;
sum *= 1.0; // to get rid of -0.0
printf("sine(%+7.2f degrees %+9f radians) = %+20.17f sin()=%+20.17f\n",
angle_degrees, angle_radians, sum, sin(angle_radians));
}
return 0;
}
Output
// This code math.h sin()
sine(-360.00 degrees -6.283185 radians) = -0.00000000000000000 sin()=+0.00000000000000024
sine(-330.00 degrees -5.759587 radians) = +0.50000000000000000 sin()=+0.49999999999999967
sine(-300.00 degrees -5.235988 radians) = +0.86602540378443871 sin()=+0.86602540378443860
sine(-270.00 degrees -4.712389 radians) = +0.99999999999999989 sin()=+1.00000000000000000
sine(-240.00 degrees -4.188790 radians) = -0.86602540378443871 sin()=+0.86602540378443882
sine(-210.00 degrees -3.665191 radians) = -0.50000000000000000 sin()=+0.50000000000000011
sine(-180.00 degrees -3.141593 radians) = -0.00000000000000000 sin()=-0.00000000000000012
sine(-150.00 degrees -2.617994 radians) = -0.50000000000000000 sin()=-0.49999999999999994
sine(-120.00 degrees -2.094395 radians) = -0.86602540378443871 sin()=-0.86602540378443860
sine( -90.00 degrees -1.570796 radians) = -0.99999999999999989 sin()=-1.00000000000000000
sine( -60.00 degrees -1.047198 radians) = -0.86602540378443871 sin()=-0.86602540378443871
sine( -30.00 degrees -0.523599 radians) = -0.50000000000000000 sin()=-0.50000000000000000
sine( +0.00 degrees +0.000000 radians) = +0.00000000000000000 sin()=+0.00000000000000000
sine( +30.00 degrees +0.523599 radians) = +0.50000000000000000 sin()=+0.50000000000000000
sine( +60.00 degrees +1.047198 radians) = +0.86602540378443871 sin()=+0.86602540378443871
sine( +90.00 degrees +1.570796 radians) = +0.99999999999999989 sin()=+1.00000000000000000
sine(+120.00 degrees +2.094395 radians) = +0.86602540378443871 sin()=+0.86602540378443860
sine(+150.00 degrees +2.617994 radians) = +0.50000000000000000 sin()=+0.49999999999999994
sine(+180.00 degrees +3.141593 radians) = +0.00000000000000000 sin()=+0.00000000000000012
sine(+210.00 degrees +3.665191 radians) = +0.50000000000000000 sin()=-0.50000000000000011
sine(+240.00 degrees +4.188790 radians) = +0.86602540378443871 sin()=-0.86602540378443882
sine(+270.00 degrees +4.712389 radians) = -0.99999999999999989 sin()=-1.00000000000000000
sine(+300.00 degrees +5.235988 radians) = -0.86602540378443871 sin()=-0.86602540378443860
sine(+330.00 degrees +5.759587 radians) = -0.50000000000000000 sin()=-0.49999999999999967
sine(+360.00 degrees +6.283185 radians) = +0.00000000000000000 sin()=-0.00000000000000024
Some minor improvements could be made.

C loop with steps smaller than 1

So I'm wondering, how do I make sure that all steps in a loop are performed if the step size is smaller than 1? Take this loop for instance:
for (float y, x = -1.0; x <= 1.0; x += 0.1) {
y = (4*x*x*x) + (3*x*x) + (5*x) - 10;
printf("x = %.2f, y = %.2f\n", x, y);
}
Output:
x = -1.00, y = -16.00
x = -0.90, y = -14.99
x = -0.80, y = -14.13
x = -0.70, y = -13.40
x = -0.60, y = -12.78
x = -0.50, y = -12.25
x = -0.40, y = -11.78
x = -0.30, y = -11.34
x = -0.20, y = -10.91
x = -0.10, y = -10.47
x = 0.00, y = -10.00
x = 0.10, y = -9.47
x = 0.20, y = -8.85
x = 0.30, y = -8.12
x = 0.40, y = -7.26
x = 0.50, y = -6.25
x = 0.60, y = -5.06
x = 0.70, y = -3.66
x = 0.80, y = -2.03
x = 0.90, y = -0.15
I intend the loop to also run for x = 1, but as you can see it doesn't do that. I've heard that it's not safe to use floats as loop counters, as the float precision isn't exact. The fact that I'm using a float variable as the loop counter is probably the cause of my problem. So what solutions are there to my problem? Thanks in advance for your kind responses!
The problem, as you noted, is that you shouldn't use floats as loop counters. So multiply everything by 10 and use integers:
for (int x = -10; x <= 10; ++x) {
float y = (0.004*x*x*x) + (0.03*x*x) + (0.5*x) - 10;
printf("x = %.2f, y = %.2f\n", 0.1*x, y);
}
Produces:
x = -1.00, y = -16.00
x = -0.90, y = -14.99
x = -0.80, y = -14.13
...
x = 0.90, y = -0.15
x = 1.00, y = 2.00
as expected.
Perhaps even better: logically separate your loop variable from the meaningful stuff.
for(int i = -10; i <= 10; ++i) {
float x = 0.1 * i;
float y = (4*x*x*x) + (3*x*x) + (5*x) - 10;
printf("x = %.2f, y = %.2f\n", x, y);
}

Grainy looking sphere in my ray tracer

I am trying to write a simple ray tracer. The final image should like this: I have read stuff about it and below is what I am doing:
create an empty image (to fill each pixel, via ray tracing)
for each pixel [for each row, each column]
create the equation of the ray emanating from our pixel
trace() ray:
if ray intersects SPHERE
compute local shading (including shadow determination)
return color;
Now, the scene data is like: It sets a gray sphere of radius 1 at (0,0,-3). It sets a white light source at the origin.
2
amb: 0.3 0.3 0.3
sphere
pos: 0.0 0.0 -3.0
rad: 1
dif: 0.3 0.3 0.3
spe: 0.5 0.5 0.5
shi: 1
light
pos: 0 0 0
col: 1 1 1
Mine looks very weird :
//check ray intersection with the sphere
boolean intersectsWithSphere(struct point rayPosition, struct point rayDirection, Sphere sp,float* t){
//float a = (rayDirection.x * rayDirection.x) + (rayDirection.y * rayDirection.y) +(rayDirection.z * rayDirection.z);
// value for a is 1 since rayDirection vector is normalized
double radius = sp.radius;
double xc = sp.position[0];
double yc =sp.position[1];
double zc =sp.position[2];
double xo = rayPosition.x;
double yo = rayPosition.y;
double zo = rayPosition.z;
double xd = rayDirection.x;
double yd = rayDirection.y;
double zd = rayDirection.z;
double b = 2 * ((xd*(xo-xc))+(yd*(yo-yc))+(zd*(zo-zc)));
double c = (xo-xc)*(xo-xc) + (yo-yc)*(yo-yc) + (zo-zc)*(zo-zc) - (radius * radius);
float D = b*b + (-4.0f)*c;
//ray does not intersect the sphere
if(D < 0 ){
return false;
}
D = sqrt(D);
float t0 = (-b - D)/2 ;
float t1 = (-b + D)/2;
//printf("D=%f",D);
//printf(" t0=%f",t0);
//printf(" t1=%f\n",t1);
if((t0 > 0) && (t1 > 0)){
*t = min(t0,t1);
return true;
}
else {
*t = 0;
return false;
}
}
Below is the trace() function:
unsigned char* trace(struct point rayPosition, struct point rayDirection, Sphere * totalspheres) {
struct point tempRayPosition = rayPosition;
struct point tempRayDirection = rayDirection;
float f=0;
float tnear = INFINITY;
boolean sphereIntersectionFound = false;
int sphereIndex = -1;
for(int i=0; i < num_spheres ; i++){
float t = INFINITY;
if(intersectsWithSphere(tempRayPosition,tempRayDirection,totalspheres[i],&t)){
if(t < tnear){
tnear = t;
sphereIntersectionFound = true;
sphereIndex = i;
}
}
}
if(sphereIndex < 0){
//printf("No interesection found\n");
mycolor[0] = 1;
mycolor[1] = 1;
mycolor[2] = 1;
return mycolor;
}
else {
Sphere sp = totalspheres[sphereIndex];
//intersection point
hitPoint[0].x = tempRayPosition.x + tempRayDirection.x * tnear;
hitPoint[0].y = tempRayPosition.y + tempRayDirection.y * tnear;
hitPoint[0].z = tempRayPosition.z + tempRayDirection.z * tnear;
//normal at the intersection point
normalAtHitPoint[0].x = (hitPoint[0].x - totalspheres[sphereIndex].position[0])/ totalspheres[sphereIndex].radius;
normalAtHitPoint[0].y = (hitPoint[0].y - totalspheres[sphereIndex].position[1])/ totalspheres[sphereIndex].radius;
normalAtHitPoint[0].z = (hitPoint[0].z - totalspheres[sphereIndex].position[2])/ totalspheres[sphereIndex].radius;
normalizedNormalAtHitPoint[0] = normalize(normalAtHitPoint[0]);
for(int j=0; j < num_lights ; j++) {
for(int k=0; k < num_spheres ; k++){
shadowRay[0].x = lights[j].position[0] - hitPoint[0].x;
shadowRay[0].y = lights[j].position[1] - hitPoint[0].y;
shadowRay[0].z = lights[j].position[2] - hitPoint[0].z;
normalizedShadowRay[0] = normalize(shadowRay[0]);
//R = 2 * ( N dot L) * N - L
reflectionRay[0].x = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].x +normalizedShadowRay[0].x;
reflectionRay[0].y = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].y +normalizedShadowRay[0].y;
reflectionRay[0].z = - 2 * dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]) * normalizedNormalAtHitPoint[0].z +normalizedShadowRay[0].z;
normalizeReflectionRay[0] = normalize(reflectionRay[0]);
struct point temp;
temp.x = hitPoint[0].x + (shadowRay[0].x * 0.0001 );
temp.y = hitPoint[0].y + (shadowRay[0].y * 0.0001);
temp.z = hitPoint[0].z + (shadowRay[0].z * 0.0001);
struct point ntemp = normalize(temp);
float f=0;
struct point tempHitPoint;
tempHitPoint.x = hitPoint[0].x + 0.001;
tempHitPoint.y = hitPoint[0].y + 0.001;
tempHitPoint.z = hitPoint[0].z + 0.001;
if(intersectsWithSphere(hitPoint[0],ntemp,totalspheres[k],&f)){
// if(intersectsWithSphere(tempHitPoint,ntemp,totalspheres[k],&f)){
printf("In shadow\n");
float r = lights[j].color[0];
float g = lights[j].color[1];
float b = lights[j].color[2];
mycolor[0] = ambient_light[0] + r;
mycolor[1] = ambient_light[1] + g;
mycolor[2] = ambient_light[2] + b;
return mycolor;
} else {
// point is not is shadow , use Phong shading to determine the color of the point.
//I = lightColor * (kd * (L dot N) + ks * (R dot V) ^ sh)
//(for each color channel separately; note that if L dot N < 0, you should clamp L dot N to zero; same for R dot V)
float x = dot(normalizedShadowRay[0],normalizedNormalAtHitPoint[0]);
if(x < 0)
x = 0;
V[0].x = - rayDirection.x;
V[0].x = - rayDirection.y;
V[0].x = - rayDirection.z;
normalizedV[0] = normalize(V[0]);
float y = dot(normalizeReflectionRay[0],normalizedV[0]);
if(y < 0)
y = 0;
float ar = totalspheres[sphereIndex].color_diffuse[0] * x;
float br = totalspheres[sphereIndex].color_specular[0] * pow(y,totalspheres[sphereIndex].shininess);
float r = lights[j].color[0] * (ar+br);
//----------------------------------------------------------------------------------
float bg = totalspheres[sphereIndex].color_specular[1] * pow(y,totalspheres[sphereIndex].shininess);
float ag = totalspheres[sphereIndex].color_diffuse[1] * x;
float g = lights[j].color[1] * (ag+bg);
//----------------------------------------------------------------------------------
float bb = totalspheres[sphereIndex].color_specular[2] * pow(y,totalspheres[sphereIndex].shininess);
float ab = totalspheres[sphereIndex].color_diffuse[2] * x;
float b = lights[j].color[2] * (ab+bb);
mycolor[0] = r + ambient_light[0];
mycolor[1] = g + ambient_light[1];
mycolor[2] = b+ ambient_light[2];
return mycolor;
}
}
}
}
}
The code calling trace() looks like :
void draw_scene()
{
//Aspect Ratio
double a = WIDTH / HEIGHT;
double angel = tan(M_PI * 0.5 * fov/ 180);
ray[0].x = 0.0;
ray[0].y = 0.0;
ray[0].z = 0.0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
unsigned int x,y;
float sx, sy;
for(x=0;x < WIDTH;x++)
{
glPointSize(2.0);
glBegin(GL_POINTS);
for(y=0;y < HEIGHT;y++)
{
sx = (((x + 0.5) / WIDTH) * 2.0 ) - 1;
sy = (((y + 0.5) / HEIGHT) * 2.0 ) - 1;;
sx = sx * angel * a;
sy = sy * angel;
//set ray direction
ray[1].x = sx;
ray[1].y = sy;
ray[1].z = -1;
normalizedRayDirection[0] = normalize(ray[1]);
unsigned char* color = trace(ray[0],normalizedRayDirection[0],spheres);
unsigned char x1 = color[0] * 255;
unsigned char y1 = color[1] * 255;
unsigned char z1 = color[2] * 255;
plot_pixel(x,y,x1 %256,y1%256,z1%256);
}
glEnd();
glFlush();
}
}
There could be many, many problems with the code/understanding.
I haven't taken the time to understand all your code, and I'm definitely not a graphics expert, but I believe the problem you have is called "surface acne". In this case it's probably happening because your shadow rays are intersecting with the object itself. What I did in my code to fix this is add epsilon * hitPoint.normal to the shadow ray origin. This effectively moves the ray away from your object a bit, so they don't intersect.
The value I'm using for epsilon is the square root of 1.19209290 * 10^-7, as that is the square root of a constant called EPSILON that is defined in the particular language I'm using.
What possible reason do you have for doing this (in the non-shadow branch of trace (...)):
V[0].x = - rayDirection.x;
V[0].x = - rayDirection.y;
V[0].x = - rayDirection.z;
You might as well comment out the first two computations since you write the results of each to the same component. I think you probably meant to do this instead:
V[0].x = - rayDirection.x;
V[0].y = - rayDirection.y;
V[0].z = - rayDirection.z;
That said, you should also avoid using GL_POINT primitives to cover a 2x2 pixel quad. Point primitives are not guaranteed to be square, and OpenGL implementations are not required to support any size other than 1.0. In practice, most support 1.0 - ~64.0 but glDrawPixels (...) is a much better way of writing 2x2 pixels, since it skips primitive assembly and the above mentioned limitations. You are using immediate mode in this example anyway, so glRasterPos (...) and glDrawPixels (...) are still a valid approach.
It seems you are implementing the formula here, but you deviate at the end from the direction the article takes.
First the article warns that D & b can be very close in value, so that -b + D gets you a very limited number. They suggest an alternative.
Also, you are testing that both t0 & t1 > 0. This doesn't have to be true for you to hit the sphere, you could be inside of it (though you obviously should not be in your test scene).
Finally, I would add a test at the beginning to confirm that the direction vector is normalized. I've messed that up more than once in my renderers.

Resources