Full BSDF
This commit is contained in:
@ -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):
|
||||
|
||||
@ -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'])
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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(');')
|
||||
|
||||
@ -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;')
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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(' );')
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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."""
|
||||
|
||||
Reference in New Issue
Block a user