static void Main() { const int width = 640; // image width const int height = 480; // image height const float w2 = width / 2.0f; const float h2 = height / 2.0f; const float fov = 3.1415f / 3.0f; // field of view angle var dirz = ( float )(-h2 / Math.Tan(fov / 2f)); var framebuffer = new Vec3f[width * height]; // Load background. var bmp = ( Bitmap )Bitmap.FromFile("envmap.jpg"); int pixelSize = 4; switch (bmp.PixelFormat) { case PixelFormat.Format24bppRgb: pixelSize = 3; break; } BitmapData bmpData = null; try { bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); var kx = .5f * 3.1415f / Math.Atan(w2 / -dirz); var ky = .4f * 3.1415f * 2f / fov; var x0 = bmp.Width / 4 - ( int )(kx * width / 2); var y0 = bmp.Height / 2 - ( int )(ky * height / 2); for (var y = 0; y < height; ++y) { unsafe { var row = ( byte * )bmpData.Scan0 + ( int )(y0 + ky * y) % bmp.Height * bmpData.Stride; for (var x = 0; x < width; ++x) { var rx = ( int )(x0 + kx * x) * pixelSize % bmpData.Stride; var r = row[rx + 2]; var g = row[rx + 1]; var b = row[rx + 0]; var c = new Vec3f(r, g, b) / 255f; var i = y * width + x; framebuffer[i] = c; } } } } finally { if (bmpData != null) { bmp.UnlockBits(bmpData); } } pixelSize = 4; // The camera is placed to (0,0,3) and it looks along the -z axis. var vcam = new Vec3f(0, 0, 3); // One light is placed to (10,10,10). var vlight = new Vec3f(10, 10, 10); var hit = new Vec3f(); // Actual rendering loop. for (var j = 0; j < height; j++) { for (var i = 0; i < width; i++) { // This flips the image at the same time. var dirx = (i + 0.5f) - w2; var diry = -(j + 0.5f) + h2; var vdir = new Vec3f(dirx, diry, dirz).normalize(); if (sphere_trace(vcam, vdir, ref hit)) { var noiseLevel = (SphereRadius - hit.norm()) / NoiseAmplitude; var lightDir = (vlight - hit).normalize(); var lightIntensity = Math.Max(0.4f, lightDir * distance_field_normal(hit)); framebuffer[i + j * width] = palette_fire((-0.2f + noiseLevel) * 2) * lightIntensity; } } } // Save the framebuffer as image. bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb); try { bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); for (var y = 0; y < height; ++y) { unsafe { var targetRow = ( byte * )bmpData.Scan0 + y * bmpData.Stride; for (var x = 0; x < width; ++x) { var i = y * width + x; var c = framebuffer[i]; var max = Math.Max(c[0], Math.Max(c[1], c[2])); if (max > 1) { c = c / max; } targetRow[x * pixelSize + 0] = ( byte )(255 * Math.Max(0f, Math.Min(1f, c[2]))); targetRow[x * pixelSize + 1] = ( byte )(255 * Math.Max(0f, Math.Min(1f, c[1]))); targetRow[x * pixelSize + 2] = ( byte )(255 * Math.Max(0f, Math.Min(1f, c[0]))); targetRow[x * pixelSize + 3] = 255; } } } } finally { if (bmpData != null) { bmp.UnlockBits(bmpData); } } bmp.Save("out.jpg", ImageFormat.Jpeg); }
static void test_func_call() { test_name("func_call"); // TVector Vec3f v31 = new Vec3f(1.0f, 2.0f, 3.0f); put("Vec3f: square ", "14.0", v31.square()); put("Vec3f: norm ", "3.7416574", v31.norm()); Vec3f v3u = v31; v3u.unitize(); Vec3f v3r = new Vec3f(0.2672612f, 0.5345225f, 0.8017837f); put2("Vec3f: unitize", edit_vector(v3r), edit_vector(v3u)); // TQuaternion System.Console.WriteLine(""); float pi = 3.1415927f; float rad90 = pi / 2; float rad45 = pi / 4; float cos45 = 0.7071069f; float sqrt3 = 1.7320508f; float sqrt3div3 = sqrt3 / 3; Quaternionf q1 = new Quaternionf(cos45, sqrt3div3, sqrt3div3, sqrt3div3); Quaternionf q2 = new Quaternionf(q1.W(), q1.X(), q1.Y(), q1.Z()); Vec3f qv1 = new Vec3f(sqrt3div3, sqrt3div3, sqrt3div3); put2("Quaternionf: W(),X(),Y(),Z()", edit_quaternion(q1), edit_quaternion(q2)); put2("Quaternionf: V", edit_vector(qv1), edit_vector(q1.V())); put2("Quaternionf: Axis", edit_vector(qv1), edit_vector(q1.Axis())); put("Quaternionf: Theta", rad90.ToString(), q1.Theta()); System.Console.WriteLine(""); float half = pi / (2 * sqrt3); Vec3f qv2 = new Vec3f(half, half, half); put2("Quaternionf: RotationHalf", edit_vector(qv2), edit_vector(q1.RotationHalf())); put2("Quaternionf: Rotation ", edit_vector(qv2), edit_vector(q1.Rotation())); float angle = rad90; float d = sqrt3; float s = (float)Math.Sin(angle / 2) / d; Quaternionf qr = new Quaternionf((float)Math.Cos(angle / 2), s, s, s); Quaternionf q3 = Quaternionf.Rot(angle, new Vec3f(1f, 1f, 1f)); put2("Quaternionf: Rot", edit_quaternion(qr), edit_quaternion(q3)); float c1 = (float)Math.Cos(angle / 2); float s1 = (float)Math.Sin(angle / 2); Quaternionf qs = new Quaternionf(c1, s1, 0f, 0f); Quaternionf q4 = Quaternionf.Rot(angle, (sbyte)'x'); put2("Quaternionf: Rot", edit_quaternion(qs), edit_quaternion(q4)); Vec3f qv3 = new Vec3f(1f, 1f, 1f); float c2 = (float)Math.Cos(sqrt3 / 2); float s2 = (float)Math.Sin(sqrt3 / 2); Vec3f qv4 = (s2 / sqrt3) * qv3; Quaternionf qt = new Quaternionf(c2, qv4[0], qv4[1], qv4[2]); Quaternionf q5 = Quaternionf.Rot(qv3); put2("Quaternionf: Rot", edit_quaternion(qt), edit_quaternion(q5)); Quaternionf qc1 = new Quaternionf(cos45, sqrt3div3, sqrt3div3, sqrt3div3); Quaternionf qc2 = new Quaternionf(cos45, sqrt3div3, sqrt3div3, sqrt3div3); Quaternionf qc = new Quaternionf(cos45, -sqrt3div3, -sqrt3div3, -sqrt3div3); qc1.Conjugate(); put2("Quaternionf: Conjugate", edit_quaternion(qc), edit_quaternion(qc1)); put2("Quaternionf: Conjugated", edit_quaternion(qc), edit_quaternion(qc2.Conjugated())); float qf1 = (float)(cos45 * cos45 + 3 * sqrt3div3 * sqrt3div3); Quaternionf qe = qc2.Conjugated() / qf1; put2("Quaternionf: Inv", edit_quaternion(qe), edit_quaternion(qc2.Inv())); // TPose System.Console.WriteLine(""); Posef p1 = new Posef(cos45, sqrt3, sqrt3, sqrt3, 1f, 2f, 3f); Posef p2 = new Posef(p1.W(), p1.X(), p1.Y(), p1.Z(), p1.Px(), p1.Py(), p1.Pz()); Vec3f pv31 = new Vec3f(1f, 2f, 3f); Quaternionf pq1 = new Quaternionf(cos45, sqrt3, sqrt3, sqrt3); Quaternionf pq2 = new Quaternionf(); Vec3f pv32 = new Vec3f(); Posef pp1 = new Posef(pq2.w, pq2.x, pq2.y, pq2.z, pv32.x, pv32.y, pv32.z); Posef pp2 = new Posef(pq2.w, pq2.x, pq2.y, pq2.z, 1f, 2f, 3f); put2("Posef: W(),X(),Y(),Z(),Px(),Py(),Pz()", edit_pose(p1), edit_pose(p2)); put2("Posef: Pos()", edit_vector(pv31), edit_vector(p1.Pos())); put2("Posef: Ori()", edit_quaternion(pq1), edit_quaternion(p1.Ori())); put2("Posef: Unit ", edit_pose(pp1), edit_pose(Posef.Unit())); put2("Posef: Trn ", edit_pose(pp2), edit_pose(Posef.Trn(1f, 2f, 3f))); put2("Posef: Trn ", edit_pose(pp2), edit_pose(Posef.Trn(pv31))); }
// This function defines the implicit surface we render. static float signed_distance(Vec3f p) { var displacement = -fractal_brownian_motion(p * 3.4f) * NoiseAmplitude; return(p.norm() - (SphereRadius + displacement)); }