/// 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); }
/// standard object services --------------------------------------------------- public Camera(StreamReader infile) { // read and condition view definition ViewPosition = new Vector(infile); viewDirection = new Vector(infile); viewAngle = infile.ReadFloat(); viewDirection = viewDirection.Unitize(); if (viewDirection.IsZero()) viewDirection = new Vector(0.0f, 0.0f, 1.0f); if (viewAngle < 10) viewAngle = 10; if (viewAngle > 160) viewAngle = 160; viewAngle *= (float)(Math.PI / 180); // make other directions of frame up = new Vector(0.0f, 1.0f, 0.0f); right = up.Cross(viewDirection).Unitize(); if (!right.IsZero()) up = viewDirection.Cross(right).Unitize(); else { up = new Vector(0.0f, 0.0f, viewDirection[1] < 0.0f ? 1.0f : -1.0f); right = up.Cross(viewDirection).Unitize(); } } // Camera
public Vector GetDefaultEmission(Vector backDirection) { // sky for downward ray, ground for upward ray if (backDirection[1] < 0) return skyEmission; return groundReflection; }
Triangle[] triangles = null; // isBranch = false #endregion Fields #region Constructors public Spatial(Vector eyePosition, List<Triangle> items) { // set overall bound // accommodate eye position // (makes tracing algorithm simpler) for (int i = 6; i-- > 0; bound[i] = eyePosition[i % 3]) ; // accommodate all items foreach (Triangle item in items) { float[] itemBound = item.GetBound(); // accommodate item for (int j = 0; j < 6; ++j) { if ((bound[j] > itemBound[j]) ^ (j > 2)) bound[j] = itemBound[j]; } } // make cubical float maxSize = 0.0f; for (int i = 0; i < 3; ++i) maxSize = Math.Max(maxSize, bound[3 + i] - bound[i]); for (int i = 0; i < 3; ++i) bound[3 + i] = Math.Max(bound[3 + i], bound[i] + maxSize); // make cell tree Construct(items, 0); }
/// <summary> /// Create image based on size from stream /// </summary> /// <param name="infile"></param> public Image(int w , int h) { // read width and height Width = w; Height = h; // clamp width and height Width = Width < 1 ? 1 : (Width > 10000 ? 10000 : Width); Height = Height < 1 ? 1 : (Height > 10000 ? 10000 : Height); pixels = new Vector[Width, Height]; for (int i = 0; i < Width; ++i) for (int j = 0; j < Height; ++j) pixels[i, j] = new Vector(); }
/// <summary> /// Create image based on size from stream /// </summary> /// <param name="infile"></param> public Image(StreamReader infile) { // read width and height Width = (int)infile.ReadFloat(); Height = (int)infile.ReadFloat(); // clamp width and height Width = Width < 1 ? 1 : (Width > 10000 ? 10000 : Width); Height = Height < 1 ? 1 : (Height > 10000 ? 10000 : Height); pixels = new Vector[Width, Height]; for (int i = 0; i < Width; ++i) for (int j = 0; j < Height; ++j) pixels[i, j] = new Vector(); }
Vector SampleEmitters(Vector rayDirection, SurfacePoint surfacePoint, Sampler random) { Vector radiance; // single emitter sample, ideal diffuse BRDF: // reflected = (emitivity * solidangle) * (emitterscount) * // (cos(emitdirection) / pi * reflectivity) // -- SurfacePoint does the first and last parts (in separate methods) // get position on an emitter Vector emitterPosition; Triangle emitter; scene.GetEmitter(random, out emitterPosition, out emitter); // check an emitter was found if (null != emitter) { // make direction to emit point Vector emitDirection = (emitterPosition - surfacePoint.Position).Unitize(); // send shadow ray Triangle hitObject; Vector hitPosition; scene.GetIntersection(surfacePoint.Position, emitDirection, surfacePoint.Item, out hitObject, out hitPosition); StatsCounter.RayTraced(); // if unshadowed, get inward emission value Vector emissionIn = null; if ((null == hitObject) || (emitter == hitObject)) emissionIn = new SurfacePoint(emitter, emitterPosition).GetEmission(surfacePoint.Position, -emitDirection, true); else emissionIn = new Vector(); // get amount reflected by surface radiance = surfacePoint.GetReflection(emitDirection, emissionIn * scene.GetEmittersCount(), -rayDirection); } else radiance = new Vector(); return radiance; }
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; }
/// <summary> /// Construct a new triangle from a stream /// </summary> /// <param name="infile"></param> public Triangle(StreamReader infile) { // read three geometry points verts[0] = new Vector(infile); verts[1] = new Vector(infile); verts[2] = new Vector(infile); // read and condition quality Reflectivity = new Vector(infile).Clamp(Vector.ZERO, Vector.ONE); Emitivity = new Vector(infile).Clamp(Vector.ZERO, Vector.MAX); MaterialId = infile.ReadInt(); // some item caching edge1 = verts[1] - verts[0]; edge2 = verts[2] - verts[0]; // make area, Tangent, normal, deltas MakeTangent(); MakeNormal(); MakeArea(); }
public Vector Clamp(Vector min, Vector max) { float xn = Math.Min(Math.Max(x, min.x), max.x); float yn = Math.Min(Math.Max(y, min.y), max.y); float zn = Math.Min(Math.Max(z, min.z), max.z); return new Vector(xn, yn, zn); }
float CalculateToneMapping(Vector[,] pixels, float divider) { // calculate log mean luminance float logMeanLuminance = 1e-4f; float sumOfLogs = 0.0f; foreach (Vector p in pixels) { float Y = p.Dot(RGB_LUMINANCE) * divider; sumOfLogs += (float)Math.Log10(Y > 1e-4f ? Y : 1e-4f); } logMeanLuminance = (float)Math.Pow(10.0f, sumOfLogs / pixels.Length); // (what do these mean again? (must check the tech paper...)) float a = 1.219f + (float)Math.Pow(DISPLAY_LUMINANCE_MAX * 0.25f, 0.4f); float b = 1.219f + (float)Math.Pow(logMeanLuminance, 0.4f); return (float)Math.Pow(a / b, 2.5f) / DISPLAY_LUMINANCE_MAX; }
public void AddToPixel(int x, int y, Vector radiance) { #if PARALLEL lock (locker) #endif pixels[x, Height - 1 - y] += radiance; }
void MakeTangent() { if (Tangent == null) Tangent = (verts[1] - verts[0]).Unitize(); }
/* * @implementation * Adapted from: * <cite>'Fast, Minimum Storage Ray-Triangle Intersection' * Moller, Trumbore; * Journal Of Graphics Tools, v2n1p21, 1997. * http://www.acm.org/jgt/papers/MollerTrumbore97/</cite> */ public bool GetIntersection(Vector rayOrigin, Vector rayDirection, ref float hitDistance) { // begin calculating determinant - also used to calculate U parameter Vector pvec = rayDirection.Cross(edge2); // if determinant is near zero, ray lies in plane of triangle float det = edge1.Dot(pvec); const float EPSILON = 0.000001f; if ((det > -EPSILON) && (det < EPSILON)) return false; float inv_det = 1.0f / det; // calculate distance from vertex 0 to ray origin Vector tvec = rayOrigin - verts[0]; // calculate U parameter and test bounds float u = tvec.Dot(pvec) * inv_det; if ((u < 0.0f) || (u > 1.0f)) return false; // prepare to test V parameter Vector qvec = tvec.Cross(edge1); // calculate V parameter and test bounds float v = rayDirection.Dot(qvec) * inv_det; if ((v < 0.0f) || (u + v > 1.0f)) return false; // calculate t, ray intersects triangle hitDistance = edge2.Dot(qvec) * inv_det; // only allow intersections in the forward ray direction return hitDistance >= 0.0f; }
/// <summary> /// Copy constructor /// </summary> public Vector(Vector a) { x = a.x; y = a.y; z = a.z; }
public float Dot(Vector v) { return (x * v.x) + (y * v.y) + (z * v.z); }
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, 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 static Vector Unitize(Vector v) { float length = (float)Math.Sqrt((v.x * v.x) + (v.y * v.y) + (v.z * v.z)); if (length == 0) return Vector.ZERO; float inv = 1.0f / length; return new Vector(v.x * inv, v.y * inv, v.z * inv); }
public static float Dot(Vector u, Vector v) { return (u.x * v.x) + (u.y * v.y) + (u.z * v.z); }
public static Vector Cross(Vector u, Vector v) { return new Vector((u.y * v.z) - (u.z * v.y), (u.z * v.x) - (u.x * v.z), (u.x * v.y) - (u.y * v.x)); }
public Vector Cross(Vector v) { return new Vector((y * v.z) - (z * v.y), (z * v.x) - (x * v.z), (x * v.y) - (y * v.x)); }
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); }