public TriangleShape(PosVector position, PosVector vb, PosVector vc, BaseMaterial frontMaterial, BaseMaterial backMaterial) : base(position) { VB = vb; VC = vc; _frontMaterial = frontMaterial; _backMaterial = backMaterial; EdgeAb = VB - VA; EdgeBc = VC - VB; EdgeCa = VA - VC; Normal = EdgeAb.Dot(EdgeBc) < EdgeBc.Dot(EdgeCa) ? EdgeAb.Cross(EdgeBc) : EdgeBc.Cross(EdgeCa); Magnitude = Normal.Magnitude(); if (Magnitude > 0.0) { Normal = Normal / Magnitude; // unit vector to triangle plane } PlaneCoefficient = Normal.Dot(VA); // same coeff for all three vertices var a = EdgeAb.MagnitudeSquared(); var b = EdgeAb.Dot(EdgeCa); var c = EdgeCa.MagnitudeSquared(); var dinv = 1.0 / (a * c - b * b); a = a * dinv; b = b * dinv; c = c * dinv; Ubeta = (EdgeAb * c).AddScaled(EdgeCa, -b); Ugamma = (EdgeCa * -a).AddScaled(EdgeAb, b); }
public KdNode(KdNodePlane plane, PosVector coord, List <IShape> list, KdNode nodeLeft, KdNode nodeRight) { Plane = plane; Coord = coord; Shapes = list; Left = nodeLeft; Right = nodeRight; }
private Ray GetRefractionRay(PosVector p, PosVector n, PosVector v, double refraction) { var c1 = n.Dot(v); var c2 = 1.0 - refraction * refraction * Math.Sqrt(1.0 - c1 * c1); var t = (n * (refraction * c1 - c2) - v * refraction * -1.0).Normalize(); return(new Ray(p, t)); }
private PosVector GetVectorPlaneIntersection(Ray ray, KdNodePlane plane, PosVector coord) { switch (plane) { case KdNodePlane.XY: if (((coord.Z < ray.Position.Z) && (ray.Direction.Z > 0.0)) || ((coord.Z > ray.Position.Z) && (ray.Direction.Z < 0.0))) { return(null); } else { double k = (coord.Z - ray.Position.Z) / ray.Direction.Z; return(new PosVector( ray.Position.X + (ray.Direction.X * k), ray.Position.Y + (ray.Direction.Y * k), coord.Z)); } case KdNodePlane.XZ: if (((coord.Y < ray.Position.Y) && (ray.Direction.Y > 0.0)) || ((coord.Y > ray.Position.Y) && (ray.Direction.Y < 0.0))) { return(null); } else { double k = (coord.Y - ray.Position.Y) / ray.Direction.Y; return(new PosVector( ray.Position.X + (ray.Direction.X * k), coord.Y, ray.Position.Z + (ray.Direction.Z * k))); } case KdNodePlane.YZ: if (((coord.X < ray.Position.X) && (ray.Direction.X > 0.0)) || ((coord.X > ray.Position.X) && (ray.Direction.X < 0.0))) { return(null); } else { double k = (coord.X - ray.Position.X) / ray.Direction.X; return(new PosVector( coord.X, ray.Position.Y + (ray.Direction.Y * k), ray.Position.Z + (ray.Direction.Z * k))); } default: throw new InvalidOperationException(); } }
private static (BoundingBox, BoundingBox) SplitBoundingBox( BoundingBox bbox, KdNodePlane plane, PosVector coord) { BoundingBox bboxLeft; BoundingBox bboxRight; switch (plane) { case KdNodePlane.XY: bboxLeft = new BoundingBox( new Bound(bbox.BoxMin.X, bbox.BoxMax.X), new Bound(bbox.BoxMin.Y, bbox.BoxMax.Y), new Bound(bbox.BoxMin.Z, coord.Z)); bboxRight = new BoundingBox( new Bound(bbox.BoxMin.X, bbox.BoxMax.X), new Bound(bbox.BoxMin.Y, bbox.BoxMax.Y), new Bound(coord.Z, bbox.BoxMax.Z)); break; case KdNodePlane.XZ: bboxLeft = new BoundingBox( new Bound(bbox.BoxMin.X, bbox.BoxMax.X), new Bound(bbox.BoxMin.Y, coord.Y), new Bound(bbox.BoxMin.Z, bbox.BoxMax.Z)); bboxRight = new BoundingBox( new Bound(bbox.BoxMin.X, bbox.BoxMax.X), new Bound(coord.Y, bbox.BoxMax.Y), new Bound(bbox.BoxMin.Z, bbox.BoxMax.Z)); break; case KdNodePlane.YZ: bboxLeft = new BoundingBox( new Bound(bbox.BoxMin.X, coord.X), new Bound(bbox.BoxMin.Y, bbox.BoxMax.Y), new Bound(bbox.BoxMin.Z, bbox.BoxMax.Z)); bboxRight = new BoundingBox( new Bound(coord.X, bbox.BoxMax.X), new Bound(bbox.BoxMin.Y, bbox.BoxMax.Y), new Bound(bbox.BoxMin.Z, bbox.BoxMax.Z)); break; default: throw new InvalidOperationException(); } return(bboxLeft, bboxRight); }
private ColorVector GetLightingColor(PosVector point, PosVector normal) { var lightColor = new ColorVector(); foreach (var light in Scene.Lights) { // if not shaded if (IsViewable(light.Position, point)) { var lightVector = light.Position - point; lightColor = lightColor + (light.Color * Math.Abs(PosVector.CosVectors(normal, lightVector))); } } return(lightColor); }
private bool IsViewable(PosVector targetPoint, PosVector startingPoint) { var dir = targetPoint - startingPoint; double targetDist = dir.Magnitude(); var intInfo = Scene.KdTree.FindIntersectionTree(new Ray(startingPoint, dir)); if (intInfo.IsHit) { // check if intersection point is closer than target point return(targetDist < intInfo.Distance); } // ray doesn't intersect any of scene objects return(true); }
public Camera(PosVector pos, PosVector lookAt, PosVector up, double fov) { Position = pos; LookAt = lookAt; Up = up; Fov = fov; _a3 = (LookAt - Position).Normalize(); _a1 = _a3.Cross(up).Normalize(); _a2 = _a1.Cross(_a3).Normalize(); var viewAngleRadians = Fov * 0.017453239; _dval = Math.Cos(viewAngleRadians / 2.0) / Math.Sin(viewAngleRadians / 2.0); _center = _a3 * _dval; }
private ColorVector GetSpecularColor(Ray ray, double p) { var lightColor = new ColorVector(); foreach (var light in Scene.Lights) { // if not shaded if (IsViewable(light.Position, ray.Position)) { var lightSourceVector = light.Position - ray.Position; var cosLightSource = PosVector.CosVectors(ray.Direction, lightSourceVector); if (cosLightSource > double.Epsilon) { lightColor = lightColor * p; // + (light.Color * Math.Pow(cosLightSource, p)); } } } return(lightColor); }
public BoundingBox(PosVector boxMin, PosVector boxMax) { BoxMin = boxMin; BoxMax = boxMax; }
private static KdNode MakeLeaf(List <IShape> shapes) { return(new KdNode(KdNodePlane.NoPlane, PosVector.NewDefault(), shapes, null, null)); }
/* * Using Surface Area Heuristic (SAH) for finding best split pane * SAH = 0.5 * voxel_surface_area * number_of_objects_in_voxel * splitted_SAH = split_cost * + 0.5 * left_voxel_surface_area * number_of_objects_in_left_voxel * + 0.5 * right_voxel_surface_area * number_of_objects_in_right_voxel * Finding coordinate of split plane (XY, XZ or YZ) which minimizing SAH * If can't find optimal split plane - returns NONE * see: http://stackoverflow.com/a/4633332/653511 */ private static (KdNodePlane, PosVector) FindPlane(List <IShape> shapes, BoundingBox bbox, int depth) { var finalPlane = KdNodePlane.NoPlane; var finalPos = PosVector.NewDefault(); if (depth >= MaxTreeDepth || shapes.Count <= NumObjectsInLeaf) { return(KdNodePlane.NoPlane, PosVector.NewDefault()); } double hx = bbox.BoxMax.X - bbox.BoxMin.X; double hy = bbox.BoxMax.Y - bbox.BoxMin.Y; double hz = bbox.BoxMax.Z - bbox.BoxMin.Z; // calculate square of each side of initial bounding box double sxy = hx * hy; double sxz = hx * hz; double syz = hy * hz; double ssum = sxy + sxz + syz; // normalize square of each side of initial bounding box to satisfy following relationship: // sxy + sxz + syz = 1 sxy = sxy / ssum; sxz = sxz / ssum; syz = syz / ssum; int maxSplits = 5; // max splits of bounding box double splitCost = 5.0; // assum that at beginning best SAH has initial bounding box // SAH = 0.5 * square * objectsCount // square of initial bounding box is sxy + sxz + syz = 1 double bestSah = Convert.ToDouble(shapes.Count); // initial bounding box doesn't have split plane finalPlane = KdNodePlane.NoPlane; var currentSplitCoord = PosVector.NewDefault(); // find split surface which have the least SAH // trying to minimize SAH by splitting across XY plane double sSplit = sxy; double sNonSplit = sxz + syz; for (int i = 1; i < maxSplits; i++) { double l = Convert.ToDouble(i) / Convert.ToDouble(maxSplits); double r = 1.0 - l; // current coordinate of split surface currentSplitCoord = new PosVector(currentSplitCoord.X, currentSplitCoord.Y, bbox.BoxMin.Z + l * hz); var(vl, vr) = SplitBoundingBox(bbox, KdNodePlane.XY, currentSplitCoord); var currentSah = (sSplit + l * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vl)) + (sSplit + r * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vr)) + splitCost; if (currentSah < bestSah) { bestSah = currentSah; finalPlane = KdNodePlane.XY; finalPos = currentSplitCoord; } } // trying to minimize SAH by splitting across XZ plane sSplit = sxz; sNonSplit = sxy + syz; for (int i = 1; i < maxSplits; i++) { double l = Convert.ToDouble(i) / Convert.ToDouble(maxSplits); double r = 1.0 - l; // current coordinate of split surface currentSplitCoord = new PosVector(currentSplitCoord.X, bbox.BoxMin.Y + l * hy, currentSplitCoord.Z); var(vl, vr) = SplitBoundingBox(bbox, KdNodePlane.XZ, currentSplitCoord); var currentSah = (sSplit + l * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vl)) + (sSplit + r * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vr)) + splitCost; if (currentSah < bestSah) { bestSah = currentSah; finalPlane = KdNodePlane.XZ; finalPos = currentSplitCoord; } } // trying to minimize SAH by splitting across YZ plane sSplit = syz; sNonSplit = sxy + sxz; for (int i = 1; i < maxSplits; i++) { double l = Convert.ToDouble(i) / Convert.ToDouble(maxSplits); double r = 1.0 - l; // current coordinate of split surface currentSplitCoord = new PosVector(bbox.BoxMin.X + l * hy, currentSplitCoord.Y, currentSplitCoord.Z); var(vl, vr) = SplitBoundingBox(bbox, KdNodePlane.YZ, currentSplitCoord); var currentSah = (sSplit + l * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vl)) + (sSplit + r * sNonSplit) * Convert.ToDouble(NumShapesInBoundingBox(shapes, vr)) + splitCost; if (currentSah < bestSah) { bestSah = currentSah; finalPlane = KdNodePlane.YZ; finalPos = currentSplitCoord; } } return(finalPlane, finalPos); }
public SphereShape(PosVector position, double radius, BaseMaterial material) : base(position) { Radius = radius; _material = material; }
public static NffParserResult ParseFile(string path, int numThreads, int rayTraceDepth, int resolutionX, int resolutionY) { var background = new Background(new ColorVector(0.0, 0.0, 0.0), 0.0); var lights = new List <Light>(); var shapes = new List <Shape>(); var cameraAt = PosVector.NewDefault(); var cameraFrom = PosVector.NewDefault(); var cameraUp = PosVector.NewDefault(); var lookingFor = LookingFor.Instruction; var currentMaterial = new SolidMaterial(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, new ColorVector(0.0, 0.0, 0.0)); var polyVectors = new List <PosVector>(); var currentItemCounter = 0; var lines = File.ReadAllLines(path); foreach (var line in lines) { var split = line.Split(' ', '\t'); switch (lookingFor) { case LookingFor.Instruction: { var instruction = split[0]; if (instruction == "b") { // background color background = new Background(new ColorVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])), 0.0); } else if (instruction == "v") { // viewpoint location lookingFor = LookingFor.ViewpointFrom; } else if (instruction == "l") { // positional light var colorVector = split.Length == 7 ? new ColorVector(double.Parse(split[4]), double.Parse(split[5]), double.Parse(split[6])) : new ColorVector(1.0, 1.0, 1.0); lights.Add( new PointLight(new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])), colorVector)); } else if (instruction == "f") { // println!("reading f: {}", num); // object material properties // "f" red green blue Kd Ks Shine T index_of_refraction // Kd Diffuse component // Ks Specular // Shine Phong cosine power for highlights // T Transmittance (fraction of contribution of the transmitting ray). // Usually, 0 <= Kd <= 1 and 0 <= Ks <= 1, though it is not required that Kd + Ks = 1. Note that transmitting objects (T > 0) are considered to have two sides for algorithms that need these (normally, objects have one side). // todo: i don't think i'm assigning the correct values into my solidmaterial yet currentMaterial = new SolidMaterial( 0.0, // kAmbient double.Parse(split[4]), // kDiffuse double.Parse(split[5]), // kSpecular double.Parse(split[7]), // kReflection double.Parse(split[8]), // kTransparent double.Parse(split[8]), // refraction -- todo: which is which here? double.Parse(split[6]), // gloss new ColorVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])) ); } else if (instruction == "c") { // cone or cylinder } else if (instruction == "s") { // sphere shapes.Add(new SphereShape( new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])), double.Parse(split[4]), currentMaterial )); } else if (instruction == "p") { // polygon currentItemCounter = int.Parse(split[1]); polyVectors = new List <PosVector>(); lookingFor = LookingFor.Polygon; } else if (instruction == "pp") { // polygon patch } else if (instruction == "#") { // comment } } break; case LookingFor.Polygon: { if (currentItemCounter > 0) { currentItemCounter--; polyVectors.Add(new PosVector(double.Parse(split[0]), double.Parse(split[1]), double.Parse(split[2]))); } if (currentItemCounter == 0) { if (polyVectors.Count >= 3) { var firstVert = polyVectors[0]; var prevVert = polyVectors[1]; var thisVert = polyVectors[2]; shapes.Add(new TriangleShape(firstVert, prevVert, thisVert, currentMaterial, currentMaterial)); for (var i = 3; i < polyVectors.Count; i++) { prevVert = thisVert; thisVert = polyVectors[i]; shapes.Add(new TriangleShape(firstVert, prevVert, thisVert, currentMaterial, currentMaterial)); } } lookingFor = LookingFor.Instruction; } } break; case LookingFor.ViewpointFrom: { cameraFrom = new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])); lookingFor = LookingFor.ViewpointAt; } break; case LookingFor.ViewpointAt: { cameraAt = new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])); lookingFor = LookingFor.ViewpointUp; } break; case LookingFor.ViewpointUp: { cameraUp = new PosVector(double.Parse(split[1]), double.Parse(split[2]), double.Parse(split[3])); lookingFor = LookingFor.ViewpointAngle; } break; case LookingFor.ViewpointAngle: { // todo: implement lookingFor = LookingFor.ViewpointHither; } break; case LookingFor.ViewpointHither: { // todo: implement lookingFor = LookingFor.ViewpointResolution; } break; case LookingFor.ViewpointResolution: { //resolutionX = int.Parse(split[1]); //resolutionY = int.Parse(split[2]); lookingFor = LookingFor.Instruction; } break; } } return(new NffParserResult(Scene.Create(background, shapes, lights), new RenderData(resolutionX, resolutionY, rayTraceDepth, numThreads, true), new Camera(cameraFrom, cameraAt, cameraUp, 50.0))); }
public static double CosVectors(PosVector v1, PosVector v2) { return(v1.Dot(v2) / Math.Sqrt(v1.MagnitudeSquared() * v2.MagnitudeSquared())); }
public PosVector AddScaled(PosVector b, double scale) { return(new PosVector(X + scale * b.X, Y + scale * b.Y, Z + scale * b.Z)); }
public PosVector Cross(PosVector b) { return(new PosVector(Y * b.Z - Z * b.Y, Z * b.X - X * b.Z, X * b.Y - Y * b.X)); }
public double Dot(PosVector b) { return(X * b.X + Y * b.Y + Z * b.Z); }
public bool IsPointInside(PosVector pos) { return(IsWithinX(pos.X) && IsWithinY(pos.Y) && IsWithinZ(pos.Z)); }
private Ray ReflectRay(Ray sourceRay, PosVector normal) { return(new Ray( sourceRay.Position, sourceRay.Direction + normal * 2.0 * -normal.Dot(sourceRay.Direction))); }
private Ray GetReflectionRay(PosVector p, PosVector n, PosVector v) { var rl = v + n * 2.0 * -n.Dot(v); return(new Ray(p, rl)); }