1c3e24a8fd
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.
738 lines
31 KiB
Python
738 lines
31 KiB
Python
import bpy
|
|
import arm.assets as assets
|
|
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
|
|
import arm.material.make_particle as make_particle
|
|
import arm.material.make_cluster as make_cluster
|
|
import arm.material.make_finalize as make_finalize
|
|
import arm.material.make_attrib as make_attrib
|
|
import arm.utils
|
|
|
|
is_displacement = False
|
|
write_material_attribs = None
|
|
write_material_attribs_post = None
|
|
write_vertex_attribs = None
|
|
|
|
def make(context_id, rpasses):
|
|
wrd = bpy.data.worlds['Arm']
|
|
rpdat = arm.utils.get_rp()
|
|
rid = rpdat.rp_renderer
|
|
|
|
con = { 'name': context_id, 'depth_write': True, 'compare_mode': 'less', 'cull_mode': 'clockwise' }
|
|
|
|
# Blend context
|
|
mat = mat_state.material
|
|
blend = mat.arm_blending
|
|
particle = mat_state.material.arm_particle_flag
|
|
dprepass = rid == 'Forward' and rpdat.rp_depthprepass
|
|
if blend:
|
|
con['name'] = 'blend'
|
|
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
|
|
con['depth_write'] = False
|
|
con['compare_mode'] = 'less'
|
|
elif particle:
|
|
pass
|
|
elif dprepass: # Depth prepass was performed
|
|
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)
|
|
|
|
con_mesh = mat_state.data.add_context(con)
|
|
mat_state.con_mesh = con_mesh
|
|
|
|
if rid == 'Forward' or blend:
|
|
if rpdat.arm_material_model == 'Mobile':
|
|
make_forward_mobile(con_mesh)
|
|
elif rpdat.arm_material_model == 'Solid':
|
|
make_forward_solid(con_mesh)
|
|
else:
|
|
make_forward(con_mesh)
|
|
elif rid == 'Deferred':
|
|
make_deferred(con_mesh, rpasses)
|
|
elif rid == 'Raytracer':
|
|
make_raytracer(con_mesh)
|
|
|
|
make_finalize.make(con_mesh)
|
|
|
|
assets.vs_equal(con_mesh, assets.shader_cons['mesh_vert'])
|
|
|
|
return con_mesh
|
|
|
|
def make_base(con_mesh, parse_opacity):
|
|
global is_displacement
|
|
global write_material_attribs
|
|
global write_material_attribs_post
|
|
global write_vertex_attribs
|
|
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')
|
|
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
|
|
|
vattr_written = False
|
|
rpdat = arm.utils.get_rp()
|
|
is_displacement = mat_utils.disp_linked(mat_state.output_node)
|
|
if is_displacement:
|
|
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)
|
|
# No displacement
|
|
else:
|
|
frag.ins = vert.outs
|
|
if write_vertex_attribs != None:
|
|
vattr_written = write_vertex_attribs(vert)
|
|
|
|
frag.add_include('compiled.inc')
|
|
|
|
written = False
|
|
if write_material_attribs != None:
|
|
written = write_material_attribs(con_mesh, frag)
|
|
if written == False:
|
|
frag.write('vec3 basecol;')
|
|
frag.write('float roughness;')
|
|
frag.write('float metallic;')
|
|
frag.write('float occlusion;')
|
|
frag.write('float specular;')
|
|
if '_Emission' in wrd.world_defs:
|
|
frag.write('float emission;')
|
|
if parse_opacity:
|
|
frag.write('float opacity;')
|
|
cycles.parse(mat_state.nodes, con_mesh, vert, frag, geom, tesc, tese, parse_opacity=parse_opacity)
|
|
if write_material_attribs_post != None:
|
|
write_material_attribs_post(con_mesh, frag)
|
|
|
|
if not is_displacement and not vattr_written:
|
|
make_attrib.write_vertpos(vert)
|
|
|
|
if con_mesh.is_elem('tex'):
|
|
vert.add_out('vec2 texCoord')
|
|
vert.add_uniform('float texUnpack', link='_texUnpack')
|
|
if mat_state.material.arm_tilesheet_flag:
|
|
if mat_state.material.arm_particle_flag and rpdat.arm_particles == 'On':
|
|
make_particle.write_tilesheet(vert)
|
|
else:
|
|
vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset')
|
|
vert.write_attrib('texCoord = tex * texUnpack + tilesheetOffset;')
|
|
else:
|
|
vert.write_attrib('texCoord = tex * texUnpack;')
|
|
|
|
if tese is not None:
|
|
tese.write_pre = True
|
|
make_tess.interpolate(tese, 'texCoord', 2, declare_out=frag.contains('texCoord'))
|
|
tese.write_pre = False
|
|
|
|
if con_mesh.is_elem('tex1'):
|
|
vert.add_out('vec2 texCoord1')
|
|
vert.add_uniform('float texUnpack', link='_texUnpack')
|
|
vert.write_attrib('texCoord1 = tex1 * texUnpack;')
|
|
if tese is not None:
|
|
tese.write_pre = True
|
|
make_tess.interpolate(tese, 'texCoord1', 2, declare_out=frag.contains('texCoord1'))
|
|
tese.write_pre = False
|
|
|
|
if con_mesh.is_elem('col'):
|
|
vert.add_out('vec3 vcolor')
|
|
vert.write_attrib('vcolor = col.rgb;')
|
|
if tese is not None:
|
|
tese.write_pre = True
|
|
make_tess.interpolate(tese, 'vcolor', 3, declare_out=frag.contains('vcolor'))
|
|
tese.write_pre = False
|
|
|
|
vert.add_out('vec3 wnormal')
|
|
make_attrib.write_norpos(con_mesh, vert)
|
|
frag.write_attrib('vec3 n = normalize(wnormal);')
|
|
|
|
if con_mesh.is_elem('tang'):
|
|
if tese is not None:
|
|
tese.add_out('mat3 TBN')
|
|
tese.write_attrib('vec3 wbitangent = normalize(cross(wnormal, wtangent));')
|
|
tese.write_attrib('TBN = mat3(wtangent, wbitangent, wnormal);')
|
|
else:
|
|
vert.add_out('mat3 TBN')
|
|
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);')
|
|
|
|
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;')
|
|
sh.add_uniform('mat4 VP', '_viewProjectionMatrix')
|
|
sh.write('wposition += wnormal * disp;')
|
|
sh.write('gl_Position = VP * vec4(wposition, 1.0);')
|
|
|
|
def make_deferred(con_mesh, rpasses):
|
|
wrd = bpy.data.worlds['Arm']
|
|
rpdat = arm.utils.get_rp()
|
|
|
|
arm_discard = mat_state.material.arm_discard
|
|
parse_opacity = arm_discard or 'translucent' in rpasses
|
|
|
|
make_base(con_mesh, parse_opacity=parse_opacity)
|
|
|
|
frag = con_mesh.frag
|
|
vert = con_mesh.vert
|
|
tese = con_mesh.tese
|
|
|
|
if parse_opacity:
|
|
if arm_discard:
|
|
opac = mat_state.material.arm_discard_opacity
|
|
else:
|
|
opac = '0.9999' # 1.0 - eps
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
|
|
|
gapi = arm.utils.get_gapi()
|
|
if '_gbuffer2' in wrd.world_defs:
|
|
frag.add_out('vec4 fragColor[3]')
|
|
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;')
|
|
if is_displacement:
|
|
vert.add_uniform('mat4 invW', link='_inverseWorldMatrix')
|
|
vert.write('prevwvpposition = prevWVP * (invW * vec4(wposition, 1.0));')
|
|
else:
|
|
vert.write('prevwvpposition = prevWVP * spos;')
|
|
else:
|
|
tese.add_out('vec4 wvpposition')
|
|
tese.add_out('vec4 prevwvpposition')
|
|
tese.write('wvpposition = gl_Position;')
|
|
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);')
|
|
else:
|
|
frag.add_out('vec4 fragColor[2]')
|
|
|
|
# Pack gbuffer
|
|
frag.add_include('std/gbuffer.glsl')
|
|
|
|
if mat_state.material.arm_two_sided:
|
|
frag.write('if (!gl_FrontFacing) n *= -1;') # Flip normal when drawing back-face
|
|
|
|
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);')
|
|
|
|
if '_Emission' in wrd.world_defs or '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
|
|
frag.write('uint matid = 0;')
|
|
if '_Emission' in wrd.world_defs:
|
|
frag.write('if (emission > 0) { basecol *= emission; matid = 1; }')
|
|
if '_SSS' in wrd.world_defs or '_Hair' in wrd.world_defs:
|
|
frag.add_uniform('int materialID')
|
|
frag.write('if (materialID == 2) matid = 2;')
|
|
else:
|
|
frag.write('const uint matid = 0;')
|
|
|
|
frag.write('fragColor[0] = vec4(n.xy, roughness, packFloatInt16(metallic, matid));')
|
|
frag.write('fragColor[1] = vec4(basecol, packFloat2(occlusion, specular));')
|
|
|
|
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);')
|
|
|
|
return con_mesh
|
|
|
|
def make_raytracer(con_mesh):
|
|
con_mesh.data['vertex_elements'] = [{'name': 'pos', 'data': 'float3'}, {'name': 'nor', 'data': 'float3'}, {'name': 'tex', 'data': 'float2'}]
|
|
wrd = bpy.data.worlds['Arm']
|
|
vert = con_mesh.make_vert()
|
|
frag = con_mesh.make_frag()
|
|
vert.add_out('vec3 n')
|
|
vert.add_out('vec2 uv')
|
|
vert.write('n = nor;')
|
|
vert.write('uv = tex;')
|
|
vert.write('gl_Position = vec4(pos.xyz, 1.0);')
|
|
|
|
def make_forward_mobile(con_mesh):
|
|
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')
|
|
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
|
frag.ins = vert.outs
|
|
|
|
make_attrib.write_vertpos(vert)
|
|
|
|
frag.add_include('compiled.inc')
|
|
frag.write('vec3 basecol;')
|
|
frag.write('float roughness;')
|
|
frag.write('float metallic;')
|
|
frag.write('float occlusion;')
|
|
frag.write('float specular;')
|
|
if '_Emission' in wrd.world_defs:
|
|
frag.write('float emission;')
|
|
|
|
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))
|
|
|
|
if con_mesh.is_elem('tex'):
|
|
vert.add_out('vec2 texCoord')
|
|
vert.add_uniform('float texUnpack', link='_texUnpack')
|
|
if mat_state.material.arm_tilesheet_flag:
|
|
vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset')
|
|
vert.write('texCoord = tex * texUnpack + tilesheetOffset;')
|
|
else:
|
|
vert.write('texCoord = tex * texUnpack;')
|
|
|
|
if con_mesh.is_elem('col'):
|
|
vert.add_out('vec3 vcolor')
|
|
vert.write('vcolor = col.rgb;')
|
|
|
|
if con_mesh.is_elem('tang'):
|
|
vert.add_out('mat3 TBN')
|
|
make_attrib.write_norpos(con_mesh, vert, declare=True)
|
|
vert.write('vec3 tangent = normalize(N * tang.xyz);')
|
|
vert.write('vec3 bitangent = normalize(cross(wnormal, tangent));')
|
|
vert.write('TBN = mat3(tangent, bitangent, wnormal);')
|
|
else:
|
|
vert.add_out('vec3 wnormal')
|
|
make_attrib.write_norpos(con_mesh, vert)
|
|
frag.write_attrib('vec3 n = normalize(wnormal);')
|
|
|
|
frag.add_include('std/math.glsl')
|
|
frag.add_include('std/brdf.glsl')
|
|
|
|
frag.add_out('vec4 fragColor')
|
|
blend = mat_state.material.arm_blending
|
|
if blend:
|
|
if parse_opacity:
|
|
frag.write('fragColor = vec4(basecol, opacity);')
|
|
else:
|
|
frag.write('fragColor = vec4(basecol, 1.0);')
|
|
return
|
|
|
|
is_shadows = '_ShadowMap' in wrd.world_defs
|
|
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)
|
|
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', '_biasLightWorldViewProjectionMatrix')
|
|
vert.write('lightPosition = LWVP * spos;')
|
|
frag.add_uniform('bool receiveShadow')
|
|
frag.add_uniform(f'sampler2DShadow {shadowmap_sun}')
|
|
frag.add_uniform('float shadowsBias', '_sunShadowsBias')
|
|
|
|
frag.write('if (receiveShadow) {')
|
|
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')
|
|
frag.write(f'svisibility = shadowTestCascade({shadowmap_sun}, eye, wposition + n * shadowsBias * 10, shadowsBias);')
|
|
else:
|
|
frag.write('if (lightPosition.w > 0.0) {')
|
|
frag.write(' vec3 lPos = lightPosition.xyz / lightPosition.w;')
|
|
if '_Legacy' in wrd.world_defs:
|
|
frag.write(f' svisibility = float(texture({shadowmap_sun}, vec2(lPos.xy)).r > lPos.z - shadowsBias);')
|
|
else:
|
|
frag.write(f' svisibility = texture({shadowmap_sun}, vec3(lPos.xy, lPos.z - shadowsBias)).r;')
|
|
frag.write('}')
|
|
frag.write('}') # receiveShadow
|
|
frag.write('direct += basecol * sdotNL * sunCol * svisibility;')
|
|
|
|
if '_SinglePoint' in wrd.world_defs:
|
|
frag.add_uniform('vec3 pointPos', '_pointPosition')
|
|
frag.add_uniform('vec3 pointCol', '_pointColor')
|
|
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;')
|
|
frag.write('vec3 ld = pointPos - wposition;')
|
|
frag.write('vec3 l = normalize(ld);')
|
|
frag.write('float dotNL = max(dot(n, l), 0.0);')
|
|
if is_shadows:
|
|
frag.add_uniform('bool receiveShadow')
|
|
frag.add_uniform('float pointBias', link='_pointShadowsBias')
|
|
frag.add_include('std/shadows.glsl')
|
|
|
|
frag.write('if (receiveShadow) {')
|
|
if '_Spot' in wrd.world_defs:
|
|
vert.add_out('vec4 spotPosition')
|
|
vert.add_uniform('mat4 LWVPSpotArray[1]', link='_biasLightWorldViewProjectionMatrixSpotArray')
|
|
vert.write('spotPosition = LWVPSpotArray[0] * spos;')
|
|
frag.add_uniform('sampler2DShadow shadowMapSpot[1]')
|
|
frag.write('if (spotPosition.w > 0.0) {')
|
|
frag.write(' vec3 lPos = spotPosition.xyz / spotPosition.w;')
|
|
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;')
|
|
frag.write('}')
|
|
else:
|
|
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj')
|
|
frag.add_uniform('samplerCubeShadow shadowMapPoint[1]')
|
|
frag.write('const float s = shadowmapCubePcfSize;') # TODO: incorrect...
|
|
frag.write('float compare = lpToDepth(ld, lightProj) - pointBias * 1.5;')
|
|
frag.write('#ifdef _InvY')
|
|
frag.write('l.y = -l.y;')
|
|
frag.write('#endif')
|
|
if '_Legacy' in wrd.world_defs:
|
|
frag.write('visibility = float(texture(shadowMapPoint[0], vec3(-l + n * pointBias * 20)).r > compare);')
|
|
else:
|
|
frag.write('visibility = texture(shadowMapPoint[0], vec4(-l + n * pointBias * 20, compare)).r;')
|
|
frag.write('}') # receiveShadow
|
|
|
|
frag.write('direct += basecol * dotNL * pointCol * attenuate(distance(wposition, pointPos)) * visibility;')
|
|
|
|
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)
|
|
|
|
if '_Irr' in wrd.world_defs:
|
|
frag.add_include('std/shirr.glsl')
|
|
frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance')
|
|
env_str = 'shIrradiance(n, shirr)'
|
|
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))
|
|
|
|
if '_LDR' in wrd.world_defs:
|
|
frag.write('fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2));')
|
|
|
|
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
|
|
|
|
for e in con_mesh.data['vertex_elements']:
|
|
if e['name'] == 'nor':
|
|
con_mesh.data['vertex_elements'].remove(e)
|
|
break
|
|
|
|
vert.write_attrib('vec4 spos = vec4(pos.xyz, 1.0);')
|
|
frag.ins = vert.outs
|
|
|
|
make_attrib.write_vertpos(vert)
|
|
|
|
frag.add_include('compiled.inc')
|
|
frag.write('vec3 basecol;')
|
|
frag.write('float roughness;')
|
|
frag.write('float metallic;')
|
|
frag.write('float occlusion;')
|
|
frag.write('float specular;')
|
|
if '_Emission' in wrd.world_defs:
|
|
frag.write('float emission;')
|
|
|
|
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, basecol_only=True)
|
|
|
|
if arm_discard:
|
|
opac = mat_state.material.arm_discard_opacity
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
|
|
|
if con_mesh.is_elem('tex'):
|
|
vert.add_out('vec2 texCoord')
|
|
vert.add_uniform('float texUnpack', link='_texUnpack')
|
|
if mat_state.material.arm_tilesheet_flag:
|
|
vert.add_uniform('vec2 tilesheetOffset', '_tilesheetOffset')
|
|
vert.write('texCoord = tex * texUnpack + tilesheetOffset;')
|
|
else:
|
|
vert.write('texCoord = tex * texUnpack;')
|
|
|
|
if con_mesh.is_elem('col'):
|
|
vert.add_out('vec3 vcolor')
|
|
vert.write('vcolor = col.rgb;')
|
|
|
|
make_attrib.write_norpos(con_mesh, vert, write_nor=False)
|
|
|
|
frag.add_out('vec4 fragColor')
|
|
if blend and parse_opacity:
|
|
frag.write('fragColor = vec4(basecol, opacity);')
|
|
else:
|
|
frag.write('fragColor = vec4(basecol, 1.0);')
|
|
|
|
if '_LDR' in wrd.world_defs:
|
|
frag.write('fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2));')
|
|
|
|
def make_forward(con_mesh):
|
|
wrd = bpy.data.worlds['Arm']
|
|
rpdat = arm.utils.get_rp()
|
|
blend = mat_state.material.arm_blending
|
|
parse_opacity = blend or mat_utils.is_transluc(mat_state.material)
|
|
|
|
make_forward_base(con_mesh, parse_opacity=parse_opacity)
|
|
|
|
frag = con_mesh.frag
|
|
|
|
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)
|
|
if '_ShadowMap' in wrd.world_defs:
|
|
if '_SinglePoint' in wrd.world_defs:
|
|
frag.add_uniform('mat4 LWVPSpot[0]', link='_biasLightViewProjectionMatrixSpot0', included=True)
|
|
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
|
|
if '_Clusters' in wrd.world_defs:
|
|
frag.add_uniform('mat4 LWVPSpotArray[4]', link='_biasLightWorldViewProjectionMatrixSpotArray', included=True)
|
|
frag.add_uniform('sampler2DShadow shadowMapSpot[4]', included=True)
|
|
|
|
if not blend:
|
|
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));')
|
|
frag.write('fragColor[1] = vec4(n.xy, roughness, metallic);')
|
|
else:
|
|
frag.add_out('vec4 fragColor[1]')
|
|
frag.write('fragColor[0] = vec4(direct + indirect, 1.0);')
|
|
|
|
if '_LDR' in wrd.world_defs:
|
|
frag.add_include('std/tonemap.glsl')
|
|
frag.write('fragColor[0].rgb = tonemapFilmic(fragColor[0].rgb);')
|
|
|
|
# Particle opacity
|
|
if mat_state.material.arm_particle_flag and arm.utils.get_rp().arm_particles == 'On' and mat_state.material.arm_particle_fade:
|
|
frag.write('fragColor[0].rgb *= p_fade;')
|
|
|
|
def make_forward_base(con_mesh, parse_opacity=False, transluc_pass=False):
|
|
global is_displacement
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
arm_discard = mat_state.material.arm_discard
|
|
make_base(con_mesh, parse_opacity=(parse_opacity or arm_discard))
|
|
|
|
vert = con_mesh.vert
|
|
frag = con_mesh.frag
|
|
tese = con_mesh.tese
|
|
|
|
if parse_opacity or arm_discard:
|
|
if arm_discard:
|
|
opac = mat_state.material.arm_discard_opacity
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
|
elif transluc_pass:
|
|
frag.write('if (opacity == 1.0) discard;')
|
|
else:
|
|
opac = '0.9999' # 1.0 - eps
|
|
frag.write('if (opacity < {0}) discard;'.format(opac))
|
|
|
|
blend = mat_state.material.arm_blending
|
|
if blend:
|
|
frag.add_out('vec4 fragColor[1]')
|
|
if parse_opacity:
|
|
frag.write('fragColor[0] = vec4(basecol, opacity);')
|
|
else:
|
|
# 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);')
|
|
|
|
sh = tese if tese is not None else vert
|
|
sh.add_out('vec3 eyeDir')
|
|
sh.add_uniform('vec3 eye', '_cameraPosition')
|
|
sh.write('eyeDir = eye - wposition;')
|
|
|
|
frag.add_include('std/light.glsl')
|
|
is_shadows = '_ShadowMap' in wrd.world_defs
|
|
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)
|
|
|
|
frag.write('vec3 albedo = surfaceAlbedo(basecol, metallic);')
|
|
frag.write('vec3 f0 = surfaceF0(basecol, metallic);')
|
|
|
|
if '_Brdf' in wrd.world_defs:
|
|
frag.add_uniform('sampler2D senvmapBrdf', link='$brdf.png')
|
|
frag.write('vec2 envBRDF = texture(senvmapBrdf, vec2(roughness, 1.0 - dotNV)).xy;')
|
|
|
|
if '_Irr' in wrd.world_defs:
|
|
frag.add_include('std/shirr.glsl')
|
|
frag.add_uniform('vec4 shirr[7]', link='_envmapIrradiance')
|
|
frag.write('vec3 indirect = shIrradiance(n, shirr);')
|
|
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:
|
|
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));')
|
|
|
|
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('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')
|
|
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:
|
|
frag.add_include('std/shadows.glsl')
|
|
frag.add_uniform('vec4 casData[shadowmapCascades * 4 + 4]', '_cascadeData', included=True)
|
|
frag.add_uniform('vec3 eye', '_cameraPosition')
|
|
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', '_biasLightWorldViewProjectionMatrix')
|
|
vert.write('lightPosition = LWVP * spos;')
|
|
frag.write('vec3 lPos = lightPosition.xyz / lightPosition.w;')
|
|
frag.write('const vec2 smSize = shadowmapSize;')
|
|
frag.write(f'svisibility = PCF({shadowmap_sun}, lPos.xy, lPos.z - shadowsBias, smSize);')
|
|
frag.write('}') # receiveShadow
|
|
if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs:
|
|
frag.write('svisibility *= 1.0 - traceShadow(voxels, voxpos, sunDir);')
|
|
frag.write('direct += (lambertDiffuseBRDF(albedo, sdotNL) + specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * specular) * sunCol * svisibility;')
|
|
# sun
|
|
|
|
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')
|
|
if is_shadows:
|
|
frag.add_uniform('bool receiveShadow')
|
|
frag.add_uniform('float pointBias', link='_pointShadowsBias')
|
|
if '_Spot' in wrd.world_defs:
|
|
# Skip world matrix, already in world-space
|
|
frag.add_uniform('mat4 LWVPSpot[1]', link='_biasLightViewProjectionMatrixSpotArray', included=True)
|
|
frag.add_uniform('sampler2DShadow shadowMapSpot[1]', included=True)
|
|
else:
|
|
frag.add_uniform('vec2 lightProj', link='_lightPlaneProj', included=True)
|
|
frag.add_uniform('samplerCubeShadow shadowMapPoint[1]', included=True)
|
|
frag.write('direct += sampleLight(')
|
|
frag.write(' wposition, n, vVec, dotNV, pointPos, pointCol, albedo, roughness, specular, f0')
|
|
if is_shadows:
|
|
frag.write(' , 0, pointBias, receiveShadow')
|
|
if '_Spot' in wrd.world_defs:
|
|
frag.write(' , true, spotData.x, spotData.y, spotDir')
|
|
if '_VoxelShadow' in wrd.world_defs and '_VoxelAOvar' in wrd.world_defs:
|
|
frag.write(' , voxels, voxpos')
|
|
frag.write(');')
|
|
|
|
if '_Clusters' in wrd.world_defs:
|
|
make_cluster.write(vert, frag)
|
|
|
|
if '_Emission' in wrd.world_defs:
|
|
frag.write('if (emission > 0.0) {')
|
|
frag.write(' direct = vec3(0.0);')
|
|
frag.write(' indirect += basecol * emission;')
|
|
frag.write('}')
|