Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Simple Ray Tracing in C# Part V (Texture Mapping)

0.00/5 (No votes)
15 Jul 2016 1  
Simple Ray Tracing in C# Part V (Texture Mapping)

Screenshot - 3ss.png
Texture mapping on a cube (2 triangles per face)

The sphere materials are (from the left to the right):
100% reflective, 96% permissive - 4% reflective, and 100% opaque

Introduction

A new article of our ray-tracing series describes how to apply texture images on triangles. The process is based on R2 and R3 points distance calculations and its proportions, using again the method tAlgebra.GetCoord already known from the last articles.

We have

  • 2 triangles formed by 3 points in R3 each: P1, P2, P3
  • 1 rectangle given by the image (0,0,width,height) or (0,0,W,H)

What we need to do is to fit the P1-P2 distance into 0-W and get a formula to map a generic point P (P1<P<P2) into a point N (0<N<W). But remember now we are in R3 so we need to computate R3 distances to perform the proportion, and a background in linear algebra is required.

Screenshot - model.png

The generic formula for distance mapping is:

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

The code, after lots of scratch and calculations, maybe not the best, but works fine.

public override tPoint getMapColor(double px, double py, double pz)
        {
            tPoint color = new tPoint();
            double dx = imgBitmap.Width;
            double dy = imgBitmap.Height;
            double Ux = 0, Uy = 0, Uz = 0;
            double Vx = 0, Vy = 0, Vz = 0;
            double v2x = 0, v2y = 0, v2z = 0;
            double distp = 0;
            Ux = tp3x - tp2x;
            Uy = tp3y - tp2y;
            Uz = tp3z - tp2z;
            v2x = px - tp2x;
            v2y = py - tp2y;
            v2z = pz - tp2z;
            distp = tAlgebra.modv(tp2x - px, tp2y - py, tp2z - pz);
            Vx = tp1x - tp2x;
            Vy = tp1y - tp2y;
            Vz = tp1z - tp2z;
            double dU = tAlgebra.modv(Ux, Uy, Uz);
            double dV = tAlgebra.modv(Vx, Vy, Vz);
            tAlgebra.Normalize(ref Ux, ref Uy, ref Uz);
            tAlgebra.Normalize(ref v2x, ref v2y, ref v2z);
            double cost = tAlgebra.Dot3(Ux, Uy, Uz, v2x, v2y, v2z);
            double t = Math.Acos(cost);
            double distY=0, distX=0;
            distY = dU - distp * Math.Cos(t);
            distX = dV - distp * Math.Sin(t);
            double x1 = 0;
            double y1 = 0;
            y1 = tAlgebra.GetCoord(0, dU, tmp3y * dy, tmp2y * dy, distY);
            x1 = tAlgebra.GetCoord(0, dV, tmp1x * dx, tmp2x * dx, distX);
            int i1 = (int)x1, j1 = (int)y1;
            if (i1 >= 0 && j1 >= 0 &&
                i1 < imgBitmap.Width &&
                j1 < imgBitmap.Height)
            {
                Color clr = imgBitmap.GetPixel(i1, j1);
                color.x = clr.R;
                color.y = clr.G;
                color.z = clr.B;
            }
            return color;
        }

Background

I recommend a quick reading of the previous articles:

The Sources

cube.obj

 

#
# cube.obj
#

v -1000.000000 -1000.000000 1000.000000
v -1000.000000 1000.000000 1000.000000
v 1000.000000 1000.000000 1000.000000
v 1000.000000 -1000.000000 1000.000000
v -1000.000000 -1000.000000 -1000.000000
v -1000.000000 1000.000000 -1000.000000
v 1000.000000 1000.000000 -1000.000000
v 1000.000000 -1000.000000 -1000.000000

f 7 3 2
f 2 6 7
f 5 8 7
f 7 6 5
f 2 1 5
f 5 6 2
f 8 4 3
f 3 7 8
f 3 4 1
f 1 2 3
f 1 4 8
f 8 5 1

Algebra Class

 

