/// <summary> /// Returns intensity (incl. color) of the source contribution to the given scene point. /// Internal integration support. /// </summary> /// <param name="intersection">Scene point (only world coordinates and normal vector are needed).</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <param name="dir">Direction to the source is set here (zero vector for omnidirectional source).</param> /// <returns>Intensity vector in current color space or null if the point is not lit.</returns> public double[] GetIntensity( Intersection intersection, int rank, int total, RandomJames rnd, out Vector3d dir ) { return GetIntensity( intersection, out dir ); }
/// <summary> /// Complete all relevant items in the given Intersection object. /// </summary> /// <param name="inter">Intersection instance to complete.</param> public virtual void CompleteIntersection( Intersection inter ) { }
/// <summary> /// Returns intensity (incl. color) of the source contribution to the given scene point. /// Simple variant, w/o an integration support. /// </summary> /// <param name="intersection">Scene point (only world coordinates and normal vector are needed).</param> /// <param name="dir">Direction to the source is set here (zero vector for omnidirectional source). Not normalized!</param> /// <returns>Intensity vector in current color space or null if the point is not lit.</returns> public virtual double[] GetIntensity( Intersection intersection, out Vector3d dir ) { dir = position - intersection.CoordWorld; if ( Vector3d.Dot( dir, intersection.Normal ) <= 0.0 ) return null; return intensity; }
/// <summary> /// Returns intensity (incl. color) of the source contribution to the given scene point. /// Internal integration support. /// </summary> /// <param name="intersection">Scene point (only world coordinates and normal vector are needed).</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <param name="dir">Direction to the source is set here (zero vector for omnidirectional source). Not normalized!</param> /// <returns>Intensity vector in current color space or null if the point is not lit.</returns> public override double[] GetIntensity( Intersection intersection, int rank, int total, RandomJames rnd, out Vector3d dir ) { if ( rnd == null ) return GetIntensity( intersection, out dir ); SamplingState ss; lock ( states ) { if ( !states.TryGetValue( rnd.GetHashCode(), out ss ) ) { ss = new SamplingState( this, rnd ); states.Add( rnd.GetHashCode(), ss ); } } // generate a [new] sample: ss.generateSample( rank, total ); dir = ss.sample - intersection.CoordWorld; if ( Vector3d.Dot( dir, intersection.Normal ) <= 0.0 ) return null; if ( Dim == null || Dim.Length < 3 ) return intensity; double dist = dir.Length; double dimCoef = 1.0 / (Dim[0] + dist * (Dim[1] + dist * Dim[2])); int bands = intensity.Length; double[] result = new double[ bands ]; for ( int i = 0; i < bands; i++ ) result[ i ] = intensity[ i ] * dimCoef; return result; }
public double[] ColorReflection( Intersection intersection, Vector3d input, Vector3d output, ReflectionComponent comp ) { return ColorReflection( intersection.Material, intersection.Normal, input, output, comp ); }
/// <summary> /// Returns intensity (incl. color) of the source contribution to the given scene point. /// Simple variant, w/o an integration support. /// </summary> /// <param name="intersection">Scene point (only world coordinates and normal vector are needed).</param> /// <param name="dir">Direction to the source is set here (zero vector for omnidirectional source).</param> /// <returns>Intensity vector in current color space or null if the point is not lit.</returns> public double[] GetIntensity( Intersection intersection, out Vector3d dir ) { dir = Vector3d.Zero; return intensity; }
/// <summary> /// Apply the relevant value-modulation in the given Intersection instance. /// Simple variant, w/o an integration support. /// </summary> /// <param name="inter">Data object to modify.</param> /// <returns>Hash value (texture signature) for adaptive subsampling.</returns> public long Apply( Intersection inter ) { double u = inter.TextureCoord.X * Fu; double v = inter.TextureCoord.Y * Fv; long ui = (long)Math.Floor( u ); long vi = (long)Math.Floor( v ); if ( ((ui + vi) & 1) != 0 ) Array.Copy( Color2, inter.SurfaceColor, Math.Min( Color2.Length, inter.SurfaceColor.Length ) ); return (ui + RandomStatic.numericRecipes( vi )); }
/// <summary> /// Apply the relevant value-modulation in the given Intersection instance. /// Internal integration support. /// </summary> /// <param name="inter">Data object to modify.</param> /// <param name="rank">Rank of this sample, 0 <= rank < total (for integration).</param> /// <param name="total">Total number of samples (for integration).</param> /// <param name="rnd">Global (per-thread) instance of the random generator.</param> /// <returns>Hash value (texture signature) for adaptive subsampling.</returns> public long Apply( Intersection inter, int rank, int total, RandomJames rnd ) { return Apply( inter ); }
/// <summary> /// Computes the complete intersection of the given ray with the object. /// </summary> /// <param name="p0">Ray origin.</param> /// <param name="p1">Ray direction vector.</param> /// <returns>Sorted list of intersection records.</returns> public override LinkedList <Intersection> Intersect(Vector3d p0, Vector3d p1) { if (children == null || children.Count == 0) { return(null); } if (BoundingVolume != null) { countBoundingBoxes++; if (BoundingVolume.Intersect(p0, p1) < -0.5) { return(null); } } LinkedList <Intersection> result = null; LinkedList <Intersection> left = null; // I'm going to reuse these two.. bool leftOp = true; // the 1st pass => left operand foreach (ISceneNode child in children) { Vector3d origin = Vector3d.TransformPosition(p0, child.FromParent); Vector3d dir = Vector3d.TransformVector(p1, child.FromParent); // ray in local child's coords: [ origin, dir ] LinkedList <Intersection> partial = child.Intersect(origin, dir); if (partial == null) { partial = leftOp ? new LinkedList <Intersection>() : emptyResult; } else if (child is ISolid) { Intersection.countIntersections += partial.Count; } if (leftOp) { leftOp = false; result = partial; left = new LinkedList <Intersection>(); } else { if (trivial && partial.Count == 0) { continue; } // resolve one binary operation (result := left # partial): { LinkedList <Intersection> tmp = left; left = result; result = tmp; } // result .. empty so far result.Clear(); double lowestT = Double.NegativeInfinity; Intersection leftFirst = (left.First == null) ? null : left.First.Value; Intersection rightFirst = (partial.First == null) ? null : partial.First.Value; // initial inside status values: bool insideLeft = (leftFirst != null && !leftFirst.Enter); bool insideRight = (rightFirst != null && !rightFirst.Enter); bool insideResult = bop(insideLeft, insideRight); // merge behavior: bool minLeft = (leftFirst != null && leftFirst.T == lowestT); bool minRight = (rightFirst != null && rightFirst.T == lowestT); while (leftFirst != null || rightFirst != null) { double leftVal = (leftFirst != null) ? leftFirst.T : double.PositiveInfinity; double rightVal = (rightFirst != null) ? rightFirst.T : double.PositiveInfinity; lowestT = Math.Min(leftVal, rightVal); Debug.Assert(!Double.IsInfinity(lowestT)); minLeft = leftVal == lowestT; minRight = rightVal == lowestT; Intersection first = null; if (minRight) { first = rightFirst; partial.RemoveFirst(); rightFirst = (partial.First == null) ? null : partial.First.Value; insideRight = first.Enter; } if (minLeft) { first = leftFirst; left.RemoveFirst(); leftFirst = (left.First == null) ? null : left.First.Value; insideLeft = first.Enter; } bool newResult = bop(insideLeft, insideRight); if (newResult != insideResult) { first.Enter = insideResult = newResult; result.AddLast(first); } } } if (shortCurcuit && result.Count == 0) { break; } } return(result); }
/// <summary> /// Complete all relevant items in the given Intersection object. /// </summary> /// <param name="inter">Intersection instance to complete.</param> public virtual void CompleteIntersection(Intersection inter) { }
/// <summary> /// Canonic sort order: by a T value. /// </summary> /// <param name="obj">Instance to be compared to..</param> /// <returns>-1, 0 or 1.</returns> public int CompareTo(object obj) { Intersection i = (Intersection)obj; return(T.CompareTo(i.T)); }
/// <summary> /// Recursive shading function - computes color contribution of the given ray (shot from the /// origin 'p0' into direction vector 'p1''). Recursion is stopped /// by a hybrid method: 'importance' and 'level' are checked. /// Internal integration support. /// </summary> /// <param name="level">Current recursion depth.</param> /// <param name="importance">Importance of the current ray.</param> /// <param name="p0">Ray origin.</param> /// <param name="p1">Ray direction vector.</param> /// <param name="color">Result color.</param> /// <returns>Hash-value (ray sub-signature) used for adaptive subsampling.</returns> protected override long shade(int level, double importance, ref Vector3d p0, ref Vector3d p1, double[] color) { int bands = color.Length; LinkedList <Intersection> intersections = scene.Intersectable.Intersect(p0, p1); Intersection.countRays++; Intersection i = Intersection.FirstIntersection(intersections, ref p1); int b; if (i == null) // no intersection -> background color { Array.Copy(scene.BackgroundColor, color, bands); return(1L); } // there was at least one intersection i.Complete(); // hash code for adaptive supersampling: long hash = i.Solid.GetHashCode(); // apply all the textures fist.. if (i.Textures != null) { foreach (ITexture tex in i.Textures) { hash = hash * HASH_TEXTURE + tex.Apply(i); } } p1 = -p1; // viewing vector p1.Normalize(); if (scene.Sources == null || scene.Sources.Count < 1) { // no light sources at all Array.Copy(i.SurfaceColor, color, bands); } else { // apply the reflectance model for each source i.Material = (IMaterial)i.Material.Clone(); i.Material.Color = i.SurfaceColor; Array.Clear(color, 0, bands); foreach (ILightSource source in scene.Sources) { Vector3d dir; double[] intensity = source.GetIntensity(i, out dir); if (intensity != null) { if (DoShadows && dir != Vector3d.Zero) { intersections = scene.Intersectable.Intersect(i.CoordWorld, dir); Intersection.countRays++; Intersection si = Intersection.FirstIntersection(intersections, ref dir); // Better shadow testing: intersection between 0.0 and 1.0 kills the lighting if (si != null && !si.Far(1.0, ref dir)) { continue; } } double[] reflection = i.ReflectanceModel.ColorReflection(i, dir, p1, ReflectionComponent.ALL); if (reflection != null) { for (b = 0; b < bands; b++) { color[b] += intensity[b] * reflection[b]; } hash = hash * HASH_LIGHT + source.GetHashCode(); } } } } // check the recursion depth: if (level++ >= MaxLevel || !DoReflections && !DoRefractions) { return(hash); // no further recursion } Vector3d r; double maxK; double[] comp = new double[bands]; double newImportance; if (DoReflections) { // !!!{{ TODO: cast the reflected ray to a bit random direction (variance should be based on Phong material's exponent H).. Geometry.SpecularReflection(ref i.Normal, ref p1, out r); double[] ks = i.ReflectanceModel.ColorReflection(i, p1, r, ReflectionComponent.SPECULAR_REFLECTION); // !!!}} if (ks != null) { maxK = ks[0]; for (b = 1; b < bands; b++) { if (ks[b] > maxK) { maxK = ks[b]; } } newImportance = importance * maxK; if (newImportance >= MinImportance) // do compute the reflected ray { hash += HASH_REFLECT * shade(level, newImportance, ref i.CoordWorld, ref r, comp); for (b = 0; b < bands; b++) { color[b] += ks[b] * comp[b]; } } } } if (DoRefractions) // trying to shoot a refracted ray.. { maxK = i.Material.Kt; // simple solution, no influence of reflectance model yet newImportance = importance * maxK; if (newImportance < MinImportance) { return(hash); } // refracted ray: if ((r = Geometry.SpecularRefraction(i.Normal, i.Material.n, p1)) == null) { return(hash); } // !!!{{ TODO: tweak the refracted ray as well? // !!!}} hash += HASH_REFRACT * shade(level, newImportance, ref i.CoordWorld, ref r, comp); for (b = 0; b < bands; b++) { color[b] += maxK * comp[b]; } } return(hash); }
/// <summary> /// Recursive shading function - computes color contribution of the given ray (shot from the /// origin 'p0' into direction vector 'p1''). Recursion is stopped /// by a hybrid method: 'importance' and 'level' are checked. /// Internal integration support. /// </summary> /// <param name="level">Current recursion depth.</param> /// <param name="importance">Importance of the current ray.</param> /// <param name="p0">Ray origin.</param> /// <param name="p1">Ray direction vector.</param> /// <param name="color">Result color.</param> /// <returns>Hash-value (ray sub-signature) used for adaptive subsampling.</returns> protected virtual long shade(int level, double importance, ref Vector3d p0, ref Vector3d p1, double[] color) { Vector3d direction = p1; int bands = color.Length; LinkedList <Intersection> intersections = scene.Intersectable.Intersect(p0, p1); // If the ray is primary, increment both counters Statistics.IncrementRaysCounters(1, level == 0); Intersection i = Intersection.FirstIntersection(intersections, ref p1); int b; if (i == null) { // No intersection -> background color rayRegisterer?.RegisterRay(AbstractRayRegisterer.RayType.rayVisualizerNormal, level, p0, direction * 100000); Array.Copy(scene.BackgroundColor, color, bands); return(1L); } // There was at least one intersection i.Complete(); rayRegisterer?.RegisterRay(AbstractRayRegisterer.RayType.unknown, level, p0, i); // Hash code for adaptive supersampling long hash = i.Solid.GetHashCode(); // Apply all the textures first if (i.Textures != null) { foreach (ITexture tex in i.Textures) { hash = hash * HASH_TEXTURE + tex.Apply(i); } } if (MT.pointCloudCheckBox && !MT.pointCloudSavingInProgress && !MT.singleRayTracing) { foreach (Intersection intersection in intersections) { if (!intersection.completed) { intersection.Complete(); } if (intersection.Textures != null && !intersection.textureApplied) { foreach (ITexture tex in intersection.Textures) { tex.Apply(intersection); } } double[] vertexColor = new double[3]; Array.Copy(intersection.SurfaceColor, vertexColor, vertexColor.Length); Master.singleton?.pointCloud?.AddToPointCloud(intersection.CoordWorld, vertexColor, intersection.Normal, MT.threadID); } } p1 = -p1; // viewing vector p1.Normalize(); if (scene.Sources == null || scene.Sources.Count < 1) { // No light sources at all. Array.Copy(i.SurfaceColor, color, bands); } else { // Apply the reflectance model for each source. i.Material = (IMaterial)i.Material.Clone(); i.Material.Color = i.SurfaceColor; Array.Clear(color, 0, bands); foreach (ILightSource source in scene.Sources) { double[] intensity = source.GetIntensity(i, out Vector3d dir); if (MT.singleRayTracing && source.position != null) { // Register shadow ray for RayVisualizer. rayRegisterer?.RegisterRay(AbstractRayRegisterer.RayType.rayVisualizerShadow, i.CoordWorld, (Vector3d)source.position); } if (intensity != null) { if (DoShadows && dir != Vector3d.Zero) { intersections = scene.Intersectable.Intersect(i.CoordWorld, dir); Statistics.allRaysCount++; Intersection si = Intersection.FirstIntersection(intersections, ref dir); // Better shadow testing: intersection between 0.0 and 1.0 kills the lighting. if (si != null && !si.Far(1.0, ref dir)) { continue; } } double[] reflection = i.ReflectanceModel.ColorReflection(i, dir, p1, ReflectionComponent.ALL); if (reflection != null) { for (b = 0; b < bands; b++) { color[b] += intensity[b] * reflection[b]; } hash = hash * HASH_LIGHT + source.GetHashCode(); } } } } // Check the recursion depth. if (level++ >= MaxLevel || !DoReflections && !DoRefractions) { // No further recursion. return(hash); } Vector3d r; double maxK; double[] comp = new double[bands]; double newImportance; if (DoReflections) { // Shooting a reflected ray. Geometry.SpecularReflection(ref i.Normal, ref p1, out r); double[] ks = i.ReflectanceModel.ColorReflection(i, p1, r, ReflectionComponent.SPECULAR_REFLECTION); if (ks != null) { maxK = ks[0]; for (b = 1; b < bands; b++) { if (ks[b] > maxK) { maxK = ks[b]; } } newImportance = importance * maxK; if (newImportance >= MinImportance) { // Do compute the reflected ray. hash += HASH_REFLECT * shade(level, newImportance, ref i.CoordWorld, ref r, comp); for (b = 0; b < bands; b++) { color[b] += ks[b] * comp[b]; } } } } if (DoRefractions) { // Shooting a refracted ray. maxK = i.Material.Kt; // simple solution - no influence of reflectance model yet newImportance = importance * maxK; if (newImportance < MinImportance) { return(hash); } // Refracted ray. if ((r = Geometry.SpecularRefraction(i.Normal, i.Material.n, p1)) == Vector3d.Zero) { return(hash); } hash += HASH_REFRACT * shade(level, newImportance, ref i.CoordWorld, ref r, comp); for (b = 0; b < bands; b++) { color[b] += maxK * comp[b]; } } return(hash); }