Full BSDF

This commit is contained in:
2026-08-07 02:04:01 -07:00
parent 7aeebf2008
commit fe017dd874
55 changed files with 2306 additions and 1215 deletions

View File

@ -192,13 +192,27 @@ def parse(nodes, con: ShaderContext,
import re as _re
def _extract_vec3(s):
m = _re.match(r'vec3\s*\(\s*([^,\s\)]+)\s*\)', s)
if m and ',' not in m.group(1):
v = m.group(1)
return v, v, v
m = _re.match(r'vec3\s*\(\s*([^,\s]+)\s*,\s*([^,\s]+)\s*,\s*([^,\s\)]+)', s)
if m:
return m.group(1), m.group(2), m.group(3)
s = s.strip()
m = _re.match(r'vec3\s*\((.*)\)\s*$', s)
if not m:
return '1.0', '1.0', '1.0'
inner = m.group(1).strip()
parts = []
depth = 0
start = 0
for i, ch in enumerate(inner):
if ch == '(':
depth += 1
elif ch == ')':
depth -= 1
elif ch == ',' and depth == 0:
parts.append(inner[start:i].strip())
start = i + 1
parts.append(inner[start:].strip())
if len(parts) == 1:
return parts[0], parts[0], parts[0]
if len(parts) == 3:
return parts[0], parts[1], parts[2]
return '1.0', '1.0', '1.0'
def _try_float(val_str, default=0.0):
@ -209,14 +223,10 @@ def parse(nodes, con: ShaderContext,
_sss_r, _sss_g, _sss_b = _extract_vec3(state.out_subsurface_radius)
_sss_scale = state.out_subsurface_scale
_sss_scale_f = _try_float(_sss_scale, 0.05)
if _sss_scale_f != 1.0:
_sss_r = str(_try_float(_sss_r, 1.0) * _sss_scale_f)
_sss_g = str(_try_float(_sss_g, 0.2) * _sss_scale_f)
_sss_b = str(_try_float(_sss_b, 0.1) * _sss_scale_f)
_sheen_tint_r, _sheen_tint_g, _sheen_tint_b = _extract_vec3(state.out_sheen_tint)
_coat_tint_r, _coat_tint_g, _coat_tint_b = _extract_vec3(state.out_coat_tint)
_spec_tint_r, _spec_tint_g, _spec_tint_b = _extract_vec3(state.out_specular_tint)
_sss_color_r, _sss_color_g, _sss_color_b = _extract_vec3(state.out_subsurface_color)
mat_state.features = {
@ -233,6 +243,7 @@ def parse(nodes, con: ShaderContext,
'sheenTintB': _sheen_tint_b,
'subsurface': state.out_subsurface,
'subsurfaceAnisotropy': state.out_subsurface_anisotropy,
'subsurfaceScale': _sss_scale,
'subsurfaceRadiusR': _sss_r,
'subsurfaceRadiusG': _sss_g,
'subsurfaceRadiusB': _sss_b,
@ -245,6 +256,9 @@ def parse(nodes, con: ShaderContext,
'transmissionRough': state.out_transmission_rough,
'ior': state.out_ior,
'thinWall': state.out_thin_wall,
'specularTintR': _spec_tint_r,
'specularTintG': _spec_tint_g,
'specularTintB': _spec_tint_b,
}
state = None
@ -751,11 +765,11 @@ def vector_curve(name, fac, points):
# Map vector
return 'mix({0}[{1}], {0}[{1} + 1], ({2} - {3}[{1}]) * (1.0 / ({3}[{1} + 1] - {3}[{1}]) ))'.format(ys_var, index_var, fac_var, facs_var)
def write_normal(inp):
def write_normal(inp, target_var='n'):
if inp.is_linked and inp.links[0].from_node.type != 'GROUP_INPUT':
normal_res = parse_vector_input(inp)
if normal_res != None:
state.curshader.write('n = {0};'.format(normal_res))
state.curshader.write('{0} = {1};'.format(target_var, normal_res))
def is_parsed(node_store_name: str):

View File

@ -77,17 +77,12 @@ def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket,
state.curshader.write('{0}float {1} = 1.0 - {2};'.format(prefix, fac_inv_var, fac_var))
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
sss_before_1 = mat_state.needs_sss
o1 = c.parse_shader_input(node.inputs[1])
sss_1 = mat_state.needs_sss
ek1 = mat_state.emission_type
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 1
o2 = c.parse_shader_input(node.inputs[2])
sss_2 = mat_state.needs_sss
ek2 = mat_state.emission_type
mat_state.needs_sss = sss_1 or sss_2
if state.parse_surface:
fm = '{0} * {3} + {1} * {2}' # fac mix template: a*fac_inv + b*fac
@ -126,17 +121,12 @@ def parse_mixshader(node: bpy.types.ShaderNodeMixShader, out_socket: NodeSocket,
def parse_addshader(node: bpy.types.ShaderNodeAddShader, out_socket: NodeSocket, state: ParserState) -> None:
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
sss_before_1 = mat_state.needs_sss
o1 = c.parse_shader_input(node.inputs[0])
sss_1 = mat_state.needs_sss
ek1 = mat_state.emission_type
mat_state.emission_type = mat_state.EmissionType.NO_EMISSION
mat_state.needs_sss = sss_before_1 # Reset to state before parsing input 0
o2 = c.parse_shader_input(node.inputs[1])
sss_2 = mat_state.needs_sss
ek2 = mat_state.emission_type
mat_state.needs_sss = sss_1 or sss_2
if state.parse_surface:
am = '{0} * 0.5 + {1} * 0.5' # add mix template (average)
@ -179,8 +169,6 @@ if bpy.app.version < (2, 91, 0):
if state.parse_surface:
c.write_normal(node.inputs['Normal'])
state.out_basecol = c.parse_vector_input(node.inputs['Base Color'])
if node.inputs[SUBSURFACE].is_linked or node.inputs[SUBSURFACE].default_value > 0.0:
mat_state.needs_sss = True
state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE])
state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS])
state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR])
@ -194,6 +182,9 @@ if bpy.app.version < (2, 91, 0):
state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT]))
state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT])
state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS])
coat_normal_socket = node.inputs.get(CLEARCOAT_NORMAL)
if coat_normal_socket is not None:
c.write_normal(coat_normal_socket, target_var='nCoat')
state.out_transmission = c.parse_value_input(node.inputs[TRANSMISSION])
trans_rough_socket = node.inputs.get(TRANSMISSION_ROUGHNESS)
if trans_rough_socket is not None:
@ -216,8 +207,6 @@ if bpy.app.version >= (2, 91, 0) and bpy.app.version < (4, 0, 0):
if state.parse_surface:
c.write_normal(node.inputs['Normal'])
state.out_basecol = c.parse_vector_input(node.inputs['Base Color'])
if node.inputs[SUBSURFACE].is_linked or node.inputs[SUBSURFACE].default_value > 0.0:
mat_state.needs_sss = True
state.out_subsurface = c.parse_value_input(node.inputs[SUBSURFACE])
state.out_subsurface_radius = c.parse_vector_input(node.inputs[SUBSURFACE_RADIUS])
state.out_subsurface_color = c.parse_vector_input(node.inputs[SUBSURFACE_COLOR])
@ -231,6 +220,9 @@ if bpy.app.version >= (2, 91, 0) and bpy.app.version < (4, 0, 0):
state.out_sheen_tint = 'vec3({})'.format(c.parse_value_input(node.inputs[SHEEN_TINT]))
state.out_clearcoat = c.parse_value_input(node.inputs[CLEARCOAT])
state.out_clearcoat_rough = c.parse_value_input(node.inputs[CLEARCOAT_ROUGHNESS])
coat_normal_socket = node.inputs.get(CLEARCOAT_NORMAL)
if coat_normal_socket is not None:
c.write_normal(coat_normal_socket, target_var='nCoat')
state.out_transmission = c.parse_value_input(node.inputs[TRANSMISSION])
state.out_transmission_rough = c.parse_value_input(node.inputs[TRANSMISSION_ROUGHNESS])
state.out_tangent = c.parse_vector_input(node.inputs[TANGENT])
@ -256,8 +248,6 @@ if bpy.app.version >= (4, 0, 0):
sss_input = node.inputs.get(SUBSURFACE)
if sss_input is not None:
if sss_input.is_linked or sss_input.default_value > 0.0:
mat_state.needs_sss = True
state.out_subsurface = c.parse_value_input(sss_input)
sss_radius_input = node.inputs.get(SUBSURFACE_RADIUS)
@ -331,6 +321,9 @@ if bpy.app.version >= (4, 0, 0):
coat_tint_socket = node.inputs.get(COAT_TINT)
if coat_tint_socket is not None:
state.out_coat_tint = c.parse_vector_input(coat_tint_socket)
coat_normal_socket = node.inputs.get(CLEARCOAT_NORMAL)
if coat_normal_socket is not None:
c.write_normal(coat_normal_socket, target_var='nCoat')
trans_socket = node.inputs.get(TRANSMISSION)
if trans_socket is not None:
@ -507,8 +500,6 @@ def parse_bsdfrefraction(node: bpy.types.ShaderNodeBsdfRefraction, out_socket: N
def parse_subsurfacescattering(node: bpy.types.ShaderNodeSubsurfaceScattering, out_socket: NodeSocket, state: ParserState) -> None:
if state.parse_surface:
# Mark that this material needs SSS
mat_state.needs_sss = True
c.write_normal(node.inputs['Normal'])
state.out_basecol = c.parse_vector_input(node.inputs['Color'])
state.out_subsurface = c.parse_value_input(node.inputs['Scale'])

View File

@ -588,7 +588,7 @@ def parse_tex_voronoi(node: bpy.types.ShaderNodeTexVoronoi, out_socket: bpy.type
co = 'bposition'
scale = c.get_value_input(node, ['Scale'])
exp = c.get_value_input(node, ['Exponent'])
exp = c.get_value_input(node, ['Exponent']) if m == 3 else '1.0'
randomness = c.get_value_input(node, ['Randomness'])
# Color or Position

View File

@ -64,8 +64,6 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
shader_data_name = material.lnx_custom_material
bind_constants = {'mesh': []}
bind_textures = {'mesh': []}
mat_uses_sss = False
make_shader.make_instancing_and_skinning(material, mat_users)
for idx, item in enumerate(material.lnx_bind_textures_list):
@ -82,11 +80,11 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
log.warn(f'Material "{material.name}": skipping export of bind texture at slot {idx + 1} ("{item.uniform_name}") with no image selected')
elif not wrd.lnx_batch_materials or material.name.startswith('lnxdefault'):
rpasses, shader_data, shader_data_name, bind_constants, bind_textures, mat_uses_sss = make_shader.build(material, mat_users, mat_lnxusers)
rpasses, shader_data, shader_data_name, bind_constants, bind_textures = make_shader.build(material, mat_users, mat_lnxusers)
sd = shader_data.sd
else:
result = mat_batch.get(material)
rpasses, shader_data, shader_data_name, bind_constants, bind_textures, mat_uses_sss = result
rpasses, shader_data, shader_data_name, bind_constants, bind_textures = result
sd = shader_data.sd
sss_used = False
@ -107,74 +105,144 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
if material.lnx_material_id != 0:
c['bind_constants'].append({'name': 'materialID', 'intValue': material.lnx_material_id})
if material.lnx_material_id == 2:
wrd.world_defs += '_Hair'
elif rpdat.rp_sss_state != 'Off':
const = {'name': 'materialID'}
# Use per-material SSS flag from shader build
if mat_uses_sss:
const['intValue'] = 2
sss_used = True
if '_SSS' not in wrd.world_defs:
wrd.world_defs += '_SSS'
else:
const['intValue'] = 0
c['bind_constants'].append(const)
# extended BRDF parameters as bind_constants
if rpdat.rp_renderer == 'Deferred':
feats = mat_state.features
def try_float(val_str, default=1.0):
import re as _re_mix
_mix_re = _re_mix.compile(r'^\s*([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*\w+\s*([+-])\s*([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*\w+\s*$')
_term_re = _re_mix.compile(r'([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*[^\s*]+(?:\s*\*\s*[^\s*]+)*')
_term_pair_re = _re_mix.compile(r'([+-]?\d*\.?\d+(?:[eE][+-]?\d+)?)\s*\*\s*([^\s*]+(?:\s*\*\s*[^\s*]+)*)')
def _parse_terms(val_str):
val_str = str(val_str).strip()
pairs = _term_pair_re.findall(val_str)
return [(float(c), v.replace(' ', '')) for c, v in pairs]
def eval_filtered_expr(val_str, filter_str):
if val_str is None or filter_str is None:
return None
filter_terms = _parse_terms(filter_str)
if len(filter_terms) < 2:
return None
filter_map = {}
for coeff, var_prod in filter_terms:
filter_map[var_prod] = coeff
val_terms = _parse_terms(val_str)
if len(val_terms) < 2:
return None
filtered_coeffs = []
for coeff, var_prod in val_terms:
fc = filter_map.get(var_prod)
if fc is not None and fc != 0.0:
filtered_coeffs.append(coeff)
if not filtered_coeffs:
return None
if all(c == filtered_coeffs[0] for c in filtered_coeffs):
return filtered_coeffs[0]
return None
def eval_mix_expr(val_str):
if val_str is None:
return None
val_str = str(val_str).strip()
try:
return float(val_str)
except (ValueError, TypeError):
return default
pass
m = _mix_re.match(val_str)
if m:
a = float(m.group(1))
op = m.group(2)
b = float(m.group(3))
if op == '+' and a == b:
return a
if op == '-' and a == b:
return 0.0
terms = _term_re.findall(val_str)
if terms and len(terms) >= 2:
coeffs = [float(t) for t in terms]
nonzero = [c for c in coeffs if c != 0.0]
if not nonzero:
return 0.0
if all(c == nonzero[0] for c in nonzero):
return nonzero[0]
return None
def feat_is_nonzero(val_str):
v = eval_mix_expr(val_str)
if v is None:
return True
return v != 0.0
def try_float(val_str, default=1.0):
v = eval_mix_expr(val_str)
return v if v is not None else default
def is_constant(val_str):
return eval_mix_expr(val_str) is not None
warned_nonconst = set()
def ext_bc(name, val_str, default):
const = is_constant(val_str)
if not const and name not in warned_nonconst:
warned_nonconst.add(name)
log.warn(f'material "{material.name}": deferred ext BRDF param "{name}" has non-constant expression "{val_str}", using default {default} - per-pixel texture-driven params require deferred texturing (Phase 4)')
c['bind_constants'].append({'name': name, 'floatValue': try_float(val_str, default), 'is_constant': const})
has_ext_brdf = False
if feats.get('clearcoat', '0.0') not in ('0.0', '0', ''):
c['bind_constants'].append({'name': 'clearcoat', 'floatValue': try_float(feats.get('clearcoat', '1.0'))})
c['bind_constants'].append({'name': 'clearcoatRough', 'floatValue': try_float(feats.get('clearcoatRough', '0.03'), 0.03)})
c['bind_constants'].append({'name': 'coatIOR', 'floatValue': try_float(feats.get('coatIOR', '1.5'), 1.5)})
c['bind_constants'].append({'name': 'coatTintR', 'floatValue': try_float(feats.get('coatTintR', '1.0'), 1.0)})
c['bind_constants'].append({'name': 'coatTintG', 'floatValue': try_float(feats.get('coatTintG', '1.0'), 1.0)})
c['bind_constants'].append({'name': 'coatTintB', 'floatValue': try_float(feats.get('coatTintB', '1.0'), 1.0)})
if feat_is_nonzero(feats.get('clearcoat', '0.0')):
ext_bc('clearcoat', feats.get('clearcoat', '1.0'), 1.0)
ext_bc('clearcoatRough', feats.get('clearcoatRough', '0.03'), 0.03)
ext_bc('coatIOR', feats.get('coatIOR', '1.5'), 1.5)
ext_bc('coatTintR', feats.get('coatTintR', '1.0'), 1.0)
ext_bc('coatTintG', feats.get('coatTintG', '1.0'), 1.0)
ext_bc('coatTintB', feats.get('coatTintB', '1.0'), 1.0)
if '_ClearCoat' not in wrd.world_defs:
wrd.world_defs += '_ClearCoat'
has_ext_brdf = True
if feats.get('sheen', '0.0') not in ('0.0', '0', ''):
c['bind_constants'].append({'name': 'sheen', 'floatValue': try_float(feats.get('sheen', '1.0'))})
c['bind_constants'].append({'name': 'sheenRough', 'floatValue': try_float(feats.get('sheenRough', '0.5'), 0.5)})
c['bind_constants'].append({'name': 'sheenTintR', 'floatValue': try_float(feats.get('sheenTintR', '1.0'), 1.0)})
c['bind_constants'].append({'name': 'sheenTintG', 'floatValue': try_float(feats.get('sheenTintG', '1.0'), 1.0)})
c['bind_constants'].append({'name': 'sheenTintB', 'floatValue': try_float(feats.get('sheenTintB', '1.0'), 1.0)})
if feat_is_nonzero(feats.get('sheen', '0.0')):
ext_bc('sheen', feats.get('sheen', '1.0'), 1.0)
ext_bc('sheenRough', feats.get('sheenRough', '0.5'), 0.5)
ext_bc('sheenTintR', feats.get('sheenTintR', '1.0'), 1.0)
ext_bc('sheenTintG', feats.get('sheenTintG', '1.0'), 1.0)
ext_bc('sheenTintB', feats.get('sheenTintB', '1.0'), 1.0)
if '_Sheen' not in wrd.world_defs:
wrd.world_defs += '_Sheen'
has_ext_brdf = True
if feats.get('subsurface', '0.0') not in ('0.0', '0', '') and rpdat.rp_sss_state != 'Off' and not mat_uses_sss:
c['bind_constants'].append({'name': 'subsurface', 'floatValue': try_float(feats.get('subsurface', '1.0'))})
c['bind_constants'].append({'name': 'subsurfaceAnisotropy', 'floatValue': try_float(feats.get('subsurfaceAnisotropy', '0.0'), 0.0)})
c['bind_constants'].append({'name': 'subsurfaceRadiusR', 'floatValue': try_float(feats.get('subsurfaceRadiusR', '1.0'), 1.0)})
c['bind_constants'].append({'name': 'subsurfaceRadiusG', 'floatValue': try_float(feats.get('subsurfaceRadiusG', '0.2'), 0.2)})
c['bind_constants'].append({'name': 'subsurfaceRadiusB', 'floatValue': try_float(feats.get('subsurfaceRadiusB', '0.1'), 0.1)})
c['bind_constants'].append({'name': 'subsurfaceColorR', 'floatValue': try_float(feats.get('subsurfaceColorR', '0.8'), 0.8)})
c['bind_constants'].append({'name': 'subsurfaceColorG', 'floatValue': try_float(feats.get('subsurfaceColorG', '0.8'), 0.8)})
c['bind_constants'].append({'name': 'subsurfaceColorB', 'floatValue': try_float(feats.get('subsurfaceColorB', '0.8'), 0.8)})
if '_Subsurface' not in wrd.world_defs:
wrd.world_defs += '_Subsurface'
if feat_is_nonzero(feats.get('subsurface', '0.0')) and rpdat.rp_sss_state != 'Off':
ext_bc('subsurface', feats.get('subsurface', '1.0'), 1.0)
ext_bc('subsurfaceAnisotropy', feats.get('subsurfaceAnisotropy', '0.0'), 0.0)
ext_bc('subsurfaceScale', feats.get('subsurfaceScale', '0.05'), 0.05)
ext_bc('subsurfaceRadiusR', feats.get('subsurfaceRadiusR', '1.0'), 1.0)
ext_bc('subsurfaceRadiusG', feats.get('subsurfaceRadiusG', '0.2'), 0.2)
ext_bc('subsurfaceRadiusB', feats.get('subsurfaceRadiusB', '0.1'), 0.1)
ext_bc('subsurfaceColorR', feats.get('subsurfaceColorR', '0.8'), 0.8)
ext_bc('subsurfaceColorG', feats.get('subsurfaceColorG', '0.8'), 0.8)
ext_bc('subsurfaceColorB', feats.get('subsurfaceColorB', '0.8'), 0.8)
if '_SSS' not in wrd.world_defs:
wrd.world_defs += '_SSS'
has_ext_brdf = True
if feats.get('anisotropy', '0.0') not in ('0.0', '0', ''):
c['bind_constants'].append({'name': 'anisotropy', 'floatValue': try_float(feats.get('anisotropy', '1.0'))})
c['bind_constants'].append({'name': 'anisoRot', 'floatValue': try_float(feats.get('anisoRot', '0.0'), 0.0)})
sss_used = True
if feat_is_nonzero(feats.get('anisotropy', '0.0')):
ext_bc('anisotropy', feats.get('anisotropy', '1.0'), 1.0)
ext_bc('anisoRot', feats.get('anisoRot', '0.0'), 0.0)
if '_Anisotropy' not in wrd.world_defs:
wrd.world_defs += '_Anisotropy'
has_ext_brdf = True
if feats.get('transmission', '0.0') not in ('0.0', '0', ''):
c['bind_constants'].append({'name': 'transmission', 'floatValue': try_float(feats.get('transmission', '1.0'))})
c['bind_constants'].append({'name': 'transmissionRough', 'floatValue': try_float(feats.get('transmissionRough', '0.0'), 0.0)})
c['bind_constants'].append({'name': 'ior', 'floatValue': try_float(feats.get('ior', '1.45'), 1.45)})
c['bind_constants'].append({'name': 'thinWall', 'floatValue': try_float(feats.get('thinWall', '0.0'), 0.0)})
if feat_is_nonzero(feats.get('transmission', '0.0')):
transm_str = feats.get('transmission', '1.0')
ext_bc('transmission', transm_str, 1.0)
ext_bc('transmissionRough', feats.get('transmissionRough', '0.0'), 0.0)
ior_str = feats.get('ior', '1.45')
if not is_constant(ior_str):
filtered = eval_filtered_expr(ior_str, transm_str)
if filtered is not None:
ior_str = str(filtered)
ext_bc('ior', ior_str, 1.45)
ext_bc('thinWall', feats.get('thinWall', '0.0'), 0.0)
if '_Transmission' not in wrd.world_defs:
wrd.world_defs += '_Transmission'
has_ext_brdf = True
@ -182,6 +250,11 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
if has_ext_brdf and '_ExtBRDF' not in wrd.world_defs:
wrd.world_defs += '_ExtBRDF'
if has_ext_brdf:
ext_bc('specularTintR', feats.get('specularTintR', '1.0'), 1.0)
ext_bc('specularTintG', feats.get('specularTintG', '1.0'), 1.0)
ext_bc('specularTintB', feats.get('specularTintB', '1.0'), 1.0)
# assign materialID for extended BRDF if not already set
# coexist with Scene.hx for last materialID in bind_constants
if has_ext_brdf and material.lnx_material_id == 0:
@ -191,6 +264,11 @@ def parse(material: Material, mat_data, mat_users: Dict[Material, List[Object]],
c['bind_constants'].append({'name': 'materialID', 'intValue': ext_id})
else:
log.warn(f'material "{material.name}": extended BRDF material limit (15) exceeded, extended BRDF will be disabled for this material')
has_ext_brdf = False
has_matid = any(bc.get('name') == 'materialID' for bc in c['bind_constants'] if isinstance(bc, dict))
if not has_matid:
c['bind_constants'].append({'name': 'materialID', 'intValue': 0})
# TODO: Mesh only material batching
if wrd.lnx_batch_materials:

View File

@ -77,6 +77,8 @@ def write(vert: shader.Shader, frag: shader.Shader):
if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True)
frag.add_uniform('mat4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
if is_shadows_atlas:
frag.add_uniform('vec4 tileBoundsSpotArray[maxLightsCluster]', link='_tileBoundsSpotArray', included=True)
frag.write('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {')
frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);')
@ -112,13 +114,13 @@ def write(vert: shader.Shader, frag: shader.Shader):
frag.add_uniform('vec3 eye', '_cameraPosition')
frag.write(', gbufferD, invVP, eye')
if '_ClearCoat' in wrd.world_defs:
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint')
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint, nCoat')
if '_Sheen' in wrd.world_defs:
frag.write(', sheen, sheenRough, sheenTint')
if '_Anisotropy' in wrd.world_defs:
frag.write(', anisotropy, anisoRot, tangent')
if '_Subsurface' in wrd.world_defs:
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy')
if '_SSS' in wrd.world_defs:
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius * subsurfaceScale, subsurfaceAnisotropy')
if '_Transmission' in wrd.world_defs:
frag.write(', transmission, transmissionRough, ior, thinWall')
frag.write(');')

View File

@ -74,8 +74,6 @@ def make(context_id, rpasses):
con['color_attachments'] = [attachment_format, attachment_format]
if '_gbuffer2' in wrd.world_defs:
con['color_attachments'].append(attachment_format)
if '_Anisotropy' in wrd.world_defs:
con['color_attachments'].append('RGBA64')
con_mesh = mat_state.data.add_context(con)
mat_state.con_mesh = con_mesh
@ -156,6 +154,8 @@ def make_base(con_mesh, parse_opacity):
vert.add_out('vec3 wnormal')
make_attrib.write_norpos(con_mesh, vert)
frag.write_attrib('vec3 n = normalize(wnormal);')
if '_ClearCoat' in wrd.world_defs:
frag.write_attrib('vec3 nCoat = vec3(0.0, 0.0, 0.0);')
if mat_state.material.lnx_two_sided:
frag.write('if (!gl_FrontFacing) n *= -1;') # Flip normal when drawing back-face
@ -254,16 +254,13 @@ def make_deferred(con_mesh, rpasses):
frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);')
is_shadeless = mat_state.emission_type == mat_state.EmissionType.SHADELESS
ext_brdf_defs = ('_ClearCoat', '_Sheen', '_Anisotropy', '_Subsurface', '_Transmission')
ext_brdf_defs = ('_ClearCoat', '_Sheen', '_Anisotropy', '_SSS', '_Transmission')
has_ext_brdf = any(d in wrd.world_defs for d in ext_brdf_defs)
if is_shadeless or '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs or has_ext_brdf:
if is_shadeless or has_ext_brdf:
frag.write('uint matid = 0;')
if is_shadeless:
frag.write('matid = 1;')
frag.write('basecol = emissionCol;')
if '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
frag.add_uniform('int materialID')
frag.write('if (materialID == 2) matid = 2;')
if has_ext_brdf:
frag.add_uniform('int materialID')
frag.write('if (materialID >= 3) matid = uint(materialID);')
@ -283,6 +280,14 @@ def make_deferred(con_mesh, rpasses):
if mat_state.material.lnx_ignore_irradiance:
frag.write('fragColor[GBUF_IDX_2].b = 1.0;')
if '_Anisotropy' in wrd.world_defs:
frag.write('#ifdef _Anisotropy')
if con_mesh.is_elem('tang'):
frag.write('fragColor[GBUF_IDX_2].a = encodeTangent(TBN[0], normalize(wnormal));')
else:
frag.write('fragColor[GBUF_IDX_2].a = -1.0;')
frag.write('#endif')
# Even if the material doesn't use emission we need to write to the
# emission buffer (if used) to prevent undefined behaviour
frag.write('#ifdef _EmissionShaded')
@ -290,14 +295,21 @@ def make_deferred(con_mesh, rpasses):
frag.write('#endif')
if '_SSRefraction' in wrd.world_defs or '_VoxelRefract' in wrd.world_defs:
frag.write('fragColor[GBUF_IDX_REFRACTION] = vec4(1.0, 0.0, 0.0, 1.0);')
frag.write('fragColor[GBUF_IDX_REFRACTION] = vec4(packIOR(1.0), 0.0, 0.0, 1.0);')
if '_Anisotropy' in wrd.world_defs:
frag.write('#ifdef _Anisotropy')
if con_mesh.is_elem('tang'):
frag.write('fragColor[GBUF_IDX_3] = vec4(normalize(TBN[0]), 0.0);')
else:
frag.write('fragColor[GBUF_IDX_3] = vec4(0.0, 0.0, 0.0, 0.0);')
if '_ClearCoat' in wrd.world_defs:
frag.write('#ifdef _ClearCoat')
frag.write('if (dot(nCoat, nCoat) > 0.0) {')
frag.write(' vec3 nCoatNorm = normalize(nCoat);')
frag.write(' nCoatNorm /= (abs(nCoatNorm.x) + abs(nCoatNorm.y) + abs(nCoatNorm.z));')
frag.write(' nCoatNorm.xy = nCoatNorm.z >= 0.0 ? nCoatNorm.xy : octahedronWrap(nCoatNorm.xy);')
frag.write(' fragColor[GBUF_IDX_COAT_NORMAL] = vec4(nCoatNorm.xy, 0.0, 0.0);')
frag.write('} else {')
frag.write(' vec3 nNorm = normalize(n);')
frag.write(' nNorm /= (abs(nNorm.x) + abs(nNorm.y) + abs(nNorm.z));')
frag.write(' nNorm.xy = nNorm.z >= 0.0 ? nNorm.xy : octahedronWrap(nNorm.xy);')
frag.write(' fragColor[GBUF_IDX_COAT_NORMAL] = vec4(nNorm.xy, 0.0, 0.0);')
frag.write('}')
frag.write('#endif')
return con_mesh
@ -403,6 +415,9 @@ def make_forward_mobile(con_mesh):
frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition')
if is_shadows_atlas:
frag.add_uniform('vec4 tileBoundsSunArray[maxLights * shadowmapCascades]', '_tileBoundsSunArray', included=True)
frag.write('tileBounds = tileBoundsSunArray[0];')
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias, opacity != 1.0);')
else:
frag.write('if (lightPosition.w > 0.0) {')
@ -582,7 +597,7 @@ def make_forward(con_mesh):
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
if rpdat.rp_ss_refraction or rpdat.lnx_voxelgi_refract:
frag.write(f'fragColor[2] = vec4(1.0, 0.0, 0.0, 1.0);')
frag.write(f'fragColor[2] = vec4(packIOR(1.0), 0.0, 0.0, 1.0);')
else:
frag.add_out('vec4 fragColor[1]')
@ -683,6 +698,8 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.write('vec3 albedo = surfaceAlbedo(basecol, metallic);')
frag.write('vec3 f0 = surfaceF0(basecol, metallic);')
if '_ExtBRDF' in wrd.world_defs:
frag.write('f0 = mix(f0, basecol, specularTint);')
if '_Brdf' in wrd.world_defs:
frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png')
@ -752,6 +769,18 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.write('}')
frag.write('vec3 direct = vec3(0.0);')
if '_ExtBRDF' in wrd.world_defs:
frag.write('#ifdef _ExtBRDF')
if '_Sheen' in wrd.world_defs:
frag.write('brdf_sheenWeight = sheenAttenuation(sheen, sheenRough, sheenTint, dotNV);')
if '_ClearCoat' in wrd.world_defs:
frag.write('brdf_coatWeight = coatAttenuation(clearcoat, coatIOR, nCoat, vVec);')
frag.write('brdf_coatTintAbsorb = coatTintAttenuation(clearcoat, coatTint, nCoat, vVec);')
frag.write('brdf_coatF0 = (coatIOR - 1.0) / (coatIOR + 1.0); brdf_coatF0 = brdf_coatF0 * brdf_coatF0;')
if '_Transmission' in wrd.world_defs:
frag.write('brdf_transmissionF0 = (ior - 1.0) / (ior + 1.0); brdf_transmissionF0 = brdf_transmissionF0 * brdf_transmissionF0;')
frag.write('#endif')
if '_Sun' in wrd.world_defs:
frag.add_uniform('vec3 sunCol', '_sunColor')
frag.add_uniform('vec3 sunDir', '_sunDirection')
@ -771,6 +800,9 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition')
if is_shadows_atlas:
frag.add_uniform('vec4 tileBoundsSunArray[maxLights * shadowmapCascades]', '_tileBoundsSunArray', included=True)
frag.write('tileBounds = tileBoundsSunArray[0];')
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun},')
if is_transparent_shadows:
frag.write(f'{shadowmap_sun_tr},')
@ -804,7 +836,21 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
if '_VoxelShadow' in wrd.world_defs:
frag.write('svisibility *= (1.0 - traceShadow(wposition, n, voxels, voxelsSDF, sunDir, clipmaps, gl_FragCoord.xy, velocity).r) * voxelgiShad;')
frag.write('}') # receiveShadow
frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;')
if '_ExtBRDF' in wrd.world_defs:
frag.write('float sunLayerWeight;')
frag.write('vec3 sdirect = applyExtBRDFLayers(')
frag.write(' lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular,')
frag.write(' albedo, f0, roughness, sdotNL, dotNV, sdotNH, sdotVH, n, sunDir, vVec, sh')
if '_ClearCoat' in wrd.world_defs:
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint, nCoat')
if '_Sheen' in wrd.world_defs:
frag.write(', sheen, sheenRough, sheenTint')
if '_Transmission' in wrd.world_defs:
frag.write(', transmission, transmissionRough, ior, thinWall')
frag.write(', sunLayerWeight);')
frag.write('direct += sdirect * sunCol * svisibility;')
else:
frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;')
# sun
if '_SinglePoint' in wrd.world_defs:
@ -846,13 +892,13 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
frag.add_uniform('vec3 eye', '_cameraPosition')
frag.write(', gbufferD, invVP, eye')
if '_ClearCoat' in wrd.world_defs:
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint')
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint, nCoat')
if '_Sheen' in wrd.world_defs:
frag.write(', sheen, sheenRough, sheenTint')
if '_Anisotropy' in wrd.world_defs:
frag.write(', anisotropy, anisoRot, tangent')
if '_Subsurface' in wrd.world_defs:
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy')
if '_SSS' in wrd.world_defs:
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius * subsurfaceScale, subsurfaceAnisotropy')
if '_Transmission' in wrd.world_defs:
frag.write(', transmission, transmissionRough, ior, thinWall')
frag.write(');')
@ -867,7 +913,7 @@ def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
if '_VoxelRefract' in wrd.world_defs and parse_opacity:
frag.write('if (opacity < 1.0) {')
frag.write(' vec3 refraction = traceRefraction(wposition, n, voxels, voxelsSDF, vVec, ior, roughness * roughness, clipmaps, gl_FragCoord.xy, velocity, opacity).rgb * (1.0 - F) * voxelgiRefr;')
frag.write(' vec3 refraction = traceRefraction(wposition, n, voxels, voxelsSDF, vVec, ior, roughness * roughness, clipmaps, gl_FragCoord.xy, velocity, opacity).rgb * (1.0 - F);')
frag.write(' indirect = mix(refraction, indirect, opacity);')
frag.write(' direct = mix(refraction, direct, opacity);')
frag.write('}')
@ -882,25 +928,27 @@ def _write_material_attribs_default(frag: shader.Shader, parse_opacity: bool):
# by the shader compiler
frag.write('vec3 emissionCol;')
# Extended BRDF parameters
frag.write('float subsurface = 0.0;')
frag.write('vec3 subsurfaceRadius = vec3(0.0);')
frag.write('vec3 subsurfaceColor = vec3(0.8);')
frag.write('vec3 specularTint = vec3(1.0);')
frag.write('float anisotropy = 0.0;')
frag.write('float anisoRot = 0.0;')
frag.write('float sheen = 0.0;')
frag.write('float sheenRough = 0.5;')
frag.write('vec3 sheenTint = vec3(1.0);')
frag.write('float clearcoat = 0.0;')
frag.write('float clearcoatRough = 0.03;')
frag.write('float transmission = 0.0;')
frag.write('float transmissionRough = 0.0;')
frag.write('float thinWall = 0.0;')
frag.write('vec3 tangent = vec3(0.0);')
frag.write('float subsurfaceScale = 0.05;')
frag.write('float subsurfaceAnisotropy = 0.0;')
frag.write('float coatIOR = 1.5;')
frag.write('vec3 coatTint = vec3(1.0);')
frag.write('float subsurface;')
frag.write('vec3 subsurfaceRadius;')
frag.write('vec3 subsurfaceColor;')
frag.write('vec3 specularTint;')
frag.write('float anisotropy;')
frag.write('float anisoRot;')
frag.write('float sheen;')
frag.write('float sheenRough;')
frag.write('vec3 sheenTint;')
frag.write('float clearcoat;')
frag.write('float clearcoatRough;')
frag.write('float transmission;')
frag.write('float transmissionRough;')
frag.write('float thinWall;')
frag.write('vec3 tangent;')
frag.write('float subsurfaceScale;')
frag.write('float subsurfaceAnisotropy;')
frag.write('float coatIOR;')
frag.write('vec3 coatTint;')
frag.write('float ior = 1.45;')
if parse_opacity:
frag.write('float opacity;')
frag.write('float ior = 1.45;')
else:
frag.write('float opacity = 1.0;')

View File

@ -40,34 +40,29 @@ def make(context_id):
# Remove fragColor = ...;
frag.main = frag.main[:frag.main.rfind('fragColor')]
frag.write('\n')
wrd = bpy.data.worlds['Lnx']
frag.write('if (opacity <= 0.0) discard;')
frag.write('n /= (abs(n.x) + abs(n.y) + abs(n.z));')
frag.write('n.xy = n.z >= 0.0 ? n.xy : octahedronWrap(n.xy);')
is_shadeless = mat_state.emission_type == mat_state.EmissionType.SHADELESS
if is_shadeless or '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
if is_shadeless:
frag.write('uint matid = 0;')
if is_shadeless:
frag.write('matid = 1;')
frag.write('basecol = emissionCol;')
if '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
frag.add_uniform('int materialID')
frag.write('if (materialID == 2) matid = 2;')
frag.write('matid = 1;')
frag.write('basecol = emissionCol;')
else:
frag.write('const uint matid = 0;')
if rpdat.rp_renderer == 'Deferred':
frag.write('fragColor[0] = vec4(n.xy, roughness, 1.0);')
frag.write('vec3 finalColor = direct + indirect;')
frag.write('fragColor[1] = vec4(finalColor * opacity, opacity);')
frag.write('fragColor[1] = vec4(finalColor * opacity, 1.0);')
else:
frag.write('vec3 finalColor = direct + indirect;')
frag.write('fragColor[0] = vec4(finalColor * opacity, opacity);')
frag.write('fragColor[0] = vec4(finalColor * opacity, 1.0);')
frag.write('fragColor[1] = vec4(n.xy, roughness, 1.0);')
frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, gl_FragCoord.z, 1.0);')
frag.write('fragColor[2] = vec4(packIOR(ior), 1.0 - opacity, gl_FragCoord.z, 1.0);')
# frag.write('fragColor[2] = vec4(ior, 1.0 - opacity, packFloat2(basecol.r, basecol.g), basecol.b);')
make_finalize.make(con_refract)

