Created
February 21, 2017 14:00
-
-
Save yhara/25989834187571f0c2750a9aa6a738d3 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
# 1. とりあえず色を出す | |
IMAGE_WIDTH = 256 | |
IMAGE_HEIGHT = 256 | |
IMAGE_DEPTH = 256 | |
class Vec | |
def initialize(x, y, z) | |
@x, @y, @z = x, y, z | |
end | |
attr_reader :x, :y, :z | |
def vnormalize! | |
len = vlength() | |
if len > 1.0e-17 | |
r_len = 1.0 / len | |
@x *= r_len | |
@y *= r_len | |
@z *= r_len | |
end | |
self | |
end | |
def vlength | |
Math.sqrt(@x * @x + @y * @y + @z * @z) | |
end | |
end | |
class Ray | |
def initialize(origin, dir) | |
@origin, @dir = origin, dir | |
end | |
attr_reader :dir | |
end | |
puts("P3") | |
puts("#{IMAGE_WIDTH} #{IMAGE_HEIGHT}") | |
puts("#{IMAGE_DEPTH-1}") | |
# t: 0 ~ 1 | |
def color(t) | |
t = 0 if t < 0 | |
t = 1 if t > 1 | |
ret = IMAGE_DEPTH * t.to_f | |
return (ret == IMAGE_DEPTH ? (IMAGE_DEPTH-1) : ret).to_i | |
end | |
IMAGE_HEIGHT.times do |row| | |
IMAGE_WIDTH.times do |col| | |
x = col.to_f / (IMAGE_WIDTH / 2) - 1.0 | |
y = (IMAGE_HEIGHT-row).to_f / (IMAGE_HEIGHT / 2) - 1.0 | |
ray = Ray.new(Vec.new(0.0, 0.0, 5.0), | |
Vec.new(x, y, -1.0).vnormalize!) | |
#p x: x, y: y, dir: ray.dir | |
r, g, b = color(ray.dir.x), color(ray.dir.y), color(ray.dir.z) | |
print("#{r} #{g} #{b} ") | |
end | |
end |
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
# 2. 白い円を出す | |
IMAGE_WIDTH = 256 | |
IMAGE_HEIGHT = 256 | |
IMAGE_DEPTH = 256 | |
class Vec | |
def initialize(x, y, z) | |
@x, @y, @z = x, y, z | |
end | |
attr_reader :x, :y, :z | |
def vsub(b) | |
Vec.new(@x - b.x, @y - b.y, @z - b.z) | |
end | |
def vdot(b) | |
@x * b.x + @y * b.y + @z * b.z | |
end | |
def vnormalize! | |
len = vlength() | |
if len > 1.0e-17 | |
r_len = 1.0 / len | |
@x *= r_len | |
@y *= r_len | |
@z *= r_len | |
end | |
self | |
end | |
def vlength | |
Math.sqrt(@x * @x + @y * @y + @z * @z) | |
end | |
end | |
class Ray | |
def initialize(origin, dir) | |
@origin, @dir = origin, dir | |
end | |
attr_reader :origin, :dir | |
end | |
class Sphere | |
def initialize(radius, position, color) | |
@radius, @position, @color = radius, position, color | |
end | |
attr_reader :radius, :position, :color | |
def intersect?(ray) | |
rs = ray.origin.vsub(@position) | |
b = rs.vdot(ray.dir) | |
c = rs.vdot(rs) - @radius * @radius | |
d = b * b - c | |
return d > 0 && (-b - Math.sqrt(d)) > 0 | |
end | |
end | |
puts("P3") | |
puts("#{IMAGE_WIDTH} #{IMAGE_HEIGHT}") | |
puts("#{IMAGE_DEPTH-1}") | |
# t: 0 ~ 1 | |
def color(t) | |
t = 0 if t < 0 | |
t = 1 if t > 1 | |
ret = IMAGE_DEPTH * t.to_f | |
return (ret == IMAGE_DEPTH ? (IMAGE_DEPTH-1) : ret).to_i | |
end | |
def print_col(c) | |
print("#{color(c.x)} #{color(c.y)} #{color(c.z)} ") | |
end | |
BLACK = Vec.new(0, 0, 0) | |
sphere = Sphere.new(1.0, Vec.new(0, 0, 0), Vec.new(1, 1, 1)) | |
IMAGE_HEIGHT.times do |row| | |
IMAGE_WIDTH.times do |col| | |
x = col.to_f / (IMAGE_WIDTH / 2) - 1.0 | |
y = (IMAGE_HEIGHT-row).to_f / (IMAGE_HEIGHT / 2) - 1.0 | |
ray = Ray.new(Vec.new(0.0, 0.0, 5.0), | |
Vec.new(x, y, -1.0).vnormalize!) | |
c = if sphere.intersect?(ray) | |
sphere.color | |
else | |
BLACK | |
end | |
print_col(c) | |
end | |
end |
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
# 3. 球に影をつける | |
IMAGE_WIDTH = 256 | |
IMAGE_HEIGHT = 256 | |
IMAGE_DEPTH = 256 | |
class Vec | |
def initialize(x, y, z) | |
@x, @y, @z = x, y, z | |
end | |
attr_reader :x, :y, :z | |
def vadd(b) | |
Vec.new(@x + b.x, @y + b.y, @z + b.z) | |
end | |
def vsub(b) | |
Vec.new(@x - b.x, @y - b.y, @z - b.z) | |
end | |
def vmul(t) | |
Vec.new(@x * t, @y * t, @z * t) | |
end | |
def vdot(b) | |
@x * b.x + @y * b.y + @z * b.z | |
end | |
def vnormalize! | |
len = vlength() | |
if len > 1.0e-17 | |
r_len = 1.0 / len | |
@x *= r_len | |
@y *= r_len | |
@z *= r_len | |
end | |
self | |
end | |
def vlength | |
Math.sqrt(@x * @x + @y * @y + @z * @z) | |
end | |
end | |
class Ray | |
def initialize(origin, dir) | |
@origin, @dir = origin, dir | |
end | |
attr_reader :origin, :dir | |
end | |
# 交点 | |
class Isect | |
def initialize(hit=false) | |
@hit = hit | |
@hit_point = Vec.new(0, 0, 0) | |
@normal = Vec.new(0, 0, 0) | |
@color = Vec.new(0, 0, 0) | |
end | |
attr_accessor :hit, :hit_point, :normal, :color | |
end | |
class Sphere | |
def initialize(radius, position, color) | |
@radius, @position, @color = radius, position, color | |
end | |
attr_reader :radius, :position, :color | |
def intersection(ray) | |
rs = ray.origin.vsub(@position) | |
b = rs.vdot(ray.dir) | |
c = rs.vdot(rs) - @radius * @radius | |
d = b * b - c | |
if d > 0 && (t = -b - Math.sqrt(d)) > 0 | |
i = Isect.new(true) | |
i.hit_point = ray.origin.vadd(ray.dir.vmul(t)) | |
i.normal = i.hit_point.vsub(@position).vnormalize! | |
d = clamp(Vec.new(1, 1, 1).vnormalize!.vdot(i.normal), 0.1, 1.0) | |
i.color = @color.vmul(d) | |
else | |
i = Isect.new(false) | |
end | |
i | |
end | |
end | |
puts("P3") | |
puts("#{IMAGE_WIDTH} #{IMAGE_HEIGHT}") | |
puts("#{IMAGE_DEPTH-1}") | |
def clamp(t, min, max) | |
if t < min | |
min | |
elsif t > max | |
max | |
else | |
t | |
end | |
end | |
# t: 0 ~ 1 | |
def color(t) | |
t = 0 if t < 0 | |
t = 1 if t > 1 | |
ret = IMAGE_DEPTH * t.to_f | |
return (ret == IMAGE_DEPTH ? (IMAGE_DEPTH-1) : ret).to_i | |
end | |
def print_col(c) | |
print("#{color(c.x)} #{color(c.y)} #{color(c.z)} ") | |
end | |
BLACK = Vec.new(0, 0, 0) | |
sphere = Sphere.new(1.0, Vec.new(0, 0, 0), Vec.new(1, 1, 1)) | |
IMAGE_HEIGHT.times do |row| | |
IMAGE_WIDTH.times do |col| | |
x = col.to_f / (IMAGE_WIDTH / 2) - 1.0 | |
y = (IMAGE_HEIGHT-row).to_f / (IMAGE_HEIGHT / 2) - 1.0 | |
ray = Ray.new(Vec.new(0.0, 0.0, 5.0), | |
Vec.new(x, y, -1.0).vnormalize!) | |
i = sphere.intersection(ray) | |
print_col(i.color) | |
end | |
end |
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
# 4. 床を出す | |
IMAGE_WIDTH = 256 | |
IMAGE_HEIGHT = 256 | |
IMAGE_DEPTH = 256 | |
class Vec | |
def initialize(x, y, z) | |
@x, @y, @z = x, y, z | |
end | |
attr_reader :x, :y, :z | |
def vadd(b) | |
Vec.new(@x + b.x, @y + b.y, @z + b.z) | |
end | |
def vsub(b) | |
Vec.new(@x - b.x, @y - b.y, @z - b.z) | |
end | |
def vmul(t) | |
Vec.new(@x * t, @y * t, @z * t) | |
end | |
def vdot(b) | |
@x * b.x + @y * b.y + @z * b.z | |
end | |
def vnormalize! | |
len = vlength() | |
if len > 1.0e-17 | |
r_len = 1.0 / len | |
@x *= r_len | |
@y *= r_len | |
@z *= r_len | |
end | |
self | |
end | |
def vlength | |
Math.sqrt(@x * @x + @y * @y + @z * @z) | |
end | |
end | |
class Ray | |
def initialize(origin, dir) | |
@origin, @dir = origin, dir | |
end | |
attr_reader :origin, :dir | |
end | |
# 交点 | |
class Isect | |
def initialize(hit=false) | |
@hit = hit | |
@hit_point = Vec.new(0, 0, 0) | |
@normal = Vec.new(0, 0, 0) | |
@color = Vec.new(0, 0, 0) | |
@distance = 1.0e+30 | |
end | |
attr_accessor :hit, :hit_point, :normal, :color, :distance | |
end | |
class Sphere | |
def initialize(radius, position, color) | |
@radius, @position, @color = radius, position, color | |
end | |
attr_reader :radius, :position, :color | |
def intersect!(ray, isect) | |
rs = ray.origin.vsub(@position) | |
b = rs.vdot(ray.dir) | |
c = rs.vdot(rs) - (@radius * @radius) | |
d = b * b - c | |
if d > 0 | |
t = -b - Math.sqrt(d) | |
if t > 0 && t < isect.distance | |
isect.hit_point = ray.origin.vadd(ray.dir.vmul(t)) | |
isect.normal = isect.hit_point.vsub(@position).vnormalize! | |
isect.color = @color.vmul(clamp(LIGHT.vdot(isect.normal), 0.1, 1.0)) | |
isect.distance = t | |
end | |
end | |
end | |
end | |
class Plane | |
def initialize(position, normal, color) | |
@position, @normal, @color = position, normal, color | |
end | |
def intersect!(ray, isect) | |
d = -(@position.vdot(@normal)) | |
v = ray.dir.vdot(@normal) | |
t = -(ray.origin.vdot(@normal) + d) / v | |
if t > 0 && t < isect.distance | |
isect.hit_point = ray.origin.vadd(ray.dir.vmul(t)) | |
isect.normal = @normal | |
d = clamp(LIGHT.vdot(isect.normal), 0.1, 1.0) | |
m = isect.hit_point.x % 2 | |
n = isect.hit_point.z % 2 | |
d *= 0.5 if ((m > 1 && n > 1) || (m < 1 && n < 1)) | |
f = 1.0 - [isect.hit_point.z.abs, 25.0].min * 0.04 | |
isect.color = @color.vmul(d * f) | |
isect.distance = t | |
end | |
end | |
end | |
def clamp(t, min, max) | |
if t < min | |
min | |
elsif t > max | |
max | |
else | |
t | |
end | |
end | |
# t: 0 ~ 1 | |
def color(t) | |
t = 0 if t < 0 | |
t = 1 if t > 1 | |
ret = IMAGE_DEPTH * t.to_f | |
return (ret == IMAGE_DEPTH ? (IMAGE_DEPTH-1) : ret).to_i | |
end | |
def print_col(c) | |
print("#{color(c.x)} #{color(c.y)} #{color(c.z)} ") | |
end | |
BLACK = Vec.new(0, 0, 0) | |
LIGHT = Vec.new(0.577, 0.577, 0.577) | |
puts("P3") | |
puts("#{IMAGE_WIDTH} #{IMAGE_HEIGHT}") | |
puts("#{IMAGE_DEPTH-1}") | |
plane = Plane.new(Vec.new(0, -1, 0), Vec.new(0, 1, 0), Vec.new(1, 1, 1)) | |
t = 0 | |
sphere1 = Sphere.new(0.5, Vec.new( 0.0, -0.5, Math.sin(0)), Vec.new(1, 0, 0)) | |
sphere2 = Sphere.new(1.0, Vec.new( 2.0, 0.0, Math.cos(t*0.666)), Vec.new(0, 1, 0)) | |
sphere3 = Sphere.new(1.5, Vec.new(-2.0, 0.5, Math.cos(t*0.333)), Vec.new(0, 0, 1)) | |
IMAGE_HEIGHT.times do |row| | |
IMAGE_WIDTH.times do |col| | |
x = col.to_f / (IMAGE_WIDTH / 2) - 1.0 | |
y = (IMAGE_HEIGHT-row).to_f / (IMAGE_HEIGHT / 2) - 1.0 | |
ray = Ray.new(Vec.new(0.0, 2.0, 6.0), | |
Vec.new(x, y, -1.0).vnormalize!) | |
i = Isect.new(false) | |
sphere1.intersect!(ray, i) | |
sphere2.intersect!(ray, i) | |
sphere3.intersect!(ray, i) | |
plane.intersect!(ray, i) | |
print_col(i.color) | |
end | |
end |
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
# 5. 反射させる | |
IMAGE_WIDTH = 512 | |
IMAGE_HEIGHT = 512 | |
IMAGE_DEPTH = 256 | |
class Vec | |
def initialize(x, y, z) | |
@x, @y, @z = x, y, z | |
end | |
attr_reader :x, :y, :z | |
def vadd(b) | |
Vec.new(@x + b.x, @y + b.y, @z + b.z) | |
end | |
def vsub(b) | |
Vec.new(@x - b.x, @y - b.y, @z - b.z) | |
end | |
def vmul(t) | |
Vec.new(@x * t, @y * t, @z * t) | |
end | |
def vmulti(b) | |
Vec.new(@x * b.x, @y * b.y, @z * b.z) | |
end | |
def vdot(b) | |
@x * b.x + @y * b.y + @z * b.z | |
end | |
def vcross(b) | |
Vec.new(@y * b.z - @z * b.y, | |
@z * b.x - @x * b.z, | |
@x * b.y - @y * b.x) | |
end | |
def vnormalize! | |
len = vlength() | |
if len > 1.0e-17 | |
r_len = 1.0 / len | |
@x *= r_len | |
@y *= r_len | |
@z *= r_len | |
end | |
self | |
end | |
def vlength | |
Math.sqrt(@x * @x + @y * @y + @z * @z) | |
end | |
def reflect(normal) | |
self.vadd(normal.vmul(-2 * normal.vdot(self))) | |
end | |
end | |
class Ray | |
def initialize(origin, dir) | |
@origin, @dir = origin, dir | |
end | |
attr_reader :origin, :dir | |
end | |
# 交点 | |
class Isect | |
def initialize() | |
@hit = 0 | |
@hit_point = Vec.new(0, 0, 0) | |
@normal = Vec.new(0, 0, 0) | |
@color = Vec.new(0, 0, 0) | |
@distance = 1.0e+30 | |
@ray_dir = Vec.new(0, 0, 0) | |
end | |
attr_accessor :hit, :hit_point, :normal, :color, :distance, :ray_dir | |
end | |
class Sphere | |
def initialize(radius, position, color) | |
@radius, @position, @color = radius, position, color | |
end | |
attr_reader :radius, :position, :color | |
def intersect!(ray, isect) | |
rs = ray.origin.vsub(@position) | |
b = rs.vdot(ray.dir) | |
c = rs.vdot(rs) - (@radius * @radius) | |
d = b * b - c | |
t = -b - Math.sqrt(d.abs) | |
if d > 0 && t > EPS && t < isect.distance | |
isect.hit_point = ray.origin.vadd(ray.dir.vmul(t)) | |
isect.normal = isect.hit_point.vsub(@position).vnormalize! | |
isect.color = @color.vmul(clamp(LIGHT.vdot(isect.normal), 0.1, 1.0)) | |
isect.distance = t | |
isect.hit += 1 | |
isect.ray_dir = ray.dir | |
end | |
end | |
end | |
class Plane | |
def initialize(position, normal, color) | |
@position, @normal, @color = position, normal, color | |
end | |
def intersect!(ray, isect) | |
d = -(@position.vdot(@normal)) | |
v = ray.dir.vdot(@normal) | |
t = -(ray.origin.vdot(@normal) + d) / v | |
if t > EPS && t < isect.distance | |
isect.hit_point = ray.origin.vadd(ray.dir.vmul(t)) | |
isect.normal = @normal | |
d = clamp(LIGHT.vdot(isect.normal), 0.1, 1.0) | |
m = isect.hit_point.x % 2 | |
n = isect.hit_point.z % 2 | |
d *= 0.5 if ((m > 1 && n > 1) || (m < 1 && n < 1)) | |
f = 1.0 - [isect.hit_point.z.abs, 25.0].min * 0.04 | |
isect.color = @color.vmul(d * f) | |
isect.distance = t | |
isect.hit += 1 | |
isect.ray_dir = ray.dir | |
end | |
end | |
end | |
def clamp(t, min, max) | |
if t < min | |
min | |
elsif t > max | |
max | |
else | |
t | |
end | |
end | |
# t: 0 ~ 1 | |
def color(t) | |
t = 0 if t < 0 | |
t = 1 if t > 1 | |
ret = IMAGE_DEPTH * t.to_f | |
return (ret == IMAGE_DEPTH ? (IMAGE_DEPTH-1) : ret).to_i | |
end | |
def print_col(c) | |
print("#{color(c.x)} #{color(c.y)} #{color(c.z)} ") | |
end | |
# 光の方向 | |
LIGHT = Vec.new(0.577, 0.577, 0.577) | |
EPS = 0.0001 | |
# 最大反射回数 | |
MAX_REF = 4 | |
PLANE = Plane.new(Vec.new(0, -1, 0), Vec.new(0, 1, 0), Vec.new(1, 1, 1)) | |
t = 0 | |
SPHERE1 = Sphere.new(0.5, Vec.new( 0.0, -0.5, Math.sin(0)), Vec.new(1, 0, 0)) | |
SPHERE2 = Sphere.new(1.0, Vec.new( 2.0, 0.0, Math.cos(t*0.666)), Vec.new(0, 1, 0)) | |
SPHERE3 = Sphere.new(1.5, Vec.new(-2.0, 0.5, Math.cos(t*0.333)), Vec.new(0, 0, 1)) | |
def intersect!(ray, i) | |
SPHERE1.intersect!(ray, i) | |
SPHERE2.intersect!(ray, i) | |
SPHERE3.intersect!(ray, i) | |
PLANE.intersect!(ray, i) | |
end | |
puts("P3") | |
puts("#{IMAGE_WIDTH} #{IMAGE_HEIGHT}") | |
puts("#{IMAGE_DEPTH-1}") | |
IMAGE_HEIGHT.times do |row| | |
IMAGE_WIDTH.times do |col| | |
x = col.to_f / (IMAGE_WIDTH / 2) - 1.0 | |
y = (IMAGE_HEIGHT-row).to_f / (IMAGE_HEIGHT / 2) - 1.0 | |
ray = Ray.new(Vec.new(0.0, 2.0, 6.0), | |
Vec.new(x, y, -1.0).vnormalize!) | |
i = Isect.new | |
intersect!(ray, i) | |
if i.hit > 0 | |
dest_col = i.color | |
temp_col = Vec.new(1, 1, 1).vmulti(i.color) | |
(1...MAX_REF).each do |j| | |
q = Ray.new(i.hit_point.vadd(i.normal.vmul(EPS)), | |
i.ray_dir.reflect(i.normal)) | |
intersect!(q, i) | |
if i.hit > j | |
dest_col = dest_col.vadd(temp_col.vmulti(i.color)) | |
temp_col = temp_col.vmulti(i.color) | |
end | |
end | |
else | |
dest_col = Vec.new(ray.dir.y, ray.dir.y, ray.dir.y) | |
end | |
print_col(dest_col) | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment