/// <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="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="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, int rank, int total, RandomJames rnd, 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, rank, total, rnd); } } 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, rank, total, rnd, out dir); if (intensity != null) { if (DoShadows && !dir.Equals(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) // trying to shoot 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, rank, total, rnd, 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); } hash += HASH_REFRACT * shade(level, newImportance, ref i.CoordWorld, ref r, rank, total, rnd, 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); return(scene.Background.GetColor(p1, color)); } // 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]; Util.ColorCopy(intersection.SurfaceColor, vertexColor); Master.singleton?.pointCloud?.AddToPointCloud(intersection.CoordWorld, vertexColor, intersection.Normal, MT.threadID); } } p1 = -p1; // viewing vector p1.Normalize(); // !!! TODO: optional light-source processing (controlled by an attribute?) !!! if (scene.Sources == null || scene.Sources.Count < 1) { // No light sources at all. Util.ColorCopy(i.SurfaceColor, color); } 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; // !!! TODO: alternative intersection handling, different from reflection + refraction !!! // Controlled by an attribute (containing a callback-function)? 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); }