Related
I'm trying to write a class for the UnitVector, having already written the one for a generic Vector. The UnitVector class diverges from the Vector class only by the fact that the abs variable is set to 1.0f. I'd like to know what the best approach to the problem would be, whether it's better to make another class, UnitVector, that inherits the Vector class (my current idea, that's giving me problems) or to just write a method in the Vector class
Here's my code for the class Vector:
Vector.h
class Vector
{
public:
Vector();
Vector(float x, float y);
void set_by_angle(float abs, float angle);
void set(float x, float y);
void rotate(float angle);
void scale(float scale_factor);
void translate(float x, float y);
void translate(Vector v);
float get_abs();
float get_angle();
static Vector create_by_angle(float abs, float angle);
static Vector create(float x, float y);
static Vector create_from_vectors(Vector v1, Vector v2, float abs);
static float get_distance(Vector v1, Vector v2);
static float get_angle(float x, float y);
static float get_angle(Vector v);
void to_string();
Vector operator=(const Vector &v);
bool operator==(const Vector &v);
bool operator!=(const Vector &v);
Vector operator+=(const Vector &v);
friend Vector operator+(const Vector &v1, const Vector &v2);
friend Vector operator-(const Vector &v1, const Vector &v2);
float x;
float y;
private:
float abs;
float angle;
};
Vector.cpp
#define _USE_MATH_DEFINES
#include <math.h>
#include <iostream>
#include <string>
#include "vector.h"
using namespace std;
Vector::Vector() : Vector(0, 0) {}
Vector::Vector(float x, float y)
{
set(x, y);
}
void Vector::set_by_angle(float abs, float angle)
{
this->abs = abs;
this->angle = angle;
x = abs * cos(angle);
y = abs * sin(angle);
}
void Vector::set(float x, float y)
{
this->x = x;
this->y = y;
abs = get_abs();
angle = get_angle();
}
void Vector::rotate(float angle)
{
set_by_angle(this->abs, this->angle + angle);
}
void Vector::scale(float scale_factor)
{
set(x * scale_factor, y * scale_factor);
}
void Vector::translate(float x, float y)
{
set(this->x + x, this->y + y);
}
void Vector::translate(Vector v)
{
translate(v.x, v.y);
}
float Vector::get_abs()
{
return sqrt(pow(x, 2) + pow(y, 2));
}
float Vector::get_angle()
{
return get_angle(x, y);
}
Vector Vector::create_by_angle(float abs, float angle)
{
Vector v;
v.set_by_angle(abs, angle);
return v;
}
Vector Vector::create(float x, float y)
{
Vector v;
v.set(x, y);
return v;
}
float Vector::get_distance(Vector v1, Vector v2)
{
return sqrt(pow(v1.x - v2.x, 2) + pow(v1.y - v2.y, 2));
}
Vector Vector::create_from_vectors(Vector v1, Vector v2, float abs)
{
float x = v2.x - v1.x;
float y = v2.y - v1.y;
Vector v;
v.set_by_angle(abs, Vector(x, y).get_angle());
return v;
}
float Vector::get_angle(float x, float y)
{
float tan = 0.0f;
float angle = 0.0f;
if (x != 0)
{
tan = y / x;
angle = atan(tan);
// this setting applies to the graphic reference system
if (x > 0 && y < 0) angle = 2 * M_PI + angle;
if (x < 0 && y > 0) angle += M_PI;
if (x < 0 && y < 0) angle += M_PI;
}
if (x == 0)
{
if (y > 0) angle = M_PI_2;
if (y < 0) angle = 3 * M_PI_2;
}
if (y == 0)
{
if (x > 0) angle = 0.0f;
if (x < 0) angle = M_PI;
}
return angle;
}
float Vector::get_angle(Vector v)
{
return get_angle(v.x, v.y);
}
void Vector::to_string()
{
cout << "x: " + std::to_string(x) + " y: " + std::to_string(y) << endl;
}
Vector operator+(const Vector &v1, const Vector &v2)
{
Vector tmp;
tmp.set(v1.x + v2.x, v1.y + v2.y);
return tmp;
}
Vector operator-(const Vector &v1, const Vector &v2)
{
return v1 + Vector(-v2.x, -v2.y);
}
Vector Vector::operator+=(const Vector &v)
{
set(x + v.x, y + v.y);
return *this;
}
Vector Vector::operator=(const Vector &v)
{
set(v.x, v.y);
return *this;
}
bool Vector::operator==(const Vector &v)
{
return
(
(x == v.x)
&&
(y == v.y)
);
}
bool Vector::operator!=(const Vector &v)
{
return !(*this == v);
}
Thanks in advance!
Just add the following two methods:
void normalize()
{
float scalar = 1.0 / this->get_abs();
this->x *= scalar;
this->y *= scalar;
}
And:
static Vector get_unit(const Vector &v)
{
float scalar = 1.0 / v.get_abs();
return Vector(v.x * scalar, v.y * scalar);
}
Is there any way I can modify the poisson-disk points generator finding here.I need to generate new poisson points using the coordinates of points in the textfile.txt to improve the distribution. below the c++ code of poisson-disk sampling in a unit square.
poissonGenerator.h:
#include <vector>
#include <random>
#include <stdint.h>
#include <time.h>
namespace PoissoGenerator
{
class DefaultPRNG
{
public:
DefaultPRNG()
: m_Gen(std::random_device()())
, m_Dis(0.0f, 1.f)
{
// prepare PRNG
m_Gen.seed(time(nullptr));
}
explicit DefaultPRNG(unsigned short seed)
: m_Gen(seed)
, m_Dis(0.0f, 1.f)
{
}
double RandomDouble()
{
return static_cast <double>(m_Dis(m_Gen));
}
int RandomInt(int Max)
{
std::uniform_int_distribution<> DisInt(0, Max);
return DisInt(m_Gen);
}
private:
std::mt19937 m_Gen;
std::uniform_real_distribution<double> m_Dis;
};
struct sPoint
{
sPoint()
: x(0)
, y(0)
, m_valid(false){}
sPoint(double X, double Y)
: x(X)
, y(Y)
, m_valid(true){}
double x;
double y;
bool m_valid;
//
bool IsInRectangle() const
{
return x >= 0 && y >= 0 && x <= 1 && y <= 1;
}
//
bool IsInCircle() const
{
double fx = x - 0.5f;
double fy = y - 0.5f;
return (fx*fx + fy*fy) <= 0.25f;
}
};
struct sGridPoint
{
sGridPoint(int X, int Y)
: x(X)
, y(Y)
{}
int x;
int y;
};
double GetDistance(const sPoint& P1, const sPoint& P2)
{
return sqrt((P1.x - P2.x)*(P1.x - P2.x) + (P1.y - P2.y)*(P1.y - P2.y));
}
sGridPoint ImageToGrid(const sPoint& P, double CellSize)
{
return sGridPoint((int)(P.x / CellSize), (int)(P.y / CellSize));
}
struct sGrid
{
sGrid(int W, int H, double CellSize)
: m_W(W)
, m_H(H)
, m_CellSize(CellSize)
{
m_Grid.resize((m_H));
for (auto i = m_Grid.begin(); i != m_Grid.end(); i++){ i->resize(m_W); }
}
void Insert(const sPoint& P)
{
sGridPoint G = ImageToGrid(P, m_CellSize);
m_Grid[G.x][G.y] = P;
}
bool IsInNeighbourhood(sPoint Point, double MinDist, double CellSize)
{
sGridPoint G = ImageToGrid(Point, CellSize);
//number of adjacent cell to look for neighbour points
const int D = 5;
// Scan the neighbourhood of the Point in the grid
for (int i = G.x - D; i < G.x + D; i++)
{
for (int j = G.y - D; j < G.y + D; j++)
{
if (i >= 0 && i < m_W && j >= 0 && j < m_H)
{
sPoint P = m_Grid[i][j];
if (P.m_valid && GetDistance(P, Point) < MinDist){ return true; }
}
}
}
return false;
}
private:
int m_H;
int m_W;
double m_CellSize;
std::vector< std::vector< sPoint> > m_Grid;
};
template <typename PRNG>
sPoint PopRandom(std::vector<sPoint>& Points, PRNG& Generator)
{
const int Idx = Generator.RandomInt(Points.size() - 1);
const sPoint P = Points[Idx];
Points.erase(Points.begin() + Idx);
return P;
}
template <typename PRNG>
sPoint GenerateRandomPointAround(const sPoint& P, double MinDist, PRNG& Generator)
{
// Start with non-uniform distribution
double R1 = Generator.RandomDouble();
double R2 = Generator.RandomDouble();
// radius should be between MinDist and 2 * MinDist
double Radius = MinDist * (R1 + 1.0f);
//random angle
double Angle = 2 * 3.141592653589f * R2;
// the new point is generated around the point (x, y)
double X = P.x + Radius * cos(Angle);
double Y = P.y + Radius * sin(Angle);
return sPoint(X, Y);
}
// Return a vector of generated points
// NewPointsCount - refer to bridson-siggraph07-poissondisk.pdf
// for details (the value 'k')
// Circle - 'true' to fill a circle, 'false' to fill a rectangle
// MinDist - minimal distance estimator, use negative value for default
template <typename PRNG = DefaultPRNG>
std::vector<sPoint> GeneratePoissonPoints(rsize_t NumPoints, PRNG& Generator, int NewPointsCount = 30,
bool Circle = true, double MinDist = -1.0f)
{
if (MinDist < 0.0f)
{
MinDist = sqrt(double(NumPoints)) / double(NumPoints);
}
std::vector <sPoint> SamplePoints;
std::vector <sPoint> ProcessList;
// create the grid
double CellSize = MinDist / sqrt(2.0f);
int GridW = (int)(ceil)(1.0f / CellSize);
int GridH = (int)(ceil)(1.0f / CellSize);
sGrid Grid(GridW, GridH, CellSize);
sPoint FirstPoint;
do
{
FirstPoint = sPoint(Generator.RandomDouble(), Generator.RandomDouble());
} while (!(Circle ? FirstPoint.IsInCircle() : FirstPoint.IsInRectangle()));
//Update containers
ProcessList.push_back(FirstPoint);
SamplePoints.push_back(FirstPoint);
Grid.Insert(FirstPoint);
// generate new points for each point in the queue
while (!ProcessList.empty() && SamplePoints.size() < NumPoints)
{
#if POISSON_PROGRESS_INDICATOR
// a progress indicator, kind of
if (SamplePoints.size() % 100 == 0) std::cout << ".";
#endif // POISSON_PROGRESS_INDICATOR
sPoint Point = PopRandom<PRNG>(ProcessList, Generator);
for (int i = 0; i < NewPointsCount; i++)
{
sPoint NewPoint = GenerateRandomPointAround(Point, MinDist, Generator);
bool Fits = Circle ? NewPoint.IsInCircle() : NewPoint.IsInRectangle();
if (Fits && !Grid.IsInNeighbourhood(NewPoint, MinDist, CellSize))
{
ProcessList.push_back(NewPoint);
SamplePoints.push_back(NewPoint);
Grid.Insert(NewPoint);
continue;
}
}
}
#if POISSON_PROGRESS_INDICATOR
std::cout << std::endl << std::endl;
#endif // POISSON_PROGRESS_INDICATOR
return SamplePoints;
}
}
and the main program is:
poisson.cpp
#include "stdafx.h"
#include <vector>
#include <iostream>
#include <fstream>
#include <memory.h>
#define POISSON_PROGRESS_INDICATOR 1
#include "PoissonGenerator.h"
const int NumPoints = 20000; // minimal number of points to generate
int main()
{
PoissonGenerator::DefaultPRNG PRNG;
const auto Points =
PoissonGenerator::GeneratePoissonPoints(NumPoints,PRNG);
std::ofstream File("Poisson.txt", std::ios::out);
File << "NumPoints = " << Points.size() << std::endl;
for (const auto& p : Points)
{
File << " " << p.x << " " << p.y << std::endl;
}
system("PAUSE");
return 0;
}
Suppose you have a point in the space [0,1] x [0,1], in the form of a std::pair<double, double>, but desire points in the space [x,y] x [w,z].
The function object
struct ProjectTo {
double x, y, w, z;
std::pair<double, double> operator(std::pair<double, double> in)
{
return std::make_pair(in.first * (y - x) + x, in.second * (z - w) + w);
}
};
will transform such an input point into the desired output point.
Suppose further you have a std::vector<std::pair<double, double>> points, all drawn from the input distribution.
std::copy(points.begin(), points.end(), points.begin(), ProjectTo{ x, y, w, z });
Now you have a vector of points in the output space.
When I add some extra formal parameters double tmin=0.0, double tmax=0.0 to the constructor of the Ray in the code below, I always obtain a wrong image with a white top border. These formal parameters currently contribute in no way (i.e. are unused) to the code. So how is it possible to obtain a different image?
System specifications:
OS: Windows 8.1
Compiler: MSVC 2015
Code:
#include "stdafx.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <random>
std::default_random_engine generator(606418532);
std::uniform_real_distribution<double> distribution = std::uniform_real_distribution<double>(0.0, 1.0);
double erand48(unsigned short *x) {
return distribution(generator);
}
#define M_PI 3.14159265358979323846
struct Vector3 {
double x, y, z;
Vector3(double x_ = 0, double y_ = 0, double z_ = 0) { x = x_; y = y_; z = z_; }
Vector3 operator+(const Vector3 &b) const { return Vector3(x + b.x, y + b.y, z + b.z); }
Vector3 operator-(const Vector3 &b) const { return Vector3(x - b.x, y - b.y, z - b.z); }
Vector3 operator*(double b) const { return Vector3(x*b, y*b, z*b); }
Vector3 mult(const Vector3 &b) const { return Vector3(x*b.x, y*b.y, z*b.z); }
Vector3& norm() { return *this = *this * (1 / sqrt(x*x + y*y + z*z)); }
double Dot(const Vector3 &b) const { return x*b.x + y*b.y + z*b.z; } // cross:
Vector3 operator%(Vector3&b) { return Vector3(y*b.z - z*b.y, z*b.x - x*b.z, x*b.y - y*b.x); }
};
//struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_, double tmin=0.0, double tmax=0.0) : o(o_), d(d_) {} };
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_) : o(o_), d(d_) {} };
enum Reflection_t { DIFFUSE, SPECULAR, REFRACTIVE };
struct Sphere {
double rad; // radius
Vector3 p, e, f; // position, emission, color
Reflection_t reflection_t; // reflection type (DIFFuse, SPECular, REFRactive)
Sphere(double rad_, Vector3 p_, Vector3 e_, Vector3 f_, Reflection_t reflection_t) :
rad(rad_), p(p_), e(e_), f(f_), reflection_t(reflection_t) {}
double intersect(const Ray &r) const {
Vector3 op = p - r.o;
double t, eps = 1e-4, b = op.Dot(r.d), det = b*b - op.Dot(op) + rad*rad;
if (det<0) return 0; else det = sqrt(det);
return (t = b - det)>eps ? t : ((t = b + det)>eps ? t : 0);
}
};
Sphere spheres[] = {
Sphere(1e5, Vector3(1e5 + 1,40.8,81.6), Vector3(),Vector3(.75,.25,.25),DIFFUSE),//Left
Sphere(1e5, Vector3(-1e5 + 99,40.8,81.6),Vector3(),Vector3(.25,.25,.75),DIFFUSE),//Rght
Sphere(1e5, Vector3(50,40.8, 1e5), Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Back
Sphere(1e5, Vector3(50,40.8,-1e5 + 170), Vector3(),Vector3(), DIFFUSE),//Frnt
Sphere(1e5, Vector3(50, 1e5, 81.6), Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Botm
Sphere(1e5, Vector3(50,-1e5 + 81.6,81.6),Vector3(),Vector3(.75,.75,.75),DIFFUSE),//Top
Sphere(16.5,Vector3(27,16.5,47), Vector3(),Vector3(1,1,1)*.999, SPECULAR),//Mirr
Sphere(16.5,Vector3(73,16.5,78), Vector3(),Vector3(1,1,1)*.999, REFRACTIVE),//Glas
Sphere(600, Vector3(50,681.6 - .27,81.6),Vector3(12,12,12), Vector3(), DIFFUSE) //Lite
};
inline double clamp(double x) { return x<0 ? 0 : x>1 ? 1 : x; }
inline int toInt(double x) { return int(pow(clamp(x), 1 / 2.2) * 255 + .5); }
inline bool intersect(const Ray &r, double &t, int &id) {
double n = sizeof(spheres) / sizeof(Sphere), d, inf = t = 1e20;
for (int i = int(n); i--;) if ((d = spheres[i].intersect(r)) && d<t) { t = d; id = i; }
return t<inf;
}
Vector3 radiance(const Ray &r_, int depth_, unsigned short *Xi) {
double t; // distance to intersection
int id = 0; // id of intersected object
Ray r = r_;
int depth = depth_;
Vector3 cl(0, 0, 0); // accumulated color
Vector3 cf(1, 1, 1); // accumulated reflectance
while (1) {
if (!intersect(r, t, id)) return cl; // if miss, return black
const Sphere &obj = spheres[id]; // the hit object
Vector3 x = r.o + r.d*t, n = (x - obj.p).norm(), nl = n.Dot(r.d)<0 ? n : n*-1, f = obj.f;
double p = f.x>f.y && f.x>f.z ? f.x : f.y>f.z ? f.y : f.z; // max refl
cl = cl + cf.mult(obj.e);
if (++depth>5) if (erand48(Xi)<p) f = f*(1 / p); else return cl; //R.R.
cf = cf.mult(f);
if (obj.reflection_t == DIFFUSE) { // Ideal DIFFUSE reflection
double r1 = 2 * M_PI*erand48(Xi), r2 = erand48(Xi), r2s = sqrt(r2);
Vector3 w = nl, u = ((fabs(w.x)>.1 ? Vector3(0, 1) : Vector3(1)) % w).norm(), v = w%u;
Vector3 d = (u*cos(r1)*r2s + v*sin(r1)*r2s + w*sqrt(1 - r2)).norm();
r = Ray(x, d);
continue;
}
else if (obj.reflection_t == SPECULAR) {
r = Ray(x, r.d - n * 2 * n.Dot(r.d));
continue;
}
Ray reflRay(x, r.d - n * 2 * n.Dot(r.d));
bool into = n.Dot(nl)>0;
double nc = 1, nt = 1.5, nnt = into ? nc / nt : nt / nc, ddn = r.d.Dot(nl), cos2t;
if ((cos2t = 1 - nnt*nnt*(1 - ddn*ddn))<0) {
r = reflRay;
continue;
}
Vector3 tdir = (r.d*nnt - n*((into ? 1 : -1)*(ddn*nnt + sqrt(cos2t)))).norm();
double a = nt - nc, b = nt + nc, R0 = a*a / (b*b), c = 1 - (into ? -ddn : tdir.Dot(n));
double Re = R0 + (1 - R0)*c*c*c*c*c, Tr = 1 - Re, P = .25 + .5*Re, RP = Re / P, TP = Tr / (1 - P);
if (erand48(Xi)<P) {
cf = cf*RP;
r = reflRay;
}
else {
cf = cf*TP;
r = Ray(x, tdir);
}
continue;
}
}
int main(int argc, char *argv[]) {
int w = 512, h = 384, samps = argc == 2 ? atoi(argv[1]) / 4 : 1; // # samples
Ray cam(Vector3(50, 52, 295.6), Vector3(0, -0.042612, -1).norm()); // cam pos, dir
Vector3 cx = Vector3(w*.5135 / h), cy = (cx%cam.d).norm()*.5135, r, *c = new Vector3[w*h];
#pragma omp parallel for schedule(dynamic, 1) private(r) // OpenMP
for (int y = 0; y<h; y++) { // Loop over image rows
fprintf(stderr, "\rRendering (%d spp) %5.2f%%", samps * 4, 100.*y / (h - 1));
for (unsigned short x = 0, Xi[3] = { 0,0,y*y*y }; x<w; x++) // Loop cols
for (int sy = 0, i = (h - y - 1)*w + x; sy<2; sy++) // 2x2 subpixel rows
for (int sx = 0; sx<2; sx++, r = Vector3()) { // 2x2 subpixel cols
for (int s = 0; s<samps; s++) {
double r1 = 2 * erand48(Xi), dx = r1<1 ? sqrt(r1) - 1 : 1 - sqrt(2 - r1);
double r2 = 2 * erand48(Xi), dy = r2<1 ? sqrt(r2) - 1 : 1 - sqrt(2 - r2);
Vector3 d = cx*(((sx + .5 + dx) / 2 + x) / w - .5) +
cy*(((sy + .5 + dy) / 2 + y) / h - .5) + cam.d;
r = r + radiance(Ray(cam.o + d * 140, d.norm()), 0, Xi)*(1. / samps);
} // Camera rays are pushed ^^^^^ forward to start in interior
c[i] = c[i] + Vector3(clamp(r.x), clamp(r.y), clamp(r.z))*.25;
}
}
FILE *fp;
fopen_s(&fp, "image.ppm", "w"); // Write image to PPM file.
fprintf(fp, "P3\n%d %d\n%d\n", w, h, 255);
for (int i = 0; i<w*h; i++)
fprintf(fp, "%d %d %d ", toInt(c[i].x), toInt(c[i].y), toInt(c[i].z));
}
First Ray structure:
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_) : o(o_), d(d_) {} };
Results in:
Second Ray structure:
struct Ray { Vector3 o, d; Ray(const Vector3 &o_, const Vector3 &d_, double tmin=0.0, double tmax=0.0) : o(o_), d(d_) {} };
Results in:
The last image has a noticeable white top border which is not present in the first image.
Edit:
I used
size_t n = sizeof(spheres) / sizeof(Sphere);
Now I obtain the same images, but I also checked if the original int(n) could differ from 9 which is never the case.
Ok this is from the Debug build, which is different from the Release build.
Sounds like a memory error, looking quickly at your code I'm sceptical of this line:
for (int i = int(n); i--;) if ((d = spheres[i].intersect(r)) && d<t)
I suspect accessing sphere[i] is out of bounds, perhaps you should try sphere[i-1]. You could also try compiling your code with a compiler that adds extra code for debugging/sanitising/checking memory addresses.
This is a ray tracer code I'm working on. When I tested it out, everything seemed to be working fine until I started changing the camera(view point) position. Here are some of the results:
campos(-60, 100, -30), lightPos(-70, 100, -30)
The light on the floor is cut off somehow.
campos(60, 100, -30), lightPos(-70, 100, -30)
This one shows the same problem.
campos(60, 30, -30), lightPos(-70, 100, -30)
The light in this screenshot seems to have two light sources although there's only one active at the moment.
campos(-70, 100, -30), lightPos(-70, 100, -30)
The final position is the last position I set on the code below. It's at the exact same location as the light sorce.
Why is the light creating shadows like that?
main.cpp
#include <iostream>
#include <algorithm>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include <vector>
#include "Vector.h"
#include "Ray.h"
#include "Camera.h"
#include "Color.h"
#include "Light.h"
#include "Sphere.h"
#include "Plane.h"
#define PI 3.141592653589793
#define INFINITY 1e6
#define FOV 60
#define KA 0.2
#define KD 0.5
#define KS 5
VECTOR X = { 1,0,0 };
VECTOR Y = { 0,1,0 };
VECTOR Z = { 0,0,1 };
VECTOR O = { 0,0,0 };
Color white(1, 1, 1);
Color black(0, 0, 0);
Color greenC(0.5, 1, 0.5);
Color gray(0.5, 0.5, 0.5);
Color maroon(0.5, 0.25, 0.25);
unsigned int width = 640;
unsigned int height = 480;
using namespace std;
Color trace(Ray &ray, vector<Object*> objects, vector<Light*> lights)
{
float hit = INFINITY;
float closest = INFINITY;
Object* objectHit = NULL;
for (int i = 0; i < objects.size(); i++)
{
if (objects.at(i)->intersect(ray, hit))
{
if (hit < closest)
{
closest = hit;
objectHit = objects.at(i);
}
}
}
if (objectHit)
{
VECTOR hitP = ray.getOrigin() + ray.getDirction() * closest;
VECTOR hitN = objectHit->getNormal(hitP);
Color finalColor = objectHit->getColor() * objectHit->getKa(); //ambient color
for (int i = 0; i < lights.size(); i++)
{
VECTOR lightDir = lights.at(i)->getPos() - hitP;
float lightDist = lightDir.Magnitude();
lightDir.Normalize();
bool shadow = false;
Ray shadowRay(hitP, lightDir);
float angle = max(hitN.DotProduct(lightDir), 0.0f);
for (int j = 0; j < objects.size() && shadow == false; j++)
{
float p;
if (objects.at(j)->intersect(shadowRay, p) && objectHit != objects.at(j))
{
VECTOR objectDist = hitP + lightDir * p;
if (objectDist.Magnitude() <= lightDist)
shadow = true;
}
}
if (!shadow)
{
VECTOR h = ray.getDirction() + lightDir;
h.Normalize();
Color diffuse = lights.at(i)->getCol() * objectHit->getKd() * angle;
Color specular = lights.at(i)->getCol() * angle * pow(max(hitN.DotProduct(h), 0.0f), objectHit->getKs());
finalColor = finalColor + diffuse + specular;
}
}
return finalColor.clip();
}
else return black;
}
void Render(void)
{
glClear(GL_COLOR_BUFFER_BIT);
vector<Object*> objects;
int radius = 20;
Sphere sphere(O, radius, greenC, KA, KD, KS);
Plane plane(Y, VECTOR(0, -radius, 0), maroon, 0.3, 0.5, 0.01);
objects.push_back(&sphere);
objects.push_back(&plane);
float xx, yy;
Color *image = new Color[width*height];
Color *pixel = image;
VECTOR lightPos(-70, 100, -30);
Light light(lightPos, gray);
//Light l2(VECTOR(10, 10, -20), white);
vector<Light*> lights;
lights.push_back(&light);
//lights.push_back(&l2);
VECTOR camPos(-70, 100, -30);
VECTOR lookat(0, 0, 0);
VECTOR diff(camPos.getX() - lookat.getX(), camPos.getY() - lookat.getY(), camPos.getZ() - lookat.getZ());
VECTOR camDir = diff;
camDir.Normalize();
VECTOR camRight = Y.CrossProduct(camDir);
camRight.Normalize();
VECTOR camUp = camRight.CrossProduct(camDir).Negative();
Camera cam(camPos, camDir, camRight, camUp);
for (int x = 0; x < width; x++)
{
for (int y = 0; y < height; y++)
{
xx = -(double)(width / 2) + x + 0.5;
yy = -(double)(height / 2) + y + 0.5;
VECTOR ray_d = camRight*xx + camUp*yy + camDir;
VECTOR ray_origin = camPos;
VECTOR ray_dir = ray_d - ray_origin;
ray_dir.Normalize();
Ray ray(ray_origin, ray_dir);
*(pixel++) = trace(ray, objects, lights);
float red = image[x*height + y].getRed();
float green = image[x*height + y].getGreen();
float blue = image[x*height + y].getBlue();
glColor3f(red, green, blue);
glBegin(GL_POINTS);
glVertex2i(x, y);
glEnd();
}
}
glutSwapBuffers();
}
struct RGBtype
{
float r, g, b;
};
int main(int argc, char ** argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
glutInitWindowSize(width, height);
glutCreateWindow("Ray tracer");
glClearColor(0.0, 0.0, 0.0, 0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, width, 0.0, height);
glutDisplayFunc(Render);
glutMainLoop();
return 0;
}
Vector.h
#ifndef _VECTOR_H_
#define _VECTOR_H_
#include <math.h>
class VECTOR
{
private:
float x, y, z;
public:
VECTOR();
~VECTOR();
VECTOR(float, float, float);
float getX() { return x; }
float getY() { return y; }
float getZ() { return z; }
float Magnitude();
VECTOR CrossProduct(VECTOR);
float DotProduct(VECTOR);
VECTOR vecAdd(VECTOR);
VECTOR vecMul(float);
void Normalize();
VECTOR Negative();
VECTOR operator - (VECTOR);
VECTOR operator + (VECTOR);
VECTOR operator * (float);
};
VECTOR VECTOR::operator-(VECTOR v)
{
VECTOR result = (*this);
result.x -= v.getX();
result.y -= v.getY();
result.z -= v.getZ();
return result;
}
VECTOR VECTOR::operator+(VECTOR v)
{
VECTOR result = (*this);
result.x += v.getX();
result.y += v.getY();
result.z += v.getZ();
return result;
}
VECTOR VECTOR::operator*(float f)
{
return VECTOR(x*f, y*f, z*f);
}
VECTOR::VECTOR()
{
x = y = z = 0;
}
VECTOR::~VECTOR(){}
VECTOR::VECTOR(float xPos, float yPos, float zPos)
{
x = xPos;
y = yPos;
z = zPos;
}
float VECTOR::Magnitude()
{
return sqrt(x * x + y * y + z * z);
}
float VECTOR::DotProduct(VECTOR v)
{
return (x * v.getX() + y * v.getY() + z * v.getZ());
}
VECTOR VECTOR::CrossProduct(VECTOR v)
{
VECTOR result;
result.x = y * v.getZ() - z * v.getY();
result.y = z * v.getX() - x * v.getZ();
result.z = x * v.getY() - y * v.getX();
return result;
}
VECTOR VECTOR::vecAdd(VECTOR v)
{
return VECTOR(x + v.getX(), y + v.getY(), +z + v.getZ());
}
VECTOR VECTOR::vecMul(float f)
{
return VECTOR(x*f, y*f, z*f);
}
void VECTOR::Normalize()
{
float w = Magnitude();
if (w < 0.00001) return;
x /= w;
y /= w;
z /= w;
}
VECTOR VECTOR::Negative()
{
return VECTOR( -x,-y,-z );
}
#endif // !_VECTOR_H_#pragma once
Ray.h
#ifndef _RAY_H_
#define _RAY_H_
#include "Vector.h"
class Ray
{
private:
VECTOR origin, direction;
public:
Ray();
~Ray();
Ray(VECTOR, VECTOR);
VECTOR getOrigin() { return origin; }
VECTOR getDirction() { return direction; }
};
Ray::Ray()
{
origin = VECTOR { 0,0,0 };
direction = VECTOR { 1,0,0 };
}
Ray::~Ray() {}
Ray::Ray(VECTOR o, VECTOR d)
{
origin = o;
direction = d;
}
#endif // !_Ray_H_#pragma once
Camera.h
#ifndef _CAMERA_H_
#define _CAMERA_H_
#include "Vector.h"
class Camera
{
private:
VECTOR camPos, camDir, camRight, camUp;
public:
Camera();
~Camera();
Camera(VECTOR, VECTOR, VECTOR, VECTOR);
VECTOR getCamPos() { return camPos; }
VECTOR getCamDir() { return camDir; }
VECTOR getCamRight() { return camRight; }
VECTOR getcamUp() { return camUp; }
};
Camera::Camera()
{
camPos = VECTOR{ 0,0,0 };
camDir = VECTOR{ 0,0,1 };
camRight = VECTOR{ 0,0,0 };
camUp = VECTOR{ 0,0,0 };
}
Camera::~Camera() {}
Camera::Camera(VECTOR pos, VECTOR dir, VECTOR right, VECTOR down)
{
camPos = pos;
camDir = dir;
camRight = right;
camUp = down;
}
#endif // !_CAMERA_H_#pragma once
Color.h
#ifndef _COLOR_H_
#define _COLOR_H_
#include "Vector.h"
class Color
{
private:
double red, green, blue;
public:
Color();
~Color();
Color(double, double, double);
double getRed() { return red; }
double getGreen() { return green; }
double getBlue() { return blue; }
void setRed(double r) { red = r; }
void setGreen(double g) { green = g; }
void setBlue(double b) { blue = b; }
double brightness() { return (red + green + blue) / 3; }
Color average(Color c) { return Color((red + c.getRed()) / 2, (green + c.getGreen()) / 2, (blue + c.getBlue()) / 2); }
Color operator * (double);
Color operator + (Color);
Color operator * (Color);
Color clip()
{
float sum = red + green + blue;
float extra = sum - 3;
if (extra > 0)
{
red = red + extra * (red / sum);
green = red + extra * (green / sum);
blue = red + extra * (blue / sum);
}
if (red > 1) { red = 1; }
if (green > 1) { green = 1; }
if (blue > 1) { blue = 1; }
if (red < 0) { red = 0; }
if (green < 0) { green = 0; }
if (blue < 0) { blue = 0; }
return Color(red, green, blue);
}
};
Color Color::operator * (double c) { return Color(red*c, green*c, blue*c); }
Color Color::operator + (Color c) { return Color(red + c.getRed(), green + c.getGreen(), blue + c.getBlue()); }
Color Color::operator * (Color c) { return Color(red*c.getRed(), green*c.getGreen(), blue*c.getBlue()); }
Color::Color()
{
red = green = blue = 1;
}
Color::~Color() {}
Color::Color(double r, double g, double b)
{
red = r;
green = g;
blue = b;
}
#endif // !_COLOR_H_#pragma once
Light.h
#ifndef _LIGHT_H_
#define _LIGHT_H_
#include "Vector.h"
#include "Color.h"
class Light
{
private:
VECTOR position;
Color color;
public:
Light();
~Light();
Light(VECTOR, Color);
virtual VECTOR getPos() { return position; }
virtual Color getCol() { return color; }
};
Light::Light()
{
position = VECTOR(0, 0, 0);
color = Color(1,1,1);
}
Light::~Light() {}
Light::Light(VECTOR v, Color c)
{
position = v;
color = c;
}
#endif // !_LIGHT_H_#pragma once
Sphere.h
#ifndef _SPHERE_H_
#define _SPHERE_H_
#include <math.h>
#include "Vector.h"
#include "Color.h"
#include "Object.h"
class Sphere : public Object
{
private:
VECTOR center;
float radius;
Color color;
float ka, kd, ks;
public:
Sphere();
~Sphere();
Sphere(VECTOR, float, Color, float, float, float);
float getKa() { return ka; }
float getKd() { return kd; }
float getKs() { return ks; }
VECTOR getCenter() { return center; }
float getRadius() { return radius; }
Color getColor() { return color; }
VECTOR getNormal(VECTOR &v)
{
VECTOR a = v - center;
a.Normalize();
return a;
}
bool intersect(Ray &ray, float &t)
{
float t0, t1;
float radius2 = radius * radius; //radius squared
VECTOR line = center - ray.getOrigin(); //vector from ray origin to sphere center
float ray_t = line.DotProduct(ray.getDirction()); //the current ray vector
if (ray_t < 0)
return false;
float d2 = line.DotProduct(line) - (ray_t * ray_t); //d2 + t2 = line2 by pythagorian theorm
if (d2 > radius2) //if larger than the radius, then the ray doesn't intersect with sphere
return false;
float ray_i = sqrt(radius2 - d2); //part of ray that is going through the sphere
t0 = ray_t - ray_i; //first sphere vertex along the ray
t1 = ray_t + ray_i; //second sphere vertex
if (t0 > t1)
{
float tmp = t0;
t0 = t1;
t1 = t0;
}
if (t0 < 0)
{
t0 = t1;
t = t0;
if (t0 < 0) return false;
}
t = t0;
return true;
}
};
Sphere::Sphere()
{
center = VECTOR(0, 0, 0);
radius = 1;
color = Color(1, 1, 1);
}
Sphere::~Sphere() {}
Sphere::Sphere(VECTOR v, float r, Color c, float a, float d, float s)
{
center = v;
radius = r;
color = c;
ka = a;
kd = d;
ks = s;
}
#endif // !_SPHERE_H_#pragma once
Object.h
#ifndef _OBJECT_H_
#define _OBJECT_H_
#include "Ray.h"
#include "Vector.h"
#include "Color.h"
class Object
{
private:
VECTOR center;
Color color;
float ka, kd, ks;
public:
Object();
~Object();
virtual float getKa() = 0;
virtual float getKd() = 0;
virtual float getKs() = 0;
virtual VECTOR getCenter() = 0;
virtual Color getColor() = 0;
virtual VECTOR getNormal(VECTOR&) = 0;
virtual bool intersect(Ray&, float&) = 0;
};
Object::Object(){}
Object::~Object() {}
#endif // !_OBJECT_H_#pragma once
Plane.h
#ifndef _PLANE_H_
#define _PLANE_H_
#include <math.h>
#include<vector>
#include "Vector.h"
#include "Color.h"
#include "Object.h"
using namespace std;
class Plane : public Object
{
private:
VECTOR normal;
float width, height;
vector<VECTOR> vertice;
VECTOR center; //to be used in equation (p - p0) * n = 0 where p is the point of intersection and p0 is the center
Color color;
float ka, kd, ks;
public:
Plane();
~Plane();
Plane(VECTOR, VECTOR, Color, float, float, float);
float getKa() { return ka; }
float getKd() { return kd; }
float getKs() { return ks; }
VECTOR getNormal(VECTOR &point)
{
VECTOR a = normal;
a.Normalize();
return a;
}
VECTOR getCenter() { return center; }
Color getColor() { return color; }
bool intersect(Ray &ray, float &t)
{
VECTOR rayDir = ray.getDirction();
float ray_f = rayDir.DotProduct(normal);
//ray doesn't intersect or is parallel to the plane - ray-plane intersection
if (fabs(ray_f) < 1e-6)
return false;
else
{
VECTOR tmp = (center - ray.getOrigin());
float plane_f = normal.DotProduct(tmp);
//returns t in parametric equation of ray point = origin + t*direction
t = plane_f / ray_f;
return (t >= 0);
}
}
};
Plane::Plane()
{
normal = VECTOR(0, 1, 0);
center = VECTOR(0, 0, 0);
color = Color(0.5, 0.5, 0.5);
width = 500;
height = 500;
}
Plane::~Plane() {}
Plane::Plane(VECTOR v, VECTOR o, Color c, float a, float d, float s)
{
normal = v;
center = o;
color = c;
ka = a;
kd = d;
ks = s;
}
#endif // !_PLANE_H_#pragma once
This is an awful lot of code, so I can only guess at what the problem is. Since the problem is in the not shadowed part of the image, the problem is in the calculation of either the diffuse or specular colors (or both). You could comment out each one individually to see what gives you the expected coloring, then further diagnose the problem from there.
The problem may be in your normalize method, which does not normalize really short vectors. This would cause the specular color to be off.
I am trying to write some position/orientation methods for my small & simple 3d-space calculation library. But I'm stuck on the following problem.
I store 3d line as start and end points. However it should be possible to store it as start point and line's length + orientation as well (it's just a good example to test if orientation calculations works).
By orientation I mean rotation from the initial "0" orientation (which places the end at start + [0,legth,0]). So I first rotate the [0,length,0] by orientation and then add start to it to get end point.
The problem is, my orientation calculations fails somewhere. After calculating the orientation I get different ending point.
I use left-handed coordinate system with Y-axis pointing up, but I don't think it's important here.
Here's the code (I've tried to name the methods in the way you can check if the steps are ok; here's the full source code if you want to compile it yourself):
Point3D start = { 5.0f, 4.0f, 7.0f };
Point3D end = { 15.0f, 6.0f, 14.0f };
Point3D direction = (end - start);
std::wcout << L"Direction: "; direction.output();
float angle = Point3D(0.0f, 1.0f, 0.0f).getAngleToAnotherVectorInRadians(direction);
Point3D axis = direction.getCrossProduct(Point3D(0.0f, 1.0f, 0.0f)).getNormalized();
Quaternion o = Quaternion(AxisAngle(axis, angle));
std::wcout << L"\nAxisAngle: "; AxisAngle(axis, angle).output();
std::wcout << L"\nOrientation: "; o.output();
//test - end2 should be equal to end
Point3D offset(0.0f, (end - start).getLengthAsVector(), 0.0f);
offset = o.rotatePoint(offset);
std::wcout << L"\nOffset: "; offset.output();
Point3D end2 = start + offset;
std::wcout << L"\nEnd2: "; end2.output();
The code produces such output (without a comments, of course):
Direction: {10, 2, 7} //looks ok
AxisAngle: {{-0.573462, 0, 0.819232}, 1.40839}
Orientation: {-0.371272, 0, 0.530388, 0.762132}
Offset: {-10, 2, -7} //Almost! It should be {10, 2, 7}
End2: {-5, 6, -9.53674e-07} //Wrong! It should be { 15, 6, 14 }
In case that all steps are ok but there are some mistakes in methods' implementations I post here the important code for classes (so you can reproduce the problem): Point3D, AxisAngle, Quaternion.
I highly believe that problem(s) lay(s) in my main steps or in AxisAngle calculations. I think that AxisAngle to Quaternion transformation is ok (but I pass the wrong AxisAngle to Quaternion constructor).
The Point3D:
struct Point3D {
protected:
float x, y, z;
public:
Point3D() : x(0.0f), y(0.0f), z(0.0f) {}
Point3D(float x, float y, float z) : x(x), y(y), z(z) {}
void output() { std::wcout << L"{" << x << L", " << y << L", " << z << L"}"; }
Point3D operator-(const Point3D &point) const {
Point3D temp;
temp.setX(getX() - point.getX());
temp.setY(getY() - point.getY());
temp.setZ(getZ() - point.getZ());
return temp;
}
Point3D operator+ (const Point3D &value) const {
Point3D temp;
temp.setX(getX() + value.getX());
temp.setY(getY() + value.getY());
temp.setZ(getZ() + value.getZ());
return temp;
}
inline float getX() const { return x; } inline float getY() const { return y; } inline float getZ() const { return z; }
inline void setX(float x) { this->x = x; } inline void setY(float y) { this->y = y; } inline void setZ(float z) { this->z = z; }
inline float getLengthAsVector() const {
return sqrt(x*x + y*y + z*z);
}
inline Point3D getCrossProduct(const Point3D &anotherVector) const {
//based on: http://www.sciencehq.com/physics/vector-product-multiplying-vectors.html
return Point3D(
y * anotherVector.z - anotherVector.y * z,
z * anotherVector.x - anotherVector.z * x,
x * anotherVector.y - anotherVector.x * y
);
}
inline float getDotProduct(const Point3D &anotherVector) const {
//based on: https://www.ltcconline.net/greenl/courses/107/Vectors/DOTCROS.HTM
return x * anotherVector.x + y * anotherVector.y + z * anotherVector.z;
}
inline float getAngleToAnotherVectorInRadians(const Point3D &anotherVector) const {
//based on: http://math.stackexchange.com/questions/974178/how-to-calculate-the-angle-between-2-vectors-in-3d-space-given-a-preset-function
return acos(getDotProduct(anotherVector) / (getLengthAsVector() * anotherVector.getLengthAsVector()));
}
Point3D getNormalized() const {
float length = std::abs(sqrt(x*x + y*y + z*z));
Point3D result(x / length, y / length, z / length);
return result;
}
};
The AxisAngle:
class AxisAngle {
protected:
Point3D axis;
float angleInRadians;
public:
AxisAngle(const AxisAngle &other) { axis = other.axis; angleInRadians = other.angleInRadians; }
AxisAngle::AxisAngle(float x, float y, float z, float angleInRadians) {
this->axis = Point3D(x, y, z);
this->angleInRadians = angleInRadians;
}
AxisAngle::AxisAngle(const Point3D &axis, float angleInRadians) {
this->axis = axis;
this->angleInRadians = angleInRadians;
}
Point3D getAxis() const { return axis; }
float getAngleInRadians() const { return angleInRadians; }
void output() { std::wcout << L"{"; axis.output(); std::wcout << L", " << angleInRadians << L"}"; }
};
And last but not least, Quaternion:
class Quaternion {
protected:
float x; float y; float z; float w;
public:
Quaternion() { x = 0.0f; y = 0.0f; z = 0.0f; w = 1.0f; }
Quaternion(const Quaternion &other) { x = other.x; y = other.y; z = other.z; w = other.w; }
Quaternion(float x, float y, float z, float w) { this->x = x; this->y = y; this->z = z; this->w = w; }
Quaternion(const AxisAngle &axisAngle) {
Point3D axis = axisAngle.getAxis();
float angleInRadians = axisAngle.getAngleInRadians();
x = sin(angleInRadians / 2) * axis.getX();
y = sin(angleInRadians / 2) * axis.getY();
z = sin(angleInRadians / 2) * axis.getZ();
w = cos(angleInRadians / 2);
normalizeIt();
}
float getLength() const {
return sqrt(x*x + y*y + z*z + w*w);
}
void normalizeIt() {
float length = getLength();
x = x / length;
y = y / length;
z = z / length;
w = w / length;
}
Quaternion getConjugated() const {
return Quaternion(-x, -y, -z, w);
}
Quaternion multiply(Quaternion by) {
//"R" for result
float wR = w * by.getW() - x * by.getX() - y * by.getY() - z * by.getZ();
float xR = x * by.getW() + w * by.getX() + y * by.getZ() - z * by.getY();
float yR = y * by.getW() + w * by.getY() + z * by.getX() - x * by.getZ();
float zR = z * by.getW() + w * by.getZ() + x * by.getY() - y * by.getX();
return Quaternion(xR, yR, zR, wR);
}
//rotate Point3D p around [0,0,0] with this Quaternion
Point3D rotatePoint(Point3D p) const {
Quaternion temp = multiply(p).multiply(getConjugated());
return Point3D(temp.getX(), temp.getY(), temp.getZ());
//G: P' = Q(P-G)Q' + G <- to rotate P around G with Quaternion
}
Quaternion multiply(Point3D r) const {
float wR = -x * r.getX() - y * r.getY() - z * r.getZ();
float xR = w * r.getX() + y * r.getZ() - z * r.getY();
float yR = w * r.getY() + z * r.getX() - x * r.getZ();
float zR = w * r.getZ() + x * r.getY() - y * r.getX();
return Quaternion(xR, yR, zR, wR);
}
inline float getX() const { return x; } inline void setX(float x) { this->x = x; }
inline float getY() const { return y; } inline void setY(float y) { this->y = y; }
inline float getZ() const { return z; } inline void setZ(float z) { this->z = z; }
inline float getW() const { return w; } inline void setW(float w) { this->w = w; }
void output() { std::wcout << L"{" << x << L", " << y << L", " << z << L", " << w << L"}"; }
};
In case somebody would ask: I do want to use quaternions. They may not look 100% needed here, but storing 3d object's orientation as quaternion has many benefits in more complex computations (and most game engines / 3d software use it as well "under the mask").
Your axis has the wrong orientation. It should be:
Point3D axis = Point3D(0.0f, 1.0f, 0.0f).getCrossProduct(direction).getNormalized();
Use the two left-hand rules to figure out the correct order.