Search code examples
c++raytracing

Calculating raytracer rays - vectors converge to same output


I've been writing a ray tracer for fun and have gotten a decent amount into it. I have watched a tutorials, lectures, and research / other code to bring into perspective calculating the vector ray in projection to the image. Unfortunately, after the 4-5th iteration depending on the size of the image I am creating, it uses the same vector ray. Though this should be different depending on which pixel is getting looked at in the image.

Right now I am currently doing a transformation that basically shifts the ray left or right x/y of the pixel depending on the dimension of the image getting created. Specifically, I have looked at these two Raytracer - Computing Eye Rays && calculation for ray generation in ray tracer answers, have tried implementing them, tweaked my code, and have gotten no where.

As a side note, the portrait and landscape implementations do not work either. It is hard coded with width = 10 and height = 10, because that has been the dimension I've been playing with. They can be changed and definitely will be changed in the future.

Coding in C++ with VS2013.

int WIDTH = 10;
int HEIGHT = 10;

int main(int argc, char **argv) {
    std::cout << "creating rays..." << std::endl;

    Vector Y(0, 1, 0);
    Vector camPos(3, 1.5, -4);
    Vector looking_at(0, 0, 0); // may change - but want to look at center for this scene
    Vector difference(camPos - looking_at);

    Vector camDir = difference.negative().normalize();
    Vector camRight = (Y.cross(camDir)).normalize();
    Vector camDown = camRight.cross(camDir);

    double aspectRatio = (double) WIDTH / (double) HEIGHT;
    double xAMT, yAMT;  //slightly left of right from direction of camera

    for (int x = 0; x < HEIGHT; x++) {
        for (int y = 0; y < WIDTH; y++) {
            if (WIDTH > HEIGHT) {
                // landscape
                xAMT = ((x + 0.5) / WIDTH) * aspectRatio - (((WIDTH - HEIGHT) / (double) HEIGHT) /2);
                yAMT = ((HEIGHT - y) + 0.5) / HEIGHT;
            }
            else if (HEIGHT > WIDTH) {
                // portrait
                xAMT = (y + 0.5) / WIDTH;
                yAMT = (((HEIGHT - y) + 0.5) / HEIGHT) / aspectRatio - (((HEIGHT - WIDTH) / (double) WIDTH) / 2);
            }
            else {
                // square
                xAMT = (x + 0.5) / WIDTH;
                yAMT = ((HEIGHT - y) + 0.5) / HEIGHT;
            }

            // YES - this indeed does work
            Vector camRayOrigin     = camPos;
            Vector camRightDir      = camRight * (yAMT - 0.5);
            Vector camDownDir       = camDown  * (xAMT - 0.5);
            Vector camRayDirection  = (camDir + (camRightDir + camDownDir)).normalize();

            Ray camRay(camRayOrigin, camRayDirection);
            camRayDirection.print_vector();
        }
    }           
}

the text produced by the code above is:

creating rays...             
-0.173037 0.117114 0.977928  
-0.325543 -0.458438 0.826956 
-0.517036 -0.198503 0.832629 
-0.54971 -0.326274 0.769002  
-0.575177 -0.269626 0.772316 
-0.573114 -0.295291 0.764423 
-0.575342 -0.283767 0.76711  
-0.574404 -0.288958 0.765874 
-0.574826 -0.286623 0.766435 
-0.574637 -0.287674 0.766183 
-0.574716 -0.287234 0.766288 
-0.574689 -0.287388 0.766251 
-0.574698 -0.287334 0.766264 
-0.574695 -0.287353 0.76626  
-0.574696 -0.287346 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 
-0.574696 -0.287348 0.766261 

Vector class:

#include <cmath>

class Vector {
    double x, y, z;
    int size = 3;

public:
    ~Vector() {};
    Vector() : x(0), y(0), z(0) {}
    Vector(double _x, double _y, double _z) : x(_x), y(_y), z(_z) {}