public class tAlgebra
    {
    public tAlgebra()
        {
        }

    public static double GetCoord(double i1, double i2,
                         double w1, double w2, double p)
        {
        double eps = 1.0E-5;
        if (Math.Abs(i2 - i1) < eps) return 0;
        return ((p - i1) / (i2 - i1)) * (w2 - w1) + w1;
        }

    public static void Cross3(double ax, double ay, double az,
            double bx, double by, double bz,
            ref double outx, ref double outy, ref double outz)
        {
        outx = ay * bz - az * by;
        outy = az * bx - ax * bz;
        outz = ax * by - ay * bx;
        }

    public static void Refract(double n1, double n2, double inx,
            double iny, double inz,
            double mirrorx, double mirrory, double mirrorz,
            ref double outx, ref double outy, ref double outz)
        {
        double c1 = -Dot3(mirrorx, mirrory, mirrorz, inx, iny, inz);
        double n = n1 / n2;
        double c2 = Math.Sqrt(1.0 - n * n * (1.0 - c1 * c1));
        outx = (n * inx) + (n * c1 - c2) * mirrorx;
        outy = (n * iny) + (n * c1 - c2) * mirrory;
        outz = (n * inz) + (n * c1 - c2) * mirrorz;
        }

    public static void Reflect(double inx, double iny, double inz,
            double mirrorx, double mirrory, double mirrorz,
            ref double outx, ref double outy, ref double outz)
        {
        double c1 = -Dot3(mirrorx, mirrory, mirrorz, inx, iny, inz);
        outx = -(inx + (2 * mirrorx * c1));
        outy = -(iny + (2 * mirrory * c1));
        outz = -(inz + (2 * mirrorz * c1));
        }

    public static void Reflect2(double inx, double iny, double inz,
            double mirrorx, double mirrory, double mirrorz,
            ref double outx, ref double outy, ref double outz)
        {
        double perp1x = 0.0, perp1y = 0.0, perp1z = 0.0;
        double perp2x = 0.0, perp2y = 0.0, perp2z = 0.0;
        Cross3(inx, iny, inz, mirrorx, mirrory, mirrorz,
                ref perp1x, ref perp1y, ref perp1z);
        Normalize(ref perp1x, ref perp1y, ref perp1z);
        Cross3(perp1x, perp1y, perp1z, mirrorx, mirrory, mirrorz,
                ref perp2x, ref perp2y, ref perp2z);
        Normalize(ref perp2x, ref perp2y, ref perp2z);

        double a = mirrorx;
        double b = perp2x;
        double c = inx;

        double x = mirrory;
        double y = perp2y;
        double z = iny;

        double i = mirrorz;
        double j = perp2z;
        double k = inz;
        double n = 0.0, m = 0.0;

        double eps = 1.0E-10;

        if (Math.Abs(a) < eps)
            {
            if (Math.Abs(i) < eps)
                {
                outx = -inx;
                outy = iny;
                outz = -inz;
                return;
                }
            else
                {
                double dn = (y - (x * j) / i);
                if (Math.Abs(dn) < eps)
                    {
                    outx = -inx;
                    outy = iny;
                    outz = -inz;
                    return;
                    }
                n = (z - (x * k) / i) / dn;
                m = (k - (j * n)) / i;
                }
            }
        else
            {
            double dn = (y - (x * b) / a);
            if (Math.Abs(dn) < eps)
                {
                outx = -inx;
                outy = iny;
                outz = -inz;
                return;
                }
            n = (z - (x * c) / a) / dn;
            m = (c - (b * n)) / a;
            }

            double v1x = mirrorx;
            double v1y = mirrory;
            double v1z = mirrorz;
            double v2x = perp2x;
            double v2y = perp2y;
            double v2z = perp2z;

            v1x *= m;
            v1y *= m;
            v1z *= m;
            v2x *= n;
            v2y *= n;
            v2z *= n;

            outx = v1x - v2x;
            outy = v1y - v2y;
            outz = v1z - v2z;
            return;
        }

    public static double Dot3(double x1, double y1, double z1, double x2,
                              double y2, double z2)
        {
        return ((x1 * x2) + (y1 * y2) + (z1 * z2));
        }

    public static double GetCosAngleV1V2(double v1x, double v1y, double v1z,
                              double v2x, double v2y, double v2z)
        {
        // cos(t) = (v.w) / (|v|.|w|) = (v.w) / 1
        return Dot3(v1x, v1y, v1z, v2x, v2y, v2z);
        }

    public static double modv(double vx, double vy, double vz)
        {
        return System.Math.Sqrt(vx * vx + vy * vy + vz * vz);
        }

    public static bool Normalize(ref double vx, ref double vy, ref double vz)
        {
        double mod_v = tAlgebra.modv(vx, vy, vz);
        double eps = 1.0E-10;
        if (Math.Abs(mod_v) < eps)
            return true;

        vx = vx / mod_v;
        vy = vy / mod_v;
        vz = vz / mod_v;
        return false;
        }

    public static void RotX(double angle, ref double y, ref double z)
        {
        double y1 = y * System.Math.Cos(angle) - z * System.Math.Sin(angle);
        double z1 = y * System.Math.Sin(angle) + z * System.Math.Cos(angle);
        y = y1;
        z = z1;
        }

    public static void RotY(double angle, ref double x, ref double z)
        {
        double x1 = x * System.Math.Cos(angle) - z * System.Math.Sin(angle);
        double z1 = x * System.Math.Sin(angle) + z * System.Math.Cos(angle);
        x = x1;
        z = z1;
        }

   public static void RotZ(double angle, ref double x, ref double y)
        {
        double x1 = x * System.Math.Cos(angle) - y * System.Math.Sin(angle);
        double y1 = x * System.Math.Sin(angle) + y * System.Math.Cos(angle);
        x = x1;
        y = y1;
        }
    }

Material Class

 

public class tMaterial
    {
    public double ambientR, ambientG, ambientB, ambientA;
    public double diffuseR, diffuseG, diffuseB, diffuseA;
    public double specularR, specularG, specularB, specularA;
    public double emissionR, emissionG, emissionB, emissionA;
    public double shininess;
    public double reflectance;
    public double alpha;
    }

Object Class

 

