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

Basic Illumination Model in C#

Rate me:
Please Sign up or sign in to vote.
4.87/5 (24 votes)
15 Jul 2016GPL32 min read 83.7K   1.1K   82  
Basic illumination model in C#.
<%@ Page Language="c#" Debug="true" Explicit="True" %>

<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Data.OleDb" %>
<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Web.Mail" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="Microsoft.CSharp" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<%@ Import Namespace="System.CodeDom.Compiler" %>
<%@ Import Namespace="System.Reflection" %>
<%@ Import Namespace="System.Collections" %>

<script language="C#" runat="server">
    
    public class tAlgebra
    {
        public tAlgebra()
        {
        }

        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 Reflect(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-5;

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

            if (Math.Abs(mod_v) < eps)
                return true;

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


    }
    public class tObject
    {
        public tObject()
        {
        }

        // Final illumination of a point (vertex) = ambient + diffuse + specular

        /*
        Ambient Light Contribution
        Ambient light = background light
        Light that is scattered by the environment
        Frequently assumed to be constant
        Very simple approximation of global illumination
        No direction: independent of light position, object
        orientation, observer�s position or orientation 
        */

        // ambient RGBA reflectance of the material default = (0.2, 0.2, 0.2, 1.0)
        public double ambientR, ambientG, ambientB, ambientA;

        /*
        Diffuse Light Calculation
        Need to decide how much light the object point receive
        from the light source � based on Lambert�s Law    
        Lambert�s law: the radiant energy D that a small surface
        patch receives from a light source is:
        D = I x cos (q)
        */
        // diffuse RGBA reflectance of the material default = (0.8, 0.8, 0.8, 1.0)
        public double diffuseR, diffuseG, diffuseB, diffuseA;

        // specular RGBA reflectance of the material default = (0.0, 0.0, 0.0, 1.0)
        // Fresnel factor, which depends on angle of incidence and refractive index 
        // of material (which in turn depends on wavelength of light) 
        // The overall effect is that the specular reflection does depend on the angle 
        // of incidence of the light (it is brighter for grazing angles of incidence), 
        // and it does reflect the material colour (more so if the light is nearly 
        // normal to surface)
        /*
        Specular light contribution
        The bright spot on the object
        The result of total reflection of
        the incident light in a concentrate 
        specular = Ks x I x cos(f)^n
         * */
        public double specularR, specularG, specularB, specularA;

        // RGBA emitted light intensity of the material default = (0.0, 0.0, 0.0, 1.0)
        public double emissionR, emissionG, emissionB, emissionA;

        // specifies the RGBA specular exponent of the material default = 0
        public double shininess;

        /*
        Illumination from each light:  (*sum all)
        Illum = ambient + diffuse + specular
        = Ka x I + Kd x I x ( cos q) + Ks x I x cos(f)^n
         q = angle between light vector and normal to surface
        f is the angle between reflection light and viewer direction
        n = shininess
        */

    }
    public class tSphere : tObject
    {
        public tSphere(double x, double y, double z, double r, double clr, double clg, double clb)
        {
            cx = x;
            cy = y;
            cz = z;
            radius = r;
            clR = clr;
            clG = clg;
            clB = clb;
        }
        public static double GetCoord(double i1, double i2, double w1, double w2, double p)
        {
            return ((p - i1) / (i2 - i1)) * (w2 - w1) + w1;
        }
        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;
        }
        void RotX(double angle)
        {
            double y = cy * System.Math.Cos(angle) - cz * System.Math.Sin(angle);
            double z = cy * System.Math.Sin(angle) + cz * System.Math.Cos(angle);
            cy = y;
            cz = z;
        }
        void RotY(double angle)
        {
            double x = cx * System.Math.Cos(angle) - cz * System.Math.Sin(angle);
            double z = cx * System.Math.Sin(angle) + cz * System.Math.Cos(angle);
            cx = x;
            cz = z;
        }
        public static double GetSphereIntersec(double cx, double cy, double cz, double radius,
                           double px, double py, double pz, double vx, double vy, double vz)
        {
            // x-xo 2 + y-yo 2 + z-zo 2 = r 2
            // x,y,z = p+tv 
            // At2 + Bt + C = 0


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

            return t;
        }
        public void getNormal(double x1, double y1, double z1, ref double vx1, ref double vy1, ref double vz1)
        {
            vx1 = x1 - cx;
            vy1 = y1 - cy;
            vz1 = z1 - cz;
        }
        public double cx, cy, cz, radius, clR, clG, clB;
    }

    private void Page_Load(object sender, System.EventArgs e)
    {
        Bitmap newBitmap = new Bitmap(200, 200, PixelFormat.Format32bppArgb);
        Graphics g = Graphics.FromImage(newBitmap);

        Pen blackPen = new Pen(Color.Black);
        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();
        tSphere sph1 = new tSphere(0.01, 0.001, 10, 200.0, 0.0, 0.0, 255.0);
        
        // ambient properties for the material   
        sph1.ambientR = 0.329412;
        sph1.ambientG = 0.223529;
        sph1.ambientB = 0.027451;

        // specular properties for the material   
        sph1.specularR = 0.992157;
        sph1.specularG = 0.941176;
        sph1.specularB = 0.807843;
        sph1.shininess = 27.8974;

        sph1.diffuseR = 0.780392;
        sph1.diffuseG = 0.568627;
        sph1.diffuseB = 0.113725;
        
        obj3dArrayList.Add(sph1);
        
        Graphics graphics = g;

        double px = 0.0;
        double py = 0.0;
        double pz = 600.0;

        double lpx = -500;// 200.0;
        double lpy = -500;// 200.0;
        double lpz = 400.0;


        double fMax = 320.0;

        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;

                double vx = x - px, vy = y - py, vz = -pz;

                double mod_v = tAlgebra.modv(vx, vy, vz);
                vx = vx / mod_v;
                vy = vy / mod_v;
                vz = vz / mod_v;

                bool bShadow = false;

                tSphere spherehit = null;

                for (int k = 0; k < (int)obj3dArrayList.Count; k++)
                {
                    tSphere sphn = (tSphere)obj3dArrayList[k];
                    double taux = tSphere.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(0, 0, 0);

                if (spherehit != null)
                {
                    double intersx = px + t * vx,
                           intersy = py + t * vy,
                           intersz = pz + t * vz;

                    double normalX = intersx - spherehit.cx,
                           normalY = intersy - spherehit.cy,
                           normalZ = intersz - spherehit.cz;

                    double lvX = lpx - intersx,
                           lvY = lpy - intersy,
                           lvZ = lpz - intersz;

                    tAlgebra.Normalize(ref normalX, ref normalY, ref normalZ);
                    tAlgebra.Normalize(ref lvX, ref lvY, ref lvZ);

                    double cost = tAlgebra.GetCosAngleV1V2(lvX, lvY, lvZ,
                        normalX, normalY, normalZ);

                    double cosf = 0;

                    double vReflX = 0, vReflY = 0, vReflZ = 0;

                    tAlgebra.Reflect(-lvX, -lvY, -lvZ,
                                     normalX, normalY, normalZ,
                                     ref vReflX, ref vReflY, ref vReflZ);

                    tAlgebra.Normalize(ref vReflX, ref vReflY, ref vReflZ);
                    tAlgebra.Normalize(ref vx, ref vy, ref vz);

                    cosf = tAlgebra.GetCosAngleV1V2(vx, vy, vz, vReflX, vReflY, vReflZ);

                    double result1 = Math.Max(0, cost) * 255.0;
                    double result2 = Math.Pow(Math.Max(0, cosf), spherehit.shininess) * 255.0;

                    double rgbR = (spherehit.ambientR * 255.0) + (spherehit.diffuseR * result1) + (spherehit.specularR * result2);
                    double rgbG = (spherehit.ambientG * 255.0) + (spherehit.diffuseG * result1) + (spherehit.specularG * result2);
                    double rgbB = (spherehit.ambientB * 255.0) + (spherehit.diffuseB * result1) + (spherehit.specularB * result2);

                    rgbR = Math.Min(rgbR, 255);
                    rgbG = Math.Min(rgbG, 255);
                    rgbB = Math.Min(rgbB, 255);
                    rgbR = Math.Max(0, rgbR);
                    rgbG = Math.Max(0, rgbG);
                    rgbB = Math.Max(0, rgbB);

                    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();

    }


</script>

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Raytracing</title>
</head>
<body style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px;
    padding-top: 0px; text-align: center;">
    <form id="form1" runat="server">
    </form>

    <script src="http://www.google-analytics.com/urchin.js" type="text/javascript">
    </script>

    <script type="text/javascript">
_uacct = "UA-2275356-1";
urchinTracker();
    </script>

</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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