// 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);
        }
        // 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));
        }
        public override number Intersect(Ray ray) {
            number normalDotRayDir = normal.Dot(ray.direction);
            if (normalDotRayDir == 0)   // Ray is parallel to plane (this early-out won't help very often!)
                return -1;

            // Any none-parallel ray will hit the plane at some point - the question now is just
            // if it in the positive or negative ray direction.
            number hitDistance = -(normal.Dot(ray.origin) - distance) / normalDotRayDir;

            if (hitDistance < 0)        // Ray dir is negative, ie we're behind the ray's origin
                return -1;
            else 
                return hitDistance;
        }
        // Given a ray with origin and direction set, fill in the intersection info
        static void CheckIntersection(ref Ray ray) {
            foreach (RTObject obj in objects) {                     // loop through objects, test for intersection
                number hitDistance = obj.Intersect(ray);             // check for intersection with this object and find distance
                if (hitDistance < ray.closestHitDistance && hitDistance > 0) {
                    ray.closestHitObject = obj;                     // object hit and closest yet found - store it
                    ray.closestHitDistance = hitDistance;
                }
            }

            ray.hitPoint = ray.origin + (ray.direction * ray.closestHitDistance);   // also store the point of intersection 
        }
        public override number Intersect(Ray ray) {
            Vector3f lightFromOrigin = position - ray.origin;               // dir from origin to us
            number v = lightFromOrigin.Dot(ray.direction);                   // cos of angle between dirs from origin to us and from origin to where the ray's pointing

            number hitDistance = 
                radius * radius + v * v - 
                lightFromOrigin.x * lightFromOrigin.x - 
                lightFromOrigin.y * lightFromOrigin.y - 
                lightFromOrigin.z * lightFromOrigin.z;

            if (hitDistance < 0)                                            // no hit (do this check now before bothering to do the sqrt below)
                return -1;

            hitDistance = v - (number)Math.Sqrt(hitDistance);			    // get actual hit distance

            if (hitDistance < 0)
                return -1;
            else
                return (number)hitDistance;
        }
 public abstract number Intersect(Ray ray);
Beispiel #7
0
 public abstract float Intersect(Ray ray);
Beispiel #8
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));
        }
Beispiel #9
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));
        }