Example #1
0
 public Ray(Vector3f o, Vector3f d)
 {
     origin = o;
     direction = d;
     closestHitDistance = WORLD_MAX;
     closestHitObject = null;
 }
 public Sphere(Vector3f p, number r, Color c) {
     position = p;
     radius = r;
     color = c;
 }
 public abstract Vector3f GetSurfaceNormalAtPoint(Vector3f p);
 public Vector3f ReflectIn(Vector3f normal) {
     Vector3f negVector = -this;
     Vector3f reflectedDir = normal * (2.0f * negVector.Dot(normal)) - negVector;
     return reflectedDir;
 }
Example #5
0
 public override Vector3f GetSurfaceNormalAtPoint(Vector3f p)
 {
     return(normal);              // This is of course the same across the entire plane
 }
 public override Vector3f GetSurfaceNormalAtPoint(Vector3f p) {
     return normal;              // This is of course the same across the entire plane
 }
        // given a ray, trace it into the scene and return the colour of the surface it hits 
        // (handles reflections recursively)
        static Color Trace(Ray ray, int traceDepth) {
            // See if the ray intersected an object
            CheckIntersection(ref ray);
            if (ray.closestHitDistance >= Ray.WORLD_MAX || ray.closestHitObject == null) // No intersection
                return BG_COLOR;
            
            // Got a hit - set initial colour to ambient light
            number r = 0.15f * ray.closestHitObject.color.R;
            number g = 0.15f * ray.closestHitObject.color.G; 
            number b = 0.15f * ray.closestHitObject.color.B;

            // Set up stuff we'll need for shading calcs
            Vector3f surfaceNormal = ray.closestHitObject.GetSurfaceNormalAtPoint(ray.hitPoint);
            Vector3f viewerDir = -ray.direction;                            // Direction back to the viewer (simply negative of ray dir)

            // Loop through the lights, adding contribution of each
            foreach (Light light in lights) {
                Vector3f lightDir = new Vector3f();
                number lightDistance;

                // Find light direction and distance
                lightDir = light.position - ray.hitPoint;               // Get direction to light
                lightDistance = lightDir.Magnitude();
                //lightDir = lightDir / lightDistance;                  // Light exponential falloff
                lightDir.Normalise();
                
                // Shadow check: check if this light's visible from the point
                // NB: Step out slightly from the hitpoint first
                Ray shadowRay = new Ray(ray.hitPoint + (lightDir * TINY), lightDir);
                shadowRay.closestHitDistance = lightDistance;           // IMPORTANT: We only want it to trace as far as the light!
                CheckIntersection(ref shadowRay);
                if (shadowRay.closestHitObject != null)                 // We hit something -- ignore this light entirely
                    continue;

                number cosLightAngleWithNormal = surfaceNormal.Dot(lightDir);

                if (MATERIAL_DIFFUSE_COEFFICIENT > TINY) {
                    // Calculate light's diffuse component - note that this is view independant
                    // Dot product of surface normal and light direction gives cos of angle between them so will be in 
                    // range -1 to 1. We use that as a scaling factor; common technique, called "cosine shading".
                    if (cosLightAngleWithNormal <= 0) continue;

                    // Add this light's diffuse contribution to our running totals
                    r += MATERIAL_DIFFUSE_COEFFICIENT * cosLightAngleWithNormal * ray.closestHitObject.color.R;
                    g += MATERIAL_DIFFUSE_COEFFICIENT * cosLightAngleWithNormal * ray.closestHitObject.color.G;
                    b += MATERIAL_DIFFUSE_COEFFICIENT * cosLightAngleWithNormal * ray.closestHitObject.color.B;
                }

                if (MATERIAL_SPECULAR_COEFFICIENT > TINY) {
                    // Specular component - dot product of light's reflection vector and viewer direction
                    // Direction to the viewer is simply negative of the ray direction
                    Vector3f lightReflectionDir = surfaceNormal * (cosLightAngleWithNormal * 2) - lightDir;
                    number specularFactor = viewerDir.Dot(lightReflectionDir);
                    if (specularFactor > 0) {
                        // To get smaller, sharper highlights we raise it to a power and multiply it
                        specularFactor = MATERIAL_SPECULAR_COEFFICIENT * (number)Math.Pow(specularFactor, MATERIAL_SPECULAR_POWER);

                        // Add the specular contribution to our running totals
                        r += specularFactor * ray.closestHitObject.color.R;
                        g += specularFactor * ray.closestHitObject.color.G;
                        b += specularFactor * ray.closestHitObject.color.B;
                    }
                }
            }
        
            // Now do reflection, unless we're too deep
            if (traceDepth < MAX_DEPTH && MATERIAL_REFLECTION_COEFFICIENT > TINY) {
                // Set up the reflected ray - notice we move the origin out a tiny bit again
                Vector3f reflectedDir = ray.direction.ReflectIn(surfaceNormal);
                Ray reflectionRay = new Ray(ray.hitPoint + reflectedDir * TINY, reflectedDir);

                // And trace!
                Color reflectionCol = Trace(reflectionRay, traceDepth + 1);

                // Add reflection results to running totals, scaling by reflect coeff.
                r += MATERIAL_REFLECTION_COEFFICIENT * reflectionCol.R;
                g += MATERIAL_REFLECTION_COEFFICIENT * reflectionCol.G;
                b += MATERIAL_REFLECTION_COEFFICIENT * reflectionCol.B;
            }

            // Clamp RGBs
            if (r > 255) r = 255;
            if (g > 255) g = 255;
            if (b > 255) b = 255;

            return (Color.FromArgb(255, (int)r, (int)g, (int)b));
        }