public abstract class tObject
    {
    public tObject()
        {
        }

    public abstract tPoint getMapColor(double px, double py, double pz);

    public tPoint get_point_color(System.Collections.ArrayList lights,
                                  tRay ray)
        {
        tPoint color = new tPoint();
        tPoint normal = new tPoint();
        tPoint rayV = ray.getRay();

        //todo: foreach light
        tPoint light = (tPoint) lights[0];

        getNormal(hitpoint.x, hitpoint.y, hitpoint.z,
                ref normal.x, ref normal.y, ref normal.z);

        double lvX = light.x - hitpoint.x,
               lvY = light.y - hitpoint.y,
               lvZ = light.z - hitpoint.z;
        tAlgebra.Normalize(ref normal.x, ref normal.y, ref normal.z);
        tAlgebra.Normalize(ref lvX, ref lvY, ref lvZ);
        double cost = tAlgebra.GetCosAngleV1V2(lvX, lvY, lvZ,
                normal.x, normal.y, normal.z);

        double cosf = 0;
        tAlgebra.Reflect(-lvX, -lvY, -lvZ,
                             normal.x, normal.y, normal.z,
                             ref vReflX, ref vReflY, ref vReflZ);
        tAlgebra.Normalize(ref vReflX, ref vReflY, ref vReflZ);
        tAlgebra.Normalize(ref rayV.x, ref rayV.y, ref rayV.z);
        cosf = tAlgebra.GetCosAngleV1V2(rayV.x, rayV.y, rayV.z,
                                      vReflX, vReflY, vReflZ);

        double result1 = Math.Max(0, cost) * 255.0;
        double result2 = Math.Pow(Math.Max(0, cosf),
                                  material.shininess) * 255.0;
        double rgbR = (material.ambientR * 255.0) +
                (material.diffuseR * result1) +
                (material.specularR * result2);
        double rgbG = (material.ambientG * 255.0) +
                (material.diffuseG * result1) +
                (material.specularG * result2);
        double rgbB = (material.ambientB * 255.0) +
                (material.diffuseB * result1) +
                (material.specularB * result2);

        if( imgBitmap!=null)
            {
            tPoint colorPt = getMapColor(hitpoint.x, hitpoint.y, hitpoint.z);
            double sz = tAlgebra.modv(colorPt.x, colorPt.y, colorPt.z);
            result1 = Math.Max(0, cost) * sz;
            result2 = Math.Pow(Math.Max(0, cosf), material.shininess) * sz;
            rgbR = (material.ambientR * colorPt.x) +
                    (material.diffuseR * result1) +
                    (material.specularR * result2);
            rgbG = (material.ambientG * colorPt.y) +
                    (material.diffuseG * result1) +
                    (material.specularG * result2);
            rgbB = (material.ambientB * colorPt.z) +
                    (material.diffuseB * result1) +
                    (material.specularB * result2);
            }

            color.x = rgbR;
            color.y = rgbG;
            color.z = rgbB;
            return color;
        }

    public tRay get_reflected_ray(tRay original_ray)
        {
        tRay ray = new tRay();
        double inx=0, iny=0, inz=0, nx=0, ny=0, nz=0, rx=0, ry=0, rz=0;
        getNormal(hitpoint.x, hitpoint.y,hitpoint.z, ref nx, ref ny, ref nz);

        inx = original_ray.getRay().x;
        iny = original_ray.getRay().y;
        inz = original_ray.getRay().z;

        tAlgebra.Normalize(ref inx, ref iny, ref inz);
        tAlgebra.Normalize(ref nx, ref ny, ref nz);
        tAlgebra.Reflect(-inx,-iny,-inz, nx, ny, nz, ref rx, ref ry, ref rz);
        tAlgebra.Normalize(ref rx, ref ry, ref rz);

        ray.x0 = hitpoint.x;
        ray.y0 = hitpoint.y;
        ray.z0 = hitpoint.z;
        ray.x1 = (hitpoint.x+rx);
        ray.y1 = (hitpoint.y+ry);
        ray.z1 = (hitpoint.z+rz);
        return ray;
        }
    public tMaterial material
        {
        get
            {
            return tmaterial;
            }
        set
            {
            tmaterial = value;
            }
        }
    public tPoint hitpoint
        {
        get
            {
            return tHitPoint;
            }
        set
            {
            tHitPoint = value;
            }
        }
    protected Bitmap imgBitmap = null;
    private tMaterial tmaterial;
    private tPoint tHitPoint;
    private double vReflX = 0, vReflY = 0, vReflZ = 0;
    public abstract void RotateXYZ(double rx, double ry, double rz);
    public abstract void RotateZYX(double rx, double ry, double rz);
    public abstract double GetIntersect(double p1x, double p1y, double p1z,
                             double p2x, double p2y, double p2z);
    public abstract void getNormal(double x, double y, double z,
            ref double nx, ref double ny, ref double nz);

    public tRay get_refracted_ray(tRay original_ray)
        {
        tRay ray = new tRay();
        double inx = 0, iny = 0, inz = 0,
        nx = 0, ny = 0, nz = 0, rx = 0, ry = 0, rz = 0;

        getNormal(hitpoint.x, hitpoint.y, hitpoint.z, ref nx, ref ny, ref nz);

        inx = original_ray.getRay().x;
        iny = original_ray.getRay().y;
        inz = original_ray.getRay().z;

        tAlgebra.Normalize(ref inx, ref iny, ref inz);
        tAlgebra.Normalize(ref nx, ref ny, ref nz);

        double n1 = 1.00;
        double n2 = 1.33;

        tAlgebra.Refract(n1,n2,-inx, -iny, -inz, nx, ny, nz,
                        ref rx, ref ry, ref rz);
        tAlgebra.Normalize(ref rx, ref ry, ref rz);

        ray.x0 = hitpoint.x;
        ray.y0 = hitpoint.y;
        ray.z0 = hitpoint.z;
        ray.x1 = (hitpoint.x + rx);
        ray.y1 = (hitpoint.y + ry);
        ray.z1 = (hitpoint.z + rz);
        return ray;
        }
    }

