Click here to Skip to main content
15,885,244 members
Articles / Web Development / ASP.NET

Simple Ray Tracing with Texture Mapping in C#

Rate me:
Please Sign up or sign in to vote.
4.84/5 (31 votes)
15 Jul 2016GPL3 68.6K   71   8
Simple Ray Tracing with texture mapping in C#

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:

C#
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):

C#
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

C#
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)


Written By
CEO
Brazil Brazil
"A well written code is self explanatory" - Anonymous Programmer
"The number of meetings is directly proportional to the bad management" - Another Anonymous Programmer
Founder @TIHUNTER.COM.BR
Linkedin Profile

Comments and Discussions

 
QuestionLink to your website not working (website down?) Pin
ZTransform4-Dec-15 0:09
ZTransform4-Dec-15 0:09 
QuestionIt's nice looking, but Pin
HaBiX6-Jan-14 0:23
HaBiX6-Jan-14 0:23 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey26-Feb-12 21:45
professionalManoj Kumar Choubey26-Feb-12 21:45 
GeneralGreat article! Pin
Marcelo Ricardo de Oliveira25-Mar-10 8:49
Marcelo Ricardo de Oliveira25-Mar-10 8:49 
GeneralRe: Great article! Pin
andalmeida25-Mar-10 8:56
andalmeida25-Mar-10 8:56 
GeneralRe: Great article! Pin
Marcelo Ricardo de Oliveira25-Mar-10 9:10
Marcelo Ricardo de Oliveira25-Mar-10 9:10 
GeneralRe: Great article! Pin
andalmeida25-Mar-10 9:11
andalmeida25-Mar-10 9:11 
GeneralRe: Great article! Pin
Adams thomas23-Jul-17 23:30
Adams thomas23-Jul-17 23:30 

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.