/// standard object services --------------------------------------------------- public Scene(StreamReader infile, Vector eyePosition) { // read and condition default sky and ground values skyEmission = new Vector(infile); groundReflection = new Vector(infile); skyEmission = skyEmission.Clamp(Vector.ZERO, skyEmission); groundReflection = skyEmission * groundReflection.Clamp(Vector.ZERO, Vector.ONE); triangles = new List<Triangle>(); emitters = new List<Triangle>(); // read objects while (infile.EndOfStream == false) { Triangle t = new Triangle(infile); if (t.Area != 0) triangles.Add(t); } // find emitting triangles foreach (Triangle t in triangles) { if (!t.Emitivity.IsZero() && (t.Area > 0.0f)) { emitters.Add(t); if (emitters.Count >= (1 << MAX_EMITTERS_P)) break; } } // make index octtree = new Spatial(eyePosition, triangles); }
public Vector GetRadiance(Vector rayOrigin, Vector rayDirection, Sampler random, Triangle lastHit) { // intersect ray with scene Triangle pHitObject; Vector hitPosition; scene.GetIntersection(rayOrigin, rayDirection, lastHit, out pHitObject, out hitPosition); Vector radiance; if (null != pHitObject) { // make surface point of intersection SurfacePoint surfacePoint = new SurfacePoint(pHitObject, hitPosition); // local emission only for first-hit if (lastHit != null) radiance = Vector.ZERO; else radiance = surfacePoint.GetEmission(rayOrigin, -rayDirection, false); // add emitter sample radiance = radiance + SampleEmitters(rayDirection, surfacePoint, random); // add recursive reflection // // single hemisphere sample, ideal diffuse BRDF: // reflected = (inradiance * pi) * (cos(in) / pi * color) * reflectance // -- reflectance magnitude is 'scaled' by the russian roulette, cos is // importance sampled (both done by SurfacePoint), and the pi and 1/pi // cancel out Vector nextDirection; Vector color; // check surface bounces ray, recurse if (surfacePoint.GetNextDirection(random, -rayDirection, out nextDirection, out color)) radiance = radiance + (color * GetRadiance(surfacePoint.Position, nextDirection, random, surfacePoint.Item)); } else // no hit: default/background scene emission radiance = scene.GetDefaultEmission(-rayDirection); return radiance; }
public void GetIntersection( Vector rayOrigin, Vector rayDirection, Triangle lastHit, out Triangle pHitObject, out Vector hitPosition, Vector pStart) { hitPosition = null; pHitObject = null; // is branch: step through subcells and recurse if (isBranch) { if (pStart == null) pStart = rayOrigin; // find which subcell holds ray origin (ray origin is inside cell) int subCell = 0; for (int i = 3; i-- > 0; ) { // compare dimension with center if (pStart[i] >= ((bound[i] + bound[i + 3]) * 0.5f)) subCell |= 1 << i; } // step through intersected subcells Vector cellPosition = new Vector(pStart); for (; ; ) { if (null != spatial[subCell]) { // intersect subcell spatial[subCell].GetIntersection(rayOrigin, rayDirection, lastHit, out pHitObject, out hitPosition, cellPosition); // exit if item hit if (null != pHitObject) break; } // find next subcell ray moves to // (by finding which face of the corner ahead is crossed first) int axis = 2; float[] step = new float[3]; // todo - move this allocation? for (int i = 3; i-- > 0; axis = step[i] < step[axis] ? i : axis) { bool high = ((subCell >> i) & 1) != 0; float face = (rayDirection[i] < 0.0f) ^ high ? bound[i + ((high ? 1 : 0) * 3)] : (bound[i] + bound[i + 3]) * 0.5f; // distance to face // (div by zero produces infinity, which is later discarded) step[i] = (face - rayOrigin[i]) / rayDirection[i]; } // leaving branch if: subcell is low and direction is negative, // or subcell is high and direction is positive if ((((subCell >> axis) & 1) != 0) ^ (rayDirection[axis] < 0.0f)) break; // move to (outer face of) next subcell cellPosition = rayOrigin + (rayDirection * step[axis]); subCell = subCell ^ (1 << axis); } } else { // is leaf: exhaustively intersect contained items float nearestDistance = float.MaxValue; // step through items foreach (Triangle item in triangles) { // avoid false intersection with surface just come from if (item != lastHit) { // intersect ray with item, and inspect if nearest so far float distance = float.MaxValue; if (item.GetIntersection(rayOrigin, rayDirection, ref distance) && (distance < nearestDistance)) { // check intersection is inside cell bound (with tolerance) Vector hit = rayOrigin + (rayDirection * distance); float t = Triangle.TOLERANCE; if ((bound[0] - hit[0] <= t) && (hit[0] - bound[3] <= t) && (bound[1] - hit[1] <= t) && (hit[1] - bound[4] <= t) && (bound[2] - hit[2] <= t) && (hit[2] - bound[5] <= t)) { pHitObject = item; nearestDistance = distance; hitPosition = hit; } } } } } }
public void GetEmitter(Sampler random, out Vector position, out Triangle triangle) { if (emitters.Count != 0) { // select emitter // not using lower bits, by treating the random as fixed-point i.f bits int index = ((((int)(int.MaxValue*random.GetNextSample()) & ((1 << MAX_EMITTERS_P) - 1)) * emitters.Count) >> MAX_EMITTERS_P); // get position on triangle position = emitters[index].GetSamplePoint(random); triangle = emitters[index]; } else { position = Vector.ZERO; triangle = null; } }
public void GetIntersection( Vector rayOrigin, Vector rayDirection, Triangle lastHit, out Triangle pHitObject, out Vector hitPosition) { octtree.GetIntersection(rayOrigin, rayDirection, lastHit, out pHitObject, out hitPosition, null); }