salad-demo / salad /utils /fresnelvis.py
DveloperY0115's picture
init repo
801501a
raw
history blame
19.8 kB
# 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