Skip to content

Commit

Permalink
Implement conclusion of discussion at: KhronosGroup/glTF#2214
Browse files Browse the repository at this point in the history
- Set `WATTS_TO_LUMENS` to 683 convention.
- Add compatibility modes for unitless pipelines and raw Blender units.
- Add explanation text. (Currently placeholder, may remove.)
- Renamed "Geometry" export subpanel to "Data".
  • Loading branch information
will-ca committed Oct 16, 2022
1 parent 6164ff9 commit 2308ce8
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 29 deletions.
87 changes: 68 additions & 19 deletions addons/io_scene_gltf2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ def reload_package_recursive(current_dir, module_dict):
BoolProperty,
EnumProperty,
IntProperty,
FloatProperty,
CollectionProperty)
from bpy.types import Operator
from bpy_extras.io_utils import ImportHelper, ExportHelper
Expand Down Expand Up @@ -279,6 +278,17 @@ def __init__(self):
default=False,
)

export_lighting_mode: EnumProperty(
name='Lighting Mode',
items=(
('SPEC', 'Standard', 'Export standard glTF lighting units'),
('COMPAT', 'Compatibility', 'Export unitless PBR lighting'),
('RAW', 'Raw', 'Export Blender lighting strengths directly'),
),
description='Optional backwards compatibility for non-standard render engines. Applies to lights',# TODO: and emissive materials',
default='SPEC'
)

export_colors: BoolProperty(
name='Vertex Colors',
description='Export vertex colors with meshes',
Expand Down Expand Up @@ -488,18 +498,6 @@ def __init__(self):
default=False
)

export_lights_factor: FloatProperty(
name='Lights Strength',
description='Different platforms scale brightness differently. Setting 1/600 here works well for many engines',
default=1.0
)

export_raw_lights: BoolProperty(
name='Raw Intensity',
description='Export raw light intensity, without converting to glTF units',
default=False
)

will_save_settings: BoolProperty(
name='Remember Export Settings',
description='Store glTF export settings in the Blender project',
Expand Down Expand Up @@ -667,8 +665,7 @@ def execute(self, context):
export_settings['gltf_morph_tangent'] = False

export_settings['gltf_lights'] = self.export_lights
export_settings['gltf_lights_factor'] = self.export_lights_factor
export_settings['gltf_raw_lights'] = self.export_raw_lights
export_settings['gltf_lighting_mode'] = self.export_lighting_mode

export_settings['gltf_binary'] = bytearray()
export_settings['gltf_binaryfilename'] = (
Expand Down Expand Up @@ -774,9 +771,6 @@ def draw(self, context):
col.prop(operator, 'export_extras')
col.prop(operator, 'export_cameras')
col.prop(operator, 'export_lights')
if operator.export_lights:
col.prop(operator, 'export_lights_factor')
col.prop(operator, 'export_raw_lights')


class GLTF_PT_export_transform(bpy.types.Panel):
Expand Down Expand Up @@ -807,7 +801,7 @@ def draw(self, context):
class GLTF_PT_export_geometry(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Geometry"
bl_label = "Data"
bl_parent_id = "FILE_PT_operator"
bl_options = {'DEFAULT_CLOSED'}

Expand Down Expand Up @@ -905,6 +899,59 @@ def draw(self, context):

layout.prop(operator, 'export_original_specular')

class GLTF_PT_export_geometry_lighting(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Lighting"
bl_parent_id = "GLTF_PT_export_geometry"
bl_options = {'DEFAULT_CLOSED'}

@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENE_OT_gltf"

def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.

sfile = context.space_data
operator = sfile.active_operator

layout.prop(operator, 'export_lighting_mode')

class GLTF_PT_export_geometry_lighting_info(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
bl_region_type = 'TOOL_PROPS'
bl_label = "Info"
bl_parent_id = "GLTF_PT_export_geometry_lighting"
bl_options = {'DEFAULT_CLOSED'}

@classmethod
def poll(cls, context):
sfile = context.space_data
operator = sfile.active_operator
return operator.bl_idname == "EXPORT_SCENE_OT_gltf"

def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False # No animation.

sfile = context.space_data
operator = sfile.active_operator

for line in (
"The GLTF specification requires lighting to be defined in candela, lux, and nits.",
"However, some viewers lorem ipsum.",
"Dolor sit amet, consectetur adipisci tempor incidunt ut labore et dolore magna aliqua.",
"Veniam, quis nostrud exercitation ullamcorpor s commodo consequat. Duis autem vel.",
"Eum irrure esse molestiae consequat, vel illum dolore eu fugi et iusto odio dignissim."
):
layout.label(text=line)


class GLTF_PT_export_geometry_compression(bpy.types.Panel):
bl_space_type = 'FILE_BROWSER'
Expand Down Expand Up @@ -1349,6 +1396,8 @@ def menu_func_import(self, context):
GLTF_PT_export_geometry_mesh,
GLTF_PT_export_geometry_material,
GLTF_PT_export_geometry_original_pbr,
GLTF_PT_export_geometry_lighting,
GLTF_PT_export_geometry_lighting_info,
GLTF_PT_export_geometry_compression,
GLTF_PT_export_animation,
GLTF_PT_export_animation_export,
Expand Down
22 changes: 13 additions & 9 deletions addons/io_scene_gltf2/blender/exp/gltf2_blender_gather_lights.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,25 @@ def __gather_intensity(blender_lamp, export_settings) -> Optional[float]:
emission_strength = emission_node.inputs["Strength"].default_value
else:
emission_strength = blender_lamp.energy
if export_settings['gltf_raw_lights']:
emission_luminous = emission_strength
if export_settings['gltf_lighting_mode'] == 'RAW':
return emission_strength
else:
# Assume at this point this is still in the appropriate watt-related SI unit, which if everything up to here was done with physical basis it hopefully should be.
WATTS_TO_LUMENS = 594
# At 580nm, according to WolframAlpha, which is probably as authoritative as a source as I can get for something so variable as physical power to perceptual brightness: https://www.wolframalpha.com/input?i=1+watt+in+lumens
# Assume at this point the computed strength is still in the appropriate watt-related SI unit, which if everything up to here was done with physical basis it hopefully should be.
WATTS_TO_LUMENS = 683
if blender_lamp.type == 'SUN': # W/m^2 in Blender to lm/m^2 for GLTF/KHR_lights_punctual.
emission_luminous = emission_strength * WATTS_TO_LUMENS
emission_luminous = emission_strength
else:
# Other than directional, only point and spot lamps are supported by GLTF.
# In Blender, points are omnidirectional W, and spots are specified as if they're points.
# Point and spot should both be lm/r^2 in GLTF.
emission_luminous = emission_strength / (4*math.pi) * WATTS_TO_LUMENS
return emission_luminous * export_settings['gltf_lights_factor']
# NOTE: The GLTF spec says light intensity is given in lumens, a unit of *perceptual* luminous flux. Perceptual luminance varies with wavelength. So in order to attempt to actually match the spec, we would have to multiply each RGB component by a different (cone cell) responsivity factor as we convert from watts, a unit of *physical* radiation. However, neither the Three.JS nor the Khronos Group reference GLTF model viewers currently technically implement that part of the spec correctly, and in my personal opinion in this PR a unit of perceptual brightness like "lumens" has no place in a model format designed for physically based workflows anyway (and may not have been intentional). I'll raise this issue on the KhronosGroup GLTF issue tracker: https://github.com/KhronosGroup/glTF/issues/2213
emission_luminous = emission_strength / (4*math.pi)
if export_settings['gltf_lighting_mode'] == 'SPEC':
emission_luminous *= WATTS_TO_LUMENS
elif export_settings['gltf_lighting_mode'] == 'COMPAT':
pass # Just so we have an exhaustive tree to catch bugged values.
else:
raise ValueError(export_settings['gltf_lighting_mode'])
return emission_luminous


def __gather_spot(blender_lamp, export_settings) -> Optional[gltf2_io_lights_punctual.LightSpot]:
Expand Down
2 changes: 1 addition & 1 deletion addons/io_scene_gltf2/blender/imp/gltf2_blender_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def create(gltf, vnode, light_id):
import_user_extensions('gather_import_light_before_hook', gltf, vnode, pylight)

if pylight['type'] == "directional":
light = BlenderLight.create_directional(gltf, light_id)
light = BlenderLight.create_directional(gltf, light_id) # ...Why not pass the pylight?
elif pylight['type'] == "point":
light = BlenderLight.create_point(gltf, light_id)
elif pylight['type'] == "spot":
Expand Down

0 comments on commit 2308ce8

Please sign in to comment.