private void balance() { if (storedPhotons == 0) { return; } photons = photonList.ToArray(); photonList = null; // photons = Photon.BalancePhotons(ref photons); Photon[] temp = new Photon[storedPhotons + 1]; balanceSegment(temp, 1, 1, storedPhotons); photons = temp; halfStoredPhotons = storedPhotons / 2; log2n = (int)Math.Ceiling(Math.Log(storedPhotons) / Math.Log(2.0)); }
public static Photon[] BalancePhotons(ref Photon[] unbalanced) { SortedList<Int64, Photon> balanced = new SortedList<Int64, Photon>(); BalancePhotons(balanced, 1, 1, unbalanced.Length-1, 1, ref unbalanced); int index=0; foreach(KeyValuePair<Int64, Photon> photon in balanced) { unbalanced[index++] = photon.Value; } balanced = null; return unbalanced; }
public void getSamples(ShadingState state) { if (storedPhotons == 0) { return; } NearestPhotons np = new NearestPhotons(state.getPoint(), gatherNum, gatherRadius * gatherRadius); locatePhotons(np); if (np.found < 8) { return; } Point3 ppos = new Point3(); Vector3 pdir = new Vector3(); Vector3 pvec = new Vector3(); float invArea = 1.0f / ((float)Math.PI * np.dist2[0]); float maxNDist = np.dist2[0] * 0.05f; float f2r2 = 1.0f / (filterValue * filterValue * np.dist2[0]); float fInv = 1.0f / (1.0f - 2.0f / (3.0f * filterValue)); for (int i = 1; i <= np.found; i++) { Photon phot = np.index[i]; Vector3.decode(phot.dir, pdir); float cos = -Vector3.dot(pdir, state.getNormal()); if (cos > 0.001) { ppos.set(phot.x, phot.y, phot.z); Point3.sub(ppos, state.getPoint(), pvec); float pcos = Vector3.dot(pvec, state.getNormal()); if ((pcos < maxNDist) && (pcos > -maxNDist)) { LightSample sample = new LightSample(); sample.setShadowRay(new Ray(state.getPoint(), pdir.negate())); sample.setRadiance(new Color().setRGBE(np.index[i].power).mul(invArea / cos), Color.BLACK); sample.getDiffuseRadiance().mul((1.0f - (float)Math.Sqrt(np.dist2[i] * f2r2)) * fInv); state.addSample(sample); } } } }
public void checkAddNearest(Photon p) { float fdist2 = p.getDist2(px, py, pz); if (fdist2 < dist2[0]) { if (found < max) { found++; dist2[found] = fdist2; index[found] = p; } else { int j; int parent; if (!gotHeap) { float dst2; Photon phot; int halfFound = found >> 1; for (int k = halfFound; k >= 1; k--) { parent = k; phot = index[k]; dst2 = dist2[k]; while (parent <= halfFound) { j = parent + parent; if ((j < found) && (dist2[j] < dist2[j + 1])) j++; if (dst2 >= dist2[j]) break; dist2[parent] = dist2[j]; index[parent] = index[j]; parent = j; } dist2[parent] = dst2; index[parent] = phot; } gotHeap = true; } parent = 1; j = 2; while (j <= found) { if ((j < found) && (dist2[j] < dist2[j + 1])) j++; if (fdist2 > dist2[j]) break; dist2[parent] = dist2[j]; index[parent] = index[j]; parent = j; j += j; } dist2[parent] = fdist2; index[parent] = p; dist2[0] = dist2[1]; } } }
private void balanceSegment(Photon[] temp, int index, int start, int end) { int median = 1; while ((4 * median) <= (end - start + 1)) median += median; if ((3 * median) <= (end - start + 1)) { median += median; median += (start - 1); } else median = end - median + 1; int axis = Photon.SPLIT_Z; Vector3 extents = bounds.getExtents(); if ((extents.x > extents.y) && (extents.x > extents.z)) axis = Photon.SPLIT_X; else if (extents.y > extents.z) axis = Photon.SPLIT_Y; int left = start; int right = end; while (right > left) { double v = photons[right].getCoord(axis); int i = left - 1; int j = right; while (true) { while (photons[++i].getCoord(axis) < v) { } while ((photons[--j].getCoord(axis) > v) && (j > left)) { } if (i >= j) break; swap(i, j); } swap(i, right); if (i >= median) right = i - 1; if (i <= median) left = i + 1; } temp[index] = photons[median]; temp[index].setSplitAxis(axis); if (median > start) { if (start < (median - 1)) { float tmp; switch (axis) { case Photon.SPLIT_X: tmp = bounds.getMaximum().x; bounds.getMaximum().x = temp[index].x; balanceSegment(temp, 2 * index, start, median - 1); bounds.getMaximum().x = tmp; break; case Photon.SPLIT_Y: tmp = bounds.getMaximum().y; bounds.getMaximum().y = temp[index].y; balanceSegment(temp, 2 * index, start, median - 1); bounds.getMaximum().y = tmp; break; default: tmp = bounds.getMaximum().z; bounds.getMaximum().z = temp[index].z; balanceSegment(temp, 2 * index, start, median - 1); bounds.getMaximum().z = tmp; break; } } else temp[2 * index] = photons[start]; } if (median < end) { if ((median + 1) < end) { float tmp; switch (axis) { case Photon.SPLIT_X: tmp = bounds.getMinimum().x; bounds.getMinimum().x = temp[index].x; balanceSegment(temp, (2 * index) + 1, median + 1, end); bounds.getMinimum().x = tmp; break; case Photon.SPLIT_Y: tmp = bounds.getMinimum().y; bounds.getMinimum().y = temp[index].y; balanceSegment(temp, (2 * index) + 1, median + 1, end); bounds.getMinimum().y = tmp; break; default: tmp = bounds.getMinimum().z; bounds.getMinimum().z = temp[index].z; balanceSegment(temp, (2 * index) + 1, median + 1, end); bounds.getMinimum().z = tmp; break; } } else temp[(2 * index) + 1] = photons[end]; } }
private void balance() { if (storedPhotons == 0) return; photons = photonList.ToArray(); photonList = null; Photon[] temp = new Photon[storedPhotons + 1]; balanceSegment(temp, 1, 1, storedPhotons); photons = temp; halfStoredPhotons = storedPhotons / 2; log2n = (int)Math.Ceiling(Math.Log(storedPhotons) / Math.Log(2.0)); }
public void store(ShadingState state, Vector3 dir, Color power, Color diffuse) { if (((state.getDiffuseDepth() == 0) && (state.getReflectionDepth() > 0 || state.getRefractionDepth() > 0))) { // this is a caustic photon Photon p = new Photon(state.getPoint(), dir, power); lock (lockObj) { storedPhotons++; photonList.Add(p); bounds.include(new Point3(p.x, p.y, p.z)); maxPower = Math.Max(maxPower, power.getMax()); } } }
private static void BalancePhotons(SortedList<Int64, Photon> balanced, int index, int start, int end, int level, ref Photon[] unbalanced) { // Console.WriteLine("index {0}, start {1}, end {2}, level {3}",index, start, end, level); switch (level % 3) { case Photon.SPLIT_X: Array.Sort(unbalanced, start, end - start + 1, new XAxisCompare()); break; case Photon.SPLIT_Y: Array.Sort(unbalanced, start, end - start + 1, new YAxisCompare()); break; case Photon.SPLIT_Z: Array.Sort(unbalanced, start, end - start + 1, new ZAxisCompare()); break; default: break; } int median = start + ((end - start) / 2); balanced[index] = unbalanced[median]; if (median > start) { if (start < (median - 1)) { BalancePhotons(balanced, index * 2, start, median-1, level + 1, ref unbalanced); } else { balanced[index * 2] = unbalanced[start]; } } if (median < end) { if ((median + 1) < end) { BalancePhotons(balanced, index * 2 + 1, median+1, end, level + 1, ref unbalanced); } else { balanced[(index * 2) + 1 ] = unbalanced[end]; } } }
public void store(ShadingState state, Vector3 dir, Color power, Color diffuse) { Photon p = new Photon(state.getPoint(), state.getNormal(), dir, power, diffuse); lock (lockObj) { storedPhotons++; photonList.Add(p); bounds.include(new Point3(p.x, p.y, p.z)); maxPower = Math.Max(maxPower, power.getMax()); } }
public void precomputeRadiance() { if (storedPhotons == 0) return; // precompute the radiance for all photons that are neither // leaves nor parents of leaves in the tree. int quadStoredPhotons = halfStoredPhotons / 2; Point3 p = new Point3(); Vector3 n = new Vector3(); Point3 ppos = new Point3(); Vector3 pdir = new Vector3(); Vector3 pvec = new Vector3(); Color irr = new Color(); Color pow = new Color(); float maxDist2 = gatherRadius * gatherRadius; NearestPhotons np = new NearestPhotons(p, numGather, maxDist2); Photon[] temp = new Photon[quadStoredPhotons + 1]; UI.taskStart("Precomputing radiance", 1, quadStoredPhotons); for (int i = 1; i <= quadStoredPhotons; i++) { UI.taskUpdate(i); Photon curr = photons[i]; p.set(curr.x, curr.y, curr.z); Vector3.decode(curr.normal, n); irr.set(Color.BLACK); np.reset(p, maxDist2); locatePhotons(np); if (np.found < 8) { curr.data = 0; temp[i] = curr; continue; } float invArea = 1.0f / ((float)Math.PI * np.dist2[0]); float maxNDist = np.dist2[0] * 0.05f; for (int j = 1; j <= np.found; j++) { Photon phot = np.index[j]; Vector3.decode(phot.dir, pdir); float cos = -Vector3.dot(pdir, n); if (cos > 0.01f) { ppos.set(phot.x, phot.y, phot.z); Point3.sub(ppos, p, pvec); float pcos = Vector3.dot(pvec, n); if ((pcos < maxNDist) && (pcos > -maxNDist)) irr.add(pow.setRGBE(phot.power)); } } irr.mul(invArea); // compute radiance irr.mul(new Color(curr.data)).mul(1.0f / (float)Math.PI); curr.data = irr.toRGBE(); temp[i] = curr; } UI.taskStop(); // resize photon map to only include irradiance photons numGather /= 4; maxRadius = 1.4f * (float)Math.Sqrt(maxPower * numGather); if (gatherRadius > maxRadius) gatherRadius = maxRadius; storedPhotons = quadStoredPhotons; halfStoredPhotons = storedPhotons / 2; log2n = (int)Math.Ceiling(Math.Log(storedPhotons) / Math.Log(2.0)); photons = temp; hasRadiance = true; }
public void checkAddNearest(Photon p) { float fdist2 = p.getDist2(px, py, pz); if (fdist2 < dist2[0]) { if (found < max) { found++; dist2[found] = fdist2; index[found] = p; } else { int j; int parent; if (!gotHeap) { float dst2; Photon phot; int halfFound = found >> 1; for (int k = halfFound; k >= 1; k--) { parent = k; phot = index[k]; dst2 = dist2[k]; while (parent <= halfFound) { j = parent + parent; if ((j < found) && (dist2[j] < dist2[j + 1])) { j++; } if (dst2 >= dist2[j]) { break; } dist2[parent] = dist2[j]; index[parent] = index[j]; parent = j; } dist2[parent] = dst2; index[parent] = phot; } gotHeap = true; } parent = 1; j = 2; while (j <= found) { if ((j < found) && (dist2[j] < dist2[j + 1])) { j++; } if (fdist2 > dist2[j]) { break; } dist2[parent] = dist2[j]; index[parent] = index[j]; parent = j; j += j; } dist2[parent] = fdist2; index[parent] = p; dist2[0] = dist2[1]; } } }
public Color getRadiance(Point3 p, Vector3 n) { if (!hasRadiance || (storedPhotons == 0) || p == null) { return(Color.BLACK); } float px = p.x; float py = p.y; float pz = p.z; int i = 1; int level = 0; int cameFrom; float dist2; float maxDist2 = gatherRadius * gatherRadius; Photon nearest = null; Photon curr; Vector3 photN = new Vector3(); float[] dist1d2 = new float[log2n]; int[] chosen = new int[log2n]; while (true) { while (i < halfStoredPhotons) { float dist1d = photons[i].getDist1(px, py, pz); dist1d2[level] = dist1d * dist1d; i += i; if (dist1d > 0) { i++; } chosen[level++] = i; } curr = photons[i]; dist2 = curr.getDist2(px, py, pz); if (dist2 < maxDist2) { Vector3.decode(curr.normal, photN); float currentDotN = Vector3.dot(photN, n); if (currentDotN > 0.9f) { nearest = curr; maxDist2 = dist2; } } do { cameFrom = i; i >>= 1; level--; if (i == 0) { return((nearest == null) ? Color.BLACK : new Color().setRGBE(nearest.data)); } } while ((dist1d2[level] >= maxDist2) || (cameFrom != chosen[level])); curr = photons[i]; dist2 = curr.getDist2(px, py, pz); if (dist2 < maxDist2) { Vector3.decode(curr.normal, photN); float currentDotN = Vector3.dot(photN, n); if (currentDotN > 0.9f) { nearest = curr; maxDist2 = dist2; } } i = chosen[level++] ^ 1; } }
public void precomputeRadiance() { if (storedPhotons == 0) { return; } // precompute the radiance for all photons that are neither // leaves nor parents of leaves in the tree. int quadStoredPhotons = halfStoredPhotons / 2; Point3 p = new Point3(); Vector3 n = new Vector3(); Point3 ppos = new Point3(); Vector3 pdir = new Vector3(); Vector3 pvec = new Vector3(); Color irr = new Color(); Color pow = new Color(); float maxDist2 = gatherRadius * gatherRadius; NearestPhotons np = new NearestPhotons(p, numGather, maxDist2); Photon[] temp = new Photon[quadStoredPhotons + 1]; UI.taskStart("Precomputing radiance", 1, quadStoredPhotons); for (int i = 1; i <= quadStoredPhotons; i++) { UI.taskUpdate(i); Photon curr = photons[i]; p.set(curr.x, curr.y, curr.z); Vector3.decode(curr.normal, n); irr.set(Color.BLACK); np.reset(p, maxDist2); locatePhotons(np); if (np.found < 8) { curr.data = 0; temp[i] = curr; continue; } float invArea = 1.0f / ((float)Math.PI * np.dist2[0]); float maxNDist = np.dist2[0] * 0.05f; for (int j = 1; j <= np.found; j++) { Photon phot = np.index[j]; Vector3.decode(phot.dir, pdir); float cos = -Vector3.dot(pdir, n); if (cos > 0.01f) { ppos.set(phot.x, phot.y, phot.z); Point3.sub(ppos, p, pvec); float pcos = Vector3.dot(pvec, n); if ((pcos < maxNDist) && (pcos > -maxNDist)) { irr.add(pow.setRGBE(phot.power)); } } } irr.mul(invArea); // compute radiance irr.mul(new Color(curr.data)).mul(1.0f / (float)Math.PI); curr.data = irr.toRGBE(); temp[i] = curr; } UI.taskStop(); // resize photon map to only include irradiance photons numGather /= 4; maxRadius = 1.4f * (float)Math.Sqrt(maxPower * numGather); if (gatherRadius > maxRadius) { gatherRadius = maxRadius; } storedPhotons = quadStoredPhotons; halfStoredPhotons = storedPhotons / 2; log2n = (int)Math.Ceiling(Math.Log(storedPhotons) / Math.Log(2.0)); photons = temp; hasRadiance = true; }