private static Vector3 ApplyWeights(Vector4 w, PositionedQuad root) { return (w.X * root.P0 + w.Y * root.P1 + w.Z * root.P2 + w.W * root.P3); }
public PositionedQuad AsPositionedQuad(PositionedQuad root) { return(new PositionedQuad( ApplyWeights(W0, root), ApplyWeights(W1, root), ApplyWeights(W2, root), ApplyWeights(W3, root) )); }
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 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)); }
/** * 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); }