Beispiel #1
0
    private Color getNaturalColor(Thing thing, Vector pos, Vector norm, Vector rd, Scene scene)
    {
        var addLight = new Func <Color, Light, Color>((col, light) =>
        {
            var ldis      = light.pos - pos;
            var livec     = Help.norm(ldis);
            var neatIsect = testRay(
                new Ray {
                start = pos, dir = livec
            }, scene);

            var isInShadow = !float.IsNaN(neatIsect) && neatIsect <= (float)ldis.LengthSquared;
            if (isInShadow)
            {
                return(col);
            }
            else
            {
                var illum  = (float)Vector.DotProduct(livec, norm);
                var lcolor = (illum > 0)
                    ? illum * light.color
                    : Color.defaultColor;
                var specular = Vector.DotProduct(livec, Help.norm(rd));
                var scolor   = (specular > 0)
                    ? (float)Math.Pow(specular, thing.surface.roughness) * light.color
                    : Color.defaultColor;
                return(col + ((thing.surface.diffuse(pos) * lcolor) +
                              (thing.surface.specular(pos) * scolor)));
            }
        });

        return(scene.lights.Aggregate(Color.defaultColor, addLight));
    }
Beispiel #2
0
    private Color shade(Intersection isect, Scene scene, float depth)
    {
        var d            = isect.ray.dir;
        var pos          = isect.dist * d + isect.ray.start;
        var normal       = isect.thing.normal(pos);
        var reflectDir   = d - 2 * (Vector.DotProduct(normal, d) * normal);
        var naturalColor = Color.background +
                           getNaturalColor(isect.thing, pos, normal, reflectDir, scene);
        var reflectedColor = (depth >= maxDepth)
            ? Color.grey
            : getReflectionColor(isect.thing, pos, normal, reflectDir, scene, depth);

        return(naturalColor + reflectedColor);
    }
Beispiel #3
0
    public override Intersection intersect(Ray ray)
    {
        var denom = Vector.DotProduct(norm, ray.dir);

        if (denom > 0)
        {
            return(null);
        }
        else
        {
            var dist = (Vector.DotProduct(norm, ray.start) + offset) / (-denom);
            return(new Intersection {
                thing = this, ray = ray, dist = (float)dist
            });
        }
    }
Beispiel #4
0
    public override Intersection intersect(Ray ray)
    {
        var eo   = Vector.Add(center, -ray.start);
        var v    = Vector.DotProduct(eo, ray.dir);
        var dist = 0.0f;

        if (v >= 0)
        {
            var disc = radius2 - (Vector.DotProduct(eo, eo) - v * v);
            if (disc >= 0)
            {
                dist = (float)(v - Math.Sqrt(disc));
            }
        }
        if (Math.Abs(dist) < 1e-5)
        {
            return(null);
        }
        return(new Intersection {
            thing = this, ray = ray, dist = dist
        });
    }
Beispiel #5
0
        //
        // Processes a ray-line intersection to see if it's a valid hit.
        //
        // Shares some code with ValidateRayHit
        //
        private void ValidateLineHit(
            RayHitTestParameters rayParams,
            FaceType facesToHit,
            int i0,
            int i1,
            int i2,
            ref Point3D v0,
            ref Point3D v1,
            ref Point3D v2,
            ref Point barycentric
            )
        {
            Matrix3D worldTransformMatrix = rayParams.HasWorldTransformMatrix ? rayParams.WorldTransformMatrix : Matrix3D.Identity;

            // OK, we have an intersection with the LINE but that could be wrong on three
            // accounts:
            //   1. We could have hit the line on the wrong side of the ray's origin.
            //   2. We may need to cull the intersection if it's beyond the far clipping
            //      plane (only if the hit test originated from a Viewport3DVisual.)
            //   3. We could have hit a back-facing triangle
            // We will transform the hit point back into world space to check these
            // things & compute the correct distance from the origin to the hit point.

            // Hit point in model space
            Point3D pointHit = M3DUtil.Interpolate(ref v0, ref v1, ref v2, ref barycentric);

            Point3D worldPointHit = pointHit;

            worldTransformMatrix.MultiplyPoint(ref worldPointHit);

            // Vector from origin to hit point
            Vector3D hitVector               = worldPointHit - rayParams.Origin;
            Vector3D originalDirection       = rayParams.Direction;
            double   rayDistanceUnnormalized = Vector3D.DotProduct(originalDirection, hitVector);

            if (rayDistanceUnnormalized > 0)
            {
                // If we have a HitTestProjectionMatrix than this hit test originated
                // at a Viewport3DVisual.
                if (rayParams.HasHitTestProjectionMatrix)
                {
                    // To test if we are in front of the far clipping plane what we
                    // do conceptually is project our hit point in world space into
                    // homogenous space and verify that it is on the correct side of
                    // the Z=1 plane.
                    //
                    // To save some cycles we only bother computing Z and W of the
                    // projected point and use a simple Z/W > 1 test to see if we
                    // are past the far plane.
                    //
                    // NOTE: HitTestProjectionMatrix is not just the camera matrices.
                    //       It has an additional translation to move the ray to the
                    //       origin.  This extra translation does not effect this test.

                    Matrix3D m = rayParams.HitTestProjectionMatrix;

                    // We directly substitute 1 for p.W below:
                    double pz = worldPointHit.X * m.M13 + worldPointHit.Y * m.M23 + worldPointHit.Z * m.M33 + m.OffsetZ;
                    double pw = worldPointHit.X * m.M14 + worldPointHit.Y * m.M24 + worldPointHit.Z * m.M34 + m.M44;

                    // Early exit if pz/pw > 1.  The negated logic is to reject NaNs.
                    if (!(pz / pw <= 1))
                    {
                        return;
                    }

                    Debug.Assert(!double.IsInfinity(pz / pw) && !double.IsNaN(pz / pw),
                                 "Expected near/far tests to cull -Inf/+Inf and NaN.");
                }

                Point3D a = v0, b = v1, c = v2;

                worldTransformMatrix.MultiplyPoint(ref a);
                worldTransformMatrix.MultiplyPoint(ref b);
                worldTransformMatrix.MultiplyPoint(ref c);

                Vector3D normal = Vector3D.CrossProduct(b - a, c - a);

                double cullSign  = -Vector3D.DotProduct(normal, hitVector);
                double det       = worldTransformMatrix.Determinant;
                bool   frontFace = (cullSign > 0) == (det >= 0);

                if (((facesToHit & FaceType.Front) == FaceType.Front && frontFace) || ((facesToHit & FaceType.Back) == FaceType.Back && !frontFace))
                {
                    double dist = hitVector.Length;
                    if (rayParams.HasModelTransformMatrix)
                    {
                        rayParams.ModelTransformMatrix.MultiplyPoint(ref pointHit);
                    }

                    rayParams.ReportResult(this, pointHit, dist, i0, i1, i2, barycentric);
                }
            }
        }