65.9K
CodeProject is changing. Read more.
Home

Simple Ray Tracing in C# Part IV (Anti-Aliasing)

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (31 votes)

Aug 12, 2007

GPL3

1 min read

viewsIcon

75704

downloadIcon

3706

Simple Ray Tracing in C# Part IV (Anti-Aliasing)

Screenshot - aliasedB.png
Aliased

Screenshot - oneshotB.png
Anti-aliased

Introduction

Anti-aliasing is the name for techniques designed to reduce or eliminate the jagged effect of square pixels attempting to approximate curved shapes.

Screenshot - aliasedcurve.png

Aliased curve

Here we focus on shading the pixels using an averaging algorithm, adding anti-aliasing to our basic ray tracing. For this, instead of only one ray per pixel, we will fire 4 extra rays around each pixel, calculate the color average between the 5 results, and then plot the pixel with the resulting value.

Screenshot - px2.png

Background

I recommend a quick reading of the previous articles:

Using the Code

At first, let's calculate the distance between 2 adjacent pixels in our model coordinates:

double deltaP = Math.Abs(tAlgebra.GetCoord(rect.Left, rect.Right, 
    -fMax, fMax, rect.Left+1)+fMax);

Now for each original ray, we will fire 4 extra rays as a bounding square around the main original ray:

...
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); 
 
// calculate the average 
color.x = (colorA.x + colorB.x + colorC.x + colorD.x + colorE.x) / 5.0;
color.y = (colorA.y + colorB.y + colorC.y + colorD.y + colorE.y) / 5.0;
color.z = (colorA.z + colorB.z + colorC.z + colorD.z + colorE.z) / 5.0;
normalizeColor(ref color);
 
// plot the pixel 
Color colorpx = Color.FromArgb((int)color.x, (int)color.y, (int)color.z);
newBitmap.SetPixel(i, j, colorpx); 
...
// a helper function to normalize colors
public void normalizeColor(ref tPoint color)
{
    color.x = Math.Min(color.x, 255);
    color.y = Math.Min(color.y, 255);
    color.z = Math.Min(color.z, 255);
    color.x = Math.Max(color.x, 0);
    color.y = Math.Max(color.y, 0);
    color.z = Math.Max(color.z, 0);
}

Algebra Class

public class tAlgebra
{
    public tAlgebra()
    {
    }
    public static double GetCoord(double i1, double i2, double w1, 
        double w2, double p)
    {
        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);

        //Rl = V + (2 * N * c1 )
        outx = -(inx + (2 * mirrorx * c1));
        outy = -(iny + (2 * mirrory * c1));
        outz = -(inz + (2 * mirrorz * c1));
    }
    public static double Dot3(double x1, double y1, double z1, 
        double x2, double y2, double z2)
    {
        return ((x1 * x2) + (y1 * y2) + (z1 * z2));
    } 
    // * note: use normalized vectors here!
    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-20;
        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;
    }
}

Now, as seen before, we need a class to define materials in terms of ambient, diffuse and specular parameters:

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 shininess;
    public double alpha;
}

R3 Point Class

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

In order to create the objects, spheres and triangles, I have created a base abstract class called tObject, and the derived tSphere and tTriangle:

Object Class

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

        //for each light // i leave it to the next article :) 
        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);

        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;
        }
    }
    private tMaterial tmaterial;
    private tPoint tHitPoint;
    private double vReflX = 0, vReflY = 0, vReflZ = 0;

    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.50;

        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)
    {
        cx = x;
        cy = y;
        cz = z;
        radius = r;
    }

    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()
    {
    }

    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 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) >= 0)
            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;
    }

    // ray p1, ray p2
    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;
    }

    // should be private with get/set
    public double tp1x, tp1y, tp1z;
    public double tp2x, tp2y, tp2z;
    public double tp3x, tp3y, tp3z;
    public double tnormalX, tnormalY, tnormalZ;
}

Now, Finally, the Ray-tracing Class