Sphere Class

 

public class tSphere : tObject
    {
    public tSphere(double x, double y, double z, double r, string txmap)
        {
        cx = x;
        cy = y;
        cz = z;
        radius = r;
        if (txmap != null)
            {
            System.Drawing.Image image1 = new Bitmap(txmap);
            imgBitmap = new Bitmap(image1);
            }
        }

    public override tPoint getMapColor(double px, double py, double pz)
        {
        tPoint color = new tPoint();
        px = px - cx;
        py = py - cy;
        pz = pz - cz;

        tAlgebra.RotX(1.5, ref py, ref pz);
        tAlgebra.RotZ(-2.5, ref px, ref py);

        double phi = Math.Acos(pz/radius);
        double S = Math.Sqrt(px*px + py*py);
        double theta;

        if(px>0)
            theta = Math.Asin(py/S);//Atan(py/px);
        else
            theta = Math.PI - Math.Asin(py / S);// Math.Atan(py / px);

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

        double x1 = tAlgebra.GetCoord(0.0, Math.PI * 2.0,
                       0.0,imgBitmap.Width - 1, theta);
        double y1 = tAlgebra.GetCoord(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)
            {
            Color clr = imgBitmap.GetPixel(i1, j1);
            color.x = clr.R;
            color.y = clr.G;
            color.z = clr.B;
            }
        return color;
        }

    public override void RotateXYZ(double rx, double ry, double rz)
        {
        tAlgebra.RotX(rx, ref cy, ref cz);
        tAlgebra.RotY(ry, ref cx, ref cz);
        tAlgebra.RotZ(rz, ref cx, ref cy);
        }

    public override void RotateZYX(double rx, double ry, double rz)
        {
        tAlgebra.RotZ(rz, ref cx, ref cy);
        tAlgebra.RotY(ry, ref cx, ref cz);
        tAlgebra.RotX(rx, ref cy, ref cz);
        }

    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;
        }

    public override double GetIntersect(double px, double py, double pz,
                              double x, double y, double z)
        {
        // x-xo 2 + y-yo 2 + z-zo 2 = r 2
        // x,y,z = p+tv
        // At2 + Bt + C = 0
        double vx = x - px;
        double vy = y - py;
        double vz = z - pz;

        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;

            tPoint pt = new tPoint();
            pt.x = px + t * vx;
            pt.y = py + t * vy;
            pt.z = pz + t * vz;
            hitpoint = pt;
            }
            return t;
        }

    public override void getNormal(double x, double y, double z,
                    ref double nx, ref double ny, ref double nz)
        {
        nx = x - cx;
        ny = y - cy;
        nz = z - cz;
        }

    public double cx, cy, cz, radius, clR, clG, clB;
    }

Triangle Class

 

public class tTriangle : tObject
    {
    public tTriangle(string txmap)
        {
        if (txmap != null)
            {
            System.Drawing.Image image1 = new Bitmap(txmap);
            imgBitmap = new Bitmap(image1);
            }
        }

    public override void RotateXYZ(double rx, double ry, double rz)
        {
        tAlgebra.RotX(rx, ref tp1y, ref tp1z);
        tAlgebra.RotY(ry, ref tp1x, ref tp1z);
        tAlgebra.RotZ(rz, ref tp1x, ref tp1y);

        tAlgebra.RotX(rx, ref tp2y, ref tp2z);
        tAlgebra.RotY(ry, ref tp2x, ref tp2z);
        tAlgebra.RotZ(rz, ref tp2x, ref tp2y);

        tAlgebra.RotX(rx, ref tp3y, ref tp3z);
        tAlgebra.RotY(ry, ref tp3x, ref tp3z);
        tAlgebra.RotZ(rz, ref tp3x, ref tp3y);
        Init();
        }

    public override void RotateZYX(double rx, double ry, double rz)
        {
        tAlgebra.RotZ(rz, ref tp1x, ref tp1y);
        tAlgebra.RotZ(rz, ref tp3x, ref tp3y);
        tAlgebra.RotZ(rz, ref tp2x, ref tp2y);

        tAlgebra.RotY(ry, ref tp2x, ref tp2z);
        tAlgebra.RotY(ry, ref tp3x, ref tp3z);
        tAlgebra.RotY(ry, ref tp1x, ref tp1z);

        tAlgebra.RotX(rx, ref tp2y, ref tp2z);
        tAlgebra.RotX(rx, ref tp1y, ref tp1z);
        tAlgebra.RotX(rx, ref tp3y, ref tp3z);
        Init();
        }

    public void Init()
        {
        GetNormal(ref tnormalX, ref tnormalY, ref tnormalZ);
        }

    public bool SameSide(double p1x, double p1y, double p1z,
                             double p2x, double p2y, double p2z,
                             double ax, double ay, double az,
                             double bx, double by, double bz)
        {
        double eps = -1.0E-5;
        double cp1x = 0, cp1y = 0, cp1z = 0, cp2x = 0, cp2y = 0,
                   cp2z = 0;
        tAlgebra.Cross3(bx - ax, by - ay, bz - az, p1x - ax,
              p1y - ay, p1z - az, ref cp1x, ref cp1y, ref cp1z);
        tAlgebra.Cross3(bx - ax, by - ay, bz - az, p2x - ax,
              p2y - ay, p2z - az, ref cp2x, ref cp2y, ref cp2z);
        if (tAlgebra.Dot3(cp1x, cp1y, cp1z, cp2x, cp2y, cp2z) >eps)
            return true;
        else
            return false;
        }

    public bool PointInTriangle(double px, double py, double pz)
        {
        if (SameSide(px, py, pz, tp1x, tp1y, tp1z,
                    tp2x, tp2y, tp2z, tp3x, tp3y, tp3z) &&
            SameSide(px, py, pz, tp2x, tp2y, tp2z,
                    tp1x, tp1y, tp1z, tp3x, tp3y, tp3z) &&
            SameSide(px, py, pz, tp3x, tp3y, tp3z,
                    tp1x, tp1y, tp1z, tp2x, tp2y, tp2z))
            return true;
        else
            return false;
        }

    public override double GetIntersect(double p1x, double p1y, double p1z,
                                 double p2x, double p2y, double p2z)
        {
        double v1x = tp3x - p1x;
        double v1y = tp3y - p1y;
        double v1z = tp3z - p1z;
        double v2x = p2x - p1x;
        double v2y = p2y - p1y;
        double v2z = p2z - p1z;
        double dot1 = tAlgebra.Dot3(tnormalX, tnormalY, tnormalZ,
                                        v1x, v1y, v1z);
        double dot2 = tAlgebra.Dot3(tnormalX, tnormalY, tnormalZ,
                                        v2x, v2y, v2z);
        if (Math.Abs(dot2) < 1.0E-6)
            return -1; // division by 0 means parallel
        double u = dot1 / dot2;

        // point in triangle?
        if (!PointInTriangle(p1x + u * (p2x - p1x),
                        p1y + u * (p2y - p1y), p1z + u * (p2z - p1z)))
            return -1;

        tPoint pt = new tPoint();
        pt.x = p1x + u * v2x;
        pt.y = p1y + u * v2y;
        pt.z = p1z + u * v2z;
        hitpoint = pt;
        return u;
        }

    protected void GetNormal(ref double nx, ref double ny, ref double nz)
        {
        double ux = tp3x - tp1x, uy = tp3y - tp1y, uz = tp3z - tp1z;
        double wx = tp2x - tp1x, wy = tp2y - tp1y, wz = tp2z - tp1z;

        // u x w
        nx = wz * uy - wy * uz;
        ny = wx * uz - wz * ux;
        nz = wy * ux - wx * uy;
        }

    public override void getNormal(double x, double y, double z,
                   ref double nx, ref double ny, ref double nz)
        {
        nx = -tnormalX;
        ny = -tnormalY;
        nz = -tnormalZ;
        }

    public override tPoint getMapColor(double px, double py, double pz)
        {
        tPoint color = new tPoint();
        double dx = imgBitmap.Width;
        double dy = imgBitmap.Height;

        double Ux = 0, Uy = 0, Uz = 0;
        double Vx = 0, Vy = 0, Vz = 0;
        double v2x = 0, v2y = 0, v2z = 0;
        double distp = 0;

        Ux = tp3x - tp2x;
        Uy = tp3y - tp2y;
        Uz = tp3z - tp2z;

        v2x = px - tp2x;
        v2y = py - tp2y;
        v2z = pz - tp2z;

        distp = tAlgebra.modv(tp2x - px, tp2y - py, tp2z - pz);

        Vx = tp1x - tp2x;
        Vy = tp1y - tp2y;
        Vz = tp1z - tp2z;

        double dU = tAlgebra.modv(Ux, Uy, Uz);
        double dV = tAlgebra.modv(Vx, Vy, Vz);

        tAlgebra.Normalize(ref Ux, ref Uy, ref Uz);
        tAlgebra.Normalize(ref v2x, ref v2y, ref v2z);
        double cost = tAlgebra.Dot3(Ux, Uy, Uz, v2x, v2y, v2z);
        double t = Math.Acos(cost);

        double distY=0, distX=0;
        distY = dU - distp * Math.Cos(t);
        distX = dV - distp * Math.Sin(t);

        double x1 = 0;
        double y1 = 0;
        y1 = tAlgebra.GetCoord(0, dU, tmp3y * dy, tmp2y * dy, distY);
        x1 = tAlgebra.GetCoord(0, dV, tmp1x * dx, tmp2x * dx, distX);

        int i1 = (int)x1, j1 = (int)y1;
        if (i1 >= 0 && j1 >= 0 && i1 < imgBitmap.Width &&
               j1 < imgBitmap.Height)
            {
            Color clr = imgBitmap.GetPixel(i1, j1);
            color.x = clr.R;
            color.y = clr.G;
            color.z = clr.B;
            }
            return color;
        }

        public double tp1x, tp1y, tp1z;
        public double tp2x, tp2y, tp2z;
        public double tp3x, tp3y, tp3z;
        public double tmp1x, tmp1y;
        public double tmp2x, tmp2y;
        public double tmp3x, tmp3y;

        public double tnormalX, tnormalY, tnormalZ;
    }

