Click here to Skip to main content
12,761,464 members (34,676 online)
Click here to Skip to main content
Add your own
alternative version

Stats

100.5K views
2.3K downloads
157 bookmarked
Posted 24 Jul 2007

Simple Ray Tracing in C#

, 15 Jul 2016 GPL3
Rate this:
Please Sign up or sign in to vote.
Simple Ray Tracing in C#

 

Screenshot - rt.png

Introduction

This article demonstrates how to make a simple and very basic Ray Tracing with spheres; it can serve you as a base for implementing more complex algorithms. In a future article I will show how to improve this using image mapping.

Background

The Ray Tracing process is an approach to generate high quality computer graphics, as deeper the level of recursivity interaction with the 3D objects it has more photo realistic appearing.

The Ray Tracing Algorithm is implemented by calculating the intersection of 3D lines with the 3D objects in the model. The first point of the 3D line we define as the viewer position, the endpoint is located at the projection plane chosen. So for each discrete point at the projection plane an equation for the line is obtained and it is calculated all intersections with all objects selecting the intersection which are nearest to the viewer, so the color is plotted. Using this approach the implemented algorithm can handle opacity, light, shadows and so on.

3D lines equations can be represented in the form:

Where (px,py,pz) are all the points lying in the 3D line, t is a scalar parameter, and (vx,vy,vz) is a direction vector.

The above equation can be obtained by the definition where a line can be defined by 2 points, so given P1(x1,y1,z1) and P2(x2,y2,z2) we have:

replacing the found v and p we have:

So we can be sure that all (x,y,z) which satisfies the above equation belongs to the line defined by P1P2.

Spheres can be represented in the form:

where

So we can be sure that all x,y,x points lies on the sphere surface.

Our objective now is to determine the intersection equation between a given line and a sphere it must be a set of (x,y,z) points which satisfies both equations. It is simple to imagine that a line intersecting a sphere can result 0 intersections, 1 intersection (if tangent) or at most 2 intersections.

The Equations

Replacing x, y and z we have:

Let's create a variable for the vector:

So now we have:

Let's replace (x1,y1,z1) with (px,py,pz) just to simplify...

Now we have a perfect 2nd degree equation which can give us 0, 1 or 2 different solutions for 't':

double A = (vx * vx + vy * vy + vz * vz);
double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
           pz * pz - 2 * pz * cz    + cz * cz - radius * radius;
double D = B * B - 4 * A * C;
double t = -1.0;
if (D >= 0)
     {
     double t1 = (-B - System.Math.Sqrt(D)) / (2.0 * A);
     double t2 = (-B + System.Math.Sqrt(D)) / (2.0 * A);
     if (t1 > t2)
          t = t1;
     else
          t = t2;  // we choose the nearest t from the first point
     }

The Source Code

