Click here to Skip to main content
15,891,749 members
Articles / Programming Languages / C#

Yet Another RayTracer for .NET

Rate me:
Please Sign up or sign in to vote.
4.87/5 (56 votes)
29 Mar 2007CPOL15 min read 129.8K   3.9K   124  
This article is meant as an introduction to raytracing and explains the basic techniques to raytrace a scene.
// Copyright 2006 Herre Kuijpers - <herre@xs4all.nl>
//
// This source file(s) may be redistributed, altered and customized
// by any means PROVIDING the authors name and all copyright
// notices remain intact.
// THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED. USE IT AT YOUR OWN RISK. THE AUTHOR ACCEPTS NO
// LIABILITY FOR ANY DATA DAMAGE/LOSS THAT THIS PRODUCT MAY CAUSE.
//-----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;

namespace RayTracer
{
    public class BoxShape : BaseShape
    {
        public Vector UpperLeftFront;
        public Vector LowerRightBack;

        public BoxShape(Vector ULF, Vector LRB, IMaterial material)
        {
            UpperLeftFront = ULF;
            LowerRightBack = LRB;
            Position = (ULF + LRB) / 2;
            Material = material;
            
        }

        public override IntersectInfo Intersect(Ray ray)
        {
            IntersectInfo best = new IntersectInfo();
            best.Distance = double.MaxValue;
            IntersectInfo info = null;

            //check each side of the box
            // this is quite a crude implementation and can be optimized

            Vector P1 = null;
            Vector P2 = null;
            Vector P3 = null;
            if (ray.Direction.z > 0)
            {
                //front
                P1 = UpperLeftFront;
                P2 = new Vector(LowerRightBack.x, LowerRightBack.y, P1.z);
                P3 = new Vector(P1.x, P2.y, P1.z);
                info = IntersectSlab(ray, P1, P2, P3);
                if (info.IsHit && info.Distance < best.Distance)
                    best = info;
            }
            else
            {
                //backside
                P1 = new Vector(UpperLeftFront.x, UpperLeftFront.y, LowerRightBack.z);
                P2 = LowerRightBack;
                P3 = new Vector(P1.x, P2.y, P1.z);
                info = IntersectSlab(ray, P1, P2, P3);
                if (info.IsHit && info.Distance < best.Distance)
                    best = info;
            }

            if (ray.Direction.x < 0)
            {
                //right side
                P1 = new Vector(LowerRightBack.x, UpperLeftFront.y, UpperLeftFront.z);
                P2 = LowerRightBack;
                P3 = new Vector(P1.x, P2.y, P1.z);
                info = IntersectSlab(ray, P1, P2, P3);
                if (info.IsHit && info.Distance < best.Distance)
                    best = info;
            }
            else
            {
                //left side
                P1 = UpperLeftFront;
                P2 = new Vector(UpperLeftFront.x, LowerRightBack.y, LowerRightBack.z);
                P3 = new Vector(P1.x, P2.y, P1.z);
                info = IntersectSlab(ray, P1, P2, P3);
                if (info.IsHit && info.Distance < best.Distance)
                    best = info;
            }

            if (ray.Direction.y < 0)
            {
                //top side
                P1 = UpperLeftFront;
                P2 = new Vector(LowerRightBack.x, UpperLeftFront.y, LowerRightBack.z);
                P3 = new Vector(P2.x, P1.y, P1.z);
                info = IntersectSlab(ray, P1, P2, P3);
                if (info.IsHit && info.Distance < best.Distance)
                    best = info;
            }
            else
            {
                //bottom side
                P1 = new Vector(UpperLeftFront.x, LowerRightBack.y, UpperLeftFront.z);
                P2 = LowerRightBack;
                P3 = new Vector(P2.x, P1.y, P1.z);
                info = IntersectSlab(ray, P1, P2, P3);
                if (info.IsHit && info.Distance < best.Distance)
                    best = info;
            }
            return best;

        }

        private IntersectInfo IntersectSlab(Ray ray, Vector P1, Vector P2, Vector P3)
        {
            Vector N = (P1 - P3).Cross(P2 - P3).Normalize();
            double d = N.Dot(P1);
            IntersectInfo info = new IntersectInfo();
            double Vd = N.Dot(ray.Direction);
            if (Vd == 0) return info; // no intersection

            double t = -(N.Dot(ray.Position) - d) / Vd;

            if (t <= 0) return info;
            Vector hit = ray.Position + ray.Direction * t;

            if ((hit.x < P1.x || hit.x > P2.x) && (P1.x != P2.x)) return info;
            if ((hit.y < P3.y || hit.y > P1.y) && (P1.y != P2.y)) return info;
            //if ((hit.z < P1.z || hit.z > P3.z) && (P1.z != P3.z)) return info;
            if ((hit.z < P1.z || hit.z > P2.z) && (P1.z != P2.z)) return info;


            info.Element = this;
            info.IsHit = true;
            info.Position = hit;
            info.Normal = N;// *-1;
            info.Distance = t;

            if (Material.HasTexture)
            {
                //Vector vecU = new Vector(hit.y - Position.y, hit.z - Position.z, Position.x-hit.x);
                Vector vecU = new Vector((P1.y + P2.y) / 2 - Position.y, (P1.z + P2.z) / 2 - Position.z, Position.x - (P1.x + P2.x) / 2).Normalize();
                Vector vecV = vecU.Cross((P1+P2)/2 - Position).Normalize();

                double u = info.Position.Dot(vecU);
                double v = info.Position.Dot(vecV);
                info.Color = Material.GetColor(u, v);
            }
            else
                info.Color = Material.GetColor(0, 0);

            return info;
        }

        public override string ToString()
        {
            return string.Format("Box ({0},{1},{2})-({3},{4},{5})", UpperLeftFront.x, UpperLeftFront.y, UpperLeftFront.z, LowerRightBack.x, LowerRightBack.y, LowerRightBack.z);
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Rubicon
Netherlands Netherlands
Currently Herre Kuijpers is employed at Rubicon. During his career he developed skills with all kinds of technologies, methodologies and programming languages such as c#, ASP.Net, .Net Core, VC++, Javascript, SQL, Agile, Scrum, DevOps, ALM. Currently he fulfills the role of software architect in various projects.

Herre Kuijpers is a very experienced software architect with deep knowledge of software design and development on the Microsoft .Net platform. He has a broad knowledge of Microsoft products and knows how these, in combination with custom software, can be optimally implemented in the often complex environment of the customer.

Comments and Discussions