public override double GetIntersection(Ray ray) { double t1, t2; double A, A2, B, C, D, sqrtD; // move ray instead of sphere - same effect ray.Origin.X -= this.Position.X; ray.Origin.Y -= this.Position.Y; ray.Origin.Z -= this.Position.Z; // solve quadratic equation: C = Utils.Sqr(ray.Origin.X) + Utils.Sqr(ray.Origin.Y) + Utils.Sqr(ray.Origin.Z) - Utils.Sqr(Radius); A = Utils.Sqr(ray.Direction.X) + Utils.Sqr(ray.Direction.Y) + Utils.Sqr(ray.Direction.Z); B = 2 * ray.Origin.Dot(ray.Direction); D = B * B - 4 * A * C; if (D < 0) { // no solution return Constants.Infinity; } else { sqrtD = Math.Sqrt(D); A2 = 2 * A; t1 = (-B + sqrtD) / A2; t2 = (-B - sqrtD) / A2; t1 = Math.Min(t1, t2); return t1; } }
public override double GetIntersection(Ray ray) { double dot = Normal.Dot(ray.Direction); // parallel to the plane ? if (Math.Abs(dot) < Constants.Epsilon) { return Constants.Infinity; } else { // linear equation double t = -(Normal.Dot(ray.Origin) + this.D) / dot; return t; } }
/// <summary> /// Gets lightness and specular ligthness at given point in the scene. /// </summary> /// <param name="specularLightness">Returned specular lightness.</param> /// <param name="pos">Point in space where to calc ligthness.</param> /// <param name="reflectionDir">Direction of reflection.</param> /// <param name="normalDir">Direction of normal.</param> /// <param name="surfaceSpecular">Specular of the material.</param> /// <returns></returns> private Color lightColorAt(Vector pos, out double specularLightness, Vector vReflection, Vector normal, double surfaceSpecular) { Ray shadowRay = new Ray(); shadowRay.Origin = pos; Color result = Color.Black; specularLightness = 0.0; foreach (Light light in this.lights) { int visibleRayCount = 0; // test all rays to this light for (int i = 0; i < light.GetRayCount(); i++) { shadowRay.Direction = light.GetNextRaySource() - shadowRay.Origin; // only front sides get lit if (shadowRay.Direction.Dot(normal) >= Constants.Epsilon) { double isectDistance = 0; objects.GetClosestIntersection(ref shadowRay, out isectDistance); //double isectDistance2 = isectDistance * isectDistance; //light visible from pos? //if (!(isectDistance2 > Constants.Epsilon && isectDistance2 < shadowRay.Direction.LenSquared - Constants.Epsilon)) double lightDist2 = shadowRay.Direction.LenSquared; shadowRay.Direction.Normalize(); if (!(isectDistance > Constants.Epsilon && isectDistance*isectDistance < lightDist2 - Constants.Epsilon)) { visibleRayCount++; Vector reflectionNorm = vReflection; reflectionNorm.Normalize(); // is reflected ray pointing towards light? double specularDot = reflectionNorm.Dot(shadowRay.Direction); if (specularDot > -Constants.Epsilon) { specularDot *= specularDot; specularDot *= specularDot; specularDot *= specularDot; specularDot *= specularDot; specularDot *= specularDot; // ^32 specularLightness += specularDot; } } } } result = Color.Combine(result, 1.0, light.Color, visibleRayCount / (double)light.GetRayCount() * light.Value * shadowRay.Direction.Dot(normal)); } specularLightness *= surfaceSpecular; return result; }
/// <summary> /// Gets color by shooting a ray into scene. Recursive. /// </summary> /// <param name="ray"></param> /// <param name="depth">Current depth of recursion (0 is primary ray).</param> /// <returns></returns> public Color tracePixel(Ray ray, int depth) { double isectDistance = Constants.Infinity; Primitive closest = objects.GetClosestIntersection(ref ray, out isectDistance); if (closest == null) { return this.settings.FillColor; } else { Vector isectPoint = ray.Origin + isectDistance * ray.Direction; Vector normal = closest.GetNormalAt(isectPoint); Material material = closest.GetMaterialAt(isectPoint); // reflection = v - 2*(v dot n)*n Vector vReflection = ray.Direction - (2 * normal.Dot(ray.Direction)) * normal; double specularLightness = 0.0; Color lightColor = lightColorAt(isectPoint, out specularLightness, vReflection, normal, material.Specular); Color primaryColor = material.Color * lightColor; Color reflectedColor = new Color(); Color alphaColor = new Color(); double reflectance = material.Reflectance; double opacity = material.Color.A; if (depth < Scene.maxRecursionDepth && reflectance > Constants.Epsilon) { Ray rayReflection; rayReflection.Direction = vReflection; // move by a small epsilon along reflection vector rayReflection.Origin = isectPoint + (Constants.Epsilon * vReflection); reflectedColor = tracePixel(rayReflection, depth + 1); } if (depth < Scene.maxRecursionDepth && opacity < 1.0 - Constants.Epsilon) { Ray rayAlpha; // direction remains the same rayAlpha.Direction = ray.Direction; // progress by small epsilon forward rayAlpha.Origin = isectPoint + (2 * Constants.Epsilon * rayAlpha.Direction); alphaColor = tracePixel(rayAlpha, depth + 1); } // first reflection, then alpha (transparent mirror is still transparent) Color withoutAlpha = Color.Combine(primaryColor, 1 - reflectance, reflectedColor, reflectance); return Color.Combine(withoutAlpha, opacity, alphaColor, 1 - opacity) * (1 + specularLightness); // first alpha, then reflection (transparent mirror is still a mirror) /*Color withoutReflection = ColorCombine(primaryColor, opacity, alphaColor, 1 - opacity); return ColorCombine(withoutReflection, 1 - reflectance, reflectedColor, reflectance);*/ } }
public void Render(System.Drawing.Rectangle rect, Color[] buffer) { if (this.settings == null) { throw new InvalidOperationException("Scene settings must be set before rendering."); } if (buffer.Length != rect.Width * rect.Height) throw new InvalidOperationException("Buffer must be same size as the rectangle to render"); Vector eye = settings.View.Origin; Ray ray; ray.Origin = eye; Parallel.For(0, rect.Height, delegate(int y) { int x = 0; for (x = 0; x < rect.Width; x++) { Vector rayEnd = // origin + linear combination of topSide (for x) and leftSide (for y) this.settings.View.RenderRectOrigin + ((rect.Left + x) / (double)this.settings.ImageWidth) * this.settings.View.RenderRectTopSide + ((rect.Top + y) / (double)this.settings.ImageHeight) * this.settings.View.RenderRectLeftSide; Ray r = new Ray(); r.Origin = ray.Origin; r.Direction = rayEnd - r.Origin; if (x == 92 && y == 76) { int a = 5; int b = a; if (b == 300) throw new Exception(); } Color c = this.tracePixel(r, 0); int bufIndex = x + y * rect.Width; // concurrent writing lock (this) { buffer[bufIndex] = c; } } }); }
public override double GetIntersection(Ray ray) { double distance = this.plane.GetIntersection(ray); if (distance < Constants.Epsilon) return Constants.Infinity; Vector isect = Vector.Combine(ray.Origin, 1.0, ray.Direction, distance); // insert point into plane equation, // test if it lies on negative side -> outside triangle if (bounds[0].liesOnNegativeSide(ref isect)) return Constants.Infinity; if (bounds[1].liesOnNegativeSide(ref isect)) return Constants.Infinity; if (bounds[2].liesOnNegativeSide(ref isect)) return Constants.Infinity; return distance; }
static double intersectPlaneZ(Ray ray, double planeZ) { return intersectPlane(ray.Origin.Z, ray.Direction.Z, planeZ); }
/// <summary> /// Intersects ray with plane with normal in X axis coming through point [x,0,0]. /// </summary> static double intersectPlaneX(Ray ray, double planeX) { return intersectPlane(ray.Origin.X, ray.Direction.X, planeX); }
static double intersectPlaneY(Ray ray, double planeY) { return intersectPlane(ray.Origin.Y, ray.Direction.Y, planeY); }
public double GetIntersection(Ray ray) { // find largest tNear, smallest tFar double tNear = -Constants.Infinity; double tFar = Constants.Infinity; double t1, t2; // for x, y, z for (int i = 0; i < 3; i++) { // intersect in one axis // TODO t1 = intersectPlane(ref ray, ref this.LeftTopFront, i); //lock (typeof(BoundingBox)) { t1 = planeIntersectors[i](ray, this.LeftTopFront[i]); } // ray parallel to planes if (t1 == Constants.Infinity) { //lock (typeof(BoundingBox)) { // ray outside the box if (ray.Origin[i] < this.LeftTopFront[i] || ray.Origin[i] > this.RightBottomBack[i]) return Constants.Infinity; } } else { //lock (typeof(BoundingBox)) { // TODO t2 = intersectPlane(ref ray, ref this.RightBottomBack , i); t2 = planeIntersectors[i](ray, this.RightBottomBack[i]); if (t1 > t2) Utils.Swap<double>(ref t1, ref t2); // want largest near intersection tNear = Math.Max(t1, tNear); // want smallest far intersection tFar = Math.Min(t2, tFar); } } // box missed if (tNear > tFar) return Constants.Infinity; // box is behind if (tFar < -Constants.Epsilon) return Constants.Infinity; } return tNear; }
public override double GetIntersection(Ray ray) { return this.box.GetIntersection(ray); }
public Primitive GetClosestIntersection(ref Ray ray, out double distance) { LinkedList<OctreeNode> hitNodes = new LinkedList<OctreeNode>(); if (root.boundingBox.GetIntersection(ray) != Constants.Infinity) { traverse(root, ray, hitNodes); } Primitive foundPrimitive = null; double minDist = Constants.Infinity; foreach (OctreeNode node in hitNodes) { double d = 0; Primitive p = node.primitives.GetClosestIntersection(ref ray, out d); if (d < minDist) { minDist = d; foundPrimitive = p; } } // test big objects double dBig = 0; Primitive pBig = bigObjects.GetClosestIntersection(ref ray, out dBig); if (dBig < minDist) { minDist = dBig; foundPrimitive = pBig; } distance = minDist; return foundPrimitive; }
// returns all leaf nodes hit by the ray private void traverse(OctreeNode node, Ray ray, LinkedList<OctreeNode> list) { if (node.primitives != null) { list.AddLast(node); } if (node.childs != null) { for (int i = 0; i < 8; i++) { if (node.childs[i].boundingBox.GetIntersection(ray) != Constants.Infinity) { traverse(node.childs[i], ray, list); } } } }
/// <summary> /// Finds intersection of this object with given ray. /// </summary> /// <param name="ray">Ray to intersect with.</param> /// <returns>Distance as ray.Direction multiplier, can be negative.</returns> public abstract double GetIntersection(Ray ray);