/raytracer.cs
C# | 267 lines | 223 code | 26 blank | 18 comment | 37 complexity | a564884aaa2c52c83be90dbac3ed344d MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using OpenTK;
-
- namespace Template
- {
- class Raytracer
- {
- public Camera cam { get; set; }
- public Scene scene { get; set; }
- int bounce, maxBounce = 4; // Maximum ray depth
- Surface screenData;
- bool debugRay = false;
-
- public Raytracer(Surface s)
- {
- cam = new Camera(new Vector3(0, 0, -2), 1);
- scene = new Scene();
- screenData = s;
- }
-
- // Stuurt een ray per pixel
- public void Render(ref Surface screen)
- {
- screen.Clear(0);
- Vector3 trace = Vector3.Zero;
- Ray r = new Ray(cam.Origin, cam.V);
- float divwidth = 1f / cam.width;
- float divheight = 1f / cam.height;
- for (float i = 0; i < cam.width; i++)
- {
- for (float j = 0; j < cam.height; j++)
- {
- debugRay = false;
- if ((j == cam.height / 2) && i % 10 == 0)
- {
- debugRay = true;
- }
-
- trace = Vector3.Zero;
- // Anti aliasing switch
- if (true)
- {
- //Supersampling x2
- for (float xfrag = i; xfrag < i + 1.0f; xfrag += 0.50f)
- {
- for (float yfrag = j; yfrag < j + 1.0f; yfrag += 0.50f)
- {
- bounce = 0;
- r.D = ((xfrag * (cam.P2 - cam.P1) * divwidth) + (yfrag * (cam.P3 - cam.P1) * divheight) + cam.P1) - r.O;
- trace += Trace(r, ref screen) * 0.25f;
- }
- }
- }
- else
- {
- bounce = 0;
- r.D = ((i * (cam.P2 - cam.P1) * divwidth) + (j * (cam.P3 - cam.P1) * divheight) + cam.P1) - r.O;
- trace = Trace(r, ref screen);
- }
-
- int location = (int) (i + j * screen.width);
- screen.pixels[location] = CreateColor(trace);
- }
- }
-
-
- // Tekent debug circles
- float theta = (2.0f * 3.1415926f) / 100;
- float t = (float)Math.Tan(theta);
- float rad = (float)Math.Cos(theta);
- for (int i = 0; i < scene.SceneObjects.Length; i++)
- {
- if (scene.SceneObjects[i].ID == "Sphere")
- {
- float x = scene.SceneObjects[i].Straal;
- float y = 0;
- for (int j = 0; j < 99; j++)
- {
- float tx = -y;
- float ty = x;
- x += tx * t;
- y += ty * t;
- x *= rad;
- y *= rad;
-
- float tx2 = -y;
- float ty2 = x;
- float x2 = x + tx * t;
- float y2 = y + ty * t;
- x2 *= rad;
- y2 *= rad;
- screen.Line(TX(scene.SceneObjects[i].V.X + x), TY(scene.SceneObjects[i].V.Z + y),
- TX(scene.SceneObjects[i].V.X + x2), TY(scene.SceneObjects[i].V.Z + y2), CreateColor(scene.SceneObjects[i].Color));
-
- }
- }
- //screen.Box(TX(scene.SceneObjects[i].V.X) - 1, TY(scene.SceneObjects[i].V.Z) - 1,
- //TX(scene.SceneObjects[i].V.X) + 1, TY(scene.SceneObjects[i].V.Z) + 1, CreateColor(scene.SceneObjects[i].Color));
- }
-
-
- // Camera
- screen.Box(TX(cam.Origin.X) - 1, TY(cam.Origin.Z) - 1, TX(cam.Origin.X) + 1, TY(cam.Origin.Z) + 1, CreateColor(255, 255, 255));
- screen.Line(TX(cam.P1.X), TY(cam.P1.Z), TX(cam.P2.X), TY(cam.P2.Z), CreateColor(255, 255, 255));
-
- //Licht
- for (int i = 0; i < scene.light.Length; i++)
- {
- screen.Box(TX(scene.light[i].pos.X) - 1, TY(scene.light[i].pos.Z) - 1,
- TX(scene.light[i].pos.X) + 1, TY(scene.light[i].pos.Z) + 1, CreateColor(255, 255, 0));
- }
- }
-
- // Bepaalt de kleur van een bepaalde ray door intersectie
- public Vector3 Trace(Ray ray, ref Surface s)
- {
- Intersection I = scene.IntersectScene(ray, float.PositiveInfinity);
- if (I == null)
- return Vector3.Zero;
- if (debugRay && (I.Object.ID == "Sphere" || bounce > 0))
- {
- // Normals Debug
- // s.Line(TX(I.Target.X), TY(I.Target.Z), TX(I.Target.X + I.Normal.X / 2), TY(I.Target.Z + I.Normal.Z / 2), CreateColor(255, 255, 255));
- s.Line(TX(ray.O.X), TY(ray.O.Z), TX(I.Target.X), TY(I.Target.Z), bounce > 0 ? bounce > 1 ? CreateColor(0, 255, 255) : CreateColor(255, 255, 0) : 255);
- }
- if (I.Object.IsMirror && bounce < maxBounce)
- {
- bounce++;
- Vector3 ReflD = Reflect(ray.D, I.Normal);
- return Trace(new Ray(I.Target, ReflD), ref s) * 0.00392f * Vector3.One * 255 * I.Object.Specular +
- (1 - I.Object.Specular) * Vector3.Clamp(DirectIllum(I, ref s), Vector3.Zero, Vector3.One) * I.Color;
- }
- else if (I.Object.IsDielectric && bounce < maxBounce)
- {
- bounce++;
- float f = Fresnel(ray.D, I);
- Vector3 Refr = Refract(ray.D, I, ref s);
- Vector3 Refl = Reflect(ray.D, I.Normal);
- return (f * Trace(new Ray(I.Target, Refl), ref s) + (1f - f) * Trace(new Ray(I.Target + 0.001f * Refr, Refr), ref s)) * I.Color;
- }
- else
- {
- return Vector3.Clamp(DirectIllum(I, ref s), Vector3.Zero, Vector3.One) * I.Color;
- }
- }
-
- public float Fresnel(Vector3 D, Intersection I)
- {
- float ior1 = Vector3.Dot(D, I.Normal) > 0 ? I.Object.IOR : 1;
- float ior2 = Vector3.Dot(D, I.Normal) > 0 ? 1 : I.Object.IOR;
- float R = (float) Math.Pow((ior1 - ior2) / (ior1 + ior2), 2);
- float cos = Vector3.Dot(I.Normal, -D);
- return (float) (R + (1 - R) * Math.Pow(1 - cos, 5));
- //float alpha = 0.1f;
- //return (float)(1f * alpha + (1 - alpha) * Math.Pow(1 - cos, 3));
- }
-
- // Refract een ray op basis van de index of refraction van het intersectie object
- public Vector3 Refract(Vector3 D, Intersection I, ref Surface s)
- {
- float ior = Vector3.Dot(D, I.Normal) > 0 ? I.Object.IOR : I.Object.rIOR;
-
- float cos = Vector3.Dot(I.Normal, -D);
- float k = 1 - (ior * ior) * (1 - cos * cos);
- if (k < 0)
- {
- return Reflect(D, I.Normal);
- }
- else
- {
- return Vector3.Normalize(ior * D + I.Normal * (ior * cos - (float) Math.Sqrt(k)));
- }
- }
-
- // Geeft een nieuwe vector op basis van een oude en een normaal
- public Vector3 Reflect(Vector3 D, Vector3 N)
- {
- Vector3 R = D - 2f * Vector3.Dot(D, N) * N;
- return Vector3.Normalize(R);
- }
-
- // Geeft een kleur op basis van een intersectie
- public Vector3 DirectIllum(Intersection I, ref Surface s)
- {
- Vector3 T = Vector3.Zero;
- for (int i = 0; i < scene.light.Length; i++)
- {
-
- Vector3 L = Vector3.Subtract(scene.light[i].pos, I.Target);
- float dist = L.Length;
- if (!isVisible(I, L, dist, ref s))
- {
- continue;
- }
-
- L = Vector3.Normalize(L);
- float attenuation = 1 / (dist * dist);
- float dot = Vector3.Dot(I.Normal, L);
- if (dot < 0)
- {
- dot = 0;
- }
-
- //Spotlight
- if (scene.light[i] is SpotLight)
- {
- float angle = Vector3.CalculateAngle(L, scene.light[i].direction);
- if (angle > scene.light[i].angle)
- {
- continue;
- }
- else
- {
- T += scene.light[i].color * dot * attenuation;
- }
- }
- else
- {
-
- T += scene.light[i].color * dot * attenuation;
- }
- }
- return T;
- }
-
- // Kijkt of een bepaald intersectie punt belicht is
- public bool isVisible(Intersection I, Vector3 L, float dist, ref Surface s)
- {
- Ray shad = new Ray(I.Target, Vector3.Normalize(L));
- Intersection Is = scene.IntersectScene(shad, L.Length);
- if (Is != null && I.Object.Specular != 1 && !I.Object.IsDielectric)
- {
- if (debugRay)
- {
- s.Line(TX(I.Target.X), TY(I.Target.Z), TX((L + I.Target).X), TY((L + I.Target).Z), CreateColor(255, 0, 0));
- }
- return false;
- }
- return true;
- }
-
- public int TX(float t)
- {
- int x = (int)((t + 4) * (screenData.width * 0.5f / 8f) + (screenData.width / 2));
- return x;
- }
-
- public int TY(float t)
- {
- int y = (int)((-t + 4) * (screenData.height * 1f / 8f));
- return y;
- }
-
- public static int CreateColor(int R, int G, int B)
- {
- return (R << 16) + (G << 8) + B;
- }
- public static int CreateColor(Vector3 V)
- {
- return CreateColor((int) V.X, (int) V.Y, (int) V.Z);
- }
- }
- }