65.9K
CodeProject is changing. Read more.
Home

Simple Ray Tracing with Texture Mapping in C#

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (30 votes)

Jul 24, 2007

GPL3
viewsIcon

69731

downloadIcon

858

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:

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.