Пример #1
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
            float r = 0.15f * ray.closestHitObject.color.R;
            float g = 0.15f * ray.closestHitObject.color.G;
            float 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();
                float    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;
                }

                float 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;
                    float    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 * (float)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));
        }
Пример #2
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));
        }