<script language="C#" runat="server">
private void Page_Load(object sender, System.EventArgs e)
    {
    Bitmap newBitmap = new Bitmap(200, 200, PixelFormat.Format32bppArgb);
    Graphics g = Graphics.FromImage(newBitmap);

    Color clrBackground = Color.Black;
    g.FillRectangle(new SolidBrush(clrBackground), new Rectangle(0, 0, 200,
                    200));
    Rectangle rect = new Rectangle(0, 0, 200, 200);

    System.Collections.ArrayList obj3dArrayList;
    obj3dArrayList = new System.Collections.ArrayList();
    obj3dArrayList.Add(new Sphere(0.0, 0.0, 90.0, 100.0, 0.0, 0.0, 255.0));
    obj3dArrayList.Add(new Sphere(-180.0, -130.0, -110.0, 15.0, 255.0, 0.0,
                       0.0));
    obj3dArrayList.Add(new Sphere(-140.0, -140.0, -150.0, 20.0, 255.0, 200.0,
                       0.0));
    Graphics graphics = g;
    // viewer position
    double px = (double)Session["eyex"],
    py = (double)Session["eyey"],
    pz = (double)Session["eyez"];
    // light position
    double lpx = (double)Session["lpx"],
    lpy = (double)Session["lpy"],
    lpz = (double)Session["lpz"];
    // light direction
    double lvx = (double)Session["lvx"],
    lvy = (double)Session["lvy"],
    lvz = (double)Session["lvz"];
    double fMax = 200.0;
    for (int i = rect.Left; i <= rect.Right; i++)
        {
        double x = Sphere.GetCoord(rect.Left, rect.Right, -fMax, fMax, i);
        for (int j = rect.Top; j <= rect.Bottom; j++)
        {
            double y = Sphere.GetCoord(rect.Top, rect.Bottom, fMax, -fMax, j);
            double t = 1.0E10;
            double vx = x - px, vy = y - py, vz = -pz;
            double mod_v = Sphere.modv(vx, vy, vz);
            vx = vx / mod_v;
            vy = vy / mod_v;
            vz = vz / mod_v;
            bool bShadow = false;
            Sphere spherehit = null;
            for (int k = 0; k < (int)obj3dArrayList.Count; k++)
                {
                Sphere sphn = (Sphere)obj3dArrayList[k];
                double taux = Sphere.GetSphereIntersec(sphn.cx, sphn.cy,
                              sphn.cz,
                              sphn.radius, px, py, pz, vx, vy, vz);
                if (taux < 0) continue;
                if (taux > 0 && taux < t)
                {
                    t = taux;
                    spherehit = sphn;
                }
            }
            Color color = Color.FromArgb(10, 20, 10);
            if (spherehit != null)
            {
                double itx = px + t * vx, ity = py + t * vy, itz = pz +
                t * vz;
                // shadow
                double tauxla = Sphere.GetSphereIntersec(spherehit.cx,
                                spherehit.cy, spherehit.cz, spherehit.radius,
                                lpx, lpy, lpz, itx - lpx,
                                ity - lpy, itz - lpz);

                for (int k = 0; k < (int)obj3dArrayList.Count; k++)
                {
                    Sphere sphnb = (Sphere)(obj3dArrayList[k]);
                    if (sphnb != spherehit)
                    {
                        double tauxlb = Sphere.GetSphereIntersec(sphnb.cx,
                                        sphnb.cy, sphnb.cz, sphnb.radius, lpx,
                                        lpy, lpz, itx - lpx, ity - lpy, itz -
                                        lpz);
                        if (tauxlb > 0 && tauxla < tauxlb)
                        {
                            bShadow = true;
                            break;
                        }
                    }
                }
                double cost = Sphere.GetCosAngleV1V2(lvx, lvy, lvz, itx -
                              spherehit.cx, ity - spherehit.cy, itz -
                              spherehit.cz);
                if (cost < 0) cost = 0;
                double fact = 1.0;
                if (bShadow == true) fact = 0.5; else fact = 1.0;
                double rgbR = spherehit.clR * cost * fact;
                double rgbG = spherehit.clG * cost * fact;
                double rgbB = spherehit.clB * cost * fact;
                color = Color.FromArgb((int)rgbR, (int)rgbG, (int)rgbB);
                pen = new Pen(color);
            }
            Brush brs = new SolidBrush(color);
            graphics.FillRectangle(brs, i, j, 1, 1);
            brs.Dispose();

        }// for pixels lines
    }// for pixels columns
    ///////////////////////////////////////
    MemoryStream tempStream = new MemoryStream();
    newBitmap.Save(tempStream, ImageFormat.Png);
    Response.ClearContent();
    Response.ContentType = "image/png";
    Response.BinaryWrite(tempStream.ToArray());
    Response.Flush();
    }
</script>

The Sphere Class

