Created
January 7, 2012 10:10
-
-
Save miura1729/1574337 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # AO render benchmark | |
| # Original program (C) Syoyo Fujita in Javascript (and other languages) | |
| # http://lucille.atso-net.jp/blog/?p=642 | |
| # http://lucille.atso-net.jp/blog/?p=711 | |
| # Ruby(yarv2llvm) version by Hideki Miura | |
| # | |
| IMAGE_WIDTH = 256 | |
| IMAGE_HEIGHT = 256 | |
| NSUBSAMPLES = 2 | |
| NAO_SAMPLES = 8 | |
| =begin | |
| def rand | |
| 0.5 | |
| end | |
| =end | |
| # Vec | |
| # [] -> { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Vec>} | |
| # self { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Vec>} | |
| # block { BOXED NilClass} | |
| # | |
| class Vec | |
| # initialize | |
| # [{ UNBOXED Float}, { UNBOXED Float}, { UNBOXED Float}] -> { BOXED Vec} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def initialize(x, y, z) | |
| @x = x | |
| @y = y | |
| @z = z | |
| end | |
| # x= | |
| # [{ UNBOXED Float}] -> { UNBOXED Float} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def x=(v); @x = v; end | |
| # y= | |
| # [{ UNBOXED Float}] -> { UNBOXED Float} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def y=(v); @y = v; end | |
| # z= | |
| # [{ UNBOXED Float}] -> { UNBOXED Float} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def z=(v); @z = v; end | |
| # x | |
| # [] -> { UNBOXED Float} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def x; @x; end | |
| # y | |
| # [] -> { UNBOXED Float} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def y; @y; end | |
| # z | |
| # [] -> { UNBOXED Float} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def z; @z; end | |
| # vadd | |
| def vadd(b) | |
| Vec.new(@x + b.x, @y + b.y, @z + b.z) | |
| end | |
| # vsub | |
| # [{ BOXED Vec}] -> { BOXED Vec} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def vsub(b) | |
| # can allocate on the stack | |
| Vec.new(@x - b.x, @y - b.y, @z - b.z) | |
| end | |
| # vcross | |
| # [{ BOXED Vec}] -> { BOXED Vec} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def vcross(b) | |
| Vec.new(@y * b.z - @z * b.y, | |
| @z * b.x - @x * b.z, | |
| @x * b.y - @y * b.x) | |
| end | |
| # vdot | |
| # [{ BOXED Vec}] -> { UNBOXED Float} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def vdot(b) | |
| r = @x * b.x + @y * b.y + @z * b.z | |
| r | |
| end | |
| # vlength | |
| # [] -> { UNBOXED Float} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def vlength | |
| Math.sqrt(@x * @x + @y * @y + @z * @z) | |
| end | |
| # vnormalize | |
| # [] -> { BOXED Vec} | |
| # self { BOXED Vec} | |
| # block { BOXED NilClass} | |
| # | |
| def vnormalize | |
| len = vlength | |
| v = Vec.new(@x, @y, @z) | |
| if len > 1.0e-17 then | |
| v.x = v.x / len | |
| v.y = v.y / len | |
| v.z = v.z / len | |
| end | |
| v | |
| end | |
| end | |
| # Sphere | |
| # [] -> { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Sphere>} | |
| # self { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Sphere>} | |
| # block { BOXED NilClass} | |
| # | |
| class Sphere | |
| # initialize | |
| # [{ BOXED Vec}, { UNBOXED Float}] -> { BOXED Sphere} | |
| # self { BOXED Sphere} | |
| # block { BOXED NilClass} | |
| # | |
| def initialize(center, radius) | |
| @center = center | |
| @radius = radius | |
| end | |
| # center | |
| def center; @center; end | |
| # radius | |
| def radius; @radius; end | |
| # intersect | |
| # [{ BOXED Ray}, { BOXED Isect}] -> { BOXED Vec} | |
| # self { BOXED Sphere} | |
| # block { BOXED NilClass} | |
| # | |
| def intersect(ray, isect) | |
| rs = ray.org.vsub(@center) | |
| b = rs.vdot(ray.dir) | |
| c = rs.vdot(rs) - (@radius * @radius) | |
| d = b * b - c | |
| if d > 0.0 then | |
| t = - b - Math.sqrt(d) | |
| if t > 0.0 and t < isect.t then | |
| isect.t = t | |
| isect.hit = true | |
| isect.pl = Vec.new(ray.org.x + ray.dir.x * t, | |
| ray.org.y + ray.dir.y * t, | |
| ray.org.z + ray.dir.z * t) | |
| n = isect.pl.vsub(@center) | |
| isect.n = n.vnormalize | |
| end | |
| end | |
| end | |
| end | |
| # Plane | |
| # [] -> { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Plane>} | |
| # self { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Plane>} | |
| # block { BOXED NilClass} | |
| # | |
| class Plane | |
| # initialize | |
| # [{ BOXED Vec}, { BOXED Vec}] -> { BOXED Plane} | |
| # self { BOXED Plane} | |
| # block { BOXED NilClass} | |
| # | |
| def initialize(p, n) | |
| @p = p | |
| @n = n | |
| end | |
| # intersect | |
| # [{ BOXED Ray}, { BOXED Isect}] -> { BOXED Vec} | |
| # self { BOXED Plane} | |
| # block { BOXED NilClass} | |
| # | |
| def intersect(ray, isect) | |
| d = [email protected](@n) | |
| v = ray.dir.vdot(@n) | |
| v0 = v | |
| if v < 0.0 then | |
| v0 = -v | |
| end | |
| if v0 < 1.0e-17 then | |
| return | |
| end | |
| t = -(ray.org.vdot(@n) + d) / v | |
| if t > 0.0 and t < isect.t then | |
| isect.hit = true | |
| isect.t = t | |
| isect.n = @n | |
| # can allocate on the stack | |
| isect.pl = Vec.new(ray.org.x + t * ray.dir.x, | |
| ray.org.y + t * ray.dir.y, | |
| ray.org.z + t * ray.dir.z) | |
| end | |
| end | |
| end | |
| # Ray | |
| # [] -> { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Ray>} | |
| # self { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Ray>} | |
| # block { BOXED NilClass} | |
| # | |
| class Ray | |
| # initialize | |
| # [{ BOXED Vec}, { BOXED Vec}] -> { BOXED Ray} | |
| # self { BOXED Ray} | |
| # block { BOXED NilClass} | |
| # | |
| def initialize(org, dir) | |
| @org = org | |
| @dir = dir | |
| end | |
| # org | |
| # [] -> { BOXED Vec} | |
| # self { BOXED Ray} | |
| # block { BOXED NilClass} | |
| # | |
| def org; @org; end | |
| # org= | |
| def org=(v); @org = v; end | |
| # dir | |
| # [] -> { BOXED Vec} | |
| # self { BOXED Ray} | |
| # block { BOXED NilClass} | |
| # | |
| def dir; @dir; end | |
| # dir= | |
| def dir=(v); @dir = v; end | |
| end | |
| # Isect | |
| # [] -> { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Isect>} | |
| # self { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Isect>} | |
| # block { BOXED NilClass} | |
| # | |
| class Isect | |
| # initialize | |
| # [] -> { BOXED Isect} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def initialize | |
| @t = 10000000.0 | |
| @hit = false | |
| @pl = Vec.new(0.0, 0.0, 0.0) | |
| @n = Vec.new(0.0, 0.0, 0.0) | |
| end | |
| # t | |
| # [] -> { UNBOXED Float} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def t; @t; end | |
| # t= | |
| # [{ UNBOXED Float}] -> { UNBOXED Float} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def t=(v); @t = v; end | |
| # hit | |
| # [] -> { BOXED TrueClass} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def hit; @hit; end | |
| # hit= | |
| # [{ BOXED TrueClass}] -> { BOXED TrueClass} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def hit=(v); @hit = v; end | |
| # pl | |
| # [] -> { BOXED Vec} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def pl; @pl; end | |
| # pl= | |
| # [{ BOXED Vec}] -> { BOXED Vec} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def pl=(v); @pl = v; end | |
| # n | |
| # [] -> { BOXED Vec} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def n; @n; end | |
| # n= | |
| # [{ BOXED Vec}] -> { BOXED Vec} | |
| # self { BOXED Isect} | |
| # block { BOXED NilClass} | |
| # | |
| def n=(v); @n = v; end | |
| end | |
| # clamp | |
| # [{ UNBOXED Float}] -> { UNBOXED Fixnum} | |
| # self { BOXED Object} | |
| # block { BOXED NilClass} | |
| # | |
| def clamp(f) | |
| i = f * 255.5 | |
| if i > 255.0 then | |
| i = 255.0 | |
| end | |
| if i < 0.0 then | |
| i = 0.0 | |
| end | |
| i.to_i | |
| end | |
| # otherBasis | |
| # [{ BOXED Array ({nil=>[{ BOXED Vec}], [2]=>[{ BOXED Vec}]})}, { BOXED Vec}] -> { BOXED Vec} | |
| # self { BOXED Object} | |
| # block { BOXED NilClass} | |
| # | |
| def otherBasis(basis, n) | |
| basis[2] = Vec.new(n.x, n.y, n.z) | |
| basis[1] = Vec.new(0.0, 0.0, 0.0) | |
| if n.x < 0.6 and n.x > -0.6 then | |
| basis[1].x = 1.0 | |
| elsif n.y < 0.6 and n.y > -0.6 then | |
| basis[1].y = 1.0 | |
| elsif n.z < 0.6 and n.z > -0.6 then | |
| basis[1].z = 1.0 | |
| else | |
| basis[1].x = 1.0 | |
| end | |
| basis[0] = basis[1].vcross(basis[2]) | |
| basis[0] = basis[0].vnormalize | |
| basis[1] = basis[2].vcross(basis[0]) | |
| basis[1] = basis[1].vnormalize | |
| # top | |
| # [] -> { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Object>} | |
| # self { BOXED NilClass} | |
| # block { BOXED NilClass} | |
| # | |
| end | |
| # Scene | |
| # [] -> { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Scene>} | |
| # self { BOXED #<Ytljit::ClassClassWrapper type=#<Class:Scene>} | |
| # block { BOXED NilClass} | |
| # | |
| class Scene | |
| # initialize | |
| # [] -> { BOXED Scene} | |
| # self { BOXED Scene} | |
| # block { BOXED NilClass} | |
| # | |
| def initialize | |
| @spheres = Array.new | |
| @spheres[0] = Sphere.new(Vec.new(-2.0, 0.0, -3.5), 0.5) | |
| @spheres[1] = Sphere.new(Vec.new(-0.5, 0.0, -3.0), 0.5) | |
| @spheres[2] = Sphere.new(Vec.new(1.0, 0.0, -2.2), 0.5) | |
| @plane = Plane.new(Vec.new(0.0, -0.5, 0.0), Vec.new(0.0, 1.0, 0.0)) | |
| end | |
| # ambient_occlusion | |
| # [{ BOXED Isect}] -> { BOXED Vec} | |
| # self { BOXED Object} | |
| # block { BOXED NilClass} | |
| # | |
| def ambient_occlusion(isect) | |
| # can allocate on the stack | |
| basis = Array.new(3) | |
| otherBasis(basis, isect.n) | |
| ntheta = NAO_SAMPLES | |
| nphi = NAO_SAMPLES | |
| eps = 0.0001 | |
| occlusion = 0.0 | |
| # can allocate on the stack | |
| p0 = Vec.new(isect.pl.x + eps * isect.n.x, | |
| isect.pl.y + eps * isect.n.y, | |
| isect.pl.z + eps * isect.n.z) | |
| nphi.times do |j| | |
| ntheta.times do |i| | |
| r = rand | |
| phi = 2.0 * 3.14159265 * rand | |
| x = Math.cos(phi) * Math.sqrt(1.0 - r) | |
| y = Math.sin(phi) * Math.sqrt(1.0 - r) | |
| z = Math.sqrt(r) | |
| rx = x * basis[0].x + y * basis[1].x + z * basis[2].x | |
| ry = x * basis[0].y + y * basis[1].y + z * basis[2].y | |
| rz = x * basis[0].z + y * basis[1].z + z * basis[2].z | |
| # can allocate on the stack | |
| raydir = Vec.new(rx, ry, rz) | |
| # can allocate on the stack | |
| ray = Ray.new(p0, raydir) | |
| # can allocate on the stack | |
| occisect = Isect.new | |
| @spheres[0].intersect(ray, occisect) | |
| @spheres[1].intersect(ray, occisect) | |
| @spheres[2].intersect(ray, occisect) | |
| @plane.intersect(ray, occisect) | |
| if occisect.hit then | |
| occlusion = occlusion + 1.0 | |
| else | |
| 0.0 | |
| end | |
| end | |
| end | |
| occlusion = (ntheta.to_f * nphi.to_f - occlusion) / (ntheta.to_f * nphi.to_f) | |
| # can allocate on the stack | |
| Vec.new(occlusion, occlusion, occlusion) | |
| end | |
| # render | |
| # [{ UNBOXED Fixnum}, { UNBOXED Fixnum}, { UNBOXED Fixnum}] -> { UNBOXED Fixnum} | |
| # self { BOXED Scene} | |
| # block { BOXED NilClass} | |
| # | |
| def render(w, h, nsubsamples) | |
| cnt = 0 | |
| nsf = nsubsamples.to_f | |
| h.times do |y| | |
| w.times do |x| | |
| # can allocate on the stack | |
| rad = Vec.new(0.0, 0.0, 0.0) | |
| # Subsmpling | |
| nsubsamples.times do |v| | |
| nsubsamples.times do |u| | |
| cnt = cnt + 1 | |
| wf = w.to_f | |
| hf = h.to_f | |
| xf = x.to_f | |
| yf = y.to_f | |
| uf = u.to_f | |
| vf = v.to_f | |
| px = (xf + (uf / nsf) - (wf / 2.0)) / (wf / 2.0) | |
| py = -(yf + (vf / nsf) - (hf / 2.0)) / (hf / 2.0) | |
| # can allocate on the stack | |
| eye = Vec.new(px, py, -1.0).vnormalize | |
| ray = Ray.new(Vec.new(0.0, 0.0, 0.0), eye) | |
| # can allocate on the stack | |
| isect = Isect.new | |
| @spheres[0].intersect(ray, isect) | |
| @spheres[1].intersect(ray, isect) | |
| @spheres[2].intersect(ray, isect) | |
| @plane.intersect(ray, isect) | |
| if isect.hit then | |
| col = ambient_occlusion(isect) | |
| rad.x = rad.x + col.x | |
| rad.y = rad.y + col.y | |
| rad.z = rad.z + col.z | |
| else | |
| 0.0 | |
| end | |
| end | |
| end | |
| r = rad.x / (nsf * nsf) | |
| g = rad.y / (nsf * nsf) | |
| b = rad.z / (nsf * nsf) | |
| printf("%c", clamp(r)) | |
| printf("%c", clamp(g)) | |
| printf("%c", clamp(b)) | |
| end | |
| end | |
| end | |
| end | |
| # File.open("ao.ppm", "w") do |fp| | |
| printf("P6\n") | |
| printf("%d %d\n", IMAGE_WIDTH, IMAGE_HEIGHT) | |
| printf("255\n", IMAGE_WIDTH, IMAGE_HEIGHT) | |
| # can allocate on the stack | |
| Scene.new.render(IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES) | |
| # Scene.new.render(256, 256, 2) | |
| # end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment