Render (ir)radiance probes if no other probes are set
This commit is contained in:
parent
c5be90d0b0
commit
656b018e5f
|
@ -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
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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):
|
||||
|
|
Loading…
Reference in a new issue