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 | |