public Photon(Photon p) { this.Position = p.Position; this.Plane = p.Plane; this.Theta = p.Theta; this.Phi = p.Phi; this.Power = p.Power; }
//balance creates a left balanced kd-tree from the flat photon array. //This function should be called before the photon map //is used for rendering. public void Balance() { if (this.stored_photons > 0) { // allocate two temporary arrays for the balancing procedure Photon[] pa1 = new Photon[this.stored_photons + 1]; Photon[] pa2 = new Photon[this.stored_photons + 1]; int i; for (i = 1; i <= this.stored_photons; i++) { pa2[i] = this.Photons[i]; } //this.Photons.CopyTo(pa2, 0); this.BalanceSegment(pa1, pa2, 1, 1, this.stored_photons); //Array.Clear(pa2, 0, pa2.Length); pa2 = null; // reorganize balanced kd-tree (make a heap) int d, j = 1, foo = 1; Photon foo_photon = this.Photons[j]; for (i = 1; i <= this.stored_photons; i++) { //d = Array.IndexOf(this.Photons, pa1[j]); //TODO: hack, linear search =( d = pa1[j].Index; //OK Eliminate linear search //Debug.Assert(d == pa1[j].Index, "Merda!"); pa1[j] = null; if (d != foo) { this.Photons[j] = this.Photons[d]; this.Photons[j].Index = j; } else { this.Photons[j] = foo_photon; this.Photons[j].Index = j; if (i < this.stored_photons) { for (; foo <= this.stored_photons; foo++) { if (pa1[foo] != null) { break; } } foo_photon = this.Photons[foo]; j = foo; } continue; } j = d; } pa1 = null; } this.half_stored_photons = this.stored_photons / 2 - 1; }
//OK //irradiance_estimate computes an irradiance estimate // at a given surface position public RGBColor IrradianceEstimate(Point3D pos, Vector3D normal, float max_dist, int nphotons) { RGBColor irrad = RGBColor.Black; NearestPhotons np = new NearestPhotons(); np.Dist2 = new float[nphotons + 1]; np.Index = new Photon[nphotons + 1]; np.Pos = pos; np.Max = nphotons; np.Found = 0; np.GotHeap = 0; np.Dist2[0] = max_dist * max_dist; // locate the nearest photons this.LocatePhotons(np, 1); // if less than 8 photons return if (np.Found < 8) { return(irrad); } Vector3D pdir; // sum irradiance from all photons for (int i = 1; i <= np.Found; i++) { Photon p = np.Index[i]; // the photon_dir call and following if can be omitted (for speed) // if the scene does not have any thin surfaces pdir = this.photonDir(p); if ((pdir * normal) < 0.0f) { irrad += p.Power; } } float tmp = (float)((1.0f / Math.PI) / (np.Dist2[0])); // estimate of // density irrad *= tmp; //Use Array.Clear(); np.Dist2 = null; np.Index = null; return(irrad); }
//OK // locate_photons finds the nearest photons in the // photon map given the parameters in np private void LocatePhotons(NearestPhotons np, int index) { Photon p = this.Photons[index]; if (index < this.half_stored_photons) { float dist1 = np.Pos[p.Plane] - p.Position[p.Plane]; if (dist1 > 0.0) { // if dist1 is positive search right plane this.LocatePhotons(np, 2 * index + 1); if (dist1 * dist1 < np.Dist2[0]) { this.LocatePhotons(np, 2 * index); } } else { // dist1 is negative search left first this.LocatePhotons(np, 2 * index); if (dist1 * dist1 < np.Dist2[0]) { this.LocatePhotons(np, 2 * index + 1); } } } // compute squared distance between current photon and np.pos //dist1 = p.pos[0] - np.pos[0]; //float dist2 = dist1 * dist1; //dist1 = p.pos[1] - np.pos[1]; //dist2 += dist1 * dist1; //dist1 = p.pos[2] - np.pos[2]; //dist2 += dist1 * dist1; float dist2 = (p.Position - np.Pos).Length2; if (dist2 < np.Dist2[0]) { // we found a photon :) Insert it in the candidate list if (np.Found < np.Max) { // heap is not full; use array np.Found++; np.Dist2[np.Found] = dist2; np.Index[np.Found] = p; } else { int j, parent; if (np.GotHeap == 0) { // Do we need to build the heap? // Build heap float dst2; Photon phot; int half_found = np.Found >> 1; for (int k = half_found; k >= 1; k--) { parent = k; phot = np.Index[k]; dst2 = np.Dist2[k]; while (parent <= half_found) { j = parent + parent; if (j < np.Found && np.Dist2[j] < np.Dist2[j + 1]) { j++; } if (dst2 >= np.Dist2[j]) { break; } np.Dist2[parent] = np.Dist2[j]; np.Index[parent] = np.Index[j]; parent = j; } np.Dist2[parent] = dst2; np.Index[parent] = phot; } np.GotHeap = 1; } // insert new photon into max heap // delete largest element, insert new and reorder the heap parent = 1; j = 2; while (j <= np.Found) { if (j < np.Found && np.Dist2[j] < np.Dist2[j + 1]) { j++; } if (dist2 > np.Dist2[j]) { break; } np.Dist2[parent] = np.Dist2[j]; np.Index[parent] = np.Index[j]; parent = j; j += j; } if (dist2 < np.Dist2[parent]) { np.Index[parent] = p; np.Dist2[parent] = dist2; } np.Dist2[0] = np.Dist2[1]; } } }
private Vector3D photonDir(Photon p) { Vector3D pDir = new Vector3D(this.sinTheta[p.Theta] * this.cosPhi[p.Phi], this.sinTheta[p.Theta] * this.sinPhi[p.Phi], this.cosTheta[p.Theta]); return pDir; }
//OK // See "Realistic image synthesis using Photon Mapping" chapter 6 // for an explanation of this function private void BalanceSegment(Photon[] pbal, Photon[] porg, int index, int start, int end) { // -------------------- // compute new median // -------------------- 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; } // -------------------------- // find axis to split along // -------------------------- int axis = 2; if ((this.bbox_max.X - this.bbox_min.X) > (this.bbox_max.Y - this.bbox_min.Y) && (this.bbox_max.X - this.bbox_min.X) > (this.bbox_max.Z - this.bbox_min.Z)) { axis = 0; } else if ((this.bbox_max.Y - this.bbox_min.Y) > (this.bbox_max.Z - this.bbox_min.Z)) { axis = 1; } // ------------------------------------------ // partition photon block around the median // ------------------------------------------ MedianSplit(porg, start, end, median, axis); pbal[index] = porg[median]; //pbal[index].Index = index; pbal[index].Plane = (short) axis; // ---------------------------------------------- // recursively balance the left and right block // ---------------------------------------------- if (median > start) { // balance left segment if (start < median - 1) { float tmp = this.bbox_max[axis]; this.bbox_max[axis] = pbal[index].Position[axis]; this.BalanceSegment(pbal, porg, 2 * index, start, median - 1); this.bbox_max[axis] = tmp; } else { pbal[2 * index] = porg[start]; //pbal[2 * index].Index = 2 * index; } } if (median < end) { // balance right segment if (median + 1 < end) { float tmp = this.bbox_min[axis]; this.bbox_min[axis] = pbal[index].Position[axis]; this.BalanceSegment(pbal, porg, 2 * index + 1, median + 1, end); this.bbox_min[axis] = tmp; } else { pbal[2 * index + 1] = porg[end]; //pbal[2 * index + 1].Index = 2 * index + 1; } } }
//OK private static void swap(Photon[] ph, int a, int b) { Photon ph2 = ph[a]; ph[a] = ph[b]; ph[b] = ph2; //ph[a].Index = a; //ph[b].Index = b; }
//OK // MedianSplit splits the photon array into two separate // pieces around the median with all photons below the // the median in the lower half and all photons above // than the median in the upper half. The comparison // criteria is the axis (indicated by the axis parameter) // (inspired by routine in "Algorithms in C++" by Sedgewick) private static void MedianSplit(Photon[] p, int start, int end, int median, int axis) { int left = start; int right = end; while (right > left) { float v = p[right].Position[axis]; int i = left - 1; int j = right; while (true) { while (p[++i].Position[axis] < v) {} while (p[--j].Position[axis] > v && j > left) {} if (i >= j) { break; } swap(p, i, j); } swap(p, i, right); if (i >= median) { right = i - 1; } if (i <= median) { left = i + 1; } } }
//OK // store puts a photon into the flat array that will form // the final kd-tree. // Call this function to store a photon. public void Store(Photon photon) { if (this.stored_photons >= this.max_photons) { return; } this.stored_photons++; this.Photons[this.stored_photons] = photon; photon.Index = this.stored_photons; if (photon.Position.X < this.bbox_min.X) { this.bbox_min.X = photon.Position.X; } if (photon.Position.X > this.bbox_max.X) { this.bbox_max.X = photon.Position.X; } if (photon.Position.Y < this.bbox_min.Y) { this.bbox_min.Y = photon.Position.Y; } if (photon.Position.Y > this.bbox_max.Y) { this.bbox_max.Y = photon.Position.Y; } if (photon.Position.Z < this.bbox_min.Z) { this.bbox_min.Z = photon.Position.Z; } if (photon.Position.Z > this.bbox_max.Z) { this.bbox_max.Z = photon.Position.Z; } }
private void storePhoton(Photon photon, EnlightenmentType enlightenmentType) { switch (enlightenmentType) { //case EnlightenmentType.Direct: // //Do nothing - Classic RayTracing // ; //break; case EnlightenmentType.Indirect: this.storedIndirect++; this.indirectEnlightenment.Store(photon); break; case EnlightenmentType.Caustics: this.storedCaustic++; this.CausticsEnlightenment.Store(photon); break; } }
public void ShootPhoton(Photon photon, int depth, EnlightenmentType enlightenmentType) { Intersection intersection; if (this.scene.FindIntersection(photon, out intersection) && (depth < this.MaxRecursions)) { Material material = intersection.HitPrimitive.Material; //this.scene.Shader = material.CreateShader(this.scene); //RGBColor color = this.scene.Shader.Shade(photon, intersection); //float avgPower = photon.Power.Average; //float probDiff = avgPower * material.KDiff; //float probSpec = avgPower * material.KSpec; //float avgPower = photon.Power.Average; RGBColor mixColor = (photon.Power * material.DiffuseColor); float maxColor = Max(mixColor.R, mixColor.G, mixColor.B) / Max(photon.Power.R, photon.Power.G, photon.Power.B); float probDiff = maxColor * material.KDiff; float probTrans = maxColor * material.KTrans; float probSpec = maxColor * material.KSpec; Photon rPhoton = new Photon(); Random rdn = new Random(); double randomValue = rdn.NextDouble(); if (randomValue <= probDiff) { photon.Position = intersection.HitPoint; //photon.Power = mixColor / probDiff; this.storePhoton(photon, enlightenmentType); rPhoton.Position = intersection.HitPoint; rPhoton.Power = mixColor / probDiff; rPhoton.Direction = Vector3D.ReflectedDiffuse(intersection.Normal); this.ShootPhoton(rPhoton, depth + 1, EnlightenmentType.Indirect); } else if (randomValue <= probSpec + probDiff) { rPhoton.Direction = Vector3D.Reflected(intersection.Normal, photon.Direction); //rPhoton.Direction.Normalize(); rPhoton.Position = intersection.HitPoint; rPhoton.Power = mixColor / probSpec; this.ShootPhoton(rPhoton, depth + 1, EnlightenmentType.Caustics); } else if (randomValue <= probSpec + probDiff + probTrans) { Vector3D T; //float eta = intersection.HitFromInSide // ? material.RefractIndex * 1 / this.scene.RefractIndex // : this.scene.RefractIndex * 1 / material.RefractIndex; //float eta = this.scene.RefractIndex * 1 / material.RefractIndex; float eta = (photon.PrevRefractIndex.IsEqual(material.RefractIndex)) ? material.RefractIndex * 1 / this.scene.RefractIndex : this.scene.RefractIndex * 1 / material.RefractIndex; if (Vector3D.Refracted(intersection.Normal, photon.Direction, out T, eta)) { rPhoton.Position = intersection.HitPoint; rPhoton.Direction = T; rPhoton.Power = mixColor / probTrans; rPhoton.PrevRefractIndex = material.RefractIndex; this.ShootPhoton(rPhoton, depth + 1, EnlightenmentType.Caustics); } } else { photon.Position = intersection.HitPoint; //Absorb this.storePhoton(photon, enlightenmentType); } } }