我的光线追踪器中的照明工作奇怪

时间:2016-05-28 23:15:33

标签: c++ opengl raytracing

这是我正在研究的光线跟踪器代码。当我测试它时,一切似乎都工作正常,直到我开始改变相机(观察点)的位置。以下是一些结果:

enter image description here 坎波斯(-60,100,-30),lightPos(-70,100,-30)

地板上的灯被切断了。

enter image description here 坎波斯(60,100,-30),lightPos(-70,100,-30)

这个问题显示了同样的问题。

enter image description here 坎波斯(60,30,-30),lightPos(-70,100,-30)

此屏幕截图中的灯光似乎有两个光源,但目前只有一个有效。

enter image description here 坎波斯(-70,100,-30),lightPos(-70,100,-30)

最终位置是我在下面的代码中设置的最后一个位置。它与光明的位置完全相同。

为什么光会像这样产生阴影?

的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

1 个答案:

答案 0 :(得分:0)

这是一个糟糕的代码,所以我只能猜测问题是什么。由于问题出在图像的非阴影部分,因此问题在于计算diffusespecular颜色(或两者)。你可以单独注释每一个,看看是什么给你预期的颜色,然后从那里进一步诊断问题。

问题可能出在您的normalize方法中,该方法不会对真正的短向量进行标准化。这会导致specular颜色关闭。