Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementation of the pathreparam integrator with OptiX 7 #157

Draft
wants to merge 9 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,7 @@ macro (add_file DST SRC)
endmacro()

add_file(data/srgb.coeff ${CMAKE_BINARY_DIR}/ext_build/rgb2spec/srgb.coeff rgb2spec_opt_run)
add_file(data/vmf-hemisphere.data ${CMAKE_CURRENT_SOURCE_DIR}/src/integrators/vmf-hemisphere.data)

file(COPY resources/data/ior DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/dist/data)

Expand Down
111 changes: 111 additions & 0 deletions docs/examples/10_inverse_rendering/invert_heightfield.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import os
import time
import enoki as ek

import mitsuba
mitsuba.set_variant('gpu_autodiff_rgb')

from mitsuba.core import Thread, xml, UInt32, Float, Vector2f, Vector3f, Transform4f, ScalarTransform4f
from mitsuba.render import SurfaceInteraction3f
from mitsuba.python.util import traverse
from mitsuba.python.autodiff import render, write_bitmap, Adam

# Convert flat array into a vector of arrays (will be included in next enoki release)
def ravel(buf, dim = 3):
idx = dim * UInt32.arange(ek.slices(buf) // dim)
if dim == 2:
return Vector2f(ek.gather(buf, idx), ek.gather(buf, idx + 1))
elif dim == 3:
return Vector3f(ek.gather(buf, idx), ek.gather(buf, idx + 1), ek.gather(buf, idx + 2))

# Return contiguous flattened array (will be included in next enoki release)
def unravel(source, target, dim = 3):
idx = UInt32.arange(ek.slices(source))
for i in range(dim):
ek.scatter(target, source[i], dim * idx + i)

# Prepare output folder
output_path = "output/invert_heightfield/"
if not os.path.isdir(output_path):
os.makedirs(output_path)

# Load example scene
scene_folder = '../../../resources/data/docs/examples/invert_heightfield/'
Thread.thread().file_resolver().append(scene_folder)
scene = xml.load_file(scene_folder + 'scene.xml')

params = traverse(scene)
positions_buf = params['grid_mesh.vertex_positions_buf']
positions_initial = ravel(positions_buf)
normals_initial = ravel(params['grid_mesh.vertex_normals_buf'])
vertex_count = ek.slices(positions_initial)

# Create a texture with the reference displacement map
disp_tex = xml.load_dict({
"type" : "bitmap",
"filename" : "mitsuba_coin.jpg",
"to_uv" : ScalarTransform4f.scale([1, -1, 1]) # texture is upside-down
}).expand()[0]

# Create a fake surface interaction with an entry per vertex on the mesh
mesh_si = SurfaceInteraction3f.zero(vertex_count)
mesh_si.uv = ravel(params['grid_mesh.vertex_texcoords_buf'], dim=2)

# Evaluate the displacement map for the entire mesh
disp_tex_data_ref = disp_tex.eval_1(mesh_si)

# Apply displacement to mesh vertex positions and update scene (e.g. OptiX BVH)
def apply_displacement(amplitude = 0.05):
new_positions = disp_tex.eval_1(mesh_si) * normals_initial * amplitude + positions_initial
unravel(new_positions, params['grid_mesh.vertex_positions_buf'])
params.set_dirty('grid_mesh.vertex_positions_buf')
params.update()

# Apply displacement before generating reference image
apply_displacement()

# Render a reference image (no derivatives used yet)
image_ref = render(scene, spp=32)
crop_size = scene.sensors()[0].film().crop_size()
write_bitmap(output_path + 'out_ref.exr', image_ref, crop_size)
print("Write " + output_path + "out_ref.exr")

# Reset texture data to a constant
disp_tex_params = traverse(disp_tex)
disp_tex_params.keep(['data'])
disp_tex_params['data'] = ek.full(Float, 0.25, len(disp_tex_params['data']))
disp_tex_params.update()

# Construct an Adam optimizer that will adjust the texture parameters
opt = Adam(disp_tex_params, lr=0.002)

time_a = time.time()

iterations = 100
for it in range(iterations):
# Perform a differentiable rendering of the scene
image = render(scene,
optimizer=opt,
spp=4,
unbiased=True,
pre_render_callback=apply_displacement)

write_bitmap(output_path + 'out_%03i.exr' % it, image, crop_size)

# Objective: MSE between 'image' and 'image_ref'
ob_val = ek.hsum(ek.sqr(image - image_ref)) / len(image)

# Back-propagate errors to input parameters
ek.backward(ob_val)

# Optimizer: take a gradient step -> update displacement map
opt.step()

# Compare iterate against ground-truth value
err_ref = ek.hsum(ek.sqr(disp_tex_data_ref - disp_tex.eval_1(mesh_si)))
print('Iteration %03i: error=%g' % (it, err_ref[0]), end='\r')

time_b = time.time()

print()
print('%f ms per iteration' % (((time_b - time_a) * 1000) / iterations))
93 changes: 93 additions & 0 deletions docs/examples/10_inverse_rendering/invert_pose.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import os
import time
import enoki as ek

import mitsuba
mitsuba.set_variant('gpu_autodiff_rgb')

from mitsuba.core import xml, Thread, Transform4f, Bitmap, Float, Vector3f, UInt32
from mitsuba.python.util import traverse
from mitsuba.python.autodiff import render, write_bitmap, Adam

# Convert flat array into a vector of arrays (will be included in next enoki release)
def ravel(buf, dim = 3):
idx = dim * UInt32.arange(ek.slices(buf) // dim)
return Vector3f(ek.gather(buf, idx), ek.gather(buf, idx + 1), ek.gather(buf, idx + 2))

# Return contiguous flattened array (will be included in next enoki release)
def unravel(source, target, dim = 3):
idx = UInt32.arange(ek.slices(source))
for i in range(dim):
ek.scatter(target, source[i], dim * idx + i)

# Prepare output folder
output_path = "output/invert_pose/"
if not os.path.isdir(output_path):
os.makedirs(output_path)

# Load example scene
scene_folder = '../../../resources/data/docs/examples/invert_pose/'
Thread.thread().file_resolver().append(scene_folder)
scene = xml.load_file(scene_folder + 'scene.xml')

params = traverse(scene)
positions_buf = params['object.vertex_positions_buf']
positions_initial = ravel(positions_buf)

# Create differential parameter to be optimized
translate_ref = Vector3f(0.0)

# Create a new ParameterMap (or dict)
params_optim = {
"translate" : translate_ref,
}

# Construct an Adam optimizer that will adjust the translation parameters
opt = Adam(params_optim, lr=0.02)

# Apply the transformation to mesh vertex position and update scene (e.g. Optix BVH)
def apply_transformation():
trasfo = Transform4f.translate(params_optim["translate"])
new_positions = trasfo.transform_point(positions_initial)
unravel(new_positions, params['object.vertex_positions_buf'])
params.set_dirty('object.vertex_positions_buf')
params.update()

# Render a reference image (no derivatives used yet)
apply_transformation()
image_ref = render(scene, spp=32)
crop_size = scene.sensors()[0].film().crop_size()
write_bitmap(output_path + 'out_ref.exr', image_ref, crop_size)
print("Write " + output_path + "out_ref.exr")

# Move object before starting the optimization process
params_optim["translate"] = Vector3f(0.5, 0.2, -0.2)

time_a = time.time()

iterations = 100
for it in range(iterations):
# Perform a differentiable rendering of the scene
image = render(scene,
optimizer=opt,
spp=4,
unbiased=True,
pre_render_callback=apply_transformation)

write_bitmap(output_path + 'out_%03i.exr' % it, image, crop_size)

# Objective: MSE between 'image' and 'image_ref'
ob_val = ek.hsum(ek.sqr(image - image_ref)) / len(image)

# Back-propagate errors to input parameters
ek.backward(ob_val)

# Optimizer: take a gradient step -> update displacement map
opt.step()

print('Iteration %03i: error=%g' % (it, ob_val[0]), end='\r')

time_b = time.time()

print()
print('%f ms per iteration' % (((time_b - time_a) * 1000) / iterations))
Loading