private Vector3D GetColor(Ray r, HittableList world, int depth) { HitRecord rec; if (world.Hit(r, 0.001, double.MaxValue, out rec)) { Ray scattered; Vector3D attenuation; if (depth < 50 && rec.matPtr.Scatter(r, rec, out attenuation, out scattered)) //只递归50次,避免无谓的性能浪费 { Vector3D color = GetColor(scattered, world, depth + 1); //每次光线衰减之后深度加一 return(new Vector3D(attenuation.X * color.X, attenuation.Y * color.Y, attenuation.Z * color.Z)); } else { return(new Vector3D(0, 0, 0)); } } else { Vector3D unitDirection = r.Direction.UnitVector(); double t = 0.5 * (unitDirection.Y + 1); return((1 - t) * new Vector3D(1, 1, 1) + t * new Vector3D(0.5, 0.7, 1)); } }
private void Form1_Load(object sender, EventArgs e) { int nx = 200; int ny = 100; Bitmap bmp = new Bitmap(nx, ny); Vector3D lowerLeft = new Vector3D(-2, 1, -1); Vector3D horizontal = new Vector3D(4, 0, 0); Vector3D vertical = new Vector3D(0, -2, 0); Vector3D origin = new Vector3D(0, 0, 0); List <Hittable> list = new List <Hittable>(); list.Add(new Sphere(new Vector3D(0, 0, -1), 0.5)); list.Add(new Sphere(new Vector3D(0, -100.5, -1), 100)); HittableList world = new HittableList(list, 2); for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { double u = (double)i / (double)nx; double v = (double)j / (double)ny; Ray ray = new Ray(origin, lowerLeft + u * horizontal + v * vertical); Vector3D p = ray.GetPoint(2); Vector3D color = GetColor(ray, world); int r = (int)(255 * color.X); int g = (int)(255 * color.Y); int b = (int)(255 * color.Z); bmp.SetPixel(i, j, Color.FromArgb(r, g, b)); //如果之前没有将判别式除以4的话这里会报错,则需要限定一下边界, //将rgb的值限制在0到255之间,会得到一个和书上不一样的球 } } pictureBox1.BackgroundImage = bmp; }
static void Main(string[] args) { int xRes = 720; int yRes = 480; int runs = 10; IMaterial cyan = new LambertianDiffuse(new Color3(0, 0.88, 0.88)); IMaterial gray = new LambertianDiffuse(new Color3(0.5, 0.5, 0.5)); IMaterial red = new LambertianDiffuse(new Color3(0.8, 0.2, 0.2)); IMaterial metal = new Metal(new Color3(0.9, 0.9, 0.9), 0); Renderer raytracer = new() { // Define objects in the scene HittableObjects = new HittableList { new Sphere(0, 1, 0, 0.5, gray), new Sphere(1, 1, 0, 0.5, red), new Sphere(0, 1, -100.5, 100, metal), new Sphere(0, -2, 0, 1, cyan) } }; // Define the camera Camera camera = new(xRes, yRes) { Origin = new Vector3(-1, 0, 0), MultithreadedRendering = true, SamplesPerPixel = 100, MaxBounces = 12 }; long[] frameTimes = new long[runs]; for (int i = 0; i < runs; i++) { raytracer.RenderScene(camera, out long frameTime); frameTimes[i] = frameTime; Console.WriteLine("Run {0} | Time: {1} | Time Per Pixel: {2}", i, frameTime, (double)frameTime / (double)(xRes * yRes)); } Console.WriteLine("------------------------------------------------------------------"); Console.WriteLine("Total Runs: {0}\nResolution: {1}x{2}", runs, xRes, yRes); Console.WriteLine("------------------------------------------------------------------"); long sum = 0; Array.ForEach(frameTimes, i => sum += i); Console.WriteLine("Total frametime: {0} ms", sum); Console.WriteLine("Average frametime: {0} ms", sum / runs); Console.WriteLine("Average time per pixel: {0} ms", (double)sum / (double)runs / (double)(xRes * yRes)); Console.WriteLine("------------------------------------------------------------------"); } } }
static async Task Main(string[] args) { var img_width = int.Parse(args[0]); var img_height = int.Parse(args[1]); var spp = int.Parse(args[2]); var destination_folder = args[3]; var film = new Film(img_height, img_width, spp); var lookFrom = new Vector3(3, 3, 2); var lookAt = new Vector3(0, 0, -1); var vup = new Vector3(0, 1, 0); var cam = new Camera( lookFrom, lookAt, vup, 20, ((double)img_width) / img_height, 1, (lookFrom - lookAt).Length ); var rng = new Random(1); var world = new HittableList(new[] { new Sphere(new Vector3(0, 0, -1), 0.5, new Lambertian(new Vector3(0.1, 0.2, 0.5))), new Sphere(new Vector3(0, -100.5, -1), 100, new Lambertian(new Vector3(0.8, 0.8, 0))), new Sphere(new Vector3(1, 0, -1), 0.5, new Metal(new Vector3(0.8, 0.6, 0.2), 0)), new Sphere(new Vector3(-1, 0, -1), 0.5, new Dielectric(1.5)), new Sphere(new Vector3(-1, 0, -1), -0.45, new Dielectric(1.5)), }); for (var j = 0; j < img_height; j++) { Console.WriteLine($"Scanlines remaining: {(img_height - j).ToString()}"); for (var i = 0; i < img_width; i++) { for (var s = 0; s < spp; s++) { var u = (i + rng.NextDouble()) / img_width; var v = (j + rng.NextDouble()) / img_height; var ray = cam.GetRay(u, v); film.AddSample(i, j, RayColor(ray, world, 50)); } } } Console.WriteLine("Writing film"); await film.WriteToFile($@"{destination_folder}\img_{DateTime.UtcNow.Ticks}.ppm"); Console.WriteLine("Done"); }
private Vector3D GetColor(Ray r, HittableList world) { HitRecord rec; if (world.Hit(r, 0, double.MaxValue, out rec)) { return(0.5 * new Vector3D(rec.normal.X + 1, rec.normal.Y + 1, rec.normal.Z + 1)); } else { Vector3D unitDirection = r.Direction.UnitVector(); double t = 0.5 * (unitDirection.Y + 1); return((1 - t) * new Vector3D(1, 1, 1) + t * new Vector3D(0.5, 0.7, 1)); } }
private HittableList RandomScene() { HittableList world = new HittableList(); world.Objects.Add(new Sphere(new Vec3(0, -1000, 0), 1000, new Lambertian(new Vec3(0.5, 0.5, 0.5)))); int i = 1; Vec3 differenceCenter = new Vec3(4, 0.2, 0); for (int a = -11; a < 11; a++) { for (int b = -11; b < 11; b++) { double chooseMat = RayTracerUtils.RandomDouble(); Vec3 center = new Vec3(a + 0.9 * RayTracerUtils.RandomDouble(), 0.2, b + 0.9 * RayTracerUtils.RandomDouble()); if ((center - differenceCenter).Length > 0.9) { if (chooseMat < 0.8) { //diffuse Vec3 albedo = Vec3.Random() * Vec3.Random(); world.Objects.Add(new Sphere(center, 0.2, new Lambertian(albedo))); } else if (chooseMat < 0.95) { //metal Vec3 albedo = Vec3.Random(0.5, 1); double fuzz = RayTracerUtils.RandomDouble(0, 0.5); world.Objects.Add(new Sphere(center, 0.2, new Metal(albedo, fuzz))); } else { //glass world.Objects.Add(new Sphere(center, 0.2, new Dielectric(1.5))); } } } } world.Objects.Add(new Sphere(new Vec3(0, 1, 0), 1.0, new Dielectric(1.5))); world.Objects.Add(new Sphere(new Vec3(-4, 1, 0), 1.0, new Lambertian(new Vec3(0.4, 0.2, 0.1)))); world.Objects.Add(new Sphere(new Vec3(4, 1, 0), 1.0, new Metal(new Vec3(0.7, 0.6, 0.5), 0.0))); return(world); }
private Vector3D GetColor(Ray r, HittableList world) { HitRecord rec; if (world.Hit(r, 0.0000001, double.MaxValue, out rec)) { Vector3D target = rec.p + rec.normal + RandomInUnitShpere( ); //击中点加法线向量得到击中点单位球的球心,球心加上 //随机向量得到反射的方向。 return(0.5 * GetColor(new Ray(rec.p, target - rec.p), world)); //每次碰撞都使光线强度减半 } else { Vector3D unitDirection = r.Direction.UnitVector(); double t = 0.5 * (unitDirection.Y + 1); return((1 - t) * new Vector3D(1, 1, 1) + t * new Vector3D(0.5, 0.7, 1)); } }
private void Form1_Load(object sender, EventArgs e) { int nx = 200; int ny = 100; int ns = 100; Bitmap bmp = new Bitmap(nx, ny); Vector3D lookFrom = new Vector3D(3, 3, 2); Vector3D lookAt = new Vector3D(0, 0, -1); double diskToFocus = (lookFrom - lookAt).Length(); double aperture = 2; Camera cam = new Camera(lookFrom, lookAt, new Vector3D(0, 1, 0), 20, (double)nx / (double)ny, aperture, diskToFocus); List <Hittable> list = new List <Hittable>(); list.Add(new Sphere(new Vector3D(0, 0, -1), 0.5, new Lambertian(new Vector3D(0.1, 0.2, 0.5)))); list.Add(new Sphere(new Vector3D(0, -100.5, -1), 100, new Lambertian(new Vector3D(0.8, 0.8, 0)))); list.Add(new Sphere(new Vector3D(1, 0, -1), 0.5, new Metal(new Vector3D(0.8, 0.6, 0.2), 0.0))); list.Add(new Sphere(new Vector3D(-1, 0, -1), 0.5, new Dielectric(1.5))); list.Add(new Sphere(new Vector3D(-1, 0, -1), -0.45, new Dielectric(1.5))); HittableList world = new HittableList(list, list.Count); for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { Vector3D color = new Vector3D(0, 0, 0); for (int s = 0; s < ns; s++) { double u = (double)(i + RandomDouble()) / (double)nx; double v = 1 - (double)(j + RandomDouble()) / (double)ny; Ray ray = cam.GetRay(u, v); color += GetColor(ray, world, 0); //将所有采样点的颜色相加 } color /= ns; //除以采样点的数量得到平均值 color = new Vector3D(Math.Sqrt(color.X), Math.Sqrt(color.Y), Math.Sqrt(color.Z)); //进行伽马校正 int r = (int)(255 * color.X); int g = (int)(255 * color.Y); int b = (int)(255 * color.Z); bmp.SetPixel(i, j, Color.FromArgb(r, g, b)); } } pictureBox1.BackgroundImage = bmp; }
private void Form1_Load(object sender, EventArgs e) { int nx = 200; int ny = 100; int ns = 100; Bitmap bmp = new Bitmap(nx, ny); Vector3D lowerLeft = new Vector3D(-2, 1, -1); Vector3D horizontal = new Vector3D(4, 0, 0); Vector3D vertical = new Vector3D(0, -2, 0); Vector3D origin = new Vector3D(0, 0, 0); List <Hittable> list = new List <Hittable>(); list.Add(new Sphere(new Vector3D(0, 0, -1), 0.5, new Lambertian(new Vector3D(0.8, 0.3, 0.3)))); list.Add(new Sphere(new Vector3D(0, -100.5, -1), 100, new Lambertian(new Vector3D(0.8, 0.8, 0)))); list.Add(new Sphere(new Vector3D(1, 0, -1), 0.5, new Metal(new Vector3D(0.8, 0.6, 0.2), 0.3))); list.Add(new Sphere(new Vector3D(-1, 0, -1), 0.5, new Metal(new Vector3D(0.8, 0.8, 0.8), 1))); HittableList world = new HittableList(list, list.Count); Camera cam = new Camera(); for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { Vector3D color = new Vector3D(0, 0, 0); for (int s = 0; s < ns; s++) { double u = (double)(i + RandomDouble()) / (double)nx; double v = (double)(j + RandomDouble()) / (double)ny; Ray ray = cam.GetRay(u, v); color += GetColor(ray, world, 0); //将所有采样点的颜色相加 } color /= ns; //除以采样点的数量得到平均值 color = new Vector3D(Math.Sqrt(color.X), Math.Sqrt(color.Y), Math.Sqrt(color.Z)); //进行伽马校正 int r = (int)(255 * color.X); int g = (int)(255 * color.Y); int b = (int)(255 * color.Z); bmp.SetPixel(i, j, Color.FromArgb(r, g, b)); } } pictureBox1.BackgroundImage = bmp; }
static void Main() { bool writeDebugInfo = false; IMaterial groundMaterial = new LambertianDiffuse(Color3.FromRgb(255, 212, 216)); IMaterial rightMaterial = new Metal(Color3.FromRgb(220, 220, 220)); IMaterial leftMaterial = new Dielectric(1.5); IMaterial centerMaterial = new LambertianDiffuse(Color3.FromRgb(222, 133, 255)); Renderer renderer = new() { // Define objects in the scene HittableObjects = new HittableList { new Sphere(new Vector3(0, 1, -100.5), 100, groundMaterial), new Sphere(new Vector3(0, 1, 0), 0.5, centerMaterial), new Sphere(new Vector3(-1, 1, 0), 0.5, leftMaterial), new Sphere(new Vector3(1, 1, 0), 0.5, rightMaterial), } }; // Define the camera Camera camera = new(1280, 720) { Origin = new Vector3(0, 0, 0), MultithreadedRendering = true, SamplesPerPixel = 100, MaxBounces = 12 }; // Render RenderResult result = new(); result.frameNumber = 0; result.camera = camera; result.pixels = renderer.RenderScene(camera, out result.frameTime); // Write to disk System.IO.Directory.CreateDirectory("./images/"); Renderer.WriteFrame("./images/image_" + result.frameNumber + ".png", result.pixels, result.camera.ResolutionHeight, result.camera.ResolutionWidth, ImageFormat.Png, writeDebugInfo, result.frameTime, result.camera); } }
static async Task Main(string[] args) { var rayTracer = new RayTracer(1024, 768); rayTracer.Progress += remaining => Console.WriteLine("Remaining scan lines: {0}", remaining); var world = new HittableList(); world.Add(new Sphere(new Vector3(0, 0, -1), 0.5d)); world.Add(new Sphere(new Vector3(0, -50.5d, -1), 50)); var filePath = Path.Combine(Directory.GetCurrentDirectory(), "result.ppm"); using (var fileStream = new FileStream(filePath, FileMode.Create)) { using (var result = await rayTracer.Go <PpmOutput>(world)) { await result.CopyToAsync(fileStream); } } }
private void Form1_Load(object sender, EventArgs e) { int nx = 200; int ny = 100; int ns = 100; //设置采样率抗锯齿,但渲染所需要的时间也变长了,获得随机数使用了大量时间, //有时间的话可以优化一下函数RandomDouble Bitmap bmp = new Bitmap(nx, ny); Vector3D lowerLeft = new Vector3D(-2, 1, -1); Vector3D horizontal = new Vector3D(4, 0, 0); Vector3D vertical = new Vector3D(0, -2, 0); Vector3D origin = new Vector3D(0, 0, 0); List <Hittable> list = new List <Hittable>(); list.Add(new Sphere(new Vector3D(0, 0, -1), 0.5)); list.Add(new Sphere(new Vector3D(0, -100.5, -1), 100)); HittableList world = new HittableList(list, 2); Camera cam = new Camera(); for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { Vector3D color = new Vector3D(0, 0, 0); for (int s = 0; s < ns; s++) { double u = (double)(i + RandomDouble()) / (double)nx; double v = (double)(j + RandomDouble()) / (double)ny; Ray ray = cam.GetRay(u, v); color += GetColor(ray, world); //将所有采样点的颜色相加 } color /= ns; //除以采样点的数量得到平均值 int r = (int)(255 * color.X); int g = (int)(255 * color.Y); int b = (int)(255 * color.Z); bmp.SetPixel(i, j, Color.FromArgb(r, g, b)); } } pictureBox1.BackgroundImage = bmp; }
private void Form1_Load(object sender, EventArgs e) { int nx = 800; //渲染这一章的图需要的时间比较长,可能需要四五个小时,建议在relese模式下进行,也可以改小分辨率和采样率 int ny = 400; int ns = 100; Bitmap bmp = new Bitmap(nx, ny); Vector3D lookFrom = new Vector3D(13, 2, 3); Vector3D lookAt = new Vector3D(0, 0, 0); double diskToFocus = (lookFrom - lookAt).Length(); double aperture = 0; Camera cam = new Camera(lookFrom, lookAt, new Vector3D(0, 1, 0), 20, (double)nx / (double)ny, aperture, 0.7 * diskToFocus); HittableList world = RandomScene(); for (int j = 0; j < ny; j++) { for (int i = 0; i < nx; i++) { Vector3D color = new Vector3D(0, 0, 0); for (int s = 0; s < ns; s++) { double u = (double)(i + RandomDouble()) / (double)nx; double v = 1 - (double)(j + RandomDouble()) / (double)ny; Ray ray = cam.GetRay(u, v); color += GetColor(ray, world, 0); //将所有采样点的颜色相加 } color /= ns; //除以采样点的数量得到平均值 color = new Vector3D(Math.Sqrt(color.X), Math.Sqrt(color.Y), Math.Sqrt(color.Z)); //进行伽马校正 int r = (int)(255 * color.X); int g = (int)(255 * color.Y); int b = (int)(255 * color.Z); bmp.SetPixel(i, j, Color.FromArgb(r, g, b)); } } pictureBox1.BackgroundImage = bmp; }
private string RenderScene() { pboxPreview.Image = null; int width = int.Parse(txtbxWidth.Text); int height = int.Parse(txtbxHeight.Text); int samples = int.Parse(txtbxSamples.Text); int chunkSize = int.Parse(txtbxChunkSize.Text); string fname = "Test"; if (txtbxFileName.Text != "") { fname = txtbxFileName.Text; } List <Hittable> activeHittables = new List <Hittable>(); foreach (string hittableName in listbxHittables.CheckedItems) { activeHittables.Add(GetHittable(hittableName)); } XmlSerializer serializer = new XmlSerializer(typeof(PTObject[])); using (XmlWriter writer = XmlWriter.Create("autosave.xml")) { List <PTObject> temp = new List <PTObject>(); foreach (PTObject material in materials.Values) { temp.Add(material); } foreach (PTObject hittable in hittables.Values) { temp.Add(hittable); } serializer.Serialize(writer, temp.ToArray()); } PTObject scene = new HittableList(activeHittables.ToArray()); Vec3 lookFrom = new Vec3(2f, 1.2f, -2f); Vec3 lookAt = new Vec3(-2f, 0f, 2f); System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); sw.Start(); if (!PTObject.RenderSceneChunked( width, height, samples, chunkSize, fname, lookFrom, lookAt, new Vec3(0f, 1f, 0f), (float)Math.PI / 3f, (float)width / height, 0f, (lookFrom - lookAt).Length, scene)) { // all objects are invalidated if rendering fails. foreach (string textureName in textures.Keys) { GetTexture(textureName).Destroy(); } textures.Clear(); foreach (string materialName in materials.Keys) { GetMaterial(materialName).Destroy(); } materials.Clear(); foreach (string hittableName in hittables.Keys) { GetHittable(hittableName).Destroy(); } hittables.Clear(); scene.Destroy(); using (XmlReader reader = XmlReader.Create("autosave.xml")) { PTObject[] autosaved = serializer.Deserialize(reader) as PTObject[]; for (int i = 0; i < autosaved.Length; i++) { switch (autosaved[i].Kind) { case PTObject.PTObjectKind.Hittable: hittables.Add(i.ToString(), autosaved[i] as Hittable); break; case PTObject.PTObjectKind.Material: materials.Add(i.ToString(), autosaved[i] as Material); break; case PTObject.PTObjectKind.Texture: textures.Add(i.ToString(), autosaved[i] as Texture); break; } } } return("Rendering failed."); } sw.Stop(); scene.Destroy(); Bitmap image = ReadBitmapFromPPM(Directory.GetCurrentDirectory() + "\\" + fname + ".ppm"); pboxPreview.Image = image; return($"Last Render Took: {sw.ElapsedMilliseconds} ms"); }
public void Output(string path) { Stopwatch sw = new Stopwatch(); sw.Start(); using (StreamWriter writer = new StreamWriter(path)) { writer.WriteLine("P3"); writer.WriteLine(IMAGE_WIDTH + " " + IMAGE_HEIGHT); writer.WriteLine("255"); double aspectRatio = IMAGE_WIDTH / IMAGE_HEIGHT; Vec3 lookFrom = new Vec3(13, 2, 3); Vec3 lookAt = new Vec3(0, 0, 0); Vec3 vUp = new Vec3(0, 1, 0); double distanceToFocus = 10.0; double aperture = 0.1; Camera cam = new Camera(lookFrom, lookAt, vUp, 20, aspectRatio, aperture, distanceToFocus); HittableList world = RandomScene(); Vec3[,] colors = new Vec3[IMAGE_HEIGHT, IMAGE_WIDTH]; int linesRemaining = IMAGE_HEIGHT; Parallel.For(0, IMAGE_HEIGHT, j => { // We handle each pixel of the line on a different thread to speed up the process Parallel.For(0, IMAGE_WIDTH, i => { Vec3 color = new Vec3(); for (int s = 0; s < SAMPLES_PER_PIXEL; ++s) { double u = (i + RayTracerUtils.RandomDouble()) / IMAGE_WIDTH; double v = (j + RayTracerUtils.RandomDouble()) / IMAGE_HEIGHT; Ray r = cam.GetRay(u, v); color += RayColor(r, world, MAX_DEPTH); } lock (colors) { colors[j, i] = color; } }); double percentage = 100.0 - (double)--linesRemaining / IMAGE_HEIGHT * 100.0; Progress(this, new ProgressArgs(percentage)); }); // Output the colors in the file for (int j = IMAGE_HEIGHT - 1; j >= 0; --j) { for (int i = 0; i < IMAGE_WIDTH; ++i) { colors[j, i].WriteColor(writer, SAMPLES_PER_PIXEL); } } } sw.Stop(); Console.WriteLine(); Console.WriteLine($"Time: {sw.Elapsed.Minutes}:{sw.Elapsed.Seconds:00}"); }
public RenderEngine(Camera camera, HittableList hittables) { this.Camera = camera; this.Hittables = hittables; }