public class Sphere
{
    public Sphere(double x, double y, double z, double r, double clr,
                  double clg, double clb)
    {
        cx = x;
        cy = y;
        cz = z;
        radius = r;
        clR = clr;
        clG = clg;
        clB = clb;
    }
    public static double GetCoord(double i1, double i2, double w1, double w2,
        double p)
    {
        return ((p - i1) / (i2 - i1)) * (w2 - w1) + w1;
    }
    public static double modv(double vx, double vy, double vz)
    {
        return System.Math.Sqrt(vx * vx + vy * vy + vz * vz);
    }
    void Move(double vx, double vy, double vz)
    {
        cx += vx;
        cy += vy;
        cz += vz;
    }
    void MoveTo(double vx, double vy, double vz)
    {
        cx = vx;
        cy = vy;
        cz = vz;
    }
    void RotX(double angle)
    {
        double y = cy * System.Math.Cos(angle) - cz * System.Math.Sin(angle);
        double z = cy * System.Math.Sin(angle) + cz * System.Math.Cos(angle);
        cy = y;
        cz = z;
    }
    void RotY(double angle)
    {
        double x = cx * System.Math.Cos(angle) - cz * System.Math.Sin(angle);
        double z = cx * System.Math.Sin(angle) + cz * System.Math.Cos(angle);
        cx = x;
        cz = z;
    }
    public static double GetSphereIntersec(double cx, double cy, double cz,
                         double radius, double px, double py, double pz,
                         double vx, double vy, double vz)
    {
        // x-xo 2 + y-yo 2 + z-zo 2 = r 2
        // x,y,z = p+tv
        // At2 + Bt + C = 0
        double A = (vx * vx + vy * vy + vz * vz);
        double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy *
                   cy - vz * cz);
        double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py *
                   cy + cy * cy + pz * pz - 2 * pz * cz + cz * cz -
                   radius * radius;
        double D = B * B - 4 * A * C;
        double t = -1.0;
        if (D >= 0)
        {
            double t1 = (-B - System.Math.Sqrt(D)) / (2.0 * A);
            double t2 = (-B + System.Math.Sqrt(D)) / (2.0 * A);
            if (t1 > t2) t = t1; else t = t2;
        }
        return t;
    }
    public static double GetCosAngleV1V2(double v1x, double v1y, double v1z,
                                         double v2x, double v2y, double v2z)
    {
        /* incident angle
         intersection pt (i)
        double ix, iy, iz;
        ix = px+t*vx;
        iy = py+t*vy;
        iz = pz+t*vz;
        normal at i
        double nx, ny, nz;
        nx = ix - cx;
        ny = iy - cy;
        nz = iz - cz;

        cos(t) = (v.w) / (|v|.|w|)
        */
        return (v1x * v2x + v1y * v2y + v1z * v2z) / (modv(v1x, v1y, v1z) *
               modv(v2x, v2y, v2z));
    }
    public double cx, cy, cz, radius, clR, clG, clB;
}

 

  • x = px + t*vx
  • y = py + t*vy
  • z = pz + t*vz
  • v = (x2-x1,y2-y1,z2-z1)
  • x = x1 + t*(x2-x1)
  • y = y1 + t*(y2-y1)
  • z = z1 + t*(z2-z1)
  • r2 = (x-cx)2+(y-cy)2+(z-cz)2
  • r is the sphere radius
  • (cx,cy,cz) is the center of the sphere
  • r2 = (x-cx)2+(y-cy)2+(z-cz)2
  • x = x1 + t*(x2-x1)
  • y = y1 + t*(y2-y1)
  • z = z1 + t*(z2-z1)
  • r2 = (x1 + t*(x2-x1)-cx)2 + (y1 + t*(y2-y1) -cy)2 + (z1+ t*(z2-z1)-cz)2
  • vx = x2 - x1
  • vy = y2 - y1
  • vz = z2 - z1
  • r2 = (x1-cx+t*vx)2 + (y1-cy+t*vy)2 + (z1-cz+t*vz)2
  • (x1-cx+t*vx)2 + (y1-cy+t*vy)2 + (z1-cz+t*vz)2 - r2 = 0

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

andalmeida
Engineer
Brazil Brazil
Senior Analyst

Linkedin Profile

You may also be interested in...