Example #8
0
        // render a pixel (ie, set pixel color to result of a trace of a ray starting from eye position and
        // passing through the world coords of the pixel)
        static Color RenderPixel(int x, int y)
        {
            // First, calculate direction of the current pixel from eye position
            float sx = screenTopLeftPos.x + (x * pixelWidth);
            float sy = screenTopLeftPos.y - (y * pixelHeight);
            Vector3f eyeToPixelDir = new Vector3f(sx, sy, 0) - eyePos;
            eyeToPixelDir.Normalise();

            // Set up primary (eye) ray
            Ray ray = new Ray(eyePos, eyeToPixelDir);

            // And send a bunch of reverse photons that way!
            // Since each photon we send into Trace with a depth of 0 will
            // bounce around randomly, we need to send many photons into
            // every pixel to get good convergence
            float r = 0, g = 0, b = 0;
            for (int i = 0; i < RAYS_PER_PIXEL; i++) {
                Color c = Trace(ray, 1);
                r += c.R;
                g += c.G;
                b += c.B;
            }
            r /= RAYS_PER_PIXEL;
            g /= RAYS_PER_PIXEL;
            b /= RAYS_PER_PIXEL;
            return (Color.FromArgb(255, (int)r, (int)g, (int)b));
        }
Example #9
0
        // given a ray, trace it into the scene and return the colour of the surface it hits
        // (handles bounces recursively)
        static Color Trace(Ray ray, int traceDepth)
        {
            // See if the ray intersected an object (only if it hasn't already got one - we don't need to
            // recalculate the first intersection for each sample on the same pixel!)
            if (ray.closestHitObject == null)
                CheckIntersection(ref ray);

            if (ray.closestHitDistance >= Ray.WORLD_MAX || ray.closestHitObject == null) // No intersection
                return Color.Black;

            // Got a hit - was it an emitter? If so just return the emitter's colour
            if (ray.closestHitObject.isEmitter)
                return ray.closestHitObject.color;

            if (traceDepth >= MAX_DEPTH)
                return Color.Black;

            // Get surface normal at intersection
            Vector3f surfaceNormal = ray.closestHitObject.GetSurfaceNormalAtPoint(ray.hitPoint);

            // Pick a point on a hemisphere placed on the intersection point (of which
            // the surface normal is the north pole)
            if (surfaceNormal.Dot(ray.direction) >= 0)
                surfaceNormal = surfaceNormal * -1.0f;
            float r1 = (float)(random.NextDouble() * Math.PI * 2.0f);
            float r2 = (float)random.NextDouble();
            float r2s = (float)Math.Sqrt(r2);
            Vector3f u = new Vector3f(1.0f, 0, 0);
            if (Math.Abs(surfaceNormal.x) > 0.1f) {
                u.x = 0;
                u.y = 1.0f;
            }
            u = Vector3f.CrossProduct(u, surfaceNormal);
            u.Normalise();
            Vector3f v = Vector3f.CrossProduct(u, surfaceNormal);

            // Now set up a direction from the hitpoint to that chosen point
            Vector3f reflectionDirection = (u * (float)Math.Cos(r1) * r2s  +  v * (float)Math.Sin(r1) * r2s  +  surfaceNormal * (float)Math.Sqrt(1 - r2));
            reflectionDirection.Normalise();

            // And follow that path (note that we're not spawning a new ray -- just following the one we were
            // originally passed for MAX_DEPTH jumps)
            Ray reflectionRay = new Ray(ray.hitPoint, reflectionDirection);
            Color reflectionCol = Trace(reflectionRay, traceDepth + 1);

            // Now factor the colour we got from the reflection
            // into this object's own colour; ie, illuminate
            // the current object with the results of that reflection
            float r = ray.closestHitObject.color.R * reflectionCol.R;
            float g = ray.closestHitObject.color.G * reflectionCol.G;
            float b = ray.closestHitObject.color.B * reflectionCol.B;

            r /= 255.0f;
            g /= 255.0f;
            b /= 255.0f;

            return (Color.FromArgb(255, (int)r, (int)g, (int)b));
        }
