private PackedLists <WeightedIndex> CalculateFormFactorsForColorComponent( ScatteringFormFactorCalculator formFactorCalculator, IMaterialSettings[] materialSettingsArray, int componentIdx) { var profiles = materialSettingsArray .Select(baseMaterialSettings => { UberMaterialSettings materialSettings = baseMaterialSettings as UberMaterialSettings; if (materialSettings is null) { return(null); } if (materialSettings.thinWalled) { return(null); } var volumeSettings = new VolumeParameters( materialSettings.transmittedMeasurementDistance, materialSettings.transmittedColor[componentIdx], materialSettings.scatteringMeasurementDistance, materialSettings.sssAmount, materialSettings.sssDirection); ScatteringProfile profile = new ScatteringProfile( volumeSettings.SurfaceAlbedo, volumeSettings.MeanFreePathLength); return(profile); }) .ToArray(); PackedLists <WeightedIndex> formFactors = formFactorCalculator.Calculate(profiles); return(formFactors); }
/** * Finds a distance beyond which the maximum contribution is always imperceptible. */ public static double FindImperceptibleDistance(this ScatteringProfile profile, double area) { double currentDistance = 1; double currentContribution = profile.CalculateMaximumContribution(currentDistance, area); if (currentContribution < PerceptibilityThreshold) { //search smaller distances until we get to perceptibility while (currentContribution < PerceptibilityThreshold) { currentDistance = currentDistance / DistanceSearchStep; if (currentDistance < MinDistanceForSearch) { break; } currentContribution = profile.CalculateMaximumContribution(currentDistance, area); } //the current contribution is just perceptible, so take one step back up to get to imperceptible return(currentDistance * DistanceSearchStep); } else { //search larger distances until we get to imperceptibility while (currentContribution >= PerceptibilityThreshold) { currentDistance = currentDistance * DistanceSearchStep; currentContribution = profile.CalculateMaximumContribution(currentDistance, area); } return(currentDistance); } }
public static Vector4 IntegrateOverQuad(this ScatteringProfile profile, Vector3 receiverPosition, PositionedQuad quad) { int nearestIdx = quad.FindClosestCorner(receiverPosition); SubQuad orientedSubQuad = SubQuad.MakeRotatedWhole(nearestIdx); return(IntegrateOverSubQuad(profile, receiverPosition, quad, orientedSubQuad)); }
public void TestFindImperceptibleDistanceSearchingUp() { var profile = new ScatteringProfile(1, 1e-1); double area = 1; Assert.IsTrue(profile.CalculateMaximumContribution(ScatteringProfileExtensions.InitialDistanceForSearch, area) >= ScatteringProfileExtensions.PerceptibilityThreshold, "search will be up"); double distance = profile.FindImperceptibleDistance(area); Assert.IsTrue(profile.CalculateMaximumContribution(distance, area) < ScatteringProfileExtensions.PerceptibilityThreshold); Assert.IsTrue(profile.CalculateMaximumContribution(distance / ScatteringProfileExtensions.DistanceSearchStep, area) >= ScatteringProfileExtensions.PerceptibilityThreshold); }
public void TestIntegrateOverQuad() { var profile = new ScatteringProfile(1, 0.1); Vector3 receiverPosition = new Vector3(0, 0, 0); float sideLength = 100; var quad = new PositionedQuad( new Vector3(sideLength, sideLength, 0), new Vector3(0, sideLength, 0), new Vector3(0, 0, 0), new Vector3(sideLength, 0, 0) ); Vector4 contribution = profile.IntegrateOverQuad(receiverPosition, quad); //quad should cover 1/4 of total area //only vertex 2 is close, so contributions from other vertices should be negligible Assert.AreEqual(0.25, contribution[2], 5e-3); Assert.AreEqual(0, contribution[0], 5e-3); Assert.AreEqual(0, contribution[1], 5e-3); Assert.AreEqual(0, contribution[3], 5e-3); }
private static Vector4 IntegrateOverSubQuad(ScatteringProfile profile, Vector3 receiverPosition, PositionedQuad root, SubQuad subQuad) { var quad = subQuad.AsPositionedQuad(root); double area = quad.Area; //this assume that P0 is the closest point to the receiver double closestDistance = Vector3.Distance(receiverPosition, quad.P0); double maximumContribution = profile.CalculateMaximumContribution(closestDistance, area); if (maximumContribution < SubdivisionThreshold) { double distance = Vector3.Distance(receiverPosition, quad.Center); return(subQuad.CenterWeight * (float)(area * profile.CalculateDiffuseReflectance(distance))); } else { Vector4 total = Vector4.Zero; foreach (SubQuad subSubQuad in subQuad.Split()) { total += IntegrateOverSubQuad(profile, receiverPosition, root, subSubQuad); } return(total); } }
public static double CalculateMaximumContribution(this ScatteringProfile profile, double nearRadius, double area) { double farRadius = Sqrt(Sqr(nearRadius) + area / PI); return(profile.EvaluateCDF(farRadius) - profile.EvaluateCDF(nearRadius)); }
/** * Returns form factors unnormalized by receiver */ private double[][] CalculateRawFormFactors(ScatteringProfile[] profilesBySurface) { double[][] formFactors = new double[vertexCount][]; //indexed by (receiver, transmitter) for (int receiverIdx = 0; receiverIdx < vertexCount; receiverIdx++) { formFactors[receiverIdx] = new double[vertexCount]; } var stopwatch = Stopwatch.StartNew(); int transmitterCount = faces.Length; for (int transmitterIdx = 0; transmitterIdx < transmitterCount; ++transmitterIdx) { if (transmitterIdx % 1000 == 0) { double fractionComplete = transmitterIdx / (double)transmitterCount; double estimatedRemainingSeconds = stopwatch.ElapsedMilliseconds / 1000.0 / fractionComplete; Console.WriteLine(fractionComplete + ": " + estimatedRemainingSeconds); } int transmitterLabel = connectedComponentLabels.FaceLabels[transmitterIdx]; int surfaceIdx = surfaceMap[transmitterIdx]; ScatteringProfile profile = profilesBySurface[surfaceIdx]; if (profile == null) { //not a scattering surface continue; } Quad transmitter = faces[transmitterIdx]; PositionedQuad positionedTransmitter = PositionedQuad.Make(vertexPositions, transmitter); double transmitterArea = positionedTransmitter.Area; if (profile.meanFreePath == 0) { //edge case: no scattering //split the face contribution evenly amongst its four corners double contribution = 0.25 * profile.surfaceAlbedo * transmitterArea; formFactors[transmitter.Index0][transmitter.Index0] = contribution; formFactors[transmitter.Index1][transmitter.Index1] = contribution; formFactors[transmitter.Index2][transmitter.Index2] = contribution; formFactors[transmitter.Index3][transmitter.Index3] = contribution; continue; } double imperceptibleDistance = profile.FindImperceptibleDistance(transmitterArea); var transmitterBoundingSphere = positionedTransmitter.BoundingSphere; var receiverBoundingSphere = new BoundingSphere(transmitterBoundingSphere.Center, (float)(transmitterBoundingSphere.Radius + imperceptibleDistance)); List <int> receiverIndices = receiverCollisionTree.GetPointsInSphere(receiverBoundingSphere); Vector4[] contributions = receiverIndices .Select(receiverIdx => { int receiverLabel = connectedComponentLabels.VertexLabels[receiverIdx]; if (receiverLabel != transmitterLabel) { return(Vector4.Zero); } Vector3 receiverPosition = vertexPositions[receiverIdx]; Vector4 contribution = profile.IntegrateOverQuad(receiverPosition, positionedTransmitter); return(contribution); }).ToArray(); double totalContribution = contributions.Sum(v => v.X + v.Y + v.Z + v.W); double normalizationFactor = profile.surfaceAlbedo * transmitterArea / totalContribution; for (int i = 0; i < contributions.Length; ++i) { int receiverIdx = receiverIndices[i]; Vector4 contribution = contributions[i]; formFactors[receiverIdx][transmitter.Index0] += normalizationFactor * contribution[0]; formFactors[receiverIdx][transmitter.Index1] += normalizationFactor * contribution[1]; formFactors[receiverIdx][transmitter.Index2] += normalizationFactor * contribution[2]; formFactors[receiverIdx][transmitter.Index3] += normalizationFactor * contribution[3]; } } return(formFactors); }