    Vector& operator=(Vector rhs);

    // Vector mathematical operations                                                                                                                                                                                               
    Vector operator+(const Vector& rhs);
    Vector& operator+=(const Vector& rhs);
    Vector operator-(const Vector& rhs);
    Vector& operator-=(const Vector& rhs);

    // Vector scalar operations
    Vector operator+(const double& rhs);
    Vector operator-(const double& rhs);
    Vector operator*(const double& rhs);
    Vector operator/(const double& rhs);

    Vector cross(const Vector& rhs); // Cross-Product
    double dot(const Vector& rhs); // Dot-Product
    Vector normalize(); // Normalize
    Vector negative(); // Negative 
    double mag(); // Magnitude

    void swap(Vector& rhs);
    void print_vector();
};

Vector& Vector::operator=(Vector rhs) {
    swap(rhs);
    return *this;
}

Vector Vector::operator+(const Vector& rhs) {
    Vector result(*this);
    result += rhs;
    return result;
}

Vector& Vector::operator+=(const Vector& rhs) {
    this->x += rhs.x;
    this->y += rhs.y;
    this->z += rhs.z;

    return *this;
}

Vector Vector::operator-(const Vector& rhs) {
    Vector result(*this);
    result -= rhs;
    return result;
}

Vector& Vector::operator-=(const Vector& rhs) {
    this->x -= rhs.x;
    this->y -= rhs.y;
    this->z -= rhs.z;

    return *this;
}

Vector Vector::operator+(const double& rhs) {
    this->x += rhs;
    this->y += rhs;
    this->z += rhs;

    return *this;
}

Vector Vector::operator-(const double& rhs) {
    this->x -= rhs;
    this->y -= rhs;
    this->z -= rhs;

    return *this;
}

Vector Vector::operator*(const double& rhs) {
    this->x *= rhs;
    this->y *= rhs;
    this->z *= rhs;

    return *this;
}

Vector Vector::operator/(const double& rhs) {
    this->x /= rhs;
    this->y /= rhs;
    this->z /= rhs;

    return *this;
}

Vector Vector::cross(const Vector& rhs) {
    double a = (y * rhs.z) - (z * rhs.y);
    double b = (z * rhs.x) - (x * rhs.z);
    double c = (x * rhs.y) - (y * rhs.x);
    Vector product(a, b, c);
    return product;
}

double Vector::dot(const Vector& rhs) {
    double scalar = (x * rhs.x) + (y * rhs.y) + (x * rhs.z);
    return scalar;
}

double Vector::mag() {
    return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));
}

Vector Vector::normalize() {
    double mag = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));

    if (mag != 0) {
        this->x /= mag;
        this->y /= mag;
        this->z /= mag;
    }
    return *this;
}

Vector Vector::negative() {
    this->x *= -1;
    this->y *= -1;
    this->z *= -1;
    return *this;
}

void Vector::swap(Vector& rhs) {
    using std::swap;

    swap(this->x, rhs.x);
    swap(this->y, rhs.y);
    swap(this->z, rhs.z);
}

void Vector::print_vector() {
    std::cout 
        << x 
        << " " 
        << y 
        << " "
        << z 
        << std::endl;
}

Solution

  • The issues are in the Vector class.

    You implement the +, -, *, / (double) the same way you implement +=, -= (const Vector&): you change the value of this.

    When implementing the binary operator (the first operand is this and the second one rhs), you usually do not want to change the value of the operands. In these case, I strongly suggest you to use const for the operator to be warned in case of mistakes like this.

    Vector operator+(const double& rhs) const;
    

    Instead of:

    Vector operator+(const double& rhs);
    

    Then, the implementation is:

    Vector Vector::operator+(const double& rhs) const {
        Vector result(*this);
        result.x += rhs;
        result.y += rhs;
        result.z += rhs;
    
        return result;
    }