Code:
#include "CCanvas.h"
#include "Color.h"
#include "Light.h"
#include "Texture.h"
#include "Object.h"
#include "Sphere.h"
using namespace std;
double objectZ;
/************************************************************************/
/* Initializes the lights and objects */
/************************************************************************/
void CCanvas::initScene()
{
static bool isInit = false;
if (isInit)
return;
//
// material coefficients for the first object (a sphere)
//
Material ObjMat;
ObjMat.ambient = Color3d(0.2, 0.2, 0.2);
ObjMat.diffuse = Color3d(0.8, 0.2, 0.2);
ObjMat.specular = Color3d(0.5, 0.5, 0.5);
ObjMat.shininess = 10.0;
ObjMat.reflectivity = 0.0;
Sphere* sphere = new Sphere(ObjMat);
sphere->translate(0.0, 0.0, 10.0);
sphere->scale(2.0, 2.0, 2.0);
objects.push_back(sphere);
//
// light source 1 (white light from top left)
//
Material LightCol;
LightCol.ambient = Color3d(0.6, 0.6, 0.6);
LightCol.diffuse = Color3d(1.0, 1.0, 1.0);
LightCol.specular = Color3d(1.0, 1.0, 1.0);
Point3d LightPos(-10.0, 5.0, -5.0);
Light* light = new Light(LightPos, LightCol);
light->setDirectional();
lights.push_back(light);
isInit = true;
cout<<"Scene initialized..."<<endl;
};
/************************************************************************/
/* For the given ray, this function finds the closest intersection with */
/* the scene objects and returns a pointer to the nearest object. */
/************************************************************************/
Object* CCanvas::findNearestObject(Ray ray)
{
Object* nearestObject = NULL; // pointer to the nearest object
// loop over all objects
vector< Object* >::iterator object;
for (object = objects.begin(); object < objects.end(); object++)
{
// ignore the current object (if any)
// [useful for avoiding numerical problems when tracing shadow or reflection rays]
if ( (*object) == currentObject )
continue;
// does the ray intersect with the current object?
if ( (*object)->computeIntersection(ray) )
{
// is the intersection point closer than the currently closest (if there already is one)?
if ( (nearestObject == NULL) || ( (*object)->getDistance() < nearestObject->getDistance() ) )
nearestObject = *object;
}
}
return nearestObject;
}
/************************************************************************/
/* Tests, if point p is lit by light l, or if it lies in the shadow. */
/************************************************************************/
bool CCanvas::inShadow(Point3d p, Light* l)
{
// set up the shadow ray
Ray ray;
ray.origin = p;
ray.direction = l->getLightVector(p);
// find closest intersection point
Object* nearestObject = findNearestObject(ray);
// is there an intersection along the shadow ray?
if (nearestObject != NULL)
{
if (l->isDirectional) // in case of a directional light, the intersected object
return true; // is always between p and the light
// otherwise, check against the distance to the light
double lightDistance = (l->getPosition() - p).norm();
if (nearestObject->getDistance() < lightDistance)
return true;
}
// either no intersection or object behind the light
return false;
}
/************************************************************************/
/* Function loops over all light sources and calculates the lighting */
/* for the surface point "p" with normal "n" of a visible object with */
/* material "mat"; the camera is in direction "v" (view vector) */
/************************************************************************/
Color3d CCanvas::PhongLighting(Point3d p, Point3d n, Point3d v, Material mat)
{
Color3d finalColor;
// loop over all light sources
vector< Light* >::iterator light;
for (light = lights.begin(); light < lights.end(); light++)
{
// get the light's colour
Material lightColor = (*light)->getColor(p);
// compute light vector
Point3d l = (*light)->getLightVector(p);
// cosine of the angle between normal and light vector
double cos_phi = n*l;
// in any case, the object receives the ambient light from the current light source
Color3d color = mat.ambient * lightColor.ambient;
// check if light source is not hidden by the object itself or by some other object
if ( ! ( (cos_phi < 0) || inShadow(p, *light) ) )
{
// compute and add diffuse reflection
color += mat.diffuse * lightColor.diffuse * cos_phi;
// compute and add specular reflection
Point3d r = 2*n*(n*l) - l;
r.normalize();
double cos_alpha = r*v;
if (cos_alpha < 0.0)
cos_alpha = 0.0;
color += mat.specular * lightColor.specular * pow(cos_alpha, mat.shininess);
}
color.clamped();
// add up colours from all light sources
finalColor += color;
}
// clamp colour
finalColor.clamped();
return finalColor;
}
/************************************************************************/
/* This function finds the closest object along the givevn ray and */
/* evokes the lighting calculation for the intersection point. */
/************************************************************************/
Color3d CCanvas::trace(Ray ray, int cnt)
{
// termination criterion
if (cnt > 3)
return Color3d(0.0, 0.0, 0.0);
// find closest intersection point
Object* nearestObject = findNearestObject(ray);
// compute lighting at this intersection point
if (nearestObject != NULL)
{
IntersectionInfo intersection = nearestObject->getIntersectionInfo();
if (cnt == 0)
objectZ = intersection.point.z();
if (intersection.inside)
{
// if camera is inside some object, then no complex lighting calculation, just black colour
return Color3d(0.0, 0.0, 0.0);
}
else
{
// call "PhongLighting" for lighting calculation
currentObject = nearestObject;
Color3d color = PhongLighting(intersection.point, intersection.normal, -ray.direction, nearestObject->getMaterial());
// set up reflection ray
Ray reflection;
reflection.origin = intersection.point;
reflection.direction = - 2.0 * intersection.normal * (intersection.normal * ray.direction) + ray.direction;
// trace reflection ray and get reflected colour
Color3d reflectionColor = nearestObject->getMaterial().reflectivity * trace(reflection, cnt+1);
reflectionColor.clamped();
// add up colours
color += reflectionColor;
color.clamped();
return color;
}
}
else
{
// no object was intersected, so set the background colour for current pixel
return Color3d(1.0, 1.0, 1.0);
}
}
/************************************************************************/
/* paint to the image */
/************************************************************************/
void CCanvas::paint()
{
// check if we have something to draw on
if (!image) return;
// initialize objects and lights
initScene();
// camera position
Point3d camera (0.0, 0.0, 0.0);
// screen resolution
int w = image->width();
int h = image->height();
// horizontal opening angle
double alpha = PI/2.0;
// horizontal window dimension [-X,X]
double X = tan(alpha/2.0);
// pixel size
double s = 2.0 * X / (double)w;
// vertical window dimension [-Y,Y]
double Y = s * h / 2.0;
// animation parameter
tau += 0.05;
// z-Buffer
vector<double> zBuffer(w*h);
// loop through all pixels of the screen
for (int i=0; i<w; i++) {
for (int j=0; j<h; j++) {
// coordinates (x,y,z) of pixel (i,j)
double x = -X + (i+0.5) * s;
double y = Y - (j+0.5) * s;
double z = 1.0;
// ray through pixel (i,j):
// gamma(t) = t * d, where t >= 0 and ||d|| = 1
Ray ray;
ray.origin = Point3d(0.0, 0.0, 0.0);
ray.direction = Point3d(x,y,z);
ray.direction.normalize();
// trace the ray and get the colour of the nearest object
currentObject = NULL;
objectZ = -1.0;
Color3d color = trace(ray,0);
image->setPixel(i,j, qRgb( (int) 255*color.r(), (int) 255*color.g(), (int) 255*color.b() ));
zBuffer[i+j*w] = 1.0 / objectZ;
}
}
//
// rasterize a line
//
// line from p1 to p2 in local coordinates
Point3d p1 (-1.0, 0.0, 0.0);
Point3d p2 ( 1.0, 0.0, 0.0);
// transformations...
Matrix4d S = Matrix4d::scaling (5.0, 1.0, 1.0);
Matrix4d R1 = Matrix4d::rotation (0.5,'z');
Matrix4d R2 = Matrix4d::rotation (tau,'x');
Matrix4d T = Matrix4d::translation (0.0, 0.0, 8.0);
Matrix4d P = Matrix4d::projection (X,Y,s);
Matrix4d M = P*T*R2*R1*S;
p1 = M*p1;
p2 = M*p2;
// screen coordinates
double x1 = p1.x();
double y1 = p1.y();
double z1 = p1.z();
double x2 = p2.x();
double y2 = p2.y();
double z2 = p2.z();
//
// line rasterization algorithm with anti-aliasing and z-test
// (for lines with slope between -1 and 1)
//
int x = round(x1);
int y = round(y1);
double I0 = 1.0;
double m = (y2-y1) / (x2-x1);
bool up = true;
if (y2 < y1) {
m = -m;
I0 = 0.0;
up = false;
}
int nextY;
double z;
for ( ; x<=x2; x++) {
if (up)
nextY = y+1;
else
nextY = y-1;
// barycentric coordinates of the current point: (1-lambda, lambda),
// i.e.: x = (1-lambda) x1 + lambda x2
double lambda = (x-x1) / (x2-x1);
// linear interpolation of the inverse z-values
z = (1.0-lambda)*z1 + lambda*z2;
if ( z > zBuffer[x+y*w] ) {
int I = (int) 255*I0;
image->setPixel( x, y, qRgb( 255-I, 255, 255-I ) );
image->setPixel( x, nextY, qRgb( I, 255, I ) );
}
if (m > I0) {
y = nextY;
I0 += 1.0;
}
I0 -= m;
}
// repaint canvas
repaint();
}
structs.h (Do not kill for using structs in C++ . The file was given)
Code:
Ray rayLine;
rayLine.origin = Point3d(x,y,z1);//(p1.x(), p1.y(), p1.z());
rayLine.direction = Point3d(x2,y2,z2);
rayLine.direction.normalize();
Color3d backgroundColor = trace(rayLine,0);
Object* objInter = findNearestObject(rayLine);
if(objInter!=NULL) cout<<"ok\n";
int I = (int) 255*I0;
if(backgroundColor.r() != 1.0 && backgroundColor.g() != 1.0
&& backgroundColor.b() != 1.0)
cout<<backgroundColor.r()<<" "<<backgroundColor.g()<<" "<<backgroundColor.b()<<endl;
image->setPixel( x, y, qRgb( (255-I) + (int)255 * backgroundColor.r(), 255 + (int)255* backgroundColor.g(), (255-I) + (int)255* backgroundColor.b() ) );
image->setPixel( x, nextY, qRgb( I + (int)255 * backgroundColor.r(), 255 + (int)255* backgroundColor.g(), I + (int)255* backgroundColor.b()) );
Hope somebody has something to say.