Skip to content

Instantly share code, notes, and snippets.

@miura1729
Created November 24, 2011 14:49
Show Gist options
  • Save miura1729/1391500 to your computer and use it in GitHub Desktop.
Save miura1729/1391500 to your computer and use it in GitHub Desktop.
ao-render.rbの型推論結果
# 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)
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)
@x * b.x + @y * b.y + @z * b.z
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 Object}
# 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
else
0.0
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
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}
#
# [{ BOXED Object}] -> { 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
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)
basis = Array.new(3)
otherBasis(basis, isect.n)
ntheta = NAO_SAMPLES
nphi = NAO_SAMPLES
eps = 0.0001
occlusion = 0.0
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
raydir = Vec.new(rx, ry, rz)
ray = Ray.new(p0, raydir)
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)
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|
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)
eye = Vec.new(px, py, -1.0).vnormalize
ray = Ray.new(Vec.new(0.0, 0.0, 0.0), eye)
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)
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