Ray Class

 

public class tRay
    {
    public double rx = 0.5, ry = 0.0, rz = 0.0;
    public double x0 = 0, y0 = 0, z0 = 0;
    public double x1 = 0, y1 = 0, z1 = 0;
    public int levels = 3;
    int level = 0;

    System.Collections.ArrayList obj3dArrayList;
    System.Collections.ArrayList lightsArrayList;

    public void LoadOBJFile(string filename)
        {
        System.IO.FileStream file = new System.IO.FileStream(filename,
                 System.IO.FileMode.Open, System.IO.FileAccess.Read);
        System.IO.StreamReader reader = new System.IO.StreamReader(file);
        string filebody = reader.ReadToEnd();
        file.Close();

        string[] lines = filebody.Split('\n');
        ArrayList pointArrayX = new ArrayList();
        ArrayList pointArrayY = new ArrayList();
        ArrayList pointArrayZ = new ArrayList();
        System.Collections.ArrayList obj3dArrayList;
        obj3dArrayList = new System.Collections.ArrayList();

        foreach (string line in lines)
            {
            if (line.Length < 1) continue;
            string auxline = line;
            if (auxline.IndexOf("mtllib") >= 0)
                {
                auxline = auxline.Replace("mtllib", "");
                }
            else
            if (auxline[0] == 'v')
                {
                auxline = auxline.Replace("v ", "");
                string[] points = auxline.Split(' ');
                double x = double.Parse(points[0]);
                double y = double.Parse(points[1]);
                double z = double.Parse(points[2]);
                pointArrayX.Add(x);
                pointArrayY.Add(y);
                pointArrayZ.Add(z);
                }
            else
            if (auxline[0] == 'f')
                {
                auxline = auxline.Replace("f ", "");
                string[] vertices = auxline.Split(' ');
                int a = int.Parse(vertices[0]) - 1;
                int b = int.Parse(vertices[1]) - 1;
                int c = int.Parse(vertices[2]) - 1;

                tMaterial mat = new tMaterial();
                mat.alpha = 0.0;
                mat.reflectance = 0.0;
                mat.ambientR = 0.13735;
                mat.ambientG = 0.13735;
                mat.ambientB = 0.23735;
                mat.specularR = 0.573911;
                mat.specularG = 0.573911;
                mat.specularB = 0.873911;
                mat.shininess = 19;
                mat.diffuseR = 0.5775;
                mat.diffuseG = 0.5775;
                mat.diffuseB = 0.85775;
                AddTriangle((double)pointArrayX[a],
                   (double)pointArrayY[a], (double)pointArrayZ[a],
                   (double)pointArrayX, (double)pointArrayY,
                   (double)pointArrayZ, (double)pointArrayX[c],
                   (double)pointArrayY[c], (double)pointArrayZ[c],
                   1, 0, 0, 0, 1, 1, mat, "./images/chess.bmp");
                }
            }
        }

    public tObject GetObject(int index)
        {
        return (tObject)obj3dArrayList[index];
        }

    public tRay()
        {
         obj3dArrayList = new System.Collections.ArrayList();
            lightsArrayList = new System.Collections.ArrayList();
        }

    public void AddMaterial(double alpha, double ar, double ag, double ab,
            double sr, double sg, double sb, double sn,
            double dr, double dg, double db)
        {
        tMaterial mat1 = new tMaterial();
        mat1.alpha = alpha;
        mat1.ambientR = ar;
        mat1.ambientG = ag;
        mat1.ambientB = ab;

        mat1.specularR = sr;
        mat1.specularG = sg;
        mat1.specularB = sb;
        mat1.shininess = sn;

        mat1.diffuseR = dr;
        mat1.diffuseG = dg;
        mat1.diffuseB = db;
        }

    public void RotateAllObjects(double ax, double ay, double az)
        {
        for (int i = 0; i < obj3dArrayList.Count; i++)
            {
            tObject obj = (tObject)obj3dArrayList[i];
            obj.RotateXYZ(ax, ay, az);
            }
        rx = ax;
        ry = ay;
        rz = az;
        }

    public void RotateObject(int index, double ax, double ay, double az)
        {
        tObject obj = (tObject)obj3dArrayList[index];
        obj.RotateZYX(-rx, -ry, -rz);
        obj.RotateXYZ(ax, ay, az);
        obj.RotateXYZ(rx, ry, rz);
        }

    public void AddTriangle(double ax, double ay, double az,
                                double bx, double by, double bz,
                                double cx, double cy, double cz,
                                double mp1x, double mp1y,
                                double mp2x, double mp2y,
                                double mp3x, double mp3y,
                                tMaterial mat, string txmap )
        {
        tTriangle tri = new tTriangle(txmap);
        tri.tp1x = ax;
        tri.tp1y = ay;
        tri.tp1z = az;
        tri.tp2x = bx;
        tri.tp2y = by;
        tri.tp2z = bz;
        tri.tp3x = cx;
        tri.tp3y = cy;
        tri.tp3z = cz;
        tri.tmp1x = mp1x;
        tri.tmp1y = mp1y;
        tri.tmp2x = mp2x;
        tri.tmp2y = mp2y;
        tri.tmp3x = mp3x;
        tri.tmp3y = mp3y;

        tri.material = mat;
        tri.Init();
        AddObject(tri);
        }

    public void AddLight(ref tPoint light)
        {
        tAlgebra.RotX(rx, ref light.y, ref light.z);
        tAlgebra.RotY(ry, ref light.x, ref light.z);
        tAlgebra.RotZ(rz, ref light.x, ref light.y);
        lightsArrayList.Add(light);
        }

   public void AddObject(tObject obj)
        {
            obj3dArrayList.Add(obj);
        }
        private tObject get_first_intersection(tRay original_ray)
        {
            double eps = 1.0E-10;
            double t = 1.0E10;
            tObject objhit = null;
            for (int k = 0; k < (int)obj3dArrayList.Count; k++)
            {
                tObject objn = (tObject)obj3dArrayList[k];
                double taux = objn.GetIntersect(original_ray.x0,
                          original_ray.y0,original_ray.z0,
                          original_ray.x1,original_ray.y1,original_ray.z1);
                if (Math.Abs(taux) <= eps) continue;
                if (taux > 0 && taux < t)
                {
                t = taux;
                objhit = objn;
                }
            }
        return objhit;
        }

    public tPoint trace_ray( tRay original_ray)
        {
        level++;
        tPoint point_color=new tPoint(),
        reflect_color=new tPoint(),
        refract_color = new tPoint(),
        clraux = new tPoint();
        if (level > levels)
            {
            level--;
            point_color.x = 0;
            point_color.y = 0;
            point_color.z = 0;
            return point_color;
            }

        tObject obj = get_first_intersection(original_ray);
        if (obj != null)
            {
            point_color = obj.get_point_color(lightsArrayList, original_ray);

            tRay rfl = null;
            tRay rfr = null;

            if (obj.material.reflectance > 0)
                {
                rfl = obj.get_reflected_ray(original_ray);
                reflect_color = trace_ray(rfl);
                }
            if (obj.material.alpha > 0)
                {
                rfr = obj.get_refracted_ray(original_ray);
                refract_color = trace_ray(rfr);
                }

            double refl = obj.material.reflectance;
            double refr = obj.material.alpha;
            double ownp = 1.0;

            clraux.x = (point_color.x * ownp + reflect_color.x * refl +
                      refract_color.x * refr);
            clraux.y = (point_color.y * ownp + reflect_color.y * refl +
                      refract_color.y * refr);
            clraux.z = (point_color.z * ownp + reflect_color.z * refl +
                      refract_color.z * refr);

            level--;
            return clraux;
            }
        level--;
        return point_color;
        }

    public tPoint getRay()
        {
        tPoint ray = new tPoint();
        ray.x = x1 - x0;
        ray.y = y1 - y0;
        ray.z = z1 - z0;
        return ray;
        }

    public void AddSphere(double cx, double cy, double cz,
                     double radius, tMaterial mat, string txmap)
        {
        tSphere sph = new tSphere(cx, cy, cz, radius, txmap);
        sph.material = mat;
        AddObject(sph);
        }
    }

