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