public class tRay
{
    // rotation
    public double rx = 0.0, 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 tRay()
    {
        obj3dArrayList = new System.Collections.ArrayList();
        lightsArrayList = new System.Collections.ArrayList();
    }
    public void AddTriangle(double ax, double ay, double az,
        double bx, double by, double bz,
        double cx, double cy, double cz, 
        tMaterial mat)
    {
        tTriangle tri = new tTriangle();
        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;

        tAlgebra.RotX(rx, ref tri.tp1y, ref tri.tp1z);
        tAlgebra.RotX(rx, ref tri.tp2y, ref tri.tp2z);
        tAlgebra.RotX(rx, ref tri.tp3y, ref tri.tp3z);
        tAlgebra.RotY(ry, ref tri.tp1x, ref tri.tp1z);
        tAlgebra.RotY(ry, ref tri.tp2x, ref tri.tp2z);
        tAlgebra.RotY(ry, ref tri.tp3x, ref tri.tp3z);
        tAlgebra.RotZ(rz, ref tri.tp1x, ref tri.tp1y);
        tAlgebra.RotZ(rz, ref tri.tp2x, ref tri.tp2y);
        tAlgebra.RotZ(rz, ref tri.tp3x, ref tri.tp3y);

        tri.material = mat;
        tri.Init();
        AddObject(tri);
    }
    public void AddLight(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-4; 
        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();
        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 = obj.get_reflected_ray(original_ray);
            tRay rfr = obj.get_refracted_ray(original_ray);
            tPoint clraux = new tPoint();
            bool brfl = false, brfr = false;
            reflect_color = trace_ray(rfl);
            if (reflect_color.x > 0 || reflect_color.y > 0 || 
                reflect_color.z > 0)
            {
                brfl = true;
            }
            if (obj.material.alpha < 1.0)
            {
                refract_color = trace_ray(rfr);
                if (refract_color.x > 0 || refract_color.y > 0 || 
                    refract_color.z > 0)
                    brfr = true;
            }
            if (brfl && brfr)
            {
                clraux.x = (3 * point_color.x + reflect_color.x + 5 * 
                    refract_color.x) / 9;
                clraux.y = (3 * point_color.y + reflect_color.y + 5 * 
                    refract_color.y) / 9;
                clraux.z = (3 * point_color.z + reflect_color.z + 5 * 
                    refract_color.z) / 9;
            }
            else
                if (brfl)
                {
                    clraux.x = (point_color.x + reflect_color.x) / 2;
                    clraux.y = (point_color.y + reflect_color.y) / 2;
                    clraux.z = (point_color.z + reflect_color.z) / 2;
                }
                else
                    if (brfr)
                    {
                        clraux.x = (3 * point_color.x + 5 * 
                            refract_color.x) / 8;
                        clraux.y = (3 * point_color.y + 5 * 
                            refract_color.y) / 8;
                        clraux.z = (3 * point_color.z + 5 * 
                            refract_color.z) / 8;
                    }
                    else
                    {
                        clraux.x = point_color.x;
                        clraux.y = point_color.y;
                        clraux.z = point_color.z;
                    }
                    level--;
                    return clraux; // combine with other
        }
        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)
    {
        tAlgebra.RotX(rx, ref cy, ref cz);
        tAlgebra.RotY(ry, ref cx, ref cz);
        tAlgebra.RotZ(rz, ref cx, ref cy);
        tSphere sph = new tSphere(cx, cy, cz, radius);
        sph.material = mat;
        AddObject(sph);
    }
}

Code Usage Samples

Follow these samples of code usage.

Bitmap newBitmap = new Bitmap(300, 300, PixelFormat.Format32bppArgb);

Graphics g = Graphics.FromImage(newBitmap);
Rectangle rect = new Rectangle(0, 0, 300, 300);
double fMax = 610.0;

tRay ray = new tRay();
ray.rx = 0.0; ray.ry = 0.0; ray.rz = 0.0;
ray.levels = 10;

tPoint pt = new tPoint();
pt.x = 250;
pt.y = 250;
pt.z = 630;
ray.AddLight(pt);

tMaterial mat1 = new tMaterial();
mat1.alpha = 1.0;
mat1.ambientR = 0.1324;
mat1.ambientG = 0.0324;
mat1.ambientB = 0.0236;
mat1.specularR = 0.651;
mat1.specularG = 0.351;
mat1.specularB = 0.351;
mat1.shininess = 20;
mat1.diffuseR = 0.4775;
mat1.diffuseG = 0.2775;
mat1.diffuseB = 0.2775;

tMaterial mat2 = new tMaterial();
mat2.alpha = 0.20;
mat2.ambientR = 0.00000;
mat2.ambientG = 0.00000;
mat2.ambientB = 0.00000;
mat2.specularR = 0.35;
mat2.specularG = 0.35;
mat2.specularB = 0.35;
mat2.shininess = 200.0;
mat2.diffuseR = 0.00;
mat2.diffuseG = 0.00;
mat2.diffuseB = 0.00;

tMaterial mat3 = new tMaterial();
mat3.alpha = 1.0;
mat3.ambientR = 0.2324;
mat3.ambientG = 0.2324;
mat3.ambientB = 0.3236;
mat3.specularR = 0.351;
mat3.specularG = 0.351;
mat3.specularB = 0.851;
mat3.shininess = 20;
mat3.diffuseR = 0.3775;
mat3.diffuseG = 0.3775;
mat3.diffuseB = 0.6775;

tMaterial mat4 = new tMaterial();
mat4.alpha = 1.0;
mat4.ambientR = 0.0124;
mat4.ambientG = 0.0124;
mat4.ambientB = 0.0136;
mat4.specularR = 0.451;
mat4.specularG = 0.451;
mat4.specularB = 0.451;
mat4.shininess = 2;
mat4.diffuseR = 0.0775;
mat4.diffuseG = 0.0775;
mat4.diffuseB = 0.0775;

tMaterial mat5 = new tMaterial();
mat5.alpha = 1.0;
mat5.ambientR = 0.39124;
mat5.ambientG = 0.39124;
mat5.ambientB = 0.39136;
mat5.specularR = 0.251;
mat5.specularG = 0.251;
mat5.specularB = 0.251;
mat5.shininess = 2;
mat5.diffuseR = 0.5775;
mat5.diffuseG = 0.5775;
mat5.diffuseB = 0.5775;

tMaterial mat6 = new tMaterial();
mat6.alpha = 1.0;
mat6.ambientR = 0.11124;
mat6.ambientG = 0.49124;
mat6.ambientB = 0.11136;
mat6.specularR = 0.151;
mat6.specularG = 0.651;
mat6.specularB = 0.151;
mat6.shininess = 35;
mat6.diffuseR = 0.1775;
mat6.diffuseG = 0.3775;
mat6.diffuseB = 0.1775;

tMaterial mat7 = new tMaterial();
mat7.alpha = 1.0;
mat7.ambientR = 0.23735;
mat7.ambientG = 0.23735;
mat7.ambientB = 0.23735;
mat7.specularR = 0.773911;
mat7.specularG = 0.773911;
mat7.specularB = 0.773911;
mat7.shininess = 89;
mat7.diffuseR = 0.2775;
mat7.diffuseG = 0.2775;
mat7.diffuseB = 0.2775;

ray.AddSphere(300, 300, 150, 150, mat1);
ray.AddSphere(0, 0, 150, 150, mat2);
ray.AddSphere(-300, 300, 150, 150, mat3);
ray.AddSphere(300, -300, 150, 150, mat6);
ray.AddSphere(-300, -300, 150, 150, mat7);

ray.AddTriangle(0, 500, 0, -500, 500, 0, -500, 0, 0, mat4);
ray.AddTriangle(0, 500, 0, -500, 0, 0, 0, 0, 0, mat4);
ray.AddTriangle(0, 0, 0, 0, -500, 0, 500, 0, 0, mat4);
ray.AddTriangle(500, 0, 0, 0, -500, 0, 500, -500, 0, mat4);
ray.AddTriangle(500, 500, 0, 0, 500, 0, 0, 0, 0, mat5);
ray.AddTriangle(500, 500, 0, 0, 0, 0, 500, 0, 0, mat5);
ray.AddTriangle(-500, 0, 0, -500, -500, 0, 0, 0, 0, mat5);
ray.AddTriangle(0, 0, 0, -500, -500, 0, 0, -500, 0, mat5);

ray.x0 = 0; ray.y0 = 0; ray.z0 = 2000;

tPoint color = new tPoint();
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 - 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); 

        // calculate the average 
        color.x = (colorA.x + colorB.x + colorC.x + colorD.x + 
            colorE.x) / 5.0;
        color.y = (colorA.y + colorB.y + colorC.y + colorD.y + 
            colorE.y) / 5.0;
        color.z = (colorA.z + colorB.z + colorC.z + colorD.z + 
            colorE.z) / 5.0;
        normalizeColor(ref color);

        // plot the pixel 
        Color colorpx = Color.FromArgb((int)color.x, (int)color.y, 
            (int)color.z);
        newBitmap.SetPixel(i, j, colorpx); 
    }
}
...