Render (ir)radiance probes if no other probes are set

This commit is contained in:
Moritz Brückner 2021-04-02 01:59:28 +02:00
parent c5be90d0b0
commit 656b018e5f
4 changed files with 126 additions and 31 deletions

View file

@ -2858,16 +2858,9 @@ class ArmoryExporter:
rpdat = arm.utils.get_rp()
solid_mat = rpdat.arm_material_model == 'Solid'
arm_irradiance = rpdat.arm_irradiance and not solid_mat
arm_radiance = False
radtex = world.arm_envtex_name.rsplit('.', 1)[0]
arm_radiance = rpdat.arm_radiance
radtex = world.arm_envtex_name.rsplit('.', 1)[0] # Remove file extension
irrsharmonics = world.arm_envtex_irr_name
# Radiance
if '_EnvTex' in world.world_defs:
arm_radiance = rpdat.arm_radiance
elif '_EnvSky' in world.world_defs:
arm_radiance = rpdat.arm_radiance
radtex = 'hosek'
num_mips = world.arm_envtex_num_mips
strength = world.arm_envtex_strength

View file

@ -17,17 +17,59 @@ shader_datas = []
def build():
"""Builds world shaders for all exported worlds."""
global shader_datas
bpy.data.worlds['Arm'].world_defs = ''
wrd = bpy.data.worlds['Arm']
rpdat = arm.utils.get_rp()
mobile_mat = rpdat.arm_material_model == 'Mobile' or rpdat.arm_material_model == 'Solid'
envpath = os.path.join(arm.utils.get_fp_build(), 'compiled', 'Assets', 'envmaps')
wrd.world_defs = ''
worlds = []
shader_datas = []
for scene in bpy.data.scenes:
# Only export worlds from enabled scenes
if scene.arm_export and scene.world is not None and scene.world not in worlds:
worlds.append(scene.world)
create_world_shaders(scene.world)
with write_probes.setup_envmap_render():
for scene in bpy.data.scenes:
world = scene.world
# Only export worlds from enabled scenes and only once per world
if scene.arm_export and world is not None and world not in worlds:
worlds.append(world)
world.arm_envtex_name = ''
create_world_shaders(world)
if rpdat.arm_irradiance:
# Plain background color
if '_EnvCol' in world.world_defs:
world_name = arm.utils.safestr(world.name)
# Irradiance json file name
world.arm_envtex_name = world_name
world.arm_envtex_irr_name = world_name
write_probes.write_color_irradiance(world_name, world.arm_envtex_color)
# Render world to envmap for (ir)radiance, if no
# other probes are exported
elif world.arm_envtex_name == '':
write_probes.render_envmap(envpath, world)
filename = f'env_{arm.utils.safesrc(world.name)}'
image_file = f'{filename}.jpg'
image_filepath = os.path.join(envpath, image_file)
world.arm_envtex_name = image_file
world.arm_envtex_irr_name = os.path.basename(image_filepath).rsplit('.', 1)[0]
write_radiance = rpdat.arm_radiance and not mobile_mat
mip_count = write_probes.write_probes(image_filepath, True, world.arm_envtex_num_mips, write_radiance)
world.arm_envtex_num_mips = mip_count
if write_radiance:
# Set world def, everything else is handled by write_probes()
wrd.world_defs += '_Rad'
def create_world_shaders(world: bpy.types.World):
@ -131,14 +173,7 @@ def build_node_tree(world: bpy.types.World, frag: Shader, vert: Shader, con: Sha
col = world.color
world.arm_envtex_color = [col[0], col[1], col[2], 1.0]
world.arm_envtex_strength = 1.0
# Irradiance/Radiance: clear to color if no texture or sky is provided
if rpdat.arm_irradiance or rpdat.arm_irradiance:
if '_EnvSky' not in world.world_defs and '_EnvTex' not in world.world_defs and '_EnvImg' not in world.world_defs:
# Irradiance json file name
world.arm_envtex_name = world_name
world.arm_envtex_irr_name = world_name
write_probes.write_color_irradiance(world_name, world.arm_envtex_color)
world.world_defs += '_EnvCol'
# Clouds enabled
if rpdat.arm_clouds and world.arm_use_clouds:

View file

@ -294,6 +294,8 @@ def parse_tex_sky(node: bpy.types.ShaderNodeTexSky, out_socket: bpy.types.NodeSo
# Pass through
return c.to_vec3([0.0, 0.0, 0.0])
state.world.world_defs += '_EnvSky'
if node.sky_type == 'PREETHAM' or node.sky_type == 'HOSEK_WILKIE':
if node.sky_type == 'PREETHAM':
log.info('Info: Preetham sky model is not supported, using Hosek Wilkie sky model instead')
@ -315,7 +317,6 @@ def parse_sky_hosekwilkie(node: bpy.types.ShaderNodeTexSky, state: ParserState)
# Match to cycles
world.arm_envtex_strength *= 0.1
world.world_defs += '_EnvSky'
assets.add_khafile_def('arm_hosek')
curshader.add_uniform('vec3 A', link="_hosekA")
curshader.add_uniform('vec3 B', link="_hosekB")

View file

@ -1,23 +1,89 @@
import bpy
from contextlib import contextmanager
import math
import multiprocessing
import os
import sys
import subprocess
import json
import re
import arm.utils
import subprocess
import bpy
import arm.assets as assets
import arm.log as log
import arm.utils
def add_irr_assets(output_file_irr):
assets.add(output_file_irr + '.arm')
def add_rad_assets(output_file_rad, rad_format, num_mips):
assets.add(output_file_rad + '.' + rad_format)
for i in range(0, num_mips):
assets.add(output_file_rad + '_' + str(i) + '.' + rad_format)
# Generate probes from environment map
def write_probes(image_filepath, disable_hdr, cached_num_mips, arm_radiance=True):
@contextmanager
def setup_envmap_render():
"""Creates a background scene for rendering environment textures.
Use it as a context manager to automatically clean up on errors.
"""
rpdat = arm.utils.get_rp()
radiance_size = int(rpdat.arm_radiance_size)
# TODO: Add world option to render .hdr in the UI
# Render worlds in a different scene so that there are no other
# objects. The actual scene might be called differently if the name
# is already taken
scene = bpy.data.scenes.new("_arm_envmap_render")
scene.render.engine = "CYCLES"
scene.render.image_settings.file_format = "JPEG"
scene.render.image_settings.quality = 100
scene.render.resolution_x = radiance_size
scene.render.resolution_y = radiance_size // 2
# Set GPU as rendering device if the user enabled it
if bpy.context.preferences.addons["cycles"].preferences.compute_device_type == "CUDA":
scene.cycles.device = "GPU"
else:
log.info('Armory: Using CPU for environment render (might be slow). Enable CUDA if possible.')
# One sample is enough for world background only
scene.cycles.samples = 1
# Setup scene
cam = bpy.data.cameras.new("_arm_cam_envmap_render")
cam_obj = bpy.data.objects.new("_arm_cam_envmap_render", cam)
scene.collection.objects.link(cam_obj)
scene.camera = cam_obj
cam_obj.location = [0.0, 0.0, 0.0]
cam.type = "PANO"
cam.cycles.panorama_type = "EQUIRECTANGULAR"
cam_obj.rotation_euler = [math.radians(90), 0, math.radians(-90)]
try:
yield
finally:
bpy.data.objects.remove(cam_obj)
bpy.data.cameras.remove(cam)
bpy.data.scenes.remove(scene)
def render_envmap(target_dir: str, world: bpy.types.World):
"""Renders an environment texture for the given world into the
target_dir. Use in combination with setup_envmap_render()."""
scene = bpy.data.scenes["_arm_envmap_render"]
scene.world = world
render_path = os.path.join(target_dir, f"env_{arm.utils.safesrc(world.name)}.jpg")
scene.render.filepath = render_path
bpy.ops.render.render(write_still=True, scene=scene.name)
def write_probes(image_filepath: str, disable_hdr: bool, cached_num_mips: int, arm_radiance=True) -> int:
"""Generate probes from environment map and returns the mipmap count"""
envpath = arm.utils.get_fp_build() + '/compiled/Assets/envmaps'
if not os.path.exists(envpath):