Example #10
0
        public static Vector3f CrossProduct(Vector3f v1, Vector3f v2)
        {
            Vector3f v = new Vector3f();

            v.x = v1.y * v2.z - v1.z * v2.y;
            v.y = v1.z * v2.x - v1.x * v2.z;
            v.z = v1.x * v2.y - v1.y * v2.x;

            return v;
        }
Example #11
0
 public Plane(Vector3f n, float d, Color c)
 {
     normal = n;
     distance = d;
     color = c;
     isEmitter = false;
 }
Example #12
0
 public static Vector3f CrossProduct(Vector3f v1, Vector3f v2)
 {
     return new Vector3f(
         v1.y * v2.z - v1.z * v2.y,
         v1.z * v2.x - v1.x * v2.z,
         v1.x * v2.y - v1.y * v2.x
     );
 }
Example #13
0
        // given a ray, trace it into the scene and return the colour of the surface it hits
        // (handles bounces recursively)
        static Color Trace(Ray ray, int traceDepth)
        {
            // See if the ray intersected an object (only if it hasn't already got one - we don't need to
            // recalculate the first intersection for each sample on the same pixel!)
            if (ray.closestHitObject == null)
            {
                CheckIntersection(ref ray);
            }

            if (ray.closestHitDistance >= Ray.WORLD_MAX || ray.closestHitObject == null) // No intersection
            {
                return(Color.Black);
            }

            // Got a hit - was it an emitter? If so just return the emitter's colour
            if (ray.closestHitObject.isEmitter)
            {
                return(ray.closestHitObject.color);
            }

            if (traceDepth >= MAX_DEPTH)
            {
                return(Color.Black);
            }

            // Get surface normal at intersection
            Vector3f surfaceNormal = ray.closestHitObject.GetSurfaceNormalAtPoint(ray.hitPoint);

            // Pick a point on a hemisphere placed on the intersection point (of which
            // the surface normal is the north pole)
            if (surfaceNormal.Dot(ray.direction) >= 0)
            {
                surfaceNormal = surfaceNormal * -1.0f;
            }
            float    r1  = (float)(random.NextDouble() * Math.PI * 2.0f);
            float    r2  = (float)random.NextDouble();
            float    r2s = (float)Math.Sqrt(r2);
            Vector3f u   = new Vector3f(1.0f, 0, 0);

            if (Math.Abs(surfaceNormal.x) > 0.1f)
            {
                u.x = 0;
                u.y = 1.0f;
            }
            u = Vector3f.CrossProduct(u, surfaceNormal);
            u.Normalise();
            Vector3f v = Vector3f.CrossProduct(u, surfaceNormal);

            // Now set up a direction from the hitpoint to that chosen point
            Vector3f reflectionDirection = (u * (float)Math.Cos(r1) * r2s + v * (float)Math.Sin(r1) * r2s + surfaceNormal * (float)Math.Sqrt(1 - r2));

            reflectionDirection.Normalise();

            // And follow that path (note that we're not spawning a new ray -- just following the one we were
            // originally passed for MAX_DEPTH jumps)
            Ray   reflectionRay = new Ray(ray.hitPoint, reflectionDirection);
            Color reflectionCol = Trace(reflectionRay, traceDepth + 1);

            // Now factor the colour we got from the reflection
            // into this object's own colour; ie, illuminate
            // the current object with the results of that reflection
            float r = ray.closestHitObject.color.R * reflectionCol.R;
            float g = ray.closestHitObject.color.G * reflectionCol.G;
            float b = ray.closestHitObject.color.B * reflectionCol.B;

            r /= 255.0f;
            g /= 255.0f;
            b /= 255.0f;

            return(Color.FromArgb(255, (int)r, (int)g, (int)b));
        }
