armory/blender/arm/material/make_mesh.py

725 lines
30 KiB
Python
Raw Normal View History

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
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:
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
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'
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'
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
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
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
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
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
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
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
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
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
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:
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)
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')
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrixSun')
2019-02-10 20:37:38 +01:00
vert.write('lightPosition = LWVP * spos;')
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}')
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
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('}')
frag.write('}') # receiveShadow
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')
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:
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')
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;')
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'
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
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)
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))
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:
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
if blend:
2019-01-23 13:54:16 +01:00
frag.add_out('vec4 fragColor[1]')
if parse_opacity:
2019-01-23 13:54:16 +01:00
frag.write('fragColor[0] = vec4(basecol, opacity);')
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);')
# TODO: Fade out fragments near depth buffer here
return
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
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')
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;')
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;')
frag.write('indirect *= vec3(1.0 - traceAO(voxpos, n, voxels));')
2019-01-27 19:12:00 +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')
frag.add_uniform('vec3 sunDir', '_sunDirection')
2018-11-22 23:22:56 +01:00
frag.write('float svisibility = 1.0;')
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:
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)
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')
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);')
else:
if tese is not None:
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')
vert.add_uniform('mat4 LWVP', '_biasLightWorldViewProjectionMatrixSun')
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);')
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;')
# 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:
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:
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)
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('}')