Comments and Discussions

 
QuestionFormulas are not visible Pin
Tachyonx5-Dec-16 1:35
memberTachyonx5-Dec-16 1:35 
Questionlink don't work Pin
Member 1243897430-Nov-16 0:40
memberMember 1243897430-Nov-16 0:40 
Questionproblem with starting the project Pin
Sayyam Kalra16-Oct-16 1:35
memberSayyam Kalra16-Oct-16 1:35 
GeneralMy vote of 3 Pin
Jason Curl18-Jul-16 9:33
professionalJason Curl18-Jul-16 9:33 
GeneralMy vote of 5 Pin
Cryptonite15-Jul-16 14:07
memberCryptonite15-Jul-16 14:07 
Praisevote of 5 Pin
Beginner Luck14-Feb-16 21:43
professionalBeginner Luck14-Feb-16 21:43 
QuestionCan you recommend some resource for a newbie who wants to learn ray tracing? Pin
Shao Voon Wong2-Sep-14 17:55
professionalShao Voon Wong2-Sep-14 17:55 
QuestionCode Error Pin
Amin al-Zanki18-Nov-12 8:33
memberAmin al-Zanki18-Nov-12 8:33 
GeneralMy vote of 5 Pin
manoj kumar choubey26-Feb-12 22:15
membermanoj kumar choubey26-Feb-12 22:15 
QuestionMy 5* Pin
Thomas.D Williams14-Aug-11 0:23
memberThomas.D Williams14-Aug-11 0:23 
GeneralMy vote of 5 Pin
manu_221b28-Jul-11 1:55
membermanu_221b28-Jul-11 1:55 
QuestionCan you help me please????? Pin
bobbyn9514-Jan-09 0:58
memberbobbyn9514-Jan-09 0:58 
Computer Graphics & Image Processing

Ray tracing is a well known technique to light up a scene in computer graphics .It involves the following steps which have to be adhered in order:

1. Parsing the scene (3D world) description file and identifying the object areas and filling in the Z buffers.
2. Calculate the ray object intersections.(A single ray is casted from a light source towards the scene)
3. Shade the object surfaces accordingly.
4. Recursive tracing.(A single ray will be reflect and refract through the surfaces. It’s sufficient to trace a ray till its energy becomes minimal or negligible.)


You are required to complete and submit the software and the report of the “Simple Ray Tracer” on or before the above mentioned deadline. Please follow the guidelines mentioned below:

1. Programming Language:- The best option for an application like this is C or C++ .If you are conversant in using the .NET platform you may choose any language which imports with it. JAVA is not recommended for this assignment.
2. Restrictions:-You cannot use any of the graphics packages or libraries which come with the language implementations or free to download (These include jME, XNA, OpenGL, JAI and other libraries which are free on net).
3. Mandatory Objects:-The program should be able to handle all the primitive 3D Graphic constructs and objects such as teapots in a textured environment.
4. Optional Objects and Effects:-If you could include features such as shadows, shading illumination, view frustum culling and transparency it will be regarded as a plus point.
5. Approach:-When developing the Ray Tracer the following guidelines will help you successfully complete it.
• Have a good idea what ray trcing is and find the equation(s) relating to this particular method.(Specially energy functions)
• Create a simple object and try o create matrices which will contain the distances from the light source.
• When shooting rays at first keep the light in equal grounds with the scene and once you succeed in doing so change the angle and the position.
• Also note that the energy of the bouncing ray is dependent on the surface it last bounced off. So surface properties do matter.
AnswerRe: Can you help me please????? Pin
andalmeida14-Jan-09 1:27
memberandalmeida14-Jan-09 1:27 
GeneralRe: Can you help me please????? Pin
bobbyn9515-Jan-09 15:30
memberbobbyn9515-Jan-09 15:30 
GeneralNice nice nice Pin
Windmiller13-Sep-07 2:09
memberWindmiller13-Sep-07 2:09 
GeneralRe: Nice nice nice Pin
andalmeida13-Sep-07 3:07
memberandalmeida13-Sep-07 3:07 
JokeMy brain just exploded Pin
Ben Daniel24-Jul-07 13:54
memberBen Daniel24-Jul-07 13:54 
GeneralRe: My brain just exploded Pin
andalmeida24-Jul-07 14:07
memberandalmeida24-Jul-07 14:07 
GeneralRe: My brain just exploded Pin
Paulo Augusto Künzel28-Oct-13 6:19
professionalPaulo Augusto Künzel28-Oct-13 6:19 
GeneralFormatting and Colors Pin
Jon Rista24-Jul-07 13:32
memberJon Rista24-Jul-07 13:32 
QuestionRe: Formatting and Colors Pin
andalmeida24-Jul-07 13:53
memberandalmeida24-Jul-07 13:53 
AnswerRe: Formatting and Colors Pin
andalmeida24-Jul-07 14:06
memberandalmeida24-Jul-07 14:06 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.170217.1 | Last Updated 15 Jul 2016
Article Copyright 2007 by andalmeida
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid