Click here to Skip to main content
Licence GPL3
First Posted 24 Jul 2007
Views 29,838
Downloads 356
Bookmarked 51 times

Simple Ray Tracing with Texture Mapping in C#

By | 10 Aug 2007 | Article
Simple Ray Tracing with texture mapping in C#

See sample

Screenshot - lune-p.gif
Source image1

Screenshot - worldmap4.gif
Source image 2

Screenshot - rtmapping.png
Resulting image

Introduction

In my previous articles Mapping Images on Spherical Surfaces Using C# and Simple Ray Tracing in C# we have seen how to map textures onto spheres and as well as a simple ray tracing algorithm. But what combining ray tracing and image mapping?

Background

I recommend at first you read my previous articles, Mapping Images on Spherical Surfaces Using C# and Simple Ray Tracing in C#.

Previously we got the formula below, representing the intersection between a 3D line and a sphere:

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
     }

where

  • r is the sphere radius
  • (cx,cy,cz) is the center of the sphere
  • (vx,vy,vz) is the line direction vector
  • (px,py,pz) is the viewer position

Also, the mapping equation between two different scales image pixels(integer) and floating points (latitudes and longitudes):

public static double MapCoordinate(double i1, double i2, double w1,
                                   double w2, double p)
{
return ((p - i1) / (i2 - i1)) * (w2 - w1) + w1;
}

Now, from each intersection we will get the mapped color from the image.

The Code

        System.Drawing.Image image1 = new Bitmap(Server.
                                      MapPath("./images/worldmap4.gif"));
        Bitmap imgBitmap = new Bitmap(image1);
        System.Drawing.Image image2 = new Bitmap(Server.
                                      MapPath("./images/lune-p.gif"));
        Bitmap imgBitmap2 = new Bitmap(image2);

        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(70.0, 70.0, 250.0, 20.0, 255.0,
                               200.0,
                               0.0));
        Graphics graphics = g;

        double px = (double)Session["eyex"],
        py = (double)Session["eyey"],
        pz = (double)Session["eyez"];

        double lpx = (double)Session["lpx"],
        lpy = (double)Session["lpy"],
        lpz = (double)Session["lpz"];

        double lvx = (double)Session["lvx"],
        lvy = (double)Session["lvy"],
        lvz = (double)Session["lvz"];

        double fMax = 200.0;

        // MAP [rect] <-> [-MAX,-MAX, MAX, MAX]
        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;
                // v (x,y,0) <- p
                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;
                    ////////////////////////////////////
                    //Rotate intersection
                    double rtx=itx-spherehit.cx, rty=ity-spherehit.cy,
                           rtz=itz-spherehit.cz;
                    Algebra.RotX(Math.PI / 2, ref rty, ref rtz);
                    Algebra.RotZ(1.5, ref rtx, ref rty);
                    double phi = Math.Acos(rtz/spherehit.radius);
                    double S = Math.Sqrt(rtx * rtx + rty * rty);
                    double theta;
                    if (rtx >= 0)
                        theta = Math.Asin(rty / S);
                    else
                        theta = Math.PI - Math.Asin(rty / S);

                    if (theta < 0) theta = 2.0 * Math.PI + theta;

                    if (spherehit.radius > 80)
                    {
                        double x1 = Algebra.MapCoordinate(0.0,
                                    Math.PI * 2.0,
                                    imgBitmap.Width - 1, 0.0, theta);
                        double y1 = Algebra.MapCoordinate(0.0, Math.PI, 0.0,
                                    imgBitmap.Height - 1, phi);
                        int i1 = (int)x1, j1 = (int)y1;

                        if (i1 >= 0 && j1 >= 0 && i1 < imgBitmap.Width &&

                            j1 < imgBitmap.Height)
                            color1 = imgBitmap.GetPixel(i1, j1);
                    }
                    else
                    {
                        double x1 = Algebra.MapCoordinate(0.0,
                                    Math.PI * 2.0,
                                    imgBitmap2.Width - 1, 0.0, theta);
                        double y1 = Algebra.MapCoordinate(0.0, Math.PI, 0.0,
                                    imgBitmap2.Height - 1, phi);
                        int i1 = (int)x1, j1 = (int)y1;

                        if (i1 >= 0 && j1 >= 0 && i1 < imgBitmap2.Width
                            && j1 < imgBitmap2.Height)
                            color1 = imgBitmap2.GetPixel(i1, j1);
                    }
                    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;
                            }
                        }
                    }
                    // shadow
                    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 = color1.R * cost * fact;
                    double rgbG = color1.G * cost * fact;
                    double rgbB = color1.B * cost * fact;

                    color = Color.FromArgb((int)rgbR, (int)rgbG, (int)rgbB);
                }
                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();

In my next articles I will start to show more complex ray tracing algorithms, using recursive functions, adding transparency, reflection and more reality to the images.

License

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

About the Author

andalmeida

Product Manager
TIHunter
Brazil Brazil

Member

Started programming in 1990 at Navy officers graduation course.
Studied mathematics and computer science with specialization in IT management.
 
Founder of TIHunter Vagas de TI
 
Linkedin Profile

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 5 Pinmembermanoj kumar choubey21:45 26 Feb '12  
GeneralGreat article! PinmemberMarcelo Ricardo de Oliveira8:49 25 Mar '10  
GeneralRe: Great article! Pinmemberandalmeida8:56 25 Mar '10  
GeneralRe: Great article! PinmemberMarcelo Ricardo de Oliveira9:10 25 Mar '10  
GeneralRe: Great article! Pinmemberandalmeida9:11 25 Mar '10  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    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 | Mobile
Web01 | 2.5.120517.1 | Last Updated 10 Aug 2007
Article Copyright 2007 by andalmeida
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid