2016-12-19 01:25:22 +01:00
|
|
|
import bpy
|
2021-08-04 22:49:38 +02:00
|
|
|
|
2017-12-20 15:37:58 +01:00
|
|
|
import arm.assets as assets
|
2017-03-15 12:30:14 +01:00
|
|
|
import arm.material.mat_state as mat_state
|
|
|
|
import arm.material.mat_utils as mat_utils
|
|
|
|
import arm.material.cycles as cycles
|
|
|
|
import arm.material.make_tess as make_tess
|
2017-09-27 00:04:47 +02:00
|
|
|
import arm.material.make_particle as make_particle
|
2018-12-10 18:18:32 +01:00
|
|
|
import arm.material.make_cluster as make_cluster
|
2018-12-14 15:27:43 +01:00
|
|
|
import arm.material.make_finalize as make_finalize
|
|
|
|
import arm.material.make_attrib as make_attrib
|
2017-03-15 12:30:14 +01:00
|
|
|
import arm.utils
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2021-08-11 14:32:21 +02:00
|
|
|
if arm.is_reload(__name__):
|
2021-08-04 22:49:38 +02:00
|
|
|
assets = arm.reload_module(assets)
|
|
|
|
mat_state = arm.reload_module(mat_state)
|
|
|
|
mat_utils = arm.reload_module(mat_utils)
|
|
|
|
cycles = arm.reload_module(cycles)
|
|
|
|
make_tess = arm.reload_module(make_tess)
|
|
|
|
make_particle = arm.reload_module(make_particle)
|
|
|
|
make_cluster = arm.reload_module(make_cluster)
|
|
|
|
make_finalize = arm.reload_module(make_finalize)
|
|
|
|
make_attrib = arm.reload_module(make_attrib)
|
|
|
|
arm.utils = arm.reload_module(arm.utils)
|
|
|
|
else:
|
2021-08-11 14:32:21 +02:00
|
|
|
arm.enable_reload(__name__)
|
2021-08-04 22:49:38 +02:00
|
|
|
|
2017-03-14 20:43:54 +01:00
|
|
|
is_displacement = False
|
2017-04-22 15:08:44 +02:00
|
|
|
write_material_attribs = None
|
2017-05-11 23:09:26 +02:00
|
|
|
write_material_attribs_post = None
|
|
|
|
write_vertex_attribs = None
|
2016-12-20 00:39:18 +01:00
|
|
|
|
2019-01-24 12:47:51 +01:00
|
|
|
def make(context_id, rpasses):
|
2020-05-06 18:11:02 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2017-10-24 13:13:26 +02:00
|
|
|
rpdat = arm.utils.get_rp()
|
|
|
|
rid = rpdat.rp_renderer
|
|
|
|
|
2017-09-21 23:47:49 +02:00
|
|
|
con = { 'name': context_id, 'depth_write': True, 'compare_mode': 'less', 'cull_mode': 'clockwise' }
|
2020-05-06 18:11:02 +02:00
|
|
|
|
2018-08-08 22:43:14 +02:00
|
|
|
# Blend context
|
|
|
|
mat = mat_state.material
|
|
|
|
blend = mat.arm_blending
|
2018-08-16 17:16:35 +02:00
|
|
|
particle = mat_state.material.arm_particle_flag
|
2018-08-16 12:10:50 +02:00
|
|
|
dprepass = rid == 'Forward' and rpdat.rp_depthprepass
|
2017-09-29 01:18:57 +02:00
|
|
|
if blend:
|
|
|
|
con['name'] = 'blend'
|
2018-08-08 22:43:14 +02:00
|
|
|
con['blend_source'] = mat.arm_blending_source
|
|
|
|
con['blend_destination'] = mat.arm_blending_destination
|
|
|
|
con['blend_operation'] = mat.arm_blending_operation
|
|
|
|
con['alpha_blend_source'] = mat.arm_blending_source_alpha
|
|
|
|
con['alpha_blend_destination'] = mat.arm_blending_destination_alpha
|
|
|
|
con['alpha_blend_operation'] = mat.arm_blending_operation_alpha
|
2017-09-29 01:18:57 +02:00
|
|
|
con['depth_write'] = False
|
2018-08-16 12:10:50 +02:00
|
|
|
con['compare_mode'] = 'less'
|
2018-08-16 17:16:35 +02:00
|
|
|
elif particle:
|
2018-08-16 12:10:50 +02:00
|
|
|
pass
|
|
|
|
elif dprepass: # Depth prepass was performed
|
2017-10-24 13:13:26 +02:00
|
|
|
con['depth_write'] = False
|
|
|
|
con['compare_mode'] = 'equal'
|
|
|
|
|
2020-05-10 10:46:12 +02:00
|
|
|
attachment_format = 'RGBA32' if '_LDR' in wrd.world_defs else 'RGBA64'
|
|
|
|
con['color_attachments'] = [attachment_format, attachment_format]
|
|
|
|
if '_gbuffer2' in wrd.world_defs:
|
|
|
|
con['color_attachments'].append(attachment_format)
|
2020-05-06 21:30:59 +02:00
|
|
|
|
2017-09-21 23:47:49 +02:00
|
|
|
con_mesh = mat_state.data.add_context(con)
|
2017-10-10 09:57:23 +02:00
|
|
|
mat_state.con_mesh = con_mesh
|
2016-12-20 00:39:18 +01:00
|
|
|
|
2017-09-29 01:18:57 +02:00
|
|
|
if rid == 'Forward' or blend:
|
2017-10-01 19:42:47 +02:00
|
|
|
if rpdat.arm_material_model == 'Mobile':
|
2017-10-01 19:09:09 +02:00
|
|
|
make_forward_mobile(con_mesh)
|
2017-10-01 19:42:47 +02:00
|
|
|
elif rpdat.arm_material_model == 'Solid':
|
|
|
|
make_forward_solid(con_mesh)
|
|
|
|
else:
|
|
|
|
make_forward(con_mesh)
|
2017-08-21 15:36:21 +02:00
|
|
|
elif rid == 'Deferred':
|
2019-01-24 12:47:51 +01:00
|
|
|
make_deferred(con_mesh, rpasses)
|
2018-11-19 13:18:40 +01:00
|
|
|
elif rid == 'Raytracer':
|
|
|
|
make_raytracer(con_mesh)
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2018-12-14 15:27:43 +01:00
|
|
|
make_finalize.make(con_mesh)
|
2017-03-21 03:06:38 +01:00
|
|
|
|
2018-01-04 11:37:27 +01:00
|
|
|
assets.vs_equal(con_mesh, assets.shader_cons['mesh_vert'])
|
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
return con_mesh
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2016-12-21 00:51:04 +01:00
|
|
|
def make_base(con_mesh, parse_opacity):
|
2017-03-14 20:43:54 +01:00
|
|
|
global is_displacement
|
2017-04-22 15:08:44 +02:00
|
|
|
global write_material_attribs
|
2017-05-11 23:09:26 +02:00
|
|
|
global write_material_attribs_post
|
|
|
|
global write_vertex_attribs
|
2019-01-23 18:09:53 +01:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2017-03-14 20:43:54 +01:00
|
|
|
|
2016-12-19 01:25:22 +01:00
|
|
|
vert = con_mesh.make_vert()
|
2016-12-21 00:51:04 +01:00
|
|
|
frag = con_mesh.make_frag()
|
2016-12-19 01:25:22 +01:00
|
|
|
geom = None
|
|
|
|
tesc = None
|
|
|
|
tese = None
|
|
|
|
|
2017-03-11 01:50:47 +01:00
|
|
|
vert.add_uniform('mat3 N', '_normalMatrix')
|
2018-12-14 15:27:43 +01:00
|
|
|
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2017-10-04 18:24:13 +02:00
|
|
|
vattr_written = False
|
2018-05-07 23:09:38 +02:00
|
|
|
rpdat = arm.utils.get_rp()
|
2017-10-04 18:24:13 +02:00
|
|
|
is_displacement = mat_utils.disp_linked(mat_state.output_node)
|
|
|
|
if is_displacement:
|
2018-05-07 23:09:38 +02:00
|
|
|
if rpdat.arm_rp_displacement == 'Vertex':
|
|
|
|
frag.ins = vert.outs
|
|
|
|
else: # Tessellation
|
|
|
|
tesc = con_mesh.make_tesc()
|
|
|
|
tese = con_mesh.make_tese()
|
|
|
|
tesc.ins = vert.outs
|
|
|
|
tese.ins = tesc.outs
|
|
|
|
frag.ins = tese.outs
|
|
|
|
make_tess.tesc_levels(tesc, rpdat.arm_tess_mesh_inner, rpdat.arm_tess_mesh_outer)
|
|
|
|
make_tess.interpolate(tese, 'wposition', 3, declare_out=True)
|
|
|
|
make_tess.interpolate(tese, 'wnormal', 3, declare_out=True, normalize=True)
|
2016-12-19 01:25:22 +01:00
|
|
|
# No displacement
|
|
|
|
else:
|
|
|
|
frag.ins = vert.outs
|
2017-05-11 23:09:26 +02:00
|
|
|
if write_vertex_attribs != None:
|
2017-10-04 18:24:13 +02:00
|
|
|
vattr_written = write_vertex_attribs(vert)
|
2017-09-27 00:04:47 +02:00
|
|
|
|
2018-08-30 15:42:25 +02:00
|
|
|
frag.add_include('compiled.inc')
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2017-05-01 22:25:36 +02:00
|
|
|
written = False
|
2017-04-22 15:08:44 +02:00
|
|
|
if write_material_attribs != None:
|
2017-05-25 20:14:53 +02:00
|
|
|
written = write_material_attribs(con_mesh, frag)
|
2017-05-01 22:25:36 +02:00
|
|
|
if written == False:
|
2017-04-22 15:08:44 +02:00
|
|
|
frag.write('vec3 basecol;')
|
|
|
|
frag.write('float roughness;')
|
|
|
|
frag.write('float metallic;')
|
|
|
|
frag.write('float occlusion;')
|
2018-05-19 19:29:14 +02:00
|
|
|
frag.write('float specular;')
|
2019-01-23 18:09:53 +01:00
|
|
|
if '_Emission' in wrd.world_defs:
|
|
|
|
frag.write('float emission;')
|
2017-04-22 15:08:44 +02:00
|
|
|
if parse_opacity:
|
|
|
|
frag.write('float opacity;')
|
2017-05-25 16:48:41 +02:00
|
|
|
cycles.parse(mat_state.nodes, con_mesh, vert, frag, geom, tesc, tese, parse_opacity=parse_opacity)
|
2017-05-11 23:09:26 +02:00
|
|
|
if write_material_attribs_post != None:
|
2017-05-25 20:14:53 +02:00
|
|
|
write_material_attribs_post(con_mesh, frag)
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2021-03-25 22:02:59 +01:00
|
|
|
vert.add_out('vec3 wnormal')
|
|
|
|
make_attrib.write_norpos(con_mesh, vert)
|
|
|
|
frag.write_attrib('vec3 n = normalize(wnormal);')
|
|
|
|
|
2017-10-04 18:24:13 +02:00
|
|
|
if not is_displacement and not vattr_written:
|
2018-12-14 15:27:43 +01:00
|
|
|
make_attrib.write_vertpos(vert)
|
2017-10-04 18:24:13 +02:00
|
|
|
|
2021-07-21 00:01:27 +02:00
|
|
|
make_attrib.write_tex_coords(con_mesh, vert, frag, tese)
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2017-05-25 16:48:41 +02:00
|
|
|
if con_mesh.is_elem('col'):
|
2016-12-19 01:25:22 +01:00
|
|
|
vert.add_out('vec3 vcolor')
|
2019-01-07 10:42:45 +01:00
|
|
|
vert.write_attrib('vcolor = col.rgb;')
|
2020-11-12 20:09:25 +01:00
|
|
|
if tese is not None:
|
2016-12-19 01:25:22 +01:00
|
|
|
tese.write_pre = True
|
2016-12-20 00:39:18 +01:00
|
|
|
make_tess.interpolate(tese, 'vcolor', 3, declare_out=frag.contains('vcolor'))
|
2016-12-19 01:25:22 +01:00
|
|
|
tese.write_pre = False
|
|
|
|
|
2017-05-25 16:48:41 +02:00
|
|
|
if con_mesh.is_elem('tang'):
|
2020-11-12 20:09:25 +01:00
|
|
|
if tese is not None:
|
2016-12-19 01:25:22 +01:00
|
|
|
tese.add_out('mat3 TBN')
|
2020-11-12 20:09:25 +01:00
|
|
|
tese.write_attrib('vec3 wbitangent = normalize(cross(wnormal, wtangent));')
|
|
|
|
tese.write_attrib('TBN = mat3(wtangent, wbitangent, wnormal);')
|
2016-12-19 01:25:22 +01:00
|
|
|
else:
|
|
|
|
vert.add_out('mat3 TBN')
|
2020-11-12 20:09:25 +01:00
|
|
|
vert.write_attrib('vec3 tangent = normalize(N * tang.xyz);')
|
|
|
|
vert.write_attrib('vec3 bitangent = normalize(cross(wnormal, tangent));')
|
|
|
|
vert.write_attrib('TBN = mat3(tangent, bitangent, wnormal);')
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2018-05-07 23:09:38 +02:00
|
|
|
if is_displacement:
|
|
|
|
if rpdat.arm_rp_displacement == 'Vertex':
|
|
|
|
sh = vert
|
|
|
|
else:
|
|
|
|
sh = tese
|
2020-09-08 00:26:44 +02:00
|
|
|
if(con_mesh.is_elem('ipos')):
|
|
|
|
vert.write('wposition = vec4(W * spos).xyz;')
|
2018-05-07 23:09:38 +02:00
|
|
|
sh.add_uniform('mat4 VP', '_viewProjectionMatrix')
|
2019-02-04 21:47:46 +01:00
|
|
|
sh.write('wposition += wnormal * disp;')
|
2018-05-07 23:09:38 +02:00
|
|
|
sh.write('gl_Position = VP * vec4(wposition, 1.0);')
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2019-01-24 12:47:51 +01:00
|
|
|
def make_deferred(con_mesh, rpasses):
|
2016-12-21 00:51:04 +01:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2017-08-21 20:16:06 +02:00
|
|
|
rpdat = arm.utils.get_rp()
|
2017-07-08 15:12:37 +02:00
|
|
|
|
2017-08-21 12:17:55 +02:00
|
|
|
arm_discard = mat_state.material.arm_discard
|
2019-01-24 12:47:51 +01:00
|
|
|
parse_opacity = arm_discard or 'translucent' in rpasses
|
2017-07-08 15:12:37 +02:00
|
|
|
|
2017-08-13 20:28:06 +02:00
|
|
|
make_base(con_mesh, parse_opacity=parse_opacity)
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
frag = con_mesh.frag
|
2016-12-21 00:51:04 +01:00
|
|
|
vert = con_mesh.vert
|
2017-01-08 13:21:54 +01:00
|
|
|
tese = con_mesh.tese
|
2016-12-21 00:51:04 +01:00
|
|
|
|
2019-01-24 12:47:51 +01:00
|
|
|
if parse_opacity:
|
|
|
|
if arm_discard:
|
|
|
|
opac = mat_state.material.arm_discard_opacity
|
|
|
|
else:
|
2019-02-11 11:12:41 +01:00
|
|
|
opac = '0.9999' # 1.0 - eps
|
2017-07-08 15:12:37 +02:00
|
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
2017-07-05 23:26:13 +02:00
|
|
|
|
2017-06-23 15:47:51 +02:00
|
|
|
gapi = arm.utils.get_gapi()
|
2018-05-19 19:29:14 +02:00
|
|
|
if '_gbuffer2' in wrd.world_defs:
|
2019-01-30 17:45:54 +01:00
|
|
|
frag.add_out('vec4 fragColor[3]')
|
2018-05-19 19:29:14 +02:00
|
|
|
if '_Veloc' in wrd.world_defs:
|
|
|
|
if tese == None:
|
|
|
|
vert.add_uniform('mat4 prevWVP', link='_prevWorldViewProjectionMatrix')
|
|
|
|
vert.add_out('vec4 wvpposition')
|
|
|
|
vert.add_out('vec4 prevwvpposition')
|
|
|
|
vert.write('wvpposition = gl_Position;')
|
2018-08-09 18:17:15 +02:00
|
|
|
if is_displacement:
|
|
|
|
vert.add_uniform('mat4 invW', link='_inverseWorldMatrix')
|
2018-08-15 23:27:46 +02:00
|
|
|
vert.write('prevwvpposition = prevWVP * (invW * vec4(wposition, 1.0));')
|
2018-08-09 18:17:15 +02:00
|
|
|
else:
|
|
|
|
vert.write('prevwvpposition = prevWVP * spos;')
|
2018-05-19 19:29:14 +02:00
|
|
|
else:
|
|
|
|
tese.add_out('vec4 wvpposition')
|
|
|
|
tese.add_out('vec4 prevwvpposition')
|
|
|
|
tese.write('wvpposition = gl_Position;')
|
2018-08-16 09:07:44 +02:00
|
|
|
if is_displacement:
|
|
|
|
tese.add_uniform('mat4 invW', link='_inverseWorldMatrix')
|
|
|
|
tese.add_uniform('mat4 prevWVP', '_prevWorldViewProjectionMatrix')
|
|
|
|
tese.write('prevwvpposition = prevWVP * (invW * vec4(wposition, 1.0));')
|
|
|
|
else:
|
|
|
|
vert.add_uniform('mat4 prevW', link='_prevWorldMatrix')
|
|
|
|
vert.add_out('vec3 prevwposition')
|
|
|
|
vert.write('prevwposition = vec4(prevW * spos).xyz;')
|
|
|
|
tese.add_uniform('mat4 prevVP', '_prevViewProjectionMatrix')
|
|
|
|
make_tess.interpolate(tese, 'prevwposition', 3)
|
|
|
|
tese.write('prevwvpposition = prevVP * vec4(prevwposition, 1.0);')
|
2016-12-21 00:51:04 +01:00
|
|
|
else:
|
2019-01-30 17:45:54 +01:00
|
|
|
frag.add_out('vec4 fragColor[2]')
|
2016-12-21 00:51:04 +01:00
|
|
|
|
|
|
|
# Pack gbuffer
|
2017-12-20 10:19:44 +01:00
|
|
|
frag.add_include('std/gbuffer.glsl')
|
2017-07-05 23:26:13 +02:00
|
|
|
|
2017-08-21 12:17:55 +02:00
|
|
|
if mat_state.material.arm_two_sided:
|
2018-01-07 22:51:20 +01:00
|
|
|
frag.write('if (!gl_FrontFacing) n *= -1;') # Flip normal when drawing back-face
|
2017-07-05 23:26:13 +02:00
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
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);')
|
2020-05-06 18:11:02 +02:00
|
|
|
|
2019-01-24 12:47:51 +01:00
|
|
|
if '_Emission' in wrd.world_defs or '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
|
2019-07-07 22:02:07 +02:00
|
|
|
frag.write('uint matid = 0;')
|
2019-01-24 12:47:51 +01:00
|
|
|
if '_Emission' in wrd.world_defs:
|
2019-07-07 22:02:07 +02:00
|
|
|
frag.write('if (emission > 0) { basecol *= emission; matid = 1; }')
|
2019-01-24 12:47:51 +01:00
|
|
|
if '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
|
|
|
|
frag.add_uniform('int materialID')
|
2019-07-07 22:02:07 +02:00
|
|
|
frag.write('if (materialID == 2) matid = 2;')
|
2019-01-23 18:09:53 +01:00
|
|
|
else:
|
2019-07-07 22:02:07 +02:00
|
|
|
frag.write('const uint matid = 0;')
|
2019-01-24 12:47:51 +01:00
|
|
|
|
2019-07-14 16:45:34 +02:00
|
|
|
frag.write('fragColor[0] = vec4(n.xy, roughness, packFloatInt16(metallic, matid));')
|
2019-05-01 21:24:39 +02:00
|
|
|
frag.write('fragColor[1] = vec4(basecol, packFloat2(occlusion, specular));')
|
2018-05-19 19:29:14 +02:00
|
|
|
|
|
|
|
if '_gbuffer2' in wrd.world_defs:
|
|
|
|
if '_Veloc' in wrd.world_defs:
|
|
|
|
frag.write('vec2 posa = (wvpposition.xy / wvpposition.w) * 0.5 + 0.5;')
|
|
|
|
frag.write('vec2 posb = (prevwvpposition.xy / prevwvpposition.w) * 0.5 + 0.5;')
|
|
|
|
frag.write('fragColor[2].rg = vec2(posa - posb);')
|
2016-12-21 00:51:04 +01:00
|
|
|
|
2021-02-20 15:30:18 +01:00
|
|
|
if mat_state.material.arm_ignore_irradiance:
|
|
|
|
frag.write('fragColor[2].b = 1.0;')
|
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
return con_mesh
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2018-11-19 13:18:40 +01:00
|
|
|
def make_raytracer(con_mesh):
|
2019-04-04 11:23:18 +02:00
|
|
|
con_mesh.data['vertex_elements'] = [{'name': 'pos', 'data': 'float3'}, {'name': 'nor', 'data': 'float3'}, {'name': 'tex', 'data': 'float2'}]
|
2018-07-29 11:39:54 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
vert = con_mesh.make_vert()
|
|
|
|
frag = con_mesh.make_frag()
|
|
|
|
vert.add_out('vec3 n')
|
2019-04-04 11:23:18 +02:00
|
|
|
vert.add_out('vec2 uv')
|
2018-07-29 11:39:54 +02:00
|
|
|
vert.write('n = nor;')
|
2019-04-04 11:23:18 +02:00
|
|
|
vert.write('uv = tex;')
|
2018-12-14 15:27:43 +01:00
|
|
|
vert.write('gl_Position = vec4(pos.xyz, 1.0);')
|
2017-03-23 13:18:13 +01:00
|
|
|
|
2017-10-01 19:09:09 +02:00
|
|
|
def make_forward_mobile(con_mesh):
|
2017-04-26 14:21:22 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
vert = con_mesh.make_vert()
|
|
|
|
frag = con_mesh.make_frag()
|
|
|
|
geom = None
|
|
|
|
tesc = None
|
|
|
|
tese = None
|
|
|
|
|
|
|
|
vert.add_uniform('mat3 N', '_normalMatrix')
|
2018-12-14 15:27:43 +01:00
|
|
|
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
2017-04-26 14:21:22 +02:00
|
|
|
frag.ins = vert.outs
|
2018-11-14 21:51:30 +01:00
|
|
|
|
2018-08-30 15:42:25 +02:00
|
|
|
frag.add_include('compiled.inc')
|
2017-04-26 14:21:22 +02:00
|
|
|
frag.write('vec3 basecol;')
|
2017-09-09 13:46:32 +02:00
|
|
|
frag.write('float roughness;')
|
|
|
|
frag.write('float metallic;')
|
|
|
|
frag.write('float occlusion;')
|
2018-05-19 19:29:14 +02:00
|
|
|
frag.write('float specular;')
|
2019-01-23 18:09:53 +01:00
|
|
|
if '_Emission' in wrd.world_defs:
|
|
|
|
frag.write('float emission;')
|
2018-11-14 22:28:19 +01:00
|
|
|
|
|
|
|
arm_discard = mat_state.material.arm_discard
|
|
|
|
blend = mat_state.material.arm_blending
|
|
|
|
is_transluc = mat_utils.is_transluc(mat_state.material)
|
|
|
|
parse_opacity = (blend and is_transluc) or arm_discard
|
|
|
|
if parse_opacity:
|
|
|
|
frag.write('float opacity;')
|
|
|
|
|
|
|
|
cycles.parse(mat_state.nodes, con_mesh, vert, frag, geom, tesc, tese, parse_opacity=parse_opacity, parse_displacement=False)
|
|
|
|
|
|
|
|
if arm_discard:
|
|
|
|
opac = mat_state.material.arm_discard_opacity
|
|
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
2017-04-26 14:21:22 +02:00
|
|
|
|
2021-07-21 00:01:27 +02:00
|
|
|
make_attrib.write_tex_coords(con_mesh, vert, frag, tese)
|
2017-04-26 14:21:22 +02:00
|
|
|
|
2017-05-25 16:48:41 +02:00
|
|
|
if con_mesh.is_elem('col'):
|
2017-04-26 14:21:22 +02:00
|
|
|
vert.add_out('vec3 vcolor')
|
2019-01-07 10:42:45 +01:00
|
|
|
vert.write('vcolor = col.rgb;')
|
2017-04-26 14:21:22 +02:00
|
|
|
|
2018-08-01 11:46:38 +02:00
|
|
|
if con_mesh.is_elem('tang'):
|
|
|
|
vert.add_out('mat3 TBN')
|
2018-12-14 15:27:43 +01:00
|
|
|
make_attrib.write_norpos(con_mesh, vert, declare=True)
|
2019-04-04 11:23:53 +02:00
|
|
|
vert.write('vec3 tangent = normalize(N * tang.xyz);')
|
2018-08-01 11:46:38 +02:00
|
|
|
vert.write('vec3 bitangent = normalize(cross(wnormal, tangent));')
|
|
|
|
vert.write('TBN = mat3(tangent, bitangent, wnormal);')
|
|
|
|
else:
|
|
|
|
vert.add_out('vec3 wnormal')
|
2018-12-14 15:27:43 +01:00
|
|
|
make_attrib.write_norpos(con_mesh, vert)
|
2018-08-01 11:46:38 +02:00
|
|
|
frag.write_attrib('vec3 n = normalize(wnormal);')
|
2017-04-26 14:21:22 +02:00
|
|
|
|
2021-05-26 12:42:11 +02:00
|
|
|
make_attrib.write_vertpos(vert)
|
|
|
|
|
2017-12-20 10:19:44 +01:00
|
|
|
frag.add_include('std/math.glsl')
|
|
|
|
frag.add_include('std/brdf.glsl')
|
2017-04-26 14:21:22 +02:00
|
|
|
|
2017-10-16 09:25:18 +02:00
|
|
|
frag.add_out('vec4 fragColor')
|
|
|
|
blend = mat_state.material.arm_blending
|
|
|
|
if blend:
|
2018-11-14 22:28:19 +01:00
|
|
|
if parse_opacity:
|
|
|
|
frag.write('fragColor = vec4(basecol, opacity);')
|
|
|
|
else:
|
|
|
|
frag.write('fragColor = vec4(basecol, 1.0);')
|
2017-10-16 09:25:18 +02:00
|
|
|
return
|
|
|
|
|
2018-11-22 11:08:03 +01:00
|
|
|
is_shadows = '_ShadowMap' in wrd.world_defs
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
|
|
|
|
shadowmap_sun = 'shadowMap'
|
|
|
|
if is_shadows_atlas:
|
2021-03-02 23:57:40 +01:00
|
|
|
is_single_atlas = '_SingleAtlas' in wrd.world_defs
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
shadowmap_sun = 'shadowMapAtlasSun' if not is_single_atlas else 'shadowMapAtlas'
|
|
|
|
frag.add_uniform('vec2 smSizeUniform', '_shadowMapSize', included=True)
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.write('vec3 direct = vec3(0.0);')
|
|
|
|
|
|
|
|
if '_Sun' in wrd.world_defs:
|
|
|
|
frag.add_uniform('vec3 sunCol', '_sunColor')
|
|
|
|
frag.add_uniform('vec3 sunDir', '_sunDirection')
|
|
|
|
frag.write('float svisibility = 1.0;')
|
|
|
|
frag.write('float sdotNL = max(dot(n, sunDir), 0.0);')
|
|
|
|
if is_shadows:
|
|
|
|
vert.add_out('vec4 lightPosition')
|
2021-03-29 15:41:46 +02:00
|
|
|
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrixSun')
|
2019-02-10 20:37:38 +01:00
|
|
|
vert.write('lightPosition = LWVP * spos;')
|
2020-07-13 23:20:58 +02:00
|
|
|
frag.add_uniform('bool receiveShadow')
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}')
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
|
2020-07-13 23:20:58 +02:00
|
|
|
|
|
|
|
frag.write('if (receiveShadow) {')
|
2018-12-15 19:03:11 +01:00
|
|
|
if '_CSM' in wrd.world_defs:
|
|
|
|
frag.add_include('std/shadows.glsl')
|
|
|
|
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
|
|
|
|
frag.add_uniform('vec3 eye', '_cameraPosition')
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias);')
|
2018-12-15 19:03:11 +01:00
|
|
|
else:
|
|
|
|
frag.write('if (lightPosition.w > 0.0) {')
|
|
|
|
frag.write(' vec3 lPos = lightPosition.xyz / lightPosition.w;')
|
2019-02-10 20:37:38 +01:00
|
|
|
if '_Legacy' in wrd.world_defs:
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.write(f' svisibility = float(texture({shadowmap_sun}, vec2(lPos.xy)).r > lPos.z - shadowsBias);')
|
2019-02-10 20:37:38 +01:00
|
|
|
else:
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.write(f' svisibility = texture({shadowmap_sun}, vec3(lPos.xy, lPos.z - shadowsBias)).r;')
|
2018-12-15 19:03:11 +01:00
|
|
|
frag.write('}')
|
2020-07-13 23:20:58 +02:00
|
|
|
frag.write('}') # receiveShadow
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.write('direct += basecol * sdotNL * sunCol * svisibility;')
|
|
|
|
|
2018-12-10 17:25:29 +01:00
|
|
|
if '_SinglePoint' in wrd.world_defs:
|
|
|
|
frag.add_uniform('vec3 pointPos', '_pointPosition')
|
|
|
|
frag.add_uniform('vec3 pointCol', '_pointColor')
|
2019-01-09 21:25:09 +01:00
|
|
|
if '_Spot' in wrd.world_defs:
|
|
|
|
frag.add_uniform('vec3 spotDir', link='_spotDirection')
|
|
|
|
frag.add_uniform('vec2 spotData', link='_spotData')
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.write('float visibility = 1.0;')
|
2018-12-10 23:29:04 +01:00
|
|
|
frag.write('vec3 ld = pointPos - wposition;')
|
|
|
|
frag.write('vec3 l = normalize(ld);')
|
|
|
|
frag.write('float dotNL = max(dot(n, l), 0.0);')
|
2019-01-09 21:25:09 +01:00
|
|
|
if is_shadows:
|
2020-07-13 23:20:58 +02:00
|
|
|
frag.add_uniform('bool receiveShadow')
|
2019-01-09 21:25:09 +01:00
|
|
|
frag.add_uniform('float pointBias', link='_pointShadowsBias')
|
|
|
|
frag.add_include('std/shadows.glsl')
|
2020-07-13 23:20:58 +02:00
|
|
|
|
|
|
|
frag.write('if (receiveShadow) {')
|
2019-01-09 21:25:09 +01:00
|
|
|
if '_Spot' in wrd.world_defs:
|
2018-12-11 23:05:18 +01:00
|
|
|
vert.add_out('vec4 spotPosition')
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
vert.add_uniform('mat4 LWVPSpotArray[1]', link='_biasLightWorldViewProjectionMatrixSpotArray')
|
|
|
|
vert.write('spotPosition = LWVPSpotArray[0] * spos;')
|
2018-12-15 15:07:30 +01:00
|
|
|
frag.add_uniform('sampler2DShadow shadowMapSpot[1]')
|
2018-12-11 23:05:18 +01:00
|
|
|
frag.write('if (spotPosition.w > 0.0) {')
|
|
|
|
frag.write(' vec3 lPos = spotPosition.xyz / spotPosition.w;')
|
2019-02-10 20:37:38 +01:00
|
|
|
if '_Legacy' in wrd.world_defs:
|
|
|
|
frag.write(' visibility = float(texture(shadowMapSpot[0], vec2(lPos.xy)).r > lPos.z - pointBias);')
|
|
|
|
else:
|
|
|
|
frag.write(' visibility = texture(shadowMapSpot[0], vec3(lPos.xy, lPos.z - pointBias)).r;')
|
2018-12-11 23:05:18 +01:00
|
|
|
frag.write('}')
|
2019-01-09 21:25:09 +01:00
|
|
|
else:
|
|
|
|
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj')
|
|
|
|
frag.add_uniform('samplerCubeShadow shadowMapPoint[1]')
|
|
|
|
frag.write('const float s = shadowmapCubePcfSize;') # TODO: incorrect...
|
2019-02-12 17:17:46 +01:00
|
|
|
frag.write('float compare = lpToDepth(ld, lightProj) - pointBias * 1.5;')
|
2020-05-11 09:03:13 +02:00
|
|
|
frag.write('#ifdef _InvY')
|
2019-02-12 12:39:41 +01:00
|
|
|
frag.write('l.y = -l.y;')
|
|
|
|
frag.write('#endif')
|
2019-02-10 20:37:38 +01:00
|
|
|
if '_Legacy' in wrd.world_defs:
|
2019-02-12 12:39:41 +01:00
|
|
|
frag.write('visibility = float(texture(shadowMapPoint[0], vec3(-l + n * pointBias * 20)).r > compare);')
|
2019-02-10 20:37:38 +01:00
|
|
|
else:
|
2019-02-12 12:39:41 +01:00
|
|
|
frag.write('visibility = texture(shadowMapPoint[0], vec4(-l + n * pointBias * 20, compare)).r;')
|
2020-07-13 23:20:58 +02:00
|
|
|
frag.write('}') # receiveShadow
|
2018-12-10 23:29:04 +01:00
|
|
|
|
2018-12-10 17:25:29 +01:00
|
|
|
frag.write('direct += basecol * dotNL * pointCol * attenuate(distance(wposition, pointPos)) * visibility;')
|
|
|
|
|
2018-12-10 18:18:32 +01:00
|
|
|
if '_Clusters' in wrd.world_defs:
|
|
|
|
frag.add_include('std/light_mobile.glsl')
|
|
|
|
frag.write('vec3 albedo = basecol;')
|
|
|
|
frag.write('vec3 f0 = surfaceF0(basecol, metallic);')
|
|
|
|
make_cluster.write(vert, frag)
|
2017-04-26 14:21:22 +02:00
|
|
|
|
2017-11-13 10:19:07 +01:00
|
|
|
if '_Irr' in wrd.world_defs:
|
2017-12-20 10:19:44 +01:00
|
|
|
frag.add_include('std/shirr.glsl')
|
2020-05-10 19:43:02 +02:00
|
|
|
frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance')
|
|
|
|
env_str = 'shIrradiance(n, shirr)'
|
2017-11-13 10:19:07 +01:00
|
|
|
else:
|
|
|
|
env_str = '0.5'
|
2018-11-22 11:08:03 +01:00
|
|
|
|
|
|
|
frag.add_uniform('float envmapStrength', link='_envmapStrength')
|
|
|
|
frag.write('fragColor = vec4(direct + basecol * {0} * envmapStrength, 1.0);'.format(env_str))
|
2017-09-08 16:43:48 +02:00
|
|
|
|
2017-08-21 15:36:21 +02:00
|
|
|
if '_LDR' in wrd.world_defs:
|
2017-04-26 14:21:22 +02:00
|
|
|
frag.write('fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2));')
|
|
|
|
|
2017-10-01 19:42:47 +02:00
|
|
|
def make_forward_solid(con_mesh):
|
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
vert = con_mesh.make_vert()
|
|
|
|
frag = con_mesh.make_frag()
|
|
|
|
geom = None
|
|
|
|
tesc = None
|
|
|
|
tese = None
|
|
|
|
|
2018-12-14 15:27:43 +01:00
|
|
|
for e in con_mesh.data['vertex_elements']:
|
2018-11-24 20:01:35 +01:00
|
|
|
if e['name'] == 'nor':
|
2018-12-14 15:27:43 +01:00
|
|
|
con_mesh.data['vertex_elements'].remove(e)
|
2018-11-24 20:01:35 +01:00
|
|
|
break
|
|
|
|
|
2018-12-14 15:27:43 +01:00
|
|
|
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
2017-10-01 19:42:47 +02:00
|
|
|
frag.ins = vert.outs
|
2018-11-14 21:51:30 +01:00
|
|
|
|
2018-08-30 15:42:25 +02:00
|
|
|
frag.add_include('compiled.inc')
|
2017-10-01 19:42:47 +02:00
|
|
|
frag.write('vec3 basecol;')
|
|
|
|
frag.write('float roughness;')
|
|
|
|
frag.write('float metallic;')
|
|
|
|
frag.write('float occlusion;')
|
2018-05-19 19:29:14 +02:00
|
|
|
frag.write('float specular;')
|
2019-01-23 18:09:53 +01:00
|
|
|
if '_Emission' in wrd.world_defs:
|
|
|
|
frag.write('float emission;')
|
2018-11-14 21:34:53 +01:00
|
|
|
|
|
|
|
arm_discard = mat_state.material.arm_discard
|
|
|
|
blend = mat_state.material.arm_blending
|
|
|
|
is_transluc = mat_utils.is_transluc(mat_state.material)
|
|
|
|
parse_opacity = (blend and is_transluc) or arm_discard
|
|
|
|
if parse_opacity:
|
|
|
|
frag.write('float opacity;')
|
2020-05-06 18:11:02 +02:00
|
|
|
|
2018-11-14 21:34:53 +01:00
|
|
|
cycles.parse(mat_state.nodes, con_mesh, vert, frag, geom, tesc, tese, parse_opacity=parse_opacity, parse_displacement=False, basecol_only=True)
|
|
|
|
|
|
|
|
if arm_discard:
|
|
|
|
opac = mat_state.material.arm_discard_opacity
|
|
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
2017-10-01 19:42:47 +02:00
|
|
|
|
|
|
|
if con_mesh.is_elem('tex'):
|
|
|
|
vert.add_out('vec2 texCoord')
|
2018-12-14 15:27:43 +01:00
|
|
|
vert.add_uniform('float texUnpack', link='_texUnpack')
|
2019-01-22 12:38:47 +01:00
|
|
|
if mat_state.material.arm_tilesheet_flag:
|
2018-11-14 22:28:19 +01:00
|
|
|
vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset')
|
2018-12-14 15:27:43 +01:00
|
|
|
vert.write('texCoord = tex * texUnpack + tilesheetOffset;')
|
2018-11-14 22:28:19 +01:00
|
|
|
else:
|
2018-12-14 15:27:43 +01:00
|
|
|
vert.write('texCoord = tex * texUnpack;')
|
2017-10-01 19:42:47 +02:00
|
|
|
|
|
|
|
if con_mesh.is_elem('col'):
|
|
|
|
vert.add_out('vec3 vcolor')
|
2019-01-07 10:42:45 +01:00
|
|
|
vert.write('vcolor = col.rgb;')
|
2017-10-01 19:42:47 +02:00
|
|
|
|
2018-12-14 15:27:43 +01:00
|
|
|
make_attrib.write_norpos(con_mesh, vert, write_nor=False)
|
2021-05-26 12:42:11 +02:00
|
|
|
make_attrib.write_vertpos(vert)
|
2017-10-01 19:42:47 +02:00
|
|
|
|
|
|
|
frag.add_out('vec4 fragColor')
|
2018-11-14 21:34:53 +01:00
|
|
|
if blend and parse_opacity:
|
|
|
|
frag.write('fragColor = vec4(basecol, opacity);')
|
|
|
|
else:
|
|
|
|
frag.write('fragColor = vec4(basecol, 1.0);')
|
2017-10-01 19:42:47 +02:00
|
|
|
|
|
|
|
if '_LDR' in wrd.world_defs:
|
|
|
|
frag.write('fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2));')
|
|
|
|
|
2016-12-21 00:51:04 +01:00
|
|
|
def make_forward(con_mesh):
|
2017-08-21 15:36:21 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2019-01-23 13:45:58 +01:00
|
|
|
rpdat = arm.utils.get_rp()
|
2018-08-08 22:43:14 +02:00
|
|
|
blend = mat_state.material.arm_blending
|
2019-01-24 12:47:51 +01:00
|
|
|
parse_opacity = blend or mat_utils.is_transluc(mat_state.material)
|
2019-01-23 13:45:58 +01:00
|
|
|
|
2018-08-08 22:43:14 +02:00
|
|
|
make_forward_base(con_mesh, parse_opacity=parse_opacity)
|
2016-12-21 00:51:04 +01:00
|
|
|
|
2016-12-21 19:15:51 +01:00
|
|
|
frag = con_mesh.frag
|
2016-12-21 00:51:04 +01:00
|
|
|
|
2019-01-27 20:13:21 +01:00
|
|
|
if '_LTC' in wrd.world_defs:
|
|
|
|
frag.add_uniform('vec3 lightArea0', '_lightArea0', included=True)
|
|
|
|
frag.add_uniform('vec3 lightArea1', '_lightArea1', included=True)
|
|
|
|
frag.add_uniform('vec3 lightArea2', '_lightArea2', included=True)
|
|
|
|
frag.add_uniform('vec3 lightArea3', '_lightArea3', included=True)
|
|
|
|
frag.add_uniform('sampler2D sltcMat', '_ltcMat', included=True)
|
|
|
|
frag.add_uniform('sampler2D sltcMag', '_ltcMag', included=True)
|
2019-01-27 23:48:54 +01:00
|
|
|
if '_ShadowMap' in wrd.world_defs:
|
|
|
|
if '_SinglePoint' in wrd.world_defs:
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.add_uniform('mat4 LWVPSpot[0]', link='_biasLightViewProjectionMatrixSpot0', included=True)
|
2019-01-27 23:48:54 +01:00
|
|
|
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
|
|
|
|
if '_Clusters' in wrd.world_defs:
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.add_uniform('mat4 LWVPSpotArray[4]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
|
2019-01-27 23:48:54 +01:00
|
|
|
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
|
2019-01-27 20:13:21 +01:00
|
|
|
|
2017-10-16 09:25:18 +02:00
|
|
|
if not blend:
|
2019-01-23 13:45:58 +01:00
|
|
|
mrt = rpdat.rp_ssr
|
|
|
|
if mrt:
|
|
|
|
# Store light gbuffer for post-processing
|
|
|
|
frag.add_out('vec4 fragColor[2]')
|
|
|
|
frag.add_include('std/gbuffer.glsl')
|
|
|
|
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);')
|
|
|
|
frag.write('fragColor[0] = vec4(direct + indirect, packFloat2(occlusion, specular));')
|
2019-07-07 22:02:07 +02:00
|
|
|
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
|
2019-01-23 13:45:58 +01:00
|
|
|
else:
|
|
|
|
frag.add_out('vec4 fragColor[1]')
|
|
|
|
frag.write('fragColor[0] = vec4(direct + indirect, 1.0);')
|
|
|
|
|
2017-10-16 09:25:18 +02:00
|
|
|
if '_LDR' in wrd.world_defs:
|
2017-12-20 10:19:44 +01:00
|
|
|
frag.add_include('std/tonemap.glsl')
|
2019-01-23 13:54:16 +01:00
|
|
|
frag.write('fragColor[0].rgb = tonemapFilmic(fragColor[0].rgb);')
|
2016-12-21 00:51:04 +01:00
|
|
|
|
2017-09-29 17:00:21 +02:00
|
|
|
# Particle opacity
|
2019-04-06 14:30:11 +02:00
|
|
|
if mat_state.material.arm_particle_flag and arm.utils.get_rp().arm_particles == 'On' and mat_state.material.arm_particle_fade:
|
2019-01-23 13:54:16 +01:00
|
|
|
frag.write('fragColor[0].rgb *= p_fade;')
|
2017-09-29 17:00:21 +02:00
|
|
|
|
2019-01-27 15:01:39 +01:00
|
|
|
def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
2018-06-13 12:20:20 +02:00
|
|
|
global is_displacement
|
2016-12-21 00:51:04 +01:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2017-09-29 17:00:21 +02:00
|
|
|
|
|
|
|
arm_discard = mat_state.material.arm_discard
|
|
|
|
make_base(con_mesh, parse_opacity=(parse_opacity or arm_discard))
|
2021-03-02 23:57:40 +01:00
|
|
|
|
2021-02-20 13:51:25 +01:00
|
|
|
blend = mat_state.material.arm_blending
|
2017-07-26 20:49:16 +02:00
|
|
|
|
2016-12-20 00:39:18 +01:00
|
|
|
vert = con_mesh.vert
|
|
|
|
frag = con_mesh.frag
|
|
|
|
tese = con_mesh.tese
|
2016-12-19 01:25:22 +01:00
|
|
|
|
2019-01-24 12:47:51 +01:00
|
|
|
if parse_opacity or arm_discard:
|
2021-02-20 13:51:25 +01:00
|
|
|
if arm_discard or blend:
|
2019-01-24 12:47:51 +01:00
|
|
|
opac = mat_state.material.arm_discard_opacity
|
2019-01-27 15:01:39 +01:00
|
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
|
|
|
elif transluc_pass:
|
|
|
|
frag.write('if (opacity == 1.0) discard;')
|
2019-01-24 12:47:51 +01:00
|
|
|
else:
|
2019-02-11 11:12:41 +01:00
|
|
|
opac = '0.9999' # 1.0 - eps
|
|
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
2017-09-29 17:00:21 +02:00
|
|
|
|
2018-11-22 11:08:03 +01:00
|
|
|
if blend:
|
2019-01-23 13:54:16 +01:00
|
|
|
frag.add_out('vec4 fragColor[1]')
|
2018-11-22 11:08:03 +01:00
|
|
|
if parse_opacity:
|
2019-01-23 13:54:16 +01:00
|
|
|
frag.write('fragColor[0] = vec4(basecol, opacity);')
|
2018-11-22 11:08:03 +01:00
|
|
|
else:
|
2019-01-23 13:54:16 +01:00
|
|
|
# frag.write('fragColor[0] = vec4(basecol * lightCol * visibility, 1.0);')
|
|
|
|
frag.write('fragColor[0] = vec4(basecol, 1.0);')
|
2018-11-22 11:08:03 +01:00
|
|
|
# TODO: Fade out fragments near depth buffer here
|
|
|
|
return
|
|
|
|
|
2020-11-12 20:11:42 +01:00
|
|
|
frag.write_attrib('vec3 vVec = normalize(eyeDir);')
|
|
|
|
frag.write_attrib('float dotNV = max(dot(n, vVec), 0.0);')
|
2017-07-26 20:49:16 +02:00
|
|
|
|
2020-07-13 23:20:58 +02:00
|
|
|
sh = tese if tese is not None else vert
|
2018-06-02 13:53:39 +02:00
|
|
|
sh.add_out('vec3 eyeDir')
|
|
|
|
sh.add_uniform('vec3 eye', '_cameraPosition')
|
|
|
|
sh.write('eyeDir = eye - wposition;')
|
2017-03-14 20:43:54 +01:00
|
|
|
|
2018-12-10 17:25:29 +01:00
|
|
|
frag.add_include('std/light.glsl')
|
2018-11-22 11:08:03 +01:00
|
|
|
is_shadows = '_ShadowMap' in wrd.world_defs
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
is_shadows_atlas = '_ShadowMapAtlas' in wrd.world_defs
|
|
|
|
is_single_atlas = is_shadows_atlas and '_SingleAtlas' in wrd.world_defs
|
|
|
|
shadowmap_sun = 'shadowMap'
|
|
|
|
if is_shadows_atlas:
|
|
|
|
shadowmap_sun = 'shadowMapAtlasSun' if not is_single_atlas else 'shadowMapAtlas'
|
|
|
|
frag.add_uniform('vec2 smSizeUniform', '_shadowMapSize', included=True)
|
2017-10-16 09:25:18 +02:00
|
|
|
|
2016-12-19 01:25:22 +01:00
|
|
|
frag.write('vec3 albedo = surfaceAlbedo(basecol, metallic);')
|
|
|
|
frag.write('vec3 f0 = surfaceF0(basecol, metallic);')
|
2019-01-27 19:12:00 +01:00
|
|
|
|
|
|
|
if '_Brdf' in wrd.world_defs:
|
2019-04-26 11:12:09 +02:00
|
|
|
frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png')
|
2019-01-27 19:12:00 +01:00
|
|
|
frag.write('vec2 envBRDF = texture(senvmapBrdf, vec2(roughness, 1.0 - dotNV)).xy;')
|
|
|
|
|
|
|
|
if '_Irr' in wrd.world_defs:
|
|
|
|
frag.add_include('std/shirr.glsl')
|
2020-05-10 19:43:02 +02:00
|
|
|
frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance')
|
|
|
|
frag.write('vec3 indirect = shIrradiance(n, shirr);')
|
2019-01-27 19:12:00 +01:00
|
|
|
if '_EnvTex' in wrd.world_defs:
|
|
|
|
frag.write('indirect /= PI;')
|
|
|
|
frag.write('indirect *= albedo;')
|
|
|
|
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('prefilteredColor = pow(prefilteredColor, vec3(2.2));')
|
|
|
|
frag.write('indirect += prefilteredColor * (f0 * envBRDF.x + envBRDF.y) * 1.5;')
|
|
|
|
elif '_EnvCol' in wrd.world_defs:
|
|
|
|
frag.add_uniform('vec3 backgroundCol', link='_backgroundCol')
|
|
|
|
frag.write('indirect += backgroundCol * f0;')
|
|
|
|
else:
|
|
|
|
frag.write('vec3 indirect = albedo;')
|
|
|
|
frag.write('indirect *= occlusion;')
|
|
|
|
|
|
|
|
frag.add_uniform('float envmapStrength', link='_envmapStrength')
|
|
|
|
frag.write('indirect *= envmapStrength;')
|
|
|
|
|
2019-04-06 18:52:21 +02:00
|
|
|
if '_VoxelAOvar' in wrd.world_defs:
|
2019-01-27 19:12:00 +01:00
|
|
|
frag.add_include('std/conetrace.glsl')
|
|
|
|
frag.add_uniform('sampler3D voxels')
|
|
|
|
if '_VoxelGICam' in wrd.world_defs:
|
|
|
|
frag.add_uniform('vec3 eyeSnap', link='_cameraPositionSnap')
|
|
|
|
frag.write('vec3 voxpos = (wposition - eyeSnap) / voxelgiHalfExtents;')
|
|
|
|
else:
|
|
|
|
frag.write('vec3 voxpos = wposition / voxelgiHalfExtents;')
|
2019-04-06 18:52:21 +02:00
|
|
|
frag.write('indirect *= vec3(1.0 - traceAO(voxpos, n, voxels));')
|
2019-01-27 19:12:00 +01:00
|
|
|
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.write('vec3 direct = vec3(0.0);')
|
|
|
|
|
|
|
|
if '_Sun' in wrd.world_defs:
|
2018-11-22 22:47:14 +01:00
|
|
|
frag.add_uniform('vec3 sunCol', '_sunColor')
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.add_uniform('vec3 sunDir', '_sunDirection')
|
2018-11-22 23:22:56 +01:00
|
|
|
frag.write('float svisibility = 1.0;')
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.write('vec3 sh = normalize(vVec + sunDir);')
|
|
|
|
frag.write('float sdotNL = dot(n, sunDir);')
|
|
|
|
frag.write('float sdotNH = dot(n, sh);')
|
|
|
|
frag.write('float sdotVH = dot(vVec, sh);')
|
|
|
|
if is_shadows:
|
2020-07-13 23:20:58 +02:00
|
|
|
frag.add_uniform('bool receiveShadow')
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}', top=True)
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
|
|
|
|
frag.write('if (receiveShadow) {')
|
|
|
|
if '_CSM' in wrd.world_defs:
|
2018-12-15 19:03:11 +01:00
|
|
|
frag.add_include('std/shadows.glsl')
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
|
|
|
|
frag.add_uniform('vec3 eye', '_cameraPosition')
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias);')
|
2018-11-22 11:08:03 +01:00
|
|
|
else:
|
2020-07-13 23:20:58 +02:00
|
|
|
if tese is not None:
|
2018-11-22 11:08:03 +01:00
|
|
|
tese.add_out('vec4 lightPosition')
|
|
|
|
tese.add_uniform('mat4 LVP', '_biasLightViewProjectionMatrix')
|
|
|
|
tese.write('lightPosition = LVP * vec4(wposition, 1.0);')
|
|
|
|
else:
|
|
|
|
if is_displacement:
|
|
|
|
vert.add_out('vec4 lightPosition')
|
|
|
|
vert.add_uniform('mat4 LVP', '_biasLightViewProjectionMatrix')
|
|
|
|
vert.write('lightPosition = LVP * vec4(wposition, 1.0);')
|
|
|
|
else:
|
|
|
|
vert.add_out('vec4 lightPosition')
|
2021-03-29 15:41:46 +02:00
|
|
|
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrixSun')
|
2018-11-22 11:08:03 +01:00
|
|
|
vert.write('lightPosition = LWVP * spos;')
|
|
|
|
frag.write('vec3 lPos = lightPosition.xyz / lightPosition.w;')
|
|
|
|
frag.write('const vec2 smSize = shadowmapSize;')
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.write(f'svisibility = PCF({shadowmap_sun}, lPos.xy, lPos.z - shadowsBias, smSize);')
|
2018-11-22 11:08:03 +01:00
|
|
|
frag.write('}') # receiveShadow
|
2019-01-27 23:48:54 +01:00
|
|
|
if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs:
|
2019-02-09 15:34:16 +01:00
|
|
|
frag.write('svisibility *= 1.0 - traceShadow(voxels, voxpos, sunDir);')
|
2018-11-22 22:47:14 +01:00
|
|
|
frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;')
|
2018-11-22 11:08:03 +01:00
|
|
|
# sun
|
|
|
|
|
2018-12-10 17:25:29 +01:00
|
|
|
if '_SinglePoint' in wrd.world_defs:
|
|
|
|
frag.add_uniform('vec3 pointPos', link='_pointPosition')
|
|
|
|
frag.add_uniform('vec3 pointCol', link='_pointColor')
|
|
|
|
if '_Spot' in wrd.world_defs:
|
|
|
|
frag.add_uniform('vec3 spotDir', link='_spotDirection')
|
|
|
|
frag.add_uniform('vec2 spotData', link='_spotData')
|
2019-01-09 21:25:09 +01:00
|
|
|
if is_shadows:
|
2020-07-13 23:20:58 +02:00
|
|
|
frag.add_uniform('bool receiveShadow')
|
2019-01-09 21:25:09 +01:00
|
|
|
frag.add_uniform('float pointBias', link='_pointShadowsBias')
|
|
|
|
if '_Spot' in wrd.world_defs:
|
|
|
|
# Skip world matrix, already in world-space
|
Add support for shadow map atlasing
With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by
grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was
done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that
access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply
using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this
limitation was solved.
The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map
can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic
to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available
space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a
modified version of drawShadowMap().
Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice
that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the
tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial
4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth
levels is added or not when compiling.
the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner
subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a
linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make
some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that
utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
|
|
|
frag.add_uniform('mat4 LWVPSpot[1]', link='_biasLightViewProjectionMatrixSpotArray', included=True)
|
2018-12-15 15:07:30 +01:00
|
|
|
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
|
2019-01-09 21:25:09 +01:00
|
|
|
else:
|
|
|
|
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True)
|
|
|
|
frag.add_uniform('samplerCubeShadow shadowMapPoint[1]', included=True)
|
2018-12-10 17:25:29 +01:00
|
|
|
frag.write('direct += sampleLight(')
|
|
|
|
frag.write(' wposition, n, vVec, dotNV, pointPos, pointCol, albedo, roughness, specular, f0')
|
|
|
|
if is_shadows:
|
2020-07-13 23:20:58 +02:00
|
|
|
frag.write(' , 0, pointBias, receiveShadow')
|
2018-12-10 17:25:29 +01:00
|
|
|
if '_Spot' in wrd.world_defs:
|
|
|
|
frag.write(' , true, spotData.x, spotData.y, spotDir')
|
2019-01-27 23:48:54 +01:00
|
|
|
if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs:
|
2019-01-27 19:12:00 +01:00
|
|
|
frag.write(' , voxels, voxpos')
|
2018-12-10 17:25:29 +01:00
|
|
|
frag.write(');')
|
|
|
|
|
2018-12-10 18:18:32 +01:00
|
|
|
if '_Clusters' in wrd.world_defs:
|
|
|
|
make_cluster.write(vert, frag)
|
2018-11-22 11:08:03 +01:00
|
|
|
|
2019-01-23 18:09:53 +01:00
|
|
|
if '_Emission' in wrd.world_defs:
|
|
|
|
frag.write('if (emission > 0.0) {')
|
|
|
|
frag.write(' direct = vec3(0.0);')
|
2019-05-01 21:27:19 +02:00
|
|
|
frag.write(' indirect += basecol * emission;')
|
2019-01-23 18:09:53 +01:00
|
|
|
frag.write('}')
|