92
|
1 //import std.stdio, std.math, std.string;
|
|
2 //import tools.base;
|
|
3
|
|
4 int atoi(char[] s) {
|
|
5 int i, fac=1;
|
|
6 bool neg = (s.length) && (s[0] == '-');
|
|
7 char[] a = neg ? s[1..$] : s;
|
|
8 foreach_reverse(c; a) {
|
|
9 i += (c-'0') * fac;
|
|
10 fac *= 10;
|
|
11 }
|
|
12 return !neg ? i : -i;
|
|
13 }
|
|
14
|
|
15 pragma(LLVM_internal, "intrinsic", "llvm.sqrt.f64")
|
|
16 double sqrt(double val);
|
|
17
|
|
18 double delta;
|
|
19 static this() { delta=sqrt(real.epsilon); }
|
|
20
|
|
21 struct Vec {
|
|
22 double x, y, z;
|
|
23 Vec opAdd(ref Vec other) { return Vec(x+other.x, y+other.y, z+other.z); }
|
|
24 Vec opSub(ref Vec other) { return Vec(x-other.x, y-other.y, z-other.z); }
|
|
25 Vec opMul(double a) { return Vec(x*a, y*a, z*a); }
|
|
26 double dot(ref Vec other) { return x*other.x+y*other.y+z*other.z; }
|
|
27 Vec unitise() { return opMul(1.0/sqrt(dot(*this))); }
|
|
28 }
|
|
29
|
|
30 struct Pair(T, U) { T first; U second; }
|
|
31 typedef Pair!(double, Vec) Hit;
|
|
32
|
|
33 struct Ray { Vec orig, dir; }
|
|
34
|
|
35 class Scene {
|
|
36 //abstract void intersect(ref Hit, ref Ray);
|
|
37 void intersect(ref Hit, ref Ray) {}
|
|
38 }
|
|
39
|
|
40 class Sphere : Scene {
|
|
41 Vec center;
|
|
42 double radius;
|
|
43 //mixin This!("center, radius");
|
|
44 this(ref Vec c, double r)
|
|
45 {
|
|
46 center = c;
|
|
47 radius = r;
|
|
48 }
|
|
49 double ray_sphere(ref Ray ray) {
|
|
50 auto v = center - ray.orig, b = v.dot(ray.dir), disc=b*b - v.dot(v) + radius*radius;
|
|
51 if (disc < 0) return double.infinity;
|
|
52 auto d = sqrt(disc), t2 = b + d;
|
|
53 if (t2 < 0) return double.infinity;
|
|
54 auto t1 = b - d;
|
|
55 return (t1 > 0 ? t1 : t2);
|
|
56 }
|
|
57 void intersect(ref Hit hit, ref Ray ray) {
|
|
58 auto lambda = ray_sphere(ray);
|
|
59 if (lambda < hit.first)
|
|
60 hit = Hit(lambda, (ray.orig + lambda*ray.dir - center).unitise);
|
|
61 }
|
|
62 }
|
|
63
|
|
64 class Group : Scene {
|
|
65 Sphere bound;
|
|
66 Scene[] children;
|
|
67 //mixin This!("bound, children");
|
|
68 this (Sphere s, Scene[] c)
|
|
69 {
|
|
70 bound = s;
|
|
71 children = c;
|
|
72 }
|
|
73 void intersect(ref Hit hit, ref Ray ray) {
|
|
74 auto l = bound.ray_sphere(ray);
|
|
75 if (l < hit.first) foreach (child; children) child.intersect(hit, ray);
|
|
76 }
|
|
77 }
|
|
78
|
|
79 double ray_trace(ref Vec light, ref Ray ray, Scene s) {
|
|
80 auto hit=Hit(double.infinity, Vec(0, 0, 0));
|
|
81 s.intersect(hit, ray);
|
|
82 if (hit.first == double.infinity) return 0.0;
|
|
83 auto g = hit.second.dot(light);
|
|
84 if (g >= 0) return 0.0;
|
|
85 auto p = ray.orig + ray.dir*hit.first + hit.second*delta;
|
|
86 auto hit2=Hit(double.infinity, Vec(0, 0, 0));
|
|
87 s.intersect(hit2, Ray(p, light*-1.0));
|
|
88 return (hit2.first < double.infinity ? 0 : -g);
|
|
89 }
|
|
90
|
|
91 Scene create(int level, ref Vec c, double r) {
|
|
92 auto s = new Sphere(c, r);
|
|
93 if (level == 1) return s;
|
|
94 Scene[] children=[s];
|
|
95 double rn = 3*r/sqrt(12.0);
|
|
96 for (int dz=-1; dz<=1; dz+=2)
|
|
97 for (int dx=-1; dx<=1; dx+=2)
|
|
98 children~=create(level-1, c + Vec(dx, 1, dz)*rn, r/2);
|
|
99 return new Group(new Sphere(c, 3*r), children);
|
|
100 }
|
|
101
|
|
102 void main(string[] args) {
|
|
103 int level = (args.length==3 ? args[1].atoi() : 9),
|
|
104 n = (args.length==3 ? args[2].atoi() : 512), ss = 4;
|
|
105 auto light = Vec(-1, -3, 2).unitise();
|
|
106 auto s=create(level, Vec(0, -1, 0), 1);
|
|
107 printf("P5\n%d %d\n255", n, n);
|
|
108 for (int y=n-1; y>=0; --y)
|
|
109 for (int x=0; x<n; ++x) {
|
|
110 double g=0;
|
|
111 for (int d=0; d<ss*ss; ++d) {
|
|
112 auto dir=Vec(x+(d%ss)*1.0/ss-n/2.0, y+(d/ss)*1.0/ss-n/2.0, n).unitise();
|
|
113 g += ray_trace(light, Ray(Vec(0, 0, -4), dir), s);
|
|
114 }
|
|
115 printf("%c", cast(ubyte)(0.5 + 255.0 * g / (ss*ss)));
|
|
116 }
|
|
117 }
|