toolkit/bake_gi/bake.py
"""
Precomputed GI using spherical harmonics.
"""
from __future__ import division, print_function
import os
import sys
os.chdir(os.path.realpath(os.path.dirname(__file__)))
sys.path.insert(0, "../../")
# Shouldn't import everything, but saves a lot of code
from panda3d.core import *
from direct.showbase.ShowBase import ShowBase
from rpcore.globals import Globals
from rpcore.render_target import RenderTarget
from rplibs.progressbar import ETA, ProgressBar, Percentage, Bar, Counter, Rate
class Application(ShowBase):
def __init__(self):
# Load settings
load_prc_file_data("", """
win-size 10 10
window-type offscreen
win-title GI Bake
textures-power-2 none
gl-coordinate-system default
sync-video #f
support-stencil #f
framebuffer-stencil #f
framebuffer-multisample #f
multisamples 0
gl-cube-map-seamless #t
gl-force-fbo-color #f
""")
ShowBase.__init__(self)
Globals.load(self)
Globals.resolution = LVecBase2i(1600, 900)
sun_vector = Vec3(0.2, 0.5, 1.2).normalized()
capture_resolution = 32
sun_shadow_map_resolution = 2048
num_probes = LVecBase3i(64)
divisor = 128
num_bakers = 8
padding = 0.5
for region in self.win.get_display_regions():
region.set_active(False)
print("Loading scene ...")
# model = loader.load_model("resources/test-scene.bam")
model = loader.load_model("scene/scene.bam")
# model = loader.load_model("scene/LivingRoom.egg")
model.reparent_to(render)
model.flatten_strong()
start_point, end_point = model.get_tight_bounds()
start_point -= padding
end_point += padding
diameter = (start_point - end_point).length() * 0.5
model_center = (start_point + end_point) * 0.5
model_size = end_point - start_point
print("Rendering sun shadow map ..")
sun_shadow_cam = Camera("SunShadowCamera")
sun_shadow_lens = OrthographicLens()
sun_shadow_lens.set_film_size(diameter * 2, diameter * 2)
sun_shadow_lens.set_near_far(0, 2 * diameter)
sun_shadow_cam.set_lens(sun_shadow_lens)
sun_shadow_cam_np = render.attach_new_node(sun_shadow_cam)
sun_shadow_cam_np.set_pos(model_center + sun_vector * diameter)
sun_shadow_cam_np.look_at(model_center)
sun_shadow_target = RenderTarget()
sun_shadow_target.size = sun_shadow_map_resolution
sun_shadow_target.add_depth_attachment(bits=32)
sun_shadow_target.prepare_render(sun_shadow_cam_np)
self.render_frame()
sun_shadow_target.active = False
shadow_mvp = self.get_mvp(sun_shadow_cam_np)
print("Computing first bounce ..")
# Target to store all results
max_probes = num_probes.x * num_probes.y * num_probes.z
if max_probes % divisor != 0:
print("WARNING: Bad divisor:", divisor, "for", max_probes)
num_rows = (max_probes + divisor - 1) // divisor
final_data = Texture("FinalProbeResult")
final_data.setup_2d_texture(6 * divisor, num_rows, Texture.T_float, Texture.F_rgba16)
final_data.set_clear_color(Vec4(1.0, 0.6, 0.2, 1.0))
worker_handles = []
store_shader = Shader.load(Shader.SL_GLSL, "resources/default.vert.glsl", "resources/copy_cubemap.frag.glsl")
convolute_shader = Shader.load(Shader.SL_GLSL, "resources/default.vert.glsl", "resources/convolute.frag.glsl")
for worked_id in range(num_bakers):
probe_position = Vec3(0, 0, 4)
capture_target = RenderTarget()
capture_target.size = capture_resolution * 6, capture_resolution
capture_target.add_depth_attachment(bits=16)
capture_target.add_color_attachment(bits=16, alpha=True)
capture_target.prepare_render(None)
# Remove all unused display regions
internal_buffer = capture_target.internal_buffer
internal_buffer.remove_all_display_regions()
internal_buffer.disable_clears()
internal_buffer.get_overlay_display_region().disable_clears()
# Setup the cubemap capture rig
directions = (Vec3(1, 0, 0), Vec3(-1, 0, 0), Vec3(0, 1, 0),
Vec3(0, -1, 0), Vec3(0, 0, 1), Vec3(0, 0, -1))
capture_regions = []
capture_cams = []
capture_rig = render.attach_new_node("CaptureRig")
capture_rig.set_pos(probe_position)
# Prepare the display regions
for i in range(6):
region = capture_target.internal_buffer.make_display_region(
i / 6, i / 6 + 1 / 6, 0, 1)
region.set_sort(25 + i)
region.set_active(True)
region.disable_clears()
# Set the correct clears
region.set_clear_depth_active(True)
region.set_clear_depth(1.0)
region.set_clear_color_active(True)
region.set_clear_color(Vec4(0.0, 0.0, 0.0, 0.0))
lens = PerspectiveLens()
lens.set_fov(90)
lens.set_near_far(0.05, 2 * diameter)
camera = Camera("CaptureCam-" + str(i), lens)
camera_np = capture_rig.attach_new_node(camera)
camera_np.look_at(camera_np, directions[i])
region.set_camera(camera_np)
capture_regions.append(region)
capture_cams.append(camera_np)
capture_cams[0].set_r(90)
capture_cams[1].set_r(-90)
capture_cams[3].set_r(180)
capture_cams[5].set_r(180)
destination_cubemap = Texture("TemporaryCubemap")
destination_cubemap.setup_cube_map(capture_resolution, Texture.T_float, Texture.F_rgba16)
# Target to convert the FBO to a cubemap
target_store_cubemap = RenderTarget()
target_store_cubemap.size = capture_resolution * 6, capture_resolution
target_store_cubemap.prepare_buffer()
target_store_cubemap.set_shader_inputs(
SourceTex=capture_target.color_tex,
DestTex=destination_cubemap)
target_store_cubemap.shader = store_shader
# Target to filter the data
store_pta = PTALVecBase2i.empty_array(1)
target_convolute = RenderTarget()
target_convolute.size = 6, 1
# target_convolute.add_color_attachment(bits=16)
target_convolute.prepare_buffer()
target_convolute.set_shader_inputs(
SourceTex=destination_cubemap,
DestTex=final_data,
storeCoord=store_pta)
target_convolute.shader = convolute_shader
# Set initial shader
shader = Shader.load(Shader.SL_GLSL,
"resources/first-bounce.vert.glsl", "resources/first-bounce.frag.glsl")
render.set_shader(shader)
render.set_shader_inputs(
ShadowMap=sun_shadow_target.depth_tex,
shadowMVP=shadow_mvp,
sunVector=sun_vector)
worker_handles.append((capture_rig, store_pta))
print("Preparing to render", max_probes, "probes ..")
widgets = [Counter(), " ", Bar(), " ", Percentage(), " ", ETA(), " ", Rate()]
progressbar = ProgressBar(widgets=widgets, maxval=max_probes).start()
progressbar.update(0)
work_queue = []
for z_pos in range(num_probes.z):
for y_pos in range(num_probes.y):
for x_pos in range(num_probes.x):
index = x_pos + y_pos * num_probes.x + z_pos * num_probes.y * num_probes.x
# print("Baking", index, "out of", max_probes)
offs_x = start_point.x + x_pos / (num_probes.x + 0.5) * model_size.x
offs_y = start_point.y + y_pos / (num_probes.y + 0.5) * model_size.y
offs_z = start_point.z + z_pos / (num_probes.z + 0.5) * model_size.z
store_x = index % divisor
store_y = index // divisor
work_queue.append((Vec3(offs_x, offs_y, offs_z), LVecBase2i(store_x, store_y)))
for i, (pos, store) in enumerate(work_queue):
worker_handles[i%num_bakers][0].set_pos(pos)
worker_handles[i%num_bakers][1][0] = store
if i % num_bakers == num_bakers - 1:
self.render_frame()
progressbar.update(i)
progressbar.finish()
self.render_frame()
print("Writing out data ..")
self.graphicsEngine.extract_texture_data(final_data, self.win.gsg)
final_data.write("raw-bake.png")
print("Writing out configuration")
with open("_bake_params.py", "w") as handle:
handle.write("#Autogenerated\n")
handle.write("from panda3d.core import LPoint3f, LVecBase3i, LVector3f\n")
handle.write("BAKE_MESH_START = " + str(start_point) + "\n")
handle.write("BAKE_MESH_END = " + str(end_point) + "\n")
handle.write("BAKE_MESH_PROBECOUNT = " + str(num_probes) + "\n")
handle.write("BAKE_DIVISOR = " + str(divisor) + "\n")
handle.write("BAKE_SUN_VECTOR = " + str(sun_vector) + "\n")
with open("_bake_params.glsl", "w") as handle:
handle.write("// Autogenerated\n")
handle.write("#define LPoint3f vec3\n")
handle.write("#define LVector3f vec3\n")
handle.write("#define LVecBase3i ivec3\n")
handle.write("const vec3 bake_mesh_start = " + str(start_point) + ";\n")
handle.write("const vec3 bake_mesh_end = " + str(end_point) + ";\n")
handle.write("const ivec3 bake_mesh_probecount = " + str(num_probes) + ";\n")
handle.write("const int bake_divisor = " + str(divisor) + ";\n")
handle.write("const vec3 sun_vector = " + str(sun_vector) + ";\n")
def render_frame(self):
""" Convenience function to render a frame """
self.graphicsEngine.render_frame()
def get_mvp(self, cam_node):
""" Computes the view-projection matrix of a camera """
return render.get_transform(cam_node).get_mat() * cam_node.node().get_lens().get_projection_mat()
Application()