我正在开发光线追踪器,并且我面临着相机的 Look_at 功能的意外行为。看起来相机的旋转被限制在一定的范围内,当我将look_at设置为vec3(100., 0.25, -3.)时,它的行为与vec3(1000., 0.25, -3.)类似。我希望相机能够围绕场景平滑地旋转 360 度。
代码:
from PIL import Image, ImageFilter
import numpy as np
class Scene():
def __init__(self, ambient_color = vec3(0.01, 0.01, 0.01), n = vec3(1.0,1.0,1.0)) :
self.scene_primitives = []
self.collider_list = []
self.shadowed_collider_list = []
self.Light_list = []
self.importance_sampled_list = []
self.ambient_color = ambient_color
self.n = n
self.importance_sampled_list = []
self.bvh_root = None
def add(self, collider):
self.collider_list.append(collider)
def build_bvh(self):
# Build BVH for the scene
self.bvh_root = build_bvh(self.collider_list)
def get_raycolor(self, ray):
distances, hit_orientation = zip(*[s.intersect(ray.origin, ray.dir) for s in self.collider_list])
nearest = np.minimum.reduce(distances)
color = vec3(0., 0., 0.)
for (coll, dis, orient) in zip(self.collider_list, distances, hit_orientation):
hit_check = (nearest != FARAWAY) & (dis == nearest)
if np.any(hit_check):
color += coll.assigned_primitive.material.get_color(
self, ray.extract(hit_check),
Hit(extract(hit_check, dis), extract(hit_check, orient), coll.assigned_primitive.material, coll,
coll.assigned_primitive)).place(hit_check)
return color
def add_Camera(self, look_from, look_at, **kwargs):
self.camera = Camera(look_from, look_at, **kwargs)
def add_PointLight(self, pos, color):
self.Light_list += [PointLight(pos, color)]
def add_DirectionalLight(self, Ldir, color):
self.Light_list += [DirectionalLight(Ldir.normalize() , color)]
def add(self,primitive, importance_sampled = False):
self.scene_primitives += [primitive]
self.collider_list += primitive.collider_list
if importance_sampled == True:
self.importance_sampled_list += [primitive]
if primitive.shadow == True:
self.shadowed_collider_list += primitive.collider_list
def add_Background(self, img, light_intensity = 0.0, blur =0.0 , spherical = False):
primitive = None
if spherical == False:
primitive = SkyBox(img, light_intensity = light_intensity, blur = blur)
else:
primitive = Panorama(img, light_intensity = light_intensity, blur = blur)
self.scene_primitives += [primitive]
self.collider_list += primitive.collider_list
def render(self, samples_per_pixel, progress_bar = False):
if self.bvh_root is None:
self.build_bvh()
color_RGBlinear = vec3(0.,0.,0.)
if progress_bar:
for i in range(samples_per_pixel):
color_RGBlinear += get_raycolor(self.camera.get_ray(self.n), scene = self)
print(i)
else:
for i in range(samples_per_pixel):
color_RGBlinear += get_raycolor(self.camera.get_ray(self.n), scene = self)
color_RGBlinear = color_RGBlinear/samples_per_pixel
rgb_linear = color_RGBlinear.to_array()
rgb = np.where( rgb_linear <= 0.00304, 12.92 * rgb_linear, 1.055 * np.power(rgb_linear, 1.0/2.4) - 0.055)
rgb_max = np.amax(rgb, axis=0) + 0.00001
intensity_cutoff = 1.0
color = np.where(rgb_max > intensity_cutoff, rgb * intensity_cutoff / (rgb_max), rgb)
img_RGB = []
for c in color:
img_RGB += [Image.fromarray((255 * np.clip(c, 0, 1).reshape((self.camera.screen_height, self.camera.screen_width))).astype(np.uint8), "L") ]
return Image.merge("RGB", img_RGB)
class Camera():
def __init__(self, look_from, look_at, screen_width = 400 ,screen_height = 300, field_of_view = 90., aperture = 0., focal_distance = 1.):
self.screen_width = screen_width
self.screen_height = screen_height
self.aspect_ratio = float(screen_width) / screen_height
self.look_from = look_from
self.look_at = look_at
self.camera_width = np.tan(field_of_view * np.pi / 180 / 2.) * 2.
self.camera_height = self.camera_width / self.aspect_ratio
self.cameraFwd = (look_at - look_from).normalize()
self.cameraRight = (self.cameraFwd.cross(vec3(0., 1., 0.))).normalize()
self.cameraUp = self.cameraRight.cross(self.cameraFwd)
self.lens_radius = aperture / 2.
self.focal_distance = focal_distance
self.near = .1
self.far = 100.
self.x = np.linspace(-self.camera_width / 2., self.camera_width / 2., self.screen_width)
self.y = np.linspace(self.camera_height / 2., -self.camera_height / 2., self.screen_height)
xx, yy = np.meshgrid(self.x, self.y)
self.x = xx.flatten()
self.y = yy.flatten()
def get_ray(self,n):
x = self.x + (np.random.rand(len(self.x)) - 0.5) * self.camera_width / (self.screen_width)
y = self.y + (np.random.rand(len(self.y)) - 0.5) * self.camera_height / (self.screen_height)
ray_origin = self.look_from + self.cameraRight * x * self.near + self.cameraUp * y * self.near
return Ray(origin=ray_origin, dir=(self.look_from + self.cameraUp * y * self.focal_distance +self.cameraRight * x * self.focal_distance +self.cameraFwd * self.focal_distance - ray_origin).normalize(), depth=0, n=n, reflections=0, transmissions=0, diffuse_reflections=0)
用途:
camera_position = vec3(2.5 * np.sin(angle), 0.25, 2.5 * np.cos(angle) - 1.5)
look_at = vec3(0., 0.25, -3.) # in this case 0. (it's not the same when you put 10 which is correct but 100 is the same with 1000 or 200 which is incorrect)
Sc.add_Camera(look_from=camera_position, look_at=look_at, screen_width=width, screen_height=height)
Sc.build_bvh()
Sc.render(samples_per_pixel=1).show()
您可能没有更改所有
look_at
值(x、y、z)。