-
-
Save omaraflak/08eed161f5390c27fc8fed136f2ff53d to your computer and use it in GitHub Desktop.
import numpy as np | |
import matplotlib.pyplot as plt | |
def normalize(vector): | |
return vector / np.linalg.norm(vector) | |
def reflected(vector, axis): | |
return vector - 2 * np.dot(vector, axis) * axis | |
def sphere_intersect(center, radius, ray_origin, ray_direction): | |
b = 2 * np.dot(ray_direction, ray_origin - center) | |
c = np.linalg.norm(ray_origin - center) ** 2 - radius ** 2 | |
delta = b ** 2 - 4 * c | |
if delta > 0: | |
t1 = (-b + np.sqrt(delta)) / 2 | |
t2 = (-b - np.sqrt(delta)) / 2 | |
if t1 > 0 and t2 > 0: | |
return min(t1, t2) | |
return None | |
def nearest_intersected_object(objects, ray_origin, ray_direction): | |
distances = [sphere_intersect(obj['center'], obj['radius'], ray_origin, ray_direction) for obj in objects] | |
nearest_object = None | |
min_distance = np.inf | |
for index, distance in enumerate(distances): | |
if distance and distance < min_distance: | |
min_distance = distance | |
nearest_object = objects[index] | |
return nearest_object, min_distance | |
width = 300 | |
height = 200 | |
max_depth = 3 | |
camera = np.array([0, 0, 1]) | |
ratio = float(width) / height | |
screen = (-1, 1 / ratio, 1, -1 / ratio) # left, top, right, bottom | |
light = { 'position': np.array([5, 5, 5]), 'ambient': np.array([1, 1, 1]), 'diffuse': np.array([1, 1, 1]), 'specular': np.array([1, 1, 1]) } | |
objects = [ | |
{ 'center': np.array([-0.2, 0, -1]), 'radius': 0.7, 'ambient': np.array([0.1, 0, 0]), 'diffuse': np.array([0.7, 0, 0]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 }, | |
{ 'center': np.array([0.1, -0.3, 0]), 'radius': 0.1, 'ambient': np.array([0.1, 0, 0.1]), 'diffuse': np.array([0.7, 0, 0.7]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 }, | |
{ 'center': np.array([-0.3, 0, 0]), 'radius': 0.15, 'ambient': np.array([0, 0.1, 0]), 'diffuse': np.array([0, 0.6, 0]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 }, | |
{ 'center': np.array([0, -9000, 0]), 'radius': 9000 - 0.7, 'ambient': np.array([0.1, 0.1, 0.1]), 'diffuse': np.array([0.6, 0.6, 0.6]), 'specular': np.array([1, 1, 1]), 'shininess': 100, 'reflection': 0.5 } | |
] | |
image = np.zeros((height, width, 3)) | |
for i, y in enumerate(np.linspace(screen[1], screen[3], height)): | |
for j, x in enumerate(np.linspace(screen[0], screen[2], width)): | |
# screen is on origin | |
pixel = np.array([x, y, 0]) | |
origin = camera | |
direction = normalize(pixel - origin) | |
color = np.zeros((3)) | |
reflection = 1 | |
for k in range(max_depth): | |
# check for intersections | |
nearest_object, min_distance = nearest_intersected_object(objects, origin, direction) | |
if nearest_object is None: | |
break | |
intersection = origin + min_distance * direction | |
normal_to_surface = normalize(intersection - nearest_object['center']) | |
shifted_point = intersection + 1e-5 * normal_to_surface | |
intersection_to_light = normalize(light['position'] - shifted_point) | |
_, min_distance = nearest_intersected_object(objects, shifted_point, intersection_to_light) | |
intersection_to_light_distance = np.linalg.norm(light['position'] - intersection) | |
is_shadowed = min_distance < intersection_to_light_distance | |
if is_shadowed: | |
break | |
illumination = np.zeros((3)) | |
# ambiant | |
illumination += nearest_object['ambient'] * light['ambient'] | |
# diffuse | |
illumination += nearest_object['diffuse'] * light['diffuse'] * np.dot(intersection_to_light, normal_to_surface) | |
# specular | |
intersection_to_camera = normalize(camera - intersection) | |
H = normalize(intersection_to_light + intersection_to_camera) | |
illumination += nearest_object['specular'] * light['specular'] * np.dot(normal_to_surface, H) ** (nearest_object['shininess'] / 4) | |
# reflection | |
color += reflection * illumination | |
reflection *= nearest_object['reflection'] | |
origin = shifted_point | |
direction = reflected(direction, normal_to_surface) | |
image[i, j] = np.clip(color, 0, 1) | |
print("%d/%d" % (i + 1, height)) | |
plt.imsave('image.png', image) |
Could you give me a detail about variable H in line 88? I am so wondering how did you solved reflection equation. Actually I modified the line with referencing this link but my work seems worse than yours.
Have you read the article I mentioned above?
https://medium.com/@omaraflak/ray-tracing-from-scratch-in-python-41670e6a96f9?source=friends_link&sk=4edf81600f5c0941aa58907bbfb2151d
Look at equation 7 (they're all labeled). The specular component of the color is:
Ks * Is * [N • ((L+V) / ||L+V||)] ^ (alpha/4)
Here,
H = (L+V) / ||L+V||
Where,
L is a direction unit vector from the intersection point towards the light;
V is a direction unit vector from the intersection point towards the camera;
You'll have more context in the article.
Hello,
Where can I change the color of the spheres by RGB values?
Hello, Where can I change the color of the spheres by RGB values?
Play around with ambiant, diffuse, specular values
Could you give me a detail about variable H in line 88?
I am so wondering how did you solved reflection equation.
Actually I modified the line with referencing this link
but my work seems worse than yours.