Fix all the bugs, PCSS works.

This commit is contained in:
Lubos Lenco 2016-09-08 14:08:31 +02:00
parent cd2e936fcf
commit 9f68ec79c9
26 changed files with 823 additions and 257 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -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)

View File

@ -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'] = []

View File

@ -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():

View File

@ -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:

View File

@ -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'

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

View File

@ -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;

View File

@ -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;

View File

@ -302,7 +302,8 @@
},
{
"name": "screenSize",
"link": "_screenSize"
"link": "_screenSize",
"ifdef": ["_Disabled"]
},
{
"name": "uid",

View File

@ -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

View File

@ -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);

View File

@ -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",

View File

@ -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;

View File

@ -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",

View File

@ -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);

View File

@ -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",

View File

@ -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);

View File

@ -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);
}