I didn’t want to post all my code, as it’s quite long, but I’ve got a silly bug somewhere, and I just can’t find it. I’ve spent a week debugging this badboy, to no avail.
As the image is coming out warped, I get the feeling that the error is somewhere in my camera code. If someone could have a quick looksie, that would be awesome.
You might need to cut and paste into notepad 
Camera Code :
using System;
using CsGL.OpenGL;
using MathBox;
using System.IO;
namespace OpenGLWindow
{
public class Camera
{
Vector3 Eye = new Vector3();
Vector3 Target = new Vector3();
Vector3 Up = new Vector3();
Vector3 Equator = new Vector3();
Vector3 Centre = new Vector3();
private double Pitch, Yaw, Roll;
// access attributes
public Vector3 GetEye()
{
return new Vector3(this.Eye.x, this.Eye.y, this.Eye.z);
}
public Vector3 GetTarget()
{
return new Vector3(this.Target.x, this.Target.y, this.Target.z);
}
public double GetPitch() { return this.Pitch; }
public double GetYaw() { return this.Yaw; }
public Camera()
{
Yaw = 0.0;
Pitch = 0.0;
Eye = new Vector3(0.0, 1.0, -5.0);
Target = new Vector3();
}
public void Update()
{
const double RadToDeg = 57.2957795;
Target.x = Eye.x + Math.Sin(2 * Yaw / RadToDeg);
Target.z = Eye.z + Math.Cos(2 * Yaw / RadToDeg);
Target.y = Eye.y + Math.Sin(2 * Pitch / RadToDeg);
Up.x = Target.x - Eye.x;
Vector3 Temp = Target - Eye;
double Radius = Temp.Magnitude;
Equator = Vector3.GetCrossProduct(Temp, Up);
Centre = this.Eye + Target;
Up.y = Math.Abs(Eye.y + (Radius * Math.Sin(Pitch + Math.PI / 2)));
Up.z = Target.z - Eye.z;
GL.gluLookAt(Eye.x, Eye.y, Eye.z, Target.x, Target.y, Target.z, 0, 1, 0);
}
/// <summary>
/// Epic Hack! This uses the opengl world transformation pipeline to correctly create the ray. This will be
/// very expensive, but will have to do for now!
/// </summary>
/// <param name="xPixel">The x coordinate of the pixel we are drawing.</param>
/// <param name="yPixel">The y coordinate of the pixel we are drawing.</param>
/// <param name="mProjection">The current opengl projection matrix</param>
/// <param name="mModelView">The current opengl modelview matrix</param>
/// <param name="Viewport">The opengl viewport data</param>
/// <returns>A correctly constructed ray in opengl world coordinates</returns>
public Ray CreateRay(int xPixel, int yPixel)
{
Ray Ray;
//Stream s = File.Open("C:\\RTRACER_RayEqns.txt", FileMode.Append, FileAccess.Write, FileShare.None);
//StreamWriter Sw = new StreamWriter(s);
unsafe
{
Vector3 RayDirection;
Vector3 RayFar = new Vector3();
Vector3 RayNear = new Vector3();
double rx = 0;
double ry = 0;
double rz = 0;
double[] mProjection = new double[16];
double[] mModelView = new double[16];
int[] Viewport = new int[4];
// if anyone knows anything about cleaning this area up so it looks a bit nicer, that would be great :)
fixed (double* pMView = &mModelView[0])
{
fixed (double* pProj = &mProjection[0])
{
fixed (int* pViewport = &Viewport[0])
{
// populate matricies and viewport data
GL.glGetDoublev(GL.GL_MODELVIEW_MATRIX, mModelView);
GL.glGetDoublev(GL.GL_PROJECTION_MATRIX, mProjection);
GL.glGetIntegerv(GL.GL_VIEWPORT, Viewport);
// calculate ray direction by doing near plane intersection and far plane intersection
int Foo = GLU.gluUnProject((double)xPixel, (double)(Viewport[3]-1)-yPixel, 0.0, pMView, pProj, pViewport, &rx, &ry, &rz);
RayNear = new Vector3(rx, ry, rz);
int Bar = GLU.gluUnProject((double)xPixel, (double)(Viewport[3] - 1) - yPixel, 1.0, pMView, pProj, pViewport, &rx, &ry, &rz);
RayFar = new Vector3(rx, ry, rz);
// compute ray direction from far plane intersect - near plane, and remember to normalize!
RayDirection = RayFar - RayNear;
Vector3.Normalize(RayDirection);
// build ray from eye point as origin, raydirection as the ray point on the farplane - the point on the ray near plane.
Ray = new Ray(this.GetEye(), RayDirection);
}
}
}
}
return Ray;
}
public void RotateCamera(double deltax, double deltay)
{
Yaw -= 0.1 * deltax;
Pitch -= 0.1 * deltay;
}
public void DollyCamera(double zAmount)
{
Vector3 LocalZ = this.Target - this.Eye;
this.Target += LocalZ * zAmount;
this.Eye += LocalZ * zAmount;
}
public void StrafeCamera(double xAmount)
{
Vector3 LocalXAxis = Vector3.GetCrossProduct(Target - Eye, Up);
this.Eye += LocalXAxis * xAmount;
this.Target += LocalXAxis * xAmount;
}
/*
public Ray CreateRay(int xPixel, int yPixel, int ViewportWidth, int ViewportHeight)
{
double xp = xPixel * 1.0f / ViewportWidth * 2 - 1;
double yp = (yPixel+1) * 1.0f / (ViewportHeight) * 2 - 1;
yp = -yp;
Vector3 Pos = this.Centre - Up * yp - Equator * xp;
Vector3 Dir = Pos - Eye;
Vector3.Normalize(Dir);
Ray ray = new Ray(Pos, Dir);
return ray;
}
}
*/
}
Raytracer code. I’m going to dbl check i’ve implemented the ray-triangle intersection correctly, but if someone could have a look through in the meantime, this is driving me nuts.
using System;
using System.Collections.Generic;
using System.Drawing;
using MathBox;
namespace OpenGLWindow
{
public class Ray
{
public Vector3 Origin;
public Vector3 Direction;
public Ray(Vector3 Origin, Vector3 Direction)
{
this.Origin = Origin;
this.Direction = Direction;
}
public override string ToString()
{
return ("{Origin : " + Origin.ToString() + " Direction : " + Direction.ToString() + "}");
}
}
class Raytracer
{
private int ShadedPixels;
private int FrustumPixels;
public Raytracer()
{
ShadedPixels = 0;
FrustumPixels = 0;
}
public Color TraceRay(Ray R, List<Mesh> Scenegraph, List<Light> SceneLights)
{
Color FinalColour = new Color(0.0, 0.0, 0.0);
RayHitInfo Closest = IntersectRayScene(R, Scenegraph);
// no hit, fill with background colour
if (!Closest.Hit)
{
FrustumPixels++;
return new Color(0.5, 0.5, 0.5);
}
else
{
ShadedPixels++;
return Closest.Element.Material.DiffuseColour;
}
// got a hit, so shade-lambert diffuse;
// TODO : Shade ambient, specular
FinalColour = new Color(1.0, 1.0, 1.0);
//foreach (Light L in SceneLights)
//{
// FinalColour += L.DiffuseIntensity * L.DiffuseColour * (Vector3.DotProduct(Closest.Normal, L.Position));
//}
// shade ambient
// shade specular
// shade shadows
// spawn reflection
// spawn refraction
return FinalColour;
}
/// <summary>
/// Tests pixel ray against all meshes in scene. Returns the ray hit info corresponding to the nearest valid hit. Returns frustum colour if no hit.
/// </summary>
/// <param name="R"></param>
/// <param name="Scenegraph"></param>
/// <returns></returns>
private RayHitInfo IntersectRayScene(Ray R, List<Mesh> Scenegraph)
{
RayHitInfo Closest = new RayHitInfo();
foreach (Mesh M in Scenegraph)
{
RayHitInfo Current = this.RayMeshIntersection(R, M);
if ((Current.tDistance < Closest.tDistance) & Current.Hit)
{
Closest = Current;
Closest.Element = M;
}
}
return Closest;
}
/// <summary>
/// Raytracing intersection algorithm based on molleur-trombadore 1995 "Minimum storage ray-triangle intersection"
/// </summary>
/// <param name="R"></param>
/// <param name="M"></param>
/// <returns></returns>
public RayHitInfo RayMeshIntersection(Ray R, Mesh M)
{
// optimizations : BVH
// backfacing sort
// R.Direction.y = -R.Direction.y; // HACK : Something is wrong with the way the y coordinate is being generated.
RayHitInfo Hit = new RayHitInfo();
RayHitInfo Final = new RayHitInfo();
for (int p = 0; p != M.Index.Count; p += 9)
{
Triangle T = new Triangle(M.Vertex[M.Index[p]], M.Vertex[M.Index[p + 3]], M.Vertex[M.Index[p + 6]]);
// TODO : Profile the culling step
Hit = this.IntersectTriangle(R, T, false);
if (Hit.tDistance < Final.tDistance) // HIT.HIT
{
Final = Hit.Clone();
Final.TriangleHit = p;
}
}
return Final;
}
// molleur-trombdore (1995)
RayHitInfo IntersectTriangle(Ray Ray, Triangle T, bool Cull)
{
double Epsilion = 0.000001; // fudge number for extreme glancing ray hits, the ray is practically parallel to the plane
RayHitInfo Hit = new RayHitInfo();
// scalar determinants
double Determinant, InverseDeterminant; // inverse for double sided intersection testing
// triangle edges
Vector3 Edge1 = T.v1 - T.v0;
Vector3 Edge2 = T.v2 - T.v0;
// hit vectors
Vector3 tVec = new Vector3();
Vector3 pVec = new Vector3();
Vector3 qVec = new Vector3();
pVec = Vector3.GetCrossProduct(Ray.Direction, Edge2); // begin calculating determinant for u
Determinant = Vector3.DotProduct(Edge1, pVec); // if det approaches 0, ray lies in plane of triangle.
if (Cull)
{
if (Determinant < Epsilion)
return new RayHitInfo(); // ray glances triangle, return no hit;
// calculate distance from vertex 0 to the ray origin.
tVec = Ray.Origin - T.v0;
Hit.HitPoint.u = Vector3.DotProduct(tVec, pVec);
if (Hit.HitPoint.u < 0.0 || Hit.HitPoint.u > Determinant) // hit point is outside triangle;
return new RayHitInfo(); // return no hit.
// u parameter seems valid, now lets move onto v;
qVec = Vector3.GetCrossProduct(tVec, Edge1);
Hit.HitPoint.v = Vector3.DotProduct(Ray.Direction, qVec);
// test v parameter for acceptable barycentric intersection
if (Hit.HitPoint.v < 0.0 || (Hit.HitPoint.u + Hit.HitPoint.v) > Determinant)
return new RayHitInfo();
// ray must have hit triangle, so get the hit distance
Hit.tDistance = Vector3.DotProduct(Edge2, qVec);
InverseDeterminant = 1.0 / Determinant; // get inverse of determinant
Hit.Hit = true;
Hit.tDistance *= InverseDeterminant;
Hit.HitPoint.u *= InverseDeterminant;
Hit.HitPoint.v *= InverseDeterminant;
}
else
{
if (Determinant > -Epsilion && Determinant < Epsilion)
return new RayHitInfo();
InverseDeterminant = 1.0 / Determinant;
tVec = Ray.Origin - T.v0;
// get U parameter and test bounds
Hit.HitPoint.u = Vector3.DotProduct(tVec, pVec) * InverseDeterminant;
// we are out of the bounds of the triangle, so no hit.
if (Hit.HitPoint.u < 0.0 || Hit.HitPoint.u > 1.0)
return new RayHitInfo();
// now we are ready to test the v parameter.
qVec = Vector3.GetCrossProduct(tVec, Edge1);
// get the v parameter
Hit.HitPoint.v = Vector3.DotProduct(Ray.Direction, qVec) * InverseDeterminant;
// if both hit points are in the acceptable range then ray intersects triangle
if (Hit.HitPoint.v < 0.0 || (Hit.HitPoint.u + Hit.HitPoint.v) > 1.0)
return new RayHitInfo();
// definite intersection, get distance to hit
Hit.Hit = true;
Hit.tDistance = Vector3.DotProduct(Edge2, qVec) * InverseDeterminant;
}
return Hit;
}
/// <summary>
/// HACK. Correctly renders an opengl scene. Ultimately, it will either need to transform all meshes, OR use a matrix based projection
/// system.
/// </summary>
/// <param name="Scenegraph"></param>
/// <param name="xRes"></param>
/// <param name="yRes"></param>
/// <param name="FrameBuffer"></param>
/// <param name="CurrentCamera"></param>
/// <param name="Scenelights"></param>
/// <param name="mProjection"></param>
/// <param name="mModelView"></param>
/// <param name="Viewport"></param>
public void RenderOpenGLScene(List<Mesh> Scenegraph, Color[,] Buffer, int Width, int Height, Camera CurrentCamera, List<Light> Scenelights)
{
int DEBUG_PixelsShaded = 0;
Decimal Percentage = 0;
for (int y = Height-1; y != -1; y--)
{
for (int x = 0; x != Width; x++)
{
if (Scenegraph.Count > 0)
{
Ray R = CurrentCamera.CreateRay(x, y);
//Ray DEBUGONLY = CurrentCamera.CreateRay(x, y, Width, Height);
Buffer[x,y] = this.TraceRay(R, Scenegraph, Scenelights);
}
DEBUG_PixelsShaded++;
decimal Det = ((decimal)Width * (decimal)Height) / DEBUG_PixelsShaded;
Percentage = 100 / Det;
Percentage = Decimal.Truncate(Percentage);
}
}
}
}
}