View File

@ -57,9 +57,6 @@ def build(material: Material, mat_users: Dict[Material, List[Object]], mat_lnxus
# Place empty material output to keep compiler happy..
mat_state.output_node = mat_state.nodes.new('ShaderNodeOutputMaterial')
# reset for each material
mat_state.needs_sss = False
wrd = bpy.data.worlds['Lnx']
rpdat = lnx.utils.get_rp()
rpasses = mat_utils.get_rpasses(material)
@ -133,9 +130,7 @@ def build(material: Material, mat_users: Dict[Material, List[Object]], mat_lnxus
shader_data_path = lnx.utils.get_fp_build() + '/compiled/Shaders/' + shader_data_name + '.lnx'
assets.add_shader_data(shader_data_path)
# Store SSS state in the return tuple so it's preserved per-material
needs_sss_result = mat_state.needs_sss
return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures, needs_sss_result
return rpasses, mat_state.data, shader_data_name, bind_constants, bind_textures
def write_shaders(rel_path: str, con: ShaderContext, rpass: str, matname: str) -> None:

View File

@ -69,42 +69,44 @@ def make_gi(context_id):
frag.add_include('std/gbuffer.glsl')
frag.add_include('std/brdf.glsl')
frag.add_include('std/aabb.glsl')
frag.write_header('#define _VoxelPass')
rpdat = lnx.utils.get_rp()
frag.add_uniform('layout(r32ui) uimage3D voxels')
frag.write('vec3 n;')
frag.write('vec3 nCoat = vec3(0.0, 0.0, 0.0);')
frag.write('vec3 wposition;')
frag.write('vec3 basecol;')
frag.write('float roughness;') #
frag.write('float metallic;') #
frag.write('float occlusion;') #
frag.write('float specular;') #
frag.write('vec3 emissionCol = vec3(0.0);')
frag.write('vec3 specularTint = vec3(1.0);')
frag.write('float subsurface = 0.0;')
frag.write('vec3 subsurfaceRadius = vec3(0.0);')
frag.write('vec3 subsurfaceColor = vec3(0.8);')
frag.write('float anisotropy = 0.0;')
frag.write('float anisoRot = 0.0;')
frag.write('float sheen = 0.0;')
frag.write('float sheenRough = 0.5;')
frag.write('vec3 sheenTint = vec3(1.0);')
frag.write('float clearcoat = 0.0;')
frag.write('float clearcoatRough = 0.03;')
frag.write('float transmission = 0.0;')
frag.write('float transmissionRough = 0.0;')
frag.write('float thinWall = 0.0;')
frag.write('vec3 tangent = vec3(0.0);')
frag.write('float subsurfaceScale = 0.05;')
frag.write('float subsurfaceAnisotropy = 0.0;')
frag.write('float coatIOR = 1.5;')
frag.write('vec3 coatTint = vec3(1.0);')
frag.write('vec3 emissionCol;')
frag.write('vec3 specularTint;')
frag.write('float subsurface;')
frag.write('vec3 subsurfaceRadius;')
frag.write('vec3 subsurfaceColor;')
frag.write('float anisotropy;')
frag.write('float anisoRot;')
frag.write('float sheen;')
frag.write('float sheenRough;')
frag.write('vec3 sheenTint;')
frag.write('float clearcoat;')
frag.write('float clearcoatRough;')
frag.write('float transmission;')
frag.write('float transmissionRough;')
frag.write('float thinWall;')
frag.write('vec3 tangent;')
frag.write('float subsurfaceScale;')
frag.write('float subsurfaceAnisotropy;')
frag.write('float coatIOR;')
frag.write('vec3 coatTint;')
blend = mat_state.material.lnx_blending
parse_opacity = blend or mat_utils.is_transluc(mat_state.material)
frag.write('float ior = 1.45;')
if parse_opacity:
frag.write('float opacity;')
frag.write('float ior;')
else:
frag.write('float opacity = 1.0;')
@ -271,10 +273,6 @@ def make_gi(context_id):
frag.write_attrib('vec3 vVec = normalize(eyeDir);')
frag.write_attrib('float dotNV = max(dot(N, vVec), 0.0);')
if '_Brdf' in wrd.world_defs:
frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png')
frag.write('vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;')
if '_Irr' in wrd.world_defs:
frag.add_include('std/shirr.glsl')
frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance')
@ -284,31 +282,12 @@ def make_gi(context_id):
else:
frag.write('vec3 envl = vec3(0.0);')
if '_Rad' in wrd.world_defs:
frag.add_uniform('sampler2D senvmapRadiance', link='_envmapRadiance')
frag.add_uniform('int envmapNumMipmaps', link='_envmapNumMipmaps')
frag.write('vec3 reflectionWorld = reflect(-vVec, N);')
frag.write('float lod = getMipFromRoughness(roughness, envmapNumMipmaps);')
frag.write('vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;')
if '_EnvLDR' in wrd.world_defs:
frag.write('envl = pow(envl, vec3(2.2));')
if '_Rad' in wrd.world_defs:
frag.write('prefilteredColor = pow(prefilteredColor, vec3(2.2));')
frag.write('envl *= albedo;')
if '_Brdf' in wrd.world_defs:
frag.write('vec3 F = f0 + (vec3(1.0) - f0) * pow(1.0 - abs(dot(N, vVec)), 5.0);')
frag.write('envl.rgb *= 1.0 - F;')
if '_Rad' in wrd.world_defs:
frag.write('envl += prefilteredColor * F;')
elif '_EnvCol' in wrd.world_defs:
frag.add_uniform('vec3 backgroundCol', link='_backgroundCol')
frag.write('envl += backgroundCol * F;')
frag.add_uniform('float envmapStrength', link='_envmapStrength')
frag.write('envl *= envmapStrength * occlusion;')
frag.write('envl *= envmapStrength * voxelgiEnv * occlusion;')
frag.add_include('std/light.glsl')
is_shadows = '_ShadowMap' in wrd.world_defs
@ -343,6 +322,9 @@ def make_gi(context_id):
frag.add_include('std/shadows.glsl')
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
frag.add_uniform('vec3 eye', '_cameraPosition')
if is_shadows_atlas:
frag.add_uniform('vec4 tileBoundsSunArray[maxLights * shadowmapCascades]', '_tileBoundsSunArray', included=True)
frag.write('tileBounds = tileBoundsSunArray[0];')
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun},')
if is_transparent_shadows:
frag.write(f'{shadowmap_sun_tr},')
@ -364,7 +346,7 @@ def make_gi(context_id):
frag.write(', false')
frag.write(');')
frag.write('}') # receiveShadow
frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;')
frag.write('direct += vec3(sdotNL) * sunCol * svisibility;')
# sun
if '_SinglePoint' in wrd.world_defs:
@ -397,13 +379,13 @@ def make_gi(context_id):
if '_Spot' in wrd.world_defs:
frag.write(', true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight')
if '_ClearCoat' in wrd.world_defs:
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint')
frag.write(', clearcoat, clearcoatRough, coatIOR, coatTint, N')
if '_Sheen' in wrd.world_defs:
frag.write(', sheen, sheenRough, sheenTint')
if '_Anisotropy' in wrd.world_defs:
frag.write(', anisotropy, anisoRot, tangent')
if '_Subsurface' in wrd.world_defs:
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy')
if '_SSS' in wrd.world_defs:
frag.write(', subsurface, subsurfaceColor, subsurfaceRadius * subsurfaceScale, subsurfaceAnisotropy')
if '_Transmission' in wrd.world_defs:
frag.write(', transmission, transmissionRough, ior, thinWall')
frag.write(');')
@ -462,6 +444,8 @@ def make_gi(context_id):
if is_transparent_shadows:
frag.add_uniform('sampler2D shadowMapSpotTransparent[4]', included=True)
frag.add_uniform('mat4 LWVPSpotArray[maxLightsCluster]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
if is_shadows_atlas:
frag.add_uniform('vec4 tileBoundsSpotArray[maxLightsCluster]', link='_tileBoundsSpotArray', included=True)
frag.write('for (int i = 0; i < min(numLights, maxLightsCluster); i++) {')
frag.write('int li = int(texelFetch(clustersData, ivec2(clusterI, i + 1), 0).r * 255);')
@ -488,13 +472,13 @@ def make_gi(context_id):
frag.write('\t, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w)') # scale
frag.write('\t, lightsArraySpot[li * 2 + 1].xyz') # right
if '_ClearCoat' in wrd.world_defs:
frag.write('\t, clearcoat, clearcoatRough, coatIOR, coatTint')
frag.write('\t, clearcoat, clearcoatRough, coatIOR, coatTint, N')
if '_Sheen' in wrd.world_defs:
frag.write('\t, sheen, sheenRough, sheenTint')
if '_Anisotropy' in wrd.world_defs:
frag.write('\t, anisotropy, anisoRot, tangent')
if '_Subsurface' in wrd.world_defs:
frag.write('\t, subsurface, subsurfaceColor, subsurfaceRadius, subsurfaceAnisotropy')
if '_SSS' in wrd.world_defs:
frag.write('\t, subsurface, subsurfaceColor, subsurfaceRadius * subsurfaceScale, subsurfaceAnisotropy')
if '_Transmission' in wrd.world_defs:
frag.write('\t, transmission, transmissionRough, ior, thinWall')
frag.write(' );')

View File

@ -38,6 +38,5 @@ texture_grad = False # Sample textures using textureGrad()
con_mesh = None # Mesh context
uses_instancing = False # Whether the current material has at least one user with instancing enabled
emission_type = EmissionType.NO_EMISSION
needs_sss = False
features = {} # tracks extended BRDF features used by current material
next_ext_mat_id = 3 # auto assigned materialID for extended BRDF

View File

@ -118,6 +118,7 @@ class ParserState:
self.out_subsurface_anisotropy: floatstr = '0.0'
self.out_coat_ior: floatstr = '1.5'
self.out_coat_tint: vec3str = 'vec3(1.0)'
self.out_coat_normal: vec3str = 'vec3(0.0)'
def reset_outs(self):
"""Reset the shader output values to their default values."""
@ -148,6 +149,7 @@ class ParserState:
self.out_subsurface_anisotropy = '0.0'
self.out_coat_ior = '1.5'
self.out_coat_tint = 'vec3(1.0)'
self.out_coat_normal = 'vec3(0.0)'
def get_outs(self) -> Tuple:
"""Return the shader output values as a tuple."""