Created
April 30, 2020 14:04
-
-
Save yhara/ea0e66e0d8bdd114d2401dd133539fa3 to your computer and use it in GitHub Desktop.
This file contains 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
# orig: http://qiita.com/doxas/items/477fda867da467116f8d | |
IMAGE_WIDTH = 512 | |
IMAGE_HEIGHT = 512 | |
IMAGE_DEPTH = 256 | |
EPS = 0.0001 | |
MAX_REF = 4 | |
class Vec | |
def initialize(x: Float, y: Float, z: Float) | |
var @x = x | |
var @y = y | |
var @z = z | |
end | |
def x -> Float; @x; end | |
def y -> Float; @y; end | |
def z -> Float; @z; end | |
def add(b: Vec) -> Vec | |
Vec.new(@x + b.x, @y + b.y, @z + b.z) | |
end | |
def sub(b: Vec) -> Vec | |
Vec.new(@x - b.x, @y - b.y, @z - b.z) | |
end | |
def mul(t: Float) -> Vec | |
Vec.new(@x * t, @y * t, @z * t) | |
end | |
def multi(b: Vec) -> Vec | |
Vec.new(@x * b.x, @y * b.y, @z * b.z) | |
end | |
def dot(b: Vec) -> Float | |
@x * b.x + @y * b.y + @z * b.z | |
end | |
def cross(b: Vec) -> Vec | |
Vec.new(@y * b.z - @z * b.y, | |
@z * b.x - @x * b.z, | |
@x * b.y - @y * b.x) | |
end | |
def length -> Float | |
Math.sqrt(@x * @x + @y * @y + @z * @z) | |
end | |
def normalize -> Vec | |
len = self.length() | |
if len > 0.00000000000000001 | |
r_len = 1.0 / len | |
@x = @x * r_len | |
@y = @y * r_len | |
@z = @z * r_len | |
end | |
self | |
end | |
def reflect(normal: Vec) -> Vec | |
self.add(normal.mul(-2.0 * normal.dot(self))) | |
end | |
end | |
LIGHT = Vec.new(0.577, 0.577, 0.577) | |
class Ray | |
def initialize(origin: Vec, dir: Vec) | |
@origin = origin | |
@dir = dir | |
end | |
def origin -> Vec; @origin; end | |
def dir -> Vec; @dir; end | |
end | |
class Isect | |
def initialize(hit: Int, hit_point: Vec, normal: Vec, | |
color: Vec, distance: Float, ray_dir: Vec) | |
var @hit = hit | |
var @hit_point = hit_point | |
var @normal = normal | |
var @color = color | |
var @distance = distance | |
var @ray_dir = ray_dir | |
end | |
def hit -> Int; @hit; end | |
def hit=(v: Int); @hit=v; end | |
def hit_point -> Vec; @hit_point; end | |
def hit_point=(v: Vec); @hit_point=v; end | |
def normal -> Vec; @normal; end | |
def normal=(v: Vec); @normal=v; end | |
def color -> Vec; @color; end | |
def color=(v: Vec); @color=v; end | |
def distance -> Float; @distance; end | |
def distance=(v: Float); @distance=v; end | |
def ray_dir -> Vec; @ray_dir; end | |
def ray_dir=(v: Vec); @ray_dir=v; end | |
end | |
class Sphere | |
def initialize(radius: Float, position: Vec, color: Vec) | |
@radius = radius | |
@position = position | |
@color = color | |
end | |
def radius -> Float; @radius; end | |
def position -> Vec; @position; end | |
def color -> Vec; @color; end | |
def intersect(ray: Ray, isect: Isect) -> Void | |
rs = ray.origin.sub(@position) | |
b = rs.dot(ray.dir) | |
c = rs.dot(rs) - @radius * @radius | |
d = b * b - c | |
t = -b - Math.sqrt(d) | |
if d > 0.0 && t > EPS && t < isect.distance | |
isect.hit_point = ray.origin.add(ray.dir.mul(t)) | |
isect.normal = isect.hit_point.sub(@position).normalize | |
isect.color = @color.mul(Util.clamp(LIGHT.dot(isect.normal), 0.1, 1.0)) | |
isect.distance = t | |
isect.hit = isect.hit + 1 | |
isect.ray_dir = ray.dir | |
end | |
end | |
end | |
class Plane | |
def initialize(position: Vec, normal: Vec, color: Vec) | |
@position = position | |
@normal = normal | |
@color = color | |
end | |
def position -> Vec; @position; end | |
def normal -> Vec; @normal; end | |
def color -> Vec; @color; end | |
def intersect(ray: Ray, isect: Isect) -> Void | |
d = -(@position.dot(@normal)) | |
v = ray.dir.dot(@normal) | |
t = -(ray.origin.dot(@normal) + d) / v | |
if t > EPS && t < isect.distance | |
isect.hit_point = ray.origin.add(ray.dir.mul(t)) | |
isect.normal = @normal | |
d2 = Util.clamp(LIGHT.dot(isect.normal), 0.1, 1.0) | |
m = isect.hit_point.x % 2 | |
n = isect.hit_point.z % 2 | |
d3 = if (m > 1.0 && n > 1.0) || (m < 1.0 && n < 1.0) | |
d2*0.5 | |
else | |
d2 | |
end | |
abs = isect.hit_point.z.abs | |
f = 1.0 - (if abs < 25.0; abs; else 25.0; end) * 0.04 | |
isect.color = @color.mul(d3 * f) | |
isect.distance = t | |
isect.hit = isect.hit + 1 | |
isect.ray_dir = ray.dir | |
end | |
end | |
end | |
PLANE = Plane.new(Vec.new(0.0, -1.0, 0.0), Vec.new(0.0, 1.0, 0.0), Vec.new(1.0, 1.0, 1.0)) | |
T = 10.0 | |
SPHERE1 = Sphere.new(0.5, Vec.new( 0.0, -0.5, Math.sin(0.0)), Vec.new(1.0, 0.0, 0.0)) | |
SPHERE2 = Sphere.new(1.0, Vec.new( 2.0, 0.0, Math.cos(T * 0.666)), Vec.new(0.0, 1.0, 0.0)) | |
SPHERE3 = Sphere.new(1.5, Vec.new(-2.0, 0.5, Math.cos(T * 0.333)), Vec.new(0.0, 0.0, 1.0)) | |
class Util | |
def self.clamp(t: Float, min: Float, max: Float) -> Float | |
if t < min | |
min | |
else | |
if t > max | |
max | |
else | |
t | |
end | |
end | |
end | |
## t: 0 ~ 1 | |
def self.color(t: Float) -> Int | |
ret = (IMAGE_DEPTH.to_f * Util.clamp(t, 0.0, 1.0)).to_i | |
if ret == IMAGE_DEPTH then (IMAGE_DEPTH-1) else ret end | |
end | |
def self.print_col(c: Vec) -> Void | |
putd(Util.color(c.x)); putchar(32) | |
putd(Util.color(c.y)); putchar(32) | |
putd(Util.color(c.z)); putchar(10) | |
end | |
def self.intersect(ray: Ray, i: Isect) -> Void | |
SPHERE1.intersect(ray, i) | |
SPHERE2.intersect(ray, i) | |
SPHERE3.intersect(ray, i) | |
PLANE.intersect(ray, i) | |
end | |
end | |
# P3\n | |
puts "P3" | |
# W H\n | |
putd(IMAGE_WIDTH); puts " "; putd(IMAGE_HEIGHT); puts "" | |
# D | |
puts "255" | |
var row = 0; while row < IMAGE_HEIGHT | |
var col = 0; while col < IMAGE_WIDTH | |
x = col.to_f / (IMAGE_WIDTH.to_f / 2.0) - 1.0 | |
y = (IMAGE_HEIGHT-row).to_f / (IMAGE_HEIGHT.to_f / 2.0) - 1.0 | |
ray = Ray.new(Vec.new(0.0, 2.0, 6.0), | |
Vec.new(x, y, -1.0).normalize) | |
i = Isect.new(0, Vec.new(0.0, 0.0, 0.0), Vec.new(0.0, 0.0, 0.0), Vec.new(0.0, 0.0, 0.0), | |
1000000000000000000000000000000.0, Vec.new(0.0, 0.0, 0.0)) | |
Util.intersect(ray, i) | |
if i.hit > 0 | |
var dest_col = i.color | |
var temp_col = Vec.new(1.0, 1.0, 1.0).multi(i.color) | |
var j = 1; while j < MAX_REF | |
q = Ray.new(i.hit_point.add(i.normal.mul(EPS)), | |
i.ray_dir.reflect(i.normal)) | |
Util.intersect(q, i) | |
if i.hit > j | |
dest_col = dest_col.add(temp_col.multi(i.color)) | |
temp_col = temp_col.multi(i.color) | |
end | |
j = j + 1 | |
end | |
Util.print_col(dest_col) | |
else | |
Util.print_col(Vec.new(ray.dir.y, ray.dir.y, ray.dir.y)) | |
end | |
col = col + 1 | |
end | |
row = row + 1 | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment