Spaces:
Sleeping
Sleeping
| # Borrowed from https://github.com/QhelDIV/xgutils/blob/main/vis/fresnelvis.py | |
| import numpy as np | |
| import fresnel | |
| import matplotlib.pyplot as plt | |
| import math | |
| import copy | |
| from scipy.spatial.transform import Rotation as R | |
| from skimage.color import rgba2rgb, rgb2gray | |
| dflt_camera = dict( | |
| camPos=np.array([2, 2, 2]), | |
| camLookat=np.array([0.0, 0.0, 0.0]), | |
| camUp=np.array([0, 1, 0]), | |
| camHeight=2, | |
| fit_camera=False, | |
| light_samples=32, | |
| samples=32, | |
| resolution=(256, 256), | |
| ) | |
| gold_color = np.array([253, 204, 134]) / 256 | |
| gray_color = np.array([0.9, 0.9, 0.9]) | |
| white_color = np.array([1, 1, 1.0]) | |
| black_color = np.array([0, 0, 0.0]) | |
| red_color = np.array([1.0, 0.0, 0.0]) | |
| voxel_mat = dict(specular=0.5, roughness=0.5, metal=1.0, spec_trans=0.0) | |
| # default_mat = dict(specular=.5, roughness=.5, metal=1., spec_trans=0.) | |
| light_preset = ["lightbox", "Cloudy", "Rembrandt", "loop", "butterfly", "ring"] | |
| def addAxes(scene, radius=[0.01, 0.01, 0.01]): | |
| axs = fresnel.geometry.Cylinder(scene, N=3) | |
| axs.material = fresnel.material.Material(solid=1.0) | |
| axs.material.primitive_color_mix = 1.0 | |
| axs.points[:] = [ | |
| [[0, 0, 0], [1, 0, 0]], | |
| [[0, 0, 0], [0, 1, 0]], | |
| [[0, 0, 0], [0, 0, 1]], | |
| ] | |
| axs.radius[:] = radius | |
| axs.color[:] = [ | |
| [[1, 0, 0], [1, 0, 0]], | |
| [[0, 1, 0], [0, 1, 0]], | |
| [[0, 0, 1], [0, 0, 1]], | |
| ] | |
| def addBBox( | |
| scene, | |
| bb_min=np.array([-1, -1, -1.0]), | |
| bb_max=np.array([1, 1, 1.0]), | |
| color=red_color, | |
| radius=0.005, | |
| solid=1.0, | |
| ): | |
| axs = fresnel.geometry.Cylinder(scene, N=12) | |
| axs.material = fresnel.material.Material( | |
| color=fresnel.color.linear(color), solid=solid, spec_trans=0.4 | |
| ) | |
| # axs.material.primitive_color_mix = 1.0 | |
| pts = [] | |
| xi, yi, zi = bb_min | |
| xa, ya, za = bb_max | |
| axs.points[:] = [ | |
| [[xi, yi, zi], [xa, yi, zi]], | |
| [[xi, yi, zi], [xi, ya, zi]], | |
| [[xi, yi, zi], [xi, yi, za]], # | |
| [[xi, ya, za], [xa, ya, za]], | |
| [[xi, ya, za], [xi, yi, za]], | |
| [[xi, ya, za], [xi, ya, zi]], # | |
| [[xa, ya, zi], [xi, ya, zi]], | |
| [[xa, ya, zi], [xa, yi, zi]], | |
| [[xa, ya, zi], [xa, ya, za]], # | |
| [[xa, yi, za], [xi, yi, za]], | |
| [[xa, yi, za], [xa, ya, za]], | |
| [[xa, yi, za], [xa, yi, zi]], # | |
| ] | |
| axs.radius[:] = radius | |
| axs.color[:] = [[[0.5, 0, 0], [0.5, 0, 0]]] * 12 | |
| def addBox( | |
| scene, | |
| center, | |
| spec=(1, 1, 1), | |
| color=gray_color, | |
| solid=0.0, | |
| outline_width=0.0, | |
| metal=0.0, | |
| specular=0.0, | |
| roughness=1.0, | |
| **kwargs | |
| ): | |
| X, Y, Z = spec[0], spec[1], spec[2] | |
| poly_info = fresnel.util.convex_polyhedron_from_vertices( | |
| [ | |
| [-X, -Y, -Z], | |
| [-X, -Y, Z], | |
| [-X, Y, -Z], | |
| [-X, Y, Z], | |
| [X, -Y, -Z], | |
| [X, -Y, Z], | |
| [X, Y, -Z], | |
| [X, Y, Z], | |
| ] | |
| ) | |
| geometry = fresnel.geometry.ConvexPolyhedron( | |
| scene, poly_info, position=center, outline_width=outline_width | |
| ) # 0.015) | |
| geometry.material = fresnel.material.Material( | |
| roughness=roughness, solid=solid, specular=specular, metal=metal, **kwargs | |
| ) | |
| # if len(color)!=3: | |
| geometry.material.primitive_color_mix = 1.0 | |
| geometry.material.color = fresnel.color.linear([1, 1, 1]) | |
| geometry.color[:] = color | |
| geometry.outline_material = fresnel.material.Material( | |
| color=fresnel.color.linear([0, 0, 0]), roughness=0.3, metal=0.0 | |
| ) | |
| # geometry.color[:] = color | |
| geometry.outline_material.primitive_color_mix = 0.7 | |
| geometry.outline_material.solid = 0.0 | |
| def addPlane( | |
| scene, center, up=(0, 1, 0), spec=(1, 1), color=white_color, solid=0.0, **kwargs | |
| ): | |
| X, Z = spec[0], spec[1] | |
| poly_info = np.array([[-X, 0, -Z], [X, 0, -Z], [X, 0, Z], [-X, 0, Z]]) | |
| vertices = poly_info[[0, 1, 3, 3, 1, 2]] | |
| geometry = fresnel.geometry.Mesh( | |
| scene, N=1, vertices=vertices, position=center, outline_width=0 | |
| ) | |
| geometry.material = fresnel.material.Material( | |
| roughness=1.0, specular=0.0, color=color, solid=solid | |
| ) | |
| geometry.material.primitive_color_mix = ( | |
| 0.0 # Set 0 to use the color specified in the Material, | |
| ) | |
| def get_cam2world(camera, lookat=np.array([0, 0, 0]), up=np.array([0, 1, 0])): | |
| shift = -camera | |
| z_axis = -lookat + camera # +z | |
| x_axis = np.cross(up, z_axis) | |
| y_axis = np.cross(z_axis, x_axis) | |
| x_axis = x_axis / np.sqrt(np.sum(x_axis**2)) | |
| y_axis = y_axis / np.sqrt(np.sum(y_axis**2)) | |
| z_axis = z_axis / np.sqrt(np.sum(z_axis**2)) | |
| rot = np.array([x_axis, y_axis, z_axis]).transpose() | |
| return shift, rot | |
| def world2camera(point, camera): | |
| point, camera = np.array(point), np.array(camera) | |
| shift, rot = get_cam2world(camera) | |
| rot = R.from_matrix(rot.transpose()) | |
| return rot.apply(point) | |
| def add_world_light(scene, direction, camera_pos, color, theta=1.0): | |
| world_dir = direction | |
| cam_dir = world2camera(world_dir, camera_pos) | |
| new_light = fresnel.light.Light(direction=cam_dir, color=color, theta=theta) | |
| scene.lights.append(new_light) | |
| return new_light | |
| def get_world_lights(directions, colors, thetas, camera_pos): | |
| lights = [] | |
| for i, direction in enumerate(directions): | |
| world_dir = direction | |
| cam_dir = world2camera(world_dir, camera_pos) | |
| new_light = fresnel.light.Light( | |
| direction=cam_dir, color=colors[i], theta=thetas[i] | |
| ) | |
| lights.append(new_light) | |
| return lights | |
| def old_renderMeshCloud( | |
| mesh=None, | |
| meshC=gray_color, | |
| mesh_outline_width=None, | |
| meshflat=False, # mesh settings | |
| cloud=None, | |
| cloudR=0.006, | |
| cloudC=None, # pc settings | |
| camPos=None, | |
| camLookat=None, | |
| camUp=np.array([0, 0, 1]), | |
| camHeight=1.0, # camera settings | |
| samples=32, | |
| axes=False, | |
| bbox=False, | |
| resolution=(1024, 1024), # render settings | |
| lights="rembrandt", | |
| **kwargs | |
| ): | |
| device = fresnel.Device() | |
| scene = fresnel.Scene(device) | |
| if mesh is not None and mesh["vert"].shape[0] > 0: | |
| mesh = fresnel.geometry.Mesh( | |
| scene, vertices=mesh["vert"][mesh["face"]].reshape(-1, 3), N=1 | |
| ) | |
| mesh.material = fresnel.material.Material( | |
| color=fresnel.color.linear(meshC), | |
| roughness=0.3, | |
| specular=1.0, | |
| spec_trans=0.0, | |
| ) | |
| if mesh_outline_width is not None: | |
| mesh.outline_width = mesh_outline_width | |
| if cloud is not None and cloud.shape[0] > 0: | |
| cloud = fresnel.geometry.Sphere(scene, position=cloud, radius=cloudR) | |
| solid = 0.7 if mesh is not None else 0.0 | |
| cloud_flat_color = gold_color | |
| if cloudC is not None and len(cloudC) == 3: | |
| cloud_flat_color = cloudC | |
| cloud.material = fresnel.material.Material( | |
| solid=solid, | |
| color=fresnel.color.linear(cloud_flat_color), | |
| roughness=1.0, | |
| specular=1.0, | |
| ) | |
| if cloudC is not None and len(cloudC) != 3: | |
| cloud.material.primitive_color_mix = 1.0 | |
| cloud.color[:] = fresnel.color.linear(plt.cm.plasma(cloudC)[:, :3]) | |
| if axes == True: | |
| addAxes(scene) | |
| if bbox == True: | |
| addBBox(scene) | |
| if camPos is None or camLookat is None: | |
| print("Fitting") | |
| scene.camera = fresnel.camera.fit(scene, margin=0) | |
| else: | |
| scene.camera = fresnel.camera.Orthographic(camPos, camLookat, camUp, camHeight) | |
| if lights == "cloudy": | |
| scene.lights = fresnel.light.cloudy() | |
| if lights == "rembrandt": | |
| scene.lights = fresnel.light.rembrandt() | |
| if lights == "lightbox": | |
| scene.lights = fresnel.light.lightbox() | |
| if lights == "loop": | |
| scene.lights = fresnel.light.loop() | |
| if lights == "butterfly": | |
| scene.lights = fresnel.light.butterfly() | |
| # scene.lights[0].theta = 3 | |
| tracer = fresnel.tracer.Path(device=device, w=resolution[0], h=resolution[1]) | |
| tracer.sample(scene, samples=samples, light_samples=32) | |
| # tracer.resize(w=450, h=450) | |
| # tracer.aa_level = 3 | |
| image = tracer.render(scene)[:] | |
| return image | |
| def renderMeshCloud( | |
| mesh=None, | |
| meshC=gray_color, | |
| mesh_outline_width=None, | |
| meshflat=False, # mesh settings | |
| cloud=None, | |
| cloudR=0.006, | |
| cloudC=None, # pc settings | |
| camPos=None, | |
| camLookat=None, | |
| camUp=np.array([0, 0, 1]), | |
| camHeight=1.0, # camera settings | |
| samples=32, | |
| axes=False, | |
| bbox=False, | |
| resolution=(1024, 1024), # render settings | |
| lights="rembrandt", | |
| **kwargs | |
| ): | |
| camera_opt = dict( | |
| resolution=resolution, | |
| samples=samples, | |
| camPos=camPos, | |
| camLookat=camLookat, | |
| camUp=camUp, | |
| camHeight=camHeight, | |
| ) | |
| renderer = FresnelRenderer(lights=lights, camera_kwargs=camera_opt) | |
| if axes == True: | |
| renderer.addAxes() | |
| if bbox == True: | |
| renderer.add_bbox() | |
| if mesh is not None and mesh["vert"].shape[0] > 0: | |
| renderer.add_mesh( | |
| mesh["vert"], mesh["face"], color=meshC, outline_width=mesh_outline_width | |
| ) | |
| if cloud is not None and cloud.shape[0] > 0: | |
| renderer.add_cloud(cloud, radius=cloudR, color=cloudC) | |
| image = renderer.render() | |
| return image | |
| def renderMeshCloud2( | |
| mesh=None, | |
| meshC=gray_color, | |
| mesh_outline_width=None, | |
| meshflat=False, # mesh settings | |
| cloud=None, | |
| cloudR=0.006, | |
| cloudC=None, # pc settings | |
| camHeight=1.0, # camera settings | |
| axes=False, | |
| bbox=False, # render settings | |
| camera_kwargs={}, | |
| **kwargs | |
| ): | |
| camera_opt = dflt_camera | |
| camera_opt.update(camera_kwargs) | |
| renderer = FresnelRenderer(camera_kwargs=camera_opt) | |
| if axes == True: | |
| renderer.addAxes() | |
| if bbox == True: | |
| renderer.addBBox() | |
| if mesh is not None and mesh["vert"].shape[0] > 0: | |
| renderer.add_mesh(mesh, color=meshC, outline_width=mesh_outline_width) | |
| if cloud is not None and cloud.shape[0] > 0: | |
| renderer.add_cloud(cloud, radius=cloudR, color=cloudC) | |
| image = renderer.render() | |
| return image | |
| def render_mesh( | |
| vert, face, camera_kwargs={}, render_kwargs={}, shadow_catcher=False, **kwargs | |
| ): | |
| renderer = FresnelRenderer(camera_kwargs=camera_kwargs) | |
| renderer.add_mesh(vert, face, **kwargs) | |
| if shadow_catcher == True: | |
| img = renderer.render( | |
| shadow_catcher=True, min_y=vert.min(axis=0)[1], **render_kwargs | |
| ) | |
| else: | |
| img = renderer.render(**render_kwargs) | |
| return img | |
| def render_cloud(cloud, camera_kwargs={}, render_kwargs={}, **kwargs): | |
| renderer = FresnelRenderer(camera_kwargs=camera_kwargs) | |
| renderer.add_cloud(cloud=cloud, **kwargs) | |
| img = renderer.render(**render_kwargs) | |
| return img | |
| class FresnelRenderer: | |
| def __init__(self, camera_kwargs={}, lights="rembrandt", **kwargs): | |
| self.setup_scene(camera_kwargs=camera_kwargs, lights=lights) | |
| def setup_scene(self, camera_kwargs={}, lights="rembrandt"): | |
| device = fresnel.Device() | |
| scene = fresnel.Scene(device) | |
| self.camera_opt = camera_opt = copy.deepcopy(dflt_camera) | |
| camera_opt.update(camera_kwargs) | |
| self.camera_kwargs = camera_opt | |
| if camera_opt["fit_camera"] == True: | |
| print("Camera is not setup, now auto-fit camera") | |
| scene.camera = fresnel.camera.fit(scene, margin=0) | |
| else: | |
| camPos = camera_opt["camPos"] | |
| camLookat = camera_opt["camLookat"] | |
| camUp = camera_opt["camUp"] | |
| camHeight = camera_opt["camHeight"] | |
| scene.camera = fresnel.camera.Orthographic( | |
| camPos, camLookat, camUp, camHeight | |
| ) | |
| # setup lightings | |
| if "lights" in camera_kwargs: | |
| lights = camera_kwargs["lights"] | |
| if type(lights) is not str: | |
| scene.lights = camera_kwargs["lights"] | |
| elif lights == "cloudy": | |
| scene.lights = fresnel.light.cloudy() | |
| elif lights == "rembrandt": | |
| scene.lights = fresnel.light.rembrandt() | |
| elif lights == "lightbox": | |
| scene.lights = fresnel.light.lightbox() | |
| elif lights == "loop": | |
| scene.lights = fresnel.light.loop() | |
| elif lights == "butterfly": | |
| scene.lights = fresnel.light.butterfly() | |
| elif lights == "up": | |
| scene.lights = get_world_lights( | |
| [np.array([0, 1, 0])], | |
| colors=[np.array([1, 1, 1])], | |
| thetas=[1.0], | |
| camera_pos=camPos, | |
| ) | |
| # addAxes(scene) | |
| # addBBox(scene) | |
| self.scene, self.device = scene, device | |
| def add_error_cloud(self, cloud, radius=0.006, color=None, solid=0.0, name=None): | |
| scene = self.scene | |
| cloud = fresnel.geometry.Sphere(scene, position=cloud, radius=radius) | |
| cloud_flat_color = gold_color | |
| if color is not None and len(color) == 3: | |
| cloud_flat_color = color | |
| cloud.material = fresnel.material.Material( | |
| solid=solid, | |
| color=fresnel.color.linear(cloud_flat_color), | |
| roughness=1.0, | |
| specular=0.0, | |
| ) | |
| if color is not None and len(color) != 3: | |
| cloud.material.primitive_color_mix = 1.0 | |
| cloud.color[:] = fresnel.color.linear(plt.cm.plasma(color)[:, :3]) | |
| def add_cloud( | |
| self, | |
| cloud, | |
| radius=0.006, | |
| color=None, | |
| solid=0.0, | |
| primitive_color_mix=1.0, | |
| cloud_flat_color=gold_color, | |
| roughness=0.2, | |
| specular=0.8, | |
| spec_trans=0.0, | |
| metal=0.0, | |
| name=None, | |
| ): | |
| scene = self.scene | |
| cloud = fresnel.geometry.Sphere(scene, position=cloud, radius=radius) | |
| if color is not None and len(color) == 3: | |
| cloud_flat_color = color | |
| cloud.material = fresnel.material.Material( | |
| solid=solid, | |
| color=fresnel.color.linear(cloud_flat_color), | |
| roughness=roughness, | |
| specular=specular, | |
| metal=metal, | |
| spec_trans=spec_trans, | |
| ) | |
| if color is not None and len(color) != 3: | |
| cloud.material.primitive_color_mix = primitive_color_mix | |
| cloud.color[:] = fresnel.color.linear(color) | |
| def add_mesh( | |
| self, | |
| vert, | |
| face, | |
| outline_width=None, | |
| name=None, | |
| color=gray_color, | |
| vert_color=None, | |
| solid=0.0, | |
| roughness=0.2, | |
| specular=0.8, | |
| spec_trans=0.0, | |
| metal=0.0, | |
| ): | |
| """vert_color: (Vn, 4)""" | |
| scene = self.scene | |
| mesh = fresnel.geometry.Mesh(scene, vertices=vert[face].reshape(-1, 3), N=1) | |
| mesh.material = fresnel.material.Material( | |
| color=fresnel.color.linear(color), | |
| solid=solid, | |
| roughness=roughness, | |
| specular=specular, | |
| spec_trans=spec_trans, | |
| metal=metal, | |
| ) | |
| if vert_color is not None: | |
| mesh.color[:] = fresnel.color.linear(vert_color) | |
| mesh.material.primitive_color_mix = 1.0 | |
| if outline_width is not None: | |
| mesh.outline_width = outline_width | |
| return self | |
| def add_light(self, direction=(0, 1, 0), color=(1, 1, 1), theta=3.14): | |
| self.scene.lights.append( | |
| fresnel.light.Light(direction=direction, color=color, theta=theta) | |
| ) | |
| def add_bbox(self, *args, **kwargs): | |
| addBBox(self.scene, *args, **kwargs) | |
| return self | |
| def add_box(self, *args, **kwargs): | |
| addBox(self.scene, *args, **kwargs) | |
| return self | |
| def add_plane(self, *args, **kwargs): | |
| addPlane(self.scene, *args, **kwargs) | |
| return self | |
| def compute_mask(self, min_y=None): | |
| scene = self.scene | |
| if min_y is None: | |
| min_y = scene.get_extents()[0, 1] | |
| # self.add_box(center=np.array([0,min_y-0.04,0]), spec=(100, 0.01, 100), color=black_color*0, solid=1.) | |
| # temp_lights = [light for light in scene.lights] | |
| # scene.lights.append( fresnel.light.Light(direction= np.array([0,1,0]), color=np.array([1,1,1])*10, theta=3.14) ) | |
| preview_tracer = fresnel.tracer.Preview( | |
| device=self.device, | |
| w=self.camera_kwargs["resolution"][0], | |
| h=self.camera_kwargs["resolution"][1], | |
| ) | |
| preview_img = np.array(preview_tracer.render(scene)[:]) | |
| mask = preview_img[..., 3] / 255 | |
| # del scene.geometry[-1] #.material.color = white_color | |
| # scene.lights = temp_lights | |
| # mask = (preview_img[...,:3].sum(axis=-1) != preview_img.min()) | |
| # mask = rgb2gray( rgba2rgb(preview_img) ) | |
| return mask | |
| def render( | |
| self, | |
| preview=False, | |
| shadow_catcher=False, | |
| invisible_catcher=False, | |
| min_y=None, | |
| shadow_percentile=80, | |
| shadow_strength=1.0, | |
| lights=None, | |
| ): | |
| scene = self.scene | |
| resolution = self.camera_opt["resolution"] | |
| samples = self.camera_opt["samples"] | |
| light_samples = self.camera_opt["light_samples"] | |
| # scene.lights[0].direction = np.array([.2,1,0.2]) | |
| if lights is not None: | |
| scene.lights = lights | |
| tracer = fresnel.tracer.Path( | |
| device=self.device, w=resolution[0], h=resolution[1] | |
| ) | |
| if preview == True: | |
| preview_tracer = fresnel.tracer.Preview( | |
| device=self.device, | |
| w=self.camera_kwargs["resolution"][0], | |
| h=self.camera_kwargs["resolution"][1], | |
| ) | |
| image = np.array(preview_tracer.render(scene)[:]) | |
| else: | |
| if shadow_catcher == True: | |
| mask = self.compute_mask(min_y) | |
| self.add_plane( | |
| center=np.array([0, min_y - 0.04, 0]), | |
| spec=(400, 400), | |
| color=white_color * 1.0, | |
| solid=0.0, | |
| ) | |
| # geos = scene.geometry | |
| # scene.geometry = [scene.geometry[-1]] | |
| # preview_tracer = fresnel.tracer.Preview(device=self.device, w=self.camera_kwargs["resolution"][0], h=self.camera_kwargs["resolution"][1]) | |
| # plane_img = np.array(preview_tracer.render(scene)[:]) | |
| # visutil.showImg(plane_img) | |
| # scene.geometry = geos | |
| tracer.sample(scene, samples=samples, light_samples=light_samples) | |
| image = tracer.render(scene)[:] | |
| if shadow_catcher == True: | |
| if invisible_catcher == True: | |
| del scene.geometry[-1] # .material.color = white_color | |
| self.add_box( | |
| center=np.array([0, min_y - 0.04, 0]), | |
| spec=(100, 0.01, 100), | |
| color=black_color * 0, | |
| solid=1.0, | |
| ) | |
| true_img = tracer.render(scene)[:] | |
| image[mask] = true_img[mask] | |
| grayscale = rgb2gray(rgba2rgb(image)) | |
| shadow_map = (1 - grayscale) * 255 # 255: opaque | |
| all_mask = image[..., 3] / 255 | |
| catcher_mask = np.maximum(mask, all_mask) - np.minimum(mask, all_mask) | |
| shadow_map = shadow_map / 255 * catcher_mask | |
| thresh = np.percentile(shadow_map.reshape(-1), shadow_percentile) | |
| shadow_map[shadow_map < thresh] = 0.0 | |
| shadow_map[shadow_map >= thresh] = ( | |
| (shadow_map[shadow_map >= thresh] - thresh) * 1 / (1 - thresh) | |
| ) ** shadow_strength | |
| image[..., 3] = ( | |
| image[..., 3] * (1 - catcher_mask) + shadow_map * 255 * catcher_mask | |
| ) | |
| return image | |