Fix all the bugs, PCSS works.
Before Width: | Height: | Size: 228 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 5.6 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 22 KiB |
|
@ -5,6 +5,7 @@ Note: The engine is still in development and will not work yet.
|
|||
Once ready, a single download SDK will be provided, as well as steps to
|
||||
setup project using git.
|
||||
|
||||
Please take a look at [engine introduction](http://luboslenco.com/notes/) for now.
|
||||
[Web](http://armory3d.org) - [Manual](http://armory3d.org/manual/)
|
||||
|
||||
![](preview.jpg)
|
||||
![](http://armory3d.org/git/img1.jpg)
|
||||
![](http://armory3d.org/git/img2.jpg)
|
||||
|
|
|
@ -1871,6 +1871,10 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
if self.object_is_mesh_cached(bobject) == True and os.path.exists(fp):
|
||||
return
|
||||
|
||||
print ('Exporting mesh ' + bobject.data.name)
|
||||
if len(bobject.data.vertices) > 40000:
|
||||
print('Armory Warning: "' + bobject.name + '" contains over 40000 vertices, split mesh to smaller parts to fit into 16-bit indices')
|
||||
|
||||
o = {}
|
||||
o['name'] = oid
|
||||
mesh = objectRef[0]
|
||||
|
@ -2142,6 +2146,8 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
o['shadows_bias'] = objref.lamp_shadows_bias
|
||||
if o['type'] == 'sun': # Scale bias for ortho light matrix
|
||||
o['shadows_bias'] *= 10.0
|
||||
if objref.shadow_soft_size != 0.1:
|
||||
o['lamp_size'] = objref.shadow_soft_size
|
||||
|
||||
# Parse nodes, only emission for now
|
||||
# Merge with nodes_material
|
||||
|
@ -2283,6 +2289,9 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
c = {}
|
||||
c['name'] = ArmoryExporter.mesh_context_empty
|
||||
o['contexts'].append(c)
|
||||
if bpy.data.worlds['Arm'].force_no_culling:
|
||||
o['override_context'] = {}
|
||||
o['override_context']['cull_mode'] = 'none'
|
||||
defs = []
|
||||
self.finalize_shader(o, defs, ArmoryExporter.renderpath_passes)
|
||||
self.output['material_datas'].append(o)
|
||||
|
@ -2373,6 +2382,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
ArmoryExporter.option_mesh_per_file = self.option_mesh_per_file
|
||||
ArmoryExporter.option_optimize_mesh = self.option_optimize_mesh
|
||||
ArmoryExporter.option_minimize = self.option_minimize
|
||||
ArmoryExporter.export_physics = False # Indicates whether rigid body is exported
|
||||
|
||||
self.cb_preprocess()
|
||||
|
||||
|
@ -2391,6 +2401,9 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
if not ArmoryExporter.option_mesh_only:
|
||||
if self.scene.camera != None:
|
||||
self.output['camera_ref'] = self.scene.camera.name
|
||||
else:
|
||||
if utils.safe_filename(self.scene.name) == utils.get_project_scene_name():
|
||||
print('Armory Warning: No camera found in active scene')
|
||||
|
||||
self.output['material_datas'] = []
|
||||
self.ExportMaterials()
|
||||
|
@ -2415,6 +2428,9 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
self.output['traits'].append(x)
|
||||
|
||||
self.ExportObjects(self.scene)
|
||||
|
||||
if len(self.output['lamp_datas']) == 0: # Asume light data stored in same file
|
||||
print('Armory Warning: No light found in active scene')
|
||||
|
||||
self.cb_postprocess()
|
||||
|
||||
|
@ -2630,6 +2646,7 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
|
||||
# Rigid body trait
|
||||
if bobject.rigid_body != None:
|
||||
ArmoryExporter.export_physics = True
|
||||
rb = bobject.rigid_body
|
||||
shape = 0 # BOX
|
||||
if rb.collision_shape == 'SPHERE':
|
||||
|
@ -2707,11 +2724,15 @@ class ArmoryExporter(bpy.types.Operator, ExportHelper):
|
|||
def cb_export_material(self, material, o):
|
||||
defs = []
|
||||
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
if material.skip_context != '':
|
||||
o['skip_context'] = material.skip_context
|
||||
if material.override_cull:
|
||||
if material.override_cull or wrd.force_no_culling:
|
||||
o['override_context'] = {}
|
||||
o['override_context']['cull_mode'] = material.override_cull_mode
|
||||
if wrd.force_no_culling:
|
||||
o['override_context']['cull_mode'] = 'none'
|
||||
else:
|
||||
o['override_context']['cull_mode'] = material.override_cull_mode
|
||||
|
||||
o['contexts'] = []
|
||||
|
||||
|
|
|
@ -36,6 +36,10 @@ def init_armory_props():
|
|||
for scene in bpy.data.scenes:
|
||||
if scene.render.engine != 'CYCLES':
|
||||
scene.render.engine = 'CYCLES'
|
||||
# Force camera far to at least 100 units for now, to prevent fighting with light far plane
|
||||
for c in bpy.data.cameras:
|
||||
if c.clip_end < 100:
|
||||
c.clip_end = 100
|
||||
# Use nodes
|
||||
for w in bpy.data.worlds:
|
||||
w.use_nodes = True
|
||||
|
@ -79,6 +83,7 @@ def export_data(fp, sdk_path, is_play=False):
|
|||
# shader_references_defs = [] # Defs to go with referenced shaders
|
||||
asset_references = []
|
||||
assets.reset()
|
||||
export_physics = bpy.data.worlds['Arm'].ArmPhysics != 'Disabled'
|
||||
|
||||
# Build node trees
|
||||
# TODO: cache
|
||||
|
@ -101,6 +106,9 @@ def export_data(fp, sdk_path, is_play=False):
|
|||
filepath=asset_path)
|
||||
shader_references += ArmoryExporter.shader_references
|
||||
asset_references += ArmoryExporter.asset_references
|
||||
if export_physics: # Disable physics anyway if no rigid body exported
|
||||
export_physics = ArmoryExporter.export_physics
|
||||
|
||||
assets.add(asset_path)
|
||||
|
||||
# Clean compiled variants if cache is disabled
|
||||
|
@ -142,18 +150,33 @@ def export_data(fp, sdk_path, is_play=False):
|
|||
write_data.write_compiledglsl()
|
||||
|
||||
# Write khafile.js
|
||||
write_data.write_khafilejs(shader_references, asset_references, is_play)
|
||||
write_data.write_khafilejs(shader_references, asset_references, is_play, export_physics)
|
||||
|
||||
# Write Main.hx
|
||||
write_data.write_main()
|
||||
|
||||
# Copy ammo.js if necessary
|
||||
#if target_name == 'html5':
|
||||
if export_physics and bpy.data.worlds['Arm'].ArmPhysics == 'Bullet':
|
||||
ammojs_path = sdk_path + '/lib/haxebullet/js/ammo/ammo.js'
|
||||
if not os.path.isfile('build/html5/ammo.js'):
|
||||
shutil.copy(ammojs_path, 'build/html5')
|
||||
if not os.path.isfile('build/debug-html5/ammo.js'):
|
||||
shutil.copy(ammojs_path, 'build/debug-html5')
|
||||
|
||||
def armory_log(text):
|
||||
print(text)
|
||||
text = (text[:80] + '..') if len(text) > 80 else text # Limit str size
|
||||
ArmoryProjectPanel.info_text = text
|
||||
armory_log.tag_redraw = True
|
||||
armory_log.tag_redraw = False
|
||||
|
||||
def compile_project(target_name=None):
|
||||
def get_kha_target(target_name): # TODO: remove
|
||||
if target_name == 'macos':
|
||||
return 'osx'
|
||||
return target_name
|
||||
|
||||
def compile_project(target_name=None, is_publish=False):
|
||||
user_preferences = bpy.context.user_preferences
|
||||
addon_prefs = user_preferences.addons['armory'].preferences
|
||||
sdk_path = addon_prefs.sdk_path
|
||||
|
@ -162,15 +185,6 @@ def compile_project(target_name=None):
|
|||
if target_name == None:
|
||||
target_name = bpy.data.worlds['Arm'].ArmProjectTarget
|
||||
|
||||
if target_name == 'html5':
|
||||
# Copy ammo.js if necessary
|
||||
if bpy.data.worlds['Arm'].ArmPhysics == 'Bullet':
|
||||
ammojs_path = sdk_path + '/lib/haxebullet/js/ammo/ammo.js'
|
||||
if not os.path.isfile('build/html5/ammo.js'):
|
||||
shutil.copy(ammojs_path, 'build/html5')
|
||||
if not os.path.isfile('build/debug-html5/ammo.js'):
|
||||
shutil.copy(ammojs_path, 'build/debug-html5')
|
||||
|
||||
if utils.get_os() == 'win':
|
||||
node_path = sdk_path + '/nodejs/node.exe'
|
||||
khamake_path = sdk_path + '/kode_studio/KodeStudio-win32/resources/app/extensions/kha/Kha/make'
|
||||
|
@ -181,14 +195,15 @@ def compile_project(target_name=None):
|
|||
node_path = sdk_path + '/nodejs/node-linux64'
|
||||
khamake_path = sdk_path + '/kode_studio/KodeStudio-linux64/resources/app/extensions/kha/Kha/make'
|
||||
|
||||
cmd = [node_path, khamake_path, target_name, '--glsl2']
|
||||
kha_target_name = get_kha_target(target_name)
|
||||
cmd = [node_path, khamake_path, kha_target_name, '--glsl2']
|
||||
|
||||
# armory_log("Building, see console...")
|
||||
|
||||
if make.play_project.playproc == None:
|
||||
if make.play_project.playproc == None or is_publish:
|
||||
return subprocess.Popen(cmd)
|
||||
else:
|
||||
# Patching running game, stay silent, disable krafix and haxe
|
||||
# Patch running game, stay silent, disable krafix and haxe
|
||||
cmd.append('--silent')
|
||||
cmd.append('--noproject')
|
||||
cmd.append('--haxe')
|
||||
|
@ -211,6 +226,15 @@ def build_project(is_play=False):
|
|||
# Save blend
|
||||
bpy.ops.wm.save_mainfile()
|
||||
|
||||
# Set camera in active scene
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
active_scene = bpy.context.screen.scene if wrd.ArmPlayActiveScene else bpy.data.scenes[wrd.ArmProjectScene]
|
||||
if active_scene.camera == None:
|
||||
for o in active_scene.objects:
|
||||
if o.type == 'CAMERA':
|
||||
active_scene.camera = o
|
||||
break
|
||||
|
||||
# Get paths
|
||||
user_preferences = bpy.context.user_preferences
|
||||
addon_prefs = user_preferences.addons['armory'].preferences
|
||||
|
@ -272,21 +296,23 @@ def watch_play():
|
|||
while play_project.playproc != None and play_project.playproc.poll() == None:
|
||||
char = play_project.playproc.stderr.read(1) # Read immediately one by one
|
||||
if char == b'\n':
|
||||
trace = str(line).split('"') # Extract trace
|
||||
if len(trace) > 2:
|
||||
armory_log(trace[1])
|
||||
msg = str(line).split('"', 1) # Extract message
|
||||
if len(msg) > 1:
|
||||
trace = msg[1].rsplit('"', 1)[0]
|
||||
armory_log(trace)
|
||||
line = b''
|
||||
else:
|
||||
line += char
|
||||
play_project.playproc = None
|
||||
play_project.playproc_finished = True
|
||||
armory_log('Ready')
|
||||
|
||||
def watch_compile():
|
||||
def watch_compile(is_publish=False):
|
||||
play_project.compileproc.wait()
|
||||
result = play_project.compileproc.poll()
|
||||
play_project.compileproc = None
|
||||
if result == 0:
|
||||
on_compiled()
|
||||
on_compiled(is_publish)
|
||||
else:
|
||||
armory_log('Build failed, check console')
|
||||
|
||||
|
@ -341,12 +367,30 @@ def run_server():
|
|||
except:
|
||||
print('Server already running')
|
||||
|
||||
def on_compiled():
|
||||
def on_compiled(is_publish=False):
|
||||
armory_log("Ready")
|
||||
user_preferences = bpy.context.user_preferences
|
||||
addon_prefs = user_preferences.addons['armory'].preferences
|
||||
sdk_path = addon_prefs.sdk_path
|
||||
|
||||
# Print info
|
||||
if is_publish:
|
||||
target_name = get_kha_target(bpy.data.worlds['Arm'].ArmPublishTarget)
|
||||
print('Project published')
|
||||
files_path = utils.get_fp() + '/build/' + target_name
|
||||
if target_name == 'html5':
|
||||
print('HTML5 files are located in ' + files_path)
|
||||
elif target_name == 'ios' or target_name == 'osx': # TODO: to macos
|
||||
print('XCode project files are located in ' + files_path + '-build')
|
||||
elif target_name == 'windows':
|
||||
print('VisualStudio 2015 project files are located in ' + files_path + '-build')
|
||||
elif target_name == 'android-native':
|
||||
print('Android Studio project files are located in ' + files_path + '-build')
|
||||
else:
|
||||
print('Makefiles are located in ' + files_path + '-build')
|
||||
return
|
||||
|
||||
# Otherwise launch project
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
if wrd.ArmPlayRuntime == 'Electron':
|
||||
electron_app_path = './build/electron.js'
|
||||
|
@ -372,6 +416,7 @@ def on_compiled():
|
|||
|
||||
play_project.playproc = None
|
||||
play_project.compileproc = None
|
||||
play_project.playproc_finished = False
|
||||
|
||||
def clean_project():
|
||||
os.chdir(utils.get_fp())
|
||||
|
@ -395,6 +440,12 @@ def clean_project():
|
|||
|
||||
print('Project cleaned')
|
||||
|
||||
def publish_project():
|
||||
clean_project()
|
||||
build_project()
|
||||
play_project.compileproc = compile_project(target_name=bpy.data.worlds['Arm'].ArmPublishTarget, is_publish=True)
|
||||
threading.Timer(0.1, watch_compile, {"is_publish" : True}).start()
|
||||
|
||||
# Registration
|
||||
arm_keymaps = []
|
||||
def register():
|
||||
|
|
|
@ -111,10 +111,13 @@ def make_texture(self, id, image_node, material):
|
|||
# Reference image name
|
||||
tex['file'] = utils.extract_filename_noext(image.filepath)
|
||||
tex['file'] = utils.safe_filename(tex['file'])
|
||||
if image_node.interpolation == 'Cubic': # Mipmap linear
|
||||
interpolation = image_node.interpolation
|
||||
if bpy.data.worlds['Arm'].force_anisotropic_filtering:
|
||||
interpolation = 'Smart'
|
||||
if interpolation == 'Cubic': # Mipmap linear
|
||||
tex['mipmap_filter'] = 'linear'
|
||||
tex['generate_mipmaps'] = True
|
||||
elif image_node.interpolation == 'Smart': # Mipmap anisotropic
|
||||
elif interpolation == 'Smart': # Mipmap anisotropic
|
||||
tex['min_filter'] = 'anisotropic'
|
||||
tex['mipmap_filter'] = 'linear'
|
||||
tex['generate_mipmaps'] = True
|
||||
|
@ -188,6 +191,9 @@ def parse_material_surface(self, material, c, defs, tree, node, factor):
|
|||
elif node.type == 'MIX_SHADER':
|
||||
parse_mix_shader(self, material, c, defs, tree, node, factor)
|
||||
|
||||
else:
|
||||
print('Armory Warning: Material node ' + node.type + ' in ' + material.name + ' is not yet supported! Please use "Armory PBR" node group for now.')
|
||||
|
||||
def parse_mix_shader(self, material, c, defs, tree, node, factor):
|
||||
mixfactor = node.inputs[0].default_value * factor
|
||||
if node.inputs[1].is_linked:
|
||||
|
|
|
@ -776,7 +776,6 @@ def load_library():
|
|||
data_to.node_groups = ['forward_path', 'forward_path_low', 'deferred_path', 'deferred_path_low', 'deferred_path_high', 'hybrid_path', 'vr_path', 'pathtrace_path', 'Armory PBR']
|
||||
|
||||
# TODO: cannot use for loop
|
||||
# TODO: import pbr group separately, no need for fake user
|
||||
bpy.data.node_groups['forward_path'].use_fake_user = True
|
||||
bpy.data.node_groups['forward_path_low'].use_fake_user = True
|
||||
bpy.data.node_groups['deferred_path'].use_fake_user = True
|
||||
|
@ -1030,7 +1029,7 @@ def make_draw_compositor(stage, node_group, node, shader_references, asset_refer
|
|||
asset_references.append('build/compiled/ShaderDatas/' + scon + '/' + data_name + '.arm')
|
||||
shader_references.append('build/compiled/Shaders/' + scon + '/' + data_name)
|
||||
# Link assets
|
||||
buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise256.png')
|
||||
# buildNodeTrees.linked_assets.append(buildNodeTrees.assets_path + 'noise256.png')
|
||||
|
||||
def make_call_function(stage, node_group, node):
|
||||
stage['command'] = 'call_function'
|
||||
|
|
|
@ -80,6 +80,13 @@ def buildNodeTree(world):
|
|||
# Percentage closer soft shadows
|
||||
if wrd.generate_pcss:
|
||||
wrd.world_defs += '_PCSS'
|
||||
user_preferences = bpy.context.user_preferences
|
||||
addon_prefs = user_preferences.addons['armory'].preferences
|
||||
sdk_path = addon_prefs.sdk_path
|
||||
assets.add(sdk_path + 'armory/Assets/noise64.png')
|
||||
|
||||
if wrd.diffuse_oren_nayar:
|
||||
wrd.world_defs += '_OrenNayar'
|
||||
|
||||
# Enable probes
|
||||
for cam in bpy.data.cameras:
|
||||
|
@ -121,6 +128,7 @@ def parse_surface(world, node, context):
|
|||
envmap_strength_const = {}
|
||||
envmap_strength_const['name'] = 'envmapStrength'
|
||||
envmap_strength_const['float'] = node.inputs[1].default_value
|
||||
# Always append for now, even though envmapStrength is not always needed
|
||||
context['bind_constants'].append(envmap_strength_const)
|
||||
|
||||
if node.inputs[0].is_linked:
|
||||
|
|
154
blender/props.py
|
@ -34,6 +34,14 @@ def on_scene_update(context):
|
|||
for area in bpy.context.screen.areas:
|
||||
if area.type == 'INFO':
|
||||
area.tag_redraw()
|
||||
break
|
||||
|
||||
# Player finished, redraw play buttons
|
||||
if make.play_project.playproc_finished == True:
|
||||
make.play_project.playproc_finished = False
|
||||
for area in bpy.context.screen.areas:
|
||||
if area.type == 'VIEW_3D' or area.type == 'PROPERTIES':
|
||||
area.tag_redraw()
|
||||
on_scene_update.last_operator = None
|
||||
|
||||
def invalidate_shader_cache(self, context):
|
||||
|
@ -59,14 +67,16 @@ def invalidate_mesh_data(self, context):
|
|||
def initProperties():
|
||||
# For project
|
||||
bpy.types.World.ArmVersion = StringProperty(name = "ArmVersion", default="")
|
||||
bpy.types.World.ArmProjectTarget = EnumProperty(
|
||||
target_prop = EnumProperty(
|
||||
items = [('html5', 'HTML5', 'html5'),
|
||||
('windows', 'Windows', 'windows'),
|
||||
('osx', 'OSX', 'osx'),
|
||||
('macos', 'MacOS', 'macos'),
|
||||
('linux', 'Linux', 'linux'),
|
||||
('ios', 'iOS', 'ios'),
|
||||
('android-native', 'Android', 'android-native')],
|
||||
name = "Target", default='html5')
|
||||
bpy.types.World.ArmProjectTarget = target_prop
|
||||
bpy.types.World.ArmPublishTarget = target_prop
|
||||
bpy.types.World.ArmProjectName = StringProperty(name = "Name", default="ArmoryGame")
|
||||
bpy.types.World.ArmProjectPackage = StringProperty(name = "Package", default="game")
|
||||
bpy.types.World.ArmPlayActiveScene = BoolProperty(name="Play Active Scene", default=True)
|
||||
|
@ -76,6 +86,10 @@ def initProperties():
|
|||
items = [('Disabled', 'Disabled', 'Disabled'),
|
||||
('Bullet', 'Bullet', 'Bullet')],
|
||||
name = "Physics", default='Bullet')
|
||||
bpy.types.World.ArmNavigation = EnumProperty(
|
||||
items = [('Disabled', 'Disabled', 'Disabled'),
|
||||
('Recast', 'Recast', 'Recast')],
|
||||
name = "Navigation", default='Disabled')
|
||||
bpy.types.World.ArmKhafile = StringProperty(name = "Khafile")
|
||||
bpy.types.World.ArmMinimize = BoolProperty(name="Minimize Data", default=True, update=invalidate_compiled_data)
|
||||
bpy.types.World.ArmOptimizeMesh = BoolProperty(name="Optimize Meshes", default=False, update=invalidate_mesh_data)
|
||||
|
@ -95,9 +109,9 @@ def initProperties():
|
|||
bpy.types.World.ArmPlayDeveloperTools = BoolProperty(name="Developer Tools", default=False)
|
||||
bpy.types.World.ArmPlayRuntime = EnumProperty(
|
||||
items = [('Electron', 'Electron', 'Electron'),
|
||||
('Browser', 'Browser', 'Browser'),
|
||||
('Native', 'Native', 'Native'),
|
||||
('Krom', 'Krom', 'Krom')],
|
||||
('Browser', 'Browser', 'Browser')],
|
||||
# ('Native', 'Native', 'Native')],
|
||||
#('Krom', 'Krom', 'Krom')],
|
||||
name = "Runtime", default='Electron')
|
||||
|
||||
# For object
|
||||
|
@ -212,7 +226,11 @@ def initProperties():
|
|||
bpy.types.World.generate_ssr_falloff_exp = bpy.props.FloatProperty(name="Falloff Exp", default=5.0, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_ssr_jitter = bpy.props.FloatProperty(name="Jitter", default=0.6, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_ssr_texture_scale = bpy.props.FloatProperty(name="Texture Scale", default=1.0, min=0.0, max=1.0, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_pcss = bpy.props.BoolProperty(name="PCSS", description="Percentage Closer Soft Shadows", default=False, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_volumetric_light = bpy.props.BoolProperty(name="Volumetric Light", description="", default=True, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_volumetric_light_air_turbidity = bpy.props.FloatProperty(name="Air Turbidity", default=1.0, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_volumetric_light_air_color = bpy.props.FloatVectorProperty(name="Air Color", size=3, default=[1.0, 1.0, 1.0], subtype='COLOR', update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_pcss = bpy.props.BoolProperty(name="Percentage Closer Soft Shadows", description="", default=False, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_pcss_rings = bpy.props.IntProperty(name="Rings", description="", default=20, update=invalidate_shader_cache)
|
||||
# Compositor
|
||||
bpy.types.World.generate_letterbox = bpy.props.BoolProperty(name="Letterbox", default=False, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_letterbox_size = bpy.props.FloatProperty(name="Size", default=0.1, update=invalidate_shader_cache)
|
||||
|
@ -225,6 +243,11 @@ def initProperties():
|
|||
# Skin
|
||||
bpy.types.World.generate_gpu_skin = bpy.props.BoolProperty(name="GPU Skinning", default=True, update=invalidate_shader_cache)
|
||||
bpy.types.World.generate_gpu_skin_max_bones = bpy.props.IntProperty(name="Max Bones", default=50, min=1, max=84, update=invalidate_shader_cache)
|
||||
# Material override flags
|
||||
bpy.types.World.force_no_culling = bpy.props.BoolProperty(name="Force No Culling", default=False)
|
||||
bpy.types.World.force_anisotropic_filtering = bpy.props.BoolProperty(name="Force Anisotropic Filtering", default=False)
|
||||
# Lighting flags
|
||||
bpy.types.World.diffuse_oren_nayar = bpy.props.BoolProperty(name="Oren Nayar Diffuse", default=False, update=invalidate_shader_cache)
|
||||
# For material
|
||||
bpy.types.Material.receive_shadow = bpy.props.BoolProperty(name="Receive Shadow", default=True)
|
||||
bpy.types.Material.override_shader = bpy.props.BoolProperty(name="Override Shader", default=False)
|
||||
|
@ -269,6 +292,9 @@ class ObjectPropsPanel(bpy.types.Panel):
|
|||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = bpy.context.object
|
||||
if obj == None:
|
||||
return
|
||||
|
||||
if bpy.data.worlds['Arm'].ArmExportHideRender == False:
|
||||
layout.prop(obj, 'hide_render')
|
||||
hide = obj.hide_render
|
||||
|
@ -355,6 +381,8 @@ class ModifiersPropsPanel(bpy.types.Panel):
|
|||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = bpy.context.object
|
||||
if obj == None:
|
||||
return
|
||||
|
||||
# Assume as first modifier
|
||||
if len(obj.modifiers) > 0 and obj.modifiers[0].type == 'OCEAN':
|
||||
|
@ -372,6 +400,8 @@ class DataPropsPanel(bpy.types.Panel):
|
|||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = bpy.context.object
|
||||
if obj == None:
|
||||
return
|
||||
|
||||
if obj.type == 'CAMERA':
|
||||
layout.prop(obj.data, 'is_probe')
|
||||
|
@ -432,8 +462,10 @@ class ScenePropsPanel(bpy.types.Panel):
|
|||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
obj = bpy.context.scene
|
||||
layout.prop(obj, 'game_export')
|
||||
scn = bpy.context.scene
|
||||
if scn == None:
|
||||
return
|
||||
layout.prop(scn, 'game_export')
|
||||
|
||||
class ReimportPathsMenu(bpy.types.Menu):
|
||||
bl_label = "OK?"
|
||||
|
@ -477,6 +509,8 @@ class MatsPropsPanel(bpy.types.Panel):
|
|||
def draw(self, context):
|
||||
layout = self.layout
|
||||
mat = bpy.context.material
|
||||
if mat == None:
|
||||
return
|
||||
|
||||
layout.prop(mat, 'receive_shadow')
|
||||
layout.prop(mat, 'override_shader')
|
||||
|
@ -510,6 +544,10 @@ class WorldPropsPanel(bpy.types.Panel):
|
|||
# wrd = bpy.context.world
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
layout.prop(wrd, 'generate_shadows')
|
||||
if wrd.generate_shadows:
|
||||
layout.prop(wrd, 'generate_pcss')
|
||||
if wrd.generate_pcss:
|
||||
layout.prop(wrd, 'generate_pcss_rings')
|
||||
layout.prop(wrd, 'generate_radiance')
|
||||
if wrd.generate_radiance:
|
||||
layout.prop(wrd, 'generate_radiance_sky')
|
||||
|
@ -523,27 +561,40 @@ class WorldPropsPanel(bpy.types.Panel):
|
|||
layout.prop(wrd, 'generate_clouds_secondary')
|
||||
layout.prop(wrd, 'generate_clouds_precipitation')
|
||||
layout.prop(wrd, 'generate_clouds_eccentricity')
|
||||
layout.prop(wrd, 'generate_ssao')
|
||||
if wrd.generate_ssao:
|
||||
layout.prop(wrd, 'generate_ssao_size')
|
||||
layout.prop(wrd, 'generate_ssao_strength')
|
||||
layout.prop(wrd, 'generate_ssao_texture_scale')
|
||||
layout.prop(wrd, 'generate_bloom')
|
||||
if wrd.generate_bloom:
|
||||
layout.prop(wrd, 'generate_bloom_treshold')
|
||||
layout.prop(wrd, 'generate_bloom_strength')
|
||||
layout.prop(wrd, 'generate_motion_blur')
|
||||
if wrd.generate_motion_blur:
|
||||
layout.prop(wrd, 'generate_motion_blur_intensity')
|
||||
layout.prop(wrd, 'generate_ssr')
|
||||
if wrd.generate_ssr:
|
||||
layout.prop(wrd, 'generate_ssr_ray_step')
|
||||
layout.prop(wrd, 'generate_ssr_min_ray_step')
|
||||
layout.prop(wrd, 'generate_ssr_search_dist')
|
||||
layout.prop(wrd, 'generate_ssr_falloff_exp')
|
||||
layout.prop(wrd, 'generate_ssr_jitter')
|
||||
layout.prop(wrd, 'generate_ssr_texture_scale')
|
||||
layout.prop(wrd, 'generate_pcss')
|
||||
|
||||
layout.label('Screen-Space Ambient Occlusion')
|
||||
# layout.prop(wrd, 'generate_ssao')
|
||||
# if wrd.generate_ssao:
|
||||
layout.prop(wrd, 'generate_ssao_size')
|
||||
layout.prop(wrd, 'generate_ssao_strength')
|
||||
layout.prop(wrd, 'generate_ssao_texture_scale')
|
||||
|
||||
layout.label('Bloom')
|
||||
# layout.prop(wrd, 'generate_bloom')
|
||||
# if wrd.generate_bloom:
|
||||
layout.prop(wrd, 'generate_bloom_treshold')
|
||||
layout.prop(wrd, 'generate_bloom_strength')
|
||||
|
||||
layout.label('Motion Blur')
|
||||
# layout.prop(wrd, 'generate_motion_blur')
|
||||
# if wrd.generate_motion_blur:
|
||||
layout.prop(wrd, 'generate_motion_blur_intensity')
|
||||
|
||||
layout.label('Screen-Space Reflections')
|
||||
# layout.prop(wrd, 'generate_ssr')
|
||||
# if wrd.generate_ssr:
|
||||
layout.prop(wrd, 'generate_ssr_ray_step')
|
||||
layout.prop(wrd, 'generate_ssr_min_ray_step')
|
||||
layout.prop(wrd, 'generate_ssr_search_dist')
|
||||
layout.prop(wrd, 'generate_ssr_falloff_exp')
|
||||
layout.prop(wrd, 'generate_ssr_jitter')
|
||||
layout.prop(wrd, 'generate_ssr_texture_scale')
|
||||
|
||||
layout.label('Volumetric Light')
|
||||
# layout.prop(wrd, 'generate_volumetric_light')
|
||||
# if wrd.generate_volumetric_light:
|
||||
layout.prop(wrd, 'generate_volumetric_light_air_turbidity')
|
||||
layout.prop(wrd, 'generate_volumetric_light_air_color')
|
||||
|
||||
layout.label('Compositor')
|
||||
layout.prop(wrd, 'generate_letterbox')
|
||||
|
@ -558,6 +609,11 @@ class WorldPropsPanel(bpy.types.Panel):
|
|||
layout.prop(wrd, 'generate_fog_amounta')
|
||||
layout.prop(wrd, 'generate_fog_amountb')
|
||||
|
||||
layout.label('Flags')
|
||||
layout.prop(wrd, 'force_no_culling')
|
||||
layout.prop(wrd, 'force_anisotropic_filtering')
|
||||
layout.prop(wrd, 'diffuse_oren_nayar')
|
||||
|
||||
# Menu in render region
|
||||
class ArmoryPlayPanel(bpy.types.Panel):
|
||||
bl_label = "Armory Play"
|
||||
|
@ -593,11 +649,13 @@ class ArmoryBuildPanel(bpy.types.Panel):
|
|||
def draw(self, context):
|
||||
layout = self.layout
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
layout.operator("arm.build")
|
||||
if make.play_project.playproc == None and make.play_project.compileproc == None:
|
||||
layout.operator("arm.build")
|
||||
else:
|
||||
layout.operator("arm.patch")
|
||||
layout.operator("arm.kode_studio")
|
||||
layout.operator("arm.clean")
|
||||
layout.prop(wrd, 'ArmProjectTarget')
|
||||
layout.prop(wrd, 'ArmPhysics')
|
||||
layout.prop(wrd, 'ArmCacheShaders')
|
||||
layout.prop(wrd, 'ArmMinimize')
|
||||
layout.prop(wrd, 'ArmOptimizeMesh')
|
||||
|
@ -607,6 +665,9 @@ class ArmoryBuildPanel(bpy.types.Panel):
|
|||
if wrd.generate_gpu_skin:
|
||||
layout.prop(wrd, 'generate_gpu_skin_max_bones')
|
||||
layout.prop(wrd, 'ArmProjectSamplesPerPixel')
|
||||
layout.label('Libraries')
|
||||
layout.prop(wrd, 'ArmPhysics')
|
||||
layout.prop(wrd, 'ArmNavigation')
|
||||
|
||||
class ArmoryProjectPanel(bpy.types.Panel):
|
||||
bl_label = "Armory Project"
|
||||
|
@ -627,8 +688,12 @@ class ArmoryProjectPanel(bpy.types.Panel):
|
|||
layout.prop_search(wrd, 'ArmProjectScene', bpy.data, 'scenes', 'Scene')
|
||||
layout.prop(wrd, 'ArmExportHideRender')
|
||||
layout.prop(wrd, 'ArmSpawnAllLayers')
|
||||
layout.label('Armory version: ' + wrd.ArmVersion)
|
||||
layout.label('Publish Project')
|
||||
layout.operator('arm.publish')
|
||||
layout.prop(wrd, 'ArmPublishTarget')
|
||||
layout.label('Armory')
|
||||
layout.operator('arm.check_updates')
|
||||
layout.label('v' + wrd.ArmVersion)
|
||||
|
||||
class ArmoryPlayButton(bpy.types.Operator):
|
||||
bl_idname = 'arm.play'
|
||||
|
@ -674,10 +739,18 @@ class ArmoryBuildButton(bpy.types.Operator):
|
|||
|
||||
def execute(self, context):
|
||||
invalidate_shader_cache.enabled = False
|
||||
if make.play_project.playproc == None:
|
||||
make.build_project()
|
||||
else:
|
||||
make.patch_project()
|
||||
make.build_project()
|
||||
make.compile_project()
|
||||
invalidate_shader_cache.enabled = True
|
||||
return{'FINISHED'}
|
||||
|
||||
class ArmoryPatchButton(bpy.types.Operator):
|
||||
bl_idname = 'arm.patch'
|
||||
bl_label = 'Live Patch'
|
||||
|
||||
def execute(self, context):
|
||||
invalidate_shader_cache.enabled = False
|
||||
make.patch_project()
|
||||
make.compile_project()
|
||||
invalidate_shader_cache.enabled = True
|
||||
return{'FINISHED'}
|
||||
|
@ -728,6 +801,15 @@ class ArmoryCleanButton(bpy.types.Operator):
|
|||
make.clean_project()
|
||||
return{'FINISHED'}
|
||||
|
||||
class ArmoryPublishButton(bpy.types.Operator):
|
||||
bl_idname = 'arm.publish'
|
||||
bl_label = 'Publish'
|
||||
|
||||
def execute(self, context):
|
||||
make.publish_project()
|
||||
self.report({'INFO'}, 'Publishing project, check console for details.')
|
||||
return{'FINISHED'}
|
||||
|
||||
# Play button in 3D View panel
|
||||
def draw_play_item(self, context):
|
||||
layout = self.layout
|
||||
|
|
|
@ -67,3 +67,10 @@ def get_render_resolution(scene_index=0):
|
|||
render = bpy.data.scenes[scene_index].render
|
||||
scale = render.resolution_percentage / 100
|
||||
return int(render.resolution_x * scale), int(render.resolution_y * scale)
|
||||
|
||||
def get_project_scene_name():
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
if wrd.ArmPlayActiveScene:
|
||||
return safe_filename(bpy.context.screen.scene.name)
|
||||
else:
|
||||
return safe_filename(wrd.ArmProjectScene)
|
||||
|
|
|
@ -7,7 +7,7 @@ def add_armory_library(sdk_path, name):
|
|||
return ('project.addLibrary("../' + bpy.path.relpath(sdk_path + '/' + name)[2:] + '");\n').replace('\\', '/')
|
||||
|
||||
# Write khafile.js
|
||||
def write_khafilejs(shader_references, asset_references, is_play):
|
||||
def write_khafilejs(shader_references, asset_references, is_play, export_physics):
|
||||
|
||||
user_preferences = bpy.context.user_preferences
|
||||
addon_prefs = user_preferences.addons['armory'].preferences
|
||||
|
@ -32,7 +32,7 @@ project.addShaders('Sources/Shaders/**');
|
|||
f.write(add_armory_library(sdk_path, 'armory'))
|
||||
f.write(add_armory_library(sdk_path, 'iron'))
|
||||
|
||||
if bpy.data.worlds['Arm'].ArmPhysics != 'Disabled':
|
||||
if export_physics:
|
||||
f.write("project.addDefine('WITH_PHYSICS');\n")
|
||||
f.write(add_armory_library(sdk_path + '/lib/', 'haxebullet'))
|
||||
|
||||
|
@ -82,13 +82,6 @@ project.addShaders('Sources/Shaders/**');
|
|||
|
||||
f.write("\n\nresolve(project);\n")
|
||||
|
||||
def get_project_scene():
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
if wrd.ArmPlayActiveScene:
|
||||
return utils.safe_filename(bpy.context.screen.scene.name)
|
||||
else:
|
||||
return utils.safe_filename(wrd.ArmProjectScene)
|
||||
|
||||
# Write Main.hx
|
||||
def write_main():
|
||||
wrd = bpy.data.worlds['Arm']
|
||||
|
@ -104,7 +97,7 @@ class Main {
|
|||
static inline var projectWidth = """ + str(resx) + """;
|
||||
static inline var projectHeight = """ + str(resy) + """;
|
||||
static inline var projectSamplesPerPixel = """ + str(wrd.ArmProjectSamplesPerPixel) + """;
|
||||
static inline var projectScene = '""" + get_project_scene() + """';
|
||||
static inline var projectScene = '""" + utils.get_project_scene_name() + """';
|
||||
public static function main() {
|
||||
iron.sys.CompileTime.importPackage('armory.trait');
|
||||
iron.sys.CompileTime.importPackage('armory.renderpath');
|
||||
|
@ -294,6 +287,17 @@ const float ssrSearchDist = """ + str(round(wrd.generate_ssr_search_dist * 100)
|
|||
const float ssrFalloffExp = """ + str(round(wrd.generate_ssr_falloff_exp * 100) / 100) + """;
|
||||
const float ssrJitter = """ + str(round(wrd.generate_ssr_jitter * 100) / 100) + """;
|
||||
const float ssrTextureScale = """ + str(round(wrd.generate_ssr_texture_scale * 10) / 10) + """;
|
||||
""")
|
||||
|
||||
if wrd.generate_volumetric_light:
|
||||
f.write(
|
||||
"""const float volumAirTurbidity = """ + str(round(wrd.generate_volumetric_light_air_turbidity * 100) / 100) + """;
|
||||
const vec3 volumAirColor = vec3(""" + str(round(wrd.generate_volumetric_light_air_color[0] * 100) / 100) + """, """ + str(round(wrd.generate_volumetric_light_air_color[1] * 100) / 100) + """, """ + str(round(wrd.generate_volumetric_light_air_color[2] * 100) / 100) + """);
|
||||
""")
|
||||
|
||||
if wrd.generate_pcss:
|
||||
f.write(
|
||||
"""const int pcssRings = """ + str(wrd.generate_pcss_rings) + """;
|
||||
""")
|
||||
|
||||
# Compositor
|
||||
|
|
BIN
preview.jpg
Before Width: | Height: | Size: 300 KiB |
|
@ -23,11 +23,10 @@ vec2 unpackFloat(float f) {
|
|||
void main() {
|
||||
vec2 tc = texCoord * ssrTextureScale;
|
||||
float roughness = unpackFloat(texture(gbuffer0, texCoord).b).y;
|
||||
if (roughness == 0.0) {
|
||||
outColor = texture(tex, tc);
|
||||
// outColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
return;
|
||||
}
|
||||
// if (roughness == 0.0) { // Always blur for now, non blured output can produce noise
|
||||
// outColor = texture(tex, tc);
|
||||
// return;
|
||||
// }
|
||||
|
||||
vec2 step = dir / screenSize * ssrTextureScale;
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ uniform sampler2D smetal;
|
|||
uniform float metalness;
|
||||
#endif
|
||||
|
||||
uniform vec2 screenSize;
|
||||
// uniform vec2 screenSize;
|
||||
uniform mat4 invVP;
|
||||
uniform mat4 invW;
|
||||
uniform mat4 V;
|
||||
|
|
|
@ -302,7 +302,8 @@
|
|||
},
|
||||
{
|
||||
"name": "screenSize",
|
||||
"link": "_screenSize"
|
||||
"link": "_screenSize",
|
||||
"ifdef": ["_Disabled"]
|
||||
},
|
||||
{
|
||||
"name": "uid",
|
||||
|
|
|
@ -681,7 +681,7 @@ void main() {
|
|||
vec3 indirectSpecular = prefilteredColor * (f0 * envBRDF.x + envBRDF.y);
|
||||
indirect += indirectSpecular;
|
||||
#endif
|
||||
indirect = indirect * lightColor * lightStrength * envmapStrength;
|
||||
indirect = indirect * envmapStrength; // * lightColor * lightStrength;
|
||||
outColor = vec4(vec3(direct * visibility + indirect), 1.0);
|
||||
|
||||
#ifdef _OccTex
|
||||
|
|
|
@ -29,6 +29,11 @@ uniform sampler2D gbuffer1;
|
|||
// #endif
|
||||
#ifndef _NoShadows
|
||||
uniform sampler2D shadowMap;
|
||||
#ifdef _PCSS
|
||||
uniform sampler2D snoise;
|
||||
uniform float lampSizeUV; // 0.55
|
||||
uniform float lampNear; // 0.5
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// #ifdef _LTC
|
||||
|
@ -67,7 +72,7 @@ uniform float spotlightCutoff;
|
|||
uniform float spotlightExponent;
|
||||
uniform vec3 eye;
|
||||
// uniform vec3 eyeLook;
|
||||
uniform vec2 screenSize;
|
||||
// uniform vec2 screenSize;
|
||||
|
||||
// in vec2 texCoord;
|
||||
in vec4 wvpposition;
|
||||
|
@ -131,27 +136,22 @@ vec3 specularBRDF(vec3 f0, float roughness, float nl, float nh, float nv, float
|
|||
float a = roughness * roughness;
|
||||
return d_ggx(nh, a) * clamp(v_smithschlick(nl, nv, a), 0.0, 1.0) * f_schlick(f0, vh) / 4.0;
|
||||
}
|
||||
// vec3 burleyDiffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh) {
|
||||
// float FD90 = 0.5 + 2.0 * vh * vh * roughness;
|
||||
// float FdV = 1.0 + (FD90 - 1.0) * pow(1.0 - nv, 5.0);
|
||||
// float FdL = 1.0 + (FD90 - 1.0) * pow(1.0 - nl, 5.0);
|
||||
// return albedo * ((1.0 / 3.1415926535) * FdV * FdL);
|
||||
// }
|
||||
// vec3 orenNayarDiffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh) {
|
||||
// float a = roughness * roughness;
|
||||
// float s = a;// / (1.29 + 0.5 * a);
|
||||
// float s2 = s * s;
|
||||
// float vl = 2.0 * vh * vh - 1.0; // double angle identity
|
||||
// float Cosri = vl - nv * nl;
|
||||
// float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
|
||||
// float test = 1.0;
|
||||
// if (Cosri >= 0.0) test = (1.0 / (max(nl, nv)));
|
||||
// float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
|
||||
// return albedo / PI * (C1 + C2) * (1.0 + roughness * 0.5);
|
||||
// }
|
||||
vec3 diffuseBRDF(vec3 albedo, float nl) {
|
||||
// lambert
|
||||
return albedo * nl; // // albedo * max(0.0, nl);
|
||||
// Gotanda 2012, Beyond a Simple Physically Based Blinn-Phong Model in Real-Time
|
||||
// http://research.tri-ace.com/Data/s2012_beyond_CourseNotes.pdf
|
||||
vec3 orenNayarDiffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh) {
|
||||
float a = roughness * roughness;
|
||||
float s = a;
|
||||
float s2 = s * s;
|
||||
float vl = 2.0 * vh * vh - 1.0; // Double angle identity
|
||||
float Cosri = vl - nv * nl;
|
||||
float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
|
||||
float test = 1.0;
|
||||
if (Cosri >= 0.0) test = (1.0 / (max(nl, nv)));
|
||||
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
|
||||
return albedo * max(0.0, nl) * (C1 + C2) * (1.0 + roughness * 0.5);
|
||||
}
|
||||
vec3 lambertDiffuseBRDF(vec3 albedo, float nl) {
|
||||
return albedo * max(0.0, nl);
|
||||
}
|
||||
|
||||
#ifndef _NoShadows
|
||||
|
@ -164,7 +164,6 @@ float texture2DShadowLerp(vec2 uv, float compare){
|
|||
const vec2 texelSize = vec2(1.0) / shadowmapSize;
|
||||
vec2 f = fract(uv * shadowmapSize + 0.5);
|
||||
vec2 centroidUV = floor(uv * shadowmapSize + 0.5) / shadowmapSize;
|
||||
|
||||
float lb = texture2DCompare(centroidUV, compare);
|
||||
float lt = texture2DCompare(centroidUV + texelSize * vec2(0.0, 1.0), compare);
|
||||
float rb = texture2DCompare(centroidUV + texelSize * vec2(1.0, 0.0), compare);
|
||||
|
@ -181,106 +180,76 @@ float PCF(vec2 uv, float compare) {
|
|||
// vec2 off = vec2(x, y) / shadowmapSize;
|
||||
// result += texture2DShadowLerp(shadowmapSize, uv + off, compare);
|
||||
compare = compare * 0.5 + 0.5;
|
||||
float result = texture2DShadowLerp(uv + (vec2(-1, -1) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(-1, 0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(-1, 1) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(0, -1) / shadowmapSize), compare);
|
||||
float result = texture2DShadowLerp(uv + (vec2(-1.0, -1.0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(-1.0, 0.0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(-1.0, 1.0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(0.0, -1.0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv, compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(0, 1) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(1, -1) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(1, 0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(1, 1) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(0.0, 1.0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(1.0, -1.0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(1.0, 0.0) / shadowmapSize), compare);
|
||||
result += texture2DShadowLerp(uv + (vec2(1.0, 1.0) / shadowmapSize), compare);
|
||||
// }
|
||||
// }
|
||||
return result / 9.0;
|
||||
}
|
||||
#else // PCSS
|
||||
// Based on ThreeJS example
|
||||
const float LIGHT_WORLD_SIZE = 3.55;
|
||||
const float LIGHT_FRUSTUM_WIDTH = 4.75;
|
||||
const float LIGHT_SIZE_UV = (LIGHT_WORLD_SIZE / LIGHT_FRUSTUM_WIDTH);
|
||||
const float NEAR_PLANE = 0.5;
|
||||
#else // _PCSS
|
||||
// Based on ThreeJS and nvidia pcss
|
||||
// const int pcssRings = 11;
|
||||
const int NUM_SAMPLES = 17;
|
||||
const int NUM_RINGS = 11;
|
||||
// vec2 poissonDisk[NUM_SAMPLES];
|
||||
vec2 poissonDisk0;
|
||||
vec2 poissonDisk1;
|
||||
vec2 poissonDisk2;
|
||||
vec2 poissonDisk3;
|
||||
vec2 poissonDisk4;
|
||||
vec2 poissonDisk5;
|
||||
vec2 poissonDisk6;
|
||||
vec2 poissonDisk7;
|
||||
vec2 poissonDisk8;
|
||||
vec2 poissonDisk9;
|
||||
vec2 poissonDisk10;
|
||||
vec2 poissonDisk11;
|
||||
vec2 poissonDisk12;
|
||||
vec2 poissonDisk13;
|
||||
vec2 poissonDisk14;
|
||||
vec2 poissonDisk15;
|
||||
vec2 poissonDisk16;
|
||||
|
||||
float rand(vec2 co) {
|
||||
return fract(sin(dot(co.xy ,vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
const float radiusStep = 1.0 / float(NUM_SAMPLES);
|
||||
const float angleStep = PI2 * float(pcssRings) / float(NUM_SAMPLES);
|
||||
vec2 poissonDisk0; vec2 poissonDisk1; vec2 poissonDisk2;
|
||||
vec2 poissonDisk3; vec2 poissonDisk4; vec2 poissonDisk5;
|
||||
vec2 poissonDisk6; vec2 poissonDisk7; vec2 poissonDisk8;
|
||||
vec2 poissonDisk9; vec2 poissonDisk10; vec2 poissonDisk11;
|
||||
vec2 poissonDisk12; vec2 poissonDisk13; vec2 poissonDisk14;
|
||||
vec2 poissonDisk15; vec2 poissonDisk16;
|
||||
void initPoissonSamples(const in vec2 randomSeed) {
|
||||
const float ANGLE_STEP = PI2 * float(NUM_RINGS) / float(NUM_SAMPLES);
|
||||
const float INV_NUM_SAMPLES = 1.0 / float(NUM_SAMPLES);
|
||||
|
||||
float angle = rand(randomSeed) * PI2;
|
||||
float radius = INV_NUM_SAMPLES;
|
||||
float radiusStep = radius;
|
||||
|
||||
float angle = texture(snoise, randomSeed).r * PI2;
|
||||
float radius = radiusStep;
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
poissonDisk0 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk1 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk2 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk3 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk4 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk5 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk6 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk7 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk8 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk9 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk10 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk11 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk12 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk13 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk14 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk15 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk16 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += ANGLE_STEP;
|
||||
radius += radiusStep; angle += angleStep;
|
||||
// }
|
||||
}
|
||||
|
||||
float penumbraSize(const in float zReceiver, const in float zBlocker) { // Parallel plane estimation
|
||||
return (zReceiver - zBlocker) / zBlocker;
|
||||
}
|
||||
|
||||
float findBlocker(const in vec2 uv, const in float zReceiver) {
|
||||
// This uses similar triangles to compute what area of the shadow map we should search
|
||||
float searchRadius = LIGHT_SIZE_UV * (zReceiver - NEAR_PLANE) / zReceiver;
|
||||
float searchRadius = lampSizeUV * (zReceiver - lampNear) / zReceiver;
|
||||
float blockerDepthSum = 0.0;
|
||||
int numBlockers = 0;
|
||||
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
float shadowMapDepth = texture(shadowMap, uv + poissonDisk0 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
|
@ -317,12 +286,10 @@ float PCF(vec2 uv, float compare) {
|
|||
shadowMapDepth = texture(shadowMap, uv + poissonDisk16 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
// }
|
||||
|
||||
if (numBlockers == 0) return -1.0;
|
||||
return blockerDepthSum / float(numBlockers);
|
||||
}
|
||||
|
||||
float PCF_Filter(vec2 uv, float zReceiver, float filterRadius) {
|
||||
float filterPCF(vec2 uv, float zReceiver, float filterRadius) {
|
||||
float sum = 0.0;
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
float depth = texture(shadowMap, uv + poissonDisk0 * filterRadius).r * 2.0 - 1.0;
|
||||
|
@ -400,32 +367,22 @@ float PCF(vec2 uv, float compare) {
|
|||
}
|
||||
float PCSS(vec2 uv, float zReceiver) {
|
||||
initPoissonSamples(uv);
|
||||
|
||||
// Blocker search
|
||||
float avgBlockerDepth = findBlocker(uv, zReceiver);
|
||||
|
||||
// There are no occluders so early out (this saves filtering)
|
||||
if (avgBlockerDepth == -1.0) return 1.0;
|
||||
|
||||
// Penumbra size
|
||||
float penumbraRatio = penumbraSize(zReceiver, avgBlockerDepth);
|
||||
float filterRadius = penumbraRatio * LIGHT_SIZE_UV * NEAR_PLANE / zReceiver;
|
||||
|
||||
// Filtering
|
||||
return PCF_Filter(uv, zReceiver, filterRadius);
|
||||
float penumbraRatio = (zReceiver - avgBlockerDepth) / avgBlockerDepth;
|
||||
float filterRadius = penumbraRatio * lampSizeUV * lampNear / zReceiver;
|
||||
return filterPCF(uv, zReceiver, filterRadius);
|
||||
}
|
||||
#endif
|
||||
|
||||
float shadowTest(vec4 lPos) {
|
||||
vec4 lPosH = lPos / lPos.w;
|
||||
lPosH.x = (lPosH.x + 1.0) / 2.0;
|
||||
lPosH.y = (lPosH.y + 1.0) / 2.0;
|
||||
|
||||
#ifdef _PCSS
|
||||
lPosH.x = lPosH.x * 0.5 + 0.5;
|
||||
lPosH.y = lPosH.y * 0.5 + 0.5;
|
||||
#ifdef _PCSS
|
||||
return PCSS(lPosH.xy, lPosH.z - shadowsBias);
|
||||
#else
|
||||
#else
|
||||
return PCF(lPosH.xy, lPosH.z - shadowsBias);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -714,7 +671,7 @@ void main() {
|
|||
vec2 metrough = unpackFloat(g0.b);
|
||||
|
||||
vec3 v = normalize(eye - p.xyz);
|
||||
float dotNV = max(dot(n, v), 0.0);
|
||||
float dotNV = dot(n, v);
|
||||
|
||||
vec3 albedo = surfaceAlbedo(g1.rgb, metrough.x); // g1.rgb - basecolor
|
||||
vec3 f0 = surfaceF0(g1.rgb, metrough.x);
|
||||
|
@ -729,11 +686,11 @@ void main() {
|
|||
}
|
||||
|
||||
vec3 h = normalize(v + l);
|
||||
float dotNH = max(dot(n, h), 0.0);
|
||||
float dotVH = max(dot(v, h), 0.0);
|
||||
float dotNL = max(dot(n, l), 0.0);
|
||||
// float dotLV = max(dot(l, v), 0.0);
|
||||
// float dotLH = max(dot(l, h), 0.0);
|
||||
float dotNH = dot(n, h);
|
||||
float dotVH = dot(v, h);
|
||||
float dotNL = dot(n, l);
|
||||
// float dotLV = dot(l, v);
|
||||
// float dotLH = dot(l, h);
|
||||
|
||||
float visibility = 1.0;
|
||||
#ifndef _NoShadows
|
||||
|
@ -744,9 +701,11 @@ void main() {
|
|||
#endif
|
||||
|
||||
// Direct
|
||||
vec3 direct = diffuseBRDF(albedo, dotNL) + specularBRDF(f0, metrough.y, dotNL, dotNH, dotNV, dotVH);
|
||||
// vec3 direct = orenNayarDiffuseBRDF(albedo, metrough.y, dotNV, dotNL, dotVH) + specularBRDF(f0, metrough.y, dotNL, dotNH, dotNV, dotVH);
|
||||
// vec3 direct = burleyDiffuseBRDF(albedo, metrough.y, dotNV, dotNL, dotVH) + specularBRDF(f0, metrough.y, dotNL, dotNH, dotNV, dotVH);
|
||||
#ifdef _OrenNayar
|
||||
vec3 direct = orenNayarDiffuseBRDF(albedo, metrough.y, dotNV, dotNL, dotVH) + specularBRDF(f0, metrough.y, dotNL, dotNH, dotNV, dotVH);
|
||||
#else
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + specularBRDF(f0, metrough.y, dotNL, dotNH, dotNV, dotVH);
|
||||
#endif
|
||||
|
||||
if (lightType == 2) { // Spot
|
||||
float spotEffect = dot(lightDir, l);
|
||||
|
|
|
@ -101,7 +101,8 @@
|
|||
},
|
||||
{
|
||||
"name": "screenSize",
|
||||
"link": "_screenSize"
|
||||
"link": "_screenSize",
|
||||
"ifdef": ["_Disabled"]
|
||||
},
|
||||
{
|
||||
"name": "shirr",
|
||||
|
@ -142,6 +143,21 @@
|
|||
"name": "time",
|
||||
"link": "_time",
|
||||
"ifdef": ["_PolyLight"]
|
||||
},
|
||||
{
|
||||
"name": "snoise",
|
||||
"link": "_noise64",
|
||||
"ifdef": ["_PCSS"]
|
||||
},
|
||||
{
|
||||
"name": "lampNear",
|
||||
"link": "_lampPlaneNear",
|
||||
"ifdef": ["_PCSS"]
|
||||
},
|
||||
{
|
||||
"name": "lampSizeUV",
|
||||
"link": "_lampSizeUV",
|
||||
"ifdef": ["_PCSS"]
|
||||
}
|
||||
],
|
||||
"vertex_shader": "deferred_light.vert.glsl",
|
||||
|
|
|
@ -34,7 +34,7 @@ precision mediump float;
|
|||
#endif
|
||||
|
||||
// uniform sampler2D gbufferD;
|
||||
uniform float envmapStrength;
|
||||
uniform float envmapStrength; // From world material
|
||||
|
||||
// in vec2 texCoord;
|
||||
in vec3 normal;
|
||||
|
|
|
@ -120,6 +120,21 @@
|
|||
"name": "maxRadSq",
|
||||
"link": "_maxRadiusSq",
|
||||
"ifdef": ["_VR"]
|
||||
},
|
||||
{
|
||||
"name": "snoise",
|
||||
"link": "_noise64",
|
||||
"ifdef": ["_PCSS"]
|
||||
},
|
||||
{
|
||||
"name": "lampNear",
|
||||
"link": "_lampPlaneNear",
|
||||
"ifdef": ["_PCSS"]
|
||||
},
|
||||
{
|
||||
"name": "lampSizeUV",
|
||||
"link": "_lampSizeUV",
|
||||
"ifdef": ["_PCSS"]
|
||||
}
|
||||
],
|
||||
"vertex_shader": "mesh.vert.glsl",
|
||||
|
|
|
@ -11,6 +11,11 @@ precision mediump float;
|
|||
#endif
|
||||
#ifndef _NoShadows
|
||||
uniform sampler2D shadowMap;
|
||||
#ifdef _PCSS
|
||||
uniform sampler2D snoise;
|
||||
uniform float lampSizeUV;
|
||||
uniform float lampNear;
|
||||
#endif
|
||||
#endif
|
||||
uniform float shirr[27];
|
||||
#ifdef _Rad
|
||||
|
@ -98,6 +103,7 @@ in vec3 eyeDir;
|
|||
out vec4 outColor;
|
||||
|
||||
#ifndef _NoShadows
|
||||
#ifndef _PCSS
|
||||
// float linstep(float low, float high, float v) {
|
||||
// return clamp((v - low) / (high - low), 0.0, 1.0);
|
||||
// }
|
||||
|
@ -156,14 +162,195 @@ float PCF(vec2 uv, float compare) {
|
|||
// }
|
||||
return result / 9.0;
|
||||
}
|
||||
#else // _PCSS
|
||||
const int NUM_SAMPLES = 17;
|
||||
const float radiusStep = 1.0 / float(NUM_SAMPLES);
|
||||
const float angleStep = PI2 * float(pcssRings) / float(NUM_SAMPLES);
|
||||
vec2 poissonDisk0; vec2 poissonDisk1; vec2 poissonDisk2;
|
||||
vec2 poissonDisk3; vec2 poissonDisk4; vec2 poissonDisk5;
|
||||
vec2 poissonDisk6; vec2 poissonDisk7; vec2 poissonDisk8;
|
||||
vec2 poissonDisk9; vec2 poissonDisk10; vec2 poissonDisk11;
|
||||
vec2 poissonDisk12; vec2 poissonDisk13; vec2 poissonDisk14;
|
||||
vec2 poissonDisk15; vec2 poissonDisk16;
|
||||
void initPoissonSamples(const in vec2 randomSeed) {
|
||||
float angle = texture(snoise, randomSeed).r * PI2;
|
||||
float radius = radiusStep;
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
poissonDisk0 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk1 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk2 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk3 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk4 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk5 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk6 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk7 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk8 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk9 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk10 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk11 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk12 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk13 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk14 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk15 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk16 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
// }
|
||||
}
|
||||
float findBlocker(const in vec2 uv, const in float zReceiver) {
|
||||
// This uses similar triangles to compute what area of the shadow map we should search
|
||||
float searchRadius = lampSizeUV * (zReceiver - lampNear) / zReceiver;
|
||||
float blockerDepthSum = 0.0;
|
||||
int numBlockers = 0;
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
float shadowMapDepth = texture(shadowMap, uv + poissonDisk0 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk1 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk2 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk3 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk4 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk5 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk6 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk7 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk8 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk9 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk10 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk11 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk12 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk13 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk14 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk15 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk16 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
// }
|
||||
if (numBlockers == 0) return -1.0;
|
||||
return blockerDepthSum / float(numBlockers);
|
||||
}
|
||||
float filterPCF(vec2 uv, float zReceiver, float filterRadius) {
|
||||
float sum = 0.0;
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
float depth = texture(shadowMap, uv + poissonDisk0 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk1 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk2 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk3 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk4 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk5 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk6 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk7 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk8 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk9 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk10 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk11 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk12 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk13 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk14 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk15 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk16 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
// }
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
depth = texture(shadowMap, uv + -poissonDisk0.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk1.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk2.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk3.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk4.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk5.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk6.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk7.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk8.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk9.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk10.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk11.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk12.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk13.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk14.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk15.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk16.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
// }
|
||||
return sum / (2.0 * float(NUM_SAMPLES));
|
||||
}
|
||||
float PCSS(vec2 uv, float zReceiver) {
|
||||
initPoissonSamples(uv);
|
||||
float avgBlockerDepth = findBlocker(uv, zReceiver);
|
||||
if (avgBlockerDepth == -1.0) return 1.0;
|
||||
float penumbraRatio = (zReceiver - avgBlockerDepth) / avgBlockerDepth;
|
||||
float filterRadius = penumbraRatio * lampSizeUV * lampNear / zReceiver;
|
||||
return filterPCF(uv, zReceiver, filterRadius);
|
||||
}
|
||||
#endif
|
||||
float shadowTest(vec4 lPos) {
|
||||
vec4 lPosH = lPos / lPos.w;
|
||||
lPosH.x = (lPosH.x + 1.0) / 2.0;
|
||||
lPosH.y = (lPosH.y + 1.0) / 2.0;
|
||||
|
||||
return PCF(lPosH.xy, lPosH.z - shadowsBias);
|
||||
#ifdef _PCSS
|
||||
return PCSS(lPosH.xy, lPosH.z - shadowsBias);
|
||||
#else
|
||||
return PCF(lPosH.xy, lPosH.z - shadowsBias);
|
||||
#endif
|
||||
// return VSM(lPosH.xy, lPosH.z);
|
||||
// Basic
|
||||
// float distanceFromLight = texture(shadowMap, lPosH.xy).r * 2.0 - 1.0;
|
||||
// return float(distanceFromLight > lPosH.z - shadowsBias);
|
||||
}
|
||||
|
@ -266,43 +453,30 @@ float d_ggx(float nh, float a) {
|
|||
return a2 * (1.0 / 3.1415926535) / denom;
|
||||
}
|
||||
|
||||
vec3 specularBRDF(vec3 f0, float roughness, float nl, float nh, float nv, float vh, float lh) {
|
||||
vec3 specularBRDF(vec3 f0, float roughness, float nl, float nh, float nv, float vh) {
|
||||
float a = roughness * roughness;
|
||||
return d_ggx(nh, a) * clamp(v_smithschlick(nl, nv, a), 0.0, 1.0) * f_schlick(f0, vh) / 4.0;
|
||||
//return vec3(LightingFuncGGX_OPT3(nl, lh, nh, roughness, f0[0]));
|
||||
}
|
||||
|
||||
|
||||
vec3 lambert(vec3 albedo, float nl) {
|
||||
return albedo * max(0.0, nl);
|
||||
#ifdef _OrenNayar
|
||||
vec3 orenNayarDiffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh) {
|
||||
float a = roughness * roughness;
|
||||
float s = a;
|
||||
float s2 = s * s;
|
||||
float vl = 2.0 * vh * vh - 1.0; // Double angle identity
|
||||
float Cosri = vl - nv * nl;
|
||||
float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
|
||||
float test = 1.0;
|
||||
if (Cosri >= 0.0) test = (1.0 / (max(nl, nv)));
|
||||
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
|
||||
return albedo * max(0.0, nl) * (C1 + C2) * (1.0 + roughness * 0.5);
|
||||
}
|
||||
|
||||
vec3 burley(vec3 albedo, float roughness, float NoV, float NoL, float VoH) {
|
||||
float FD90 = 0.5 + 2 * VoH * VoH * roughness;
|
||||
float FdV = 1 + (FD90 - 1) * pow( 1 - NoV, 5 );
|
||||
float FdL = 1 + (FD90 - 1) * pow( 1 - NoL, 5 );
|
||||
return albedo * ( (1.0 / 3.1415926535) * FdV * FdL );
|
||||
}
|
||||
|
||||
vec3 orenNayar(vec3 albedo, float roughness, float NoV, float NoL, float VoH ) {
|
||||
float pi = 3.1415926535;
|
||||
float a = roughness * roughness;
|
||||
float s = a;// / ( 1.29 + 0.5 * a );
|
||||
float s2 = s * s;
|
||||
float VoL = 2.0 * VoH * VoH - 1.0; // double angle identity
|
||||
float Cosri = VoL - NoV * NoL;
|
||||
float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
|
||||
float test = 1.0;
|
||||
if (Cosri >= 0.0) test = (1.0 / ( max( NoL, NoV ) ));
|
||||
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
|
||||
return albedo / pi * ( C1 + C2 ) * ( 1.0 + roughness * 0.5 );
|
||||
}
|
||||
|
||||
vec3 diffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh, float lv) {
|
||||
return lambert(albedo, nl);
|
||||
//return burley(albedo, roughness, nv, nl, vh);
|
||||
//return orenNayar(albedo, roughness, lv, nl, nv);
|
||||
#else
|
||||
vec3 lambertDiffuseBRDF(vec3 albedo, float nl) {
|
||||
return albedo * max(0.0, nl);
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 surfaceAlbedo(vec3 baseColor, float metalness) {
|
||||
return mix(baseColor, vec3(0.0), metalness);
|
||||
|
@ -526,7 +700,7 @@ void main() {
|
|||
else { // Point, spot
|
||||
l = normalize(lightPos - position.xyz);
|
||||
}
|
||||
float dotNL = max(dot(n, l), 0.0);
|
||||
float dotNL = dot(n, l);
|
||||
|
||||
float visibility = 1.0;
|
||||
#ifndef _NoShadows
|
||||
|
@ -551,11 +725,11 @@ void main() {
|
|||
vec3 v = normalize(eyeDir);
|
||||
vec3 h = normalize(v + l);
|
||||
|
||||
float dotNV = max(dot(n, v), 0.0);
|
||||
float dotNH = max(dot(n, h), 0.0);
|
||||
float dotVH = max(dot(v, h), 0.0);
|
||||
float dotLV = max(dot(l, v), 0.0);
|
||||
float dotLH = max(dot(l, h), 0.0);
|
||||
float dotNV = dot(n, v);
|
||||
float dotNH = dot(n, h);
|
||||
float dotVH = dot(v, h);
|
||||
float dotLV = dot(l, v);
|
||||
float dotLH = dot(l, h);
|
||||
|
||||
#ifdef _MetTex
|
||||
float metalness = texture(smetal, texCoord).r;
|
||||
|
@ -675,9 +849,12 @@ void main() {
|
|||
// ltccol /= 2.0*PI;
|
||||
|
||||
|
||||
|
||||
// Direct
|
||||
vec3 direct = diffuseBRDF(albedo, roughness, dotNV, dotNL, dotVH, dotLV) + specularBRDF(f0, roughness, dotNL, dotNH, dotNV, dotVH, dotLH);
|
||||
#ifdef _OrenNayar
|
||||
vec3 direct = orenNayarDiffuseBRDF(albedo, roughness, dotNV, dotNL, dotVH) + specularBRDF(f0, roughness, dotNL, dotNH, dotNV, dotVH);
|
||||
#else
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + specularBRDF(f0, roughness, dotNL, dotNH, dotNV, dotVH);
|
||||
#endif
|
||||
|
||||
if (lightType == 2) { // Spot
|
||||
float spotEffect = dot(lightDir, l);
|
||||
|
|
|
@ -104,6 +104,21 @@
|
|||
{
|
||||
"name": "envmapStrength",
|
||||
"link": "_envmapStrength"
|
||||
},
|
||||
{
|
||||
"name": "snoise",
|
||||
"link": "_noise64",
|
||||
"ifdef": ["_PCSS"]
|
||||
},
|
||||
{
|
||||
"name": "lampNear",
|
||||
"link": "_lampPlaneNear",
|
||||
"ifdef": ["_PCSS"]
|
||||
},
|
||||
{
|
||||
"name": "lampSizeUV",
|
||||
"link": "_lampSizeUV",
|
||||
"ifdef": ["_PCSS"]
|
||||
}
|
||||
],
|
||||
"vertex_shader": "mesh.vert.glsl",
|
||||
|
|
|
@ -13,6 +13,11 @@ precision mediump float;
|
|||
#endif
|
||||
#ifndef _NoShadows
|
||||
uniform sampler2D shadowMap;
|
||||
#ifdef _PCSS
|
||||
uniform sampler2D snoise;
|
||||
uniform float lampSizeUV;
|
||||
uniform float lampNear;
|
||||
#endif
|
||||
#endif
|
||||
uniform float shirr[27];
|
||||
#ifdef _Rad
|
||||
|
@ -76,6 +81,7 @@ in vec3 eyeDir;
|
|||
out vec4[2] outColor;
|
||||
|
||||
#ifndef _NoShadows
|
||||
#ifndef _PCSS
|
||||
float texture2DCompare(vec2 uv, float compare) {
|
||||
float depth = texture(shadowMap, uv).r * 2.0 - 1.0;
|
||||
return step(compare, depth);
|
||||
|
@ -122,11 +128,194 @@ float PCF(vec2 uv, float compare) {
|
|||
// }
|
||||
return result / 9.0;
|
||||
}
|
||||
#else // _PCSS
|
||||
const int NUM_SAMPLES = 17;
|
||||
const float radiusStep = 1.0 / float(NUM_SAMPLES);
|
||||
const float angleStep = PI2 * float(pcssRings) / float(NUM_SAMPLES);
|
||||
vec2 poissonDisk0; vec2 poissonDisk1; vec2 poissonDisk2;
|
||||
vec2 poissonDisk3; vec2 poissonDisk4; vec2 poissonDisk5;
|
||||
vec2 poissonDisk6; vec2 poissonDisk7; vec2 poissonDisk8;
|
||||
vec2 poissonDisk9; vec2 poissonDisk10; vec2 poissonDisk11;
|
||||
vec2 poissonDisk12; vec2 poissonDisk13; vec2 poissonDisk14;
|
||||
vec2 poissonDisk15; vec2 poissonDisk16;
|
||||
void initPoissonSamples(const in vec2 randomSeed) {
|
||||
float angle = texture(snoise, randomSeed).r * PI2;
|
||||
float radius = radiusStep;
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
poissonDisk0 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk1 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk2 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk3 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk4 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk5 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk6 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk7 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk8 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk9 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk10 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk11 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk12 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk13 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk14 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk15 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
poissonDisk16 = vec2(cos(angle), sin(angle)) * pow(radius, 0.75);
|
||||
radius += radiusStep; angle += angleStep;
|
||||
// }
|
||||
}
|
||||
float findBlocker(const in vec2 uv, const in float zReceiver) {
|
||||
// This uses similar triangles to compute what area of the shadow map we should search
|
||||
float searchRadius = lampSizeUV * (zReceiver - lampNear) / zReceiver;
|
||||
float blockerDepthSum = 0.0;
|
||||
int numBlockers = 0;
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
float shadowMapDepth = texture(shadowMap, uv + poissonDisk0 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk1 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk2 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk3 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk4 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk5 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk6 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk7 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk8 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk9 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk10 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk11 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk12 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk13 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk14 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk15 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
shadowMapDepth = texture(shadowMap, uv + poissonDisk16 * searchRadius).r * 2.0 - 1.0;
|
||||
if (shadowMapDepth < zReceiver) { blockerDepthSum += shadowMapDepth; numBlockers++; }
|
||||
// }
|
||||
if (numBlockers == 0) return -1.0;
|
||||
return blockerDepthSum / float(numBlockers);
|
||||
}
|
||||
float filterPCF(vec2 uv, float zReceiver, float filterRadius) {
|
||||
float sum = 0.0;
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
float depth = texture(shadowMap, uv + poissonDisk0 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk1 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk2 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk3 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk4 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk5 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk6 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk7 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk8 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk9 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk10 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk11 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk12 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk13 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk14 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk15 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + poissonDisk16 * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
// }
|
||||
// for (int i = 0; i < NUM_SAMPLES; i++) {
|
||||
depth = texture(shadowMap, uv + -poissonDisk0.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk1.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk2.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk3.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk4.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk5.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk6.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk7.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk8.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk9.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk10.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk11.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk12.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk13.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk14.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk15.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
depth = texture(shadowMap, uv + -poissonDisk16.yx * filterRadius).r * 2.0 - 1.0;
|
||||
if (zReceiver <= depth) sum += 1.0;
|
||||
// }
|
||||
return sum / (2.0 * float(NUM_SAMPLES));
|
||||
}
|
||||
float PCSS(vec2 uv, float zReceiver) {
|
||||
initPoissonSamples(uv);
|
||||
float avgBlockerDepth = findBlocker(uv, zReceiver);
|
||||
if (avgBlockerDepth == -1.0) return 1.0;
|
||||
float penumbraRatio = (zReceiver - avgBlockerDepth) / avgBlockerDepth;
|
||||
float filterRadius = penumbraRatio * lampSizeUV * lampNear / zReceiver;
|
||||
return filterPCF(uv, zReceiver, filterRadius);
|
||||
}
|
||||
#endif
|
||||
float shadowTest(vec4 lPos) {
|
||||
vec4 lPosH = lPos / lPos.w;
|
||||
lPosH.x = (lPosH.x + 1.0) / 2.0;
|
||||
lPosH.y = (lPosH.y + 1.0) / 2.0;
|
||||
#ifdef _PCSS
|
||||
return PCSS(lPosH.xy, lPosH.z - shadowsBias);
|
||||
#else
|
||||
return PCF(lPosH.xy, lPosH.z - shadowsBias);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -226,19 +415,30 @@ float d_ggx(float nh, float a) {
|
|||
return a2 * (1.0 / 3.1415926535) / denom;
|
||||
}
|
||||
|
||||
vec3 specularBRDF(vec3 f0, float roughness, float nl, float nh, float nv, float vh, float lh) {
|
||||
vec3 specularBRDF(vec3 f0, float roughness, float nl, float nh, float nv, float vh) {
|
||||
float a = roughness * roughness;
|
||||
return d_ggx(nh, a) * clamp(v_smithschlick(nl, nv, a), 0.0, 1.0) * f_schlick(f0, vh) / 4.0;
|
||||
//return vec3(LightingFuncGGX_OPT3(nl, lh, nh, roughness, f0[0]));
|
||||
}
|
||||
|
||||
vec3 lambert(vec3 albedo, float nl) {
|
||||
return albedo * max(0.0, nl);
|
||||
#ifdef _OrenNayar
|
||||
vec3 orenNayarDiffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh) {
|
||||
float a = roughness * roughness;
|
||||
float s = a;
|
||||
float s2 = s * s;
|
||||
float vl = 2.0 * vh * vh - 1.0; // Double angle identity
|
||||
float Cosri = vl - nv * nl;
|
||||
float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
|
||||
float test = 1.0;
|
||||
if (Cosri >= 0.0) test = (1.0 / (max(nl, nv)));
|
||||
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
|
||||
return albedo * max(0.0, nl) * (C1 + C2) * (1.0 + roughness * 0.5);
|
||||
}
|
||||
|
||||
vec3 diffuseBRDF(vec3 albedo, float roughness, float nv, float nl, float vh, float lv) {
|
||||
return lambert(albedo, nl);
|
||||
#else
|
||||
vec3 lambertDiffuseBRDF(vec3 albedo, float nl) {
|
||||
return albedo * max(0.0, nl);
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 surfaceAlbedo(vec3 baseColor, float metalness) {
|
||||
return mix(baseColor, vec3(0.0), metalness);
|
||||
|
@ -350,7 +550,11 @@ void main() {
|
|||
#endif
|
||||
|
||||
// Direct
|
||||
vec3 direct = diffuseBRDF(albedo, roughness, dotNV, dotNL, dotVH, dotLV) + specularBRDF(f0, roughness, dotNL, dotNH, dotNV, dotVH, dotLH);
|
||||
#ifdef _OrenNayar
|
||||
vec3 direct = orenNayarDiffuseBRDF(albedo, roughness, dotNV, dotNL, dotVH) + specularBRDF(f0, roughness, dotNL, dotNH, dotNV, dotVH);
|
||||
#else
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) + specularBRDF(f0, roughness, dotNL, dotNH, dotNV, dotVH);
|
||||
#endif
|
||||
|
||||
if (lightType == 2) { // Spot
|
||||
float spotEffect = dot(lightDir, l);
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
precision mediump float;
|
||||
#endif
|
||||
|
||||
#include "../compiled.glsl"
|
||||
|
||||
uniform sampler2D gbufferD;
|
||||
#ifndef _NoShadows
|
||||
uniform sampler2D shadowMap;
|
||||
|
@ -28,7 +30,7 @@ const float tScat = 0.08;
|
|||
const float tAbs = 0.0;
|
||||
const float tExt = 0.0; //tScat + tAbs;
|
||||
// const float stepLen = 1.0 / 11.0;
|
||||
const float stepLen = 1.0 / 80;
|
||||
const float stepLen = 1.0 / 80; // Temporary..
|
||||
|
||||
vec3 getPos(float depth, vec2 coord) {
|
||||
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
||||
|
@ -190,7 +192,6 @@ void main() {
|
|||
// }
|
||||
|
||||
// curOpticalDepth
|
||||
// outColor = vec4(scatteredLightAmount * lightColor.rgb * max(1.0, lightStrength / 2.0), 0.0);
|
||||
outColor = vec4(scatteredLightAmount * lightColor.rgb, 0.0);
|
||||
outColor = vec4(scatteredLightAmount * lightColor.rgb * volumAirColor * volumAirTurbidity, 0.0);
|
||||
// outColor = vec4(scatteredLightAmount * lightColor.rgb * ((1.0 - depth) * 10.0), 0.0);
|
||||
}
|
||||
|
|