Example #14
0
 // dot product -- returns the cosine of the angle between two vectors
 public float Dot(Vector3f b)
 {
     return(x * b.x + y * b.y + z * b.z);
 }
 public override Vector3f GetSurfaceNormalAtPoint(Vector3f p) {
     Vector3f normal = p - position;
     normal.Normalise();
     return normal;
 }
Example #16
0
 public Sphere(Vector3f p, float r, Color c)
 {
     position = p;
     radius = r;
     color = c;
     isEmitter = false;
 }
 public Plane(Vector3f n, number d, Color c) {
     normal = n;
     distance = d;
     color = c;
 }
Example #18
0
 public float Dot(Vector3f b)
 {
     return (x * b.x + y * b.y + z * b.z);
 }
        // raytrace a pixel (ie, set pixel color to result of a trace of a ray starting from eye position and
        // passing through the world coords of the pixel)
        static Color RenderPixel(int x, int y) {            
            // First, calculate direction of the current pixel from eye position
            number sx = screenTopLeftPos.x + (x * pixelWidth);
            number sy = screenTopLeftPos.y - (y * pixelHeight);
            Vector3f eyeToPixelDir = new Vector3f(sx, sy, 0) - eyePos;
            eyeToPixelDir.Normalise();

            // Set up primary (eye) ray
            Ray ray = new Ray(eyePos, eyeToPixelDir);

            // And trace it!
            return Trace(ray, 0);
        }
Example #20
0
 public Plane(Vector3f n, float d, Color c)
 {
     normal = n;
     distance = d;
     color = c;
 }
 public number Dot(Vector3f b) {
     return (x * b.x + y * b.y + z * b.z);
 }
Example #22
0
 public Sphere(Vector3f p, float r, Color c)
 {
     position = p;
     radius = r;
     color = c;
 }
 public Light(Vector3f p) {
     position = p;
 }
Example #24
0
 // return the surface normal (perpendicular vector to the surface) for a given point on the surface on the object
 public abstract Vector3f GetSurfaceNormalAtPoint(Vector3f p);