private static void RenderFunc(int y, int w, int h, int nb_samples, Vector3 eye, Vector3 gaze, Vector3 cx, Vector3 cy, Vector3[] Ls, RNG rng) { // pixel row for (int x = 0; x < w; ++x) { // pixel column for (int sy = 0, i = (h - 1 - y) * w + x; sy < 2; ++sy) { // 2 subpixel row for (int sx = 0; sx < 2; ++sx) { // 2 subpixel column Vector3 L = new Vector3(); for (int s = 0; s < nb_samples; ++s) { // samples per subpixel double u1 = 2.0 * rng.UniformFloat(); double u2 = 2.0 * rng.UniformFloat(); double dx = u1 < 1 ? Math.Sqrt(u1) - 1.0 : 1.0 - Math.Sqrt(2.0 - u1); double dy = u2 < 1 ? Math.Sqrt(u2) - 1.0 : 1.0 - Math.Sqrt(2.0 - u2); Vector3 d = cx * (((sx + 0.5 + dx) / 2 + x) / w - 0.5) + cy * (((sy + 0.5 + dy) / 2 + y) / h - 0.5) + gaze; L += Radiance(new Ray(eye + d * 130, d.Normalize(), Sphere.EPSILON_SPHERE), rng) * (1.0 / nb_samples); } Ls[i] += 0.25 * Vector3.Clamp(L); } } } }
public static Vector3 Radiance(Ray ray, RNG rng) { Ray r = ray; Vector3 L = new Vector3(); Vector3 F = new Vector3(1.0); while (true) { if (!Intersect(r, out int id)) { return(L); } Sphere shape = spheres[id]; Vector3 p = r.Eval(r.tmax); Vector3 n = (p - shape.p).Normalize(); L += F * shape.e; F *= shape.f; // Russian roulette if (r.depth > 4) { double continue_probability = shape.f.Max(); if (rng.UniformFloat() >= continue_probability) { return(L); } F /= continue_probability; } // Next path segment switch (shape.reflection_t) { case Sphere.Reflection_t.SPECULAR: { Vector3 d = Specular.IdealSpecularReflect(r.d, n); r = new Ray(p, d, Sphere.EPSILON_SPHERE, double.PositiveInfinity, r.depth + 1); break; } case Sphere.Reflection_t.REFRACTIVE: { Vector3 d = Specular.IdealSpecularTransmit(r.d, n, REFRACTIVE_INDEX_OUT, REFRACTIVE_INDEX_IN, out double pr, rng); F *= pr; r = new Ray(p, d, Sphere.EPSILON_SPHERE, double.PositiveInfinity, r.depth + 1); break; } default: { Vector3 w = n.Dot(r.d) < 0 ? n : -n; Vector3 u = ((Math.Abs(w.x) > 0.1 ? new Vector3(0.0, 1.0, 0.0) : new Vector3(1.0, 0.0, 0.0)).Cross(w)).Normalize(); Vector3 v = w.Cross(u); Vector3 sample_d = Sampling.CosineWeightedSampleOnHemisphere(rng.UniformFloat(), rng.UniformFloat()); Vector3 d = (sample_d.x * u + sample_d.y * v + sample_d.z * w).Normalize(); r = new Ray(p, d, Sphere.EPSILON_SPHERE, double.PositiveInfinity, r.depth + 1); break; } } } }
static void Main(string[] args) { RNG rng = new RNG(); int nb_samples = (args.Length > 0) ? int.Parse(args[0]) / 4 : 1; const int w = 1024; const int h = 768; Vector3 eye = new Vector3(50, 52, 295.6); Vector3 gaze = new Vector3(0, -0.042612, -1).Normalize(); const double fov = 0.5135; Vector3 cx = new Vector3(w * fov / h, 0.0, 0.0); Vector3 cy = (cx.Cross(gaze)).Normalize() * fov; Vector3[] Ls = new Vector3[w * h]; for (int i = 0; i < w * h; ++i) { Ls[i] = new Vector3(); } for (int y = 0; y < h; ++y) { // pixel row Console.Write("\rRendering ({0} spp) {1:0.00}%", nb_samples * 4, 100.0 * y / (h - 1)); for (int x = 0; x < w; ++x) { // pixel column for (int sy = 0, i = (h - 1 - y) * w + x; sy < 2; ++sy) { // 2 subpixel row for (int sx = 0; sx < 2; ++sx) { // 2 subpixel column Vector3 L = new Vector3(); for (int s = 0; s < nb_samples; ++s) { // samples per subpixel double u1 = 2.0 * rng.UniformFloat(); double u2 = 2.0 * rng.UniformFloat(); double dx = u1 < 1 ? Math.Sqrt(u1) - 1.0 : 1.0 - Math.Sqrt(2.0 - u1); double dy = u2 < 1 ? Math.Sqrt(u2) - 1.0 : 1.0 - Math.Sqrt(2.0 - u2); Vector3 d = cx * (((sx + 0.5 + dx) / 2 + x) / w - 0.5) + cy * (((sy + 0.5 + dy) / 2 + y) / h - 0.5) + gaze; L += Radiance(new Ray(eye + d * 130, d.Normalize(), Sphere.EPSILON_SPHERE), rng) * (1.0 / nb_samples); } Ls[i] += 0.25 * Vector3.Clamp(L); } } } } ImageIO.WritePPM(w, h, Ls); }
public static Vector3 IdealSpecularTransmit(Vector3 d, Vector3 n, double n_out, double n_in, out double pr, RNG rng) { Vector3 d_Re = IdealSpecularReflect(d, n); bool out_to_in = n.Dot(d) < 0; Vector3 nl = out_to_in ? n : -n; double nn = out_to_in ? n_out / n_in : n_in / n_out; double cos_theta = d.Dot(nl); double cos2_phi = 1.0 - nn * nn * (1.0 - cos_theta * cos_theta); // Total Internal Reflection if (cos2_phi < 0) { pr = 1.0; return(d_Re); } Vector3 d_Tr = (nn * d - nl * (nn * cos_theta + Math.Sqrt(cos2_phi))).Normalize(); double c = 1.0 - (out_to_in ? -cos_theta : d_Tr.Dot(n)); double Re = SchlickReflectance(n_out, n_in, c); double p_Re = 0.25 + 0.5 * Re; if (rng.UniformFloat() < p_Re) { pr = (Re / p_Re); return(d_Re); } else { double Tr = 1.0 - Re; double p_Tr = 1.0 - p_Re; pr = (Tr / p_Tr); return(d_Tr); } }