Point Class

 public class tPoint
    {
    public double x=0, y=0, z=0;
    }

Using the Code

int scrsize = 800;
Bitmap newBitmap = new Bitmap(scrsize, scrsize,
        PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(newBitmap);
Rectangle rect = new Rectangle(0, 0, scrsize, scrsize);
double fMax = 900.0;

tRay ray = new tRay();
ray.rx = 0.0; ray.ry = 0.0; ray.rz = 0;
ray.x0 = 0;   ray.y0 = 0;   ray.z0 = 900;
ray.levels = 5;

tPoint light1 = new tPoint();
light1.x = 400;
light1.y = 400;
light1.z = 400;
ray.AddLight(ref light1);

ray.LoadOBJFile("./objs/cube.obj"); // change here

tMaterial mat2 = new tMaterial();
mat2.alpha = 0.96;       //96% permissive
mat2.reflectance = 0.04; // 4% reflective
mat2.ambientR = 0.00000;
mat2.ambientG = 0.00000;
mat2.ambientB = 0.00000;
mat2.specularR = 1.00;
mat2.specularG = 1.00;
mat2.specularB = 1.00;
mat2.shininess = 200.0;
mat2.diffuseR = 0.00;
mat2.diffuseG = 0.00;
mat2.diffuseB = 0.00;
ray.AddSphere(0, 0, 0, 150, mat2, null);

tMaterial mat3 = new tMaterial();
mat3.alpha = 0.000;         // 0% permissive
mat3.reflectance = 0.000;   // 0% reflective
mat3.ambientR = 0.60000;
mat3.ambientG = 0.60000;
mat3.ambientB = 0.60000;
mat3.specularR = 0.800;
mat3.specularG = 0.800;
mat3.specularB = 0.800;
mat3.shininess = 200.0;
mat3.diffuseR = 0.500;
mat3.diffuseG = 0.500;
mat3.diffuseB = 0.500;
ray.AddSphere(420, 0, 0, 150, mat3, null);

tMaterial mat4 = new tMaterial();
mat4.alpha = 0.000;         // 0% permissive
mat4.reflectance = 1.000;   // 100% reflective
mat4.ambientR = 0.040000;
mat4.ambientG = 0.040000;
mat4.ambientB = 0.040000;
mat4.specularR = 0.800;
mat4.specularG = 0.800;
mat4.specularB = 0.800;
mat4.shininess = 60.0;
mat4.diffuseR = 0.0500;
mat4.diffuseG = 0.0500;
mat4.diffuseB = 0.0500;
ray.AddSphere(-420, 0, 0, 150, mat4, null);
ray.RotateAllObjects(-1.2, -0.3, 0.0);

tPoint color = new tPoint();
double deltaP = Math.Abs(tAlgebra.GetCoord(rect.Left,
        rect.Right, -fMax, fMax, rect.Left+1)+fMax)/2.0;

for (int i = rect.Left; i < rect.Right; i++)
{
    double x = tAlgebra.GetCoord(rect.Left, rect.Right,
        -fMax, fMax, i);

    for (int j = rect.Top; j < rect.Bottom; j++)
    {
        double y = tAlgebra.GetCoord(rect.Top, rect.Bottom,
            fMax, -fMax, j);
        ray.x1 = x; ray.y1 = y; ray.z1 = 0.0;
        color.x = 0; color.y = 0; color.z = 0;

        ray.x1 = x - deltaP; ray.y1 = y - deltaP; ray.z1 = 0.0;
        tPoint colorA = ray.trace_ray(ray);
        normalizeColor(ref colorA);

        ray.x1 = x - deltaP; ray.y1 = y + deltaP; ray.z1 = 0.0;
        tPoint colorB = ray.trace_ray(ray);
        normalizeColor(ref colorB);
        ray.x1 = x + deltaP; ray.y1 = y - deltaP; ray.z1 = 0.0;
        tPoint colorC = ray.trace_ray(ray);
        normalizeColor(ref colorC);
        ray.x1 = x + deltaP ; ray.y1 = y + deltaP; ray.z1 = 0.0;
        tPoint colorD = ray.trace_ray(ray);
        normalizeColor(ref colorD);
        ray.x1 = x; ray.y1 = y; ray.z1 = 0.0;
        tPoint colorE = ray.trace_ray(ray);
        normalizeColor(ref colorE);
        ray.x1 = x; ray.y1 = y-deltaP; ray.z1 = 0.0;
        tPoint colorF = ray.trace_ray(ray);
        normalizeColor(ref colorF);
        ray.x1 = x; ray.y1 = y+deltaP; ray.z1 = 0.0;
        tPoint colorG = ray.trace_ray(ray);
        normalizeColor(ref colorG);
        ray.x1 = x-deltaP; ray.y1 = y; ray.z1 = 0.0;
        tPoint colorH = ray.trace_ray(ray);
        normalizeColor(ref colorH);
        ray.x1 = x+deltaP; ray.y1 = y; ray.z1 = 0.0;
        tPoint colorI = ray.trace_ray(ray);
        normalizeColor(ref colorI);

        color.x = (colorA.x + colorB.x + colorC.x + colorD.x +
        colorE.x + colorF.x + colorG.x +
        colorH.x + colorI.x) / 9.0;
        color.y = (colorA.y + colorB.y + colorC.y + colorD.y +
        colorE.y + colorF.y + colorG.y +
        colorH.y + colorI.y) / 9.0;
        color.z = (colorA.z + colorB.z + colorC.z + colorD.z +
        colorE.z + colorF.z + colorG.z +
        colorH.z + colorI.z) / 9.0;

        normalizeColor(ref color);
        Color colorpx = Color.FromArgb((int)color.x,
        (int)color.y, (int)color.z);
        newBitmap.SetPixel(i, j, colorpx);
    }
}
newBitmap.Save("oneshot.bmp"); // change here
}

Some Shots

Screenshot - 1.png Screenshot - 5.png Screenshot - img3.png Screenshot - test3s.png

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here