commit cbbebc0bd20ee5f743a3e9a8d2616c4a8b1da35f Author: Azumgi Date: Mon Jan 22 12:10:53 2018 +0300 init commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..140ef3e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin/ +TEMP/ +Docs/ \ No newline at end of file diff --git a/FEATURES_LIST.MD b/FEATURES_LIST.MD new file mode 100644 index 0000000..6b2f3d8 --- /dev/null +++ b/FEATURES_LIST.MD @@ -0,0 +1,147 @@ +Goals +===== +The old Vermintide Mod Framework is a good tool, but it has a lot of legacy code, some of its components' code is hardly readable, and after the new workshop update it's more like a bunch of functions than a framework. So I decided to rewrite the whole thing from the scratch. My goals were: + +1. Write VMF using self-documenting code. So it would be easy to read and maintain. +2. Move all the managing code from the mods to VMF if possible. So the modders will spend less time on writing managing code. +3. Make public VMF functions names short and clear. This and #2 will make mods' code shorter and clearer. +4. Make it easy for the mods to communicate with each other. +5. Make it work with bundled mods (new mod format). + +In short, make VMF the real framework, which will provide a very user-friendly API and will do all the managing for the modders. + +New Approach +============ +From now on, I'll refer to old VMF as "Old-VMF" and to new VMF as "VMF". +Also, when comparing functions, (*) means same functionality and (+) means new functionality. + +Mods Handling +------------- + +The modders who's working with Old-VMF are used to the string `local mod, mod_name, oi = Mods.new_mod("ModName")`. That was an unnecessary step. The created "mod" was just a table which could be accessed from anywhere with `Mods.get_mod("ModName")` function, and all the interactions with VMF were made via public functions. So a lot of modders just skipped this step since they didn't need it. + +But, from now on, the mod instance is not a table anymore, it's the object of the class `VMFMod` and it is the core element of any mod. It is the bridge between the mod and VMF. From this moment, there are only 2 public functions in VMF: + +* `new_mod(mod_name)` - creates the new instance of `VMFMod` class object and returns it +* `get_mod(mod_name)` - returns already created mod instance or `nil` if it doesn't exist + +All the other VMF functionality can be accessed only through the created `VMFMod` objects. + +To understand what I'm talking about, just look at this little example: +```lua +local mod = new_mod("totally_useless_mod") + +mod:hook("MatchmakingManager.update", function(func, ...) + func(...) + mod:echo("Some annoying message you'll see a lot of times no matter what") +end) +``` +This approach allows to simplify a lot of things for the modder and makes it much easier for VMF to handle things on its own. + +Basic functions and events +-------------------------- + +Now let's get to some basic functions. + +Before: +`EchoConsole(message [string])` +After: +`mod:echo(message [string], show_mod_name [bool])` +(*) Shows the message in the chat. +(+) If `show_mod_name` is `true` the message will be prefixed with `[mod_name] `. +(+) The message will also appear in the log as `[ECHO][mod_name] message` + +Before: +`Safe_pcall(...)` +After: +`mod:pcall(...)` +(*) Lets safely execute function, shows the error message in the chat +(+) Shows which mod failed to execute the code. +(+) Log it. + +Also, there is the new thing in VMF: events. It's working like this: + +1. The modder defines `mod.some_event_name = some_function` +2. Certain conditions for some_event are met +3. If the mod has `mod.some_event_name` defined, VMF calls it and passes specific event arguments + +Here are some basic events: + +`mod.unload()` - is called when the mod is about to be reloaded +`mod.update(dt)` - is called every tick + +Now, the example from above, but using `update` event this time: +```lua +local mod = new_mod("totally_useless_mod") + +mod.update = function(dt) + mod:echo("Some annoying message you'll see a lot of times no matter what") +end +``` + +Hooking system +-------------- +Imagine that you're debugging your code and experimenting with different hooks, but some time later you notice, that some hook you deleted from the code 20 mins ago is still working. You do the /reload, but it doesn't help, so you have to restart the game to stop it. Sounds familiar? Well, that's not the case anymore. Now VMF removes all the hooks when it's about to be reloaded. + +Before: +`Mods.hook.set(mod_name, "Object.Function", new_function)` +After: +`mod:hook("Object.Function", new_function)` +(*) Hooks the function +(+) It won't crash anymore if there's misprint in `"Object.Function"` or it doesn't exist. The error message with the mod name will be shown in the chat instead. Actually, there are many small changes like that, which will make debugging easier. There is no point to describe them all so I just skip them from now on. + +Before: +`Mods.hook.enable(false, mod_name, "Object.Function")` +After: +`mod:hook_disable("Object.Function")` +(*) Disables hook (but not removes) + +Before: +`Mods.hook.enable(true, mod_name, "Object.Function")` +After: +`mod:hook_enable("Object.Function")` +(*) Enables disabled hook + +Before: +`Mods.hook.remove("Object.Function", mod_name)` +After: +`mod:hook_remove("Object.Function")` +(*) Removes hook + +Also, new functions: +`mod:disable_all_hooks()` +`mod:enable_all_hooks()` +`mod:remove_all_hooks()` +(*) Disables/enables/removes all hooks for certain mod + + +Settings manager +------------------------- +This is the new thing. In the Old-VMF mods interacted with user settings via 'Application' directly. Now VMF is using settings manager. What's the difference? + +* Settings manager operates settings within the mod namespace, so now you can define settings with the same name for different mods. Forget about `cb_bot_improvements_keep_tomes`. `keep_tomes` will do it. +* In Old-VMF, in order to actually save changed settings, some mods had to save settings to file right after changing it. That wasn't good for HDD. Now, setting manager takes care of it. It will automatically save changed settings to file when changing map / reloading VMF / closing options menu. + +FUNCTIONS: + +`mod:get(setting_name)` - returns setting value or `nil` + +`mod:set(setting_name, setting_value, call_setting_changed_event)` - changes setting value. If `call_setting_changed_event` is `true`, the `setting_changed` event will be called for the mod. + +EVENT: + +`mod.setting_changed(setting_name)` - is called whenever something changes mods setting with `call_setting_changed_event` set to `true`. + +With this event you won't need to repeatedly check if some option is changed. By default, after mod initilizing, the only thing that will call `mod:set` is the the VMF options menu. And it will always call `setting_changed` event. + + + +Localization +Gui (WIP) +Options menu (WIP) +Keybindings (Will do) +Network (Will do) +Actions +Chat +Game modes +Debug modules \ No newline at end of file diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..26c455b --- /dev/null +++ b/README.MD @@ -0,0 +1,4 @@ +1) Clone project somewhere +2) Make a hardlink of SDK bin folder inside cloned project +`mklink /J "C:\Vermintide\VMF_SDK\bin" "C:\Program Files (x86)\Steam\steamapps\common\Warhammer End Times Vermintide Mod Tools\bin"` +3) Run .bat-file every time you need to compile project \ No newline at end of file diff --git a/compile_mod.bat b/compile_mod.bat new file mode 100644 index 0000000..232ebaf --- /dev/null +++ b/compile_mod.bat @@ -0,0 +1,3 @@ +"./bin/stingray_win64_dev_x64.exe" --compile-for win32 --source-dir vmf_source --data-dir TEMP/compile --bundle-dir TEMP/bundle +copy TEMP\bundle\*. "C:\Programs (x86)\Steam\steamapps\common\Warhammer End Times Vermintide\bundle\mods" +pause \ No newline at end of file diff --git a/vmf_source/core/physx_metadata/physx_334_win32.physx_metadata b/vmf_source/core/physx_metadata/physx_334_win32.physx_metadata new file mode 100644 index 0000000..ec592bd Binary files /dev/null and b/vmf_source/core/physx_metadata/physx_334_win32.physx_metadata differ diff --git a/vmf_source/core/physx_metadata/physx_334_win32_64bit.physx_metadata b/vmf_source/core/physx_metadata/physx_334_win32_64bit.physx_metadata new file mode 100644 index 0000000..d1ac91a Binary files /dev/null and b/vmf_source/core/physx_metadata/physx_334_win32_64bit.physx_metadata differ diff --git a/vmf_source/core/shader_nodes/abs.shader_node b/vmf_source/core/shader_nodes/abs.shader_node new file mode 100644 index 0000000..436ba03 --- /dev/null +++ b/vmf_source/core/shader_nodes/abs.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Absolute" +inputs = { + "f36f88b8-8111-4fc3-b56c-32a80e7db111" = { name = "a" display_name = "A" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(abs(a)); +""" diff --git a/vmf_source/core/shader_nodes/add.shader_node b/vmf_source/core/shader_nodes/add.shader_node new file mode 100644 index 0000000..cac0d2f --- /dev/null +++ b/vmf_source/core/shader_nodes/add.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Add" +inputs = { + "f72597c4-7487-419a-affb-df690e6582e1" = { name = "a" display_name = "A" type = "auto" } + "0806db0d-2c4a-43ca-99cc-f5a2f036a8e8" = { name = "b" display_name = "B" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT(a + b); +""" diff --git a/vmf_source/core/shader_nodes/billboard_particles/particle_id.shader_node b/vmf_source/core/shader_nodes/billboard_particles/particle_id.shader_node new file mode 100644 index 0000000..de5945f --- /dev/null +++ b/vmf_source/core/shader_nodes/billboard_particles/particle_id.shader_node @@ -0,0 +1,21 @@ + + +group = "Particle Billboard" +display_name = "Particle ID" + +imports = { + particle_id = { + type = "float" + semantic = "TEXCOORD5" + domain = "vertex" + } +} + +output = { + type = { typeof: "particle_id" } +} + +code = """ + RESULT(particle_id); +""" + diff --git a/vmf_source/core/shader_nodes/billboard_particles/particle_uv_frame.shader_node b/vmf_source/core/shader_nodes/billboard_particles/particle_uv_frame.shader_node new file mode 100644 index 0000000..e783d5a --- /dev/null +++ b/vmf_source/core/shader_nodes/billboard_particles/particle_uv_frame.shader_node @@ -0,0 +1,20 @@ +group = "Particle Billboard" +display_name = "UV Frame" + +imports = { + uv_frame = { + type = "float" + domain = "vertex" + output_channel = "uv_frame" + } +} + +defines = ["NEEDS_UV_ANIMATION"] + +output = { + type = { typeof: "uv_frame" } +} + +code = """ + RESULT(uv_frame); +""" diff --git a/vmf_source/core/shader_nodes/billboard_particles/particle_uv_scale.shader_node b/vmf_source/core/shader_nodes/billboard_particles/particle_uv_scale.shader_node new file mode 100644 index 0000000..14c1828 --- /dev/null +++ b/vmf_source/core/shader_nodes/billboard_particles/particle_uv_scale.shader_node @@ -0,0 +1,20 @@ +group = "Particle Billboard" +display_name = "UV Scale" + +imports = { + uv_scale = { + type = "float2" + domain = "vertex" + output_channel = "uv_scale" + } +} + +defines = ["NEEDS_UV_SCALE"] + +output = { + type = { typeof: "uv_scale" } +} + +code = """ + RESULT(uv_scale); +""" diff --git a/vmf_source/core/shader_nodes/billboard_particles/particle_vertex_color.shader_node b/vmf_source/core/shader_nodes/billboard_particles/particle_vertex_color.shader_node new file mode 100644 index 0000000..f113c6c --- /dev/null +++ b/vmf_source/core/shader_nodes/billboard_particles/particle_vertex_color.shader_node @@ -0,0 +1,41 @@ +group = "Particle Billboard" +display_name = "Vertex Color" + +imports = { + color = { + type = "float4" + semantic = "COLOR" + domain = "vertex" + } +} + +output = { + type = { typeof: "color" } +} + +options = { + "c5b9991a-f14d-4f37-ad66-8a41103d5967" = "VC_COMPRESSED" + "71f11ae4-8f03-4a75-b3a0-4899eff35d29" = "FAST_GAMMA_DECODE" + "dbf19835-b8f4-4a3c-829b-c5fafdb60989" = "FAST_GAMMA_DECODE_ALPHA" +} + +ui = [ + { type = "checkbox" display_name = "Fast Gamma Decode" option = "71f11ae4-8f03-4a75-b3a0-4899eff35d29" } + { type = "checkbox" display_name = "Fast Gamma Decode Alpha" option = "dbf19835-b8f4-4a3c-829b-c5fafdb60989" } + { type = "checkbox" display_name = "Compressed" option = "c5b9991a-f14d-4f37-ad66-8a41103d5967" } +] + +code = """ + #if defined(FAST_GAMMA_DECODE) + color = fast_gamma_to_linear_rgb(color); + #endif + + #if defined(FAST_GAMMA_DECODE_ALPHA) + color.a *= color.a; + #endif + + #if defined(VC_COMPRESSED) + color = decode_vertex_color(color); + #endif + RESULT(color); +""" diff --git a/vmf_source/core/shader_nodes/billboard_particles/soft_particles.shader_node b/vmf_source/core/shader_nodes/billboard_particles/soft_particles.shader_node new file mode 100644 index 0000000..905d348 --- /dev/null +++ b/vmf_source/core/shader_nodes/billboard_particles/soft_particles.shader_node @@ -0,0 +1,59 @@ +group = "Particle Billboard" +display_name = "Soft Particles" + +depends_on = [ + "core/stingray_renderer/output_nodes/standard_base" + "core/stingray_renderer/output_nodes/unlit_base", + "core/stingray_renderer/output_nodes/particle_base", + "core/stingray_renderer/output_nodes/particle_gbuffer_base", + "core/stingray_renderer/output_nodes/particle_distortion_base"] + +inputs = { + "a9330f88-8b4b-4979-a25e-e7fbe031eabd" = { + name = "opacity" + display_name = "Opacity" + type = "scalar" + domain = "pixel" + is_required = true + } + + "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" = { + name = "depth_fade_distance" + display_name = "Depth Fade Distance" + type = "scalar" + domain = "pixel" + is_required = true + } +} + +imports = { + pixel_depth = { + type = "float" + domain = "pixel" + output_channel = "pixel_depth" + } + + screen_pos = { + type = "float2" + domain = "pixel" + output_channel = "screen_pos" + } +} + +defines = ["NEEDS_PIXEL_DEPTH", "NEEDS_LINEAR_DEPTH", "NEEDS_SCREEN_POS"] + +domain = "pixel" +output = { + type = { typeof: "opacity" } +} + +language = "hlsl" +code = """ + #if defined(HAS_LINEAR_DEPTH) + float gbuffer_depth = gbuffer_decode_depth(TEX2D(linear_depth, screen_pos.xy)); + float result = opacity * saturate(abs(gbuffer_depth - pixel_depth) / depth_fade_distance); + #else + float result = opacity; + #endif + RESULT(result); +""" diff --git a/vmf_source/core/shader_nodes/billboard_particles/texcoord.shader_node b/vmf_source/core/shader_nodes/billboard_particles/texcoord.shader_node new file mode 100644 index 0000000..e364c87 --- /dev/null +++ b/vmf_source/core/shader_nodes/billboard_particles/texcoord.shader_node @@ -0,0 +1,21 @@ +group = "Particle Billboard" +display_name = "Particle UV" + +imports = { + // TODO: fix + //"on_platform(GL)": { + // corner_info = { type = "float2" semantic = "COLOR1" domain = "vertex" } + //} + //"!on_platform(GL)": { + corner_info = { type = "float2" semantic = "POSITION1" domain = "vertex" } + //} +} + +domain = "vertex" +output = { + type = "float2" +} + +code = """ + RESULT(corner_info * float2(1,-1) * 0.5 + 0.5); +""" diff --git a/vmf_source/core/shader_nodes/billboard_particles/uv_animation.shader_node b/vmf_source/core/shader_nodes/billboard_particles/uv_animation.shader_node new file mode 100644 index 0000000..fdb3af8 --- /dev/null +++ b/vmf_source/core/shader_nodes/billboard_particles/uv_animation.shader_node @@ -0,0 +1,40 @@ +group = "Particle Billboard" +display_name = "UV Animation" + +inputs = { + "8a8a12c1-e3b5-4666-a9dd-b6b083f75e7a" = { name = "uv" display_name = "UV" type = "float2" is_required = true } + "242d1648-a626-445b-9534-bccec094112f" = { name = "frame_size" display_name = "Frame Size" type = "float2" is_required = true } + "c5823c75-4ae5-4c71-b070-315fa4d03e8e" = { name = "graph_uv_frame" display_name = "UV Frame (Debug Only)" type = { scalar: ["HAS_UV_FRAME"] } is_required = false } +} + +imports = { + vertex_uv_frame = { + type = "float" + domain = "vertex" + output_channel = "uv_frame" + } +} + +defines = ["NEEDS_UV_ANIMATION"] + +output = { + type = { typeof: "uv" } +} + +code = """ + float uv_frame; + #if defined(HAS_UV_FRAME) + uv_frame = graph_uv_frame; + #else + uv_frame = vertex_uv_frame; + #endif + + uv *= frame_size; + float n_frames = 1.0 / frame_size.x; + int frame_x = fmod(uv_frame, n_frames); + int frame_y = uv_frame / n_frames; + float2 offset = float2(frame_x * frame_size.x, frame_y * frame_size.y); + uv += offset; + + RESULT(uv); +""" \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/blackbody.shader_node b/vmf_source/core/shader_nodes/blackbody.shader_node new file mode 100644 index 0000000..68dad83 --- /dev/null +++ b/vmf_source/core/shader_nodes/blackbody.shader_node @@ -0,0 +1,227 @@ +group = "Fatshark" +display_name = "Black-Body Radiation (In progress)" +inputs = { + "8deba514-fbf0-4f71-808a-5654f82238b0" = { name = "T" display_name = "T" type = "scalar" is_required = true } + "149ae0c3-db91-4009-9aed-45281b8e3d4c" = { name = "blend" display_name = "Blend" type = { scalar: ["HAS_BLEND_FACTOR"] } is_required = false } +} + +output = { + type = "float3" +} + +code = """ + float3 table[89] = { + float3(3.769647E-03, 4.146161E-04, 1.847260E-02), + float3(9.382967E-03, 1.059646E-03, 4.609784E-02), + float3(2.214302E-02, 2.452194E-03, 1.096090E-01), + float3(4.742986E-02, 4.971717E-03, 2.369246E-01), + float3(8.953803E-02, 9.079860E-03, 4.508369E-01), + float3(1.446214E-01, 1.429377E-02, 7.378822E-01), + float3(2.035729E-01, 2.027369E-02, 1.051821E+00), + float3(2.488523E-01, 2.612106E-02, 1.305008E+00), + float3(2.918246E-01, 3.319038E-02, 1.552826E+00), + float3(3.227087E-01, 4.157940E-02, 1.748280E+00), + float3(3.482554E-01, 5.033657E-02, 1.917479E+00), + float3(3.418483E-01, 5.743393E-02, 1.918437E+00), + float3(3.224637E-01, 6.472352E-02, 1.848545E+00), + float3(2.826646E-01, 7.238339E-02, 1.664439E+00), + float3(2.485254E-01, 8.514816E-02, 1.522157E+00), + float3(2.219781E-01, 1.060145E-01, 1.428440E+00), + float3(1.806905E-01, 1.298957E-01, 1.250610E+00), + float3(1.291920E-01, 1.535066E-01, 9.991789E-01), + float3(8.182895E-02, 1.788048E-01, 7.552379E-01), + float3(4.600865E-02, 2.064828E-01, 5.617313E-01), + float3(2.083981E-02, 2.379160E-01, 4.099313E-01), + float3(7.097731E-03, 2.850680E-01, 3.105939E-01), + float3(2.461588E-03, 3.483536E-01, 2.376753E-01), + float3(3.649178E-03, 4.277595E-01, 1.720018E-01), + float3(1.556989E-02, 5.204972E-01, 1.176796E-01), + float3(4.315171E-02, 6.206256E-01, 8.283548E-02), + float3(7.962917E-02, 7.180890E-01, 5.650407E-02), + float3(1.268468E-01, 7.946448E-01, 3.751912E-02), + float3(1.818026E-01, 8.575799E-01, 2.438164E-02), + float3(2.405015E-01, 9.071347E-01, 1.566174E-02), + float3(3.098117E-01, 9.544675E-01, 9.846470E-03), + float3(3.804244E-01, 9.814106E-01, 6.131421E-03), + float3(4.494206E-01, 9.890228E-01, 3.790291E-03), + float3(5.280233E-01, 9.994608E-01, 2.327186E-03), + float3(6.133784E-01, 9.967737E-01, 1.432128E-03), + float3(7.016774E-01, 9.902549E-01, 8.822531E-04), + float3(7.967750E-01, 9.732611E-01, 5.452416E-04), + float3(8.853376E-01, 9.424569E-01, 3.386739E-04), + float3(9.638388E-01, 8.963613E-01, 2.117772E-04), + float3(1.051011E+00, 8.587203E-01, 1.335031E-04), + float3(1.109767E+00, 8.115868E-01, 8.494468E-05), + float3(1.143620E+00, 7.544785E-01, 5.460706E-05), + float3(1.151033E+00, 6.918553E-01, 3.549661E-05), + float3(1.134757E+00, 6.270066E-01, 2.334738E-05), + float3(1.083928E+00, 5.583746E-01, 1.554631E-05), + float3(1.007344E+00, 4.895950E-01, 1.048387E-05), + float3(9.142877E-01, 4.229897E-01, 0.000000E+00), + float3(8.135565E-01, 3.609245E-01, 0.000000E+00), + float3(6.924717E-01, 2.980865E-01, 0.000000E+00), + float3(5.755410E-01, 2.416902E-01, 0.000000E+00), + float3(4.731224E-01, 1.943124E-01, 0.000000E+00), + float3(3.844986E-01, 1.547397E-01, 0.000000E+00), + float3(2.997374E-01, 1.193120E-01, 0.000000E+00), + float3(2.277792E-01, 8.979594E-02, 0.000000E+00), + float3(1.707914E-01, 6.671045E-02, 0.000000E+00), + float3(1.263808E-01, 4.899699E-02, 0.000000E+00), + float3(9.224597E-02, 3.559982E-02, 0.000000E+00), + float3(6.639960E-02, 2.554223E-02, 0.000000E+00), + float3(4.710606E-02, 1.807939E-02, 0.000000E+00), + float3(3.292138E-02, 1.261573E-02, 0.000000E+00), + float3(2.262306E-02, 8.661284E-03, 0.000000E+00), + float3(1.575417E-02, 6.027677E-03, 0.000000E+00), + float3(1.096778E-02, 4.195941E-03, 0.000000E+00), + float3(7.608750E-03, 2.910864E-03, 0.000000E+00), + float3(5.214608E-03, 1.995557E-03, 0.000000E+00), + float3(3.569452E-03, 1.367022E-03, 0.000000E+00), + float3(2.464821E-03, 9.447269E-04, 0.000000E+00), + float3(1.703876E-03, 6.537050E-04, 0.000000E+00), + float3(1.186238E-03, 4.555970E-04, 0.000000E+00), + float3(8.269535E-04, 3.179738E-04, 0.000000E+00), + float3(5.758303E-04, 2.217445E-04, 0.000000E+00), + float3(4.058303E-04, 1.565566E-04, 0.000000E+00), + float3(2.856577E-04, 1.103928E-04, 0.000000E+00), + float3(2.021853E-04, 7.827442E-05, 0.000000E+00), + float3(1.438270E-04, 5.578862E-05, 0.000000E+00), + float3(1.024685E-04, 3.981884E-05, 0.000000E+00), + float3(7.347551E-05, 2.860175E-05, 0.000000E+00), + float3(5.259870E-05, 2.051259E-05, 0.000000E+00), + float3(3.806114E-05, 1.487243E-05, 0.000000E+00), + float3(2.758222E-05, 1.080001E-05, 0.000000E+00), + float3(2.004122E-05, 7.863920E-06, 0.000000E+00), + float3(1.458792E-05, 5.736935E-06, 0.000000E+00), + float3(1.068141E-05, 4.211597E-06, 0.000000E+00), + float3(7.857521E-06, 3.106561E-06, 0.000000E+00), + float3(5.768284E-06, 2.286786E-06, 0.000000E+00), + float3(4.259166E-06, 1.693147E-06, 0.000000E+00), + float3(3.167765E-06, 1.262556E-06, 0.000000E+00), + float3(2.358723E-06, 9.422514E-07, 0.000000E+00), + float3(1.762465E-06, 7.053860E-07, 0.000000E+00) + }; + + float3 table2[89] = { + float3(0.003769647000000E6, 0.000414616100000E6, 0.018472600000000E6), + float3(0.009382967000000E6, 0.001059646000000E6, 0.046097840000000E6), + float3(0.022143020000000E6, 0.002452194000000E6, 0.109609000000000E6), + float3(0.047429860000000E6, 0.004971717000000E6, 0.236924600000000E6), + float3(0.089538030000000E6, 0.009079860000000E6, 0.450836900000000E6), + float3(0.144621400000000E6, 0.014293770000000E6, 0.737882200000000E6), + float3(0.203572900000000E6, 0.020273690000000E6, 1.051821000000000E6), + float3(0.248852300000000E6, 0.026121060000000E6, 1.305008000000000E6), + float3(0.291824600000000E6, 0.033190380000000E6, 1.552826000000000E6), + float3(0.322708700000000E6, 0.041579400000000E6, 1.748280000000000E6), + float3(0.348255400000000E6, 0.050336570000000E6, 1.917479000000000E6), + float3(0.341848300000000E6, 0.057433930000000E6, 1.918437000000000E6), + float3(0.322463700000000E6, 0.064723520000000E6, 1.848545000000000E6), + float3(0.282664600000000E6, 0.072383390000000E6, 1.664439000000000E6), + float3(0.248525400000000E6, 0.085148160000000E6, 1.522157000000000E6), + float3(0.221978100000000E6, 0.106014500000000E6, 1.428440000000000E6), + float3(0.180690500000000E6, 0.129895700000000E6, 1.250610000000000E6), + float3(0.129192000000000E6, 0.153506600000000E6, 0.999178900000000E6), + float3(0.081828950000000E6, 0.178804800000000E6, 0.755237900000000E6), + float3(0.046008650000000E6, 0.206482800000000E6, 0.561731300000000E6), + float3(0.020839810000000E6, 0.237916000000000E6, 0.409931300000000E6), + float3(0.007097731000000E6, 0.285068000000000E6, 0.310593900000000E6), + float3(0.002461588000000E6, 0.348353600000000E6, 0.237675300000000E6), + float3(0.003649178000000E6, 0.427759500000000E6, 0.172001800000000E6), + float3(0.015569890000000E6, 0.520497200000000E6, 0.117679600000000E6), + float3(0.043151710000000E6, 0.620625600000000E6, 0.082835480000000E6), + float3(0.079629170000000E6, 0.718089000000000E6, 0.056504070000000E6), + float3(0.126846800000000E6, 0.794644800000000E6, 0.037519120000000E6), + float3(0.181802600000000E6, 0.857579900000000E6, 0.024381640000000E6), + float3(0.240501500000000E6, 0.907134700000000E6, 0.015661740000000E6), + float3(0.309811700000000E6, 0.954467500000000E6, 0.009846470000000E6), + float3(0.380424400000000E6, 0.981410600000000E6, 0.006131421000000E6), + float3(0.449420600000000E6, 0.989022800000000E6, 0.003790291000000E6), + float3(0.528023300000000E6, 0.999460800000000E6, 0.002327186000000E6), + float3(0.613378400000000E6, 0.996773700000000E6, 0.001432128000000E6), + float3(0.701677400000000E6, 0.990254900000000E6, 0.000882253100000E6), + float3(0.796775000000000E6, 0.973261100000000E6, 0.000545241600000E6), + float3(0.885337600000000E6, 0.942456900000000E6, 0.000338673900000E6), + float3(0.963838800000000E6, 0.896361300000000E6, 0.000211777200000E6), + float3(1.051011000000000E6, 0.858720300000000E6, 0.000133503100000E6), + float3(1.109767000000000E6, 0.811586800000000E6, 0.000084944680000E6), + float3(1.143620000000000E6, 0.754478500000000E6, 0.000054607060000E6), + float3(1.151033000000000E6, 0.691855300000000E6, 0.000035496610000E6), + float3(1.134757000000000E6, 0.627006600000000E6, 0.000023347380000E6), + float3(1.083928000000000E6, 0.558374600000000E6, 0.000015546310000E6), + float3(1.007344000000000E6, 0.489595000000000E6, 0.000010483870000E6), + float3(0.914287700000000E6, 0.422989700000000E6, 0E6), + float3(0.813556500000000E6, 0.360924500000000E6, 0E6), + float3(0.692471700000000E6, 0.298086500000000E6, 0E6), + float3(0.575541000000000E6, 0.241690200000000E6, 0E6), + float3(0.473122400000000E6, 0.194312400000000E6, 0E6), + float3(0.384498600000000E6, 0.154739700000000E6, 0E6), + float3(0.299737400000000E6, 0.119312000000000E6, 0E6), + float3(0.227779200000000E6, 0.089795940000000E6, 0E6), + float3(0.170791400000000E6, 0.066710450000000E6, 0E6), + float3(0.126380800000000E6, 0.048996990000000E6, 0E6), + float3(0.092245970000000E6, 0.035599820000000E6, 0E6), + float3(0.066399600000000E6, 0.025542230000000E6, 0E6), + float3(0.047106060000000E6, 0.018079390000000E6, 0E6), + float3(0.032921380000000E6, 0.012615730000000E6, 0E6), + float3(0.022623060000000E6, 0.008661284000000E6, 0E6), + float3(0.015754170000000E6, 0.006027677000000E6, 0E6), + float3(0.010967780000000E6, 0.004195941000000E6, 0E6), + float3(0.007608750000000E6, 0.002910864000000E6, 0E6), + float3(0.005214608000000E6, 0.001995557000000E6, 0E6), + float3(0.003569452000000E6, 0.001367022000000E6, 0E6), + float3(0.002464821000000E6, 0.000944726900000E6, 0E6), + float3(0.001703876000000E6, 0.000653705000000E6, 0E6), + float3(0.001186238000000E6, 0.000455597000000E6, 0E6), + float3(0.000826953500000E6, 0.000317973800000E6, 0E6), + float3(0.000575830300000E6, 0.000221744500000E6, 0E6), + float3(0.000405830300000E6, 0.000156556600000E6, 0E6), + float3(0.000285657700000E6, 0.000110392800000E6, 0E6), + float3(0.000202185300000E6, 0.000078274420000E6, 0E6), + float3(0.000143827000000E6, 0.000055788620000E6, 0E6), + float3(0.000102468500000E6, 0.000039818840000E6, 0E6), + float3(0.000073475510000E6, 0.000028601750000E6, 0E6), + float3(0.000052598700000E6, 0.000020512590000E6, 0E6), + float3(0.000038061140000E6, 0.000014872430000E6, 0E6), + float3(0.000027582220000E6, 0.000010800010000E6, 0E6), + float3(0.000020041220000E6, 0.000007863920000E6, 0E6), + float3(0.000014587920000E6, 0.000005736935000E6, 0E6), + float3(0.000010681410000E6, 0.000004211597000E6, 0E6), + float3(0.000007857521000E6, 0.000003106561000E6, 0E6), + float3(0.000005768284000E6, 0.000002286786000E6, 0E6), + float3(0.000004259166000E6, 0.000001693147000E6, 0E6), + float3(0.000003167765000E6, 0.000001262556000E6, 0E6), + float3(0.000002358723000E6, 0.000000942251400E6, 0E6), + float3(0.000001762465000E6, 0.000000705386000E6, 0E6) + }; + + // TODO: convert to RGB + float3x3 xyz2srgb = { + 3.2404542, -1.5371385, -0.4985314, + -0.9692660, 1.8760108, 0.0415560, + 0.0556434, -0.2040259, 1.0572252 + }; + + float3x3 xyz2rgb = { + 0.41847, -0.15866, -0.082835, + -0.091169, 0.25243, 0.015708, + 0.00092090, -0.0025498, 0.17860 + }; + + float3 XYZ = float3(0, 0, 0); + [unroll] + for (uint i = 0u; i < 89u; ++i) { + float w = (390.0 + 5.0*float(i-1)); + XYZ += (3.7402 * table2[i])/(pow(w, 5.0) * 1E-14 * (exp(14384800.0/(w*T)) - 1.0)); + //XYZ += 3.7402/(pow(w, 5.0) * 1E-20 * (exp(1.43848/(w*0.0000001*T)) - 1.0)) * table[i]; + } + XYZ *= 5.0; // integrate with step size + + float3 result = mul(xyz2srgb, XYZ); + + #if defined(HAS_BLEND_FACTOR) + //result = lerp(result, normalize(result), blend); + //TODO: convert to xyZ space and lerp http://magnuswrenninge.com/content/pubs/ProductionVolumeRenderingSystems2011.pdf + #endif + + RESULT(result); +""" diff --git a/vmf_source/core/shader_nodes/blend_normals.shader_node b/vmf_source/core/shader_nodes/blend_normals.shader_node new file mode 100644 index 0000000..a40c5b9 --- /dev/null +++ b/vmf_source/core/shader_nodes/blend_normals.shader_node @@ -0,0 +1,49 @@ +group = "Utility" +display_name = "Blend Normals" +inputs = { + "67493629-fffe-4fe8-bf7c-0c6467b09013" = { name = "base" display_name = "Base" type = "vector3" } + "22ed0f5a-9b5c-4e06-80d6-46eec7c75e34" = { name = "detail" display_name = "Detail" type = "vector3" } +} + +output = { + type = { typeof: "base" } +} + +options = { + "8ad8224b-0141-4598-8240-c9d6fbbd2508" = "WHITEOUT" + "f2ff7295-8050-4f0c-ba42-3d1aa83de416" = "IGNOREZ" + "bac6bd71-9ed1-4948-886f-00fb6cf48489" = "REORIENTED" +} + +ui = [ + { + type = "drop_down" + display_name = "Method" + options = { + "Whiteout" = "8ad8224b-0141-4598-8240-c9d6fbbd2508" + "Ignore Detail Z" = "f2ff7295-8050-4f0c-ba42-3d1aa83de416" + "Reoriented" = "bac6bd71-9ed1-4948-886f-00fb6cf48489" + } + default = "8ad8224b-0141-4598-8240-c9d6fbbd2508" + } +] + +code = """ + // we assume the user has set the sample_texture node to normal_map and + // the decode step (*2 -1) has already happened on our inputs + float3 blended_normals; + float2 xy = base.xy + detail.xy; + #if defined(IGNOREZ) + blended_normals = normalize(new_float3(xy.x, xy.y, base.z)); + #elif defined(REORIENTED) + // Since our decode_normal step (*2 -1) already unpacks the normals, we compensate the + // original math below to give the same results + float3 t = new_float3(base.x, base.y, base.z + 1.0); + float3 u = detail.xyz * new_float3(-1.0, -1.0, 1.0); + blended_normals = normalize(t*dot(t, u) - u*t.z); + #else // whiteout + blended_normals = normalize(new_float3(xy.x, xy.y, base.z*detail.z)); + #endif + + RESULT(blended_normals); +""" diff --git a/vmf_source/core/shader_nodes/camera_position.shader_node b/vmf_source/core/shader_nodes/camera_position.shader_node new file mode 100644 index 0000000..b570f0c --- /dev/null +++ b/vmf_source/core/shader_nodes/camera_position.shader_node @@ -0,0 +1,18 @@ +group = "Input" +display_name = "Camera Position" + +imports = { + camera_pos = { + type = "float3" + domain = "global" + source = "engine" + } +} + +output = { + type = { typeof: "camera_pos" } +} + +code = """ + RESULT(camera_pos); +""" diff --git a/vmf_source/core/shader_nodes/ceil.shader_node b/vmf_source/core/shader_nodes/ceil.shader_node new file mode 100644 index 0000000..721851f --- /dev/null +++ b/vmf_source/core/shader_nodes/ceil.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Ceil" +inputs = { + "15a35abd-ee72-4498-8233-3542fde59a81" = { name = "a" display_name = "A" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(ceil(a)); +""" diff --git a/vmf_source/core/shader_nodes/clamp.shader_node b/vmf_source/core/shader_nodes/clamp.shader_node new file mode 100644 index 0000000..b3c4dbc --- /dev/null +++ b/vmf_source/core/shader_nodes/clamp.shader_node @@ -0,0 +1,15 @@ +group = "Math" +display_name = "Clamp" +inputs = { + "be3ca146-3448-40d8-bd4b-da66ef6ff935" = { name = "x" display_name = "Value" type = "auto" } + "6f471281-8e67-4fd5-978f-5e378ef0668d" = { name = "max" display_name = "Max" type = "auto" } + "e64169ad-12a5-41ba-a708-4cc5526e1ea0" = { name = "min" display_name = "Min" type = "auto" } +} + +output = { + type = { typeof: "x" } +} + +code = """ + RESULT(clamp(x,min,max)); +""" diff --git a/vmf_source/core/shader_nodes/colormap_jet.shader_node b/vmf_source/core/shader_nodes/colormap_jet.shader_node new file mode 100644 index 0000000..8566a0c --- /dev/null +++ b/vmf_source/core/shader_nodes/colormap_jet.shader_node @@ -0,0 +1,19 @@ +group = "Color Map" +display_name = "Jet" +inputs = { + "ff0fa2dd-930d-44f4-ab07-bbf75dd71f2e" = { name = "v" display_name = "A" type = "scalar" } +} + +output = { + type = "float3" +} + +code = """ + v *= 4.0f; + float3 result = float3( + saturate(min(v - 1.5f, -v + 4.5f)), + saturate(min(v - 0.5f, -v + 3.5f)), + saturate(min(v + 0.5f, -v + 2.5f))); + + RESULT(result); +""" diff --git a/vmf_source/core/shader_nodes/constant_scalar.shader_node b/vmf_source/core/shader_nodes/constant_scalar.shader_node new file mode 100644 index 0000000..cbbca39 --- /dev/null +++ b/vmf_source/core/shader_nodes/constant_scalar.shader_node @@ -0,0 +1,13 @@ +group = "Constant" +display_name = "Constant Scalar" +inputs = { + "c4d6bc08-c489-430f-a836-ed490e59c3f9" = { name = "a" display_name = "A" type = "scalar"} +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(a); +""" diff --git a/vmf_source/core/shader_nodes/constant_vector2.shader_node b/vmf_source/core/shader_nodes/constant_vector2.shader_node new file mode 100644 index 0000000..e692932 --- /dev/null +++ b/vmf_source/core/shader_nodes/constant_vector2.shader_node @@ -0,0 +1,13 @@ +group = "Constant" +display_name = "Constant Vector2" +inputs = { + "6ff26be7-68a1-4b89-b9dd-551d216086c2" = { name = "a" display_name = "XY" type = "float2"} +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(a); +""" diff --git a/vmf_source/core/shader_nodes/constant_vector3.shader_node b/vmf_source/core/shader_nodes/constant_vector3.shader_node new file mode 100644 index 0000000..89d94c4 --- /dev/null +++ b/vmf_source/core/shader_nodes/constant_vector3.shader_node @@ -0,0 +1,13 @@ +group = "Constant" +display_name = "Constant Vector3" +inputs = { + "6ff26be7-68a1-4b89-b9dd-551d216086c2" = { name = "a" display_name = "RGB" type = "float3"} +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(a); +""" diff --git a/vmf_source/core/shader_nodes/constant_vector4.shader_node b/vmf_source/core/shader_nodes/constant_vector4.shader_node new file mode 100644 index 0000000..afb2c3d --- /dev/null +++ b/vmf_source/core/shader_nodes/constant_vector4.shader_node @@ -0,0 +1,13 @@ +group = "Constant" +display_name = "Constant Vector4" +inputs = { + "6ff26be7-68a1-4b89-b9dd-551d216086c2" = { name = "a" display_name = "RGBA" type = "float4"} +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(a); +""" diff --git a/vmf_source/core/shader_nodes/construct_vector2.shader_node b/vmf_source/core/shader_nodes/construct_vector2.shader_node new file mode 100644 index 0000000..5a03fec --- /dev/null +++ b/vmf_source/core/shader_nodes/construct_vector2.shader_node @@ -0,0 +1,14 @@ +group = "Construct" +display_name = "Construct Vector2" +inputs = { + "8231a2cb-b751-4ecd-94eb-3e6d048ba173" = { name = "x" display_name = "X" type = "scalar"} + "46536067-b418-47a5-bc6b-234972e963e9" = { name = "y" display_name = "Y" type = "scalar"} +} + +output = { + type = "float2" +} + +code = """ + RESULT(float2(x,y)); +""" diff --git a/vmf_source/core/shader_nodes/construct_vector3.shader_node b/vmf_source/core/shader_nodes/construct_vector3.shader_node new file mode 100644 index 0000000..c0f77bc --- /dev/null +++ b/vmf_source/core/shader_nodes/construct_vector3.shader_node @@ -0,0 +1,15 @@ +group = "Construct" +display_name = "Construct Vector3" +inputs = { + "c010729f-6137-412c-99a5-f263fdeaa53c" = { name = "r" display_name = "R" type = "scalar"} + "d6179868-364c-4f38-bdc0-ea639b305015" = { name = "g" display_name = "G" type = "scalar"} + "6cce8b1d-5c70-44ea-bea3-b1ceafeb750e" = { name = "b" display_name = "B" type = "scalar"} +} + +output = { + type = "float3" +} + +code = """ + RESULT(float3(r,g,b)); +""" diff --git a/vmf_source/core/shader_nodes/construct_vector4.shader_node b/vmf_source/core/shader_nodes/construct_vector4.shader_node new file mode 100644 index 0000000..9e6c3fc --- /dev/null +++ b/vmf_source/core/shader_nodes/construct_vector4.shader_node @@ -0,0 +1,16 @@ +group = "Construct" +display_name = "Construct Vector4" +inputs = { + "e51d4226-628e-4714-809e-af60a5a09c87" = { name = "r" display_name = "R" type = "scalar"} + "00e9b0e5-91d3-4102-8909-de18ecefb1b5" = { name = "g" display_name = "G" type = "scalar"} + "49508d27-9f26-4716-aba6-3b2684ba4b08" = { name = "b" display_name = "B" type = "scalar"} + "d2e05875-2413-4431-af0f-0de8658cff7d" = { name = "a" display_name = "A" type = "scalar"} +} + +output = { + type = "float4" +} + +code = """ + RESULT(float4(r,g,b,a)); +""" diff --git a/vmf_source/core/shader_nodes/cos.shader_node b/vmf_source/core/shader_nodes/cos.shader_node new file mode 100644 index 0000000..cc720c5 --- /dev/null +++ b/vmf_source/core/shader_nodes/cos.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Cosine" +inputs = { + "d0ff3f8a-3bc5-4a02-8edf-562e3985985e" = { name = "a" display_name = "Angle" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(cos(a)); +""" diff --git a/vmf_source/core/shader_nodes/cross.shader_node b/vmf_source/core/shader_nodes/cross.shader_node new file mode 100644 index 0000000..7052634 --- /dev/null +++ b/vmf_source/core/shader_nodes/cross.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Cross Product" +inputs = { + "316b7c7c-7541-4973-ba8d-322cd8525716" = { name = "a" display_name = "A" type = "vector3" } + "295b5797-0402-479f-93cc-77708fcd453f" = { name = "b" display_name = "B" type = "vector3" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(cross(a,b)); +""" diff --git a/vmf_source/core/shader_nodes/current_time.shader_node b/vmf_source/core/shader_nodes/current_time.shader_node new file mode 100644 index 0000000..413e278 --- /dev/null +++ b/vmf_source/core/shader_nodes/current_time.shader_node @@ -0,0 +1,18 @@ +group = "Input" +display_name = "Time" + +imports = { + time = { + type = "float" + domain = "global" + source = "engine" + } +} + +output = { + type = { typeof: "time" } +} + +code = """ + RESULT(time); +""" diff --git a/vmf_source/core/shader_nodes/ddx.shader_node b/vmf_source/core/shader_nodes/ddx.shader_node new file mode 100644 index 0000000..c22e186 --- /dev/null +++ b/vmf_source/core/shader_nodes/ddx.shader_node @@ -0,0 +1,19 @@ +group = "Math" +display_name = "Ddx" +inputs = { + "F301E39A-AA1D-4F5E-BA28-3BEFBEB7AF1D" = { name = "a" display_name = "A" type = "auto" } +} + +domain = "pixel" + +output = { + type = { typeof: "a" } +} + +code = """ + #if defined(STAGE_PIXEL) + RESULT(ddx(a)); + #else + RESULT(new_float3(0, 0, 0)); + #endif +""" diff --git a/vmf_source/core/shader_nodes/ddy.shader_node b/vmf_source/core/shader_nodes/ddy.shader_node new file mode 100644 index 0000000..386bba0 --- /dev/null +++ b/vmf_source/core/shader_nodes/ddy.shader_node @@ -0,0 +1,19 @@ +group = "Math" +display_name = "Ddy" +inputs = { + "FF05235F-0371-4500-8CD3-2B849D67EA92" = { name = "a" display_name = "A" type = "auto" } +} + +domain = "pixel" + +output = { + type = { typeof: "a" } +} + +code = """ + #if defined(STAGE_PIXEL) + RESULT(ddy(a)); + #else + RESULT(a); // Need a way to splat a vector of 'a_type' to return 0 + #endif +""" diff --git a/vmf_source/core/shader_nodes/decal_box_distance.shader_node b/vmf_source/core/shader_nodes/decal_box_distance.shader_node new file mode 100644 index 0000000..47b00be --- /dev/null +++ b/vmf_source/core/shader_nodes/decal_box_distance.shader_node @@ -0,0 +1,23 @@ +group = "Decal" +display_name = "Decal Box Edge Distance" +depends_on = ["core/stingray_renderer/output_nodes/decal_base"] + + +defines = ["NEEDS_OBJECT_POSITION"] + +imports = { + object_position = { + type = "float3" + domain = "pixel" + output_channel = "object_position" + } +} + +output = { + type = "float" +} + +code = """ + float3 dist = min(object_position - bounding_volume._m00_m01_m02, bounding_volume._m10_m11_m12 - object_position) * bounding_volume._m20_m21_m22; + RESULT(min(dist.x, min(dist.y, dist.z))); +""" \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/decal_parallax.shader_node b/vmf_source/core/shader_nodes/decal_parallax.shader_node new file mode 100644 index 0000000..20e6a58 --- /dev/null +++ b/vmf_source/core/shader_nodes/decal_parallax.shader_node @@ -0,0 +1,66 @@ +group = "Decal" +display_name = "Decal Parallax / Bump Offset" +depends_on = ["core/stingray_renderer/output_nodes/decal_base"] + +// scale and bias map the height value into a range that better represents the physical properties (size) of the surface +inputs = { + "95FB056C-9DDF-434C-8D8E-A03F30F5C42D" = { name = "uv" display_name = "UV" type = "vector2"} + "B897C7FC-96E3-45D9-9AF1-A1C797386C61" = { name = "height" display_name = "Height" type = "scalar"} + "6046b049-ae9d-49f6-9f75-58a34a3bac15" = { name = "scale" display_name = "Parallax Scale" is_required = false type = { scalar: ["HAS_SCALE"] }} + "ed989589-8b44-4ec4-b5a7-8fb5cd739854" = { name = "bias" display_name = "Parallax Bias" is_required = false type = { scalar: ["HAS_BIAS"] }} +} + +defines = ["NEEDS_TANGENT_SPACE", "NEEDS_EYE_VECTOR"] + +imports = { + eye_vector = { + type = "float3" + domain = "pixel" + output_channel = "eye_vector" + } + tsm0 = { + type = "float3" + domain = "pixel" + output_channel = "tsm0" + } + tsm1 = { + type = "float3" + domain = "pixel" + output_channel = "tsm1" + } + tsm2 = { + type = "float3" + domain = "pixel" + output_channel = "tsm2" + } +} + +output = { + type = { typeof: "uv" } +} + +code = """ + float3 dir = normalize(eye_vector); + + // Get camera vector in tangent space + float3 dir_ts = new_float3( + dot(dir, float3(tsm0.x, tsm1.x, tsm2.x)), + dot(dir, float3(tsm0.y, tsm1.y, tsm2.y)), + dot(dir, float3(tsm0.z, tsm1.z, tsm2.z))); + + float2 norm_dir_ts = normalize(dir_ts).xy; + + #if defined(HAS_SCALE) + float scale_value = scale; + #else + float scale_value = 0.04; + #endif + + #if defined(HAS_BIAS) + float bias_value = bias; + #else + float bias_value = 0.02; + #endif + + RESULT(uv + float2(norm_dir_ts.x, -norm_dir_ts.y) * (height*scale_value - bias_value)); +""" diff --git a/vmf_source/core/shader_nodes/decal_tangent_to_world.shader_node b/vmf_source/core/shader_nodes/decal_tangent_to_world.shader_node new file mode 100644 index 0000000..3d0d764 --- /dev/null +++ b/vmf_source/core/shader_nodes/decal_tangent_to_world.shader_node @@ -0,0 +1,39 @@ +group = "Decal" +display_name = "Decal Tangent To World" +depends_on = ["core/stingray_renderer/output_nodes/decal_base"] + +inputs = { + "f72597c4-7487-419a-affb-df690e6582e1" = { name = "v" display_name = "Vector" type = "float3" } +} + +defines = ["NEEDS_TANGENT_SPACE"] + +imports = { + tsm0 = { + type = "float3" + domain = "pixel" + output_channel = "tsm0" + } + tsm1 = { + type = "float3" + domain = "pixel" + output_channel = "tsm1" + } + tsm2 = { + type = "float3" + domain = "pixel" + output_channel = "tsm2" + } +} + +output = { + type = { typeof: "v" } +} + +code = """ + float3 res = float3( + dot(v, tsm0), + dot(v, tsm1), + dot(v, tsm2)); + RESULT(normalize(res)); +""" diff --git a/vmf_source/core/shader_nodes/decal_uv.shader_node b/vmf_source/core/shader_nodes/decal_uv.shader_node new file mode 100644 index 0000000..6b47ef7 --- /dev/null +++ b/vmf_source/core/shader_nodes/decal_uv.shader_node @@ -0,0 +1,19 @@ +group = "Decal" +display_name = "Decal UV" +depends_on = ["core/stingray_renderer/output_nodes/decal_base"] + +imports = { + decal_uv = { + type = "float2" + domain = "pixel" + output_channel = "decal_uv" + } +} + +output = { + type = { typeof: "decal_uv" } +} + +code = """ + RESULT(decal_uv); +""" diff --git a/vmf_source/core/shader_nodes/decal_world_position.shader_node b/vmf_source/core/shader_nodes/decal_world_position.shader_node new file mode 100644 index 0000000..bde6344 --- /dev/null +++ b/vmf_source/core/shader_nodes/decal_world_position.shader_node @@ -0,0 +1,22 @@ +group = "Decal" +display_name = "Decal World Position" +depends_on = ["core/stingray_renderer/output_nodes/decal_base"] + + +defines = ["NEEDS_WORLD_SPACE_POSITION"] + +imports = { + world_position = { + type = "float3" + domain = "pixel" + output_channel = "world_position" + } +} + +output = { + type = "float3" +} + +code = """ + RESULT(world_position); +""" \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/desaturation.shader_node b/vmf_source/core/shader_nodes/desaturation.shader_node new file mode 100644 index 0000000..d154b23 --- /dev/null +++ b/vmf_source/core/shader_nodes/desaturation.shader_node @@ -0,0 +1,28 @@ +group = "Utility" +display_name = "Desaturation" +// Luminance controls what channel is brighter then another. E.g. Green is brighter then red and blue when desaturated +inputs = { + "1E4F2254-F27B-418F-AB46-9229A599980F" = { name = "color" display_name = "Color" type = "vector3" } + "FFCB2ED4-2061-432C-8AB5-E1A9EDED9038" = { name = "amount" display_name = "Amount" is_required = false type = { scalar: ["HAS_AMOUNT"] } } + "fb2bf997-f6f1-42a0-bd29-b6a12f9f3417" = { name = "luminance" display_name = "Luminance" is_required = false type = { vector3: ["HAS_LUM"] }} +} + +output = { + type = { typeof: "color" } +} + +code = """ + #if defined(HAS_LUM) + float3 lum = luminance; + #else + float3 lum = float3(0.3, 0.6, 0.1); + #endif + + float l = dot(lum, color); + float3 desaturated = new_float3(l, l, l); + #if defined(HAS_AMOUNT) + RESULT(lerp(color, desaturated, amount)); + #else + RESULT(desaturated); + #endif +""" diff --git a/vmf_source/core/shader_nodes/distance.shader_node b/vmf_source/core/shader_nodes/distance.shader_node new file mode 100644 index 0000000..3ea262a --- /dev/null +++ b/vmf_source/core/shader_nodes/distance.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Distance" +inputs = { + "4baab897-370b-4def-9790-ece5b1abf91f" = { name = "a" display_name = "A" type = { vector2:[] vector3:[] vector4:[] } } + "6fc97f2b-a585-4430-9f2d-93069a891bfc" = { name = "b" display_name = "B" type = { vector2:[] vector3:[] vector4:[] } } +} + +output = { + type = "float" +} + +code = """ + RESULT(distance(a, b)); +""" diff --git a/vmf_source/core/shader_nodes/distance_fade.shader_node b/vmf_source/core/shader_nodes/distance_fade.shader_node new file mode 100644 index 0000000..3be0a0a --- /dev/null +++ b/vmf_source/core/shader_nodes/distance_fade.shader_node @@ -0,0 +1,72 @@ +group = "Fatshark" +display_name = "Distance Fade" + +depends_on = [ + "core/stingray_renderer/output_nodes/standard_base" + "core/stingray_renderer/output_nodes/unlit_base", + "core/stingray_renderer/output_nodes/particle_base", + "core/stingray_renderer/output_nodes/particle_gbuffer_base", + "core/stingray_renderer/output_nodes/particle_distortion_base"] + +inputs = { + "01a82a06-57f6-4dc2-8a63-f3c7119064aa" = { + name = "depth_fade_distance" + display_name = "Depth Fade Distance" + type = "scalar" + domain = "pixel" + is_required = true + } +} + +imports = { + pixel_depth = { + type = "float" + domain = "pixel" + output_channel = "pixel_depth" + } + + screen_pos = { + type = "float2" + domain = "pixel" + output_channel = "screen_pos" + } +} + +defines = ["NEEDS_PIXEL_DEPTH", "NEEDS_LINEAR_DEPTH", "NEEDS_SCREEN_POS"] + +domain = "pixel" +output = { + type = "float" +} + +options = { + "b110967d-d67a-485f-af33-90ad4bed2eec" = "SMOOTH_STEP" +} + + +ui = [ + { + type = "drop_down" + display_name = "Fade Curve" + options = { + "Linear" = "00000000-0000-0000-0000-000000000000" + "Smooth" = "b110967d-d67a-485f-af33-90ad4bed2eec" + } + default = "00000000-0000-0000-0000-000000000000" + } +] + +language = "hlsl" +code = """ + #if defined(HAS_LINEAR_DEPTH) + float gbuffer_depth = gbuffer_decode_depth(TEX2D(linear_depth, screen_pos.xy)); + #ifdef SMOOTH_STEP + float result = smoothstep(gbuffer_depth, gbuffer_depth - depth_fade_distance, pixel_depth); + #else + float result = saturate((gbuffer_depth - pixel_depth) / depth_fade_distance); + #endif + #else + float result = 1.0; + #endif + RESULT(result); +""" diff --git a/vmf_source/core/shader_nodes/div.shader_node b/vmf_source/core/shader_nodes/div.shader_node new file mode 100644 index 0000000..8b92276 --- /dev/null +++ b/vmf_source/core/shader_nodes/div.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Divide" +inputs = { + "78dddd43-ff51-4173-bb0c-99d8c27b6bb2" = { name = "a" display_name = "A" type = "auto" } + "03986150-1b08-4045-80c9-d08d58842627" = { name = "b" display_name = "B" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT(a / b); +""" diff --git a/vmf_source/core/shader_nodes/dot.shader_node b/vmf_source/core/shader_nodes/dot.shader_node new file mode 100644 index 0000000..77838f9 --- /dev/null +++ b/vmf_source/core/shader_nodes/dot.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Dot Product" +inputs = { + "1227923c-0ef1-4823-b673-301cc9a5fc69" = { name = "a" display_name = "A" type = "auto" } + "0e627d0f-2503-4925-9811-d44aca00e49c" = { name = "b" display_name = "B" type = "auto" } +} + +output = { + type = "float" +} + +code = """ + RESULT(dot(a,b)); +""" diff --git a/vmf_source/core/shader_nodes/exp.shader_node b/vmf_source/core/shader_nodes/exp.shader_node new file mode 100644 index 0000000..939775c --- /dev/null +++ b/vmf_source/core/shader_nodes/exp.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Exp" +inputs = { + "837f2c43-756b-4f27-8911-b623e50b982a" = { name = "a" display_name = "A" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(exp(a)); +""" diff --git a/vmf_source/core/shader_nodes/eye_intensity.shader_node b/vmf_source/core/shader_nodes/eye_intensity.shader_node new file mode 100644 index 0000000..0bf32bc --- /dev/null +++ b/vmf_source/core/shader_nodes/eye_intensity.shader_node @@ -0,0 +1,18 @@ +group = "Fatshark" +display_name = "Eye Intensity" + +imports = { + eye_intensity = { + type = "float" + domain = "global" + source = "shading_environment" + } +} + +output = { + type = { typeof: "eye_intensity" } +} + +code = """ + RESULT(eye_intensity); +""" diff --git a/vmf_source/core/shader_nodes/eye_vector.shader_node b/vmf_source/core/shader_nodes/eye_vector.shader_node new file mode 100644 index 0000000..63a4b46 --- /dev/null +++ b/vmf_source/core/shader_nodes/eye_vector.shader_node @@ -0,0 +1,25 @@ +group = "Input" +display_name = "Eye Vector" +depends_on = [ + "core/stingray_renderer/output_nodes/standard_base", + "core/stingray_renderer/output_nodes/unlit_base" + "core/stingray_renderer/output_nodes/terrain_base" +] + +imports = { + dir = { + type = "float3" + domain = "vertex" + output_channel = "eye_vector" + } +} + +defines = ["NEEDS_EYE_VECTOR"] + +output = { + type = { typeof: "dir" } +} + +code = """ + RESULT(dir); +""" diff --git a/vmf_source/core/shader_nodes/flipbook.shader_node b/vmf_source/core/shader_nodes/flipbook.shader_node new file mode 100644 index 0000000..6293c55 --- /dev/null +++ b/vmf_source/core/shader_nodes/flipbook.shader_node @@ -0,0 +1,44 @@ +group = "Utility" +display_name = "Flipbook" +inputs = { + "3BA4F382-41EB-4796-B32F-7A8B51BD8DFB" = { name = "fps" display_name = "FPS" is_required = false type = {scalar: ["HAS_FPS"]} } + "75F62304-E764-4DD7-9BDF-A486E92C92F2" = { name = "time" display_name = "Time" type = "scalar" } + "EE08FB38-9E0A-4009-BB48-A40EB8B85092" = { name = "uv" display_name = "UV" type = "vector2" } + "6b59bfdf-20fc-4817-88ea-4c2f0d08d4af" = { name = "sprite_rows" display_name = "Sprite Rows" is_required = false type = { scalar: ["HAS_ROWS"] }} + "3aba8613-fe85-4516-a049-0edd78e35d48" = { name = "sprite_cols" display_name = "Sprite Columns" is_required = false type = { scalar: ["HAS_COLS"] }} +} + +output = { + type = { typeof: "uv" } +} + +code = """ + #if defined(HAS_FPS) + float frame_per_sec = fps; + #else + float frame_per_sec = 5.0; + #endif + + #if defined(HAS_ROWS) + float sprite_per_row = sprite_rows; + #else + float sprite_per_row = 2.0; + #endif + + #if defined(HAS_COLS) + float sprite_per_col = sprite_cols; + #else + float sprite_per_col = 2.0; + #endif + + float current_frame = floor( fmod((frame_per_sec * time), (sprite_per_row * sprite_per_col)) ); + + float sprite_u = fmod(current_frame, sprite_per_row) / sprite_per_row; + float sprite_v = floor(current_frame / sprite_per_row) / sprite_per_col; + + // add local UV offset + sprite_u += uv.x / sprite_per_row; + sprite_v += uv.y / sprite_per_col; + + RESULT(float2(sprite_u, sprite_v)); +""" diff --git a/vmf_source/core/shader_nodes/flipbook_sampler.shader_node b/vmf_source/core/shader_nodes/flipbook_sampler.shader_node new file mode 100644 index 0000000..126ccee --- /dev/null +++ b/vmf_source/core/shader_nodes/flipbook_sampler.shader_node @@ -0,0 +1,185 @@ +group = "Sampling" +display_name = "Flipbook Sample Texture" +inputs = { + "c85ccdaf-b0e2-4526-a617-2b14f402f43c" = { name = "uv" display_name = "UV" type = "vector2" } + "4571da41-20ef-47cc-9811-7ed4d59cd85a" = { name = "mip_level" is_required = false display_name = "Mip Level" type = { scalar: ["HAS_MIPLEVEL"] } } + "1fd6339d-ba9f-4ac8-b463-4a49f5f3a90e" = { name = "fps" display_name = "FPS" is_required = false type = {scalar: ["HAS_FPS"]} } + "42b961f8-1b7f-4f41-98ce-11b98adc05e4" = { name = "time" display_name = "Time" type = "scalar" } + "7c9904c4-2039-451b-a38a-5f77ec69f878" = { name = "sprite_rows" display_name = "Sprite Rows" is_required = false type = { scalar: ["HAS_ROWS"] }} + "eee52ea3-790c-40e0-ab60-c53308bc4043" = { name = "sprite_cols" display_name = "Sprite Columns" is_required = false type = { scalar: ["HAS_COLS"] }} +} + +domain = "pixel" +output = { + type = "float4" +} + +options = { + "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" = "ADDRESS_CLAMP" + "5dd59b3d-1762-4a14-9930-7500230ef3db" = "ADDRESS_WRAP" + "f669a3a6-0376-4187-840e-80000e2939d5" = "FILTER_LINEAR" + "43dea0e2-a77d-410d-88bb-945dac9139d8" = "FILTER_POINT" + "1e067464-12d8-4826-9b72-cfd5765003e3" = "FILTER_ANISOTROPIC" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" = "SRGB" + "43710e4f-f52a-4038-8ec8-d6cb0546103b" = "RGBM_DECODE" + "e94e53e6-49b6-4194-a747-8f064a5932e0" = "LINEAR" + "0268506C-B417-49DC-BBBE-3D5949595940" = "FLIP_GREEN" +} + +ui = [ + { + type = "drop_down" + display_name = "Encoding" + options = { + "Linear Color" = "e94e53e6-49b6-4194-a747-8f064a5932e0" + "sRGB Color" = "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + "RGBM Color" = "43710e4f-f52a-4038-8ec8-d6cb0546103b" + } + default = "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + } + { + type = "drop_down" + display_name = "Address mode" + options = { + "Clamp" = "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" + "Wrap" = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + default = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + { + type = "drop_down" + display_name = "Filter mode" + options = { + "Anisotropic" = "1e067464-12d8-4826-9b72-cfd5765003e3" + "Linear" = "f669a3a6-0376-4187-840e-80000e2939d5" + "Point" = "43dea0e2-a77d-410d-88bb-945dac9139d8" + } + default = "1e067464-12d8-4826-9b72-cfd5765003e3" + } + { type = "checkbox" display_name = "Invert Green Channel" option = "0268506C-B417-49DC-BBBE-3D5949595940" } +] + +code_blocks = { + default = { + include: ["misc"] + language = "hlsl" + samplers = { + texture_map = { + display_name = "Texture" + type = "2d" + sampler_state = "core/shader_nodes/graph_common#default_node_sampler" + source = "material" + } + } + + code = """ + #if defined(HAS_FPS) + float frame_per_sec = fps; + #else + float frame_per_sec = 5.0; + #endif + + #if defined(HAS_ROWS) + float sprite_per_row = sprite_rows; + #else + float sprite_per_row = 2.0; + #endif + + #if defined(HAS_COLS) + float sprite_per_col = sprite_cols; + #else + float sprite_per_col = 2.0; + #endif + + float frame_value = frame_per_sec * time; + + float frame = fmod(frame_value, sprite_per_row * sprite_per_col); + float lerp_value = frac(frame); + float current_frame = floor(frame); + float next_frame = floor(fmod(frame_value + 1.0, sprite_per_row * sprite_per_col)); + + float2 sprites_dim = float2(sprite_per_row, sprite_per_col); + float2 current_uv = calculate_uv(uv, sprites_dim, current_frame); + float2 next_uv = calculate_uv(uv, sprites_dim, next_frame); + + // sample textures + + float4 result_current_frame; + #if defined(HAS_MIPLEVEL) + result_current_frame = sample_texture_mip(texture_map, current_uv, mip_level); + #else + result_current_frame = sample_texture(texture_map, current_uv); + #endif + + float4 result_next_frame; + #if defined(HAS_MIPLEVEL) + result_next_frame = sample_texture_mip(texture_map, next_uv, mip_level); + #else + result_next_frame = sample_texture(texture_map, next_uv); + #endif + + float4 result = lerp(result_current_frame, result_next_frame, lerp_value); + RESULT(result); + """ + } + + misc = { + language = "hlsl" + code=""" + inline float2 calculate_uv(float2 uv, float2 sprites_dim, float frame) + { + float sprite_u = fmod(frame, sprites_dim.x) / sprites_dim.x; + float sprite_v = floor(frame / sprites_dim.x) / sprites_dim.y; + + sprite_u += uv.x / sprites_dim.x; + sprite_v += uv.y / sprites_dim.y; + float2 texcoord = float2(sprite_u, sprite_v); + + return texcoord; + } + + inline float4 sample_texture(Sampler2D texture_map, float2 texcoord) + { + float4 result; + result = TEX2D(texture_map, texcoord); + + #if defined(FLIP_GREEN) + result.y = 1.0-result.y; + #endif + + #if defined(RENDERER_GL) && defined(SRGB) + result = fast_gamma_to_linear_rgb(result); + #endif + + #if defined(RGBM_DECODE) + result = float4(rgbm_decode(result), result.a); + #endif + + return result; + } + + inline float4 sample_texture_mip(Sampler2D texture_map, float2 texcoord, float mip_level) + { + float4 result; + result = TEX2DLOD(texture_map, texcoord, mip_level); + + #if defined(FLIP_GREEN) + result.y = 1.0-result.y; + #endif + + #if defined(RENDERER_GL) && defined(SRGB) + result = fast_gamma_to_linear_rgb(result); + #endif + + #if defined(RGBM_DECODE) + result = float4(rgbm_decode(result), result.a); + #endif + + return result; + } + """ + } +} + + + diff --git a/vmf_source/core/shader_nodes/floor.shader_node b/vmf_source/core/shader_nodes/floor.shader_node new file mode 100644 index 0000000..1a12629 --- /dev/null +++ b/vmf_source/core/shader_nodes/floor.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Floor" +inputs = { + "9535d4e4-6a99-4590-952d-c2c11d0872b6" = { name = "a" display_name = "A" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(floor(a)); +""" diff --git a/vmf_source/core/shader_nodes/fmod.shader_node b/vmf_source/core/shader_nodes/fmod.shader_node new file mode 100644 index 0000000..863d52d --- /dev/null +++ b/vmf_source/core/shader_nodes/fmod.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Fmod" +inputs = { + "f4e67b79-cc94-444f-99ca-c01316eb4cab" = { name = "a" display_name = "A" type = "auto" } + "abb0de2c-a337-4cb5-a2c1-5ffb3770a960" = { name = "b" display_name = "B" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT(fmod(a,b)); +""" diff --git a/vmf_source/core/shader_nodes/frac.shader_node b/vmf_source/core/shader_nodes/frac.shader_node new file mode 100644 index 0000000..fa02370 --- /dev/null +++ b/vmf_source/core/shader_nodes/frac.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Fractional (Decimal)" +inputs = { + "06acde87-5eb5-4800-881d-ed7c05de6ac6" = { name = "a" display_name = "A" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(frac(a)); +""" diff --git a/vmf_source/core/shader_nodes/fresnel.shader_node b/vmf_source/core/shader_nodes/fresnel.shader_node new file mode 100644 index 0000000..ec6a84f --- /dev/null +++ b/vmf_source/core/shader_nodes/fresnel.shader_node @@ -0,0 +1,85 @@ +group = "Utility" +display_name = "Fresnel" +inputs = { + "904B4365-2955-4899-B615-62A05F0D5726" = { name = "custom_min" display_name = "Minimum" is_required = false type = {scalar: ["HAS_FRESNELMIN"]} } + "B89878F6-57BC-4491-9825-E28D1A96A519" = { name = "custom_max" display_name = "Maximum / Exponent" is_required = false type = {scalar: ["HAS_FRESNELMAX"]} } + "98C44AA8-6F34-4C99-8AD9-CA8A787F12BB" = { name = "custom_normal" display_name = "Normal" is_required = false type = {vector3: ["HAS_CUSTOMNORMAL"]} } +} + +depends_on = ["core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/unlit_base", + "core/stingray_renderer/output_nodes/billboard_base", "core/stingray_renderer/output_nodes/billboard_unlit_base"] + +output = { + type = "float" +} + +defines = ["NEEDS_WORLD_SPACE_NORMAL", "NEEDS_EYE_VECTOR"] + +imports = { + normal = { + type = "float3" + domain = "vertex" + output_channel = "world_space_normal" + } + eye_vector = { + type = "float3" + domain = "vertex" + output_channel = "eye_vector" + } +} + +options = { + "8E67341F-137F-4555-9E8C-FAE0E41D3D0D" = "USE_MINMAX" + "9653C6EC-D10A-4EBC-B76F-E2E8EABB9194" = "USE_EXPONENT" +} + +ui = [ + { + type = "drop_down" + display_name = "Method" + options = { + "Use Min/Max" = "8E67341F-137F-4555-9E8C-FAE0E41D3D0D" + "Use Exponent" = "9653C6EC-D10A-4EBC-B76F-E2E8EABB9194" + } + default = "8E67341F-137F-4555-9E8C-FAE0E41D3D0D" + } +] + + +code = """ + float result = 0.0; + #if defined(HAS_FRESNELMAX) + float maximum = custom_max; + #else + float maximum = 1.0; + #endif + + #if defined(HAS_CUSTOMNORMAL) + float3 n = custom_normal; + #else + float3 n = normalize(normal); + #endif + + // The eye_vector channel is per-vertex, if we interpolate it and use it per-pixel it has to be renormalized. + float3 dir = normalize(eye_vector); + + // method 1, artist friendly Fresnel controls: + #if defined(USE_MINMAX) + + #if defined(HAS_FRESNELMIN) + float minimum = custom_min; + #else + float minimum = 0.8; + #endif + + float v_dot_n = saturate(1.0 - dot(dir, n)); + float range = max(maximum, minimum) - minimum; + result = saturate((v_dot_n - minimum) / range); + + // method 2, classic exponent control: + #else + result = pow(max(1.0 - abs(dot(n, dir)), 0.0001), maximum); + #endif + + RESULT(result); +""" diff --git a/vmf_source/core/shader_nodes/graph_common.shader_source b/vmf_source/core/shader_nodes/graph_common.shader_source new file mode 100644 index 0000000..0112ff0 --- /dev/null +++ b/vmf_source/core/shader_nodes/graph_common.shader_source @@ -0,0 +1,42 @@ +sampler_states = { + default_node_sampler = { + states = { + "defined(TAA_ENABLED)" = { + mip_lod_bias = "-2.0" + } + "defined(ADDRESS_CLAMP)" = { + "on_renderer(GL)" = { + address_u = "address_clamp_to_edge" + address_v = "address_clamp_to_edge" + address_w = "address_clamp_to_edge" + } + "on_renderer(D3D11, D3D12, GNM)" = { + address_u = "address_clamp" + address_v = "address_clamp" + address_w = "address_clamp" + } + } + "defined(ADDRESS_WRAP)" = { + address_u = "address_wrap" + address_v = "address_wrap" + address_w = "address_wrap" + } + "defined(FILTER_LINEAR)" = { + filter = "min_mag_mip_linear" + } + "defined(FILTER_POINT)" = { + filter = "min_mag_mip_point" + } + "defined(FILTER_ANISOTROPIC)" = { + filter = "anisotropic" + max_anisotropy = "8" + } + "defined(SRGB)" = { + srgb = "true" + } + "!defined(SRGB)" = { + srgb = "false" + } + } + } +} \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/hsv_to_rgb.shader_node b/vmf_source/core/shader_nodes/hsv_to_rgb.shader_node new file mode 100644 index 0000000..1d985ac --- /dev/null +++ b/vmf_source/core/shader_nodes/hsv_to_rgb.shader_node @@ -0,0 +1,38 @@ +group = "Utility" +display_name = "HSV to RGB" +inputs = { + "BCBF488E-5D40-43DF-A021-0762C58443EB" = { name = "hsv" display_name = "HSV" type = "vector3" } +} + +output = { + type = { typeof: "hsv" } +} + +code_blocks = { + hsv_to_rgb_block = { + code = """ + + // Convert pure hue to RGB + inline float3 hue_to_rgb( float h ) { + float r = abs(h * 6.0 - 3.0) - 1.0; + float g = 2.0 - abs(h * 6.0 - 2.0); + float b = 2.0 - abs(h * 6.0 - 4.0); + return saturate(float3(r,g,b)); + } + + // HSV to RGB conversion based on work by Sam Hocevar and Emil Persson + // explanation: http://chilliant.blogspot.ca/2014/04/rgbhsv-in-hlsl-5.html + inline float3 hsv_to_rgb( float3 hsv ) { + float3 rgb = hue_to_rgb(hsv.x); + return ((rgb - 1.0) * hsv.y + 1.0) * hsv.z; + } + """ + } + + default = { + include: ["hsv_to_rgb_block"] + code = """ + RESULT(hsv_to_rgb(hsv)); + """ + } +} \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/if.shader_node b/vmf_source/core/shader_nodes/if.shader_node new file mode 100644 index 0000000..ab6919e --- /dev/null +++ b/vmf_source/core/shader_nodes/if.shader_node @@ -0,0 +1,69 @@ +// todo: output_a and output_b should be locked such that both are the same data type +// For now we will assume the user does this manually +group = "Math" +display_name = "If" +inputs = { + "CED7BBF3-0B48-4335-B933-095A41CA0294" = { name = "input_a" display_name = "A" type = "auto" } + "39BC7619-2768-480B-ACFD-63FA66EF6905" = { name = "input_b" display_name = "B" type = "auto" } + "4CBB4480-79E8-4CE7-AC0F-8B09BAF12390" = { name = "output_a" display_name = "True" type = "auto" } + "F2F74E58-402D-472B-87DD-331E00DB416C" = { name = "output_b" display_name = "False" type = "auto" } +} + +output = { + type = { typeof: "output_a" } +} + +options = { + "9A84282B-F1A2-46D4-9FC4-5A76FC9B30DD" = "OP_EQUAL" + "6F615C8C-0C2E-4571-87BF-2131125BB9BD" = "OP_NOTEQUAL" + "9D6AE0AA-46AA-41C6-BD40-2F4030EA9668" = "OP_SMALLER" + "B153EA25-006C-4918-B18D-F934D95B1DDF" = "OP_LARGER" + "C14A2EBF-0E4F-40FC-A77B-552CFE5A8AA8" = "OP_SMALLEREQUAL" + "C5160115-6432-4FB7-9F16-44C0C45B4423" = "OP_LARGEREQUAL" + "B593D445-6CB9-4606-BDEC-87985B85CEE0" = "OP_AND" + "DAD8F6A0-EEE4-4CC1-9D34-B5BDD67FEF44" = "OP_OR" +} + +ui = [ + { + type = "drop_down" + display_name = "Operator" + options = { + "Equal (==)" = "9A84282B-F1A2-46D4-9FC4-5A76FC9B30DD" + "Not Equal (!=)" = "6F615C8C-0C2E-4571-87BF-2131125BB9BD" + "Less (<)" = "9D6AE0AA-46AA-41C6-BD40-2F4030EA9668" + "Greater (>)" = "B153EA25-006C-4918-B18D-F934D95B1DDF" + "Less Equal (<=)" = "C14A2EBF-0E4F-40FC-A77B-552CFE5A8AA8" + "Greater Equal (>=)" = "C5160115-6432-4FB7-9F16-44C0C45B4423" + "And (&&)" = "B593D445-6CB9-4606-BDEC-87985B85CEE0" + "Or (||)" = "DAD8F6A0-EEE4-4CC1-9D34-B5BDD67FEF44" + } + default = "9A84282B-F1A2-46D4-9FC4-5A76FC9B30DD" + } +] + +code = """ + output_a_type result; + + #if defined(OP_EQUAL) + result = (input_a == input_b) ? output_a : output_b; + #elif defined(OP_NOTEQUAL) + result = (input_a != input_b) ? output_a : output_b; + #elif defined(OP_SMALLER) + result = (input_a < input_b) ? output_a : output_b; + #elif defined(OP_LARGER) + result = (input_a > input_b) ? output_a : output_b; + #elif defined(OP_SMALLEREQUAL) + result = (input_a <= input_b) ? output_a : output_b; + #elif defined(OP_LARGEREQUAL) + result = (input_a >- input_b) ? output_a : output_b; + #elif defined(OP_AND) + result = (input_a && input_b) ? output_a : output_b; + #elif defined(OP_OR) + result = (input_a || input_b) ? output_a : output_b; + #else + result = output_a; + #endif + + RESULT( result ); +""" diff --git a/vmf_source/core/shader_nodes/invert.shader_node b/vmf_source/core/shader_nodes/invert.shader_node new file mode 100644 index 0000000..64dcec2 --- /dev/null +++ b/vmf_source/core/shader_nodes/invert.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Invert / One Minus" +inputs = { + "DB6BAC1D-3931-42BD-BD08-829BFBCBAD47" = { name = "a" display_name = "A" type = "auto"} +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(1.0 - a); +""" diff --git a/vmf_source/core/shader_nodes/length.shader_node b/vmf_source/core/shader_nodes/length.shader_node new file mode 100644 index 0000000..2defb95 --- /dev/null +++ b/vmf_source/core/shader_nodes/length.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Length" +inputs = { + "6fc97f2b-a585-4430-9f2d-93069a891bfc" = { name = "v" display_name = "Vector" type = { vector2:[] vector3:[] vector4:[] } } +} + +output = { + type = "float" +} + +code = """ + RESULT(length(v)); +""" diff --git a/vmf_source/core/shader_nodes/lerp.shader_node b/vmf_source/core/shader_nodes/lerp.shader_node new file mode 100644 index 0000000..774efab --- /dev/null +++ b/vmf_source/core/shader_nodes/lerp.shader_node @@ -0,0 +1,15 @@ +group = "Math" +display_name = "Linear Interpolate" +inputs = { + "995996dc-408e-4e6f-a310-0db5b3276075" = { name = "a" display_name = "A" type = "auto" } + "5551ea38-126d-4d89-ab57-14b36b92a93c" = { name = "b" display_name = "B" type = "auto" } + "cdc83a63-3365-4c10-9353-79ff5f2fd1f0" = { name = "w" display_name = "Weight" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT(lerp(a, b, w)); +""" diff --git a/vmf_source/core/shader_nodes/light_color.shader_node b/vmf_source/core/shader_nodes/light_color.shader_node new file mode 100644 index 0000000..4bdd784 --- /dev/null +++ b/vmf_source/core/shader_nodes/light_color.shader_node @@ -0,0 +1,19 @@ +group = "Light" +display_name = "Light Color" +depends_on = ["core/stingray_renderer/output_nodes/light_base"] + +imports = { + light_color = { + type = "float3" + domain = "global" + source = "engine" + } +} + +output = { + type = { typeof: "light_color" } +} + +code = """ + RESULT(light_color); +""" diff --git a/vmf_source/core/shader_nodes/light_vector.shader_node b/vmf_source/core/shader_nodes/light_vector.shader_node new file mode 100644 index 0000000..0e83ce1 --- /dev/null +++ b/vmf_source/core/shader_nodes/light_vector.shader_node @@ -0,0 +1,19 @@ +group = "Light" +display_name = "Light Vector" +depends_on = ["core/stingray_renderer/output_nodes/light_base"] + +imports = { + light_vector = { + type = "float3" + domain = "pixel" + output_channel = "light_vector" + } +} + +output = { + type = "float3" +} + +code = """ + RESULT(light_vector); +""" diff --git a/vmf_source/core/shader_nodes/material_variable.shader_node b/vmf_source/core/shader_nodes/material_variable.shader_node new file mode 100644 index 0000000..9bc50a6 --- /dev/null +++ b/vmf_source/core/shader_nodes/material_variable.shader_node @@ -0,0 +1,24 @@ +group = "Input" +display_name = "Material Variable" + +exports = { + material_variable = { + type = "float3" + value = [0.0 0.0 0.0] + + ui = { + is_editable = true + min = [0.0 0.0 0.0] + max = [1.0 1.0 1.0] + step = [0.001 0.001 0.001] + } + } +} + +output = { + type = { typeof: "material_variable" } +} + +code = """ + RESULT(material_variable); +""" diff --git a/vmf_source/core/shader_nodes/max.shader_node b/vmf_source/core/shader_nodes/max.shader_node new file mode 100644 index 0000000..44f2b84 --- /dev/null +++ b/vmf_source/core/shader_nodes/max.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Maximum" +inputs = { + "f72597c4-7487-419a-affb-ef690e6582e1" = { name = "a" display_name = "A" type = "auto" } + "0806db0d-2c4a-43ca-99cc-e5a2f036a8e8" = { name = "b" display_name = "B" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT( max(a,b) ); +""" diff --git a/vmf_source/core/shader_nodes/min.shader_node b/vmf_source/core/shader_nodes/min.shader_node new file mode 100644 index 0000000..8d44d9c --- /dev/null +++ b/vmf_source/core/shader_nodes/min.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Minimum" +inputs = { + "f72597c4-7487-419a-affb-af690e6582e1" = { name = "a" display_name = "A" type = "auto" } + "0806db0d-2c4a-43ca-99cc-a5a2f036a8e8" = { name = "b" display_name = "Min" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT( min(a,b) ); +""" diff --git a/vmf_source/core/shader_nodes/mul.shader_node b/vmf_source/core/shader_nodes/mul.shader_node new file mode 100644 index 0000000..1c0a99a --- /dev/null +++ b/vmf_source/core/shader_nodes/mul.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Multiply" +inputs = { + "c5823c75-4ae5-4c71-b070-315fa4d03e8e" = { name = "a" display_name = "A" type = "auto" } + "242d1648-a626-445b-9534-bccec094112f" = { name = "b" display_name = "B" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT(a * b); +""" diff --git a/vmf_source/core/shader_nodes/noise/noise2D.shader_node b/vmf_source/core/shader_nodes/noise/noise2D.shader_node new file mode 100644 index 0000000..13f21bc --- /dev/null +++ b/vmf_source/core/shader_nodes/noise/noise2D.shader_node @@ -0,0 +1,151 @@ +group = "Noise (Expensive)" +display_name = "Noise 2D" +inputs = { + "43350130-56d2-4f38-8946-2db61bc0dc27" = { name = "uv" display_name = "UV" is_required = true type = "vector2" domain = "pixel" } + "73505236-5232-4270-a364-5c30356d44d5" = { name = "a" display_name = "A" is_required = false type = { scalar: ["HAS_A_INPUT"] } domain = "pixel" } + "2ea53a8d-bd55-4b4b-81f4-7bf2166c4d9c" = { name = "b" display_name = "B" is_required = false type = { scalar: ["HAS_B_INPUT"] } domain = "pixel" } + "5a2a194b-1ba3-4b56-834d-349aea030a90" = { name = "c" display_name = "C" is_required = false type = { scalar: ["HAS_C_INPUT"] } domain = "pixel" } +} + +domain = "pixel" +output = { + type = "float" +} + +options = { + "fa47640c-251a-45ea-9b64-b66548831364" = "VALUE_2D" + "bf7f9537-897f-4765-937c-a8f6ed136c57" = "VALUE_PERLIN_2D" + "4d8f1b18-68b6-4583-89ce-55bbae9e26fb" = "VALUE_HERMITE_2D" + "ea90178c-95ca-4f4a-9e65-40d4ff1c7de2" = "PERLIN_2D" + "1df75adb-e671-4b6a-a312-b9cefbdf662b" = "HERMITE_2D" + "a32eedbc-2170-4ef1-ae28-cd99278fd7b7" = "CUBIST_2D" + "d34696e9-a9d1-4c99-93ed-c3e0d84eb03a" = "CELLULAR_2D" + "968553bd-efc4-43ef-b271-887992a50bcb" = "POLKA_DOT_2D" + "dc1b5dfb-3629-415c-9dbc-ee0f88a75c4b" = "STARS_2D" + "a7e6eafc-1322-48e6-94a0-9c57b12ee1e1" = "SIMPLEX_PERLIN_2D" + "cb95451e-3c58-458d-a1db-1a900822a3df" = "SIMPLEX_CELLULAR_2D" + "1e2afae4-0c42-4dd2-9ba6-34fab2887aed" = "SIMPLEX_POLKA_DOT_2D" +} + +ui = [ + { + type = "drop_down" + display_name = "Noise Type" + options = { + "Value [0, 1]" = "fa47640c-251a-45ea-9b64-b66548831364" + "Value-Perlin [-1, 1], a" = "bf7f9537-897f-4765-937c-a8f6ed136c57" + "Value-Hermite [-1, 1], a, b" = "4d8f1b18-68b6-4583-89ce-55bbae9e26fb" + "Perlin [-1, 1]" = "ea90178c-95ca-4f4a-9e65-40d4ff1c7de2" + "Hermite [-1, 1]" = "1df75adb-e671-4b6a-a312-b9cefbdf662b" + "Cubist [0, 1], a, b" = "a32eedbc-2170-4ef1-ae28-cd99278fd7b7" + "Cellular [0, 1]" = "d34696e9-a9d1-4c99-93ed-c3e0d84eb03a" + "Polka Dot [0, 1], a, b" = "968553bd-efc4-43ef-b271-887992a50bcb" + "Stars [0, 1], a, b, c" = "dc1b5dfb-3629-415c-9dbc-ee0f88a75c4b" + "Simplex Perlin [-1, 1]" = "a7e6eafc-1322-48e6-94a0-9c57b12ee1e1" + "Simplex Cellular [0, ~1]" = "cb95451e-3c58-458d-a1db-1a900822a3df" + "Simplex Polka Dot [0, 1], a, b" = "1e2afae4-0c42-4dd2-9ba6-34fab2887aed" + + } + default = "fa47640c-251a-45ea-9b64-b66548831364" + } +] + +code_blocks = { + default = { + include:["core/stingray_renderer/shader_libraries/noise#noise_functions"] + + code = { + hlsl = """ + #if defined(VALUE_2D) + float result = Value2D(uv); + #elif defined(VALUE_PERLIN_2D) + #ifndef HAS_A_INPUT + float blend = 0.5; + #else + float blend = a; + #endif + float result = ValuePerlin2D(uv, blend); + #elif defined(VALUE_HERMITE_2D) + #ifndef HAS_A_INPUT + float MAXVALUE = 1.0; + #else + float MAXVALUE = a; + #endif + #ifndef HAS_B_INPUT + float MAXGRADIENT = 1.0; + #else + float MAXGRADIENT = b; + #endif + float result = ValueHermite2D(uv, 2.0*MAXVALUE, 2.0*MAXGRADIENT, 1.0 / ( MAXVALUE + MAXGRADIENT * 0.3125 * 2.0 )); + #elif defined(PERLIN_2D) + float result = Perlin2D(uv); + #elif defined(HERMITE_2D) + float result = Hermite2D(uv); + #elif defined(CUBIST_2D) + #ifndef HAS_A_INPUT + float high = 1.0; + #else + float high = a; + #endif + #ifndef HAS_B_INPUT + float low = -2.0; + #else + float low = b; + #endif + float result = Cubist2D(uv, float2(low, 1.0/(high-low))); + #elif defined(CELLULAR_2D) + float result = Cellular2D(uv); + #elif defined(POLKA_DOT_2D) + #ifndef HAS_A_INPUT + float high_radius = 1.0; + #else + float high_radius = a; + #endif + #ifndef HAS_B_INPUT + float low_radius = 0.1; + #else + float low_radius = b; + #endif + float result = PolkaDot2D(uv, low_radius, high_radius); + #elif defined(STARS_2D) + #ifndef HAS_A_INPUT + float probability = 0.5; + #else + float probability = a; + #endif + #ifndef HAS_B_INPUT + float max_dimness = 1.0; + #else + float max_dimness = b; + #endif + #ifndef HAS_C_INPUT + float two_over_radius = 4.0; + #else + float two_over_radius = b; + #endif + float result = Stars2D(uv, probability, max_dimness, two_over_radius); + #elif defined(SIMPLEX_PERLIN_2D) + float result = SimplexPerlin2D(uv); + #elif defined(SIMPLEX_CELLULAR_2D) + float result = SimplexCellular2D(uv); + #elif defined(SIMPLEX_POLKA_DOT_2D) + #ifndef HAS_A_INPUT + float radius = 1.0; + #else + float radius = a; + #endif + #ifndef HAS_B_INPUT + float variation = 0.1; + #else + float variation = b; + #endif + float result = SimplexPolkaDot2D(uv, radius, variation); + #else + float result = 0.0; + #endif + + RESULT(result); + """ + } + } +} diff --git a/vmf_source/core/shader_nodes/noise/noise3D.shader_node b/vmf_source/core/shader_nodes/noise/noise3D.shader_node new file mode 100644 index 0000000..744efd9 --- /dev/null +++ b/vmf_source/core/shader_nodes/noise/noise3D.shader_node @@ -0,0 +1,130 @@ +group = "Noise (Expensive)" +display_name = "Noise 3D" +inputs = { + "3679b6ae-ea55-4c84-a531-7d3f350f505f" = { name = "uvw" display_name = "UVW" is_required = true type = "vector3" domain = "pixel" } + "93aded2c-9f72-44e4-9b0a-9e6b892b99e3" = { name = "a" display_name = "A" is_required = false type = { scalar: ["HAS_A_INPUT"] } domain = "pixel" } + "ba717440-5742-42f5-b81a-145b3ab1ddec" = { name = "b" display_name = "B" is_required = false type = { scalar: ["HAS_B_INPUT"] } domain = "pixel" } +} + +domain = "pixel" +output = { + type = "float" +} + +options = { + "b154b1bb-8b43-44c1-ba03-8c552176375e" = "VALUE_3D" + "8d01fa80-ecec-4140-b09a-635b6e96e646" = "VALUE_PERLIN_3D" + "ee3af997-15bf-46b0-b121-901260a53611" = "VALUE_HERMITE_3D" + "4739a4bb-9b3d-481d-bf32-0988f06fc6b8" = "PERLIN_3D" + "43513af3-331c-4cd3-a8af-57898d2629e1" = "HERMITE_3D" + "efd29873-4430-45c8-a4fb-5631704e3fa2" = "CUBIST_3D" + "59d1c03c-f309-4432-aee2-590f909e7459" = "CELLULAR_3D" + "526f7923-e8a6-4499-b9f7-4ae586a67bf4" = "POLKA_DOT_3D" + "5c697940-af02-4d4e-bbe3-8be2506641e8" = "SIMPLEX_PERLIN_3D" + "c617d225-bffb-466b-9022-56993ee40265" = "SIMPLEX_CELLULAR_3D" + "56dd9739-c575-4f04-bc4b-a525ff413985" = "SIMPLEX_POLKA_DOT_3D" +} + +ui = [ + { + type = "drop_down" + display_name = "Noise Type" + options = { + "Value [0, 1]" = "b154b1bb-8b43-44c1-ba03-8c552176375e" + "Value-Perlin [-1, 1], a" = "8d01fa80-ecec-4140-b09a-635b6e96e646" + "Value-Hermite [-1, 1], a, b" = "ee3af997-15bf-46b0-b121-901260a53611" + "Perlin [-1, 1]" = "4739a4bb-9b3d-481d-bf32-0988f06fc6b8" + "Hermite [-1, 1]" = "43513af3-331c-4cd3-a8af-57898d2629e1" + "Cubist [0, 1], a, b" = "efd29873-4430-45c8-a4fb-5631704e3fa2" + "Cellular [0, 1]" = "59d1c03c-f309-4432-aee2-590f909e7459" + "Polka Dot [0, 1], a, b" = "526f7923-e8a6-4499-b9f7-4ae586a67bf4" + "Simplex Perlin [-1, 1]" = "5c697940-af02-4d4e-bbe3-8be2506641e8" + "Simplex Cellular [0, ~1]" = "c617d225-bffb-466b-9022-56993ee40265" + "Simplex Polka Dot [0, 1], a, b" = "56dd9739-c575-4f04-bc4b-a525ff413985" + } + default = "b154b1bb-8b43-44c1-ba03-8c552176375e" + } +] + +code_blocks = { + default = { + include:["core/stingray_renderer/shader_libraries/noise#noise_functions"] + + code = { + hlsl = """ + #if defined(VALUE_3D) + float result = Value3D(uvw); + #elif defined(VALUE_PERLIN_3D) + #ifndef HAS_A_INPUT + float blend = 0.5; + #else + float blend = a; + #endif + float result = ValuePerlin3D(uvw, blend); + #elif defined(VALUE_HERMITE_3D) + #ifndef HAS_A_INPUT + float MAXVALUE = 1.0; + #else + float MAXVALUE = a; + #endif + #ifndef HAS_B_INPUT + float MAXGRADIENT = 1.0; + #else + float MAXGRADIENT = b; + #endif + float result = ValueHermite3D(uvw, 2.0*MAXVALUE, 2.0*MAXGRADIENT, 1.0 / ( MAXVALUE + MAXGRADIENT * 0.3125 * 2.0 )); + #elif defined(PERLIN_3D) + float result = Perlin3D(uvw); + #elif defined(HERMITE_3D) + float result = Hermite3D(uvw); + #elif defined(CUBIST_3D) + #ifndef HAS_A_INPUT + float high = 1.0; + #else + float high = a; + #endif + #ifndef HAS_B_INPUT + float low = -2.0; + #else + float low = b; + #endif + float result = Cubist3D(uvw, float2(low, 1.0/(high-low))); + #elif defined(CELLULAR_3D) + float result = Cellular3D(uvw); + #elif defined(POLKA_DOT_3D) + #ifndef HAS_A_INPUT + float high_radius = 1.0; + #else + float high_radius = a; + #endif + #ifndef HAS_B_INPUT + float low_radius = 0.1; + #else + float low_radius = b; + #endif + float result = PolkaDot3D(uvw, low_radius, high_radius); + #elif defined(SIMPLEX_PERLIN_3D) + float result = SimplexPerlin3D(uvw); + #elif defined(SIMPLEX_CELLULAR_3D) + float result = SimplexCellular3D(uvw); + #elif defined(SIMPLEX_POLKA_DOT_3D) + #ifndef HAS_A_INPUT + float radius = 1.0; + #else + float radius = a; + #endif + #ifndef HAS_B_INPUT + float variation = 0.1; + #else + float variation = b; + #endif + float result = SimplexPolkaDot3D(uvw, radius, variation); + #else + float result = 0.0; + #endif + + RESULT(result); + """ + } + } +} diff --git a/vmf_source/core/shader_nodes/noise/noise4D.shader_node b/vmf_source/core/shader_nodes/noise/noise4D.shader_node new file mode 100644 index 0000000..32aca4b --- /dev/null +++ b/vmf_source/core/shader_nodes/noise/noise4D.shader_node @@ -0,0 +1,51 @@ +group = "Noise (Expensive)" +display_name = "Noise 4D" +inputs = { + "e339bcc7-ba49-41b4-ac71-d581785422f4" = { name = "uvwx" display_name = "UVWX" is_required = true type = "vector4" domain = "pixel" } +} + +domain = "pixel" +output = { + type = "float" +} + +options = { + "8c8ae6d6-a4af-493e-ad52-16c51418000d" = "VALUE_4D" + "41aa617b-94b2-4741-91b8-2d51bf66032d" = "PERLIN_4D" + "12684be0-1484-4f68-bbe0-176fad7c0307" = "SIMPLEX_PERLIN_4D" +} + +ui = [ + { + type = "drop_down" + display_name = "Noise Type" + options = { + "Value [0, 1]" = "8c8ae6d6-a4af-493e-ad52-16c51418000d" + "Perlin [-1, 1]" = "41aa617b-94b2-4741-91b8-2d51bf66032d" + "Simplex Perlin [-1, 1]" = "12684be0-1484-4f68-bbe0-176fad7c0307" + } + default = "8c8ae6d6-a4af-493e-ad52-16c51418000d" + } +] + +code_blocks = { + default = { + include:["core/stingray_renderer/shader_libraries/noise#noise_functions"] + + code = { + hlsl = """ + #if defined(VALUE_4D) + float result = Value4D(uvwx); + #elif defined(PERLIN_4D) + float result = Perlin4D(uvwx); + #elif defined(SIMPLEX_PERLIN_4D) + float result = simplex_noise4D(uvwx); + #else + float result = 0.0; + #endif + + RESULT(result); + """ + } + } +} diff --git a/vmf_source/core/shader_nodes/normalize.shader_node b/vmf_source/core/shader_nodes/normalize.shader_node new file mode 100644 index 0000000..82c52f5 --- /dev/null +++ b/vmf_source/core/shader_nodes/normalize.shader_node @@ -0,0 +1,15 @@ +group = "Math" +display_name = "Normalize" +domain = "pixel" + +inputs = { + "7bc3f92e-1302-40b5-9aea-723e5a7b6b94" = { name = "a" display_name = "Vector" type = { vector2:[] vector3:[] vector4:[] } } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(normalize(a)); +""" diff --git a/vmf_source/core/shader_nodes/normalize_vs.shader_node b/vmf_source/core/shader_nodes/normalize_vs.shader_node new file mode 100644 index 0000000..cf55eda --- /dev/null +++ b/vmf_source/core/shader_nodes/normalize_vs.shader_node @@ -0,0 +1,15 @@ +group = "Math" +display_name = "Normalize VS" +domain = "vertex" + +inputs = { + "7bc3f92e-1302-40b5-9aea-723e5a7b6b94" = { name = "a" display_name = "Vector" type = { vector2:[] vector3:[] vector4:[] } } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(normalize(a)); +""" diff --git a/vmf_source/core/shader_nodes/object_to_world.shader_node b/vmf_source/core/shader_nodes/object_to_world.shader_node new file mode 100644 index 0000000..7fd45a7 --- /dev/null +++ b/vmf_source/core/shader_nodes/object_to_world.shader_node @@ -0,0 +1,48 @@ +group = "Transform" +display_name = "Object To World" +depends_on = ["core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/light_base", "core/stingray_renderer/output_nodes/unlit_base", "core/stingray_renderer/output_nodes/terrain_base"] + +inputs = { + "f72597c4-7487-419a-affb-df690e6582e1" = { name = "v" display_name = "Vector" type = "float3" } +} + +options = { + "43344bde-3298-4b5f-8993-0be2a71a83b3" = "ROTATE_ONLY" +} + +ui = [ + { + type = "drop_down" + display_name = "Mode" + options = { + "Full Transform" = "00000000-0000-0000-0000-000000000000" + "Rotation only" = "43344bde-3298-4b5f-8993-0be2a71a83b3" + } + } +] + +defines = ["NEEDS_WORLD_POSE"] + +imports = { + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = { typeof: "v" } +} + +code = """ + #if defined(BILLBOARD) + RESULT(world._m30_m31_m32 + v); + #else + #if defined(ROTATE_ONLY) + RESULT(mul(v, to_mat3(world))); + #else + RESULT(mul(float4(v, 1), world).xyz); + #endif + #endif +""" diff --git a/vmf_source/core/shader_nodes/option.shader_node b/vmf_source/core/shader_nodes/option.shader_node new file mode 100644 index 0000000..820e22b --- /dev/null +++ b/vmf_source/core/shader_nodes/option.shader_node @@ -0,0 +1,35 @@ +group = "Utility" +display_name = "Option Switch" +inputs = { + "34654E2D-A837-462D-8A9E-27E9700AF34B" = { name = "a" display_name = "Enabled" type = "auto" } + "E1797178-8357-4545-A104-DDDFC5452428" = { name = "b" display_name = "Disabled" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +options = { + "B7F82F51-D658-453E-9E48-FC00F79D57D3" = "OPTION_DISABLED" + "96FC39FC-908B-454E-8DC5-FED9B0848619" = "OPTION_ENABLED" +} + +ui = [ + { + type = "drop_down" + display_name = "Option" + options = { + "Disabled" = "B7F82F51-D658-453E-9E48-FC00F79D57D3" + "Enabled" = "96FC39FC-908B-454E-8DC5-FED9B0848619" + } + default = "B7F82F51-D658-453E-9E48-FC00F79D57D3" + } +] + +code = """ + #if defined(OPTION_ENABLED) + RESULT(a); + #else + RESULT(b); + #endif +""" diff --git a/vmf_source/core/shader_nodes/orientation_forward.shader_node b/vmf_source/core/shader_nodes/orientation_forward.shader_node new file mode 100644 index 0000000..c434917 --- /dev/null +++ b/vmf_source/core/shader_nodes/orientation_forward.shader_node @@ -0,0 +1,31 @@ +group = "Orientation" +display_name = "Forward Axis" +depends_on = ["core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/light_base"] + +defines = ["NEEDS_WORLD_POSE"] + +imports = { + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = "float3" +} + +code_blocks = { + default = { + code = { + glsl = """ + RESULT(normalize(vec3(world[0].y, world[1].y, world[2].y))); + """ + + hlsl = """ + RESULT(normalize(world._m10_m11_m12)); + """ + } + } +} \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/orientation_position.shader_node b/vmf_source/core/shader_nodes/orientation_position.shader_node new file mode 100644 index 0000000..2559887 --- /dev/null +++ b/vmf_source/core/shader_nodes/orientation_position.shader_node @@ -0,0 +1,31 @@ +group = "Orientation" +display_name = "Position" +depends_on = ["core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/light_base"] + +defines = ["NEEDS_WORLD_POSE"] + +imports = { + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = "float3" +} + +code_blocks = { + default = { + code = { + glsl = """ + RESULT(vec3(world[0].w, world[1].w, world[2].w)); + """ + + hlsl = """ + RESULT(world._m30_m31_m32); + """ + } + } +} diff --git a/vmf_source/core/shader_nodes/orientation_right.shader_node b/vmf_source/core/shader_nodes/orientation_right.shader_node new file mode 100644 index 0000000..f8b3511 --- /dev/null +++ b/vmf_source/core/shader_nodes/orientation_right.shader_node @@ -0,0 +1,31 @@ +group = "Orientation" +display_name = "Right Axis" +depends_on = ["core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/light_base"] + +defines = ["NEEDS_WORLD_POSE"] + +imports = { + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = "float3" +} + +code_blocks = { + default = { + code = { + glsl = """ + RESULT(normalize(vec3(world[0].x, world[1].x, world[2].x))); + """ + + hlsl = """ + RESULT(normalize(world._m00_m01_m02)); + """ + } + } +} \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/orientation_up.shader_node b/vmf_source/core/shader_nodes/orientation_up.shader_node new file mode 100644 index 0000000..357a37c --- /dev/null +++ b/vmf_source/core/shader_nodes/orientation_up.shader_node @@ -0,0 +1,31 @@ +group = "Orientation" +display_name = "Up Axis" +depends_on = ["core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/light_base"] + +defines = ["NEEDS_WORLD_POSE"] + +imports = { + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = "float3" +} + +code_blocks = { + default = { + code = { + glsl = """ + RESULT(normalize(vec3(world[0].z, world[1].z, world[2].z))); + """ + + hlsl = """ + RESULT(normalize(world._m20_m21_m22)); + """ + } + } +} \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/panner.shader_node b/vmf_source/core/shader_nodes/panner.shader_node new file mode 100644 index 0000000..a23b09a --- /dev/null +++ b/vmf_source/core/shader_nodes/panner.shader_node @@ -0,0 +1,29 @@ +group = "Utility" +display_name = "Panner" +inputs = { + "40A76949-E7DB-47FB-9AEC-3FA98DD3FA2E" = { name = "uv" display_name = "UV" type = "vector2" } + "0CE9AD0A-0B3F-43B1-9139-12176F3AAD8E" = { name = "time" display_name = "Time" type = "scalar" } + "9a282471-a2d2-4591-a57e-c70179bb1962" = { name = "speed_u" display_name = "Speed U" is_required = false type = { scalar: ["HAS_SPEED_U"] }} + "e3419eb2-3635-4586-b58c-89ffd88d20d5" = { name = "speed_v" display_name = "Speed V" is_required = false type = { scalar: ["HAS_SPEED_V"] }} +} + +output = { + type = { typeof: "uv" } +} + +code = """ + + #if defined(HAS_SPEED_U) + float speed_x = speed_u; + #else + float speed_x = 0.33; + #endif + + #if defined(HAS_SPEED_V) + float speed_y = speed_v; + #else + float speed_y = 0.33; + #endif + + RESULT( float2(time*speed_x, time*speed_y) + uv ); +""" diff --git a/vmf_source/core/shader_nodes/parallax.shader_node b/vmf_source/core/shader_nodes/parallax.shader_node new file mode 100644 index 0000000..d6f425d --- /dev/null +++ b/vmf_source/core/shader_nodes/parallax.shader_node @@ -0,0 +1,65 @@ +group = "Utility" +display_name = "Parallax / Bump Offset" + +// scale and bias map the height value into a range that better represents the physical properties (size) of the surface +inputs = { + "95FB056C-9DDF-434C-8D8E-A03F30F5C42D" = { name = "uv" display_name = "UV" type = "vector2"} + "B897C7FC-96E3-45D9-9AF1-A1C797386C61" = { name = "height" display_name = "Height" type = "scalar"} + "6046b049-ae9d-49f6-9f75-58a34a3bac15" = { name = "scale" display_name = "Parallax Scale" is_required = false type = { scalar: ["HAS_SCALE"] }} + "ed989589-8b44-4ec4-b5a7-8fb5cd739854" = { name = "bias" display_name = "Parallax Bias" is_required = false type = { scalar: ["HAS_BIAS"] }} +} + +defines = ["NEEDS_TANGENT_SPACE", "NEEDS_EYE_VECTOR"] + +imports = { + eye_vector = { + type = "float3" + domain = "vertex" + output_channel = "eye_vector" + } + tsm0 = { + type = "float3" + domain = "vertex" + output_channel = "tsm0" + } + tsm1 = { + type = "float3" + domain = "vertex" + output_channel = "tsm1" + } + tsm2 = { + type = "float3" + domain = "vertex" + output_channel = "tsm2" + } +} + +output = { + type = { typeof: "uv" } +} + +code = """ + float3 dir = normalize(eye_vector); + + // Get camera vector in tangent space + float3 dir_ts = new_float3( + dot(dir, float3(tsm0.x, tsm1.x, tsm2.x)), + dot(dir, float3(tsm0.y, tsm1.y, tsm2.y)), + dot(dir, float3(tsm0.z, tsm1.z, tsm2.z))); + + float2 norm_dir_ts = normalize(dir_ts).xy; + + #if defined(HAS_SCALE) + float scale_value = scale; + #else + float scale_value = 0.04; + #endif + + #if defined(HAS_BIAS) + float bias_value = bias; + #else + float bias_value = 0.02; + #endif + + RESULT(uv + float2(norm_dir_ts.x, -norm_dir_ts.y) * (height*scale_value - bias_value)); +""" diff --git a/vmf_source/core/shader_nodes/pixel_depth.shader_node b/vmf_source/core/shader_nodes/pixel_depth.shader_node new file mode 100644 index 0000000..ffc26a3 --- /dev/null +++ b/vmf_source/core/shader_nodes/pixel_depth.shader_node @@ -0,0 +1,29 @@ +group = "Fatshark" +display_name = "Camera Depth" + +depends_on = [ + "core/stingray_renderer/output_nodes/standard_base" + "core/stingray_renderer/output_nodes/unlit_base", + "core/stingray_renderer/output_nodes/particle_base", + "core/stingray_renderer/output_nodes/particle_gbuffer_base", + "core/stingray_renderer/output_nodes/particle_distortion_base"] + +imports = { + pixel_depth = { + type = "float" + domain = "pixel" + output_channel = "pixel_depth" + } +} + +defines = ["NEEDS_PIXEL_DEPTH"] + +domain = "pixel" +output = { + type = "float" +} + +language = "hlsl" +code = """ + RESULT(pixel_depth - camera_near_far.x); +""" diff --git a/vmf_source/core/shader_nodes/power.shader_node b/vmf_source/core/shader_nodes/power.shader_node new file mode 100644 index 0000000..a151db6 --- /dev/null +++ b/vmf_source/core/shader_nodes/power.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Power" +inputs = { + "447b3fe2-c820-4511-a923-b38b0f6c9fe6" = { name = "a" display_name = "Value" type = "auto" } + "e16267f4-8b4e-4079-b13d-f380cfd386ec" = { name = "b" display_name = "Power" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT(pow(a,b)); +""" diff --git a/vmf_source/core/shader_nodes/projected_texcoord.shader_node b/vmf_source/core/shader_nodes/projected_texcoord.shader_node new file mode 100644 index 0000000..1c39593 --- /dev/null +++ b/vmf_source/core/shader_nodes/projected_texcoord.shader_node @@ -0,0 +1,71 @@ +group = "Sampling" +display_name = "Projected Texcoord" +inputs = { + "a75dd59e-43f1-4dc8-bdb9-e0ec41e304c2" = { name = "hit_position" display_name = "Hit Position" type = "float4" } + "3a1f306e-4d12-45e8-9262-0b709ae4662f" = { name = "hit_normal" display_name = "Hit Normal" type = "float4" } + "814da82a-eccb-40b6-b9c2-074ae141ead4" = { name = "hit_tangent" display_name = "Hit Tangent" type = "float4" } + "70164922-9e7e-4c74-bbaf-92de6838bb86" = { name = "max_dist" display_name = "Diameter" type = "float" } +} + +defines = ["NEEDS_UNSKINNED_WORLD_POS", "NEEDS_WORLD_POSE"] + +domain = "pixel" +output = { + type = "float3" +} + +imports = { + wpos = { + type = "float3" + domain = "vertex" + output_channel = "unskinned_world_pos" + } + normal = { + type = "float3" + domain = "vertex" + output_channel = "unskinned_normal" + } + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +code = """ + float3 output = float3(0,0,0); + [branch] + if( hit_position.w > 0.5 ) { + float3 pos = mul(hit_position, world).xyz; + float dist = distance(wpos, pos); + + [branch] + if( dist < max_dist ) { + float3 world_normal = mul(normal, (float3x3)world); + float3 hit_normal_world = mul(normalize(hit_normal), world); + float side = sqrt( max_dist * max_dist + max_dist * max_dist ); + float dot_value = dot(hit_normal_world, world_normal ); + + [branch] + if( dot_value > 0.0 ) { + float3 hit_tangent_world = mul(normalize(hit_tangent), world); + float3 hit_binormal_world = cross(hit_tangent_world, hit_normal_world); + + float3 t_hit_pos = rotate_point3(pos, (float3)hit_tangent_world, (float3)hit_binormal_world, (float3)hit_normal_world); + float3 t_pos = rotate_point3(wpos, (float3)hit_tangent_world, (float3)hit_binormal_world, (float3)hit_normal_world); + + float offset = side * 0.4; + float diff_x = (t_hit_pos.x - t_pos.x); + float diff_y = (t_hit_pos.y - t_pos.y); + float x = (offset + diff_x)/(offset * 2); + float y = (offset + diff_y)/(offset * 2); + float valid = clamp(dot_value * 3,0,1); + if( abs(diff_x) > offset || abs(diff_y) > offset ) + valid = 0; + output = float3(x,y,valid); + } + } + } + + RESULT(output); +""" diff --git a/vmf_source/core/shader_nodes/reflect.shader_node b/vmf_source/core/shader_nodes/reflect.shader_node new file mode 100644 index 0000000..b573c57 --- /dev/null +++ b/vmf_source/core/shader_nodes/reflect.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Reflect" +inputs = { + "b79bdce4-ed04-4d7c-9f79-cb6174a5e1cc" = { name = "ray" display_name = "Vector" type = { vector2:[] vector3:[] vector4:[] } } + "3d065d63-3541-4223-91bf-4db3ecf7d4a6" = { name = "normal" display_name = "Normal" type = { vector2:[] vector3:[] vector4:[] } } +} + +output = { + type = { typeof: "ray" } +} + +code = """ + RESULT(reflect(ray, normal)); +""" diff --git a/vmf_source/core/shader_nodes/refract.shader_node b/vmf_source/core/shader_nodes/refract.shader_node new file mode 100644 index 0000000..089b18a --- /dev/null +++ b/vmf_source/core/shader_nodes/refract.shader_node @@ -0,0 +1,42 @@ +group = "Math" +display_name = "Refract" +inputs = { + "061d4f41-aed3-4f91-843f-bcb4ed0084ed" = { name = "ray" display_name = "In Vector" type = { vector2:[] vector3:[] vector4:[] } } + "93312e6b-6a60-4ac1-991e-d999857424a9" = { name = "normal" display_name = "Normal" type = { vector2:[] vector3:[] vector4:[] } } + "1eabad46-a82f-4f11-9e92-414d90b5fff3" = { name = "index" display_name = "Index" type = "scalar" } +} + +output = { + type = { typeof: "ray" } +} + +code_blocks = { + gnm_refract_block = { + code = """ + #if defined(RENDERER_GNM) + inline float2 gnm_refract(float2 r, float2 n, float i) { return refract(float3(r,0), float3(n,0), i).xy; } + inline float2 gnm_refract(float2 r, float3 n, float i) { return refract(float3(r,0), n, i).xy; } + inline float2 gnm_refract(float2 r, float4 n, float i) { return refract(float3(r,0), n.xyz, i).xy; } + + inline float3 gnm_refract(float3 r, float2 n, float i) { return refract(r, float3(n,0), i); } + inline float3 gnm_refract(float3 r, float3 n, float i) { return refract(r, n, i); } + inline float3 gnm_refract(float3 r, float4 n, float i) { return refract(r, n.xyz, i); } + + inline float4 gnm_refract(float4 r, float2 n, float i) { return float4(refract(r.xyz, float3(n,0), i), 0); } + inline float4 gnm_refract(float4 r, float3 n, float i) { return float4(refract(r.xyz, n, i), 0); } + inline float4 gnm_refract(float4 r, float4 n, float i) { return float4(refract(r.xyz, n.xyz, 0), 0); } + #endif + """ + } + + default = { + include: ["gnm_refract_block"] + code = """ + #if defined(RENDERER_GNM) + RESULT(gnm_refract(ray, normal, index)); + #else + RESULT(refract(ray, normal, index)); + #endif + """ + } +} \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/rgb_to_hsv.shader_node b/vmf_source/core/shader_nodes/rgb_to_hsv.shader_node new file mode 100644 index 0000000..890826d --- /dev/null +++ b/vmf_source/core/shader_nodes/rgb_to_hsv.shader_node @@ -0,0 +1,57 @@ +group = "Utility" +display_name = "RGB to HSV" +inputs = { + "96D9D117-6A3A-469A-A9B2-3EEDE48F9848" = { name = "rgb" display_name = "RGB" type = "vector3" } + "DAFF208B-6720-4EA8-99C2-423C636A230C" = { name = "hue" is_required = false display_name = "Hue" type = { scalar: ["HAS_HUE"] } } + "7E1968C6-238F-4FB0-A142-9A52B189223C" = { name = "sat" is_required = false display_name = "Saturation" type = { scalar: ["HAS_SAT"] } } + "57AD6551-795C-4A19-BD00-A814628F2976" = { name = "bright" is_required = false display_name = "Brightness / Value" type = { scalar: ["HAS_BRIGHT"] } } +} + +output = { + type = { typeof: "rgb" } +} + +code_blocks = { + rgb_to_hsv_block = { + code = """ + + // epsilon is used to remove the need to check for division-by-zero + const float epsilon_hsv = 1e-10; + + // RGB to Hue Chroma Value + inline float3 rgb_to_hcv( float3 rgb ) { + float4 p = (rgb.g < rgb.b) ? float4(rgb.bg, -1.0, 2.0/3.0) : float4(rgb.gb, 0.0, -1.0/3.0); + float4 q = (rgb.r < p.x) ? float4(p.xyw, rgb.r) : float4(rgb.r, p.yzx); + float c = q.x - min(q.w, q.y); + float h = abs((q.w - q.y) / (6.0 * c + epsilon_hsv) + q.z); + return float3(h, c, q.x); + } + + // RGB to HSV conversion based on work by Sam Hocevar and Emil Persson + // explanation: http://chilliant.blogspot.ca/2014/04/rgbhsv-in-hlsl-5.html + inline float3 rgb_to_hsv( float3 rgb ) { + float3 hcv = rgb_to_hcv(rgb); + float s = hcv.y / (hcv.z + epsilon_hsv); + return float3(hcv.x, s, hcv.z); + } + """ + } + + default = { + include: ["rgb_to_hsv_block"] + code = """ + float3 hsv = rgb_to_hsv(rgb); + #if defined(HAS_HUE) + float n = 0.0; + hsv.x = modf(hsv.x + hue, n); + #endif + #if defined(HAS_SAT) + hsv.y *= sat; + #endif + #if defined(HAS_BRIGHT) + hsv.z *= bright; + #endif + RESULT(hsv); + """ + } +} \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/ribbon_particles/ribbon_texcoord.shader_node b/vmf_source/core/shader_nodes/ribbon_particles/ribbon_texcoord.shader_node new file mode 100644 index 0000000..f24c058 --- /dev/null +++ b/vmf_source/core/shader_nodes/ribbon_particles/ribbon_texcoord.shader_node @@ -0,0 +1,29 @@ +group = "Particle Ribbon" +display_name = "Ribbon UV" + +imports = { + strip_info = { type = "float" semantic = "POSITION1" domain = "vertex" } + ribbon_distance = { type = "float" semantic = "TEXCOORD6" domain = "vertex" } +} + +options = { + "10abb86e-61d5-4927-9d22-6e87ce3b85ba" = "FLIP_TEXCOORDS" +} + +ui = [ + { type = "checkbox" display_name = "flip texcoords" option = "10abb86e-61d5-4927-9d22-6e87ce3b85ba" } +] + + +domain = "vertex" +output = { + type = "float2" +} + +code = """ +#if defined(FLIP_TEXCOORDS) + RESULT(float2(strip_info * 0.5 + 0.5, ribbon_distance)); +#else + RESULT(float2(ribbon_distance, strip_info * 0.5 + 0.5)); +#endif +""" diff --git a/vmf_source/core/shader_nodes/ribbon_particles/ribbon_uv_frame.shader_node b/vmf_source/core/shader_nodes/ribbon_particles/ribbon_uv_frame.shader_node new file mode 100644 index 0000000..1be832d --- /dev/null +++ b/vmf_source/core/shader_nodes/ribbon_particles/ribbon_uv_frame.shader_node @@ -0,0 +1,20 @@ +group = "Particle Ribbon" +display_name = "UV Frame" + +imports = { + uv_frame = { + type = "float" + domain = "vertex" + output_channel = "uv_frame" + } +} + +defines = ["NEEDS_UV_ANIMATION"] + +output = { + type = { typeof: "uv_frame" } +} + +code = """ + RESULT(uv_frame); +""" diff --git a/vmf_source/core/shader_nodes/ribbon_particles/ribbon_uv_scale.shader_node b/vmf_source/core/shader_nodes/ribbon_particles/ribbon_uv_scale.shader_node new file mode 100644 index 0000000..47d285f --- /dev/null +++ b/vmf_source/core/shader_nodes/ribbon_particles/ribbon_uv_scale.shader_node @@ -0,0 +1,20 @@ +group = "Particle Ribbon" +display_name = "UV Scale" + +imports = { + uv_scale = { + type = "float2" + domain = "vertex" + output_channel = "uv_scale" + } +} + +defines = ["NEEDS_UV_SCALE"] + +output = { + type = { typeof: "uv_scale" } +} + +code = """ + RESULT(uv_scale); +""" diff --git a/vmf_source/core/shader_nodes/ribbon_particles/ribbon_vertex_color.shader_node b/vmf_source/core/shader_nodes/ribbon_particles/ribbon_vertex_color.shader_node new file mode 100644 index 0000000..48cfd55 --- /dev/null +++ b/vmf_source/core/shader_nodes/ribbon_particles/ribbon_vertex_color.shader_node @@ -0,0 +1,25 @@ +group = "Particle Ribbon" +display_name = "Vertex Color" + +imports = { + color = { + type = "float4" + semantic = "COLOR" + domain = "vertex" + } +} + +output = { + type = { typeof: "color" } +} + +options = { +} + +ui = [ +] + +code = """ + color = fast_gamma_to_linear_rgba(decode_vertex_color(color)); + RESULT(color); +""" diff --git a/vmf_source/core/shader_nodes/rotator.shader_node b/vmf_source/core/shader_nodes/rotator.shader_node new file mode 100644 index 0000000..fb55097 --- /dev/null +++ b/vmf_source/core/shader_nodes/rotator.shader_node @@ -0,0 +1,41 @@ +group = "Utility" +display_name = "Rotator" +inputs = { + "96650519-54EC-47A2-B34E-3B1989F56BB3" = { name = "uv" display_name = "UV" type = "vector2" } + "178ABCED-40BA-40B1-BAA4-FB5F9732353B" = { name = "time" display_name = "Time" type = "scalar" } + "ffac2478-636b-4b9b-95b9-cd504781e2c2" = { name = "pivot" display_name = "Pivot" is_required = false type = { vector2: ["HAS_PIVOT"] }} + "399aba19-4e60-4287-b1d3-ee2acb8fd406" = { name = "speed" display_name = "Speed" is_required = false type = { scalar: ["HAS_SPEED"] }} +} + +output = { + type = { typeof: "uv" } +} + +code = """ + #ifdef HAS_SPEED + float speed_value = speed; + #else + float speed_value = 0.1; + #endif + + #ifdef HAS_PIVOT + float2 pivot_value = pivot; + #else + float2 pivot_value = float2(0.5, 0.5); + #endif + + time_type t = time * speed_value; + time_type cos_t = cos(t); + time_type sin_t = sin(t); + + // rotate vector around a pivot + #if defined(RENDERER_GL) + // gl constructs matrices in columns. + float2x2 rot_matrix = float2x2(cos_t, sin_t, -sin_t, cos_t); + #else + // hlsl constructs matrices in rows. + float2x2 rot_matrix = float2x2(cos_t, -sin_t, sin_t, cos_t); + #endif + RESULT( pivot_value + mul(uv - pivot_value, rot_matrix) ); +""" + diff --git a/vmf_source/core/shader_nodes/sample_cube.shader_node b/vmf_source/core/shader_nodes/sample_cube.shader_node new file mode 100644 index 0000000..2fe20f1 --- /dev/null +++ b/vmf_source/core/shader_nodes/sample_cube.shader_node @@ -0,0 +1,91 @@ +group = "Sampling" +display_name = "Sample Cube Texture" +inputs = { + "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" = { name = "texcoord" display_name = "UVW" type = "vector3" domain = "pixel" } + "aa23e053-3c53-40f7-a06f-23d5d5a65924" = { name = "mip_level" is_required = false display_name = "Mip Level" type = { scalar: ["HAS_MIPLEVEL"] } } +} + +domain = "pixel" +output = { + type = "float4" +} + +options = { + "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" = "ADDRESS_CLAMP" + "5dd59b3d-1762-4a14-9930-7500230ef3db" = "ADDRESS_WRAP" + "f669a3a6-0376-4187-840e-80000e2939d5" = "FILTER_LINEAR" + "43dea0e2-a77d-410d-88bb-945dac9139d8" = "FILTER_POINT" + "1e067464-12d8-4826-9b72-cfd5765003e3" = "FILTER_ANISOTROPIC" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" = "SRGB" + "90e20826-8689-42fa-8e24-f484ec64c5c3" = "NORMAL_MAP_DECODE" + "43710e4f-f52a-4038-8ec8-d6cb0546103b" = "RGBM_DECODE" + "e94e53e6-49b6-4194-a747-8f064a5932e0" = "LINEAR" +} + +ui = [ + { + type = "drop_down" + display_name = "Encoding" + options = { + "Linear Color" = "e94e53e6-49b6-4194-a747-8f064a5932e0" + "sRGB Color" = "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + "Normal Map" = "90e20826-8689-42fa-8e24-f484ec64c5c3" + "RGBM Color" = "43710e4f-f52a-4038-8ec8-d6cb0546103b" + } + default = "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + } + { + type = "drop_down" + display_name = "Address mode" + options = { + "Clamp" = "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" + "Wrap" = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + default = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + { + type = "drop_down" + display_name = "Filter mode" + options = { + "Anisotropic" = "1e067464-12d8-4826-9b72-cfd5765003e3" + "Linear" = "f669a3a6-0376-4187-840e-80000e2939d5" + "Point" = "43dea0e2-a77d-410d-88bb-945dac9139d8" + } + default = "f669a3a6-0376-4187-840e-80000e2939d5" + } +] + +code_blocks = { + default = { + language = "hlsl" + + samplers = { + texture_map = { + display_name = "Texture" + type = "cube" + sampler_state = "core/shader_nodes/graph_common#default_node_sampler" + source = "material" + } + } + + code = """ + float4 result; + #if defined(HAS_MIPLEVEL) + result = TEXCUBELOD(texture_map, texcoord, mip_level); + #else + result = TEXCUBE(texture_map, texcoord); + #endif + + #if defined(RENDERER_GL) && defined(SRGB) + result = fast_gamma_to_linear_rgba(result); + #endif + #if defined(NORMAL_MAP_DECODE) + result = float4(decode_normal_map(result), 0); + #elif defined(RGBM_DECODE) + result = float4(rgbm_decode(result), 0); + #endif + + RESULT(result); + """ + } +} diff --git a/vmf_source/core/shader_nodes/sample_texture.shader_node b/vmf_source/core/shader_nodes/sample_texture.shader_node new file mode 100644 index 0000000..76192d4 --- /dev/null +++ b/vmf_source/core/shader_nodes/sample_texture.shader_node @@ -0,0 +1,197 @@ +group = "Sampling" +display_name = "Sample Texture" +inputs = { + "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" = { name = "texcoord" display_name = "UV" type = "vector2" domain = "pixel" } + "aa23e053-3c53-40f7-a06f-23d5d5a65924" = { name = "mip_level" is_required = false display_name = "Mip Level" type = { scalar: ["HAS_MIPLEVEL"] } } +} + +domain = "pixel" +output = { + type = "float4" +} + +options = { + "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" = "ADDRESS_CLAMP" + "5dd59b3d-1762-4a14-9930-7500230ef3db" = "ADDRESS_WRAP" + "f669a3a6-0376-4187-840e-80000e2939d5" = "FILTER_LINEAR" + "43dea0e2-a77d-410d-88bb-945dac9139d8" = "FILTER_POINT" + "1e067464-12d8-4826-9b72-cfd5765003e3" = "FILTER_ANISOTROPIC" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" = "SRGB" + "90e20826-8689-42fa-8e24-f484ec64c5c3" = "NORMAL_MAP_DECODE" + "43710e4f-f52a-4038-8ec8-d6cb0546103b" = "RGBM_DECODE" + "e94e53e6-49b6-4194-a747-8f064a5932e0" = "LINEAR" + "0268506C-B417-49DC-BBBE-3D5949595940" = "FLIP_GREEN" + "aea8c8f4-81e6-4784-bc83-bee2f73eea58" = "NORMAL_ROUGHNESS_DECODE" +} + +ui = [ + { + type = "drop_down" + display_name = "Encoding" + options = { + "Linear Color" = "e94e53e6-49b6-4194-a747-8f064a5932e0" + "sRGB Color" = "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + "Normal Map" = "90e20826-8689-42fa-8e24-f484ec64c5c3" + "Normal Roughness Map" = "aea8c8f4-81e6-4784-bc83-bee2f73eea58" + "RGBM Color" = "43710e4f-f52a-4038-8ec8-d6cb0546103b" + } + default = "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + } + { + type = "drop_down" + display_name = "Address mode" + options = { + "Clamp" = "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" + "Wrap" = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + default = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + { + type = "drop_down" + display_name = "Filter mode" + options = { + "Anisotropic" = "1e067464-12d8-4826-9b72-cfd5765003e3" + "Linear" = "f669a3a6-0376-4187-840e-80000e2939d5" + "Point" = "43dea0e2-a77d-410d-88bb-945dac9139d8" + } + default = "1e067464-12d8-4826-9b72-cfd5765003e3" + } + { type = "checkbox" display_name = "Invert Green Channel" option = "0268506C-B417-49DC-BBBE-3D5949595940" } +] + +code_blocks = { + default = { + include: ["texture_debug"] + language = "hlsl" + samplers = { + texture_map = { + display_name = "Texture" + type = "2d" + sampler_state = "core/shader_nodes/graph_common#default_node_sampler" + source = "material" + } + } + + code = """ + float4 result; + #if defined(HAS_MIPLEVEL) + result = TEX2DLOD(texture_map, texcoord, mip_level); + #else + result = TEX2D(texture_map, texcoord); + #endif + + #if defined(FLIP_GREEN) + result.y = 1.0-result.y; + #endif + + #if defined(RENDERER_GL) && defined(SRGB) + result = fast_gamma_to_linear_rgb(result); + #endif + + #if defined(NORMAL_MAP_DECODE) + result = float4(decode_normal_map(result), result.a); + #elif defined(RGBM_DECODE) + result = float4(rgbm_decode(result), result.a); + #elif defined(NORMAL_ROUGHNESS_DECODE) + float3 N = 2.0*result.rgb - 1.0; + float roughness = result.a; + #if 0 + // Specular AA + // TODO: Pre-bake this code + // http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf + float r = length(N); + if (r < 1.0) { + float rr = r * r ; + float kappa = (3.0 * r - r * rr) / (1.0 - rr) ; + float variance = 1.0 / (2.0 * kappa) ; + roughness = sqrt (roughness * roughness + variance); + } + #endif + result = float4(normalize(N), roughness); + #endif + + #if defined(MIPMAP_LEVEL_VISUALIZATION) + // inspired by http://aras-p.info/blog/2011/05/03/a-way-to-visualize-mip-levels/ + #if defined(HAS_MIPLEVEL) + float4 mip_color = miplevel_debug_color(mip_level); + result = lerp(result, mip_color, mip_color.a); + #else + float mip_level_ = calculate_miplevel(texture_map, texcoord); + float4 mip_color = miplevel_debug_color(mip_level_); + result = lerp(result, mip_color, mip_color.a); + #endif + #elif defined(TEXTURE_DENSITY_VISUALIZATION) + float texture_density = calculate_texture_density(texture_map, texcoord); + float4 blend_color = texture_density_debug(texture_density, 10.0); + result = lerp(result, blend_color, blend_color.a); + #elif defined(COLORED_TEXTURE_DENSITY_VISUALIZATION) + float texture_density = calculate_texture_density(texture_map, texcoord); + float4 blend_color = texture_density_debug(texture_density, 10.0); + result = lerp(float4(0.0, 1.0, 0.0, 0.0), blend_color, blend_color.a); + #endif + + RESULT(result); + """ + } + + texture_debug = { + language = "hlsl" + code=""" + // Seems to be fairly accurate to HLSL + inline float calculate_miplevel(Sampler2D texture_map, float2 uv) + { + // The OpenGL Graphics System: A Specification 4.2 + // - chapter 3.9.11, equation 3.21 + float2 texture_dim; + texture_map.tex.GetDimensions(texture_dim.x, texture_dim.y); + float2 texcoord = uv * texture_dim; + float2 dx_vtc = ddx(texcoord); + float2 dy_vtc = ddy(texcoord); + float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); + return 0.5 * log2(delta_max_sqr); // == log2(sqrt(delta_max_sqr)); + } + + inline float calculate_texture_density(Sampler2D texture_map, float2 uv) + { + float2 texture_dim; + texture_map.tex.GetDimensions(texture_dim.x, texture_dim.y); + float2 texcoord = uv * texture_dim; + return max(length(ddx(texcoord)), length(ddy(texcoord))); + } + + inline float calculate_mipmap_texture_density(Sampler2D texture_map, float2 uv) + { + uint mip_level_ = calculate_miplevel(texture_map, uv); + return calculate_texture_density(texture_map, uv/float(1< 0 ) { + result = TEX2DLOD(texture_map, texcoord, mip_level); + + #if defined(HAS_TINT) + result.rgb *= tint_color; + #endif + + #if !defined(NORMAL_MAP_DECODE) && !defined(NORMAL_ROUGHNESS_DECODE) + result = lerp(float4(input,1), result, result.a * texcoord.z); + #endif + + #if defined(FLIP_GREEN) + result.y = 1.0-result.y; + #endif + + #if defined(RENDERER_GL) && defined(SRGB) + result = fast_gamma_to_linear_rgba(result); + #endif + + #if defined(NORMAL_MAP_DECODE) + float alpha = result.a; + result = float4(decode_normal_map(result), 0.0); + float2 xy = result.xy + input.xy; + result = lerp(float4(input,1), float4(normalize(new_float3(xy.x, xy.y, input.z*result.z)), 1), alpha * texcoord.z); + result.a = alpha; + #elif defined(RGBM_DECODE) + result = float4(rgbm_decode(result), 0.0); + #elif defined(NORMAL_ROUGHNESS_DECODE) + float3 N = 2.0*result.rgb - 1.0; + float roughness = result.a; + #if 0 + // Specular AA + // TODO: Pre-bake this code + // http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf + float r = length(N); + if (r < 1.0) { + float rr = r * r ; + float kappa = (3.0 * r - r * rr) / (1.0 - rr) ; + float variance = 1.0 / (2.0 * kappa) ; + roughness = sqrt (roughness * roughness + variance); + } + #endif + + N = lerp(input, N, result.a); + result = float4(normalize(N), roughness); + #endif + } else { + result = float4(input,0); + } + + RESULT(result); + """ + } + + texture_mip_calculate = { + language = "hlsl" + code=""" + // Seems to be fairly accurate to HLSL + inline float calculate_miplevel_test(Sampler2D texture_map, float2 uv) + { + // The OpenGL Graphics System: A Specification 4.2 + // - chapter 3.9.11, equation 3.21 + float2 texture_dim; + texture_map.tex.GetDimensions(texture_dim.x, texture_dim.y); + float2 texcoord = uv * texture_dim; + float2 dx_vtc = ddx(texcoord); + float2 dy_vtc = ddy(texcoord); + float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); + return 0.5 * log2(delta_max_sqr); // == log2(sqrt(delta_max_sqr)); + } + """ + } +} diff --git a/vmf_source/core/shader_nodes/sample_texture_vertex.shader_node b/vmf_source/core/shader_nodes/sample_texture_vertex.shader_node new file mode 100644 index 0000000..a7d9a25 --- /dev/null +++ b/vmf_source/core/shader_nodes/sample_texture_vertex.shader_node @@ -0,0 +1,115 @@ +group = "Sampling" +display_name = "Sample Texture VS" +inputs = { + "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" = { name = "texcoord" display_name = "UV" type = "vector2" domain = "pixel" } + "aa23e053-3c53-40f7-a06f-23d5d5a65924" = { name = "mip_level" is_required = true display_name = "Mip Level" type = { scalar: ["HAS_MIPLEVEL"] } } +} + +domain = "vertex" +output = { + type = "float4" +} + +options = { + "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" = "ADDRESS_CLAMP" + "5dd59b3d-1762-4a14-9930-7500230ef3db" = "ADDRESS_WRAP" + "f669a3a6-0376-4187-840e-80000e2939d5" = "FILTER_LINEAR" + "43dea0e2-a77d-410d-88bb-945dac9139d8" = "FILTER_POINT" + "1e067464-12d8-4826-9b72-cfd5765003e3" = "FILTER_ANISOTROPIC" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" = "SRGB" + "90e20826-8689-42fa-8e24-f484ec64c5c3" = "NORMAL_MAP_DECODE" + "43710e4f-f52a-4038-8ec8-d6cb0546103b" = "RGBM_DECODE" + "e94e53e6-49b6-4194-a747-8f064a5932e0" = "LINEAR" + "0268506C-B417-49DC-BBBE-3D5949595940" = "FLIP_GREEN" + "aea8c8f4-81e6-4784-bc83-bee2f73eea58" = "NORMAL_ROUGHNESS_DECODE" +} + +ui = [ + { + type = "drop_down" + display_name = "Encoding" + options = { + "Linear Color" = "e94e53e6-49b6-4194-a747-8f064a5932e0" + "sRGB Color" = "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + "Normal Map" = "90e20826-8689-42fa-8e24-f484ec64c5c3" + "Normal Roughness Map" = "aea8c8f4-81e6-4784-bc83-bee2f73eea58" + "RGBM Color" = "43710e4f-f52a-4038-8ec8-d6cb0546103b" + } + default = "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + } + { + type = "drop_down" + display_name = "Address mode" + options = { + "Clamp" = "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" + "Wrap" = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + default = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + { + type = "drop_down" + display_name = "Filter mode" + options = { + "Anisotropic" = "1e067464-12d8-4826-9b72-cfd5765003e3" + "Linear" = "f669a3a6-0376-4187-840e-80000e2939d5" + "Point" = "43dea0e2-a77d-410d-88bb-945dac9139d8" + } + default = "1e067464-12d8-4826-9b72-cfd5765003e3" + } + { type = "checkbox" display_name = "Invert Green Channel" option = "0268506C-B417-49DC-BBBE-3D5949595940" } +] + +code_blocks = { + default = { + language = "hlsl" + samplers = { + texture_map = { + display_name = "Texture" + type = "2d" + sampler_state = "core/shader_nodes/graph_common#default_node_sampler" + source = "material" + } + } + + code = """ + float4 result; + #if defined(HAS_MIPLEVEL) + result = TEX2DLOD(texture_map, texcoord, mip_level); + #else + result = TEX2D(texture_map, texcoord); + #endif + + #if defined(FLIP_GREEN) + result.y = 1.0-result.y; + #endif + + #if defined(RENDERER_GL) && defined(SRGB) + result = fast_gamma_to_linear_rgb(result); + #endif + + #if defined(NORMAL_MAP_DECODE) + result = float4(decode_normal_map(result), result.a); + #elif defined(RGBM_DECODE) + result = float4(rgbm_decode(result), result.a); + #elif defined(NORMAL_ROUGHNESS_DECODE) + float3 N = 2.0*result.rgb - 1.0; + float roughness = result.a; + #if 0 + // Specular AA + // TODO: Pre-bake this code + // http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf + float r = length(N); + if (r < 1.0) { + float rr = r * r ; + float kappa = (3.0 * r - r * rr) / (1.0 - rr) ; + float variance = 1.0 / (2.0 * kappa) ; + roughness = sqrt (roughness * roughness + variance); + } + #endif + result = float4(normalize(N), roughness); + #endif + + RESULT(result); + """ + } +} diff --git a/vmf_source/core/shader_nodes/sin.shader_node b/vmf_source/core/shader_nodes/sin.shader_node new file mode 100644 index 0000000..4b5d51f --- /dev/null +++ b/vmf_source/core/shader_nodes/sin.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Sine" +inputs = { + "0505200e-439a-476b-b6a6-d643cf80e20b" = { name = "a" display_name = "Angle" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(sin(a)); +""" diff --git a/vmf_source/core/shader_nodes/skydome/skydome_cloud_map.shader_node b/vmf_source/core/shader_nodes/skydome/skydome_cloud_map.shader_node new file mode 100644 index 0000000..902d963 --- /dev/null +++ b/vmf_source/core/shader_nodes/skydome/skydome_cloud_map.shader_node @@ -0,0 +1,81 @@ +group = "Skydome" +display_name = "Cloud Map" +depends_on = ["core/stingray_renderer/output_nodes/skydome_base"] + +inputs = { + "72b20d47-88d1-48a4-99f5-819433b2d601" = { name = "texcoord" display_name = "UV" type = "vector2" domain = "pixel" } +} + +domain = "pixel" +output = { + type = "float4" +} + +options = { + "49e69d88-c7ce-4223-b3ce-149c0bf72a0a" = "ADDRESS_CLAMP" + "80c5c862-1d7e-4fc8-acfb-6fed1cd608f2" = "ADDRESS_WRAP" + "d293ab9d-8dda-4a7d-9114-0ff75dfe2378" = "FILTER_LINEAR" + "b780e0f4-527f-43c1-a39c-860e17417114" = "FILTER_POINT" + "04dac948-1138-4283-861f-20c70a20548f" = "FILTER_ANISOTROPIC" + "63b1e469-c7c0-4a32-b5da-5cf14d304ca0" = "SRGB" + "c3acf080-6620-435c-aeca-1f6ba47ee1b5" = "LINEAR" + "3f78e559-e91b-4b93-a68b-0140a240193c" = "RGBM_DECODE" +} + +ui = [ + { + type = "drop_down" + display_name = "Encoding" + options = { + "Linear Color" = "c3acf080-6620-435c-aeca-1f6ba47ee1b5" + "sRGB Color" = "63b1e469-c7c0-4a32-b5da-5cf14d304ca0" + "RGBM Color" = "3f78e559-e91b-4b93-a68b-0140a240193c" + } + default = "63b1e469-c7c0-4a32-b5da-5cf14d304ca0" + } + { + type = "drop_down" + display_name = "Address mode" + options = { + "Clamp" = "49e69d88-c7ce-4223-b3ce-149c0bf72a0a" + "Wrap" = "80c5c862-1d7e-4fc8-acfb-6fed1cd608f2" + } + default = "80c5c862-1d7e-4fc8-acfb-6fed1cd608f2" + } + { + type = "drop_down" + display_name = "Filter mode" + options = { + "Anisotropic" = "04dac948-1138-4283-861f-20c70a20548f" + "Linear" = "c3acf080-6620-435c-aeca-1f6ba47ee1b5" + "Point" = "b780e0f4-527f-43c1-a39c-860e17417114" + } + default = "04dac948-1138-4283-861f-20c70a20548f" + } +] + +code_blocks = { + default = { + language = "hlsl" + samplers = { + skydome_cloud_map = { + sampler_state = "core/shader_nodes/graph_common#default_node_sampler" + source = "resource_set" + slot_name = "skydome_cloud_map" + type = "2d" + } + } + + code = """ + float4 result = TEX2D(skydome_cloud_map, texcoord); + + #if defined(RENDERER_GL) && defined(SRGB) + result = fast_gamma_to_linear_rgba(result); + #elif defined(RGBM_DECODE) + result = float4(rgbm_decode(result), 0); + #endif + + RESULT(result); + """ + } +} diff --git a/vmf_source/core/shader_nodes/skydome/skydome_cloud_speed_scale.shader_node b/vmf_source/core/shader_nodes/skydome/skydome_cloud_speed_scale.shader_node new file mode 100644 index 0000000..7a96315 --- /dev/null +++ b/vmf_source/core/shader_nodes/skydome/skydome_cloud_speed_scale.shader_node @@ -0,0 +1,19 @@ +group = "Skydome" +display_name = "Cloud Speed/Scale" +depends_on = ["core/stingray_renderer/output_nodes/skydome_base"] + +imports = { + skydome_cloud_speed_scale = { + type = "float2" + domain = "global" + source = "shading_environment" + } +} + +output = { + type = { typeof: "skydome_cloud_speed_scale" } +} + +code = """ + RESULT(skydome_cloud_speed_scale); +""" diff --git a/vmf_source/core/shader_nodes/skydome/skydome_color.shader_node b/vmf_source/core/shader_nodes/skydome/skydome_color.shader_node new file mode 100644 index 0000000..7471f3f --- /dev/null +++ b/vmf_source/core/shader_nodes/skydome/skydome_color.shader_node @@ -0,0 +1,98 @@ +group = "Skydome" +display_name = "Skydome Color" +depends_on = ["core/stingray_renderer/output_nodes/skydome_base"] + +inputs = { + "33d9da7d-4833-40a3-84d9-c88daa3b2c1e" = { name = "texcoord" display_name = "UV" type = "vector2" domain = "pixel" } +} + +imports = { + skydome_u_offset = { + type = "float" + domain = "global" + source = "shading_environment" + } + + skydome_intensity = { + type = "float" + domain = "global" + source = "shading_environment" + } +} + + +domain = "pixel" +output = { + type = "float4" +} + +options = { + "e333e9d1-0cb9-4caa-a40e-d212b375c777" = "ADDRESS_CLAMP" + "6599f9bf-2828-4b29-a20f-2f78e991862b" = "ADDRESS_WRAP" + "b5ec6d15-ed64-4c8a-b850-5c269a296f4a" = "FILTER_LINEAR" + "4f6b3270-b92a-433a-88e4-b2a3d015bd8a" = "FILTER_POINT" + "f7162c47-1353-4a4c-adfc-5b52dedc6053" = "FILTER_ANISOTROPIC" + "2392b33c-e963-4553-bebd-066f2a27bc30" = "SRGB" + "cf74777c-db6d-4ccc-b20a-801302770d6d" = "LINEAR" + "1f9c9f28-91ea-4bc9-ad22-f099146fead1" = "RGBM_DECODE" +} + +ui = [ + { + type = "drop_down" + display_name = "Encoding" + options = { + "Linear Color" = "cf74777c-db6d-4ccc-b20a-801302770d6d" + "sRGB Color" = "2392b33c-e963-4553-bebd-066f2a27bc30" + "RGBM Color" = "1f9c9f28-91ea-4bc9-ad22-f099146fead1" + } + default = "cf74777c-db6d-4ccc-b20a-801302770d6d" + } + { + type = "drop_down" + display_name = "Address mode" + options = { + "Clamp" = "e333e9d1-0cb9-4caa-a40e-d212b375c777" + "Wrap" = "6599f9bf-2828-4b29-a20f-2f78e991862b" + } + default = "6599f9bf-2828-4b29-a20f-2f78e991862b" + } + { + type = "drop_down" + display_name = "Filter mode" + options = { + "Anisotropic" = "f7162c47-1353-4a4c-adfc-5b52dedc6053" + "Linear" = "b5ec6d15-ed64-4c8a-b850-5c269a296f4a" + "Point" = "4f6b3270-b92a-433a-88e4-b2a3d015bd8a" + } + default = "b5ec6d15-ed64-4c8a-b850-5c269a296f4a" + } +] + +code_blocks = { + default = { + language = "hlsl" + samplers = { + skydome_map = { + sampler_state = "core/shader_nodes/graph_common#default_node_sampler" + source = "resource_set" + slot_name = "skydome_map" + type = "2d" + } + } + + code = """ + float4 skydome_color = TEX2D(skydome_map, texcoord + float2(skydome_u_offset, 0)); + + #if defined(RENDERER_GL) && defined(SRGB) + skydome_color = fast_gamma_to_linear_rgba(skydome_color); + #elif defined(RGBM_DECODE) + skydome_color = float4(rgbm_decode(skydome_color), 0); + #endif + + skydome_color.rgb *= skydome_intensity; + + RESULT(skydome_color); + """ + } +} diff --git a/vmf_source/core/shader_nodes/skydome/skydome_fog_color.shader_node b/vmf_source/core/shader_nodes/skydome/skydome_fog_color.shader_node new file mode 100644 index 0000000..ea7df0e --- /dev/null +++ b/vmf_source/core/shader_nodes/skydome/skydome_fog_color.shader_node @@ -0,0 +1,114 @@ +group = "Skydome" +display_name = "Fog Color/Sun Blend" +depends_on = ["core/stingray_renderer/output_nodes/skydome_base"] + +imports = { + eye_vector = { + type = "float3" + domain = "vertex" + output_channel = "eye_vector" + } + + fog_color = { + type = "float3" + domain = "global" + source = "shading_environment" + } + + sun_direction = { + type = "float3" + domain = "global" + source = "shading_environment" + } + + sun_color = { + type = "float3" + domain = "global" + source = "engine" + } + + fog_sun_blend = { + type = "float3" + domain = "global" + source = "shading_environment" + } + + /* + // TODO + custom_fog_blend_direction = { + type = "float3" + domain = "global" + source = "shading_environment" + } + + custom_fog_blend_color = { + type = "float3" + domain = "global" + source = "shading_environment" + } + + custom_fog_blend = { + type = "float3" + domain = "global" + source = "shading_environment" + } + + secondary_sun_direction = { + type = "float3" + domain = "global" + source = "shading_environment" + } + + secondary_sun_color = { + type = "float3" + domain = "global" + source = "shading_environment" + } + + secondary_sun_blend = { + type = "float3" + domain = "global" + source = "shading_environment" + }*/ +} + +defines = ["NEEDS_EYE_VECTOR", "HAS_SUN_COLOR"] + +domain = "pixel" +output = { + type = "float4" +} + +options = { + "44123bdc-7a38-4a69-b738-ad93e819f28c" = "SECONDARY_SUN_BLEND" + "f169202d-4a88-4347-8f26-af46d249a5f6" = "CUSTOM_FOG_BLEND" +} + +ui = [ + { type = "checkbox" display_name = "Secondary sun blend" option = "44123bdc-7a38-4a69-b738-ad93e819f28c" } + { type = "checkbox" display_name = "Custom fog blend" option = "f169202d-4a88-4347-8f26-af46d249a5f6" } +] + +language = "hlsl" +code = """ + float3 view_dir = normalize(eye_vector); + + half sa = 0.0; + float3 c = fog_color; + + /*#if defined(SECONDARY_SUN_BLEND) + sa = secondary_sun_blend.x * pow(saturate(dot(view_dir, secondary_sun_direction)), secondary_sun_blend.y); + c = lerp(c, secondary_sun_blend.z * secondary_sun_color, sa); + #endif + + #if defined(CUSTOM_FOG_BLEND) + sa = custom_fog_blend.x * pow(saturate(dot(view_dir, custom_fog_blend_direction)), custom_fog_blend.y); + c = lerp(c, custom_fog_blend.z * custom_fog_blend_color, sa); + #endif*/ + + sa = fog_sun_blend.x * pow(saturate(dot(view_dir, sun_direction)), fog_sun_blend.y); + c = lerp(c, fog_sun_blend.z * sun_color, sa); + + RESULT(float4(c, sa)); +""" + diff --git a/vmf_source/core/shader_nodes/skydome/skydome_fog_height_falloff.shader_node b/vmf_source/core/shader_nodes/skydome/skydome_fog_height_falloff.shader_node new file mode 100644 index 0000000..7fde5d9 --- /dev/null +++ b/vmf_source/core/shader_nodes/skydome/skydome_fog_height_falloff.shader_node @@ -0,0 +1,19 @@ +group = "Skydome" +display_name = "Skydome Fog Height/Falloff" +depends_on = ["core/stingray_renderer/output_nodes/skydome_base"] + +imports = { + skydome_fog_height_falloff = { + type = "float2" + domain = "global" + source = "shading_environment" + } +} + +output = { + type = { typeof: "skydome_fog_height_falloff" } +} + +code = """ + RESULT(skydome_fog_height_falloff); +""" diff --git a/vmf_source/core/shader_nodes/skydome/skydome_intensity.shader_node b/vmf_source/core/shader_nodes/skydome/skydome_intensity.shader_node new file mode 100644 index 0000000..c602af5 --- /dev/null +++ b/vmf_source/core/shader_nodes/skydome/skydome_intensity.shader_node @@ -0,0 +1,19 @@ +group = "Skydome" +display_name = "Skydome Intensity" +depends_on = ["core/stingray_renderer/output_nodes/skydome_base"] + +imports = { + skydome_intensity = { + type = "float" + domain = "global" + source = "shading_environment" + } +} + +output = { + type = { typeof: "skydome_intensity" } +} + +code = """ + RESULT(skydome_intensity); +""" diff --git a/vmf_source/core/shader_nodes/skydome/skydome_tint_color.shader_node b/vmf_source/core/shader_nodes/skydome/skydome_tint_color.shader_node new file mode 100644 index 0000000..6625c67 --- /dev/null +++ b/vmf_source/core/shader_nodes/skydome/skydome_tint_color.shader_node @@ -0,0 +1,19 @@ +group = "Skydome" +display_name = "Skydome Tint Color" +depends_on = ["core/stingray_renderer/output_nodes/skydome_base"] + +imports = { + skydome_tint_color = { + type = "float3" + domain = "global" + source = "shading_environment" + } +} + +output = { + type = { typeof: "skydome_tint_color" } +} + +code = """ + RESULT(skydome_tint_color); +""" diff --git a/vmf_source/core/shader_nodes/smoothstep.shader_node b/vmf_source/core/shader_nodes/smoothstep.shader_node new file mode 100644 index 0000000..1c89c60 --- /dev/null +++ b/vmf_source/core/shader_nodes/smoothstep.shader_node @@ -0,0 +1,15 @@ +group = "Math" +display_name = "Smoothstep" +inputs = { + "1a8071d5-29fa-44f1-a22d-9c08b526e4a5" = { name = "max" display_name = "Max" type = "auto" } + "026ff907-516a-41d3-9170-d09ff255ace8" = { name = "min" display_name = "Min" type = "auto" } + "19ef1275-ff3f-4a97-be76-9f8c65bed7aa" = { name = "x" display_name = "X" type = "auto" } +} + +output = { + type = { typeof: "max" } +} + +code = """ + RESULT(smoothstep(min,max,x)); +""" diff --git a/vmf_source/core/shader_nodes/square_root.shader_node b/vmf_source/core/shader_nodes/square_root.shader_node new file mode 100644 index 0000000..587bf03 --- /dev/null +++ b/vmf_source/core/shader_nodes/square_root.shader_node @@ -0,0 +1,13 @@ +group = "Math" +display_name = "Square Root" +inputs = { + "19ef1275-ff3f-4a97-be76-9f8c65bed7aa" = { name = "a" display_name = "A" type = "auto" } +} + +output = { + type = { typeof: "a" } +} + +code = """ + RESULT(sqrt(a)); +""" diff --git a/vmf_source/core/shader_nodes/subtract.shader_node b/vmf_source/core/shader_nodes/subtract.shader_node new file mode 100644 index 0000000..5117e65 --- /dev/null +++ b/vmf_source/core/shader_nodes/subtract.shader_node @@ -0,0 +1,14 @@ +group = "Math" +display_name = "Subtract" +inputs = { + "d5e375bc-daa4-4464-8448-14bf08d97f0e" = { name = "a" display_name = "A" type = "auto" } + "cff85d3c-d5da-4ffc-ad20-f3872809ac4f" = { name = "b" display_name = "B" type = "auto" } +} + +output = { + type = { largestof: ["a", "b"] } +} + +code = """ + RESULT(a - b); +""" diff --git a/vmf_source/core/shader_nodes/sun_color.shader_node b/vmf_source/core/shader_nodes/sun_color.shader_node new file mode 100644 index 0000000..a1cc978 --- /dev/null +++ b/vmf_source/core/shader_nodes/sun_color.shader_node @@ -0,0 +1,20 @@ +group = "Input" +display_name = "Sun Color" + +imports = { + sun_color = { + type = "float3" + domain = "global" + source = "engine" + } +} + +defines = ["HAS_SUN_COLOR"] + +output = { + type = { typeof: "sun_color" } +} + +code = """ + RESULT(sun_color); +""" diff --git a/vmf_source/core/shader_nodes/sun_direction.shader_node b/vmf_source/core/shader_nodes/sun_direction.shader_node new file mode 100644 index 0000000..79b8bd3 --- /dev/null +++ b/vmf_source/core/shader_nodes/sun_direction.shader_node @@ -0,0 +1,21 @@ +group = "Input" +display_name = "Sun Direction" +depends_on = ["core/stingray_renderer/output_nodes/standard_base"] + +imports = { + sun_direction = { + type = "float3" + domain = "global" + source = "engine" + } +} + +defines = ["HAS_SUN_DIRECTION"] + +output = { + type = { typeof: "sun_direction" } +} + +code = """ + RESULT(sun_direction); +""" diff --git a/vmf_source/core/shader_nodes/sun_shadow_mask.shader_node b/vmf_source/core/shader_nodes/sun_shadow_mask.shader_node new file mode 100644 index 0000000..7cf4e80 --- /dev/null +++ b/vmf_source/core/shader_nodes/sun_shadow_mask.shader_node @@ -0,0 +1,27 @@ +group = "Input" +display_name = "Sun Shadow Mask" + +depends_on = ["core/stingray_renderer/output_nodes/standard_base"] + +defines = ["NEEDS_SUN_SHADOW_MASK"] + +imports = { + sun_shadow_mask = { + type = "float" + domain = "pixel" + output_channel = "sun_shadow_mask" + } +} + +domain = "pixel" +output = { + type = "float" +} + +code = """ + #if defined(HAS_SUN_SHADOW_MASK) + RESULT(sun_shadow_mask); + #else + RESULT(1.0); + #endif +""" diff --git a/vmf_source/core/shader_nodes/tangent_to_world.shader_node b/vmf_source/core/shader_nodes/tangent_to_world.shader_node new file mode 100644 index 0000000..fb30269 --- /dev/null +++ b/vmf_source/core/shader_nodes/tangent_to_world.shader_node @@ -0,0 +1,42 @@ +group = "Transform" +display_name = "Tangent To World" +depends_on = [ + "core/stingray_renderer/output_nodes/standard_base" + "core/stingray_renderer/output_nodes/terrain_base" +] + +inputs = { + "f72597c4-7487-419a-affb-df690e6582e1" = { name = "v" display_name = "Vector" type = "float3" } +} + +defines = ["NEEDS_TANGENT_SPACE"] + +imports = { + tsm0 = { + type = "float3" + domain = "vertex" + output_channel = "tsm0" + } + tsm1 = { + type = "float3" + domain = "vertex" + output_channel = "tsm1" + } + tsm2 = { + type = "float3" + domain = "vertex" + output_channel = "tsm2" + } +} + +output = { + type = { typeof: "v" } +} + +code = """ + float3 res = float3( + dot(v, tsm0), + dot(v, tsm1), + dot(v, tsm2)); + RESULT(normalize(res)); +""" diff --git a/vmf_source/core/shader_nodes/terrain_base_normal.shader_node b/vmf_source/core/shader_nodes/terrain_base_normal.shader_node new file mode 100644 index 0000000..6240cb1 --- /dev/null +++ b/vmf_source/core/shader_nodes/terrain_base_normal.shader_node @@ -0,0 +1,43 @@ +group = "Terrain" +display_name = "Terrain Base Normal" +depends_on = ["core/stingray_renderer/output_nodes/terrain_base"] + +inputs = { +// "e3419eb2-3635-4586-b58c-89ffd88d20d5" = { name = "speed_v" display_name = "Speed V" is_required = false type = { scalar: ["HAS_SPEED_V"] }} +} + +imports = { + uv = { + type = "float2" + domain = "vertex" + output_channel = "terrain_uv" + } + + terrain_size = { + type = "float3" + domain = "global" + source = "engine" + } +} + +domain = "pixel" +output = { + type = "float3" +} + +code_blocks = { + default = { + language = "hlsl" + + code = """ + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float2 res; + TEXTURE_NAME(hmap).GetDimensions(res.x, res.y); + float2 inv_hmap_size = 1.0/res; + #endif + + float3 n = normal_from_hmap(hmap, uv, inv_hmap_size, terrain_size.z); + RESULT(n); + """ + } +} diff --git a/vmf_source/core/shader_nodes/terrain_blend_mask.shader_node b/vmf_source/core/shader_nodes/terrain_blend_mask.shader_node new file mode 100644 index 0000000..749536d --- /dev/null +++ b/vmf_source/core/shader_nodes/terrain_blend_mask.shader_node @@ -0,0 +1,62 @@ +group = "Terrain" +display_name = "Sample Blend Mask" +depends_on = ["core/stingray_renderer/output_nodes/terrain_base"] + +inputs = { + "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" = { name = "texcoord" display_name = "UV" type = "vector2" domain = "pixel" } +} + +domain = "pixel" +output = { + type = "float4" +} + +options = { + "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" = "ADDRESS_CLAMP" + "5dd59b3d-1762-4a14-9930-7500230ef3db" = "ADDRESS_WRAP" + "f669a3a6-0376-4187-840e-80000e2939d5" = "FILTER_LINEAR" + "43dea0e2-a77d-410d-88bb-945dac9139d8" = "FILTER_POINT" + "1e067464-12d8-4826-9b72-cfd5765003e3" = "FILTER_ANISOTROPIC" +} + +ui = [ + { + type = "drop_down" + display_name = "Address mode" + options = { + "Clamp" = "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" + "Wrap" = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + default = "5dd59b3d-1762-4a14-9930-7500230ef3db" + } + { + type = "drop_down" + display_name = "Filter mode" + options = { + "Anisotropic" = "1e067464-12d8-4826-9b72-cfd5765003e3" + "Linear" = "f669a3a6-0376-4187-840e-80000e2939d5" + "Point" = "43dea0e2-a77d-410d-88bb-945dac9139d8" + } + default = "f669a3a6-0376-4187-840e-80000e2939d5" + } +] + +code_blocks = { + default = { + language = "hlsl" + + samplers = { + mask = { + type = "2d" + sampler_state = "core/shader_nodes/graph_common#default_node_sampler" + source = "resource_set" + slot_name = "blend_mask" + } + } + + code = """ + float4 c = TEX2D(mask, texcoord); + RESULT(c); + """ + } +} diff --git a/vmf_source/core/shader_nodes/terrain_height.shader_node b/vmf_source/core/shader_nodes/terrain_height.shader_node new file mode 100644 index 0000000..5aea5f7 --- /dev/null +++ b/vmf_source/core/shader_nodes/terrain_height.shader_node @@ -0,0 +1,33 @@ +group = "Terrain" +display_name = "Terrain Height" +depends_on = ["core/stingray_renderer/output_nodes/terrain_base"] + +imports = { + uv = { + type = "float2" + domain = "vertex" + output_channel = "terrain_uv" + } + + terrain_size = { + type = "float3" + domain = "global" + source = "engine" + } +} + +domain = "pixel" +output = { + type = "float" +} + +code_blocks = { + default = { + language = "hlsl" + + code = """ + float h = TEX2D(hmap, uv).r * terrain_size.z; + RESULT(h); + """ + } +} diff --git a/vmf_source/core/shader_nodes/terrain_uv.shader_node b/vmf_source/core/shader_nodes/terrain_uv.shader_node new file mode 100644 index 0000000..7c70c00 --- /dev/null +++ b/vmf_source/core/shader_nodes/terrain_uv.shader_node @@ -0,0 +1,19 @@ +group = "Terrain" +display_name = "Terrain UV" +depends_on = ["core/stingray_renderer/output_nodes/terrain_base"] + +imports = { + uv = { + type = "float2" + domain = "vertex" + output_channel = "terrain_uv" + } +} + +output = { + type = { typeof: "uv" } +} + +code = """ + RESULT(uv); +""" diff --git a/vmf_source/core/shader_nodes/texture_coordinate0.shader_node b/vmf_source/core/shader_nodes/texture_coordinate0.shader_node new file mode 100644 index 0000000..38d254c --- /dev/null +++ b/vmf_source/core/shader_nodes/texture_coordinate0.shader_node @@ -0,0 +1,18 @@ +group = "Vertex Inputs/Texcoord" +display_name = "Texcoord0" + +imports = { + texcoord = { + type = "float2" + semantic = "TEXCOORD0" + domain = "vertex" + } +} + +output = { + type = { typeof: "texcoord" } +} + +code = """ + RESULT(texcoord); +""" diff --git a/vmf_source/core/shader_nodes/texture_coordinate1.shader_node b/vmf_source/core/shader_nodes/texture_coordinate1.shader_node new file mode 100644 index 0000000..e39f86a --- /dev/null +++ b/vmf_source/core/shader_nodes/texture_coordinate1.shader_node @@ -0,0 +1,18 @@ +group = "Vertex Inputs/Texcoord" +display_name = "Texcoord1" + +imports = { + texcoord = { + type = "float2" + semantic = "TEXCOORD1" + domain = "vertex" + } +} + +output = { + type = { typeof: "texcoord" } +} + +code = """ + RESULT(texcoord); +""" diff --git a/vmf_source/core/shader_nodes/texture_coordinate2.shader_node b/vmf_source/core/shader_nodes/texture_coordinate2.shader_node new file mode 100644 index 0000000..0f6f7ba --- /dev/null +++ b/vmf_source/core/shader_nodes/texture_coordinate2.shader_node @@ -0,0 +1,18 @@ +group = "Vertex Inputs/Texcoord" +display_name = "Texcoord2" + +imports = { + texcoord = { + type = "float2" + semantic = "TEXCOORD2" + domain = "vertex" + } +} + +output = { + type = { typeof: "texcoord" } +} + +code = """ + RESULT(texcoord); +""" diff --git a/vmf_source/core/shader_nodes/texture_coordinate3.shader_node b/vmf_source/core/shader_nodes/texture_coordinate3.shader_node new file mode 100644 index 0000000..f2c0ad2 --- /dev/null +++ b/vmf_source/core/shader_nodes/texture_coordinate3.shader_node @@ -0,0 +1,18 @@ +group = "Vertex Inputs/Texcoord" +display_name = "Texcoord3" + +imports = { + texcoord = { + type = "float2" + semantic = "TEXCOORD3" + domain = "vertex" + } +} + +output = { + type = { typeof: "texcoord" } +} + +code = """ + RESULT(texcoord); +""" diff --git a/vmf_source/core/shader_nodes/texture_coordinate4.shader_node b/vmf_source/core/shader_nodes/texture_coordinate4.shader_node new file mode 100644 index 0000000..631b177 --- /dev/null +++ b/vmf_source/core/shader_nodes/texture_coordinate4.shader_node @@ -0,0 +1,18 @@ +group = "Vertex Inputs/Texcoord" +display_name = "Texcoord4" + +imports = { + texcoord = { + type = "float2" + semantic = "TEXCOORD4" + domain = "vertex" + } +} + +output = { + type = { typeof: "texcoord" } +} + +code = """ + RESULT(texcoord); +""" diff --git a/vmf_source/core/shader_nodes/translation.shader_node b/vmf_source/core/shader_nodes/translation.shader_node new file mode 100644 index 0000000..6a15d25 --- /dev/null +++ b/vmf_source/core/shader_nodes/translation.shader_node @@ -0,0 +1,21 @@ +group = "Transform" +display_name = "Translation" +depends_on = ["core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/light_base", "core/stingray_renderer/output_nodes/unlit_base", "core/stingray_renderer/output_nodes/terrain_base"] + +defines = ["NEEDS_WORLD_POSE"] + +imports = { + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = "float3" +} + +code = """ + RESULT(world._m30_m31_m32); +""" diff --git a/vmf_source/core/shader_nodes/vegetation_bending.shader_node b/vmf_source/core/shader_nodes/vegetation_bending.shader_node new file mode 100644 index 0000000..2cc1aaf --- /dev/null +++ b/vmf_source/core/shader_nodes/vegetation_bending.shader_node @@ -0,0 +1,129 @@ +group = "Animation" +display_name = "Vegetation Animation" +depends_on = ["core/stingray_renderer/output_nodes/standard_base"] + +inputs = { + "3e1d53e1-301e-4d75-90f9-485c5f5b001c" = { name = "vertex_position" display_name = "Vertex Position" type = "vector4"} + "40bc989e-34f6-4422-9742-e45597516edd" = { name = "vertex_color" display_name = "Vertex Color" type = "vector4" } + "f7bc6467-5f47-49b0-89b4-55711dcd7bb2" = { name = "normal" display_name = "World Normal" type = "vector3" } + "12bc956e-5e4a-46b0-8264-e785975cbe12" = { name = "time" display_name = "Time" type = "scalar" } + "AA743CA6-7F0A-4836-BEEB-DCAECC466E08" = { name = "vegetation_speed" display_name = "Speed" is_required = false type = {scalar: ["HAS_SPEED"]} } + "F4547919-4A0A-44B7-B78F-1624EB8D1A81" = { name = "branch_settings" display_name = "Bend Amplitude/Frequency" is_required = false type = {vector2: ["HAS_BRANCH"]} } + "B73F4EB0-088F-424D-95F8-BF18FE90C24A" = { name = "detail_settings" display_name = "Detail Amplitude/Frequency" is_required = false type = {vector2: ["HAS_DETAIL"]} } + "237C535E-B07D-4F06-B13F-4485CCF6B8BD" = { name = "wind_vector" display_name = "Wind" is_required = false type = {vector3: ["HAS_WIND"]} } + "E4EE528C-37B0-4DD4-8D60-CA8D8F4162E0" = { name = "bend_scale" display_name = "Bend Scale" is_required = false type = {scalar: ["HAS_BENDSCALE"]} } +} + +defines = ["NEEDS_WORLD_POSE"] + +imports = { + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = "float3" +} + +code_blocks = { + vegetation_bending = { + code=""" + // Vegetation vertex animation (based on GPUGems 3 chapter by Tiago Sousa / Crytek) + // sine waves approximation by smoothing a number of triangle waves using bicubic interpolation. + inline float4 smooth_curve( float4 x ) { + return x * x * (3.0 - 2.0 * x); + } + + inline float4 triangle_wave( float4 x ) { + return abs(frac(x + 0.5) * 2.0 - 1.0); + } + + inline float4 smooth_triangle_wave( float4 x ) { + return smooth_curve(triangle_wave(x)); + } + + static const float4 vegetation_freq = float4(1.975, 0.793, 0.375, 0.193); + inline float3 vegetation_detail_bending(float3 object_pos, float3 vnormal, float4 vdata, float3 vertex_pos, float speed, float2 branch_settings, float2 detail_settings, float time) { + float detail_amp = detail_settings.x; + float detail_freq = detail_settings.y; + + float branch_amp = branch_settings.x; + float branch_freq = branch_settings.y; + + float edge_attenuation = vdata.x * vdata.z; + float branch_phase = vdata.y * 2.0 - 1.0; + float branch_attenuation = vdata.z; + + float detail_phase = dot(vertex_pos.xyz, float3(branch_phase, branch_phase, branch_phase)); + + float2 waves_in = time + float2(detail_phase, branch_phase); + + float4 waves = (frac(waves_in.xyxy * vegetation_freq * float2(detail_freq, branch_freq).xyxy * speed) * 2.0 - 1.0); + waves = smooth_triangle_wave(waves); + + float2 waves_sum = (waves.xz + waves.yw); + return waves_sum.xxy * float3(edge_attenuation * detail_amp * vnormal.xy, branch_attenuation * branch_amp); + } + + inline float3 vegetation_main_bending(float3 vertex_pos, float3 wind, float bend_scale, float3 object_pos) { + // Reset the world vertex to object space + vertex_pos -= object_pos; + float bend_factor = vertex_pos.z * bend_scale; + // Smooth bending factor and increase its nearby height limit. + bend_factor += 1.0; + bend_factor *= bend_factor; + bend_factor = bend_factor * bend_factor - bend_factor; + // Displace position + float3 new_pos = vertex_pos; + new_pos.xy += wind.xy * bend_factor; + float height = length(vertex_pos); + // reduce stretch + new_pos = normalize(new_pos) * height; + // output as offset to existing vertex position + return (new_pos - vertex_pos); + } + + inline float3 vegetation_bending(float3 object_pos, float3 vnormal, float4 vdata, float4 vertex_pos, float speed, float2 branch_settings, float2 detail_settings, float time, float3 wind, float bend_scale) { + float3 new_pos = vegetation_main_bending(vertex_pos.xyz, wind, bend_scale, object_pos); + new_pos += vegetation_detail_bending(object_pos, vnormal, vdata, vertex_pos.xyz, speed, branch_settings, detail_settings, time); + return new_pos; + } + + """ + } + + default = { + include: ["vegetation_bending"] + code = """ + #if defined(RENDERER_GL) + float3 object_pos = float3(world[0].w, world[1].w, world[2].w); + #else + float3 object_pos = world._m30_m31_m32; + #endif + + #if !defined(HAS_SPEED) + float vegetation_speed = 0.5; + #endif + + #if !defined(HAS_BRANCH) + float2 branch_settings = float2(0.3, 0.1); + #endif + + #if !defined(HAS_DETAIL) + float2 detail_settings = float2(0.05, 0.5); + #endif + + #if !defined(HAS_WIND) + float3 wind_vector = float3(1.0, 1.0, 0.0); + #endif + + #if !defined(HAS_BENDSCALE) + float bend_scale = 0.005; + #endif + RESULT(vegetation_bending(object_pos, normal, vertex_color, vertex_position, vegetation_speed, branch_settings, detail_settings, time, wind_vector, bend_scale)); + """ + } +} diff --git a/vmf_source/core/shader_nodes/vertex_binormal.shader_node b/vmf_source/core/shader_nodes/vertex_binormal.shader_node new file mode 100644 index 0000000..44a48b0 --- /dev/null +++ b/vmf_source/core/shader_nodes/vertex_binormal.shader_node @@ -0,0 +1,18 @@ +group = "Vertex Inputs" +display_name = "Binormal" + +imports = { + binormal = { + type = "float3" + semantic = "BINORMAL" + domain = "vertex" + } +} + +output = { + type = { typeof: "binormal" } +} + +code = """ + RESULT(binormal); +""" diff --git a/vmf_source/core/shader_nodes/vertex_color0.shader_node b/vmf_source/core/shader_nodes/vertex_color0.shader_node new file mode 100644 index 0000000..cee841f --- /dev/null +++ b/vmf_source/core/shader_nodes/vertex_color0.shader_node @@ -0,0 +1,34 @@ +group = "Vertex Inputs" +display_name = "Color0" + +imports = { + color0 = { + type = "float4" + semantic = "COLOR0" + domain = "vertex" + } +} + +output = { + type = { typeof: "color0" } +} + +options = { + "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" = "VC_COMPRESSED" + "43710e4f-f52a-4038-8ec8-d6cb0546103b" = "FAST_GAMMA_DECODE" +} + +ui = [ + { type = "checkbox" display_name = "Fast Gamma Decode" option = "43710e4f-f52a-4038-8ec8-d6cb0546103b" } + { type = "checkbox" display_name = "Compressed" option = "acb6ef9d-5ba0-42e4-85f3-2924b4b4be25" } +] + +code = """ + #if defined(FAST_GAMMA_DECODE) + color0 = fast_gamma_to_linear_rgb(color0); + #endif + #if defined(VC_COMPRESSED) + color0 = decode_vertex_color(color0); + #endif + RESULT(color0); +""" diff --git a/vmf_source/core/shader_nodes/vertex_instanceid.shader_node b/vmf_source/core/shader_nodes/vertex_instanceid.shader_node new file mode 100644 index 0000000..3bda719 --- /dev/null +++ b/vmf_source/core/shader_nodes/vertex_instanceid.shader_node @@ -0,0 +1,21 @@ + + +group = "Vertex Inputs" +display_name = "Instance Id" + +imports = { + instance_id = { + type = "uint" + semantic = "SV_InstanceID" + domain = "vertex" + } +} + +output = { + type = "float" +} + +code = """ + RESULT((float)instance_id); +""" + diff --git a/vmf_source/core/shader_nodes/vertex_position.shader_node b/vmf_source/core/shader_nodes/vertex_position.shader_node new file mode 100644 index 0000000..c4fbb71 --- /dev/null +++ b/vmf_source/core/shader_nodes/vertex_position.shader_node @@ -0,0 +1,23 @@ +group = "Vertex Inputs" +display_name = "Position" +depends_on = [ + "core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/unlit_base", + "core/stingray_renderer/output_nodes/billboard_base", "core/stingray_renderer/output_nodes/billboard_unlit_base", + "core/stingray_renderer/output_nodes/terrain_base" +] + +imports = { + pos = { + type = "float4" + domain = "vertex" + output_channel = "vertex_position" + } +} + +output = { + type = { typeof: "pos" } +} + +code = """ + RESULT(pos); +""" diff --git a/vmf_source/core/shader_nodes/vertex_tangent.shader_node b/vmf_source/core/shader_nodes/vertex_tangent.shader_node new file mode 100644 index 0000000..a464614 --- /dev/null +++ b/vmf_source/core/shader_nodes/vertex_tangent.shader_node @@ -0,0 +1,18 @@ +group = "Vertex Inputs" +display_name = "Tangent" + +imports = { + tangent = { + type = "float3" + semantic = "TANGENT" + domain = "vertex" + } +} + +output = { + type = { typeof: "tangent" } +} + +code = """ + RESULT(tangent); +""" diff --git a/vmf_source/core/shader_nodes/wave_displacement.shader_node b/vmf_source/core/shader_nodes/wave_displacement.shader_node new file mode 100644 index 0000000..a51b0ac --- /dev/null +++ b/vmf_source/core/shader_nodes/wave_displacement.shader_node @@ -0,0 +1,65 @@ +group = "Fatshark/Animation" +display_name = "Wave Displacement" + +inputs = { + "f05346d4-d78c-47df-bcc6-0a165227ba0c" = { name = "position" display_name = "Position" type = "vector3" is_required = true } +} + +imports = { +} + +exports = { + wave_params = { + display_name = "Wave Dir/Length/Amp/Steep" + type = "float4" + min = [0 0.01 0.01 0] + max = [1 200 3 1] + step = [0.001 0.01 0.01 0.01] + value = [0 100 0.3 0.1] + } +} + +output = { + type = "float3" +} + +code_blocks = { + wave_displacement = { + language = "hlsl" + code=""" + // Gerstner waves + // http://http.developer.nvidia.com/GPUGems/gpugems_ch01.html + // http://graphics.ucsd.edu/courses/rendering/2005/jdewall/tessendorf.pdf + float3 gerstner_wave_displacement( float2 wave_direction, float wave_length, float wave_amplitude, float wave_steepness, float3 wp ) + { + const float GRAVITY = 9.82; + const float TWO_PI = 6.28318530718; + + float w = TWO_PI / wave_length; // angular frequency + float s = sqrt( GRAVITY * w ); // phase animation multiplier + float q = wave_steepness/(w*wave_amplitude); // steepness 0 -> 1 + + float2 xy = q * wave_amplitude * wave_direction * cos( w * dot( wave_direction, wp.xy ) + s * time ); + float z = wave_amplitude * sin( w * dot( wave_direction, wp.xy ) + s * time ); + + return float3(xy, z); + } + + float2 wave_direction_to_vector(float dir) + { + float x = dir < 0.5 ? dir*4-1 : (1-dir)*4-1; + float y = dir < 0.5 ? abs(x)-1 : 1-abs(x); + + return float2(x,y); + } + """ + } + + default = { + include: ["wave_displacement"] + language = "hlsl" + code = """ + RESULT(gerstner_wave_displacement(wave_direction_to_vector(wave_params.x), wave_params.y, wave_params.z, wave_params.w, position)); + """ + } +} \ No newline at end of file diff --git a/vmf_source/core/shader_nodes/wind_amount.shader_node b/vmf_source/core/shader_nodes/wind_amount.shader_node new file mode 100644 index 0000000..6ede67d --- /dev/null +++ b/vmf_source/core/shader_nodes/wind_amount.shader_node @@ -0,0 +1,18 @@ +group = "Input" +display_name = "Wind Amount" + +imports = { + wind_amount = { + type = "float3" + domain = "global" + source = "shading_environment" + } +} + +output = { + type = { typeof: "wind_amount" } +} + +code = """ + RESULT(wind_amount); +""" diff --git a/vmf_source/core/shader_nodes/world_space_normal.shader_node b/vmf_source/core/shader_nodes/world_space_normal.shader_node new file mode 100644 index 0000000..88a4534 --- /dev/null +++ b/vmf_source/core/shader_nodes/world_space_normal.shader_node @@ -0,0 +1,28 @@ +group = "Vertex Inputs" +display_name = "World Normal" +depends_on = [ + "core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/unlit_base", + "core/stingray_renderer/output_nodes/particle_base", "core/stingray_renderer/output_nodes/particle_gbuffer_base", + "core/stingray_renderer/output_nodes/particle_distortion_base", "core/stingray_renderer/output_nodes/billboard_base", + "core/stingray_renderer/output_nodes/anisotropic_base"] + +// This node will only work with a base node that exposes the per-vertex world space normal as +// the world_space_normal output channel. + +imports = { + normal = { + type = "float3" + domain = "vertex" + output_channel = "world_space_normal" + } +} + +defines = ["NEEDS_WORLD_SPACE_NORMAL"] + +output = { + type = { typeof: "normal" } +} + +code = """ + RESULT(normal); +""" diff --git a/vmf_source/core/shader_nodes/world_to_object.shader_node b/vmf_source/core/shader_nodes/world_to_object.shader_node new file mode 100644 index 0000000..94d6f17 --- /dev/null +++ b/vmf_source/core/shader_nodes/world_to_object.shader_node @@ -0,0 +1,44 @@ +group = "Transform" +display_name = "World To Object" +depends_on = ["core/stingray_renderer/output_nodes/standard_base", "core/stingray_renderer/output_nodes/light_base"] + +inputs = { + "39c3e91e-9dab-4fc1-916c-02b46390fb19" = { name = "v" display_name = "Vector" type = "float3" } +} + +options = { + "157d0a11-6e9d-4360-bbf2-0348cb7432d1" = "ROTATE_ONLY" +} + +ui = [ + { + type = "drop_down" + display_name = "Mode" + options = { + "Full Transform" = "00000000-0000-0000-0000-000000000000" + "Rotation only" = "157d0a11-6e9d-4360-bbf2-0348cb7432d1" + } + } +] + +defines = ["NEEDS_INVERSE_WORLD_POSE"] + +imports = { + inv_world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = { typeof: "v" } +} + +code = """ + #if defined(ROTATE_ONLY) + RESULT(mul(v, to_mat3(inv_world))); + #else + RESULT(mul(float4(v, 1), inv_world).xyz); + #endif +""" diff --git a/vmf_source/core/shader_nodes/world_to_tangent.shader_node b/vmf_source/core/shader_nodes/world_to_tangent.shader_node new file mode 100644 index 0000000..10055a9 --- /dev/null +++ b/vmf_source/core/shader_nodes/world_to_tangent.shader_node @@ -0,0 +1,42 @@ +group = "Transform" +display_name = "World To Tangent Space" +depends_on = [ + "core/stingray_renderer/output_nodes/standard_base" + "core/stingray_renderer/output_nodes/terrain_base" +] + +inputs = { + "f72597c4-7487-419a-affb-df690e6582e1" = { name = "v" display_name = "Vector" type = "float3" } +} + +defines = ["NEEDS_TANGENT_SPACE"] + +imports = { + tsm0 = { + type = "float3" + domain = "vertex" + output_channel = "tsm0" + } + tsm1 = { + type = "float3" + domain = "vertex" + output_channel = "tsm1" + } + tsm2 = { + type = "float3" + domain = "vertex" + output_channel = "tsm2" + } +} + +output = { + type = { typeof: "v" } +} + +code = """ + float3 res = float3( + dot(v, float3(tsm0.x, tsm1.x, tsm2.x)), + dot(v, float3(tsm0.y, tsm1.y, tsm2.y)), + dot(v, float3(tsm0.z, tsm1.z, tsm2.z))); + RESULT(normalize(res)); +""" diff --git a/vmf_source/core/shader_nodes/world_to_view.shader_node b/vmf_source/core/shader_nodes/world_to_view.shader_node new file mode 100644 index 0000000..b854f6e --- /dev/null +++ b/vmf_source/core/shader_nodes/world_to_view.shader_node @@ -0,0 +1,44 @@ +group = "Transform" +display_name = "World To View" +depends_on = ["core/stingray_renderer/output_nodes/standard_base"] + +inputs = { + "f72597c4-7487-419a-affb-df690e6582e1" = { name = "v" display_name = "Vector" type = "float3" } +} + +options = { + "43344bde-3298-4b5f-8993-0be2a71a83b3" = "ROTATE_ONLY" +} + +ui = [ + { + type = "drop_down" + display_name = "Mode" + options = { + "Full Transform" = "00000000-0000-0000-0000-000000000000" + "Rotation only" = "43344bde-3298-4b5f-8993-0be2a71a83b3" + } + } +] + +defines = ["NEEDS_WORLD_POSE"] + +imports = { + world = { + type = "float4x4" + domain = "global" + source = "engine" + } +} + +output = { + type = { typeof: "v" } +} + +code = """ + #if defined(ROTATE_ONLY) + RESULT(mul(v, to_mat3(camera_view))); + #else + RESULT(mul(float4(v, 1), camera_view).xyz); + #endif +""" diff --git a/vmf_source/core/stingray_renderer/environments/color_grading_identity.dds b/vmf_source/core/stingray_renderer/environments/color_grading_identity.dds new file mode 100644 index 0000000..a90106a Binary files /dev/null and b/vmf_source/core/stingray_renderer/environments/color_grading_identity.dds differ diff --git a/vmf_source/core/stingray_renderer/environments/color_grading_identity.texture b/vmf_source/core/stingray_renderer/environments/color_grading_identity.texture new file mode 100644 index 0000000..90cedd8 --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/color_grading_identity.texture @@ -0,0 +1,18 @@ +common = { + input = { + filename = "core/stingray_renderer/environments/color_grading_identity" + } + output = { + apply_processing = false + category = "" + srgb = true + cut_alpha_threshold = 0.5 + enable_cut_alpha_threshold = false + format = "R8G8B8A8" + mipmap_filter = "box" + mipmap_filter_wrap_mode = "mirror" + mipmap_keep_original = false + mipmap_num_largest_steps_to_discard = 0 + mipmap_num_smallest_steps_to_discard = 0 + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/environments/default_shading_environment.object_creator_item b/vmf_source/core/stingray_renderer/environments/default_shading_environment.object_creator_item new file mode 100644 index 0000000..d760928 --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/default_shading_environment.object_creator_item @@ -0,0 +1,11 @@ +type = "entity" +entity_resource = "core/stingray_renderer/environments/midday/midday_shading_environment" +placeable = false +menu_item = { + path = "Create/Shading Environment" + section = "LIGHTING" + order = 200 +} +enabled = """ + return Application.render_config_resource() == "core/stingray_renderer/renderer" +""" \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/environments/empty_black.dds b/vmf_source/core/stingray_renderer/environments/empty_black.dds new file mode 100644 index 0000000..3b95d75 Binary files /dev/null and b/vmf_source/core/stingray_renderer/environments/empty_black.dds differ diff --git a/vmf_source/core/stingray_renderer/environments/empty_black.texture b/vmf_source/core/stingray_renderer/environments/empty_black.texture new file mode 100644 index 0000000..28d5c61 --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/empty_black.texture @@ -0,0 +1,18 @@ +common = { + input = { + filename = "core/stingray_renderer/environments/empty_black" + } + output = { + apply_processing = true + category = "texture_categories/environment_df" + correct_gamma = true + cut_alpha_threshold = 0.5 + enable_cut_alpha_threshold = false + format = "DXT5" + mipmap_filter = "kaiser" + mipmap_filter_wrap_mode = "mirror" + mipmap_keep_original = false + mipmap_num_largest_steps_to_discard = 0 + mipmap_num_smallest_steps_to_discard = 0 + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/environments/env_materials.material b/vmf_source/core/stingray_renderer/environments/env_materials.material new file mode 100644 index 0000000..f3247a2 --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/env_materials.material @@ -0,0 +1,35 @@ +// TODO: We are currently only using these materials as intermediate storage for shader constants, might want something cleaner that this.. + +global_lighting = { + shader = "global_lighting" +} +global_shadow_mask_fill = { + shader = "sun_shadow_mask:FILL" +} +global_shadow_mask_fill_shadow = { + shader = "sun_shadow_mask:FILL_SHADOW" +} +global_shadow_mask_slice0 = { + shader = "sun_shadow_mask" +} +global_shadow_mask_slice1= { + shader = "sun_shadow_mask" +} +global_shadow_mask_slice2 = { + shader = "sun_shadow_mask" +} +global_shadow_mask_slice3 = { + shader = "sun_shadow_mask" +} +shadow_cutter_slice0 = { + shader = "sun_shadow_cutter" +} +shadow_cutter_slice1 = { + shader = "sun_shadow_cutter" +} +shadow_cutter_slice2 = { + shader = "sun_shadow_cutter" +} +shadow_cutter_slice3 = { + shader = "sun_shadow_cutter" +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/environments/lensdirt.dds b/vmf_source/core/stingray_renderer/environments/lensdirt.dds new file mode 100644 index 0000000..2f68a86 Binary files /dev/null and b/vmf_source/core/stingray_renderer/environments/lensdirt.dds differ diff --git a/vmf_source/core/stingray_renderer/environments/lensdirt.texture b/vmf_source/core/stingray_renderer/environments/lensdirt.texture new file mode 100644 index 0000000..924bb5e --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/lensdirt.texture @@ -0,0 +1,25 @@ + +input = [ + { + texture = "core/stingray_renderer/environments/lensdirt.dds" + } +] +output = { + win32 = { + alpha_coverage_mips = false + alpha_coverage_reference = 0 + category = "" + filter_wrap_mode = "mirror" + format = "A8R8G8B8" + kaiser_settings = { + alpha = 4 + stretch = 1 + width = 3 + } + manual_compression = false + mip_level_clamp = -1 + mipmap_filter = "kaiser" + normal_map = false + use_existing_mips = false + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/environments/midday/diffuse_cube.dds b/vmf_source/core/stingray_renderer/environments/midday/diffuse_cube.dds new file mode 100644 index 0000000..ccef195 Binary files /dev/null and b/vmf_source/core/stingray_renderer/environments/midday/diffuse_cube.dds differ diff --git a/vmf_source/core/stingray_renderer/environments/midday/diffuse_cube.texture b/vmf_source/core/stingray_renderer/environments/midday/diffuse_cube.texture new file mode 100644 index 0000000..6fbb6a9 --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/midday/diffuse_cube.texture @@ -0,0 +1,42 @@ +android = { + output = { + srgb = false + } +} +common = { + input = { + filename = "core/stingray_renderer/environments/midday/diffuse_cube" + } + output = { + apply_processing = true + category = "" + cut_alpha_threshold = 0.5 + enable_cut_alpha_threshold = false + format = "R8G8B8A8" + mipmap_filter = "box" + mipmap_filter_wrap_mode = "mirror" + mipmap_keep_original = false + mipmap_num_largest_steps_to_discard = 0 + mipmap_num_smallest_steps_to_discard = 0 + } +} +ios = { + output = { + srgb = false + } +} +macosx = { + output = { + srgb = false + } +} +ps4 = { + output = { + srgb = false + } +} +win32 = { + output = { + srgb = false + } +} diff --git a/vmf_source/core/stingray_renderer/environments/midday/midday.shading_environment b/vmf_source/core/stingray_renderer/environments/midday/midday.shading_environment new file mode 100644 index 0000000..146083c --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/midday/midday.shading_environment @@ -0,0 +1,137 @@ + +settings = { + default = { + editor_variables = { + shadow_slice_depth_ranges = [ + 0 + 80 + 0.4 + ] + sun_shadow_slice_depth_ranges = [ + 0 + 80 + 0.381 + ] + } + variable_weights = { + } + variables = { + ao_enabled = 1 + ao_intensity = 0.6 + ao_quality = 2 + ao_radius = 0.2 + bloom_enabled = 1 + bloom_lens_dirt_amount = 0 + bloom_threshold_offset = [ + 2.8 + 1 + ] + bloom_threshold_offset_falloff = [ + 2 + 1 + 1 + ] + bloom_tint = [ + 1 + 1 + 1 + ] + color_grading_enabled = 0 + color_grading_map = "core/stingray_renderer/environments/color_grading_identity" + dof_enabled = 0 + dof_focal_distance = 0.25 + dof_focal_far_scale = 1 + dof_focal_near_scale = 1 + dof_focal_region = 0.1 + dof_focal_region_end = 0.2 + dof_focal_region_start = 0.2 + emissive_particle_intensity = 1 + exposure = 1.25 + fog_color = [ + 0.6 + 0.6 + 0.6 + ] + fog_depth_range = [ + 100 + 400 + ] + fog_enabled = 1 + fog_sun_blend = [ + 0.997 + 8 + 1 + ] + global_diffuse_map = "core/stingray_renderer/environments/midday/diffuse_cube" + global_lens_dirt_map = "" + global_specular_map = "core/stingray_renderer/environments/midday/specular_cube" + ibl_intensity = 1 + lens_quality_enabled = 0 + lens_quality_properties = [ + 0 + 0 + 0 + ] + motion_blur_amount = 2 + motion_blur_enabled = 0 + shadow_slice_depth_ranges = [ + 0 + 8.2590977538167 + 8.2590977538167 + 17.6370562748477 + 17.6370562748477 + 32.9654474234073 + 32.9654474234073 + 79.94 + ] + skydome_intensity = 1 + skydome_map = "core/stingray_renderer/environments/midday/skydome" + ssr_angle_facing_camera_threshold = [ + 1 + 1 + ] + ssr_dangerous_reflection_angle = [ + 0.01 + 0.01 + ] + ssr_enabled = 0 + ssr_screen_edge_threshold = 0.05 + ssr_surface_thickness_threshold = [ + 0.001 + 10.0 + ] + ssr_up_normal_threshold = [ + 0 + 1 + ] + sun_color = [ + 4.59479341998814 + 4.59203760386544 + 4.36054904955738 + ] + sun_direction = [ + -0.393756360085283 + -0.4391705917301 + -0.807517876119051 + ] + sun_shadow_slice_depth_ranges = [ + 0 + 7.8873025160209 + 7.8873025160209 + 16.9288963902179 + 16.9288963902179 + 32.1093532584819 + 32.1093532584819 + 79.9381 + ] + sun_shadows_enabled = 1 + vignette_enabled = 1 + vignette_properties = [ + 1 + 0.5 + 1 + ] + } + } +} +template = "core/stingray_renderer/environments/outdoor.shading_environment_template" \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/environments/midday/midday_shading_environment.entity b/vmf_source/core/stingray_renderer/environments/midday/midday_shading_environment.entity new file mode 100644 index 0000000..e99c922 --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/midday/midday_shading_environment.entity @@ -0,0 +1,164 @@ +components = { + "06c78e06-c921-4208-8069-d377b6415d30" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/fog" + "$resource_type" = "component" + } + data = { + fog_enabled = true + fog_color = { + rgb = [0.6, 0.6, 0.6] + alpha = 1 + intensity = 1 + } + fog_depth_range = [ + 100 + 400 + ] + fog_sun_blend = 0.997 + fog_sun_exponent = 8 + fog_sun_strength = 1 + } + } + "a9f83eb8-b54a-4c45-bf62-4fe7e02a8ce4" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/ssao" + "$resource_type" = "component" + } + data = { + ao_enabled = true + ao_intensity = 0.6 + ao_quality = 2 + ao_radius = 0.2 + } + } + "778c5da5-eaa2-4a5f-951f-8c8bf13a2a6e" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/dof" + "$resource_type" = "component" + } + data = { + dof_enabled = false + dof_focal_distance = 0.25 + dof_focal_far_scale = 1 + dof_focal_near_scale = 1 + dof_focal_region = 0.1 + dof_focal_region_end = 0.2 + dof_focal_region_start = 0.2 + } + } + "3eb1ba63-3a53-4846-970b-57e3d4d75d2b" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/global_lighting" + "$resource_type" = "component" + } + data = { + skydome_intensity = 1 + skydome_map = { + "$resource_name" = "core/stingray_renderer/environments/midday/skydome" + "$resource_type" = "texture" + } + global_diffuse_map = { + "$resource_name" = "core/stingray_renderer/environments/midday/diffuse_cube" + "$resource_type" = "texture" + } + global_specular_map = { + "$resource_name" = "core/stingray_renderer/environments/midday/specular_cube" + "$resource_type" = "texture" + } + emissive_particle_intensity = 1 + } + } + "fd909d00-3e4d-4339-b1a9-784c7d6a60ad" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/bloom" + "$resource_type" = "component" + } + data = { + bloom_enabled = true + bloom_lens_dirt_amount = 0 + bloom_threshold = 2 + bloom_offset = 1 + bloom_falloff = 1 + bloom_tint = { + rgb = [1, 1, 1] + alpha = 1 + intensity = 1 + } + global_lens_dirt_map = null + } + } + "cd8f2898-5821-4e3f-8348-a7d2f3c869d2" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/motion_blur" + "$resource_type" = "component" + } + data = { + motion_blur_enabled = true + motion_blur_amount = 2 + } + } + "04d5202f-c4cb-45b5-8e0f-4c5605fb7533" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/color_grading" + "$resource_type" = "component" + } + data = { + color_grading_enabled = false + color_grading_map = { + "$resource_name" = "core/stingray_renderer/environments/color_grading_identity" + "$resource_type" = "texture" + } + } + } + "6d36e8c5-dac7-4e28-b873-c4555b95e2bf" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/lens_quality" + "$resource_type" = "component" + } + data = { + lens_quality_enabled = false + lens_quality_distortion = 0 + lens_quality_fringe_color = 0 + lens_quality_fringe_intensity = 0 + } + } + "dd1dccf2-6f2f-4b8c-9199-290c069ab62b" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/vignette" + "$resource_type" = "component" + } + data = { + vignette_enabled = true + vignette_radius = 1 + vignette_falloff = 0.5 + vignette_opacity = 1 + } + } + "8386face-d486-49d6-9b8b-6d8cba24d4f7" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/ssr" + "$resource_type" = "component" + } + data = { + ssr_enabled = false + ssr_screen_edge_threshold = 0.05 + ssr_surface_thickness_threshold = [0.001, 10] + } + } + "68333c83-9659-4d2e-b664-35790e366f08" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/exposure" + "$resource_type" = "component" + } + data = { + exposure = 1.25 + } + } + "597d400f-c916-454b-9912-b9bb35e6e689" = { + component = { + "$resource_name" = "core/stingray_renderer/shading_environment_components/cascaded_shadow_mapping" + "$resource_type" = "component" + } + } +} diff --git a/vmf_source/core/stingray_renderer/environments/midday/skydome.dds b/vmf_source/core/stingray_renderer/environments/midday/skydome.dds new file mode 100644 index 0000000..4eacbd6 Binary files /dev/null and b/vmf_source/core/stingray_renderer/environments/midday/skydome.dds differ diff --git a/vmf_source/core/stingray_renderer/environments/midday/skydome.texture b/vmf_source/core/stingray_renderer/environments/midday/skydome.texture new file mode 100644 index 0000000..e458482 --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/midday/skydome.texture @@ -0,0 +1,47 @@ +android = { + output = { + mipmap_num_largest_steps_to_discard = 2 + } +} +common = { + input = { + filename = "core/stingray_renderer/environments/midday/skydome" + } + output = { + apply_processing = true + category = "" + srgb = false + cut_alpha_threshold = 0.5 + enable_cut_alpha_threshold = false + format = "R16G16B16A16F" + mipmap_filter = "box" + mipmap_filter_wrap_mode = "mirror" + mipmap_keep_original = false + mipmap_num_smallest_steps_to_discard = 0 + } +} +ios = { + output = { + mipmap_num_largest_steps_to_discard = 2 + } +} +macosx = { + output = { + mipmap_num_largest_steps_to_discard = 0 + } +} +ps4 = { + output = { + mipmap_num_largest_steps_to_discard = 0 + } +} +win32 = { + output = { + mipmap_num_largest_steps_to_discard = 0 + } +} +xb1 = { + output = { + mipmap_num_largest_steps_to_discard = 0 + } +} diff --git a/vmf_source/core/stingray_renderer/environments/midday/specular_cube.dds b/vmf_source/core/stingray_renderer/environments/midday/specular_cube.dds new file mode 100644 index 0000000..08346d9 Binary files /dev/null and b/vmf_source/core/stingray_renderer/environments/midday/specular_cube.dds differ diff --git a/vmf_source/core/stingray_renderer/environments/midday/specular_cube.texture b/vmf_source/core/stingray_renderer/environments/midday/specular_cube.texture new file mode 100644 index 0000000..5c8863e --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/midday/specular_cube.texture @@ -0,0 +1,46 @@ +android = { + output = { + apply_processing = false + srgb = false + } +} +common = { + input = { + filename = "core/stingray_renderer/environments/midday/specular_cube" + } + output = { + category = "" + cut_alpha_threshold = 0.5 + enable_cut_alpha_threshold = false + format = "R8G8B8A8" + mipmap_filter = "box" + mipmap_filter_wrap_mode = "mirror" + mipmap_keep_original = false + mipmap_num_largest_steps_to_discard = 0 + mipmap_num_smallest_steps_to_discard = 0 + } +} +ios = { + output = { + apply_processing = false + srgb = false + } +} +macosx = { + output = { + apply_processing = false + srgb = false + } +} +ps4 = { + output = { + apply_processing = false + srgb = false + } +} +win32 = { + output = { + apply_processing = false + srgb = false + } +} diff --git a/vmf_source/core/stingray_renderer/environments/outdoor.shading_environment_template b/vmf_source/core/stingray_renderer/environments/outdoor.shading_environment_template new file mode 100644 index 0000000..66c7e6d --- /dev/null +++ b/vmf_source/core/stingray_renderer/environments/outdoor.shading_environment_template @@ -0,0 +1,252 @@ +material = "core/stingray_renderer/environments/env_materials" + +variables = { + sun_enabled = { type="scalar" value=1.0 min=0.0 max=1.0 step=1.0 } + sun_direction = { type="vector3" ui_type="hemisphere_direction" value=[0.0 0.0 -1.0] } + sun_color = { type="vector3" ui_type="hdr_color" value=[1.0 1.0 1.0] min=[0 0 0] max=[5 5 5] step=[0.003 0.003 0.003] } + sun_shadows_enabled = { type="scalar" value=1.0 min=0.0 max=1.0 step=1.0 } + + sun_shadow_slice_depth_ranges = { type="vector2_array" ui_name="Cascaded Shadow Mapping" value=[0 8 8 19 19 56 56 80] ui_type="cascaded_shadow_mapping" num_slices=4 start=0 end=80 log_linear_blend=0.4 } + + skydome_map = { type="resource" ui_name="Skydome Texture" value="" } + skydome_intensity = { type="scalar" ui_name="Skydome Intensity" value=1 min=0.001 max=2 step=0.01 } + global_diffuse_map = { type="resource" ui_name="Global Diffuse Map" value="" } + global_specular_map = { type="resource" ui_name="Global Specular Map" value="" } + ambient_tint = { type="vector3" ui_name="Ambient Tint" ui_type="hdr_color" value=[1.0 1.0 1.0] min=[0 0 0] max=[5 5 5] step=[0.003 0.003 0.003] } + + emissive_particle_intensity = { type="scalar" ui_name="Emissive Particle Intensity" value=1 min=0.001 max=16 step=0.01 } + + offscreen_target_projection = { type="scalar" ui_name="Enable offscreen gui projection" value=0.0 min=0.0 max=1.0 step=1.0 } + + fog_enabled = { type="scalar" ui_name="Enable Fog" value=1.0 min=0.0 max=1.0 step=1.0 } + fog_depth_range = { type="vector2" ui_name="Fog Depth Range" value=[10 100] min=[0 0] max=[5000 5000] step=[0.01 0.01 0.01] } + fog_color = { type="vector3" ui_name="Fog Color" ui_type="hdr_color" value=[0.6 0.6 0.6] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] } + fog_sun_blend = { type="vector3" ui_name="Fog Sun Blend / Exponent / Strength" value=[1.0 8.0 1.0] min=[0 1 0] max = [1 16 1] step = [0.001 0.01 0.001] } + + exposure = { type="scalar" ui_name="Exposure/Grey Value" value=1 min=0.001 max=10 step=0.01 } + + // Used to globally expose world to shadow map transforms for sun shadow mapping + sun_world_to_shadow_slice0 = { type="matrix4x4" value=[1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1] } + sun_world_to_shadow_slice1 = { type="matrix4x4" value=[1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1] } + sun_world_to_shadow_slice2 = { type="matrix4x4" value=[1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1] } + sun_world_to_shadow_slice3 = { type="matrix4x4" value=[1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1] } + + + bloom_enabled = { type="scalar" ui_name="Enable Bloom" value=1.0 min=0.0 max=1.0 step=1.0 } + bloom_threshold_offset_falloff = { type="vector3" ui_name="Bloom Threshold / Offset / Falloff" value=[0.8 1.0 1.0] min=[0.0 0.01 0.0] max=[5.0 2 1.0] step=[0.001 0.001 0.001] } + global_lens_dirt_map = { type="resource" ui_name="Global Lens Dirt Map" value="core/stingray_renderer/environments/lensdirt" } + bloom_lens_dirt_amount = { type="scalar" ui_name="Lens Dirt Amount" value=1.0 min=0.0 max=10.0 step=0.001 } + bloom_tint = { type="vector3" ui_name="Bloom Tint" ui_type="hdr_color" value=[1.0 1.0 1.0] min=[0 0 0] max=[5 5 5] step=[0.003 0.003 0.003] } + + motion_blur_enabled = { type="scalar" ui_name="Enable Motion Blur" value=0.0 min=0.0 max=1.0 step=1.0 } + motion_blur_amount = { type="scalar" ui_name="Motion Blur Amount" value=2.0 min=0.0 max=5.0 step=0.001 } + + ao_enabled = { type="scalar" ui_name="Enable AO" value=1.0 min=0.0 max=1.0 step=1.0 } + ao_quality = { type="scalar" ui_name="AO Quality" value=2.0 min=1.0 max=3.0 step=1.0 } + ao_radius = { type="scalar" ui_name="AO Radius" value=0.2 min=0.1 max=1.0 step=0.001 } + ao_intensity = { type="scalar" ui_name="AO Intensity" value=0.6 min=0.0 max=1.0 step=0.01 } + + dof_enabled = { type="scalar" ui_name="Enable Depth Of Field" value=1.0 min=0.0 max=1.0 step=1.0 } + dof_focal_distance = { type="scalar" ui_name="Focus Distance" value=10.0 min=0.0 max=5000 step=0.01 } + dof_focal_region = { type="scalar" ui_name="Focus Region" value=5 min=0.0 max=2500 step=0.01 } + dof_focal_region_start = { type="scalar" ui_name="Focus Region Start" value=5 min=0.01 max=2500 step=0.01 } + dof_focal_region_end = { type="scalar" ui_name="Focus Region End" value=5 min=0.01 max=2500 step=0.01 } + dof_focal_near_scale = { type="scalar" ui_name="Near Scale" value=1.0 min=0.0 max=1.0 step=0.001 } + dof_focal_far_scale = { type="scalar" ui_name="Far Scale" value=1.0 min=0.0 max=1.0 step=0.001 } + + ssr_enabled = { type="scalar" ui_name="Enable Screen Space Reflections" value=0 min=0 max=1 step=1 } + ssr_surface_thickness_threshold = { type="vector2" ui_name="SSR Mask: Surface Thickness Threshold" value=[0.001 5] min=[0.0001 0.1] max=[0.01 20] step=[0.000001 0.001] } + ssr_screen_edge_threshold = { type="scalar" ui_name="SSR Mask: Screen Edge Threshold" value=0.05 min=0.01 max=0.2 step=0.00001 } + ssr_ray_bending_enabled = { type="scalar" ui_name="SSR Bending" value=0 min=0 max=1 step=0.0001 } + + lens_quality_enabled = { type="scalar" ui_name="Enable Lens Quality" value=0.0 min=0.0 max=1.0 step=1.0 } + lens_quality_properties = {type="vector3" ui_name="Lens Quality (Distortion | Fringe Intensity | Fringe Color)" value=[0.0 0.0 0.0] min=[-0.6 0.0 0.0] max=[0.9 1.0 1.0] step=[0.001 0.001 0.001] } + + vignette_enabled = { type="scalar" ui_name="Enable Vignette" value=1.0 min=0.0 max=1.0 step=1.0 } + //vignette_properties = { type="vector3" ui_name="Vignette (Radius | Falloff | Opacity)" value=[1.0 0.5 1.0] min=[0.0 0.0 0.0] max=[1.0 1.0 1.0] step=[0.001 0.001 0.001] } + + color_grading_enabled = { type="scalar" ui_name="Enable Color Grading" value=0.0 min=0.0 max=1.0 step=1.0 } + color_grading_map = { type="resource" ui_name="Color Grading Volume Map" value="core/stingray_renderer/environments/color_grading_identity" } + + // Fatshark + + global_roughness_multiplier = { type="scalar" ui_name="Global Roughness Multiplier" value=1.0 min=0.0 max=2.0 step=0.001 } + + secondary_sun_enabled = { type="scalar" ui_name="Enable Secondary Sun" value=0.0 min=0.0 max=1.0 step=1.0 } + secondary_sun_direction = { type="vector3" ui_name="Secondary Sun Direction" ui_type="hemisphere_direction" value=[0 0 -1] } + secondary_sun_color = { type="vector3" ui_name="Secondary Sun Color" ui_type="hdr_color" value=[1.0 1.0 1.0] min=[0 0 0] max=[5 5 5] step=[0.003 0.003 0.003] } + secondary_sun_blend = { type="vector3" ui_name="Secondary Sun Blend / Exponent / Strength" value=[1.0 8.0 1.0] min=[0 1 0] max = [1 16 1] step = [0.001 0.01 0.001] } + + custom_fog_blend_enabled = { type="scalar" ui_name="Enable Custom Fog Blend" value=0.0 min=0.0 max=1.0 step=1.0 } + custom_fog_blend_direction = { type="vector3" ui_name="Custom Fog Blend Direction" ui_type="hemisphere_direction" value=[0 0 -1] } + custom_fog_blend_color = { type="vector3" ui_name="Custom Fog Blend Color" ui_type="hdr_color" value=[1.0 1.0 1.0] min=[0 0 0] max=[5 5 5] step=[0.003 0.003 0.003] } + custom_fog_blend = { type="vector3" ui_name="Custom Fog Blend / Exponent / Strength" value=[1.0 8.0 1.0] min=[0 1 0] max = [1 16 1] step = [0.001 0.01 0.001] } + + light_shaft_enabled = { type="scalar" ui_name="Enable Light Shafts" value=1.0 min=0.0 max=1.0 step=1.0 } + light_shaft_settings = { type="vector2" ui_name="Light Shaft Exposure/Falloff" value=[1 1] min=[0.001 0.001] max=[8 2] step=[0.001 0.001] } + light_shaft_weigth = { type="scalar" ui_name="Light Shaft Weight" value=0.01 min=0.0 max=1.0 step=0.001 } + + vignette_scale_falloff_opacity = { type="vector3" ui_name="Vignette [scale, falloff, opacity]" value=[3 2.5 0] min=[0 0.1 0] max=[10 10 1] step=[0.001 0.001 0.001] } + vignette_color = { type="vector3" ui_name="Vignette Color" value=[0 0 0] min=[0.0 0.0 0.0] max=[1.0 1.0 1.0] step=[0.001 0.001 0.001] } + + outline_thickness = { type="vector4" ui_name="Outline Thickness" value=[1.0 1.0 1.0 1.0] min=[0.1 0.1 0.1 0.1] max=[4.0 4.0 4.0 4.0] step=[0.01 0.01 0.01 0.01] } + + outline_multiplier_red = { type="scalar" ui_name="Outline Intensity Red" value=1.0 min=0.0 max=1.0 step=0.001 } + outline_multiplier_green = { type="scalar" ui_name="Outline Intensity Green" value=1.0 min=0.0 max=1.0 step=0.001 } + outline_multiplier_blue = { type="scalar" ui_name="Outline Intensity Blue" value=1.0 min=0.0 max=1.0 step=0.001 } + outline_multiplier_alpha = { type="scalar" ui_name="Outline Intensity Alpha" value=1.0 min=0.0 max=1.0 step=0.001 } + + outline_color_red = { type="vector3" ui_name="Outline Color Red Channel" value=[1.0 1.0 0.3] min=[0.0 0.0 0.0] max=[1.0 1.0 1.0] step=[0.001 0.001 0.001] } + outline_color_green = { type="vector3" ui_name="Outline Color Green Channel" value=[1.0 1.0 0.3] min=[0.0 0.0 0.0] max=[1.0 1.0 1.0] step=[0.001 0.001 0.001] } + outline_color_blue = { type="vector3" ui_name="Outline Color Blue Channel" value=[1.0 1.0 0.3] min=[0.0 0.0 0.0] max=[1.0 1.0 1.0] step=[0.001 0.001 0.001] } + outline_color_alpha = { type="vector3" ui_name="Outline Color Red Channel" value=[1.0 1.0 0.3] min=[0.0 0.0 0.0] max=[1.0 1.0 1.0] step=[0.001 0.001 0.001] } + + heatmap_world_position = { type="vector2" ui_name="Heatmap World Position" value=[-500 -500] min=[-1000.0 -1000.0] max=[1000.0 1000.0] step=[1 1] } + heatmap_world_extents = { type="vector2" ui_name="Heatmap World Extents" value=[1000 1000] min=[-1000.0 -1000.0] max=[1000.0 1000.0] step=[1 1] } + + skydome_fog_height_falloff = { type="vector2" ui_name="Skydome Fog Height/Falloff" value=[0 -50] min=[0 -50] max=[1 0] step=[0.001 0.01] } + skydome_tint_color = { type="vector3" ui_name="Skydome Tint Color" ui_type="hdr_color" value=[1.0 1.0 1.0] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] } + skydome_u_offset = { type="scalar" ui_name="Skydome Rotation" value=0 min=0 max=1 step=0.001 } + skydome_cloud_map = { type="resource" ui_name="Skydome Cloud Map" value="core/stingray_renderer/environments/empty_black" } + skydome_cloud_speed_scale = { type="vector2" ui_name="Skydome Cloud Speed/Scale" value=[0 1.0] min=[0 0] max=[1 2.0] step=[0.001 0.001] } + + fog_type = { type="integer" ui_name="Default Exp Height/Exp Height/Exp/Linear Depth" value=0.0 min=0.0 max=3.0 step=1.0 } + height_fog_offset_falloff = { type="vector2" ui_name="Exp Height Offset/Falloff" value=[0 500.0] min=[-1000 0.1] max=[1000 5000.0] step=[0.001 0.001] } + + ambient_diffuse_fade_type = { type="integer" ui_name="Ambient Fade Type None/Up/Down/Mirror" value=0.0 min=0.0 max=3.0 step=1.0 } + ambient_diffuse_fade = { type="vector3" ui_name="Ambient Fade Min/Offset/Falloff" value=[0 0 10] min=[0 -100 1] max=[1.0 100 100.0] step=[0.001 0.001 0.001] } + + eye_adaptation_enabled = { type="scalar" ui_name="Eye Adaptation Enabled" value=0.0 min=0.0 max=1.0 step=1.0 } + eye_adaptation_speed_min_max = { type="vector3" ui_name="Eye Adaptation Speed/Min/Max" value=[1.0 0.01 30] min=[0.1 0.01 0.01] max=[10.0 30 30.0] step=[0.001 0.001 0.001] } + + outline_enabled = { type="integer" ui_name="Outline Enabled" value=1.0 min=0.0 max=1.0 step=1.0 } + + + ssm_enabled = { type="scalar" ui_name="SSM Enabled" value=0.0 min=0.0 max=1.0 step=1.0 } + ssm_constant_update_enabled = { type="scalar" ui_name="SSM Constant Update (for debug)" value=0.0 min=0.0 max=1.0 step=1.0 } + ssm_center = { type="vector3" ui_name="SSM Center" value=[0 0 0] min=[-2000 -2000 -2000] max=[2000 2000 2000] step=[0.001 0.001 0.001] } + ssm_near_far = { type="vector2" ui_name="SSM Near/Far Offset" value=[0 0] min=[-1000 -1000] max=[1000 1000] step=[0.001 0.001] } + + ssm_radius = { type="scalar" ui_name="SSM Radius" value=200 min=1 max=1000 step=0.001 } + ssm_rotation = { type="scalar" ui_name="SSM Rotation" value=0 min=-360 max=360 step=0.001 } + + local_shadow_map_bias = { type="vector3" ui_name="Local Shadow Map Bias Min/Max/Blend Distance" value=[0.0001 0.015 50.0] min=[0.0 0.0 10] max=[0.005 0.1 100.0] step=[0.0001 0.0001 1.0] } + sun_shadow_map_bias = { type="vector3" ui_name="Sun Shadow Map Bias Min/Max/Blend Distance" value=[0.0001 0.001 50.0] min=[0.0 0.0 10] max=[0.005 0.1 100.0] step=[0.0001 0.0001 1.0] } + ssm_shadow_map_bias = { type="vector3" ui_name="SSM Shadow Map Bias Min/Max/Blend Distance" value=[0.0005 0.0001 20.0] min=[0.0 0.0 10] max=[0.005 0.1 100.0] step=[0.0001 0.0001 1.0] } + + eye_intensity = { type="scalar" ui_name="Eye Intensity" value=1.0 min=0.0 max=10.0 step=0.01 } + + temporal_effects_enabled = { type="scalar" ui_name="Temporal Effects Enabled (taa, ssr, ssao etc)" value=1.0 min=0.0 max=1.0 step=1.0 } + + wind_amount = { type="vector3" ui_name="Wind Amount" value=[0.0 0.0 0.0] min=[0.0 0.0 0.0] max=[1.0 1.0 1.0] step=[0.0001 0.0001 0.0001] } +} + +editor = [ + "global_roughness_multiplier" + + "skydome_map" + "skydome_u_offset" + "skydome_intensity" + "skydome_tint_color" + "skydome_cloud_map" + "skydome_cloud_speed_scale" + "global_diffuse_map" + "global_specular_map" + "ambient_tint" + "ambient_diffuse_fade_type" + "ambient_diffuse_fade" + + "sun_enabled" + "sun_direction" + "sun_color" + "sun_shadows_enabled" + "sun_shadow_slice_depth_ranges" + "sun_shadow_map_bias" + + "local_shadow_map_bias" + + "ssm_enabled" + "ssm_constant_update_enabled" + "ssm_center" + "ssm_near_far" + "ssm_shadow_map_bias" + "ssm_radius" + "ssm_rotation" + + "secondary_sun_enabled" + "secondary_sun_direction" + "secondary_sun_color" + "secondary_sun_blend" + + "light_shaft_enabled" + "light_shaft_settings" + "light_shaft_weigth" + + "emissive_particle_intensity" + + "offscreen_target_projection" + + "fog_enabled" + "fog_type" + "fog_depth_range" + "height_fog_offset_falloff" + "fog_color" + "fog_sun_blend" + "custom_fog_blend_enabled" + "custom_fog_blend_direction" + "custom_fog_blend_color" + "custom_fog_blend" + + "skydome_fog_height_falloff" + + "exposure" + "eye_adaptation_enabled" + "eye_adaptation_speed_min_max" + + "bloom_enabled" + "bloom_threshold_offset_falloff" + "global_lens_dirt_map" + "bloom_lens_dirt_amount" + "bloom_tint" + + "motion_blur_enabled" + "motion_blur_amount" + + "ao_enabled" + "ao_quality" + "ao_radius" + "ao_intensity" + + "dof_enabled" + "dof_focal_distance" + "dof_focal_region" + "dof_focal_region_start" + "dof_focal_region_end" + "dof_focal_near_scale" + "dof_focal_far_scale" + + "ssr_enabled" + "ssr_surface_thickness_threshold" + "ssr_screen_edge_threshold" + "ssr_ray_bending_enabled" + + "lens_quality_enabled" + "lens_quality_properties" + + "vignette_enabled" + //"vignette_properties" + "vignette_scale_falloff_opacity" + "vignette_color" + + //"color_grading_enabled" + "color_grading_map" + + "outline_enabled" + "outline_thickness" + + "eye_intensity" + + "temporal_effects_enabled" + + "wind_amount" +] diff --git a/vmf_source/core/stingray_renderer/output_nodes/anisotropic_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/anisotropic_base.shader_node new file mode 100644 index 0000000..056d308 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/anisotropic_base.shader_node @@ -0,0 +1,345 @@ +group = "Output" +display_name = "Anisostropic Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "aee6e47b-be7b-4d67-a123-2ab5d660b94e" = { + name = "vertex_offset" + display_name = "Position offset" + is_required = false + type = { vector3: ["HAS_VERTEX_OFFSET"] } + domain = "vertex" + } + + "aca690cb-6305-4a2f-bf3d-69183a493db3" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "34259752-b962-4b65-92c3-903a57338519" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "7a9306c6-95ae-4cdb-9fef-0eedacce4e83" = { + name = "opacity_threshold" + is_required = false + display_name = "Opacity Threshold" + type = { scalar: ["HAS_OPACITY_THRESHOLD"] } + domain = "pixel" + } + + "b1c86408-aacb-4466-b754-ddcf37a3a2c8" = { + is_required = false + name = "normal" + display_name = "Normal" + type = { vector3: ["HAS_NORMAL"] } + domain = "pixel" + } + + "ad5e052f-d316-4a0f-8b79-53c38204d61b" = { + is_required = false + name = "metallic" + display_name = "Metallic" + type = { scalar: ["HAS_METALLIC"] } + domain = "pixel" + } + + "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" = { + is_required = false + name = "roughness" + display_name = "Roughness" + type = { scalar: ["HAS_ROUGHNESS"] } + domain = "pixel" + } + + "e142ce4c-8a75-4602-a079-2996646bf37b" = { + is_required = false + name = "anisotropy" + display_name = "Anisotropy" + type = { scalar: ["HAS_ANISOTROPY"] } + domain = "pixel" + } + + "1164a5ef-4563-4795-b3b5-42825d6df037" = { + is_required = false + name = "emissive" + display_name = "Emissive" + type = { vector3: ["HAS_EMISSIVE" ] } + domain = "pixel" + } + + "59fd1cf4-f736-470d-8510-1dd7c016639e" = { + is_required = false + name = "ambient_occlusion" + display_name = "Ambient Occlusion" + type = { scalar: ["HAS_AMBIENT_OCCLUSION"] } + domain = "pixel" + } +} + +options = { + "b2c7c0d2-beff-4b1a-a9d4-068a507625a2" = "USE_FBX_PERMUTATIONS" + "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" = "INSTANCED" + "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" = "CULL_NONE" + "c198c109-2cdf-49ee-af18-a982c23e2729" = "CULL_FRONT" + "2b136447-676e-4943-997b-04a28ae68497" = "WORLD_SPACE_NORMAL" + "dd7fcf97-0627-48ab-b29a-95b5685bb123" = "TRANSPARENT" + "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" = "TRANSPARENT_FADE" + "b5bb2062-c8fa-43c5-8657-493a0be6860c" = "SKINNED_DISABLED" + "901b44ce-d128-498a-9912-32cd9635c556" = "HAS_FOV" + "da4e717b-5d0d-47fa-93b0-01c988f8f3ff" = "LOCK_NORMAL_ROTATION" + "979cb7cf-c255-42d0-a389-78fc0fb0539f" = "DEFERRED_DECALS_GROUP_1" + "d557b597-fa90-46b4-a751-eb51ae61ba5b" = "DEFERRED_DECALS_GROUP_2" + "fe20cc01-4cec-4217-98a0-07220ad3add9" = "DEFERRED_DECALS_GROUP_3" + "524a5842-23b7-46d1-ab22-cb3a14746ce0" = "USE_GLOBAL_ROUGHNESS_MULTIPLIER" + "11256995-4805-4018-bd6b-9214414fd363" = "NORMAL_FLIP_DISABLED" + "580cc6eb-3591-4c71-aa50-528af18ba160" = "JITTER_TRANSPARENCY" +} + +ui = [ + { + type = "drop_down" + display_name = "Normals In" + options = { + "Tangent Space" = "00000000-0000-0000-0000-000000000000" + "World Space" = "2b136447-676e-4943-997b-04a28ae68497" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Blend Mode" + options = { + "Opaque" = "00000000-0000-0000-0000-000000000000" + "Transparent" = "dd7fcf97-0627-48ab-b29a-95b5685bb123" + "Transparent Fade" = "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Face Culling" + options = { + "Back" = "00000000-0000-0000-0000-000000000000" + "Front" = "c198c109-2cdf-49ee-af18-a982c23e2729" + "None (double sided)" = "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Disable Skinning" option = "b5bb2062-c8fa-43c5-8657-493a0be6860c" } + { type = "checkbox" display_name = "Instancing" option = "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" } + { type = "checkbox" display_name = "Custom FOV" option = "901b44ce-d128-498a-9912-32cd9635c556" } + { type = "checkbox" display_name = "Avoid Tangent Space Transform" option = "da4e717b-5d0d-47fa-93b0-01c988f8f3ff" } + { type = "checkbox" display_name = "Use Global Roughness Multiplier" option = "524a5842-23b7-46d1-ab22-cb3a14746ce0" } + { type = "checkbox" display_name = "Normal Flip Disabled (double sided)" option = "11256995-4805-4018-bd6b-9214414fd363" } + { type = "checkbox" display_name = "Jitter Transparency" option = "580cc6eb-3591-4c71-aa50-528af18ba160" } +] + + +render_state = { +} + +sampler_state = { +} + +channels = { + "(defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE) || defined(HAS_ANISOTROPY)": { + vertex_tangent = { type = "float3" semantic = "TANGENT" domain = "vertex" } + vertex_binormal = { type = "float3" semantic = "BINORMAL" domain = "vertex" } + + tsm0 = { type = "float3" domains = ["vertex", "pixel"] } + tsm1 = { type = "float3" domains = ["vertex", "pixel"] } + tsm2 = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(MOTION_BLUR)": { + last_clip_position = { type = "float3" domains = ["vertex", "pixel"] } + } + + vertex_position = { type = "float4" domain = "vertex" } + vertex_normal = { type = "float3" semantic = "NORMAL" domain = "vertex" } + + "defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING)": { + vertex_color1 = { type = "float4" semantic = "COLOR1" domains = ["vertex"] } + baked_light = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)": { + lightmap_uv_input = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + lightmap_uv = { type = "float2" domains = ["vertex", "pixel"] } + } + + "defined(MATERIAL_TRANSFER)": { + lightmap_uv = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + } + + "defined(NEEDS_UNSKINNED_WORLD_POS)": { + unskinned_world_pos = { type = "float3" domains = ["vertex", "pixel"] } + unskinned_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } + + "defined(NEEDS_SUN_SHADOW_MASK)": { + //"defined(NEEDS_SUN_SHADOW_MASK) && (defined(EMISSIVE_PASS) || defined(TRANSPARENT) || defined(TRANSPARENT_FADE))": { + sun_shadow_mask = { type = "float" domain = "pixel" } + } +} + +//log_permutations = true +permutation_sets = { + vertex_modifiers = [ + { if: "num_skin_weights() == 4" define: { "macros": ["SKINNED_4WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 3" define: { "macros": ["SKINNED_3WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 2" define: { "macros": ["SKINNED_2WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 1" define: { "macros": ["SKINNED_1WEIGHT"] stages: ["vertex"] } } + { default = true } + ] + + instanced_modifiers = [ + // { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] } + { default = true } + ] + + lightmap_modifiers = [ + { if: "lightmap_format() == directional_irradiance" define: ["HAS_DIRECTIONAL_LIGHTMAPS"] } + { default = true } + ] + + non_instanced_modifiers = [ + // { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] permute_with: "lightmap_modifiers" } + // { if: "mesh_baked_lighting_type() == vertex" define: ["HAS_VERTEX_BAKED_DIFFUSE_LIGHTING"] } + { permute_with: "vertex_modifiers" } + ] + + instanced_and_non_instanced = [ + { if: "defined(INSTANCED)" permute_with: "instanced_modifiers" } + { if: "!defined(INSTANCED)" permute_with: "non_instanced_modifiers" } + ] + + default = [ + // FBX Standard material exclusive permutations, these will only compile of the + // 'USE_FBX_PERMUTATIONS' option is set, which it is in the shader used for imported fbx files. + { if: "defined(USE_FBX_PERMUTATIONS) && is_any_material_variable_set(use_emissive_map, emissive)" define: ["FBX_EMISSIVE"] permute_with: "instanced_and_non_instanced" } + + // Normal default permutation set + { if: "on_renderer(D3D11, D3D12, GNM, GL) && (defined(TRANSPARENT) || defined(TRANSPARENT_FADE)) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] permute_with: "instanced_and_non_instanced" } + { permute_with: "instanced_and_non_instanced" } + ] + + shadow_caster = [ + { if: "defined(INSTANCED)" } + { if: "!defined(INSTANCED)" permute_with: "vertex_modifiers" } + ] +} + +shader_contexts = { + shadow_caster = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: "shadow_caster" } + ] + + passes = [ + { code_block="core/stingray_renderer/output_nodes/standard_base#depth_only" render_state="core/stingray_renderer/output_nodes/standard_base#shadow_caster" } + ] + } + + material_transfer = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12)" } + ] + + passes = [ + { if: "defined(HAS_ANISOTROPY)" then: [ + { code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines=["MATERIAL_TRANSFER" "TRANSPARENT_FADE"] render_state="core/stingray_renderer/output_nodes/standard_base#material_transfer" } + ] else: [ + { code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines=["MATERIAL_TRANSFER"] render_state="core/stingray_renderer/output_nodes/standard_base#material_transfer" } + ]} + ] + } + + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: [ + // TODO: create permutation for when local lights is off. + { permute_with: "default" } + ] } + ] + + passes = [ + { if: "defined(HAS_ANISOTROPY)" then: [ + { if: "defined(TRANSPARENT) || defined(TRANSPARENT_FADE)" then: [ + { if: "defined(CULL_NONE)" then: [ + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" render_state="core/stingray_renderer/output_nodes/standard_base#transparent_double_sided_ccw" } + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" render_state="core/stingray_renderer/output_nodes/standard_base#transparent_double_sided_cw" } + ] else: [ + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" render_state="core/stingray_renderer/output_nodes/standard_base#transparent" } + ]} + ] else: [ + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines=["TRANSPARENT_FADE"] render_state="core/stingray_renderer/output_nodes/standard_base#gbuffer_material" } // TODO: write o another layer + ]} + // todo has fov? + ] else: [ + // Standard base logic. + { if: "defined(TRANSPARENT) || defined(TRANSPARENT_FADE)" then: [ + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" render_state="core/stingray_renderer/output_nodes/standard_base#transparent" } + ] else: [ + { if: "defined(HAS_FOV)" then: [ + { layer="gbuffer_fpr" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines="MOTION_BLUR" render_state="core/stingray_renderer/output_nodes/standard_base#gbuffer_material" } + ] else: [ + { if: "defined(HAS_OPACITY)" then: [ + { layer="gbuffer_alpha_masked" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines="MOTION_BLUR" render_state="core/stingray_renderer/output_nodes/standard_base#gbuffer_material" } + ] else: [ + { layer="gbuffer" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines="MOTION_BLUR" render_state="core/stingray_renderer/output_nodes/standard_base#gbuffer_material" } + ]} + ]} + + // This bit of logic is a bit complicated. The gist of it is that we want to disable this pass + // for materials that has a value connected on emissive for all permutations, but should have it + // discarded for all but the special permutations with the define FBX_EMISSIVE + { if: "defined(HAS_EMISSIVE) && (!defined(USE_FBX_PERMUTATIONS) || defined(FBX_EMISSIVE))" then: [ + { layer="emissive" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines={ macros: ["EMISSIVE_PASS"] stages: ["pixel"] } render_state="core/stingray_renderer/output_nodes/standard_base#emissive" } + ]} + ]} + ]} + + { if: "!on_renderer(GL)" then: [ + { layer="wireframe" code_block="core/stingray_renderer/output_nodes/standard_base#depth_only" define="DRAW_WIREFRAME" render_state="core/stingray_renderer/output_nodes/standard_base#wireframe" branch_key="dev_wireframe" } + ]} + + { layer="outline" code_block="core/stingray_renderer/output_nodes/standard_base#depth_only" define="DRAW_OUTLINE" render_state="core/stingray_renderer/output_nodes/standard_base#outline_stencil_write" branch_key="outline_unit" } + { layer="outline" code_block="core/stingray_renderer/output_nodes/standard_base#depth_only" define="DRAW_OUTLINE" render_state="core/stingray_renderer/output_nodes/standard_base#outline_stencil_write_z" branch_key="outline_unit_z" } + + { layer="outline_mask" code_block="core/stingray_renderer/output_nodes/standard_base#depth_only" defines=["DRAW_OUTLINE" "OUTLINE_MASK_WRITE"] render_state="core/stingray_renderer/output_nodes/standard_base#outline_mask_write" branch_key="outline_unit_z_fail" } + { layer="outline" code_block="core/stingray_renderer/output_nodes/standard_base#depth_only" defines=["DRAW_OUTLINE" "OUTLINE_MASK_READ"] render_state="core/stingray_renderer/output_nodes/standard_base#outline_stencil_write" branch_key="outline_unit_z_fail" } + + { layer="cascade_visualizer" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" define="CASCADE_VISUALIZER" render_state="core/stingray_renderer/output_nodes/standard_base#cascade_visualizer" branch_key="cascade_visualizer" } + ] + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/output_nodes/billboard_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/billboard_base.shader_node new file mode 100644 index 0000000..80d0183 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/billboard_base.shader_node @@ -0,0 +1,361 @@ +group = "Output" +display_name = "Billboard Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "aee6e47b-be7b-4d67-a123-2ab5d660b94e" = { + name = "vertex_offset" + display_name = "Position offset" + is_required = false + type = { vector3: ["HAS_VERTEX_OFFSET"] } + domain = "vertex" + } + + "aca690cb-6305-4a2f-bf3d-69183a493db3" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "34259752-b962-4b65-92c3-903a57338519" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "7a9306c6-95ae-4cdb-9fef-0eedacce4e83" = { + name = "opacity_threshold" + is_required = false + display_name = "Opacity Threshold" + type = { scalar: ["HAS_OPACITY_THRESHOLD"] } + domain = "pixel" + } + + "b1c86408-aacb-4466-b754-ddcf37a3a2c8" = { + is_required = false + name = "normal" + display_name = "Normal" + type = { vector3: ["HAS_NORMAL"] } + domain = "pixel" + } + + "ad5e052f-d316-4a0f-8b79-53c38204d61b" = { + is_required = false + name = "metallic" + display_name = "Metallic" + type = { scalar: ["HAS_METALLIC"] } + domain = "pixel" + } + + "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" = { + is_required = false + name = "roughness" + display_name = "Roughness" + type = { scalar: ["HAS_ROUGHNESS"] } + domain = "pixel" + } + + "1164a5ef-4563-4795-b3b5-42825d6df037" = { + is_required = false + name = "emissive" + display_name = "Emissive" + type = { vector3: ["HAS_EMISSIVE" ] } + domain = "pixel" + } + + "59fd1cf4-f736-470d-8510-1dd7c016639e" = { + is_required = false + name = "ambient_occlusion" + display_name = "Ambient Occlusion" + type = { scalar: ["HAS_AMBIENT_OCCLUSION"] } + domain = "pixel" + } + + "0544ddb6-e168-452d-86f2-42a79e8c98e3" = { + is_required = false + name = "sss_strength" + display_name = "Skin SSS" + type = { scalar: ["SKIN", "HAS_SKIN_SSS_STRENGTH"] } + domain = "pixel" + } +} + +options = { + "b2c7c0d2-beff-4b1a-a9d4-068a507625a2" = "USE_FBX_PERMUTATIONS" + "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" = "INSTANCED" + "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" = "CULL_NONE" + "c198c109-2cdf-49ee-af18-a982c23e2729" = "CULL_FRONT" + "34994d84-9d51-48ac-af85-bc053b2c65c3" = "SKIN" + "2b136447-676e-4943-997b-04a28ae68497" = "WORLD_SPACE_NORMAL" + "dd7fcf97-0627-48ab-b29a-95b5685bb123" = "TRANSPARENT" + "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" = "TRANSPARENT_FADE" + "b5bb2062-c8fa-43c5-8657-493a0be6860c" = "SKINNED_DISABLED" + "901b44ce-d128-498a-9912-32cd9635c556" = "HAS_FOV" + "da4e717b-5d0d-47fa-93b0-01c988f8f3ff" = "LOCK_NORMAL_ROTATION" + "979cb7cf-c255-42d0-a389-78fc0fb0539f" = "DEFERRED_DECALS_GROUP_1" + "d557b597-fa90-46b4-a751-eb51ae61ba5b" = "DEFERRED_DECALS_GROUP_2" + "fe20cc01-4cec-4217-98a0-07220ad3add9" = "DEFERRED_DECALS_GROUP_3" + "524a5842-23b7-46d1-ab22-cb3a14746ce0" = "USE_GLOBAL_ROUGHNESS_MULTIPLIER" + "11256995-4805-4018-bd6b-9214414fd363" = "NORMAL_FLIP_DISABLED" + "580cc6eb-3591-4c71-aa50-528af18ba160" = "JITTER_TRANSPARENCY" + + "a669a3b8-2bf0-4b3f-ba67-aebdeaf0e0d1" = "BILLBOARD_TANGENT_ALIGNED" + "b2df1da0-8e59-4ad4-9ab7-7a4d5ddcf998" = "BILLBOARD_CAMERA_ALIGNED" + "f0642710-b291-4a17-80d0-1319d0decfd9" = "BILLBOARD_TANGENT_ALIGNED_X_LOCKED" + "32b48986-5602-4157-8a25-44fc7f71ee47" = "BILLBOARD_TANGENT_ALIGNED_Y_LOCKED" + "5bdca8b0-737e-417e-a2b4-d35cc935f605" = "BILLBOARD_SPHERICAL_NORMAL" + "c1c06809-56d3-4a8e-882c-7d84c31299ba" = "SECONDARY_SUN_DIRECTION" +} + +ui = [ + { + type = "drop_down" + display_name = "Billboard Option" + options = { + "Screen Aligned" = "00000000-0000-0000-0000-000000000000" + "Camera Aligned" = "b2df1da0-8e59-4ad4-9ab7-7a4d5ddcf998" + "Tangent Aligned" = "a669a3b8-2bf0-4b3f-ba67-aebdeaf0e0d1" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Tangent Aligned Axis" + options = { + "Z-Axis" = "00000000-0000-0000-0000-000000000000" + "Y-Axis" = "32b48986-5602-4157-8a25-44fc7f71ee47" + "X-Axis" = "f0642710-b291-4a17-80d0-1319d0decfd9" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Normal Options" + options = { + "Plane Normal" = "00000000-0000-0000-0000-000000000000" + "Spherical Plane Normal" = "5bdca8b0-737e-417e-a2b4-d35cc935f605" + + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Normals In" + options = { + "Tangent Space" = "00000000-0000-0000-0000-000000000000" + "World Space" = "2b136447-676e-4943-997b-04a28ae68497" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Blend Mode" + options = { + "Opaque" = "00000000-0000-0000-0000-000000000000" + "Transparent" = "dd7fcf97-0627-48ab-b29a-95b5685bb123" + "Transparent Fade" = "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Decal Group" + options = { + "Group 0" = "00000000-0000-0000-0000-000000000000" + "Group 1" = "979cb7cf-c255-42d0-a389-78fc0fb0539f" + "Group 2" = "d557b597-fa90-46b4-a751-eb51ae61ba5b" + "Group 3" = "fe20cc01-4cec-4217-98a0-07220ad3add9" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Disable Skinning" option = "b5bb2062-c8fa-43c5-8657-493a0be6860c" } + { type = "checkbox" display_name = "Instancing" option = "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" } + { type = "checkbox" display_name = "Custom FOV" option = "901b44ce-d128-498a-9912-32cd9635c556" } + { type = "checkbox" display_name = "Avoid Tangent Space Transform" option = "da4e717b-5d0d-47fa-93b0-01c988f8f3ff" } + { type = "checkbox" display_name = "Use Global Roughness Multiplier" option = "524a5842-23b7-46d1-ab22-cb3a14746ce0" } + { type = "checkbox" display_name = "Normal Flip Disabled (double sided)" option = "11256995-4805-4018-bd6b-9214414fd363" } + { type = "checkbox" display_name = "Jitter Transparency" option = "580cc6eb-3591-4c71-aa50-528af18ba160" } +] + +render_state = { +} + +sampler_state = { +} + +channels = { + "(defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE)": { + vertex_tangent = { type = "float3" semantic = "TANGENT" domain = "vertex" } + vertex_binormal = { type = "float3" semantic = "BINORMAL" domain = "vertex" } + + tsm0 = { type = "float3" domains = ["vertex", "pixel"] } + tsm1 = { type = "float3" domains = ["vertex", "pixel"] } + tsm2 = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(MOTION_BLUR)": { + last_clip_position = { type = "float3" domains = ["vertex", "pixel"] } + } + + vertex_position = { type = "float4" domain = "vertex" } + vertex_normal = { type = "float3" semantic = "NORMAL" domain = "vertex" } + + "defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING)": { + vertex_color1 = { type = "float4" semantic = "COLOR1" domains = ["vertex"] } + baked_light = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)": { + lightmap_uv_input = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + lightmap_uv = { type = "float2" domains = ["vertex", "pixel"] } + } + + "defined(MATERIAL_TRANSFER)": { + lightmap_uv = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + } + + "defined(NEEDS_UNSKINNED_WORLD_POS)": { + unskinned_world_pos = { type = "float3" domains = ["vertex", "pixel"] } + unskinned_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } + + "defined(NEEDS_SUN_SHADOW_MASK)": { + //"defined(NEEDS_SUN_SHADOW_MASK) && (defined(EMISSIVE_PASS) || defined(TRANSPARENT) || defined(TRANSPARENT_FADE))": { + sun_shadow_mask = { type = "float" domain = "pixel" } + } +} + +//log_permutations = true +permutation_sets = { + vertex_modifiers = [ + { if: "num_skin_weights() == 4" define: { "macros": ["SKINNED_4WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 3" define: { "macros": ["SKINNED_3WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 2" define: { "macros": ["SKINNED_2WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 1" define: { "macros": ["SKINNED_1WEIGHT"] stages: ["vertex"] } } + { default = true } + ] + + instanced_modifiers = [ + // { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] } + { default = true } + ] + + lightmap_modifiers = [ + { if: "lightmap_format() == directional_irradiance" define: ["HAS_DIRECTIONAL_LIGHTMAPS"] } + { default = true } + ] + + non_instanced_modifiers = [ + // { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] permute_with: "lightmap_modifiers" } + // { if: "mesh_baked_lighting_type() == vertex" define: ["HAS_VERTEX_BAKED_DIFFUSE_LIGHTING"] } + { permute_with: "vertex_modifiers" } + ] + + instanced_and_non_instanced = [ + { if: "defined(INSTANCED)" permute_with: "instanced_modifiers" } + { if: "!defined(INSTANCED)" permute_with: "non_instanced_modifiers" } + ] + + default = [ + // FBX Standard material exclusive permutations, these will only compile of the + // 'USE_FBX_PERMUTATIONS' option is set, which it is in the shader used for imported fbx files. + { if: "defined(USE_FBX_PERMUTATIONS) && is_any_material_variable_set(use_emissive_map, emissive)" define: ["FBX_EMISSIVE"] permute_with: "instanced_and_non_instanced" } + + // Normal default permutation set + { if: "on_renderer(D3D11, D3D12, GNM, GL) && (defined(TRANSPARENT) || defined(TRANSPARENT_FADE)) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] permute_with: "instanced_and_non_instanced" } + { permute_with: "instanced_and_non_instanced" } + ] + + shadow_caster = [ + { if: "defined(INSTANCED)" } + { if: "!defined(INSTANCED)" permute_with: "vertex_modifiers" } + ] +} + +shader_contexts = { + shadow_caster = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: "shadow_caster" } + ] + + passes = [ + { code_block="core/stingray_renderer/output_nodes/standard_base#depth_only" defines={ macros: ["BILLBOARD"] stages: ["vertex"] } render_state="core/stingray_renderer/output_nodes/standard_base#shadow_caster" } + ] + } + + material_transfer = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12)" } + ] + + passes = [ + { code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines=["BILLBOARD" "MATERIAL_TRANSFER"] render_state="core/stingray_renderer/output_nodes/standard_base#material_transfer" } + ] + } + + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: [ + // TODO: create permutation for when local lights is off. + { permute_with: "default" } + ] } + ] + + passes = [ + { if: "(defined(TRANSPARENT) || defined(TRANSPARENT_FADE))" then: [ + { if: "defined(CULL_NONE)" then: [ + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines={ macros: ["BILLBOARD"] stages: ["vertex"] } render_state="core/stingray_renderer/output_nodes/standard_base#transparent_double_sided_ccw" } + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines={ macros: ["BILLBOARD"] stages: ["vertex"] } render_state="core/stingray_renderer/output_nodes/standard_base#transparent_double_sided_cw" } + ] else: [ + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines={ macros: ["BILLBOARD"] stages: ["vertex"] } render_state="core/stingray_renderer/output_nodes/standard_base#transparent" } + ]} + ] else: [ + { if: "defined(HAS_FOV)" then: [ + { layer="gbuffer_fpr" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines=["BILLBOARD" "MOTION_BLUR"] render_state="core/stingray_renderer/output_nodes/standard_base#gbuffer_material" } + ] else: [ + { if: "defined(HAS_OPACITY)" then: [ + { layer="gbuffer_alpha_masked" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines=["BILLBOARD" "MOTION_BLUR"] render_state="core/stingray_renderer/output_nodes/standard_base#gbuffer_material" } + ] else: [ + { layer="gbuffer" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines=["BILLBOARD" "MOTION_BLUR"] render_state="core/stingray_renderer/output_nodes/standard_base#gbuffer_material" } + ]} + ]} + + // This bit of logic is a bit complicated. The gist of it is that we want to disable this pass + // for materials that has a value connected on emissive for all permutations, but should have it + // discarded for all but the special permutations with the define FBX_EMISSIVE + { if: "defined(HAS_EMISSIVE) && (!defined(USE_FBX_PERMUTATIONS) || defined(FBX_EMISSIVE))" then: [ + { layer="emissive" code_block="core/stingray_renderer/output_nodes/standard_base#gbuffer_base" defines=["BILLBOARD" "EMISSIVE_PASS"] render_state="core/stingray_renderer/output_nodes/standard_base#emissive" } + ]} + ]} + + { if: "!on_renderer(GL)" then: [ + { layer="wireframe" code_block="core/stingray_renderer/output_nodes/standard_base#depth_only" defines=["BILLBOARD" "DRAW_WIREFRAME"] render_state="core/stingray_renderer/output_nodes/standard_base#wireframe" branch_key="dev_wireframe" } + ]} + ] + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/output_nodes/billboard_unlit_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/billboard_unlit_base.shader_node new file mode 100644 index 0000000..0cd106b --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/billboard_unlit_base.shader_node @@ -0,0 +1,243 @@ +group = "Output" +display_name = "Billboard Unlit Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "aee6e47b-be7b-4d67-a123-2ab5d660b94e" = { + name = "vertex_offset" + display_name = "Position offset" + is_required = false + type = { vector3: ["HAS_VERTEX_OFFSET"] } + domain = "vertex" + } + + "aca690cb-6305-4a2f-bf3d-69183a493db3" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "34259752-b962-4b65-92c3-903a57338519" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } +} + + +options = { + "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" = "INSTANCED" + "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" = "DOUBLE_SIDED" + "34994d84-9d51-48ac-af85-bc053b2c65c3" = "SKIN" + "e1bfa889-2503-4ac3-9134-c08a7fa04568" = "PROJECT_TO_FAR_PLANE" + "6a6241cc-7d21-4e2e-87c8-8d9c7bdcd322" = "CAMERA_TRANSLATION_LOCK" + "435e14e4-556d-4ac1-af14-8dafe63aff8f" = "BLEND_TRANSPARENT" + "52c7ce01-ee57-4770-914e-727fc1966962" = "LAYER_EMISSIVE" + "da09a694-b81f-4684-9912-61294914fd70" = "LAYER_SKYDOME_BILLBOARD" + "4c3163d4-c086-4645-ba1c-0d68a98022a1" = "LAYER_HDR_TRANSPARENT" + "c8d8b754-c567-4c7b-9cbd-8acab22beff5" = "LAYER_TRANSPARENT" + "afe47c59-33c4-43b2-af4a-817085b1113c" = "DEPTH_TEST_INVERTED" + "774556cd-2d1e-4df8-8ae2-5e84800f0c04" = "DEPTH_TEST_DISABLED" + "7b8bc0bf-c453-49d2-9415-0e80fec1039f" = "DISABLE_DEPTH_WRITE" + "b2556764-e8e9-47cf-9ecc-f53b5d5d73c7" = "HAS_CUSTOM_FOV" + "72ecf75c-f85f-49a2-8708-cd3158c1afa8" = "BILLBOARD_TANGENT_ALIGNED" + "33aec36d-02fd-4494-81b5-02b9d6ebf4bb" = "BILLBOARD_CAMERA_ALIGNED" + "7ae0cdd4-6508-433d-a994-9f2d4ba7b336" = "BILLBOARD_TANGENT_ALIGNED_X_LOCKED" + "774ef841-1d96-4a1f-8b6d-da8007bfe93e" = "BILLBOARD_TANGENT_ALIGNED_Y_LOCKED" + "d8ad7502-a44d-4cf0-920e-957a477f5c86" = "BILLBOARD_SPHERICAL_NORMAL" + "7afd3648-ebd1-4a40-94b4-a07e59a6fbb2" = "DISABLE_WRITE_DEPTH" + "7fb0b18d-1725-4e76-b3fe-f7a59b607f98" = "SECONDARY_SUN_DIRECTION" +} + +ui = [ + { + type = "drop_down" + display_name = "Billboard Option" + options = { + "Screen Aligned" = "00000000-0000-0000-0000-000000000000" + "Camera Aligned" = "33aec36d-02fd-4494-81b5-02b9d6ebf4bb" + "Tangent Aligned" = "72ecf75c-f85f-49a2-8708-cd3158c1afa8" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Tangent Aligned Axis" + options = { + "Z-Axis" = "00000000-0000-0000-0000-000000000000" + "Y-Axis" = "774ef841-1d96-4a1f-8b6d-da8007bfe93e" + "X-Axis" = "7ae0cdd4-6508-433d-a994-9f2d4ba7b336" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Skydome Billboard Direction" + options = { + "Sun Direction" = "00000000-0000-0000-0000-000000000000" + "Secondary Sun Direction" = "7fb0b18d-1725-4e76-b3fe-f7a59b607f98" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Normal Options" + options = { + "Plane Normal" = "00000000-0000-0000-0000-000000000000" + "Spherical Plane Normal" = "d8ad7502-a44d-4cf0-920e-957a477f5c86" + + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Layer" + options = { + "Emissive" = "52c7ce01-ee57-4770-914e-727fc1966962" + "Skydome Billboard" = "da09a694-b81f-4684-9912-61294914fd70" + "HDR Transparent" = "4c3163d4-c086-4645-ba1c-0d68a98022a1" + "LDR Transparent" = "c8d8b754-c567-4c7b-9cbd-8acab22beff5" + } + default = "4c3163d4-c086-4645-ba1c-0d68a98022a1" + } + + { + type = "drop_down" + display_name = "Depth Testing" + options = { + "Normal" = "00000000-0000-0000-0000-000000000000" + "Inverted" = "afe47c59-33c4-43b2-af4a-817085b1113c" + "Disabled" = "774556cd-2d1e-4df8-8ae2-5e84800f0c04" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Blend Mode" + options = { + "Opaque" = "00000000-0000-0000-0000-000000000000" + "Transparent" = "435e14e4-556d-4ac1-af14-8dafe63aff8f" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Disable Depth Writes" option = "7b8bc0bf-c453-49d2-9415-0e80fec1039f" } + { type = "checkbox" display_name = "Double Sided" option = "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" } + { type = "checkbox" display_name = "Instancing" option = "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" } + { type = "checkbox" display_name = "Project to Far Plane" option = "e1bfa889-2503-4ac3-9134-c08a7fa04568" } + { type = "checkbox" display_name = "Camera Translation Lock" option = "6a6241cc-7d21-4e2e-87c8-8d9c7bdcd322" } + { type = "checkbox" display_name = "Custom FOV" option = "b2556764-e8e9-47cf-9ecc-f53b5d5d73c7" } +] + +render_state = { + skydome_billboard = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + write_mask0 = "red|green|blue" + + "defined(BLEND_TRANSPARENT)" = { + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_one" + } + + "defined(DEPTH_TEST_DISABLED)" = { + z_enable = "false" + } + + z_write_enable = "false" + } + } +} + +sampler_state = { } + +channels = { + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(MOTION_BLUR)": { + last_clip_position = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(MATERIAL_TRANSFER)": { + lightmap_uv = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + vertex_position = { type = "float4" domain = "vertex" } + vertex_normal = { type = "float3" semantic = "NORMAL" domain = "vertex" } +} + +permutation_sets = { + vertex_modifiers = [ + { if: "num_skin_weights() == 4" define: { "macros": ["SKINNED_4WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 3" define: { "macros": ["SKINNED_3WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 2" define: { "macros": ["SKINNED_2WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 1" define: { "macros": ["SKINNED_1WEIGHT"] stages: ["vertex"] } } + { default = true } + ] + + instanced_modifiers = [ + { default = true } + ] + + non_instanced_modifiers = [ + { permute_with: "vertex_modifiers" } + ] + + default = [ + { if: "defined(INSTANCED)" permute_with: "instanced_modifiers" } + { if: "!defined(INSTANCED)" permute_with: "non_instanced_modifiers" } + ] +} + +shader_contexts = { + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL) && defined(BLEND_TRANSPARENT) && defined(LAYER_HDR_TRANSPARENT) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] permute_with: "default" } + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: "default" } + ] + + passes = [ + { if: "defined(LAYER_EMISSIVE)" then: [ + { layer="emissive" code_block="core/stingray_renderer/output_nodes/unlit_base#unlit" defines=["BILLBOARD" "MOTION_BLUR"] render_state="core/stingray_renderer/output_nodes/unlit_base#unlit" } + ]} + { if: "defined(LAYER_SKYDOME_BILLBOARD)" then: [ + { layer="skydome_billboard" code_block="core/stingray_renderer/output_nodes/unlit_base#unlit" defines={ macros: ["BILLBOARD"] stages: ["vertex"] } render_state="skydome_billboard" } + ]} + { if: "defined(LAYER_HDR_TRANSPARENT)" then: [ + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/unlit_base#unlit" defines={ macros: ["BILLBOARD"] stages: ["vertex"] } render_state="core/stingray_renderer/output_nodes/unlit_base#unlit" } + ]} + + { if: "defined(LAYER_TRANSPARENT)" then: [ + { layer="transparent" code_block="core/stingray_renderer/output_nodes/unlit_base#unlit" defines={ macros: ["BILLBOARD"] stages: ["vertex"] } render_state="core/stingray_renderer/output_nodes/unlit_base#unlit" } + ] else: [ + { if: "!on_renderer(GL)" then: [ + { layer="wireframe" code_block="core/stingray_renderer/output_nodes/unlit_base#unlit" defines=["BILLBOARD" "DRAW_WIREFRAME"] render_state="core/stingray_renderer/output_nodes/unlit_base#wireframe" branch_key="dev_wireframe" } + ]} + ]} + ] + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/output_nodes/decal_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/decal_base.shader_node new file mode 100644 index 0000000..03d6d09 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/decal_base.shader_node @@ -0,0 +1,1107 @@ +group = "Output" +display_name = "Decal Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "aca690cb-6305-4a2f-bf3d-69183a493db3" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "ad5e052f-d316-4a0f-8b79-53c38204d61b" = { + is_required = false + name = "base_color_opacity" + display_name = "Base Color Opacity" + type = { scalar: ["HAS_BASE_COLOR_OPACITY"] } + domain = "pixel" + } + + "b1c86408-aacb-4466-b754-ddcf37a3a2c8" = { + is_required = false + name = "normal" + display_name = "Normal" + type = { vector3: ["HAS_NORMAL"] } + domain = "pixel" + } + + "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" = { + is_required = false + name = "normal_opacity" + display_name = "Normal Opacity" + type = { scalar: ["HAS_NORMAL_OPACITY"] } + domain = "pixel" + } + + "7cadc6f1-f753-41c0-9d6d-ca9fb101f144" = { + is_required = false + name = "metallic" + display_name = "Metallic" + type = { scalar: ["HAS_METALLIC"] } + domain = "pixel" + } + + "f6a9f7bc-f229-42ce-9240-146828cbf665" = { + is_required = false + name = "roughness" + display_name = "Roughness" + type = { scalar: ["HAS_ROUGHNESS"] } + domain = "pixel" + } + + "7c1ec98b-14e9-41fb-b356-e881723b8731" = { + is_required = false + name = "metallic_roughness_mask" + display_name = "Metallic/Roughness Mask" + type = { scalar: ["HAS_METALLIC_ROUGHNESS_MASK"] } + domain = "pixel" + } + + "927a23bd-14d7-48a8-9934-04199a82af19" = { + is_required = false + name = "metallic_roughness_threshold" + display_name = "Metallic/Roughness Threshold" + type = { scalar: ["HAS_METALLIC_ROUGHNESS_THRESHOLD"] } + domain = "pixel" + } + + "1164a5ef-4563-4795-b3b5-42825d6df037" = { + is_required = false + name = "emissive" + display_name = "Emissive" + type = { vector3: ["HAS_EMISSIVE"] } + domain = "pixel" + } + + "34259752-b962-4b65-92c3-903a57338519" = { + name = "mask" + is_required = false + display_name = "Mask" + type = { scalar: ["HAS_MASK"] } + domain = "pixel" + } + + "7a9306c6-95ae-4cdb-9fef-0eedacce4e83" = { + name = "mask_threshold" + is_required = false + display_name = "Mask Threshold" + type = { scalar: ["HAS_MASK_THRESHOLD"] } + domain = "pixel" + } + + "59fd1cf4-f736-470d-8510-1dd7c016639e" = { + is_required = false + name = "normal_threshold" + display_name = "Angle Threshold" + type = { scalar: ["HAS_NORMAL_THRESHOLD"] } + domain = "pixel" + } +} + +options = { + "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" = "INSTANCED" + "2b136447-676e-4943-997b-04a28ae68497" = "WORLD_SPACE_NORMAL" + "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" = "TANGENT_BASIS_FROM_SURFACE" + "d00a0e06-a753-47fd-9ffd-20f091ff7810" = "SOFT_NORMAL_THRESHOLD" + "a2c093fa-a258-44ba-a16d-dc43792ffa38" = "COLOR_ALPHA_FADE" + "462ac943-c3a7-4a8a-ad2e-95f1e7ab9db3" = "FADE_OVER_TIME" + + "d902d6eb-36e4-479f-aac8-10a7afdcbf35" = "GROUP_0" + "2395c420-550a-41fb-9d06-d6988794dca4" = "GROUP_1" + "6cc950bc-76c1-43a5-a413-979084d5f841" = "GROUP_2" + "efb12ce5-bcf8-47ad-94aa-c9094833b2d1" = "GROUP_3" + "421b1c9b-9eb1-4ca6-9364-99178675c355" = "GROUP_01" + "81bd528a-a801-452b-b237-3b442c80557e" = "GROUP_02" + "2c0f5484-4d92-4d38-8e88-972885de2ba4" = "GROUP_13" + "2f1738ff-75c4-4ccf-8345-6879833536a9" = "GROUP_23" + "4035efa3-d637-4d4d-865d-24653f90b7a3" = "GROUP_012" + "82ad5599-11b0-4bf8-bcdf-f77c817f183d" = "GROUP_123" + "1695f356-fa07-4ccd-b181-9d0e6844570d" = "GROUP_023" + "cf3416fb-c210-463b-bb8d-0cf789b72e6c" = "GROUP_013" +} + +ui = [ + { + type = "drop_down" + display_name = "Decal Group" + options = { + "None" = "00000000-0000-0000-0000-000000000000" + "0" = "d902d6eb-36e4-479f-aac8-10a7afdcbf35" + "1" = "2395c420-550a-41fb-9d06-d6988794dca4" + "2" = "6cc950bc-76c1-43a5-a413-979084d5f841" + "3" = "efb12ce5-bcf8-47ad-94aa-c9094833b2d1" + "0, 1" = "421b1c9b-9eb1-4ca6-9364-99178675c355" + "0, 2" = "81bd528a-a801-452b-b237-3b442c80557e" + "1, 3" = "2c0f5484-4d92-4d38-8e88-972885de2ba4" + "2, 3" = "2f1738ff-75c4-4ccf-8345-6879833536a9" + "0, 1, 2" = "4035efa3-d637-4d4d-865d-24653f90b7a3" + "1, 2, 3" = "82ad5599-11b0-4bf8-bcdf-f77c817f183d" + "0, 2, 3" = "1695f356-fa07-4ccd-b181-9d0e6844570d" + "0, 1, 3" = "cf3416fb-c210-463b-bb8d-0cf789b72e6c" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Normals In" + options = { + "Tangent Space" = "00000000-0000-0000-0000-000000000000" + "World Space" = "2b136447-676e-4943-997b-04a28ae68497" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Normal Threshold" + options = { + "Hard" = "00000000-0000-0000-0000-000000000000" + "Soft" = "d00a0e06-a753-47fd-9ffd-20f091ff7810" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Tangent Basis From" + options = { + "Decal Box" = "00000000-0000-0000-0000-000000000000" + "Surface" = "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Particle Option" + options = { + "none" = "00000000-0000-0000-0000-000000000000" + "Color Alpha Fade" = "a2c093fa-a258-44ba-a16d-dc43792ffa38" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Instancing" option = "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" } + { type = "checkbox" display_name = "Fade over time" option = "462ac943-c3a7-4a8a-ad2e-95f1e7ab9db3" } + +] + +render_state = { + decal_stencil = { + state: { + "defined(GROUP_0) || defined(GROUP_1) || defined(GROUP_2) || defined(GROUP_3) || + defined(GROUP_01) || defined(GROUP_02) || defined(GROUP_13) || defined(GROUP_23) || + defined(GROUP_012) || defined(GROUP_123) || defined(GROUP_023) || defined(GROUP_013)" = { + stencil_enable = "true" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_write_mask = "0x0" + } + + "defined(GROUP_0)" = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x60" + stencil_ref = "0x0" + } + "defined(GROUP_1)" = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x60" + stencil_ref = "0x20" + } + "defined(GROUP_2)" = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x60" + stencil_ref = "0x40" + } + "defined(GROUP_3)" = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x60" + stencil_ref = "0x60" + } + "defined(GROUP_01)" = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x40" + stencil_ref = "0x0" + } + "defined(GROUP_02)" = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x20" + stencil_ref = "0x0" + } + "defined(GROUP_13)" = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x20" + stencil_ref = "0x60" + } + "defined(GROUP_23)" = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x40" + stencil_ref = "0x60" + } + "defined(GROUP_012)" = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + stencil_mask = "0x60" + stencil_ref = "0x60" + } + "defined(GROUP_123)" = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + stencil_mask = "0x60" + stencil_ref = "0x0" + } + "defined(GROUP_023)" = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + stencil_mask = "0x60" + stencil_ref = "0x20" + } + "defined(GROUP_013)" = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + stencil_mask = "0x60" + stencil_ref = "0x40" + } + } + } + + decal = { + inherit: ["core/stingray_renderer/shader_libraries/common#opacity" "decal_stencil"] + state: { + cull_mode = "cull_ccw" + z_func = "greater_equal" + z_write_enable = "false" + + "defined(HAS_BASE_COLOR)": { + write_mask0 = "red|green|blue" + } + "!defined(HAS_BASE_COLOR)": { + write_mask0 = "0x0" + } + "defined(HAS_NORMAL)": { + write_mask1 = "red|green|blue" + } + "!defined(HAS_NORMAL)": { + write_mask1 = "0x0" + } + + + } + } + + decal_metal_roughness = { + inherit: ["core/stingray_renderer/shader_libraries/common#default" "decal_stencil"] + state: { + cull_mode = "cull_ccw" + z_func = "greater_equal" + z_write_enable = "false" + + independent_blend_enable = "true" + + "defined(HAS_METALLIC)": { + write_mask0 = "alpha" + } + "!defined(HAS_METALLIC)": { + write_mask0 = "0x0" + } + "defined(HAS_ROUGHNESS)": { + write_mask1 = "alpha" + } + "!defined(HAS_ROUGHNESS)": { + write_mask1 = "0x0" + } + } + } + + emissive = { + inherit: ["core/stingray_renderer/shader_libraries/common#default" "decal_stencil"] + state: { + cull_mode = "cull_ccw" + write_mask0 = "red|green|blue" + z_write_enable = "false" + z_func = "greater_equal" + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_one" + src_blend = "blend_one" + } + } + + wireframe = { + inherit: ["core/stingray_renderer/shader_libraries/common#opacity"] + state: { + fill_mode = "fill_wireframe" + "on_renderer(D3D11, D3D12)" = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + } +} + +sampler_state = {} + +channels = { + "(defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE)": { + tsm0 = { type = "float3" domain = "pixel" } + tsm1 = { type = "float3" domain = "pixel" } + tsm2 = { type = "float3" domain = "pixel" } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "pixel" } + } + + decal_uv = { type = "float2" domain = "pixel" } + + "defined(NEEDS_OBJECT_POSITION)": { + object_position = { type = "float3" domain = "pixel" } + } + + "defined(NEEDS_WORLD_SPACE_POSITION)": { + world_position = { type = "float3" domain = "pixel" } + } +} + +//log_permutations = true +permutation_sets = { +} + +shader_contexts = { + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" } + ] + + passes = [ + { if: "defined(HAS_BASE_COLOR) || defined(HAS_NORMAL)" then: [ + { layer="decals" code_block="decal" defines=["GBUFFER_PASS" "BASE_COLOR_NORMAL_PASS"] render_state="decal" } + ]} + + { if: "defined(HAS_METALLIC) || defined(HAS_ROUGHNESS)" then: [ + { layer="decals" code_block="decal" defines=["GBUFFER_PASS" "METALLIC_ROUGHNESS_PASS"] render_state="decal_metal_roughness" } + ]} + + { if: "defined(HAS_EMISSIVE)" then: [ + { layer="emissive" code_block="decal" defines=["EMISSIVE_PASS"] render_state="emissive" } + ]} + + { if: "!on_renderer(GL)" then: [ + { layer="wireframe" code_block="decal" defines=["WIREFRAME_PASS"] render_state="wireframe" branch_key="dev_wireframe" } + ]} + ] + } +} + +code_blocks = { + decal_common = { + code = { + shared = """ + #if !(defined(RENDERER_GL) && defined(STAGE_VERTEX)) + inline float2 compute_decal_uv(in float3 op, in float4x4 bounding_volume) { + #if defined(RENDERER_GL) + vec2 uv = (op.xz / (vec2(bounding_volume[0].z, bounding_volume[2].z) * 0.5)) * 0.5 + 0.5; + #else + float2 uv = (op.xz / (bounding_volume._m20_m22 * 0.5)) * 0.5 + 0.5; + #endif + + uv.y = 1.0 - uv.y; + return uv; + } + + // Reference: + // Schuler C., "Normal Mapping without Precomputed Tangents", + // Shader X5, pp. 131-140, 2006. + // + inline float3x3 tangent_frame(in float3 n, in float3 p, in float2 uv) + { + // Get edge vectors of the pixel triangle + float3 dp1 = ddx(p); + float3 dp2 = ddy(p); + float2 duv1 = ddx(uv); + float2 duv2 = ddy(uv); + + // solve the linear system + float3 dp2perp = cross(dp2, n); + float3 dp1perp = cross(n, dp1); + float3 tangent = dp2perp * duv1.x + dp1perp * duv2.x; + float3 binormal = dp2perp * duv1.y + dp1perp * duv2.y; + + // construct a scale-invariant frame + float invmax = rsqrt(max(dot(tangent,tangent), dot(binormal,binormal))); + return float3x3(tangent * invmax, binormal * invmax, n); + } + #endif + """ + } + } + + decal = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/common#taa_offsets", + "decal_common" + ] + + instance_data = { + "on_renderer(D3D11, D3D12) && defined(INSTANCED)" : { + world = { type = "matrix4x4" } + + "defined(WIREFRAME_PASS)" : { + dev_wireframe_color = { type = "vector4" } + } + } + } + + samplers = { + linear_depth = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "linear_depth" + type = "2d" + } + + "defined(EMISSIVE_PASS)" : { + gbuffer1 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "gbuffer1" + type = "2d" + } + } + "defined(GBUFFER_PASS)" : { + normals_copy = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "normals_copy" + type = "2d" + } + } + } + + code = { + glsl = """ + #define INVERSE_Y_TEXCOORD + + #if defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL) + #define NEEDS_TANGENT_SPACE + #endif + + #if defined(HAS_NORMAL_THRESHOLD) || (defined(NEEDS_TANGENT_SPACE) && defined(TANGENT_BASIS_FROM_SURFACE)) + #define NEEDS_GBUFFER_NORMAL + #endif + + CBUFFER_START(c_per_object) + UNIFORM mat4 world_view_proj; + UNIFORM mat4 world; + UNIFORM mat4 inv_world; + UNIFORM mat4 bounding_volume; + CBUFFER_END + + #if defined(STAGE_VERTEX) + layout (location = POSITION0) in vec4 in_position; + + out vec4 v_w; + + #if defined(NEEDS_TANGENT_SPACE) && !defined(TANGENT_BASIS_FROM_SURFACE) + flat out vec3 v_tsm0; + flat out vec3 v_tsm1; + flat out vec3 v_tsm2; + #endif + + #if defined(HAS_NORMAL_THRESHOLD) + flat out vec3 v_decal_box_y; + #endif + + void main() { + vec4 p = in_position * world_view_proj; + v_w = encode_world_pos(p); + + #if defined(NEEDS_TANGENT_SPACE) && !defined(TANGENT_BASIS_FROM_SURFACE) + tspace_transpose( + v_tsm0, v_tsm1, v_tsm2, + normalize(vec3(world[0].x,world[1].x,world[2].x)), + normalize(vec3(world[0].z,world[1].z,world[2].z)), + -normalize(vec3(world[0].y,world[1].y,world[2].y)) + ); + #endif + + #if defined(HAS_NORMAL_THRESHOLD) + v_decal_box_y = -normalize(vec3(world[0].y,world[1].y,world[2].y)); + #endif + + gl_Position = p; + } + #elif defined(STAGE_FRAGMENT) + uniform highp usampler2D linear_depth; + #if defined(NEEDS_GBUFFER_NORMAL) + DECLARE_SAMPLER_2D(gbuffer1); + #endif + + in highp vec4 v_w; + + #if defined(HAS_NORMAL_THRESHOLD) + flat in highp vec3 v_decal_box_y; + #endif + + #if defined(GBUFFER_PASS) + #if defined(NEEDS_TANGENT_SPACE) && !defined(TANGENT_BASIS_FROM_SURFACE) + flat in highp vec3 v_tsm0; + flat in highp vec3 v_tsm1; + flat in highp vec3 v_tsm2; + #endif + + layout(location = 0) out mediump vec4 out_gbuffer0; + layout(location = 1) out mediump vec4 out_gbuffer1; + + #define out_base_color_opacity out_gbuffer0.a + #define out_normal_opacity out_gbuffer1.a + + void main() { + highp vec2 uv = gl_FragCoord.xy/back_buffer_size; + highp float depth = uintBitsToFloat(texture(linear_depth, uv).r); + highp vec3 wp = decode_world_pos(v_w, depth); + highp vec3 op = (vec4(wp, 1.0) * inv_world).xyz; + + // Discard pixel out of decal volume + if (!(all(greaterThan(op, vec3(bounding_volume[0].x,bounding_volume[1].x,bounding_volume[2].x))) && + all(lessThan(op,vec3(bounding_volume[0].y,bounding_volume[1].y,bounding_volume[2].y))))) + discard; + + GraphManualChannels params; + GraphResults graph; + + // Compute graph input data + highp vec2 decal_uv = compute_decal_uv(op, bounding_volume); + GRAPH_PARAM(params, decal_uv) = decal_uv; + + // Compute eye vector + highp vec3 eye_vector = camera_pos - wp; + #if defined(NEEDS_EYE_VECTOR) + GRAPH_PARAM(params, eye_vector) = eye_vector; + #endif + + #if defined(NEEDS_GBUFFER_NORMAL) + highp vec3 wn = gbuffer_decode_normal(TEX2D(gbuffer1, uv)); + #endif + + #if defined(NEEDS_TANGENT_SPACE) + #if defined(TANGENT_BASIS_FROM_SURFACE) + mat3 surface_tbn = tangent_frame(wn, eye_vector, decal_uv); + tspace_transpose( + GRAPH_PARAM(params, tsm0), + GRAPH_PARAM(params, tsm1), + GRAPH_PARAM(params, tsm2), + surface_tbn[0], + -surface_tbn[1], + surface_tbn[2] + ); + #else + GRAPH_PARAM(params, tsm0) = v_tsm0; + GRAPH_PARAM(params, tsm1) = v_tsm1; + GRAPH_PARAM(params, tsm2) = v_tsm2; + #endif + #endif + + graph_evaluate(graph, params); + + #if defined(HAS_MASK) + #if defined(HAS_MASK_THRESHOLD) + float mask_threshold = graph.mask_threshold; + #else + float mask_threshold = 0.5; + #endif + if (graph.mask <= mask_threshold) + discard; + #endif + + #if defined(HAS_NORMAL_THRESHOLD) + if (dot(v_decal_box_y, wn) < graph.normal_threshold) + discard; + #endif + + // Base color + #if defined(HAS_BASE_COLOR) + out_base_color = gbuffer_encode_base_color(graph.base_color); + #if defined(HAS_BASE_COLOR_OPACITY) + out_base_color_opacity = graph.base_color_opacity; + #else + out_base_color_opacity = 1.0; + #endif + #endif + + // Normal + #if defined(HAS_NORMAL) + highp vec3 graph_normal = graph.normal; + #if !defined(WORLD_SPACE_NORMAL) + graph_normal = rotate_vector3( + graph_normal, + GRAPH_PARAM(params, tsm0), + GRAPH_PARAM(params, tsm1), + GRAPH_PARAM(params, tsm2) + ); + #endif + + out_normal = gbuffer_encode_normal(graph_normal); + #if defined(HAS_NORMAL_OPACITY) + out_normal_opacity = graph.normal_opacity; + #else + out_normal_opacity = 1.0; + #endif + #endif + } + #elif defined(EMISSIVE_PASS) + layout(location = 0) out mediump vec4 out_color; + + void main() { + highp vec2 uv = gl_FragCoord.xy/back_buffer_size; + highp float depth = uintBitsToFloat(texture(linear_depth, uv).r); + highp vec3 wp = decode_world_pos(v_w, depth); + highp vec3 op = (vec4(wp, 1.0) * inv_world).xyz; + + // Discard pixel out of decal volume + if (!(all(greaterThan(op, vec3(bounding_volume[0].x,bounding_volume[1].x,bounding_volume[2].x))) && + all(lessThan(op,vec3(bounding_volume[0].y,bounding_volume[1].y,bounding_volume[2].y))))) + discard; + + GraphManualChannels params; + GraphResults graph; + + highp vec2 decal_uv = compute_decal_uv(op, bounding_volume); + GRAPH_PARAM(params, decal_uv) = decal_uv; + graph_evaluate(graph, params); + + #if defined(HAS_MASK) + #if defined(HAS_MASK_THRESHOLD) + float mask_threshold = graph.mask_threshold; + #else + float mask_threshold = 0.5; + #endif + if (graph.mask <= mask_threshold) + discard; + #endif + + #if defined(HAS_NORMAL_THRESHOLD) + highp vec3 wn = gbuffer_decode_normal(TEX2D(gbuffer1, uv)); + if (dot(v_decal_box_y, wn) < graph.normal_threshold) + discard; + #endif + + out_color = vec4(graph.emissive, 0); + } + #endif + #endif + """ + + hlsl = """ + #if defined(INSTANCED) && defined(RENDERER_D3D11) + #define HAS_INSTANCING + #endif + + #if defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL) + #define NEEDS_TANGENT_SPACE + #endif + + #if defined(HAS_NORMAL_THRESHOLD) || (defined(NEEDS_TANGENT_SPACE) && defined(TANGENT_BASIS_FROM_SURFACE)) + #define NEEDS_GBUFFER_NORMAL + #endif + + #if defined(GBUFFER_PASS) || defined(EMISSIVE_PASS) + DECLARE_SAMPLER_2D(linear_depth); + #if defined(NEEDS_GBUFFER_NORMAL) + DECLARE_SAMPLER_2D(gbuffer1); + #if defined(EMISSIVE_PASS) + #else + DECLARE_SAMPLER_2D(normals_copy); + #endif + #endif + #endif + + struct VS_INPUT { + float4 position : POSITION; + }; + + inline void tspace_transpose(out float3 tsm0, out float3 tsm1, in float3 t, in float3 b, in float3 n) { + tsm0 = float3(t.x, b.x, n.x); + tsm1 = float3(t.y, b.y, n.y); + } + + #if defined(HAS_NORMAL_THRESHOLD) + #define DECAL_BOX_Y(o) o.decal_box_y.xyz + #if defined(FADE_OVER_TIME) + #define FADE_ALPHA(o) o.decal_box_y.w + #endif + #else + #if defined(FADE_OVER_TIME) + #define FADE_ALPHA(o) o.fade_alpha + #endif + #endif + + struct PS_INPUT { + float4 position : SV_POSITION; + + #if defined(GBUFFER_PASS) || defined(EMISSIVE_PASS) + float4 w : TEXCOORD0; + #endif + + #if defined(HAS_NORMAL_THRESHOLD) + #if defined(FADE_OVER_TIME) + NO_INTERPOLATION float4 decal_box_y : TEXCOORD1; + #else + NO_INTERPOLATION float3 decal_box_y : TEXCOORD1; + #endif + #else + #if defined(FADE_OVER_TIME) + NO_INTERPOLATION float fade_alpha: TEXCOORD1; + #endif + #endif + + #if defined(NEEDS_TANGENT_SPACE) && !defined(TANGENT_BASIS_FROM_SURFACE) + NO_INTERPOLATION float3 tsm0 : TEXCOORD2; + NO_INTERPOLATION float3 tsm1 : TEXCOORD3; + // NO_INTERPOLATION float3 tsm2 : TEXCOORD4; // cross in ps to calculate this instead of exporting + #endif + + + #if defined(HAS_INSTANCING) + float4 instance_wireframe_color : COLOR0; + #endif + }; + + CBUFFER_START(c_per_object) + float4x4 world_view_proj; + float4x4 world; + float4x4 inv_world; + float4x4 bounding_volume; + float4 dev_wireframe_color; + #if defined(FADE_OVER_TIME) + float start_time; + float life_time; + #endif + #if defined(COLOR_ALPHA_FADE) + float alpha; + #endif + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + #if defined(HAS_INSTANCING) + Buffer idata; + float ioffset; + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input + #if defined(HAS_INSTANCING) + , uint instance_id : SV_InstanceId + #endif + ) + { + PS_INPUT o; + + + #if defined(HAS_INSTANCING) + uint offset = (uint)ioffset + instance_id * IDATA_STRIDE; + world[0] = idata.Load(offset + IDATA_world + 0); + world[1] = idata.Load(offset + IDATA_world + 1); + world[2] = idata.Load(offset + IDATA_world + 2); + world[3] = idata.Load(offset + IDATA_world + 3); + + #if defined(WIREFRAME_PASS) + o.instance_wireframe_color = idata.Load(offset + IDATA_dev_wireframe_color); + #endif + #endif + + #if defined(WIREFRAME_PASS) + o.position = mul(input.position, world_view_proj); + return o; + #else + #if defined(NEEDS_TANGENT_SPACE) && !defined(TANGENT_BASIS_FROM_SURFACE) + //tspace_transpose( + // o.tsm0, o.tsm1, o.tsm2, + // normalize(world._m00_m01_m02), + // normalize(world._m20_m21_m22), + // -normalize(world._m10_m11_m12) + // ); + tspace_transpose( + o.tsm0, o.tsm1, + normalize(world._m00_m01_m02), + normalize(world._m20_m21_m22), + -normalize(world._m10_m11_m12) + ); + #endif + + #if defined(HAS_NORMAL_THRESHOLD) + DECAL_BOX_Y(o) = -normalize(world._m10_m11_m12); + //o.decal_box_y = -normalize(world._m10_m11_m12); + #endif + + #if defined(FADE_OVER_TIME) + float fade_alpha = saturate(((start_time + life_time) - time) / 2.0); + //o.fade_alpha = fade_alpha * fade_alpha; + FADE_ALPHA(o) = fade_alpha * fade_alpha; + #endif + + + float4 p = mul(input.position, world_view_proj); + float4 view_space = p / p.w; + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + o.w = encode_world_pos(o.position); + + return o; + #endif + } + + #if defined(GBUFFER_PASS) + #define BASE_COLOR_OPACITY(gbuffer) gbuffer.buffer0.a + #define NORMAL_OPACITY(gbuffer) gbuffer.buffer1.a + + struct PS_OUTPUT { + half4 buffer0 : SV_TARGET0; + half4 buffer1 : SV_TARGET1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) + { + PS_OUTPUT o; + + half2 uv = input.position.xy/back_buffer_size; + float depth = gbuffer_decode_depth(TEX2D(linear_depth, uv)); + float3 wp = decode_world_pos(input.w, depth); + float3 op = mul(float4(wp, 1.0f), inv_world).xyz; + + // Discard pixel out of decal volume + bool3 inside = ((op > bounding_volume._m00_m01_m02) && (op < bounding_volume._m10_m11_m12)); + if(!all(inside)) + discard; + + GraphPixelParams params; + GraphPixelResults graph; + + // Compute graph input data + float2 decal_uv = compute_decal_uv(op, bounding_volume); + GRAPH_PIXEL_PARAM(params, decal_uv) = decal_uv; + + // Compute eye vector + float3 eye_vector = camera_pos - wp; + #if defined(NEEDS_EYE_VECTOR) + GRAPH_PIXEL_PARAM(params, eye_vector) = eye_vector; + #endif + + #if defined(NEEDS_GBUFFER_NORMAL) + float4 normals = TEX2D(normals_copy, uv); + float3 wn = gbuffer_decode_normal(normals); + float old_roughness = normals.a; + #endif + + #if defined(NEEDS_TANGENT_SPACE) + #if defined(TANGENT_BASIS_FROM_SURFACE) + float3x3 surface_tbn = tangent_frame(wn, eye_vector, decal_uv); + tspace_transpose( + GRAPH_PIXEL_PARAM(params, tsm0), + GRAPH_PIXEL_PARAM(params, tsm1), + GRAPH_PIXEL_PARAM(params, tsm2), + surface_tbn._m00_m01_m02, + -surface_tbn._m10_m11_m12, + surface_tbn._m20_m21_m22 + ); + #else + GRAPH_PIXEL_PARAM(params, tsm0) = input.tsm0; + GRAPH_PIXEL_PARAM(params, tsm1) = input.tsm1; + //GRAPH_PIXEL_PARAM(params, tsm2) = input.tsm2; + GRAPH_PIXEL_PARAM(params, tsm2) = cross(input.tsm0, input.tsm1); + + #endif + #endif + + #if defined(NEEDS_OBJECT_POSITION) + GRAPH_PIXEL_PARAM(params, object_position) = op; + #endif + + #if defined(NEEDS_WORLD_SPACE_POSITION) + GRAPH_PIXEL_PARAM(params, world_position) = wp; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_MASK) + #if defined(HAS_MASK_THRESHOLD) + float mask_threshold = graph.mask_threshold; + #else + float mask_threshold = 0.5f; + #endif + if (graph.mask <= mask_threshold) + discard; + #endif + + float threshold_fade = 1.0; + #if defined(HAS_NORMAL_THRESHOLD) + #if defined(SOFT_NORMAL_THRESHOLD) + //threshold_fade = saturate((dot(input.decal_box_y, wn) - graph.normal_threshold) / (1.0 - graph.normal_threshold)); + threshold_fade = saturate((dot(DECAL_BOX_Y(input), wn) - graph.normal_threshold) / (1.0 - graph.normal_threshold)); + #else + //if (dot(input.decal_box_y, wn) < graph.normal_threshold) + if (dot(DECAL_BOX_Y(input), wn) < graph.normal_threshold) + discard; + #endif + #endif + #if defined(COLOR_ALPHA_FADE) + threshold_fade *= alpha; + #endif + + #if defined(FADE_OVER_TIME) + //threshold_fade *= input.fade_alpha; + threshold_fade *= FADE_ALPHA(input); + #endif + + // Base color + #if defined(BASE_COLOR_NORMAL_PASS) + #if defined(HAS_BASE_COLOR) + BASE_COLOR(o) = gbuffer_encode_base_color(graph.base_color); + #if defined(HAS_BASE_COLOR_OPACITY) + BASE_COLOR_OPACITY(o) = graph.base_color_opacity * threshold_fade; + #else + BASE_COLOR_OPACITY(o) = threshold_fade; + #endif + #endif + + // Normal + #if defined(HAS_NORMAL) + float3 graph_normal = graph.normal; + #if !defined(WORLD_SPACE_NORMAL) + graph_normal = rotate_vector3( + graph_normal, + GRAPH_PIXEL_PARAM(params, tsm0), + GRAPH_PIXEL_PARAM(params, tsm1), + GRAPH_PIXEL_PARAM(params, tsm2) + ); + #endif + + NORMAL(o) = gbuffer_encode_normal(float3(graph_normal)); + #if defined(HAS_NORMAL_OPACITY) + NORMAL_OPACITY(o) = graph.normal_opacity * threshold_fade; + #else + NORMAL_OPACITY(o) = threshold_fade; + #endif + #endif + #elif defined(METALLIC_ROUGHNESS_PASS) + #if defined(HAS_METALLIC_ROUGHNESS_MASK) + #if defined(HAS_METALLIC_ROUGHNESS_THRESHOLD) + float metallic_roughness_threshold = graph.metallic_roughness_threshold; + #else + float metallic_roughness_threshold = 0.5f; + #endif + if (graph.metallic_roughness_mask * threshold_fade <= metallic_roughness_threshold) + discard; + #endif + + #if defined(HAS_METALLIC) + METALLIC(o) = gbuffer_encode_metallic_mask(graph.metallic); + #endif + + #if defined(HAS_ROUGHNESS) + #if defined(FADE_OVER_TIME) + //ROUGHNESS(o) = gbuffer_encode_roughness(lerp(old_roughness, graph.roughness, input.fade_alpha)); + ROUGHNESS(o) = gbuffer_encode_roughness(lerp(old_roughness, graph.roughness, FADE_ALPHA(input))); + #else + ROUGHNESS(o) = gbuffer_encode_roughness(graph.roughness); + #endif + #endif + #endif + + return o; + } + #elif defined(EMISSIVE_PASS) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + half2 uv = input.position.xy/back_buffer_size; + float depth = gbuffer_decode_depth(TEX2D(linear_depth, uv)); + float3 wp = decode_world_pos(input.w, depth); + float3 op = mul(float4(wp, 1.0f), inv_world).xyz; + + // Discard pixel out of decal volume + bool3 inside = ((op > bounding_volume._m00_m01_m02) && (op < bounding_volume._m10_m11_m12)); + if(!all(inside)) + discard; + + GraphPixelParams params; + GraphPixelResults graph; + + // Compute graph input data + float2 decal_uv = compute_decal_uv(op, bounding_volume); + GRAPH_PIXEL_PARAM(params, decal_uv) = decal_uv; + + #if defined(NEEDS_OBJECT_POSITION) + GRAPH_PIXEL_PARAM(params, object_position) = op; + #endif + + #if defined(NEEDS_WORLD_SPACE_POSITION) + GRAPH_PIXEL_PARAM(params, world_position) = wp; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_MASK) + #if defined(HAS_MASK_THRESHOLD) + float mask_threshold = graph.mask_threshold; + #else + float mask_threshold = 0.5f; + #endif + if (graph.mask <= mask_threshold) + discard; + #endif + + float threshold_fade = 1.0; + #if defined(HAS_NORMAL_THRESHOLD) + float3 wn = gbuffer_decode_normal(TEX2D(gbuffer1, uv)); + #if defined(SOFT_NORMAL_THRESHOLD) + threshold_fade = saturate((dot(input.decal_box_y, wn) - graph.normal_threshold) / (1.0 - graph.normal_threshold)); + #else + if (dot(input.decal_box_y, wn) < graph.normal_threshold) + discard; + #endif + #endif + #if defined(COLOR_ALPHA_FADE) + threshold_fade *= alpha; + #endif + + return float4(graph.emissive * threshold_fade, 0.f); + } + #elif defined(WIREFRAME_PASS) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if defined(HAS_INSTANCING) + return input.instance_wireframe_color; + #else + return dev_wireframe_color; + #endif + } + #endif + """ + } + } +} diff --git a/vmf_source/core/stingray_renderer/output_nodes/light_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/light_base.shader_node new file mode 100644 index 0000000..f2cf195 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/light_base.shader_node @@ -0,0 +1,434 @@ +group = "Output" +display_name = "Light Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "c09a9bb3-95e4-41b6-92af-33c5865fc691" = { + name = "light_color" + is_required = false + display_name = "Light Color" + type = { vector3: ["HAS_LIGHT_COLOR"] } + domain = "pixel" + } +} + +options = { +} + +ui = [ +] + +render_state = { + light_source = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + z_enable = "true" + z_write_enable = "false" + z_func = "greater_equal" + + cull_mode = "cull_ccw" + + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_one" + src_blend = "blend_one" + } + } + + light_shadow_mask_write = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + z_enable = "true" + z_write_enable = "false" + z_func = "greater_equal" + cull_mode = "cull_ccw" + + blend_enable = "false" + + "defined(WRITE_R)" = { + write_mask0 = "red" + } + "defined(WRITE_G)" = { + write_mask0 = "green" + } + "defined(WRITE_B)" = { + write_mask0 = "blue" + } + "defined(WRITE_A)" = { + write_mask0 = "alpha" + } + } + } + + light_stencil = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + z_enable = "true" + z_write_enable = "false" + z_func = "less_equal" + + cull_mode = "cull_none" + + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + + stencil_enable = "true" + stencil_func = "always" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_ref = "0x0" + stencil_func_back_side = "always" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail = "stencil_op_incr" + stencil_z_fail_back_side = "stencil_op_decr" + } + } +} + +channels = { + vertex_position = { type = "float4" domain = "vertex" } + light_vector = { type="float3" domain="pixel" } +} + +permutation_sets = { + shadow_modifiers = [ + { if: "light_cast_shadows() == true" define: ["SHADOW_MAPPING"] } + { default = true } + ] + + default = [ + { if: "light_type() == omni" define: ["OMNI"] permute_with: "shadow_modifiers" } + { if: "light_type() == spot" define: ["SPOT"] permute_with: "shadow_modifiers" } + { if: "light_type() == box" define: ["BOX"] permute_with: "shadow_modifiers" } + ] +} + +shader_contexts = { + default = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM)" permute_with: "default" } + ] + + passes = [ + // TODO: skin shading + { code_block="light_source" render_state="light_source" } + ] + } +} + +code_blocks = { + light_source = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/lighting_common#brdf", + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_bias", + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map_filtering", + "core/stingray_renderer/shader_libraries/common#taa_offsets" + ] + + samplers = { + gbuffer0 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "gbuffer0" + type = "2d" + } + + gbuffer1 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "gbuffer1" + type = "2d" + } + + linear_depth = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "linear_depth" + type = "2d" + } + + local_lights_shadow_atlas = { + sampler_state = "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map" + slot_name = "shadow_map_sampler" + type = "2d" + } + + fullscreen_shadow_mask = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "fullscreen_shadow_mask" + type = "2d" + } + } + + code = { + hlsl = """ + DECLARE_SAMPLER_2D(gbuffer0); + DECLARE_SAMPLER_2D(gbuffer1); + DECLARE_SAMPLER_2D(linear_depth); + + #if defined(SHADOW_MASK) + DECLARE_SAMPLER_2D(fullscreen_shadow_mask); + #endif + + #if defined(SHADOW_MAPPING) && (defined(D3D11) || defined(D3D12) || defined(GNM)) + DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas); + #endif + + struct VS_INPUT { + float4 position : POSITION; + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if !defined(STENCIL_MARK) + float4 w : TEXCOORD1; + #endif + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c0) + #if defined(SPOT) || defined(BOX) || defined(NEEDS_WORLD_POSE) + float4x4 world; + #endif + #if defined(SPOT) || defined(BOX) || defined(NEEDS_INVERSE_WORLD_POSE) + float4x4 inv_world; + #endif + float4x4 world_view_proj; + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + CBUFFER_START(light) + float3 light_position; + float3 light_color; + #if defined(OMNI) || defined(SPOT) || defined(PYRAMID) + // start, 1.f/(end-start), exp + float3 light_falloff; + #endif + + #if defined(SPOT) + // scale + bias + float2 light_spot_falloff; + #endif + + #if defined(SHADOW_MAPPING) + #if defined(OMNI) + float4x4 world_to_shadow_maps[6]; + #else + float4x4 world_to_shadow_map; + #endif + #endif + + #if defined(SHADOW_MASK) + float4 shadow_mask; + #endif + + #if defined(PYRAMID) + float3 face_normal_right; + float3 face_normal_left; + float3 face_normal_up; + float3 face_normal_down; + #endif + + #if defined(BOX) + float3 light_box_min; + float3 light_box_max; + #endif + + float3 light_proxy_scale; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + GraphVertexParams params; + GraphVertexResults results; + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + float4 position; + #if defined(BOX) + float3 p = input.position.xyz * light_proxy_scale; + p += (light_box_max + light_box_min) * 0.5; + position = mul(float4(p, 1), world_view_proj); + #else + position = mul(float4(input.position.xyz * light_proxy_scale, 1), world_view_proj); + #endif + + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + o.position = position; + #if !defined(STENCIL_MARK) + #if defined(SHADOW_MAPPING) + // When using temporal antialiasing we try to cancel out the jitter + // that was introduced in the depth buffer. This is to minimize self occlusion + // that can arrise when performing a depth test beween the jittered depth buffer + // and a non jittered shadow map. + float4 tmp = o.position; + float4 view_space = tmp / tmp.w; + view_space.xy -= get_vs_halton_offset(frame_number); + tmp = view_space * tmp.w; + o.w = encode_world_pos(tmp); + #else + o.w = encode_world_pos(o.position); + #endif + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + struct PS_OUTPUT { + half4 base : SV_TARGET0; + }; + + #if defined(STENCIL_MARK) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + return half4(0,0,0,0); + } + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input + #if defined(GL2) + , float2 wpos : VPOS + #endif + ) + { + PS_OUTPUT o; + o.base = half4(0,0,0,0); + + half2 uv = input.position.xy / back_buffer_size; + float d = gbuffer_decode_depth(TEX2D(linear_depth, uv)); + + float3 wp = decode_world_pos(input.w, d); + float3 V = normalize(camera_world._m30_m31_m32 - wp); + float3 L = float3(0,0,1); + float attn = 1.f; + + #if defined(OMNI) || defined(SPOT) + L = light_position - wp; + float len_L = length(L) + 0.00001f; + attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + L *= 1.0/len_L; + + float normalized_distance = (len_L - light_falloff.x) * light_falloff.y; + [branch] + if (normalized_distance > 1.0f) discard; + + #if defined(OMNI) && defined(SHADOW_MAPPING) + // The shadows from the faces of an onmni light are populated in the following order and directions. + // float3(-1.0f, 0.0f, 0.0f), + // float3( 1.0f, 0.0f, 0.0f), + // float3( 0.0f, -1.0f, 0.0f), + // float3( 0.0f, 1.0f, 0.0f), + // float3( 0.0f, 0.0f, -1.0f) + // float3( 0.0f, 0.0f, 1.0f), + + // Based on the biggest component of the vector from the shaded position to the light source and its sign, chose the correct + // shadow map index to get the correct world position to shadow map matrix. + const float3 shadow_L = -L; + const int3 is_positive = shadow_L > 0; + const float3 abs_shadow_L = abs(shadow_L); + int test_index = (abs_shadow_L.x > abs_shadow_L.y && abs_shadow_L.x > abs_shadow_L.z) ? 0 + is_positive[0]: (abs_shadow_L.y > abs_shadow_L.z) ? 2 + is_positive[1] : 4 + is_positive[2]; + + // On the cpu side, the whole matrix will be full of zeroes if the face is not casting any shadows. Just test the first element. + const float shadow_active = world_to_shadow_maps[uint(test_index)]._m00 != 0.0f? 1.0f : 0.0f; + float4 ls = mul(float4(wp, 1), world_to_shadow_maps[uint(test_index)]); + #elif defined(SPOT) + float spot_attenuation = spot_angle_attenuation(dot(-L, world._m10_m11_m12), light_spot_falloff.x, light_spot_falloff.y); + if (spot_attenuation == 0.0f) discard; + attn *= spot_attenuation; + #endif + #elif defined(BOX) + L = -world._m10_m11_m12; + float3 op = mul(float4(wp, 1), inv_world).xyz; + float3 containment = (op > light_box_min) * (op < light_box_max); + float mask = dot(containment, containment) == 3; + attn = light_attenuation(op.y, light_box_min.y, 1.f / (light_box_max.y - light_box_min.x)) * mask; + #endif + + #if defined(SHADOW_MAPPING) + #if !defined(OMNI) + float4 ls = mul(float4(wp,1), world_to_shadow_map); + #endif + + #if defined(D3D11) || defined(D3D12) || defined(GNM) + ls.xyz /= ls.w; + ls.z = apply_shadow_depth_comparison_bias(d, ls.z, local_shadow_map_bias); + float2 sm_resolution; + local_lights_shadow_atlas.tex.GetDimensions(sm_resolution.x, sm_resolution.y); + float2 tscale = float2(1.f / sm_resolution.x, 1.f / sm_resolution.y); + half shadow = shadow_intensity_2d(local_lights_shadow_atlas, sm_resolution, ls.xy, ls.z); + #if defined(OMNI) + shadow = (1.0f - shadow_active) + (shadow * 0.25f * shadow_active); + #else + shadow = (shadow * 0.25); + #endif + #endif + #endif + #if defined(SHADOW_MASK) + attn *= dot(TEX2D(fullscreen_shadow_mask, uv), shadow_mask); + #elif defined(SHADOW_MAPPING) + attn *= shadow; + #endif + + #if defined(WRITE_R) || defined(WRITE_G)|| defined(WRITE_B)|| defined(WRITE_A) + o.base = shadow * 0.25; + #else + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_PARAM(params, light_vector) = L; + + #if defined(HAS_LIGHT_COLOR) + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + float3 light_col = graph.light_color; + #else + float3 light_col = light_color; + #endif + + half4 gbuffer_0 = TEX2D(gbuffer0, uv); + half4 gbuffer_1 = TEX2D(gbuffer1, uv); + + float3 N = normalize(gbuffer_decode_normal(gbuffer_1)); + half roughness = gbuffer_decode_roughness(gbuffer_1); + + float3 base_color = gbuffer_decode_base_color(gbuffer_0); + half metallic = gbuffer_decode_metallic_mask(gbuffer_0); + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + float3 specular_color = lerp(float3(0.04,0.04,0.04), base_color, metallic); + float3 diffuse_color = lerp(base_color, new_half3(0,0,0), metallic); + + float3 acc_diff = float3(0,0,0); + float3 acc_spec = float3(0,0,0); + + bsdf(L, V, N, light_col, diffuse_color, specular_color, roughness, attn, acc_diff, acc_spec); + + o.base = half4(acc_diff + acc_spec, 0); + #endif + + return o; + } + #endif + """ + } + } +} diff --git a/vmf_source/core/stingray_renderer/output_nodes/particle_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/particle_base.shader_node new file mode 100644 index 0000000..c97551a --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/particle_base.shader_node @@ -0,0 +1,1105 @@ +group = "Output/Particles" +display_name = "Blend Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +// TODO: disable sun if it's disabled in environment settings. +// TODO: options to disable shadows, local lights, sun_light. + +inputs = { + "e1d24468-d03e-4884-9b0d-dba23aaa94d6" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "0752e133-0c01-4fc4-b45a-a629cad4f850" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "6fd5ebe2-4d15-4ecc-abad-ab9731ea861a" = { + name = "fade_range" + is_required = false + display_name = "Angle Fade Range" + type = { vector2: ["HAS_ANGLE_FADE" "NEEDS_EYE_VECTOR"] } + domain = "pixel" + } +} + +options = { + "e6347231-6647-4bc3-a1d0-f24e1b3e0562" = "EXTERNAL_ROTATION" + "914232a9-5352-43d7-a877-cedfe798541d" = "TANGENT_LOCKED" + "e8e86308-83cd-47a1-ab84-82776b3cf0ca" = "SCREEN_SPACE" + "9775d054-ef17-4ca4-b5a4-8751c6e1eec6" = "PIVOT" + "aaebf627-efab-4c86-8244-359d29d00f33" = "ROTATION" + "47a74b09-1932-40d4-ab90-0ec090fb9643" = "BLEND_ADDITIVE" + "e267562a-65dc-4547-a9fa-6a605b451cae" = "BLEND_PREMULTIPLIED_ALPHA" + "4a42f2e1-1069-4afe-a93f-c7558572780f" = "EMISSIVE_PARTICLES" + "5e380f16-5cc6-482b-9df6-af0d2b3bda3c" = "HAS_CUSTOM_FOV" + "fea9af88-d417-433a-b475-346b6b03e249" = "HIGH_QUALITY" +} + +ui = [ + { + type = "drop_down" + display_name = "Resolution" + options = { + "Low" = "00000000-0000-0000-0000-000000000000" + "Full" = "fea9af88-d417-433a-b475-346b6b03e249" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Blend Mode" + options = { + "Alpha Blend" = "00000000-0000-0000-0000-000000000000" + "Additive Blend" = "47a74b09-1932-40d4-ab90-0ec090fb9643" + "Pre-Multiplied Alpha Blend" = "e267562a-65dc-4547-a9fa-6a605b451cae" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Turn-up Algorithms" + options = { + "Disabled" = "00000000-0000-0000-0000-000000000000" + "Screen Space" = "e8e86308-83cd-47a1-ab84-82776b3cf0ca" + "Aligned" = "914232a9-5352-43d7-a877-cedfe798541d" + "Facing" = "e6347231-6647-4bc3-a1d0-f24e1b3e0562" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Light Calculation Options" + options = { + "Per-Vertex Lighting" = "00000000-0000-0000-0000-000000000000" + "Per-Pixel Shadows" = "22f9eada-ca3f-4261-8ee1-530a7bd828ed" + "Per-Pixel Lighting" = "9514fa9b-ba05-4587-9cb1-663f750a3d5d" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Pivot" option = "9775d054-ef17-4ca4-b5a4-8751c6e1eec6" } + { type = "checkbox" display_name = "Rotation" option = "aaebf627-efab-4c86-8244-359d29d00f33" } + { type = "checkbox" display_name = "Emissive particles" option = "4a42f2e1-1069-4afe-a93f-c7558572780f" } + { type = "checkbox" display_name = "Custom FOV" option = "5e380f16-5cc6-482b-9df6-af0d2b3bda3c" } +] + +render_state = { + // TODO: does not work. + shadow_caster = { + inherit: ["core/stingray_renderer/shader_libraries/common#shadow_caster"] + state: { + z_write_enable = "false" + + write_mask0 = "red" + cull_mode = "cull_none" + blend_enable = "true" + + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_color" + } + } + + opacity = { + inherit: ["core/stingray_renderer/shader_libraries/common#opacity"] + state: { + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_alpha" + + "defined(SCREEN_SPACE)": { + cull_mode = "cull_none" + z_enable = "false" + } + "!defined(SCREEN_SPACE)": { + z_enable = "true" + } + "defined(LOW_RES)": { + write_mask0 = "red|green|blue|alpha" + write_mask1 = "red|green" + } + "!defined(LOW_RES)": { + write_mask0 = "red|green|blue" + } + } + } +} + +sampler_state = { + // TODO: inherit from lighting + shadow_map = { + inherit: ["core/stingray_renderer/shader_libraries/common#clamp_point"] + state: { + "on_platform(D3D11, D3D12)": { + comparison_func = "less" + filter = "comparison_min_mag_linear_mip_point" + } + "on_platform(GNM)": { + comparison_func = "less" + filter = "min_mag_mip_linear" + } + "on_platform(GL)": { + comparison_func = "less" + filter = "min_mag_linear" + } + } + } +} + +channels = { + "defined(PARTICLE_LIGHTING)": { + basis0 = { type = "float4" domains = ["vertex", "pixel"] } + basis1 = { type = "float4" domains = ["vertex", "pixel"] } + basis2 = { type = "float4" domains = ["vertex", "pixel"] } + back_lighting = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(HAS_ANGLE_FADE)": { + world_space_plane_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } + + "on_platform(GL)": { + vertex_corner_info = { type = "float2" semantic = "COLOR1" domain = "vertex" } + } + "!on_platform(GL)": { + vertex_corner_info = { type = "float2" semantic = "POSITION1" domain = "vertex" } + } + vertex_size = { type = "float2" semantic = "TEXCOORD7" domain = "vertex" } + "defined(ROTATION)": { + vertex_rotation = { type = "float" semantic = "TEXCOORD1" domains = ["vertex"] } + } + "defined(NEEDS_UV_SCALE)": { + "defined(NEEDS_UV_ANIMATION)": { + vertex_uv_data = { type = "float3" semantic = "TEXCOORD0" domains = ["vertex"] } + uv_frame = { type = "float" domains = ["vertex"] } + } + "!defined(NEEDS_UV_ANIMATION)": { + vertex_uv_data = { type = "float2" semantic = "TEXCOORD0" domains = ["vertex"] } + } + uv_scale = { type = "float2" domains = ["vertex"] } + } + "!defined(NEEDS_UV_SCALE)": { + "defined(NEEDS_UV_ANIMATION)": { + vertex_uv_frame = { type = "float" semantic = "TEXCOORD0" domains = ["vertex"] } + uv_frame = { type = "float" domains = ["vertex"] } + } + } + "defined(PIVOT)": { + vertex_pivot = { type = "float2" semantic = "TEXCOORD6" domains = ["vertex"] } + } + "defined(EXTERNAL_ROTATION) || defined(TANGENT_LOCKED)": { + vertex_tangent = { type = "float3" semantic = "TANGENT" domains = ["vertex"] } + "defined(EXTERNAL_ROTATION)": { + vertex_binormal = { type = "float3" semantic = "BINORMAL" domains = ["vertex"] } + } + } +} + +log_permutations = false +permutation_sets = { + vertex_modifiers = [ + ] + + default = [ + ] + + shadow_caster = [ + ] +} + +shader_contexts = { + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL) && render_cap(development) && render_setting(particle_visualization)" defines=["PARTICLE_DEBUG"] } + { if: "on_renderer(D3D11, D3D12, GNM, GL) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED" "LOCAL_LIGHTS"] } + { if: "on_renderer(D3D11, D3D12, GNM, GL)" defines=["LOCAL_LIGHTS"] } + ] + + passes = [ + { if: "defined(PARTICLE_DEBUG)" then: [ + { if: "defined(SCREEN_SPACE)" then: [ + { layer="hdr_transparent_screen_space" code_block="billboard" render_state="opacity" } + ] else: [ + { layer="hdr_transparent" code_block="billboard" render_state="opacity" } + ]} + ] else: [ + { if: "defined(LOW_RES_ENABLED) && !defined(HIGH_QUALITY)" then: [ + { if: "defined(SCREEN_SPACE)" then: [ + { if: "defined(EMISSIVE_PARTICLES)" then: [ + { layer="hdr_transparent_screen_space_low_res" code_block="billboard" render_state="opacity" defines=["LOW_RES"] } + ] else: [ + { layer="hdr_transparent_screen_space_low_res" code_block="billboard" render_state="opacity" defines=["PARTICLE_LIGHTING" "LOW_RES" "VS_FOG"] } + ]} + ] else: [ + { if: "defined(EMISSIVE_PARTICLES)" then: [ + { layer="hdr_transparent_low_res" code_block="billboard" render_state="opacity" defines=["LOW_RES"] } + ] else: [ + { layer="hdr_transparent_low_res" code_block="billboard" render_state="opacity" defines=["PARTICLE_LIGHTING" "LOW_RES" "VS_FOG"] } + ]} + ]} + ] else: [ + { if: "defined(SCREEN_SPACE)" then: [ + { if: "defined(EMISSIVE_PARTICLES)" then: [ + { layer="hdr_transparent_screen_space" code_block="billboard" render_state="opacity" } + ] else: [ + { layer="hdr_transparent_screen_space" code_block="billboard" render_state="opacity" defines=["PARTICLE_LIGHTING" "VS_FOG"] } + ]} + ] else: [ + { if: "defined(EMISSIVE_PARTICLES)" then: [ + { layer="hdr_transparent" code_block="billboard" render_state="opacity" } + ] else: [ + { layer="hdr_transparent" code_block="billboard" render_state="opacity" defines=["PARTICLE_LIGHTING" "VS_FOG"] } + ]} + ]} + ]} + ]} + ] + } +} + +code_blocks = { + particle_lighting = { + code=""" + #if defined(PARTICLE_LIGHTING) + #if defined(D3D11) || defined(GNM) + #define BACK_LIGHTING + #endif + + #if defined(SHADOW_RECEIVING) + #define CALCULATE_LIGHTING + #endif + #endif + """ + } + + billboard = { + language = "hlsl" + + include:[ + "particle_lighting" + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/common#fog", + "core/stingray_renderer/shader_libraries/lighting_common#brdf", + "core/stingray_renderer/shader_libraries/common#taa_offsets", + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_bias" + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map_filtering" + "core/stingray_renderer/shader_libraries/lighting_common#clustered_shading" + "core/stingray_renderer/shader_libraries/particle_lighting_common#radiosity_normal_mapping" + "core/stingray_renderer/shader_libraries/particle_lighting_common#particle_debug" ] + + instance_data = { + } + + stage_conditions = { + } + + samplers = { + "defined(DISTORTION)": { + hdr0 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr0" + type = "2d" + } + } + "defined(PARTICLE_LIGHTING)": { + global_diffuse_map = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "global_diffuse_map" + type = "cube" + } + } + "defined(PARTICLE_LIGHTING) && defined(SHADOW_RECEIVING)": { + sun_shadow_map = { + sampler_state = "shadow_map" + source = "resource_set" + slot_name = "sun_shadow_map" + type = "2d" + } + } + "defined(NEEDS_LINEAR_DEPTH)": { + "defined(LOW_RES)": { + linear_depth = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "linear_depth_div4" + type = "2d" + } + } + "!defined(LOW_RES)": { + linear_depth = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "linear_depth" + type = "2d" + } + } + } + "!defined(LOW_RES)": { + "defined(LOW_RES_ENABLED)": { + hdr_transparent_div4 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr_transparent_div4" + type = "2d" + } + + hdr_linear_depth_div4 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr_linear_depth_div4" + type = "2d" + } + } + } + } + + code = """ + #if defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL) + #define NEEDS_TANGENT_SPACE + #endif + + #if defined(NEEDS_PIXEL_DEPTH) || defined(PARTICLE_LIGHTING) || (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) || defined(LOW_RES) + #define PS_NEEDS_WP + #endif + + #if defined(NEEDS_LINEAR_DEPTH) + DECLARE_SAMPLER_2D(linear_depth); + #define HAS_LINEAR_DEPTH + #endif + + struct VS_INPUT { + float4 position : POSITION; + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(PS_NEEDS_WP) + float3 world_pos : TEXCOORD15; + #endif + #if defined(VS_FOG) + float4 fog_params : TEXCOORD16; + #endif + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_billboard) + #if defined(SCREEN_SPACE) + float4x4 proj; + float4x4 view; + #else + float4x4 view; + float4x4 view_proj; + #endif + #if defined(EMISSIVE_PARTICLES) + float emissive_particle_intensity; + #endif + CBUFFER_END + + CBUFFER_START(c_material_exports) + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) + { + PS_INPUT o; + + GraphVertexParams params; + GraphVertexResults results; + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + #if defined(EXTERNAL_ROTATION) + float3 y = GRAPH_VERTEX_DATA(input, vertex_tangent); + float3 x = GRAPH_VERTEX_DATA(input, vertex_binormal); + #elif defined(TANGENT_LOCKED) + float3 y = GRAPH_VERTEX_DATA(input, vertex_tangent); + float3 x = normalize(cross(normalize(input.position.xyz - camera_pos), y)); + #elif defined(SCREEN_SPACE) + float3 x = float3(1,0,0); + float3 y = float3(0,1,0); //float3(0,0,1); + #else + float3 x = view._m00_m10_m20; + float3 y = view._m02_m12_m22; + #endif + + #if defined(ROTATION) + float rotation = GRAPH_VERTEX_DATA(input, vertex_rotation); + float c = cos(rotation); + float s = sin(rotation); + float3 x_axis = x * c + y * s; + float3 y_axis = y * c - x * s; + #else + float3 x_axis = x; + float3 y_axis = y; + #endif + + float2 corner_info = GRAPH_VERTEX_DATA(input, vertex_corner_info); + float2 size = GRAPH_VERTEX_DATA(input, vertex_size); + #if defined(PIVOT) + float2 pivot = GRAPH_VERTEX_DATA(input, vertex_pivot); + float2 corner = corner_info * ( (1-(corner_info*0.5+0.5)) * size + corner_info * (pivot * size) ); + #else + float2 corner = corner_info * (size * 0.5); + #endif + + #if defined(SCREEN_SPACE) + float3 wp = input.position.xzy + (x_axis * corner.x + y_axis * corner.y) / float3(normalize(camera_unprojection.xz), 1); + float4 p = float4(wp, 1); //mul(float4(wp, 1), proj); + #else + float3 wp = input.position.xyz + (x_axis * corner.x + y_axis * corner.y); + #if defined(HAS_CUSTOM_FOV) + float4 p = mul(float4(wp, 1),camera_custom_fov_view_projection); + #else + float4 p = mul(float4(wp, 1), view_proj); + #endif + + // TODO: Do we need this for screen space? + #if defined(GBUFFER_PARTICLES) + float4 proj_pos = p / p.w; + proj_pos.xy += get_vs_halton_offset(frame_number); + p = proj_pos * p.w; + #endif + #endif + + #if defined(NEEDS_WORLD_SPACE_NORMAL) || defined(NEEDS_TANGENT_SPACE) || defined(PARTICLE_LIGHTING) + #if defined(SCREEN_SPACE) + float3 normal = view._m01_m11_m21; + #else + float3 normal = normalize(lerp(wp - input.position.xyz, -view._m01_m11_m21, 0.2)); + #endif + normal = mul(normal, (float3x3)view); + #endif + + #if defined(PARTICLE_LIGHTING) + float4 basis0 = float4(0,0,0,1); + float4 basis1 = float4(0,0,0,1); + float4 basis2 = float4(0,0,0,1); + float3 back_lighting = float3(0,0,0); + calc_basis_lighting(basis0, basis1, basis2, back_lighting, wp, normal, view, p); + GRAPH_VERTEX_PARAM(params, basis0) = basis0; + GRAPH_VERTEX_PARAM(params, basis1) = basis1; + GRAPH_VERTEX_PARAM(params, basis2) = basis2; + #if defined(BACK_LIGHTING) + GRAPH_VERTEX_PARAM(params, back_lighting) = back_lighting; + #endif + #endif + + #if defined(NEEDS_TANGENT_SPACE) || defined(HAS_ANGLE_FADE) + float3 n = cross(x_axis, y_axis); + #endif + #if defined(NEEDS_TANGENT_SPACE) + float3 tangent = x_axis; + float3 binormal = y_axis; + GRAPH_VERTEX_PARAM(params, tsm0).rgb = float3(tangent.x, binormal.x, n.x); + GRAPH_VERTEX_PARAM(params, tsm1).rgb = float3(tangent.y, binormal.y, n.y); + GRAPH_VERTEX_PARAM(params, tsm2).rgb = float3(tangent.z, binormal.z, n.z); + #endif + + #if defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_VERTEX_PARAM(params, world_space_normal).rgb = normal; + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp; + #endif + + // TOOD: Move this code to its own shader node! + #if defined(NEEDS_UV_SCALE) + #if defined(NEEDS_UV_ANIMATION) + GRAPH_VERTEX_PARAM(params, uv_frame) = GRAPH_VERTEX_DATA(input, vertex_uv_data).z; + #endif + GRAPH_VERTEX_PARAM(params, uv_scale) = GRAPH_VERTEX_DATA(input, vertex_uv_data).xy; + #elif defined(NEEDS_UV_ANIMATION) + GRAPH_VERTEX_PARAM(params, uv_frame) = GRAPH_VERTEX_DATA(input, vertex_uv_frame); + #endif + + #if defined(HAS_ANGLE_FADE) + GRAPH_VERTEX_PARAM(params, world_space_plane_normal) = n; + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + #if defined(PS_NEEDS_WP) + o.world_pos = wp; + #endif + + #if defined(VS_FOG) + const float3 view_dir = camera_world._m30_m31_m32 - wp.xyz; + const float3 camera_dir = camera_world._m10_m11_m12; + const float depth = dot(-view_dir, camera_dir); + o.fog_params = calc_fog_vs(wp, depth); + #endif + + o.position = p; + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + #if defined(GBUFFER_PARTICLES) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + GBUFFER_OUT ps_main(PS_INPUT input) { + GBUFFER_OUT o; + + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if defined(NEEDS_SCREEN_POS) + float2 screen_position = (input.position.xy / back_buffer_size); + GRAPH_PIXEL_PARAM(params, screen_pos) = screen_position; + #endif + + #if defined(PS_NEEDS_WP) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + + const float depth = dot(-view_dir, camera_dir); + #endif + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + discard; + #endif + + // Base color + float3 base_color = float3(0,0,0); + #if defined(HAS_BASE_COLOR) + base_color = graph.base_color; + #else + base_color = float3(0.5, 0.5, 0.5); + #endif + BASE_COLOR(o) = gbuffer_encode_base_color(base_color); + + // World space normal + #if defined(HAS_NORMAL) + #if !defined(WORLD_SPACE_NORMAL) + float3 tsm0 = GRAPH_PIXEL_DATA(input, tsm0).xyz; + float3 tsm1 = GRAPH_PIXEL_DATA(input, tsm1).xyz; + float3 tsm2 = GRAPH_PIXEL_DATA(input, tsm2).xyz; + float3 wn = rotate_vector3(graph.normal, tsm0, tsm1, tsm2); + #else + float3 wn = graph.normal; + #endif + #else + float3 wn = normalize((float3)GRAPH_PIXEL_DATA(input, world_space_normal).rgb); + #endif + + NORMAL(o) = gbuffer_encode_normal(wn); + + // Metallic + half metallic_ = 0.f; + #if defined(HAS_METALLIC) + metallic_ = graph.metallic; + #else + metallic_ = 0.f; + #endif + METALLIC(o) = gbuffer_encode_metallic_mask(metallic_); + + // Roughness + half roughness_ = 0.f; + #if defined(HAS_ROUGHNESS) + roughness_ = graph.roughness; + #else + roughness_ = 0.5; + #endif + ROUGHNESS(o) = gbuffer_encode_roughness(roughness_); + + // TODO: Velocity vector + VELOCITY(o) = encode_velocity(0.0); + + // TODO: Expose density + DENSITY(o) = 1.0; + + // Since we don't have baked lighting, we don't use this + // // Ambient Diffuse + // float3 ambient = rgbm_decode(TEXCUBELOD(global_diffuse_map, wn, 0)); + // AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(ambient); + + #if defined(HAS_AMBIENT_OCCLUSION) + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(graph.ambient_occlusion); + #else + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(1.f); + #endif + + return o; + } + #elif defined(DISTORTION) // TODO: avoid sample from objects infront of the "distortion" + DECLARE_SAMPLER_2D(hdr0); + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + DECLARE_SAMPLER_2D(hdr_transparent_div4); + DECLARE_SAMPLER_2D(hdr_linear_depth_div4); + #endif + + #ifdef LOW_RES + struct PS_OUTPUT { + half4 color : SV_TARGET0; + half4 depth : SV_TARGET1; + }; + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + #endif + { + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) || defined(NEEDS_SCREEN_POS) + #if defined(LOW_RES) + float2 screen_position = input.position.xy / (back_buffer_size * 0.25); + #else + float2 screen_position = (input.position.xy / back_buffer_size); + #endif + #endif + + #if defined(NEEDS_SCREEN_POS) + GRAPH_PIXEL_PARAM(params, screen_pos) = screen_position; + #endif + + #if defined(PS_NEEDS_WP) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + + const float depth = dot(-view_dir, camera_dir); + #endif + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + float3 normal = graph.distortion_normal; + normal.rg = normal.rg * 2 - 1; + half2 distortion = normal.xy / back_buffer_size; + + #if defined(HAS_OPACITY) + half opacity = saturate(graph.opacity); + #else + half opacity = 0.5; + #endif + + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + // Fade out high resolution particle if the low resolution particles is infront of the high resolution particle + float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_position).rg; + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + float background_depth = depth; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * max(depth_variance, 0.001))); + float alpha_modifier = TEX2D(hdr_transparent_div4, screen_position).a * (1.0 - T); + opacity *= 1.0 - alpha_modifier; + #endif + + float3 color = TEX2D(hdr0, screen_position + (distortion * graph.distortion_strength)).rgb; + + #if defined(HAS_BASE_COLOR) + color *= graph.base_color; + #endif + + #ifdef LOW_RES + PS_OUTPUT o; + o.color = half4(color * opacity, opacity); + float alpha_depth = depth * opacity; + o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0.0, opacity); + return o; + #else + #if defined(PARTICLE_DEBUG) + return DISTORTION_PARTICLES_DEBUG_COLOR; + #else + return half4(color * opacity, opacity); + #endif + #endif + } + #else + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + DECLARE_SAMPLER_2D(hdr_transparent_div4); + DECLARE_SAMPLER_2D(hdr_linear_depth_div4); + #endif + + #ifdef LOW_RES + struct PS_OUTPUT { + half4 color : SV_TARGET0; + half4 depth : SV_TARGET1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + #endif + { + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) || defined(NEEDS_SCREEN_POS) + #if defined(LOW_RES) + float2 screen_position = input.position.xy / (back_buffer_size * 0.25); + #else + float2 screen_position = (input.position.xy / back_buffer_size); + #endif + #endif + + #if defined(NEEDS_SCREEN_POS) + GRAPH_PIXEL_PARAM(params, screen_pos) = screen_position; + #endif + + #if defined(PS_NEEDS_WP) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + + const float depth = dot(-view_dir, camera_dir); + #endif + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY) + half opacity = saturate(graph.opacity); + #else + half opacity = 0.5; + #endif + + #if defined(HAS_BASE_COLOR) + float4 color = float4(graph.base_color, opacity); + #else + float4 color = float4(1,1,1, opacity); + #endif + + #if defined(PARTICLE_LIGHTING) + float4 basis0 = GRAPH_PIXEL_DATA(input, basis0); + float4 basis1 = GRAPH_PIXEL_DATA(input, basis1); + float4 basis2 = GRAPH_PIXEL_DATA(input, basis2); + #if defined(BACK_LIGHTING) + float3 back_lighting = GRAPH_PIXEL_DATA(input, back_lighting); + #endif + float3 normal = normalize(float3(basis0.w, basis1.w, basis2.w)); + color.rgb = calc_lighting(color, normal, back_lighting, basis0.xyz, basis1.xyz, basis2.xyz); + #if defined(VS_FOG) + // apply vs calculated fog + color.rgb = lerp(color.rgb, input.fog_params.rgb, input.fog_params.a); + #else + color = apply_fog(color, world_pos, depth); + #endif + #elif defined(EMISSIVE_PARTICLES) + color.rgb *= emissive_particle_intensity; + #endif + + #if defined(HAS_ANGLE_FADE) + float3 plane_wn = normalize(GRAPH_PIXEL_DATA(input, world_space_plane_normal)); + float3 eye_dir = normalize(GRAPH_PIXEL_DATA(input, eye_vector)); + float fade = dot(plane_wn, eye_dir); + opacity *= saturate((fade - graph.fade_range.x) / (graph.fade_range.y - graph.fade_range.x)); + #endif + + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) && !defined(SCREEN_SPACE) + // Fade out high resolution particle if the low resolution particles is infront of the high resolution particle + float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_position).rg; + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + float background_depth = depth; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * max(depth_variance, 0.001))); + float alpha_modifier = TEX2D(hdr_transparent_div4, screen_position).a * (1.0 - T); + opacity *= 1.0 - alpha_modifier; + #endif + + half4 output_color; + #if defined(BLEND_ADDITIVE) + output_color = half4(color.rgb * opacity, 0.0); + #elif defined(BLEND_PREMULTIPLIED_ALPHA) + output_color = half4(color.rgb, opacity); + #else + output_color = half4(color.rgb * opacity, opacity); + #endif + + #ifdef LOW_RES + PS_OUTPUT o; + o.color = output_color; + float alpha_depth = depth * opacity; + o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0.0, opacity); + return o; + #else + #if defined(PARTICLE_DEBUG) + #if defined(EMISSIVE_PARTICLES) + return EMISSIVE_PARTICLES_DEBUG_COLOR; + #else + return LIT_PARTICLES_DEBUG_COLOR; + #endif + #else + return output_color; + #endif + #endif + } + #endif + """ + } + + depth_only = { + language = "hlsl" + + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access" ] + + instance_data = { + } + + /*stage_conditions = { + tessellation_control = "defined(DX11_DISPLACEMENT_MAPPING)" + }*/ + + samplers = { + "defined(DISTORTION)": { + hdr0 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr0" + type = "2d" + } + } + } + + code = """ + #if defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL) + #define NEEDS_TANGENT_SPACE + #endif + + struct VS_INPUT { + float4 position : POSITION; + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_billboard) + #if defined(SCREEN_SPACE) + float4x4 proj; + float4x4 view; + #else + float4x4 view; + float4x4 view_proj; + #endif + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) + { + PS_INPUT o; + + GraphVertexParams params; + GraphVertexResults results; + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + #if defined(EXTERNAL_ROTATION) + float3 y = GRAPH_VERTEX_DATA(input, vertex_tangent); + float3 x = GRAPH_VERTEX_DATA(input, vertex_binormal); + #elif defined(TANGENT_LOCKED) + float3 y = GRAPH_VERTEX_DATA(input, vertex_tangent); + float3 x = normalize(cross(normalize(input.position.xyz - camera_pos), y)); + #elif defined(SCREEN_SPACE) + float3 x = float3(1,0,0); + float3 y = float3(0,1,0); //float3(0,0,1); + #else + float3 x = view._m00_m10_m20; + float3 y = view._m02_m12_m22; + #endif + + #if defined(ROTATION) + float rotation = GRAPH_VERTEX_DATA(input, vertex_rotation); + float c = cos(rotation); + float s = sin(rotation); + float3 x_axis = x * c + y * s; + float3 y_axis = y * c - x * s; + #else + float3 x_axis = x; + float3 y_axis = y; + #endif + + float2 corner_info = GRAPH_VERTEX_DATA(input, vertex_corner_info); + float2 size = GRAPH_VERTEX_DATA(input, vertex_size); + #if defined(PIVOT) + float2 pivot = GRAPH_VERTEX_DATA(input, vertex_pivot); + float2 corner = corner_info * ( (1-(corner_info*0.5+0.5)) * size + corner_info * (pivot * size) ); + #else + float2 corner = corner_info * (size * 0.5); + #endif + + #if defined(SCREEN_SPACE) + float3 wp = input.position.xzy + (x_axis * corner.x + y_axis * corner.y) / float3(camera_unprojection.xz, 1); + float4 p = float4(wp, 1); //mul(float4(wp, 1), proj); + #else + float3 wp = input.position.xyz + (x_axis * corner.x + y_axis * corner.y); + #if defined(HAS_CUSTOM_FOV) + float4 p = mul(float4(wp, 1),camera_custom_fov_view_projection); + #else + float4 p = mul(float4(wp, 1), view_proj); + #endif + #endif + + #if defined(NEEDS_WORLD_SPACE_NORMAL) || defined(NEEDS_TANGENT_SPACE) + #if defined(SCREEN_SPACE) + float3 normal = view._m01_m11_m21; + #else + float3 normal = normalize(lerp(wp - input.position.xyz, -view._m01_m11_m21, 0.2)); + #endif + normal = mul(normal, (float3x3)view); + #endif + + #if defined(NEEDS_UV) + GRAPH_VERTEX_PARAM(params, uv) = (corner_info * float2(1,-1) * 0.5 + 0.5); + #endif + + #if defined(NEEDS_TANGENT_SPACE) + float3 n = cross(x_axis, y_axis); + float3 tangent = x_axis; + float3 binormal = y_axis; + GRAPH_VERTEX_PARAM(params, tsm0).rgb = float3(tangent.x, binormal.x, n.x); + GRAPH_VERTEX_PARAM(params, tsm1).rgb = float3(tangent.y, binormal.y, n.y); + GRAPH_VERTEX_PARAM(params, tsm2).rgb = float3(tangent.z, binormal.z, n.z); + #endif + + #if defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_VERTEX_PARAM(params, world_space_normal).rgb = normal; + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp; + #endif + + #if defined(NEEDS_UV_SCALE) + #if defined(NEEDS_UV_ANIMATION) + GRAPH_VERTEX_PARAM(params, uv_frame) = GRAPH_VERTEX_DATA(input, vertex_uv_data).z; + #endif + GRAPH_VERTEX_PARAM(params, uv_scale) = GRAPH_VERTEX_DATA(input, vertex_uv_data).xy; + #elif defined(NEEDS_UV_ANIMATION) + GRAPH_VERTEX_PARAM(params, uv_frame) = GRAPH_VERTEX_DATA(input, vertex_uv_frame); + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + o.position = p; + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + #if defined(GBUFFER_PARTICLES) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if defined(HAS_OPACITY) + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + discard; + #endif + + return float4(1,1,1,1); + } + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if defined(HAS_OPACITY) + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + half opacity = saturate(graph.opacity); + #else + half opacity = 0.5; + #endif + + return opacity.xxxx; + } + #endif + """ + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/output_nodes/particle_distortion_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/particle_distortion_base.shader_node new file mode 100644 index 0000000..d9eb164 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/particle_distortion_base.shader_node @@ -0,0 +1,175 @@ +group = "Output/Particles" +display_name = "Distortion Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "1570ceed-8c40-4b7d-ba45-1bfe7722fed2" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "cdc6c110-5859-461a-9934-17e54089c764" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "c050103c-1b43-4fbc-9547-d0c0d3f40ba6" = { + is_required = true + name = "distortion_normal" + display_name = "Distortion Normal" + domain = "pixel" + type = "vector3" + } + + "402a4a8d-b721-4746-9a2d-90027c4aad68" = { + is_required = true + name = "distortion_strength" + display_name = "Distortion Strength" + domain = "pixel" + type = "vector2" + } +} + +options = { + "2b136447-676e-4943-997b-04a28ae68497" = "WORLD_SPACE_NORMAL" + "a5733c57-be1f-457f-a6fc-366d9c0e3c8c" = "EXTERNAL_ROTATION" + "d63603f1-8cc0-4b03-a9aa-b787902c6997" = "TANGENT_LOCKED" + "f38425db-9f78-4f2c-95ba-634a367b7aed" = "SCREEN_SPACE" + "cae9d51b-35c3-40f7-9805-119d7932cfcf" = "PIVOT" + "6ce8aa19-350e-49b8-bb39-444a60db482d" = "ROTATION" + "b770b6bb-1e81-4937-8032-fec369a05ea3" = "HAS_CUSTOM_FOV" + "fea9af88-d417-433a-b475-346b6b03e249" = "HIGH_QUALITY" +} + +ui = [ + { + type = "drop_down" + display_name = "Resolution" + options = { + "Low" = "00000000-0000-0000-0000-000000000000" + "Full" = "fea9af88-d417-433a-b475-346b6b03e249" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Turn-up Algorithms" + options = { + "Disabled" = "00000000-0000-0000-0000-000000000000" + "Screen Space" = "f38425db-9f78-4f2c-95ba-634a367b7aed" + "Aligned" = "d63603f1-8cc0-4b03-a9aa-b787902c6997" + "Facing" = "a5733c57-be1f-457f-a6fc-366d9c0e3c8c" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Pivot" option = "cae9d51b-35c3-40f7-9805-119d7932cfcf" } + { type = "checkbox" display_name = "Rotation" option = "6ce8aa19-350e-49b8-bb39-444a60db482d" } + { type = "checkbox" display_name = "Custom FOV" option = "b770b6bb-1e81-4937-8032-fec369a05ea3" } +] + +render_state = { + shadow_caster = { + inherit: ["core/stingray_renderer/output_nodes/particle_base#shadow_caster"] + } + + opacity = { + inherit: ["core/stingray_renderer/output_nodes/particle_base#opacity"] + } +} + +channels = { + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } + + "on_platform(GL)": { + vertex_corner_info = { type = "float2" semantic = "COLOR1" domain = "vertex" } + } + "!on_platform(GL)": { + vertex_corner_info = { type = "float2" semantic = "POSITION1" domain = "vertex" } + } + vertex_size = { type = "float2" semantic = "TEXCOORD7" domain = "vertex" } + "defined(ROTATION)": { + vertex_rotation = { type = "float" semantic = "TEXCOORD1" domains = ["vertex"] } + } + "defined(NEEDS_UV_SCALE)": { + "defined(NEEDS_UV_ANIMATION)": { + vertex_uv_data = { type = "float3" semantic = "TEXCOORD0" domains = ["vertex"] } + uv_frame = { type = "float" domains = ["vertex"] } + } + "!defined(NEEDS_UV_ANIMATION)": { + vertex_uv_data = { type = "float2" semantic = "TEXCOORD0" domains = ["vertex"] } + } + uv_scale = { type = "float2" domains = ["vertex"] } + } + "!defined(NEEDS_UV_SCALE)": { + "defined(NEEDS_UV_ANIMATION)": { + vertex_uv_frame = { type = "float" semantic = "TEXCOORD0" domains = ["vertex"] } + uv_frame = { type = "float" domains = ["vertex"] } + } + } + "defined(PIVOT)": { + vertex_pivot = { type = "float2" semantic = "TEXCOORD6" domains = ["vertex"] } + } + "defined(EXTERNAL_ROTATION) || defined(TANGENT_LOCKED)": { + vertex_tangent = { type = "float3" semantic = "TANGENT" domains = ["vertex"] } + "defined(EXTERNAL_ROTATION)": { + vertex_binormal = { type = "float3" semantic = "BINORMAL" domains = ["vertex"] } + } + } +} + +shader_contexts = { + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL) && render_cap(development) && render_setting(particle_visualization)" defines=["PARTICLE_DEBUG"] } + //{ if: "on_renderer(D3D11, D3D12, GNM, GL) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] } + { if: "on_renderer(D3D11, D3D12, GNM, GL)" } + ] + + passes = [ + { if: "defined(PARTICLE_DEBUG)" then: [ + { if: "defined(SCREEN_SPACE)" then: [ + { layer="hdr_transparent_screen_space_low_res" code_block="core/stingray_renderer/output_nodes/particle_base#billboard" defines=["DISTORTION"] render_state="opacity" } + ] else: [ + { layer="hdr_transparent_low_res" code_block="core/stingray_renderer/output_nodes/particle_base#billboard" defines=["DISTORTION"] render_state="opacity" } + ]} + ] else: [ + { if: "defined(LOW_RES_ENABLED) && !defined(HIGH_QUALITY)" then: [ + { if: "defined(SCREEN_SPACE)" then: [ + { layer="hdr_transparent_screen_space_low_res" code_block="core/stingray_renderer/output_nodes/particle_base#billboard" defines=["DISTORTION" "LOW_RES"] render_state="opacity" } + ] else: [ + { layer="hdr_transparent_low_res" code_block="core/stingray_renderer/output_nodes/particle_base#billboard" defines=["DISTORTION" "LOW_RES"] render_state="opacity" } + ]} + ] else: [ + { if: "defined(SCREEN_SPACE)" then: [ + { layer="hdr_transparent_screen_space" code_block="core/stingray_renderer/output_nodes/particle_base#billboard" defines=["DISTORTION"] render_state="opacity" } + ] else: [ + { layer="hdr_transparent" code_block="core/stingray_renderer/output_nodes/particle_base#billboard" defines=["DISTORTION"] render_state="opacity" } + ]} + ]} + ]} + ] + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/output_nodes/particle_gbuffer_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/particle_gbuffer_base.shader_node new file mode 100644 index 0000000..1c261ea --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/particle_gbuffer_base.shader_node @@ -0,0 +1,198 @@ +group = "Output/Particles" +display_name = "GBuffer Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "f47209cb-36b0-499c-8c37-857888098e1c" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "2edbfd6b-4985-4acd-ad0e-cbafb0272a27" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "9b191b4d-05f1-4f21-b883-d34d9c440b99" = { + name = "opacity_threshold" + is_required = false + display_name = "Opacity Threshold" + type = { scalar: ["HAS_OPACITY_THRESHOLD"] } + domain = "pixel" + } + + "4d3bf516-a2b7-4709-b973-707ebf593845" = { + is_required = false + name = "normal" + display_name = "Normal" + type = { vector3: ["HAS_NORMAL"] } + domain = "pixel" + } + + "98d6ab4a-2569-415d-82c4-ab8115438a28" = { + is_required = false + name = "metallic" + display_name = "Metallic" + type = { scalar: ["HAS_METALLIC"] } + domain = "pixel" + } + + "2a34232c-88b9-485e-b009-9d3470d02943" = { + is_required = false + name = "roughness" + display_name = "Roughness" + type = { scalar: ["HAS_ROUGHNESS"] } + domain = "pixel" + } + + "cfd2c734-e369-47db-b315-41a9bcacc441" = { + is_required = false + name = "ambient_occlusion" + display_name = "Ambient Occlusion" + type = { scalar: ["HAS_AMBIENT_OCCLUSION"] } + domain = "pixel" + } +} + +options = { + "2b136447-676e-4943-997b-04a28ae68497" = "WORLD_SPACE_NORMAL" + "a5733c57-be1f-457f-a6fc-366d9c0e3c8c" = "EXTERNAL_ROTATION" + "d63603f1-8cc0-4b03-a9aa-b787902c6997" = "TANGENT_LOCKED" + "f38425db-9f78-4f2c-95ba-634a367b7aed" = "SCREEN_SPACE" + "cae9d51b-35c3-40f7-9805-119d7932cfcf" = "PIVOT" + "6ce8aa19-350e-49b8-bb39-444a60db482d" = "ROTATION" + "3eca2035-4349-4d1a-ae8b-6b1d8b91033f" = "HAS_CUSTOM_FOV" +} + +ui = [ + { + type = "drop_down" + display_name = "Normals In" + options = { + "Tangent Space" = "00000000-0000-0000-0000-000000000000" + "World Space" = "2b136447-676e-4943-997b-04a28ae68497" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Turn-up Algorithms" + options = { + "Disabled" = "00000000-0000-0000-0000-000000000000" + "Screen Space" = "f38425db-9f78-4f2c-95ba-634a367b7aed" + "Aligned" = "d63603f1-8cc0-4b03-a9aa-b787902c6997" + "Facing" = "a5733c57-be1f-457f-a6fc-366d9c0e3c8c" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Pivot" option = "cae9d51b-35c3-40f7-9805-119d7932cfcf" } + { type = "checkbox" display_name = "Rotation" option = "6ce8aa19-350e-49b8-bb39-444a60db482d" } + { type = "checkbox" display_name = "Custom FOV" option = "3eca2035-4349-4d1a-ae8b-6b1d8b91033f" } +] + +render_state = { + gbuffer_material = { + inherit: ["core/stingray_renderer/output_nodes/standard_base#gbuffer_material"] + } + + shadow_caster = { + inherit: ["core/stingray_renderer/output_nodes/standard_base#shadow_caster"] + } +} + +channels = { + "(defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE)": { + tsm0 = { type = "float3" domains = ["vertex", "pixel"] } + tsm1 = { type = "float3" domains = ["vertex", "pixel"] } + tsm2 = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } + + "on_platform(GL)": { + vertex_corner_info = { type = "float2" semantic = "COLOR1" domain = "vertex" } + } + "!on_platform(GL)": { + vertex_corner_info = { type = "float2" semantic = "POSITION1" domain = "vertex" } + } + vertex_size = { type = "float2" semantic = "TEXCOORD7" domain = "vertex" } + "defined(ROTATION)": { + vertex_rotation = { type = "float" semantic = "TEXCOORD1" domains = ["vertex"] } + } + "defined(NEEDS_UV_SCALE)": { + "defined(NEEDS_UV_ANIMATION)": { + vertex_uv_data = { type = "float3" semantic = "TEXCOORD0" domains = ["vertex"] } + uv_frame = { type = "float" domains = ["vertex"] } + } + "!defined(NEEDS_UV_ANIMATION)": { + vertex_uv_data = { type = "float2" semantic = "TEXCOORD0" domains = ["vertex"] } + } + uv_scale = { type = "float2" domains = ["vertex"] } + } + "!defined(NEEDS_UV_SCALE)": { + "defined(NEEDS_UV_ANIMATION)": { + vertex_uv_frame = { type = "float" semantic = "TEXCOORD0" domains = ["vertex"] } + uv_frame = { type = "float" domains = ["vertex"] } + } + } + "defined(PIVOT)": { + vertex_pivot = { type = "float2" semantic = "TEXCOORD6" domains = ["vertex"] } + } + "defined(EXTERNAL_ROTATION) || defined(TANGENT_LOCKED)": { + vertex_tangent = { type = "float3" semantic = "TANGENT" domains = ["vertex"] } + "defined(EXTERNAL_ROTATION)": { + vertex_binormal = { type = "float3" semantic = "BINORMAL" domains = ["vertex"] } + } + } +} + +shader_contexts = { + shadow_caster = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" } + ] + + passes = [ + { code_block="core/stingray_renderer/output_nodes/particle_base#depth_only" define="GBUFFER_PARTICLES" render_state="shadow_caster" } + ] + } + + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" } + ] + + passes = [ + { if: "defined(HAS_OPACITY)" then: [ + { layer="gbuffer_alpha_masked" code_block="core/stingray_renderer/output_nodes/particle_base#billboard" define="GBUFFER_PARTICLES" render_state="gbuffer_material" } + ] else: [ + { layer="gbuffer" code_block="core/stingray_renderer/output_nodes/particle_base#billboard" define="GBUFFER_PARTICLES" render_state="gbuffer_material" } + ]} + ] + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/output_nodes/probe_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/probe_base.shader_node new file mode 100644 index 0000000..0db74e8 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/probe_base.shader_node @@ -0,0 +1,321 @@ +group = "Output" +display_name = "Reflection Probe Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { +} + +options = { +} + +ui = [ +] + +render_state = { + reflection_probe = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + z_enable = "true" + z_write_enable = "false" + z_func = "greater_equal" + + cull_mode = "cull_ccw" + + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_one" + src_blend = "blend_inv_dest_alpha" + } + } + + debug_visualization = { + inherit: ["core/stingray_renderer/shader_libraries/common#opacity"] + state: { + "defined(FRONT_CULLING)" = { + cull_mode = "cull_ccw" + } + "defined(BACK_CULLING)" = { + cull_mode = "cull_cw" + } + "defined(WIREFRAME)" = { + cull_mode = "cull_none" + fill_mode = "fill_wireframe" + "on_renderer(D3D11, D3D12)" = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + "defined(DRAW_INFLUENCE)" = { + cull_mode = "cull_none" + z_func = "greater_equal" + } + } + } +} + +sampler_state = { + +} + +channels = { + vertex_position = { type = "float4" domain = "vertex" } +} + +permutation_sets = { +} + +shader_contexts = { + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" } + ] + passes = [ + { if: "on_renderer(D3D11, D3D12, GNM)" then: [ + { layer="reflections" code_block="reflection_probe" render_state="reflection_probe" } + + // { layer="wireframe" code_block="reflection_probe" defines=["DRAW_INFLUENCE_VOLUME" "FRONT_CULLING"] render_state="dev_volume_visualization" } + + { layer="wireframe" code_block="reflection_probe" defines=["DRAW_INFLUENCE"] render_state="debug_visualization" branch_key="dev_volume_visualization" } + + { layer="wireframe" code_block="reflection_probe" defines=["DRAW_TRACE_VOLUME" "FRONT_CULLING"] render_state="debug_visualization" branch_key="dev_volume_visualization" } + { layer="wireframe" code_block="reflection_probe" defines=["DRAW_TRACE_VOLUME" "BACK_CULLING"] render_state="debug_visualization" branch_key="dev_volume_visualization" } + { layer="wireframe" code_block="reflection_probe" defines=["DRAW_TRACE_VOLUME" "WIREFRAME"] render_state="debug_visualization" branch_key="dev_volume_visualization" } + + { layer="wireframe" code_block="reflection_probe" defines=["DRAW_INFLUENCE_VOLUME" "BACK_CULLING"] render_state="debug_visualization" branch_key="dev_volume_visualization" } + { layer="wireframe" code_block="reflection_probe" defines=["DRAW_INFLUENCE_VOLUME" "WIREFRAME"] render_state="debug_visualization" branch_key="dev_volume_visualization" } + ] } + ] + } +} + +code_blocks = { + reflection_probe = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/lighting_common#brdf", + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_bias" + ] + + samplers = { + gbuffer1 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "gbuffer1" + type = "2d" + } + + linear_depth = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "linear_depth" + type = "2d" + } + + reflection_map = { + display_name = "Reflection Map" + type = "cube" + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "material" + slot_name = "reflection_map" + } + + radiation_map = { + display_name = "Reflection Map" + type = "cube" + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "material" + slot_name = "radiation_map" + } + } + + code = { + hlsl = """ + DECLARE_SAMPLER_2D(gbuffer1); + DECLARE_SAMPLER_2D(linear_depth); + DECLARE_SAMPLER_CUBE(reflection_map); + DECLARE_SAMPLER_CUBE(radiation_map); + + struct VS_INPUT { + float4 position : POSITION; + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 w : TEXCOORD1; + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c0) + float4x4 world; + float4x4 inv_world; + float4x4 world_view_proj; + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + CBUFFER_START(light) + float3 light_position; + float3 light_proxy_scale; + float3 light_box_min; + float3 light_box_max; + + // controlled from script data on probe unit propagated through the light material. + float3 trace_box_min; + float3 trace_box_max; + float3 falloff; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + GraphVertexParams params; + GraphVertexResults results; + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + #if defined(DRAW_TRACE_VOLUME) + float3 scale = trace_box_max - trace_box_min; + float4 position = float4(input.position.xyz * scale, 1); + position.xyz += (trace_box_min + trace_box_max) * 0.5f; + #else + float4 position = float4(input.position.xyz * light_proxy_scale, 1); + position.xyz += (light_box_min + light_box_max) * 0.5f; + #endif + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + o.position = mul(position, world_view_proj); + #if !defined(STENCIL_MARK) + o.w = encode_world_pos(o.position); + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + #if defined(DRAW_INFLUENCE_VOLUME) || defined(DRAW_TRACE_VOLUME) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main() : SV_TARGET0 + { + #if defined(WIREFRAME) + #if defined(DRAW_INFLUENCE_VOLUME) + return float4(1,0.8,0.8,0.5); + #else + return float4(0.8,1,0.8,0.5); + #endif + #elif defined(DRAW_INFLUENCE_VOLUME) + return float4(1,0,0,0.1); + #else + return float4(0,1,0,0.1); + #endif + } + #else + // TODO: does not support scaling atm. + + #if defined(DRAW_INFLUENCE) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input + #if defined(GL2) + , float2 wpos : VPOS + #endif + ) : SV_TARGET0 + #else + struct PS_OUTPUT { + float4 reflection : SV_TARGET0; + float4 radiation : SV_TARGET1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input + #if defined(GL2) + , float2 wpos : VPOS + #endif + ) + #endif + { + #if defined(GL2) + half2 uv = wpos.xy / back_buffer_size; + #else + half2 uv = input.position.xy / back_buffer_size; + #endif + float d = gbuffer_decode_depth(TEX2D(linear_depth, uv)); + + float3 wp = decode_world_pos(input.w, d); + float3 op = mul(float4(wp, 1), inv_world).xyz; + float3 V = mul(normalize(camera_world._m30_m31_m32 - wp), (float3x3)inv_world); + + #if defined(HAS_LIGHT_COLOR) + GraphPixelParams params; + GraphPixelResults graph; + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + #else + //float3 light_col = light_color; + #endif + + half4 gbuffer_1 = TEX2D(gbuffer1, uv); + + float3 gbuffer_normal = gbuffer_decode_normal(gbuffer_1); + float3 N = mul(gbuffer_normal, (float3x3)inv_world); + half roughness = gbuffer_decode_roughness(gbuffer_1); + float mipmap_index = roughness * 7; + + float3 reflection_dir; + { + reflection_dir = reflect(-V, N); + float3 r_max = (trace_box_max - op) / reflection_dir; + float3 r_min = (trace_box_min - op) / reflection_dir; + float3 r_min_max = max(r_max, r_min); + float intersect_dist = min(min(r_min_max.x, r_min_max.y), r_min_max.z); + reflection_dir = mul(op + reflection_dir * intersect_dist, (float3x3)world); + } + + float3 reflection = rgbm_decode(TEXCUBELOD(reflection_map, reflection_dir, mipmap_index)); + + float3 radiation_dir = gbuffer_normal; + /*{ + radiation_dir = N; + float3 r_max = (trace_box_max - op) / radiation_dir; + float3 r_min = (trace_box_min - op) / radiation_dir; + float3 r_min_max = max(r_max, r_min); + float intersect_dist = min(min(r_min_max.x, r_min_max.y), r_min_max.z); + radiation_dir = mul(op + radiation_dir * intersect_dist, (float3x3)world); + }*/ + + // TODO: this might be wanted, but requires more probes in order to get good result. + // float diff_attn = saturate(dot(gbuffer_normal, normalize(world._m30_m31_m32 - wp))); + float3 radiation = rgbm_decode(TEXCUBELOD(radiation_map, radiation_dir, 0)); + + // Attenuate + float3 distances = min(op - light_box_min, light_box_max - op) / falloff; + float attn = saturate(min(distances.x, min(distances.y, distances.z))); + + #if defined(DRAW_INFLUENCE) + bool3 inside = (distances > 0.0); + if(!all(inside)) + discard; + return float4(0, 0, 1, (1.0 - attn) * 0.5); + #else + PS_OUTPUT o; + o.reflection = float4(reflection * attn, attn); + o.radiation = float4(radiation * attn, attn); + return o; + #endif + } + #endif + + + """ + } + } +} diff --git a/vmf_source/core/stingray_renderer/output_nodes/ribbon_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/ribbon_base.shader_node new file mode 100644 index 0000000..d8b8497 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/ribbon_base.shader_node @@ -0,0 +1,532 @@ +group = "Particle Ribbon/Output" +display_name = "Ribbon Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +// TODO: disable sun if it's disabled in environment settings. +// TODO: options to disable shadows, local lights, sun_light. + +inputs = { + "e1d24468-d03e-4884-9b0d-dba23aaa94d6" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "0752e133-0c01-4fc4-b45a-a629cad4f850" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } +/* + "6fd5ebe2-4d15-4ecc-abad-ab9731ea861a" = { + name = "fade_range" + is_required = false + display_name = "Angle Fade Range" + type = { vector2: ["HAS_ANGLE_FADE" "NEEDS_EYE_VECTOR"] } + domain = "pixel" + } +*/ +} + +options = { + "e6347231-6647-4bc3-a1d0-f24e1b3e0562" = "EXTERNAL_ROTATION" + "914232a9-5352-43d7-a877-cedfe798541d" = "TANGENT_LOCKED" + "e8e86308-83cd-47a1-ab84-82776b3cf0ca" = "SCREEN_SPACE" + "9775d054-ef17-4ca4-b5a4-8751c6e1eec6" = "PIVOT" + "aaebf627-efab-4c86-8244-359d29d00f33" = "ROTATION" + "47a74b09-1932-40d4-ab90-0ec090fb9643" = "BLEND_ADDITIVE" + "e267562a-65dc-4547-a9fa-6a605b451cae" = "BLEND_PREMULTIPLIED_ALPHA" + "4a42f2e1-1069-4afe-a93f-c7558572780f" = "EMISSIVE_PARTICLES" + "5e380f16-5cc6-482b-9df6-af0d2b3bda3c" = "HAS_CUSTOM_FOV" + "fea9af88-d417-433a-b475-346b6b03e249" = "HIGH_QUALITY" +} + +ui = [ + + { + type = "drop_down" + display_name = "Blend Mode" + options = { + "Alpha Blend" = "00000000-0000-0000-0000-000000000000" + "Additive Blend" = "47a74b09-1932-40d4-ab90-0ec090fb9643" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Emissive particles" option = "4a42f2e1-1069-4afe-a93f-c7558572780f" } + { type = "checkbox" display_name = "Custom FOV" option = "5e380f16-5cc6-482b-9df6-af0d2b3bda3c" } +] + +render_state = { + + ribbon_opacity = { + inherit: ["core/stingray_renderer/shader_libraries/common#opacity"] + state: { + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_alpha" + write_mask0 = "red|green|blue" + z_enable = "true" + } + } +} + +sampler_state = { +} + +channels = { + + "defined(PARTICLE_LIGHTING)": { + basis0 = { type = "float4" domains = ["vertex", "pixel"] } + basis1 = { type = "float4" domains = ["vertex", "pixel"] } + basis2 = { type = "float4" domains = ["vertex", "pixel"] } + back_lighting = { type = "float3" domains = ["vertex", "pixel"] } + } +/* + "defined(HAS_ANGLE_FADE)": { + world_space_plane_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } +*/ + strip_info = { type = "float" semantic = "POSITION1" domain = "vertex" } + vertex_size = { type = "float2" semantic = "TEXCOORD7" domain = "vertex" } + + + "defined(NEEDS_UV_SCALE)": { + "defined(NEEDS_UV_ANIMATION)": { + vertex_uv_data = { type = "float3" semantic = "TEXCOORD0" domains = ["vertex"] } + uv_frame = { type = "float" domains = ["vertex"] } + } + "!defined(NEEDS_UV_ANIMATION)": { + vertex_uv_data = { type = "float2" semantic = "TEXCOORD0" domains = ["vertex"] } + } + uv_scale = { type = "float2" domains = ["vertex"] } + } + "!defined(NEEDS_UV_SCALE)": { + "defined(NEEDS_UV_ANIMATION)": { + vertex_uv_frame = { type = "float" semantic = "TEXCOORD0" domains = ["vertex"] } + uv_frame = { type = "float" domains = ["vertex"] } + } + } + vertex_tangent = { type = "float3" semantic = "TANGENT" domains = ["vertex"] } + ribbon_distance = { type = "float" semantic = "TEXCOORD6" domains = ["vertex"] } + +} + +log_permutations = false +permutation_sets = { + vertex_modifiers = [ + ] + + default = [ + ] +} + +shader_contexts = { + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" defines=["LOCAL_LIGHTS"] } + ] + + passes = [ + { if: "defined(EMISSIVE_PARTICLES)" then: [ + { layer="hdr_transparent" code_block="ribbon" render_state="ribbon_opacity" } + ] else: [ + { layer="hdr_transparent" code_block="ribbon" render_state="ribbon_opacity" defines=["PARTICLE_LIGHTING" "VS_FOG"] } + ]} + ] + } +} + +code_blocks = { + particle_lighting = { + code=""" + #if defined(PARTICLE_LIGHTING) + #if defined(D3D11) || defined(GNM) + #define BACK_LIGHTING + #endif + + #if defined(SHADOW_RECEIVING) + #define CALCULATE_LIGHTING + #endif + #endif + """ + } + + ribbon = { + language = "hlsl" + + include:[ + "particle_lighting" + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/common#fog", + "core/stingray_renderer/shader_libraries/lighting_common#brdf", + "core/stingray_renderer/shader_libraries/common#taa_offsets", + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_bias" + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map_filtering" + "core/stingray_renderer/shader_libraries/lighting_common#clustered_shading" + "core/stingray_renderer/shader_libraries/particle_lighting_common#radiosity_normal_mapping" + "core/stingray_renderer/shader_libraries/particle_lighting_common#particle_debug" ] + + instance_data = { + } + + stage_conditions = { + } + + samplers = { + "defined(DISTORTION)": { + hdr0 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr0" + type = "2d" + } + } + "defined(PARTICLE_LIGHTING)": { + global_diffuse_map = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "global_diffuse_map" + type = "cube" + } + } + "defined(NEEDS_LINEAR_DEPTH)": { + linear_depth = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "linear_depth" + type = "2d" + } + } + + } + + code = """ + #if defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL) + #define NEEDS_TANGENT_SPACE + #endif + + #if defined(NEEDS_PIXEL_DEPTH) || defined(PARTICLE_LIGHTING) + #define PS_NEEDS_WP + #endif + + #if defined(NEEDS_LINEAR_DEPTH) + DECLARE_SAMPLER_2D(linear_depth); + #define HAS_LINEAR_DEPTH + #endif + + struct VS_INPUT { + float4 position : POSITION; + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(PS_NEEDS_WP) + float3 world_pos : TEXCOORD15; + #endif + #if defined(VS_FOG) + float4 fog_params : TEXCOORD16; + #endif + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_ribbon) + float4x4 view; + float4x4 view_proj; + #if defined(EMISSIVE_PARTICLES) + float emissive_particle_intensity; + #endif + CBUFFER_END + + CBUFFER_START(c_material_exports) + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) + { + PS_INPUT o; + + GraphVertexParams params; + GraphVertexResults results; + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + + float3 y = GRAPH_VERTEX_DATA(input, vertex_tangent); + float3 x = normalize(cross(normalize(input.position.xyz - camera_pos), y)); + + float3 x_axis = x; + float3 y_axis = y; + + float strip_info = GRAPH_VERTEX_DATA(input, strip_info); + float2 size = GRAPH_VERTEX_DATA(input, vertex_size); + float corner = strip_info * (size.x * 0.5); + + float3 wp = input.position.xyz + (x_axis * corner); + + #if defined(HAS_CUSTOM_FOV) + float4 p = mul(float4(wp, 1),camera_custom_fov_view_projection); + #else + float4 p = mul(float4(wp, 1), view_proj); + #endif + + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp; + #endif + + // TOOD: Move this code to its own shader node! + #if defined(NEEDS_UV_SCALE) + #if defined(NEEDS_UV_ANIMATION) + GRAPH_VERTEX_PARAM(params, uv_frame) = GRAPH_VERTEX_DATA(input, vertex_uv_data).z; + #endif + GRAPH_VERTEX_PARAM(params, uv_scale) = GRAPH_VERTEX_DATA(input, vertex_uv_data).xy; + #elif defined(NEEDS_UV_ANIMATION) + GRAPH_VERTEX_PARAM(params, uv_frame) = GRAPH_VERTEX_DATA(input, vertex_uv_frame); + #endif + + + #if defined(PARTICLE_LIGHTING) + float3 normal = normalize(lerp(wp - input.position.xyz, -view._m01_m11_m21, 0.2)); + normal = mul(normal, (float3x3)view); + #endif + + + #if defined(PARTICLE_LIGHTING) + float4 basis0 = float4(0,0,0,1); + float4 basis1 = float4(0,0,0,1); + float4 basis2 = float4(0,0,0,1); + float3 back_lighting = float3(0,0,0); + calc_basis_lighting(basis0, basis1, basis2, back_lighting, wp, normal, view, p); + GRAPH_VERTEX_PARAM(params, basis0) = basis0; + GRAPH_VERTEX_PARAM(params, basis1) = basis1; + GRAPH_VERTEX_PARAM(params, basis2) = basis2; + #if defined(BACK_LIGHTING) + GRAPH_VERTEX_PARAM(params, back_lighting) = back_lighting; + #endif + #endif + + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + + #if defined(PS_NEEDS_WP) + o.world_pos = wp; + #endif + + #if defined(VS_FOG) + const float3 view_dir = camera_world._m30_m31_m32 - wp.xyz; + const float3 camera_dir = camera_world._m10_m11_m12; + const float depth = dot(-view_dir, camera_dir); + o.fog_params = calc_fog_vs(wp, depth); + #endif + + o.position = p; + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + /* + #elif defined(DISTORTION) // TODO: avoid sample from objects infront of the "distortion" + DECLARE_SAMPLER_2D(hdr0); + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + DECLARE_SAMPLER_2D(hdr_transparent_div4); + DECLARE_SAMPLER_2D(hdr_linear_depth_div4); + #endif + + #ifdef LOW_RES + struct PS_OUTPUT { + half4 color : SV_TARGET0; + half4 depth : SV_TARGET1; + }; + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + #endif + { + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) || defined(NEEDS_SCREEN_POS) + #if defined(LOW_RES) + float2 screen_position = input.position.xy / (back_buffer_size * 0.25); + #else + float2 screen_position = (input.position.xy / back_buffer_size); + #endif + #endif + + #if defined(NEEDS_SCREEN_POS) + GRAPH_PIXEL_PARAM(params, screen_pos) = screen_position; + #endif + + #if defined(PS_NEEDS_WP) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + + const float depth = dot(-view_dir, camera_dir); + #endif + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + float3 normal = graph.distortion_normal; + normal.rg = normal.rg * 2 - 1; + half2 distortion = normal.xy / back_buffer_size; + + #if defined(HAS_OPACITY) + half opacity = saturate(graph.opacity); + #else + half opacity = 0.5; + #endif + + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + // Fade out high resolution particle if the low resolution particles is infront of the high resolution particle + float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_position).rg; + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + float background_depth = depth; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * max(depth_variance, 0.001))); + float alpha_modifier = TEX2D(hdr_transparent_div4, screen_position).a * (1.0 - T); + opacity *= 1.0 - alpha_modifier; + #endif + + float3 color = TEX2D(hdr0, screen_position + (distortion * graph.distortion_strength)).rgb; + + #if defined(HAS_BASE_COLOR) + color *= graph.base_color; + #endif + + #ifdef LOW_RES + PS_OUTPUT o; + o.color = half4(color * opacity, opacity); + float alpha_depth = depth * opacity; + o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0.0, opacity); + return o; + #else + #if defined(PARTICLE_DEBUG) + return DISTORTION_PARTICLES_DEBUG_COLOR; + #else + return half4(color * opacity, opacity); + #endif + #endif + } + #else + */ + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + { + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if defined(PS_NEEDS_WP) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + + const float depth = dot(-view_dir, camera_dir); + #endif + + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY) + half opacity = saturate(graph.opacity); + #else + half opacity = 0.5; + #endif + + + + #if defined(HAS_BASE_COLOR) + float4 color = float4(graph.base_color, opacity); + #else + float4 color = float4(1,1,1, opacity); + #endif + + + #if defined(PARTICLE_LIGHTING) + float4 basis0 = GRAPH_PIXEL_DATA(input, basis0); + float4 basis1 = GRAPH_PIXEL_DATA(input, basis1); + float4 basis2 = GRAPH_PIXEL_DATA(input, basis2); + #if defined(BACK_LIGHTING) + float3 back_lighting = GRAPH_PIXEL_DATA(input, back_lighting); + #endif + float3 normal = normalize(float3(basis0.w, basis1.w, basis2.w)); + color.rgb = calc_lighting(color, normal, back_lighting, basis0.xyz, basis1.xyz, basis2.xyz); + #if defined(VS_FOG) + // apply vs calculated fog + color.rgb = lerp(color.rgb, input.fog_params.rgb, input.fog_params.a); + #else + color = apply_fog(color, world_pos, depth); + #endif + #elif defined(EMISSIVE_PARTICLES) + color.rgb *= emissive_particle_intensity; + #endif + + + /* + #if defined(HAS_ANGLE_FADE) + float3 plane_wn = normalize(GRAPH_PIXEL_DATA(input, world_space_plane_normal)); + float3 eye_dir = normalize(GRAPH_PIXEL_DATA(input, eye_vector)); + float fade = dot(plane_wn, eye_dir); + opacity *= saturate((fade - graph.fade_range.x) / (graph.fade_range.y - graph.fade_range.x)); + #endif + */ + + half4 output_color = half4(color.rgb * opacity, opacity); + #if defined(BLEND_ADDITIVE) + output_color.a = 0.0; + #endif + + return output_color; + } + //#endif + """ + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/output_nodes/skydome_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/skydome_base.shader_node new file mode 100644 index 0000000..9bacdc3 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/skydome_base.shader_node @@ -0,0 +1,207 @@ +group = "Output" +display_name = "Skydome Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "19499850-aa42-40f8-a51e-75abb592e92b" = { + name = "base_color" + is_required = true + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } +} + +options = { + "582dd9e3-a746-4913-8eae-d8569b6d7b39" = "CAMERA_LOCK_XY" + "1e637d96-cadf-4d3c-a1c6-fc31ce81adf8" = "CAMERA_LOCK_Z" + "efe865a9-2225-4b9b-92dd-6080d4bfa417" = "SKYDOME_RGBM" +} + +ui = [ + { type = "checkbox" display_name = "Lock Camera in XY-plane" option = "582dd9e3-a746-4913-8eae-d8569b6d7b39" } + { type = "checkbox" display_name = "Lock Camera in Z-plane" option = "1e637d96-cadf-4d3c-a1c6-fc31ce81adf8" } + { type = "checkbox" display_name = "Skydome Texture RGBM encoded" option = "efe865a9-2225-4b9b-92dd-6080d4bfa417" } +] + +render_state = { + ambient_no_depth_write = { + inherit: ["core/stingray_renderer/shader_libraries/common#ambient"] + states = { + z_write_enable = "false" + } + } + + filter = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + states = { + cull_mode = "cull_none" + z_write_enable = "false" + z_enable = "false" + } + } +} + +sampler_state = { } + +channels = { + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + vertex_position = { type = "float4" domain = "vertex" } +} + +log_permutations = false +permutation_sets = { +} + +shader_contexts = { + material_transfer = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" } + ] + + passes = [ + { code_block="skydome" defines=["MATERIAL_TRANSFER"] render_state="filter" } + ] + } + + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" } + ] + + passes = [ + { layer="skydome" code_block="skydome" defines=["PROJECT_TO_FAR_PLANE"] render_state="ambient_no_depth_write" } + ] + } +} + +code_blocks = { + skydome = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access"] + + instance_data = { + } + + stage_conditions = { + } + + samplers = { + } + + code = { + hlsl = """ + #if defined(NEEDS_EYE_VECTOR) || defined(CAMERA_LOCK_XY) || defined(CAMERA_LOCK_Z) + #define NEEDS_WORLD_SPACE + #endif + + struct VS_INPUT { + float4 position : POSITION; + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c0) + #if defined(CAMERA_LOCK_XY) || defined(NEEDS_WORLD_SPACE) + float4x4 world; + #endif + #if defined(CAMERA_LOCK_XY) + float4x4 view; + float4x4 proj; + #endif + float4x4 world_view_proj; + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) + { + PS_INPUT o; + float4 p; + + GraphVertexParams params; + GraphVertexResults results; + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + // Write output channels + float4 position = input.position; + #if defined(NEEDS_WORLD_SPACE) + float4 wp = mul(position, world); + #endif + + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + #if defined(NEEDS_EYE_VECTOR) + // TODO: this eye_vector can't be used to calculate distance (length(eye_vector) in this case + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = -wp.rgb; + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + #if defined(CAMERA_LOCK_XY) + #if defined(CAMERA_LOCK_Z) + view._m30_m31_m32 = float3(0,0,0); + #else + view._m30_m31 = float2(0,0); + #endif + p = mul(mul(wp, view), proj); + #else + p = mul(position, world_view_proj); + #endif + + #if defined(PROJECT_TO_FAR_PLANE) + p.z = p.w; + #endif + + o.position = p; + + /* + // TODO: try import uv again! + #if defined(MATERIAL_TRANSFER) + float2 tmp = GRAPH_VERTEX_DATA(input, uv); + tmp.y = 1 - tmp.y; + o.position = float4(tmp * 2 - 1, 0, 1); + #endif + */ + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + { + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + half4 c = half4(graph.base_color, 1.0); + + //#if defined(MATERIAL_TRANSFER) + // c.rgb = pow(c.rgb, 1.f / 2.2f); + //#endif + + return c; + } + """ + } + } +} diff --git a/vmf_source/core/stingray_renderer/output_nodes/standard_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/standard_base.shader_node new file mode 100644 index 0000000..2e134dc --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/standard_base.shader_node @@ -0,0 +1,2340 @@ +group = "Output" +display_name = "Standard Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "aee6e47b-be7b-4d67-a123-2ab5d660b94e" = { + name = "vertex_offset" + display_name = "Position offset" + is_required = false + type = { vector3: ["HAS_VERTEX_OFFSET"] } + domain = "vertex" + } + + "aca690cb-6305-4a2f-bf3d-69183a493db3" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "34259752-b962-4b65-92c3-903a57338519" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "7a9306c6-95ae-4cdb-9fef-0eedacce4e83" = { + name = "opacity_threshold" + is_required = false + display_name = "Opacity Threshold" + type = { scalar: ["HAS_OPACITY_THRESHOLD"] } + domain = "pixel" + } + + "b1c86408-aacb-4466-b754-ddcf37a3a2c8" = { + is_required = false + name = "normal" + display_name = "Normal" + type = { vector3: ["HAS_NORMAL"] } + domain = "pixel" + } + + "ad5e052f-d316-4a0f-8b79-53c38204d61b" = { + is_required = false + name = "metallic" + display_name = "Metallic" + type = { scalar: ["HAS_METALLIC"] } + domain = "pixel" + } + + "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" = { + is_required = false + name = "roughness" + display_name = "Roughness" + type = { scalar: ["HAS_ROUGHNESS"] } + domain = "pixel" + } + + "1164a5ef-4563-4795-b3b5-42825d6df037" = { + is_required = false + name = "emissive" + display_name = "Emissive" + type = { vector3: ["HAS_EMISSIVE"] } + domain = "pixel" + } + + "59fd1cf4-f736-470d-8510-1dd7c016639e" = { + is_required = false + name = "ambient_occlusion" + display_name = "Ambient Occlusion" + type = { scalar: ["HAS_AMBIENT_OCCLUSION"] } + domain = "pixel" + } + + "0544ddb6-e168-452d-86f2-42a79e8c98e3" = { + is_required = false + name = "sss_strength" + display_name = "Skin SSS" + type = { scalar: ["SKIN", "HAS_SKIN_SSS_STRENGTH"] } + domain = "pixel" + } + + "cc5a1773-27a2-430b-96d2-d81815d97feb" = { + is_required = false + name = "refraction" + display_name = "Refraction" + type = { scalar: ["HAS_REFRACTION"] } + domain = "pixel" + } + + "20346ea6-ecb0-41c1-bdcc-35bbe3ce1a5c" = { + is_required = false + name = "density" + display_name = "Density" + type = { scalar: ["HAS_DENSITY"] } + domain = "pixel" + } +} + +options = { + "b2c7c0d2-beff-4b1a-a9d4-068a507625a2" = "USE_FBX_PERMUTATIONS" + "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" = "INSTANCED" + "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" = "CULL_NONE" + "c198c109-2cdf-49ee-af18-a982c23e2729" = "CULL_FRONT" + "2b136447-676e-4943-997b-04a28ae68497" = "WORLD_SPACE_NORMAL" + "dd7fcf97-0627-48ab-b29a-95b5685bb123" = "TRANSPARENT" + "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" = "TRANSPARENT_FADE" + "b5bb2062-c8fa-43c5-8657-493a0be6860c" = "SKINNED_DISABLED" + "901b44ce-d128-498a-9912-32cd9635c556" = "HAS_FOV" + "da4e717b-5d0d-47fa-93b0-01c988f8f3ff" = "LOCK_NORMAL_ROTATION" + "979cb7cf-c255-42d0-a389-78fc0fb0539f" = "DEFERRED_DECALS_GROUP_1" + "d557b597-fa90-46b4-a751-eb51ae61ba5b" = "DEFERRED_DECALS_GROUP_2" + "fe20cc01-4cec-4217-98a0-07220ad3add9" = "DEFERRED_DECALS_GROUP_3" + "524a5842-23b7-46d1-ab22-cb3a14746ce0" = "USE_GLOBAL_ROUGHNESS_MULTIPLIER" + "11256995-4805-4018-bd6b-9214414fd363" = "NORMAL_FLIP_DISABLED" + "580cc6eb-3591-4c71-aa50-528af18ba160" = "JITTER_TRANSPARENCY" + "b649332d-7a8a-42b3-a809-6d0eb9c7c070" = "GBUFFER_POST_OUTLINE" + "08e965b9-90ed-4046-b314-c97ea7e7f30e" = "OPAQUE_FORWARD_DISABLED" +} + +ui = [ + { + type = "drop_down" + display_name = "Normals In" + options = { + "Tangent Space" = "00000000-0000-0000-0000-000000000000" + "World Space" = "2b136447-676e-4943-997b-04a28ae68497" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Blend Mode" + options = { + "Opaque" = "00000000-0000-0000-0000-000000000000" + "Transparent" = "dd7fcf97-0627-48ab-b29a-95b5685bb123" + "Transparent Fade" = "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Face Culling" + options = { + "Back" = "00000000-0000-0000-0000-000000000000" + "Front" = "c198c109-2cdf-49ee-af18-a982c23e2729" + "None (double sided)" = "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Decal Group" + options = { + "Group 0" = "00000000-0000-0000-0000-000000000000" + "Group 1" = "979cb7cf-c255-42d0-a389-78fc0fb0539f" + "Group 2" = "d557b597-fa90-46b4-a751-eb51ae61ba5b" + "Group 3" = "fe20cc01-4cec-4217-98a0-07220ad3add9" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Disable Skinning" option = "b5bb2062-c8fa-43c5-8657-493a0be6860c" } + { type = "checkbox" display_name = "Instancing" option = "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" } + { type = "checkbox" display_name = "Custom FOV" option = "901b44ce-d128-498a-9912-32cd9635c556" } + { type = "checkbox" display_name = "Avoid Tangent Space Transform" option = "da4e717b-5d0d-47fa-93b0-01c988f8f3ff" } + { type = "checkbox" display_name = "Use Global Roughness Multiplier" option = "524a5842-23b7-46d1-ab22-cb3a14746ce0" } + { type = "checkbox" display_name = "Normal Flip Disabled (double sided)" option = "11256995-4805-4018-bd6b-9214414fd363" } + { type = "checkbox" display_name = "Jitter Transparency" option = "580cc6eb-3591-4c71-aa50-528af18ba160" } + { type = "checkbox" display_name = "Gbuffer Post-Outline" option = "b649332d-7a8a-42b3-a809-6d0eb9c7c070" } + { type = "checkbox" display_name = "Disable Opaque Forward" option = "08e965b9-90ed-4046-b314-c97ea7e7f30e" } +] + + +render_state = { + culling_base_default = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + "defined(CULL_NONE)" = { + cull_mode = "cull_none" + } + "defined(CULL_FRONT)" = { + cull_mode = "cull_ccw" + } + "!defined(CULL_NONE) && !defined(CULL_FRONT)" = { + cull_mode = "cull_cw" + } + } + } + + culling_base_gbuffer = { + inherit: ["core/stingray_renderer/shader_libraries/common#gbuffer_material"] + state: { + "defined(CULL_NONE)" = { + cull_mode = "cull_none" + } + "defined(CULL_FRONT)" = { + cull_mode = "cull_ccw" + } + "!defined(CULL_NONE) && !defined(CULL_FRONT)" = { + cull_mode = "cull_cw" + } + } + } + + gbuffer_material = { + inherit: ["culling_base_gbuffer"] + state: { + "defined(HAS_FOV)" = { + stencil_enable = "true" + stencil_func = "always" + stencil_fail = "stencil_op_keep" + stencil_mask = "0xf8" + stencil_pass = "stencil_op_replace" + stencil_write_mask = "0xf8" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "always" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_replace" + stencil_z_fail_back_side = "stencil_op_keep" + + defined_DEFERRED_DECALS_GROUP_1 = { + defined_SKIN = { + stencil_ref = "0xa8" + } + ndefined_SKIN = { + stencil_ref = "0xa0" + } + } + ndefined_DEFERRED_DECALS_GROUP_1 = { + defined_DEFERRED_DECALS_GROUP_2 = { + defined_SKIN = { + stencil_ref = "0xc8" + } + ndefined_SKIN = { + stencil_ref = "0xc0" + } + } + ndefined_DEFERRED_DECALS_GROUP_2 = { + defined_DEFERRED_DECALS_GROUP_3 = { + defined_SKIN = { + stencil_ref = "0xe8" + } + ndefined_SKIN = { + stencil_ref = "0xe0" + } + } + ndefined_DEFERRED_DECALS_GROUP_3 = { + defined_SKIN = { + stencil_ref = "0x88" + } + ndefined_SKIN = { + stencil_ref = "0x80" + } + } + } + } + } + } + } + + emissive = { + inherit: ["culling_base_default"] + state: { + z_write_enable = "false" + z_func = "less_equal" + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_one" + src_blend = "blend_one" + } + } + + transparent_base = { + state : { + z_write_enable = "false" + // with refraction we are essentially taking over the blending in our shader, so we disable alpha blending + "!defined(HAS_REFRACTION)" = { + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_one" + } + + "defined(HAS_FOV)" = { + stencil_enable = "true" + stencil_func = "always" + stencil_fail = "stencil_op_keep" + stencil_mask = "0x80" + stencil_pass = "stencil_op_replace" + stencil_ref = "0x80" + stencil_write_mask = "0x80" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "always" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_replace" + stencil_z_fail_back_side = "stencil_op_keep" + } + } + } + + transparent = { + inherit: ["culling_base_default", "transparent_base"] + } + + transparent_double_sided_cw = { + inherit: ["core/stingray_renderer/shader_libraries/common#default", "transparent_base"] + state : { + cull_mode = "cull_cw" + } + } + + transparent_double_sided_ccw = { + inherit: ["core/stingray_renderer/shader_libraries/common#default", "transparent_base"] + state : { + cull_mode = "cull_ccw" + } + } + + wireframe = { + inherit: ["transparent"] + state: { + fill_mode = "fill_wireframe" + src_blend = "blend_src_alpha" + cull_mode = "cull_none" + "on_renderer(D3D11, D3D12)" = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + } + + depth_only = { + inherit: ["culling_base_default"] + state: { + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + } + } + + outline_stencil_write = { + inherit: ["culling_base_default"] + state: { + z_enable = "false" + z_write_enable = "false" + + stencil_enable = "true" + stencil_func = "always" + stencil_fail = "stencil_op_keep" + stencil_mask = "0x80" + stencil_pass = "stencil_op_replace" + stencil_ref = "0x80" + stencil_write_mask = "0x80" + stencil_z_fail = "stencil_op_replace" + + stencil_func_back_side = "always" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_replace" + stencil_z_fail_back_side = "stencil_op_replace" + } + } + + outline_stencil_write_z = { + inherit: ["culling_base_default"] + state: { + z_enable = "true" + z_write_enable = "false" + + stencil_enable = "true" + stencil_func = "always" + stencil_fail = "stencil_op_keep" + stencil_mask = "0x80" + stencil_pass = "stencil_op_replace" + stencil_ref = "0x80" + stencil_write_mask = "0x80" + stencil_z_fail = "stencil_op_replace" + + stencil_func_back_side = "always" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_replace" + stencil_z_fail_back_side = "stencil_op_replace" + } + } + + outline_mask_write = { + inherit: ["culling_base_default"] + state: { + z_enable = "true" + z_write_enable = "false" + stencil_enable = "false" + write_mask0 = "red" + } + } + + + shadow_caster = { + inherit: ["depth_only"] + state: { + "on_renderer(D3D11, D3D12)" = { + depth_bias = "0xff" + slope_scale_depth_bias = "2.5" + } + "on_renderer(GNM)" = { + offset_front = "0.0001" + offset_scale_front = "40.0" + offset_back = "0.0001" + offset_scale_back = "40.0" + } + "on_renderer(GL)" = { + offset_factor = "1.0" + offset_units = "1024.0" + depth_bias_enable = "true" + } + } + } + + material_transfer = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + cull_mode = "cull_none" + z_write_enable = "false" + z_enable = "false" + } + } +} + +sampler_state = { + shadow_map = { + inherit: ["core/stingray_renderer/shader_libraries/common#clamp_point"] + states = { + "on_renderer(D3D11, D3D12)" = { + comparison_func = "less" + filter = "comparison_min_mag_linear_mip_point" + } + "on_renderer(GNM)" = { + comparison_func = "less" + filter = "min_mag_mip_linear" + + } + "on_renderer(GL)" = { + comparison_func = "less" + filter = "min_mag_linear" + } + } + } + + clamp_point_no_mip = { + inherits: ["core/stingray_renderer/shader_libraries/common#clamp"] + states = { + "on_renderer(GL)" = { + filter = "min_mag_point" + } + "on_renderer(D3D11, D3D12)" = { + filter = "min_mag_mip_point" + } + "on_renderer(GNM)" = { + filter = "min_mag_mip_point" + } + } + } +} + +channels = { + "(defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE)": { + vertex_tangent = { type = "float3" semantic = "TANGENT" domain = "vertex" } + vertex_binormal = { type = "float3" semantic = "BINORMAL" domain = "vertex" } + + tsm0 = { type = "float3" domains = ["vertex", "pixel"] } + tsm1 = { type = "float3" domains = ["vertex", "pixel"] } + tsm2 = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(MOTION_BLUR)": { + last_clip_position = { type = "float3" domains = ["vertex", "pixel"] } + } + + vertex_position = { type = "float4" domain = "vertex" } + vertex_normal = { type = "float3" semantic = "NORMAL" domain = "vertex" } + + "defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING)": { + vertex_color1 = { type = "float4" semantic = "COLOR1" domains = ["vertex"] } + baked_light = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)": { + lightmap_uv_input = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + lightmap_uv = { type = "float2" domains = ["vertex", "pixel"] } + } + + "defined(MATERIAL_TRANSFER)": { + lightmap_uv = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + } + + "defined(NEEDS_UNSKINNED_WORLD_POS)": { + unskinned_world_pos = { type = "float3" domains = ["vertex", "pixel"] } + unskinned_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } + + "defined(NEEDS_SUN_SHADOW_MASK)": { + //"defined(NEEDS_SUN_SHADOW_MASK) && (defined(EMISSIVE_PASS) || defined(TRANSPARENT) || defined(TRANSPARENT_FADE))": { + sun_shadow_mask = { type = "float" domain = "pixel" } + } +} + +//log_permutations = true +permutation_sets = { + vertex_modifiers = [ + { if: "num_skin_weights() == 4" define: { "macros": ["SKINNED_4WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 3" define: { "macros": ["SKINNED_3WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 2" define: { "macros": ["SKINNED_2WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 1" define: { "macros": ["SKINNED_1WEIGHT"] stages: ["vertex"] } } + { default = true } + ] + + instanced_modifiers = [ + // { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] } + { default = true } + ] + + lightmap_modifiers = [ + // { if: "lightmap_format() == directional_irradiance" define: ["HAS_DIRECTIONAL_LIGHTMAPS"] } + { default = true } + ] + + non_instanced_modifiers = [ + // { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] permute_with: "lightmap_modifiers" } + // { if: "mesh_baked_lighting_type() == vertex" define: ["HAS_VERTEX_BAKED_DIFFUSE_LIGHTING"] } + { permute_with: "vertex_modifiers" } + ] + + instanced_and_non_instanced = [ + { if: "defined(INSTANCED)" permute_with: "instanced_modifiers" } + { if: "!defined(INSTANCED)" permute_with: "non_instanced_modifiers" } + ] + + default = [ + // FBX Standard material exclusive permutations, these will only compile of the + // 'USE_FBX_PERMUTATIONS' option is set, which it is in the shader used for imported fbx files. + //{ if: "defined(USE_FBX_PERMUTATIONS) && is_any_material_variable_set(use_emissive_map, emissive)" define: ["FBX_EMISSIVE"] permute_with: "instanced_and_non_instanced" } + + // Normal default permutation set + { if: "on_renderer(D3D11, D3D12, GNM, GL) && render_cap(development) && render_setting(mipmap_visualization)" defines=["MIPMAP_LEVEL_VISUALIZATION"] permute_with: "instanced_and_non_instanced" } + { if: "on_renderer(D3D11, D3D12, GNM, GL) && (defined(TRANSPARENT) || defined(TRANSPARENT_FADE)) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] permute_with: "instanced_and_non_instanced" } + { permute_with: "instanced_and_non_instanced" } + ] + + shadow_caster = [ + { if: "defined(INSTANCED)" } + { if: "!defined(INSTANCED)" permute_with: "vertex_modifiers" } + ] +} + +//log_permutations = true +//log_constant_buffers = true +shader_contexts = { + shadow_caster = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: "shadow_caster" } + ] + + passes = [ + { code_block="depth_only" render_state="shadow_caster" } + ] + } + + //material_transfer = { + // passes_sort_mode = "immediate" + // compile_with = [ + // { if: "on_renderer(D3D11, D3D12)" } + // ] + // + // passes = [ + // { code_block="gbuffer_base" defines=["MATERIAL_TRANSFER"] render_state="material_transfer" } + // ] + //} + + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: [ + // TODO: create permutation for when local lights is off. + { permute_with: "default" } + ] } + ] + + passes = [ + { if: "(defined(TRANSPARENT) || defined(TRANSPARENT_FADE))" then: [ + { if: "defined(CULL_NONE)" then: [ + // TODO: render transparency in two passes makes the last pass not being sorted correctly with particles + //{ layer="hdr_transparent" code_block="gbuffer_base" defines="CALCULATE_LIGHTING" render_state="transparent_double_sided_ccw" } + //{ layer="hdr_transparent" code_block="gbuffer_base" defines="CALCULATE_LIGHTING" render_state="transparent_double_sided_cw" } + { layer="hdr_transparent" code_block="gbuffer_base" defines="CALCULATE_LIGHTING" render_state="transparent" } + ] else: [ + { layer="hdr_transparent" code_block="gbuffer_base" defines="CALCULATE_LIGHTING" render_state="transparent" } + ]} + ] else: [ + { if: "defined(GBUFFER_POST_OUTLINE)" then: [ + { layer="gbuffer_post_outline" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" } + ] else: [ + { if: "defined(HAS_FOV)" then: [ + { layer="gbuffer_fpr" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" } + ] else: [ + { if: "defined(HAS_OPACITY) || defined(JITTER_TRANSPARENCY)" then: [ + { layer="gbuffer_alpha_masked" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" } + ] else: [ + { layer="gbuffer" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" } + ]} + ]} + ]} + + // This bit of logic is a bit complicated. The gist of it is that we want to disable this pass + // for materials that has a value connected on emissive for all permutations, but should have it + // discarded for all but the special permutations with the define FBX_EMISSIVE + { if: "defined(HAS_EMISSIVE) && (!defined(USE_FBX_PERMUTATIONS) || defined(FBX_EMISSIVE))" then: [ + { layer="emissive" code_block="gbuffer_base" defines={ macros: ["EMISSIVE_PASS"] stages: ["pixel"] } render_state="emissive" } + ]} + + { if: "!defined(OPAQUE_FORWARD_DISABLED)" then: [ + { layer="opaque_forward" code_block="gbuffer_base" defines=["OPAQUE_FORWARD" "CALCULATE_LIGHTING" "MOTION_BLUR"] render_state="culling_base_default" } + ]} + ]} + + // Note we define MOTION_VECTOR for these additional passes to ensure we share constant buffer for bone stransforms with the opaque gbuffer pass + // For transparent surfaces the MOTION_VECTOR define gets undefined in the SKINNED code block + { if: "!on_renderer(GL)" then: [ + { layer="wireframe" code_block="depth_only" defines=["DRAW_WIREFRAME" "MOTION_BLUR"] render_state="wireframe" branch_key="dev_wireframe" } + ]} + + { layer="outline" code_block="depth_only" define="DRAW_OUTLINE" render_state="outline_stencil_write" branch_key="outline_unit" } + { layer="outline" code_block="depth_only" define="DRAW_OUTLINE" render_state="outline_stencil_write_z" branch_key="outline_unit_z" } + + // not used + // { layer="outline_mask" code_block="depth_only" defines=["DRAW_OUTLINE" "OUTLINE_MASK_WRITE"] render_state="outline_mask_write" branch_key="outline_unit_z_fail" } + // { layer="outline" code_block="depth_only" defines=["DRAW_OUTLINE" "OUTLINE_MASK_READ"] render_state="outline_stencil_write" branch_key="outline_unit_z_fail" } + ] + } +} + +code_blocks = { + gbuffer_base = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/common#skinning", + "core/stingray_renderer/shader_libraries/common#taa_offsets", + "core/stingray_renderer/shader_libraries/lighting_common#brdf", + "core/stingray_renderer/shader_libraries/common#fog", + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_bias", + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map_filtering", + "core/stingray_renderer/shader_libraries/lighting_common#lighting", + "core/stingray_renderer/shader_libraries/lighting_common#clustered_shading", + "core/stingray_renderer/shader_libraries/common#billboard_transformation"] + + instance_data = { + "on_renderer(D3D11) && !defined(MATERIAL_TRANSFER) && defined(INSTANCED)": { + world = { type = "matrix4x4" } + "defined(MOTION_BLUR)": { + last_world = { type = "matrix4x4" } + } + } + } + + stage_conditions = { + tessellation_control = "defined(DX11_DISPLACEMENT_MAPPING)" + } + + samplers = { + "defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)": { + lightmap = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "lightmap" + type = "2d" + } + lightmap_ambient_term = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "lightmap_ambient_term" + type = "2d" + } + lightmap_directional_term = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "lightmap_directional_term" + type = "2d" + } + lightmap_dominant_direction = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "lightmap_dominant_direction" + type = "2d" + } + } + + "defined(HAS_REFRACTION)": { + hdr0 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr0" + type = "2d" + } + } + "defined(HAS_REFRACTION) || defined(NEEDS_LINEAR_DEPTH)": { + linear_depth = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "linear_depth" + type = "2d" + } + } + + "defined(CALCULATE_LIGHTING)": { + sun_shadow_map = { + sampler_state = "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map" + slot_name = "sun_shadow_map" + type = "2d" + } + local_lights_shadow_atlas = { + sampler_state = "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map" + slot_name = "local_lights_shadow_atlas" + type = "2d" + } + //cs_cluster_buffer = { + // sampler_state = "clamp_point_no_mip" + // slot_name = "cs_cluster_buffer" + // type = "2d" + //} + //cs_light_index_buffer = { + // sampler_state = "clamp_point_no_mip" + // slot_name = "cs_light_index_buffer" + // type = "2d" + //} + //cs_light_data_buffer = { + // sampler_state = "clamp_point_no_mip" + // slot_name = "cs_light_data_buffer" + // type = "2d" + //} + //cs_light_shadow_matrices_buffer = { + // sampler_state = "clamp_point_no_mip" + // slot_name = "cs_light_shadow_matrices_buffer" + // type = "2d" + //} + static_sun_shadow_map = { + sampler_state = "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map" + slot_name = "static_sun_shadow_map" + type = "2d" + } + global_diffuse_map = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "global_diffuse_map" + type = "cube" + } + global_specular_map = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "global_specular_map" + type = "cube" + } + brdf_lut = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "brdf_lut" + type = "2d" + } + } + + "!defined(LOW_RES)": { + "defined(LOW_RES_ENABLED)": { + hdr_transparent_div4 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr_transparent_div4" + type = "2d" + } + + hdr_linear_depth_div4 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr_linear_depth_div4" + type = "2d" + } + } + } + + // TODO: this is incorrect, we need to actually sample the shadow maps... + /* + "defined(NEEDS_SUN_SHADOW_MASK) && defined(EMISSIVE_PASS)": { + ldr0 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "ldr0" + type = "2d" + } + } + */ + } + + code = { + glsl = """ + #if defined(NEEDS_WORLD_SPACE_POSITION) || defined(NEEDS_TANGENT_SPACE) || defined(NEEDS_WORLD_POSE) || defined(NEEDS_WORLD_SPACE_NORMAL) + #define NEEDS_WORLD + #endif + + #if defined(NEEDS_WORLD_SPACE_POSITION) + #define NEEDS_VIEW_PROJECTION + #endif + + CBUFFER_START(c_per_object) + UNIFORM mat4 world_view_proj; + + #if defined(NEEDS_WORLD) + UNIFORM mat4 world; + #endif + + #if defined(NEEDS_VIEW_PROJECTION) + UNIFORM mat4 view_proj; + #endif + + #if defined(NEEDS_INVERSE_WORLD_POSE) + UNIFORM mat4 inv_world; + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + UNIFORM vec2 lightmap_ambient_term_uv_scale; + UNIFORM vec2 lightmap_ambient_term_uv_offset; + #else + UNIFORM vec2 lightmap_uv_scale; + UNIFORM vec2 lightmap_uv_offset; + #endif + #endif + CBUFFER_END + + #if defined(CALCULATE_LIGHTING) + DECLARE_CLUSTER_DATA(cs_cluster_buffer); + DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer); + DECLARE_LIGHT_DATA(cs_light_data_buffer); + DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer); + + DECLARE_COMPARISON_SAMPLER_2D(sun_shadow_map); + DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas); + DECLARE_SAMPLER_CUBE(global_specular_map); + DECLARE_SAMPLER_2D(brdf_lut); + #endif + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + #if defined(HAS_REFRACTION) || defined(NEEDS_LINEAR_DEPTH) + DECLARE_SAMPLER_2D(hdr0); + uniform highp usampler2D linear_depth; + #define HAS_LINEAR_DEPTH + #endif + #endif + + #if defined(STAGE_VERTEX) + layout(location = POSITION0) in vec4 in_position; + + void main() { + GraphManualChannels params; + GraphResults graph; + + #if defined(SKINNED) + vec4 p = vec4(skin_point(in_position, blendindices, blendweights), 1); + mediump vec3 n = skin_vector(GRAPH_DATA(vertex_normal).xyz, blendindices, blendweights); + #if defined(NEEDS_TANGENT_SPACE) + mediump vec3 t = skin_vector(GRAPH_DATA(vertex_tangent).xyz, blendindices, blendweights); + mediump vec3 b = skin_vector(GRAPH_DATA(vertex_binormal).xyz, blendindices, blendweights); + #endif + #else + vec4 p = in_position; + mediump vec3 n = GRAPH_DATA(vertex_normal); + #if defined(NEEDS_TANGENT_SPACE) + mediump vec3 t = GRAPH_DATA(vertex_tangent); + mediump vec3 b = GRAPH_DATA(vertex_binormal); + #endif + #endif + + #if defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_PARAM(params, world_space_normal) = n * mat3(world); + #endif + + GRAPH_PARAM(params, vertex_position) = p; + + #if defined(NEEDS_WORLD_SPACE_POSITION) + vec4 wp = p * world; + #if defined(NEEDS_WORLD_POS_CHANNEL) + GRAPH_PARAM(params, world_pos) = wp.xyz; + #endif + #endif + + #if defined(NEEDS_TANGENT_SPACE) + tspace_transform_transpose( + GRAPH_PARAM(params, tsm0), + GRAPH_PARAM(params, tsm1), + GRAPH_PARAM(params, tsm2), + t, b, n, + mat3(world)); + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_PARAM(params, eye_vector) = camera_pos - wp.rgb; + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + mediump vec2 light_uv_in = GRAPH_DATA(lightmap_uv); + + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + mediump vec2 luv = light_uv_in*lightmap_ambient_term_uv_scale + lightmap_ambient_term_uv_offset; + #else + mediump vec2 luv = light_uv_in*lightmap_uv_scale + lightmap_uv_offset; + #endif + + GRAPH_PARAM(params, lightmap_uv) = luv; + #endif + + #if defined(NEEDS_SCREEN_SPACE_NORMAL) + GRAPH_PARAM(params, screen_space_normal) = mul(n.xyz, mat3(world_view_proj)); + #endif + + graph_evaluate(graph, params); + + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(HAS_VERTEX_OFFSET) + wp.xyz += graph.vertex_offset; + #if defined(NEEDS_WORLD_POS_CHANNEL) + GRAPH_PARAM(params, world_pos) = wp.xyz; + #endif + #endif + gl_Position = wp * view_proj; + #else + gl_Position = p * world_view_proj; + #endif + } + #elif defined(STAGE_FRAGMENT) + #if defined(EMISSIVE_PASS) + layout(location = 0) out mediump vec4 out_color; + + void main() { + GraphManualChannels params; + GraphResults graph; + graph_evaluate(graph, params); + + #if defined(EMISSIVE_PASS) + #if defined(HAS_OPACITY) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + discard; + #endif + out_color = vec4(graph.emissive, 0); + #endif + } + #else + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + layout(location = 0) out mediump vec4 out_color; + #else + GBUFFER_OUTPUT; + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + DECLARE_SAMPLER_2D(lightmap_ambient_term); + DECLARE_SAMPLER_2D(lightmap_directional_term); + DECLARE_SAMPLER_2D(lightmap_dominant_direction); + #else + DECLARE_SAMPLER_2D(lightmap); + #endif + #endif + + DECLARE_SAMPLER_CUBE(global_diffuse_map); + + void main() { + GraphManualChannels params; + GraphResults graph; + + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + float3 world_pos = GRAPH_DATA(world_pos); + float3 view_dir = float3(camera_world[0].w, camera_world[1].w, camera_world[2].w) - world_pos; + float3 camera_dir = float3(camera_world[0].y, camera_world[1].y, camera_world[2].y); + + float depth = dot(-view_dir, camera_dir); + + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PARAM(params, pixel_depth) = depth; + #define HAS_PIXEL_DEPTH + #endif + + #if defined(NEEDS_SCREEN_POS) || defined(HAS_REFRACTION) + float2 screen_pos = gl_FragCoord.xy/back_buffer_size; + + #if defined(NEEDS_SCREEN_POS) + GRAPH_PARAM(params, screen_pos) = screen_pos; + #endif + #endif + #endif + + graph_evaluate(graph, params); + + #if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5; + #endif + if (graph.opacity < threshold) + discard; + #endif + + // Base color + lowp vec3 base_color = vec3(0); + #if defined(HAS_BASE_COLOR) + base_color = graph.base_color; + #else + base_color = vec3(0.5, 0.5, 0.5); + #endif + + // World space normal + #if defined(HAS_NORMAL) + #if defined(WORLD_SPACE_NORMAL) + mediump vec3 wn = normalize(graph.normal); + #if defined(CULL_NONE) || defined(CULL_FRONT) + wn = !gl_FrontFacing ? wn : -wn; + #endif + #else + float3 tsm0 = GRAPH_DATA(tsm0).xyz; + float3 tsm1 = GRAPH_DATA(tsm1).xyz; + float3 tsm2 = GRAPH_DATA(tsm2).xyz; + #if defined(CULL_NONE) || defined(CULL_FRONT) + if (gl_FrontFacing) { + tsm0.z = -tsm0.z; + tsm1.z = -tsm1.z; + tsm2.z = -tsm2.z; + } + #endif + mediump vec3 wn = rotate_vector3(graph.normal, tsm0, tsm1, tsm2); + #endif + #else + mediump vec3 wn = normalize(GRAPH_DATA(world_space_normal)); + #if defined(CULL_NONE) || defined(CULL_FRONT) + wn = !gl_FrontFacing ? wn : -wn; + #endif + #endif + + // Metallic + lowp float metallic = 0.0; + #if defined(HAS_METALLIC) + metallic = graph.metallic; + #else + metallic = 0.0; + #endif + + // Roughness + half roughness = 0.0; + #if defined(HAS_ROUGHNESS) + roughness = max(graph.roughness, 1.f / 255.f); + #else + roughness = 0.5; + #endif + + // Ambient Diffuse + mediump vec3 ambient = vec3(0.0, 0.0, 0.0); + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + ambient = TEX2D(lightmap_ambient_term, GRAPH_DATA(lightmap_uv)).rgb; + mediump vec3 dd = TEX2D(lightmap_dominant_direction, GRAPH_DATA(lightmap_uv)).rgb * 2.0 - vec3(1.0); + ambient += TEX2D(lightmap_directional_term, GRAPH_DATA(lightmap_uv)).rgb * max(0.0, dot(dd, wn)); + #else + ambient = TEX2D(lightmap, GRAPH_DATA(lightmap_uv)).rgb; + #endif + #else + ambient = gbuffer_decode_ambient_diffuse_light(TEXCUBELOD(global_diffuse_map, wn, 0.0)); + #endif + + lowp float ambient_occlusion = 1.0; + #if defined(HAS_AMBIENT_OCCLUSION) + ambient_occlusion = graph.ambient_occlusion; + #endif + + // Density + half density = 1.0; + #if defined(HAS_DENSITY) + density = graph.density; + #endif + #if !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + out_base_color = gbuffer_encode_base_color(base_color); + out_normal = gbuffer_encode_normal(wn); + out_metallic = gbuffer_encode_metallic_mask(metallic); + out_roughness = gbuffer_encode_roughness(roughness); + out_ambient_occlusion = gbuffer_encode_ambient_occlusion(ambient_occlusion); + out_ambient_diffuse_light = gbuffer_encode_ambient_diffuse_light(ambient); + + out_density = gbuffer_encode_density(density); + #else + #if defined(HAS_OPACITY) + half opacity = graph.opacity; + #else + half opacity = 0.5; + #endif + + float3 V = normalize(view_dir); + float3 N = normalize(wn); + + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + float3 specular_color = lerp(new_half3(0.04, 0.04, 0.04), base_color, metallic); + float3 diffuse_color = lerp(base_color, new_half3(0.0, 0.0, 0.0), metallic); + + float3 acc_diff = new_half3(0.0, 0.0, 0.0); + float3 acc_spec = new_half3(0.0, 0.0, 0.0); + float3 translucency = new_half3(0.0, 0.0, 0.0); + #if defined(CALCULATE_LIGHTING) + calculate_lighting(world_pos, depth, sun_shadow_map, brdf_lut, global_specular_map, N, V, roughness, ambient, diffuse_color, specular_color, density, base_color, acc_diff, acc_spec, translucency); + clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, cs_light_shadow_matrices_buffer,local_lights_shadow_atlas, world_pos, V, N, diffuse_color, specular_color, roughness, gl_FragCoord.xy, depth, density, base_color, acc_diff, acc_spec, translucency); + #endif + + float3 acc_refraction = new_half3(0,0,0); + #if defined(HAS_REFRACTION) + #if defined(HAS_NORMAL) + float3 distortion_normal = normalize( mul(N, mat3(view_proj)) ); + #else + float3 distortion_normal = GRAPH_DATA(screen_space_normal); + #endif + + // put refraction value more towards the range of real material IOR values (air=1.0 glass=1.5). + half ior_air = 1.0; + half ior_range_bias = 0.1; + half2 distorted_uv = screen_pos - distortion_normal.xy * (graph.refraction - ior_air) * ior_range_bias; + + // avoid including pixels from objects in front of the refractive object + highp float refraction_depth = uintBitsToFloat(texture(linear_depth, distorted_uv).r); + bool depth_ok = depth < refraction_depth; + distorted_uv = lerp(screen_pos, distorted_uv, vec2(depth_ok, depth_ok)); + + acc_refraction = TEX2D(hdr0, distorted_uv).rgb; + acc_refraction *= (1.0 - opacity); + + #if defined(TRANSPARENT_FADE) + acc_diff *= opacity_to_use; + acc_spec *= opacity_to_use; + #endif + #endif + + #if defined(TRANSPARENT) + float3 accumulated_color = acc_diff * opacity + acc_refraction + acc_spec; + #else + float3 accumulated_color = acc_diff + acc_refraction + acc_spec; + #endif + accumulated_color += translucency; + + #if defined(HAS_EMISSIVE) + accumulated_color += graph.emissive; + #endif + + out_color = apply_fog(new_half4_xyz(accumulated_color, opacity), world_pos, depth); + #endif + } + #endif + #endif + """ + + hlsl = """ + // We need to disable instancing for the material transfer context as it doesn't use the world transform. + #if defined(INSTANCED) && defined(MATERIAL_TRANSFER) + #undef INSTANCED + #endif + + #if defined(CALCULATE_LIGHTING) || defined(NEEDS_PIXEL_DEPTH) || (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) + #define PS_NEEDS_WP + #endif + + #if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) || ((defined(RENDERER_D3D11)) && defined(INSTANCED)) || defined(BILLBOARD) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + #if (defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(HAS_ANISOTROPY) + #define NEEDS_TANGENT_SPACE + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + DECLARE_SAMPLER_2D(lightmap_ambient_term); + DECLARE_SAMPLER_2D(lightmap_directional_term); + DECLARE_SAMPLER_2D(lightmap_dominant_direction); + #else + DECLARE_SAMPLER_2D(lightmap); + #endif + #endif + + #if defined(NEEDS_INVERSE_WORLD_POSE) + #define NEEDS_INV_WORLD; + #endif + + #if defined(NEEDS_SUN_SHADOW_MASK) && defined(EMISSIVE_PASS) + DECLARE_SAMPLER_2D(ldr0); + #endif + + struct VS_INPUT + { + float4 position : POSITION; + SKIN_INPUT + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT + { + float4 position : SV_POSITION; + #if defined(PS_NEEDS_WP) + float3 world_pos : TEXCOORD15; + #endif + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_per_object) + float4x4 view_proj; + float4x4 world_view_proj; + #if defined(NEEDS_INV_WORLD) + float4x4 inv_world; + #endif + float4x4 world; + float4x4 last_world; + + #if defined(JITTER_TRANSPARENCY) + float inv_jitter_alpha; + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + float2 lightmap_ambient_term_uv_scale; + float2 lightmap_ambient_term_uv_offset; + #else + float2 lightmap_uv_scale; + float2 lightmap_uv_offset; + #endif + #endif + + #if defined(BILLBOARD) && defined(SKYDOME_BILLBOARD) + #if defined(SECONDARY_SUN_DIRECTION) + float3 secondary_sun_direction; + #define billboard_direction secondary_sun_direction + #else + float3 sun_direction; + #define billboard_direction sun_direction + #endif + #endif + + #if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER) + float global_roughness_multiplier; + #endif + + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + #if defined(CALCULATE_LIGHTING) + DECLARE_CLUSTER_DATA(cs_cluster_buffer); + DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer); + DECLARE_LIGHT_DATA(cs_light_data_buffer); + DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer); + + DECLARE_COMPARISON_SAMPLER_2D(sun_shadow_map); + DECLARE_COMPARISON_SAMPLER_2D_SHARED(static_sun_shadow_map, sun_shadow_map); + DECLARE_COMPARISON_SAMPLER_2D_SHARED(local_lights_shadow_atlas, sun_shadow_map); + DECLARE_SAMPLER_CUBE(global_specular_map); + DECLARE_SAMPLER_CUBE_SHARED(global_diffuse_map, global_specular_map); + DECLARE_SAMPLER_2D_SHARED(brdf_lut, global_specular_map); + #endif + + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + #if defined(HAS_REFRACTION) + DECLARE_SAMPLER_2D_SHARED(hdr0, global_specular_map); + #endif + #if defined(HAS_REFRACTION) || defined(NEEDS_LINEAR_DEPTH) + DECLARE_SAMPLER_2D(linear_depth); + #define HAS_LINEAR_DEPTH + #endif + #endif + + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + DECLARE_SAMPLER_2D_SHARED(hdr_transparent_div4, global_specular_map); + DECLARE_SAMPLER_2D_SHARED(hdr_linear_depth_div4, global_specular_map); + #endif + + #if defined(INSTANCED) && (defined(RENDERER_D3D11)) + Buffer idata; + float ioffset; + #endif + + inline float3 calc_clip_position_with_halton_offset(float3 wp, float4x4 view_proj) + { + float4 clip_pos = mul(float4(wp, 1), view_proj); + float4 view_space = clip_pos / clip_pos.w; + view_space.xy += get_vs_halton_offset(frame_number); + view_space.xy = view_space.xy * 0.5 + 0.5; + view_space.y = 1.0 - view_space.y; + clip_pos = view_space * clip_pos.w; + return clip_pos.xyw; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input + #if defined(INSTANCED) && (defined(RENDERER_D3D11)) + , uint instance_id : SV_InstanceId + #endif + ) + { + PS_INPUT o; + float4 p; + + GraphVertexParams params; + GraphVertexResults results; + + #if defined(INSTANCED) && (defined(RENDERER_D3D11)) + uint offset = (uint)ioffset + instance_id*IDATA_STRIDE; + world[0] = idata.Load(offset + IDATA_world + 0); + world[1] = idata.Load(offset + IDATA_world + 1); + world[2] = idata.Load(offset + IDATA_world + 2); + world[3] = idata.Load(offset + IDATA_world + 3); + + #if defined(MOTION_BLUR) + last_world[0] = idata.Load(offset + IDATA_last_world + 0); + last_world[1] = idata.Load(offset + IDATA_last_world + 1); + last_world[2] = idata.Load(offset + IDATA_last_world + 2); + last_world[3] = idata.Load(offset + IDATA_last_world + 3); + #endif + #endif + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + // Write output channels + #if defined(SKINNED) + float4 position = float4(skin_point(input.position, input.blendindices, input.blendweights), 1); + #if defined(MOTION_BLUR) + float4 last_position = float4(skin_point_last_frame(input.position, input.blendindices, input.blendweights), 1); + #endif + #if !defined(BILLBOARD) + float3 normal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_normal).xyz, input.blendindices, input.blendweights); + #if defined(NEEDS_TANGENT_SPACE) + float3 tangent = skin_vector(GRAPH_VERTEX_DATA(input, vertex_tangent).xyz, input.blendindices, input.blendweights); + float3 binormal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_binormal).xyz, input.blendindices, input.blendweights); + #endif + #endif + #else + float4 position = input.position; + #if defined(MOTION_BLUR) + float4 last_position = position; + #endif + #if !defined(BILLBOARD) + float3 normal = GRAPH_VERTEX_DATA(input, vertex_normal).xyz; + #if defined(NEEDS_TANGENT_SPACE) + float3 tangent = GRAPH_VERTEX_DATA(input, vertex_tangent).xyz; + float3 binormal = GRAPH_VERTEX_DATA(input, vertex_binormal).xyz; + #endif + #endif + #endif + + #if defined(BILLBOARD) + float4 wp; + float3 normal, tangent, binormal; + #if !defined(SKYDOME_BILLBOARD) + get_billboard_data_from_position(world._m30_m31_m32, camera_world._m30_m31_m32, camera_view, position, wp, normal, tangent, binormal); + #else + get_billboard_data_from_direction(billboard_direction, position, wp, normal, tangent, binormal); + #endif + #elif defined(NEEDS_WORLD_SPACE_POSITION) + float4 wp = mul(position, world); + #endif + + #if defined(NEEDS_UNSKINNED_WORLD_POS) + float4 unskinned_wp = mul(input.position, world); + GRAPH_VERTEX_PARAM(params, unskinned_world_pos) = unskinned_wp.xyz; + GRAPH_VERTEX_PARAM(params, unskinned_normal) = GRAPH_VERTEX_DATA(input, vertex_normal).xyz; + #endif + + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + #if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_VERTEX_PARAM(params, world_space_normal).rgb = mul(normal, (float3x3)world); + #endif + + #if defined(NEEDS_EYE_VECTOR) + #if defined(BILLBOARD) && defined(SKYDOME_BILLBOARD) + // TODO: not correct length, we can't use length(eye_vector) to determine the distance + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = billboard_direction; + #else + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp.rgb; + #endif + #endif + + #if defined(NEEDS_TANGENT_SPACE) + #if defined(LOCK_NORMAL_ROTATION) + tspace_transpose( + GRAPH_VERTEX_PARAM(params, tsm0), + GRAPH_VERTEX_PARAM(params, tsm1), + GRAPH_VERTEX_PARAM(params, tsm2), + tangent, binormal, normal); + #else + tspace_transform_transpose( + GRAPH_VERTEX_PARAM(params, tsm0), + GRAPH_VERTEX_PARAM(params, tsm1), + GRAPH_VERTEX_PARAM(params, tsm2), + tangent, binormal, normal, + (float3x3)world); + #endif + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(HAS_VERTEX_OFFSET) + wp += float4(results.vertex_offset, 0); + #endif + + #if defined(HAS_FOV) + p = mul(wp, camera_custom_fov_view_projection); + #else + p = mul(wp, view_proj); + #endif + #else + #if defined(HAS_FOV) + // TODO: create camera_custom_fov_world_view_projection? + p = mul(mul(position, world), camera_custom_fov_view_projection); + #else + p = mul(position, world_view_proj); + #endif + #endif + + #if defined(MOTION_BLUR) + GRAPH_VERTEX_PARAM(params, last_clip_position) = float3(0.0, 0.0, 0.0); + #if defined(NEEDS_WORLD_SPACE_POSITION) + float4 last_wp = mul(last_position, last_world); + + #if defined(HAS_VERTEX_OFFSET) + // TODO: Add _last_ position offset here to support vertex animation. + // The way it works now will only yield correct results of the offset is constant + last_wp += float4(results.vertex_offset, 0); + #endif + #else + float4 last_wp = mul(last_position, last_world); + #endif + #if defined(HAS_FOV) + float4 last_clip_pos = mul(last_wp, camera_custom_fov_last_view_projection); + #else + float4 last_clip_pos = mul(last_wp, camera_last_view_projection); + #endif + float4 last_view_space = last_clip_pos / last_clip_pos.w; + last_view_space.xy += get_vs_halton_offset(frame_number); + last_view_space.xy = last_view_space.xy * 0.5 + 0.5; + last_view_space.y = 1.0 - last_view_space.y; + last_clip_pos = last_view_space * last_clip_pos.w; + GRAPH_VERTEX_PARAM(params, last_clip_position) = last_clip_pos.xyw; + #endif + + #if defined(MATERIAL_TRANSFER) + float2 unwrapped_uv = GRAPH_VERTEX_DATA(input, lightmap_uv); + float2 ndc = float2(unwrapped_uv.x, unwrapped_uv.y) * 2 - 1; + ndc.y *= -1; + p = float4(ndc, 0, 1); + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + GRAPH_VERTEX_PARAM(params, lightmap_uv) = float2(0,0); + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + GRAPH_VERTEX_PARAM(params, lightmap_uv) = GRAPH_VERTEX_DATA(input, lightmap_uv_input)*lightmap_ambient_term_uv_scale + lightmap_ambient_term_uv_offset; + #else + GRAPH_VERTEX_PARAM(params, lightmap_uv) = GRAPH_VERTEX_DATA(input, lightmap_uv_input)*lightmap_uv_scale + lightmap_uv_offset; + #endif + #endif + + #if defined(PS_NEEDS_WP) + o.world_pos = wp.xyz; + #endif + + #if defined(MATERIAL_TRANSFER) + o.position = p; + #else + float4 view_space = p / p.w; + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + #endif + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + #if defined(MATERIAL_TRANSFER) + struct MATERIAL_TRANSFER_OUT { + float4 albedo_op : SV_TARGET0; + float4 emissive : SV_TARGET1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + MATERIAL_TRANSFER_OUT ps_main(PS_INPUT input) : SV_TARGET0 + { + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if defined(PS_NEEDS_WP) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + const float depth = dot(-view_dir, camera_dir); + #endif + + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #endif + + #if defined(NEEDS_SCREEN_POS) + float2 screen_position = (input.position.xy / back_buffer_size); + GRAPH_PIXEL_PARAM(params, screen_pos) = screen_position; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + MATERIAL_TRANSFER_OUT o; + + float opacity = 1.0; + + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + #if defined(HAS_OPACITY) + opacity = graph.opacity; + #else + opacity = 0.5; + #endif + #elif defined(HAS_OPACITY) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + opacity = 0; + else + opacity = 1.0; + #endif + + #if defined(HAS_BASE_COLOR) + o.albedo_op = float4(graph.base_color, opacity); + #else + o.albedo_op = float4(0.5, 0.5, 0.5, opacity); + #endif + + #if defined(HAS_EMISSIVE) + o.emissive = float4(graph.emissive, 1.0); + #else + o.emissive = float4(0.0, 0.0, 0.0, 0.0); + #endif + + return o; + } + #elif defined(EMISSIVE_PASS) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if defined(JITTER_TRANSPARENCY) + uint2 pos = uint2(input.position.xy); + float alpha = (float)((pos.x%2u) + 2u*(pos.y%2u)) * 0.25; + if (alpha >= 1.0 - inv_jitter_alpha) + discard; + #endif + + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if defined(PS_NEEDS_WP) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + + const float depth = dot(-view_dir, camera_dir); + #endif + + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #endif + + #if defined(NEEDS_SCREEN_POS) + float2 screen_position = (input.position.xy / back_buffer_size); + GRAPH_PIXEL_PARAM(params, screen_pos) = screen_position; + #endif + + #if defined(NEEDS_SUN_SHADOW_MASK) + GRAPH_PIXEL_PARAM(params, sun_shadow_mask) = ldr0.tex.Load(int3(input.position.xy, 0)).r; + #define HAS_SUN_SHADOW_MASK + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(EMISSIVE_PASS) + #if defined(HAS_OPACITY) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + discard; + #endif + return float4(graph.emissive, 0); + #endif + } + #else + #if defined(CALCULATE_LIGHTING) + float4 apply_fog_data(float4 color, float4 fog_data) { + return float4(color.rgb * (1.0 - fog_data.a) + fog_data.rgb * fog_data.a, color.a); + } + + struct PS_OUTPUT { + half4 color : SV_TARGET0; + #if defined(MOTION_BLUR) + half2 buffer4 : SV_TARGET1; + #endif + }; + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + GBUFFER_OUT ps_main(PS_INPUT input + #endif + #if defined(CULL_NONE) || defined(CULL_FRONT) + #if defined(GNM) + , bool vface : S_FRONT_FACE + #else + , float vface : VFACE + #endif + #endif + ) + { + #if defined(CALCULATE_LIGHTING) + PS_OUTPUT o; + #else + GBUFFER_OUT o; + #endif + + #if defined(JITTER_TRANSPARENCY) && !(defined(TRANSPARENT) || defined(TRANSPARENT_FADE)) + uint2 pos = uint2(input.position.xy); + float alpha = (float)((pos.x%2u) + 2u*(pos.y%2u)) * 0.25; + if (alpha >= 1.0 - inv_jitter_alpha) + discard; + #endif + + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if defined(PS_NEEDS_WP) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + + const float depth = dot(-view_dir, camera_dir); + #endif + + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #define HAS_PIXEL_DEPTH + #endif + + #if defined(NEEDS_SCREEN_POS) || defined(HAS_REFRACTION) || defined(CALCULATE_LIGHTING) + float2 screen_pos = (input.position.xy / back_buffer_size); + + #if defined(NEEDS_SCREEN_POS) + GRAPH_PIXEL_PARAM(params, screen_pos) = screen_pos; + #endif + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + discard; + #endif + + + // Base color + float3 base_color_to_use = float3(0,0,0); + #if defined(HAS_BASE_COLOR) + base_color_to_use = graph.base_color; + #else + base_color_to_use = float3(0.5, 0.5, 0.5); + #endif + + // World space normal + #if defined(HAS_NORMAL) + #if !defined(WORLD_SPACE_NORMAL) + float3 tsm0 = GRAPH_PIXEL_DATA(input, tsm0).xyz; + float3 tsm1 = GRAPH_PIXEL_DATA(input, tsm1).xyz; + float3 tsm2 = GRAPH_PIXEL_DATA(input, tsm2).xyz; + #if (defined(CULL_NONE) || defined(CULL_FRONT)) && !defined(NORMAL_FLIP_DISABLED) + if (!front_facing(vface)) { + tsm0.z = -tsm0.z; + tsm1.z = -tsm1.z; + tsm2.z = -tsm2.z; + } + #endif + float3 wn = rotate_vector3(graph.normal, tsm0, tsm1, tsm2); + #if defined(LOCK_NORMAL_ROTATION) + wn = mul(wn, (float3x3)world); + #endif + #else + float3 wn = normalize(graph.normal); + #if (defined(CULL_NONE) || defined(CULL_FRONT)) && !defined(NORMAL_FLIP_DISABLED) + wn = !front_facing(vface) ? -wn : wn; + #endif + #endif + #else + float3 wn = normalize((float3)GRAPH_PIXEL_DATA(input, world_space_normal).rgb); + #if (defined(CULL_NONE) || defined(CULL_FRONT)) && !defined(NORMAL_FLIP_DISABLED) + wn = !front_facing(vface) ? -wn : wn; + #endif + #endif + + // Metallic + half metallic_ = 0.f; + #if defined(HAS_METALLIC) + metallic_ = graph.metallic; + #else + metallic_ = 0.f; + #endif + + // Roughness + half roughness_ = 0.f; + #if defined(HAS_ROUGHNESS) + roughness_ = graph.roughness; + #else + roughness_ = 0.5; + #endif + #if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER) + roughness_ *= global_roughness_multiplier; + #endif + + // Velocity vector + #if defined(MOTION_BLUR) + float3 last_clip_pos = GRAPH_PIXEL_DATA(input, last_clip_position); + float2 current_screen_pos = (input.position.xy / back_buffer_size - viewport.zw) / viewport.xy; + float2 last_screen_pos = last_clip_pos.xy / last_clip_pos.z; + VELOCITY(o) = encode_velocity(viewport.xy*(current_screen_pos - last_screen_pos)); + #endif + + #if defined(CALCULATE_LIGHTING) + // Ambient Diffuse + float3 ambient = float3(0, 0, 0); + #if defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING) + ambient = GRAPH_PIXEL_DATA(input, baked_light).rgb; + #elif defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + ambient = TEX2D(lightmap_ambient_term, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb; + float3 dd = TEX2D(lightmap_dominant_direction, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb * 2 - 1; + ambient += TEX2D(lightmap_directional_term, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb * max(0, dot(dd, wn)); + #else + ambient = TEX2D(lightmap, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb; + #endif + #else + ambient = rgbm_decode(TEXCUBELOD(global_diffuse_map, wn, 0)); + #endif + + // ensure that lightmap debug grid is visible in dark areas of a level + #if defined(DRAW_LIGHTMAP_TEXELS) && defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + ambient += float3(0.1, 0.1, 0.1); + #endif + #endif + + // Density + half density_ = 1.0f; + #if defined(HAS_SKIN_SSS_STRENGTH) && (!defined(TRANSPARENT) && !defined(TRANSPARENT_FADE)) + density_ = lerp(1.0, graph.sss_strength, skin_material_enabled); + #elif defined(HAS_DENSITY) + density_ = graph.density; + #endif + #if !defined(CALCULATE_LIGHTING) + BASE_COLOR(o) = gbuffer_encode_base_color(base_color_to_use); + NORMAL(o) = gbuffer_encode_normal(wn); + METALLIC(o) = gbuffer_encode_metallic_mask(metallic_); + ROUGHNESS(o) = gbuffer_encode_roughness(roughness_); + // AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(ambient); + + #if defined(HAS_AMBIENT_OCCLUSION) + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(graph.ambient_occlusion); + #else + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(1.f); + #endif + DENSITY(o) = gbuffer_encode_density(density_); + #else + #if defined(HAS_OPACITY) + half opacity_to_use = graph.opacity; + #else + half opacity_to_use = 0.5; + #endif + + + const float3 V = normalize(view_dir); + const float3 N = wn; + + #if defined(CALCULATE_LIGHTING) && defined(HAS_ANISOTROPY) + float anisotropy_ = graph.anisotropy; + + // TODO: Reuse and use the real tangent and binormal. Optional should be to input a tangent/binormal map. + float3 tsm00 = GRAPH_PIXEL_DATA(input, tsm0).xyz; + float3 tsm11 = GRAPH_PIXEL_DATA(input, tsm1).xyz; + float3 tsm22 = GRAPH_PIXEL_DATA(input, tsm2).xyz; + + // create tangentspace vectors + #ifdef HAS_NORMAL + float3 B = normalize(float3(tsm00.y, tsm11.y, tsm22.y)); + float3 T = cross(B, N); + B = cross(N, T); + #else + float3 T = normalize(float3(tsm00.x, tsm11.x, tsm22.x)); + float3 B = normalize(float3(tsm00.y, tsm11.y, tsm22.y)); + #endif + #endif + + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + const float3 specular_color = lerp(float3(0.04,0.04,0.04), base_color_to_use, metallic_); + float3 diffuse_color = lerp(base_color_to_use, new_half3(0,0,0), metallic_); + + float3 acc_diff = 0; + float3 acc_spec = 0; + float3 translucency = 0; + #if defined(CALCULATE_LIGHTING) + #if defined(D3D11) + // If we are currently capturing a reflection probe, use specular F0 as diffuse color for metallics + diffuse_color = (capture_cubemap == 1) ? lerp(diffuse_color, specular_color, metallic_) : diffuse_color; + #endif + + calculate_lighting(world_pos, depth, sun_shadow_map, static_sun_shadow_map, brdf_lut, global_specular_map, N, V, roughness_, ambient, diffuse_color, specular_color, density_, base_color_to_use, acc_diff, acc_spec, translucency); + clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, cs_light_shadow_matrices_buffer, local_lights_shadow_atlas, world_pos, V, N, diffuse_color, specular_color, roughness_, input.position.xy, input.position.w, density_, base_color_to_use, acc_diff, acc_spec, translucency); + #endif + + float3 acc_refraction = float3(0,0,0); + #if defined(HAS_REFRACTION) + #if defined(HAS_NORMAL) + float3 distortion_normal = normalize( mul(N, (float3x3)view_proj) ); + #else + float3 distortion_normal = GRAPH_CHANNEL(input, screen_space_normal); + #endif + + // A negative screen-space normal (-y) means we want to sample more towards the bottom of the screen, so we need to flip this value, because our screen_pos uses 0,0 for top left corner + distortion_normal.y = 1-distortion_normal.y; + + // put refraction value more towards the range of real material IOR values (air=1.0 glass=1.5). + half ior_air = 1.0; + half ior_range_bias = 0.1; + half2 distorted_uv = screen_pos - distortion_normal.xy * (graph.refraction - ior_air) * ior_range_bias; + + // avoid including pixels from objects in front of the refractive object + float refraction_depth = TEX2D(linear_depth, distorted_uv).r; + bool depth_ok = depth < refraction_depth; + distorted_uv = lerp(screen_pos, distorted_uv, depth_ok); + + acc_refraction = TEX2D(hdr0, distorted_uv).rgb; + acc_refraction *= (1 - opacity_to_use); + #endif + + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + float2 screen_uv = input.position.xy / back_buffer_size; + + // Fade out high resolution particle if the low resolution particles is infront of the high resolution particle + float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg; + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + float background_depth = depth; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * max(depth_variance, 0.001))); + float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T); + opacity_to_use *= 1.0 - alpha_modifier; + #endif + + #if defined(JITTER_TRANSPARENCY) + opacity_to_use *= 1.0 - inv_jitter_alpha; + #endif + + float3 accumulated_color = acc_diff + translucency; + #if defined(HAS_EMISSIVE) + accumulated_color += graph.emissive; + #endif + #if defined(TRANSPARENT) + // Transparent don't apply opacity on specular reflection or refraction. + float4 fog_data = calc_fog_vs(world_pos, depth); + float4 color = apply_fog_data(half4(accumulated_color, opacity_to_use), fog_data); + color.rgb = color.rgb * color.a + acc_spec * (1.0 - fog_data.a); + #else + accumulated_color += acc_spec; + float4 color = apply_fog(half4(accumulated_color, opacity_to_use), world_pos, depth); + color.rgb = color.rgb * color.a; + #endif + color.rgb += acc_refraction; + o.color = color; + #if defined(OPAQUE_FORWARD) + o.color.a = 1.0; // make sure it is solid + #endif + #endif + + return o; + } + #endif + """ + } + } + + depth_only = { + include: [ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#skinning", + "core/stingray_renderer/shader_libraries/common#billboard_transformation", + "core/stingray_renderer/shader_libraries/common#taa_offsets"] + + instance_data = { + "on_renderer(D3D11) && defined(INSTANCED)": { + world = { type = "matrix4x4" } + + "defined(DRAW_WIREFRAME)": { + dev_wireframe_color = { type = "vector4" } + } + "defined(DRAW_OUTLINE)": { + outline_color = { type = "vector4" } + outline_time = { type = "scalar" } + "defined(JITTER_TRANSPARENCY)": { + inv_jitter_alpha = { type = "scalar" } + } + } + } + } + + samplers = { + "defined(OUTLINE_MASK_READ)": { + outline_mask = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "outline_mask" + type = "2d" + } + } + } + + code = { + glsl = """ + CBUFFER_START(c_per_object) + UNIFORM mat4 world_view_proj; + #if defined(NEEDS_WORLD_SPACE_POSITION) || defined(NEEDS_WORLD_SPACE_NORMAL) + UNIFORM mat4 world; + UNIFORM mat4 view_proj; + #endif + #if defined(NEEDS_INVERSE_WORLD_POSE) + UNIFORM mat4 inv_world; + #endif + CBUFFER_END + + #if defined(STAGE_VERTEX) + layout(location = POSITION0) in vec4 in_position; + + void main() { + GraphManualChannels params; + GraphResults graph; + + #if defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_PARAM(params, world_space_normal) = GRAPH_DATA(vertex_normal) * mat3(world); + #endif + + #if defined(SKINNED) + vec4 op = vec4(skin_point(in_position, blendindices, blendweights), 1); + #else + vec4 op = in_position; + #endif + GRAPH_PARAM(params, vertex_position) = op; + + #if defined(NEEDS_WORLD_SPACE_POSITION) + vec4 wp = op * world; + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_PARAM(params, eye_vector) = camera_pos - wp.rgb; + #endif + + graph_evaluate(graph, params); + + #if defined(HAS_VERTEX_OFFSET) + wp.xyz += graph.vertex_offset; + gl_Position = wp * view_proj; + #else + gl_Position = op * world_view_proj; + #endif + } + #elif defined(STAGE_FRAGMENT) + void main() { + #if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + GraphManualChannels params; + GraphResults graph; + graph_evaluate(graph, params); + + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + + if (graph.opacity < threshold) + discard; + #endif + } + #endif + """ + + hlsl = """ + + #if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) || ((defined(RENDERER_D3D11)) && defined(INSTANCED)) || defined(BILLBOARD) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + struct VS_INPUT { + float4 position : POSITION; + SKIN_INPUT + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if (defined(RENDERER_D3D11)) && defined(INSTANCED) + #if defined(DRAW_WIREFRAME) + float4 instance_wireframe_color : COLOR0; + #if defined(DRAW_OUTLINE) + float4 instance_outline_color : COLOR1; + float instance_outline_time : COLOR2; + #if defined(JITTER_TRANSPARENCY) + float instance_inv_jitter_alpha : COLOR3; + #endif + #endif + #elif defined(DRAW_OUTLINE) + float4 instance_outline_color : COLOR0; + float instance_outline_time : COLOR1; + #if defined(JITTER_TRANSPARENCY) + float instance_inv_jitter_alpha : COLOR2; + #endif + #endif + #endif + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_depth_only) + float4x4 world; + #if defined(NEEDS_WORLD_SPACE_POSITION) + float4x4 view_proj; + #else + float4x4 world_view_proj; + #endif + #if defined(NEEDS_INVERSE_WORLD_POSE) + float4x4 inv_world; + #endif + float4 dev_wireframe_color; + + #if defined(BILLBOARD) && defined(SKYDOME_BILLBOARD) + #if defined(SECONDARY_SUN_DIRECTION) + float3 secondary_sun_direction; + #define billboard_direction secondary_sun_direction + #else + float3 sun_direction; + #define billboard_direction sun_direction + #endif + #endif + + float4 outline_color; + float outline_time; + float inv_jitter_alpha; + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + #if defined(OUTLINE_MASK_READ) + DECLARE_SAMPLER_2D(outline_mask); + #endif + + #if defined(INSTANCED) && (defined(RENDERER_D3D11)) + Buffer idata; + float ioffset; + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input + #if defined(INSTANCED) && (defined(RENDERER_D3D11)) + , uint instance_id : SV_InstanceId + #endif + ) + { + PS_INPUT o; + float4 p; + + GraphVertexParams params; + GraphVertexResults results; + + #if ((defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE)) + GRAPH_VERTEX_PARAM(params, tsm0) = float3(0, 0, 0); + GRAPH_VERTEX_PARAM(params, tsm1) = float3(0, 0, 0); + GRAPH_VERTEX_PARAM(params, tsm2) = float3(0, 0, 0); + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + GRAPH_VERTEX_PARAM(params, lightmap_uv) = float2(0, 0); + #endif + + #if defined(INSTANCED) && (defined(RENDERER_D3D11)) + uint offset = (uint)ioffset; + world[0] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 0)); + world[1] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 1)); + world[2] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 2)); + world[3] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 3)); + + #if defined(DRAW_WIREFRAME) + o.instance_wireframe_color = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_dev_wireframe_color)); + #endif + #if defined(DRAW_OUTLINE) + o.instance_outline_color = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_outline_color)); + o.instance_outline_time = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_outline_time)); + #if defined(JITTER_TRANSPARENCY) + o.instance_inv_jitter_alpha = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_inv_jitter_alpha)); + #endif + #endif + #endif + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + // Write output channels + #if defined(SKINNED) + float4 position = float4(skin_point(input.position, input.blendindices, input.blendweights), 1); + #if !defined(BILLBOARD) && (!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)) + float3 normal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_normal).xyz, input.blendindices, input.blendweights); + #endif + #else + float4 position = input.position; + #if !defined(BILLBOARD) && (!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)) + float3 normal = GRAPH_VERTEX_DATA(input, vertex_normal).xyz; + #endif + #endif + + #if defined(BILLBOARD) + #if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL) + float4 wp; + float3 normal, tangent, binormal; + #if !defined(SKYDOME_BILLBOARD) + get_billboard_data_from_position(world._m30_m31_m32, camera_world._m30_m31_m32, camera_view, position, wp, normal, tangent, binormal); + #else + get_billboard_data_from_direction(billboard_direction, position, wp, normal, tangent, binormal); + #endif + #else + float4 wp; + #if !defined(SKYDOME_BILLBOARD) + get_billboard_positions_from_position(world._m30_m31_m32, camera_world._m30_m31_m32, camera_view, position, wp); + #else + get_billboard_positions_from_direction(billboard_direction, position, wp); + #endif + #endif + #elif defined(NEEDS_WORLD_SPACE_POSITION) + float4 wp = mul(position, world); + // TODO: Expose output channel here + #endif + + // Write output channels + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + // All members of the params struct has to be initialized, so assign dummy normal here. + #if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_VERTEX_PARAM(params, world_space_normal).rgb = mul(normal, (float3x3)world); + #endif + + #if defined(NEEDS_TANGENT_SPACE) + GRAPH_VERTEX_PARAM(params, tsm0) = + GRAPH_VERTEX_PARAM(params, tsm1) = + GRAPH_VERTEX_PARAM(params, tsm2) = + float3(0, 0, 0); + #endif + + #if defined(NEEDS_EYE_VECTOR) + #if defined(BILLBOARD) && defined(SKYDOME_BILLBOARD) + // TODO: not correct length, we can't use length(eye_vector) to determine the distance + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = billboard_direction; + #else + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp.rgb; + #endif + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + // Apply world pos offset + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(HAS_VERTEX_OFFSET) + wp += float4(results.vertex_offset, 0); + #endif + p = mul(wp, view_proj); + #else + p = mul(position, world_view_proj); + #endif + + #if defined(DRAW_OUTLINE) || defined(OUTLINE_MASK_READ) || defined(OUTLINE_MASK_WRITE) || defined(HAS_FOV) + float4 view_space = p / p.w; + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + #else + o.position = p; + #endif + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + #if (((defined(RENDERER_D3D11)) && defined(INSTANCED)) || defined(HAS_OPACITY)) || defined(OUTLINE_MASK_READ) + float4 ps_main(PS_INPUT input) : SV_TARGET0 + #else + float4 ps_main() : SV_TARGET0 + #endif + + #if defined(DRAW_WIREFRAME) + { + #if (defined(RENDERER_D3D11)) && defined(INSTANCED) + return input.instance_wireframe_color; + #else + return dev_wireframe_color; + #endif + } + #elif defined(OUTLINE_MASK_WRITE) + { + return float4(1,1,1,1); + } + #elif defined(OUTLINE_MASK_READ) + { + float2 uv = input.position.xy / back_buffer_size; + float mask = 1.0 - TEX2D(outline_mask, uv).r; + if( mask < 1.0 ) + discard; + + #if (defined(RENDERER_D3D11)) && defined(INSTANCED) + return input.instance_outline_color; + #else + return outline_color; + #endif + } + #elif defined(DRAW_OUTLINE) + { + #if defined(HAS_OPACITY) + GraphPixelParams params; + GraphPixelResults graph; + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + + if (graph.opacity < threshold) + discard; + #endif + + #if (defined(RENDERER_D3D11)) && defined(INSTANCED) + float diff = time - input.instance_outline_time; + float multiplier = min(diff / 0.25, 1); + #if defined(JITTER_TRANSPARENCY) + multiplier *= 1.0 - input.instance_inv_jitter_alpha; + #endif + return input.instance_outline_color * multiplier; + #else + float diff = time - outline_time; + float multiplier = min(diff / 0.25, 1); + #if defined(JITTER_TRANSPARENCY) + multiplier *= 1.0 - inv_jitter_alpha; + #endif + return outline_color * multiplier; + #endif + } + #else + { + #if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + GraphPixelParams params; + GraphPixelResults graph; + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + + if (graph.opacity < threshold) + discard; + #endif + + return float4(1, 1, 1, 1); + } + #endif + """ + } + } +} diff --git a/vmf_source/core/stingray_renderer/output_nodes/standard_base_bitsquid.shader_node b/vmf_source/core/stingray_renderer/output_nodes/standard_base_bitsquid.shader_node new file mode 100644 index 0000000..82c4e45 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/standard_base_bitsquid.shader_node @@ -0,0 +1,1740 @@ +group = "Output/Bitsquid" +display_name = "Bitsquid - Standard Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "aee6e47b-be7b-4d67-a123-2ab5d660b94e" = { + name = "vertex_offset" + display_name = "Position offset" + is_required = false + type = { vector3: ["HAS_VERTEX_OFFSET"] } + domain = "vertex" + } + + "aca690cb-6305-4a2f-bf3d-69183a493db3" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "34259752-b962-4b65-92c3-903a57338519" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "7a9306c6-95ae-4cdb-9fef-0eedacce4e83" = { + name = "opacity_threshold" + is_required = false + display_name = "Opacity Threshold" + type = { scalar: ["HAS_OPACITY_THRESHOLD"] } + domain = "pixel" + } + + "b1c86408-aacb-4466-b754-ddcf37a3a2c8" = { + is_required = false + name = "normal" + display_name = "Normal" + type = { vector3: ["HAS_NORMAL"] } + domain = "pixel" + } + + "ad5e052f-d316-4a0f-8b79-53c38204d61b" = { + is_required = false + name = "metallic" + display_name = "Metallic" + type = { scalar: ["HAS_METALLIC"] } + domain = "pixel" + } + + "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" = { + is_required = false + name = "roughness" + display_name = "Roughness" + type = { scalar: ["HAS_ROUGHNESS"] } + domain = "pixel" + } + + "1164a5ef-4563-4795-b3b5-42825d6df037" = { + is_required = false + name = "emissive" + display_name = "Emissive" + type = { vector3: ["HAS_EMISSIVE" ] } + domain = "pixel" + } + + "59fd1cf4-f736-470d-8510-1dd7c016639e" = { + is_required = false + name = "ambient_occlusion" + display_name = "Ambient Occlusion" + type = { scalar: ["HAS_AMBIENT_OCCLUSION"] } + domain = "pixel" + } +} + +options = { + "b2c7c0d2-beff-4b1a-a9d4-068a507625a2" = "USE_FBX_PERMUTATIONS" + "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" = "INSTANCED" + "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" = "CULL_NONE" + "c198c109-2cdf-49ee-af18-a982c23e2729" = "CULL_FRONT" + "34994d84-9d51-48ac-af85-bc053b2c65c3" = "SKIN" + "2b136447-676e-4943-997b-04a28ae68497" = "WORLD_SPACE_NORMAL" + "dd7fcf97-0627-48ab-b29a-95b5685bb123" = "TRANSPARENT" + "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" = "TRANSPARENT_FADE" +} + +ui = [ + { + type = "drop_down" + display_name = "Normals In" + options = { + "Tangent Space" = "00000000-0000-0000-0000-000000000000" + "World Space" = "2b136447-676e-4943-997b-04a28ae68497" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Blend Mode" + options = { + "Opaque" = "00000000-0000-0000-0000-000000000000" + "Transparent" = "dd7fcf97-0627-48ab-b29a-95b5685bb123" + "Transparent Fade" = "3b55d6c6-4398-4dbc-b9ef-570aff8696ae" + } + default = "00000000-0000-0000-0000-000000000000" + } + { + type = "drop_down" + display_name = "Face Culling" + options = { + "Back" = "00000000-0000-0000-0000-000000000000" + "Front" = "c198c109-2cdf-49ee-af18-a982c23e2729" + "None" = "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" + } + default = "00000000-0000-0000-0000-000000000000" + } + { type = "checkbox" display_name = "Instancing" option = "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" } +] + +render_state = { + culling_base = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + "defined(CULL_NONE)" = { + cull_mode = "cull_none" + } + "defined(CULL_FRONT)" = { + cull_mode = "cull_ccw" + } + "!defined(CULL_NONE) && !defined(CULL_FRONT)" = { + cull_mode = "cull_cw" + } + } + } + + gbuffer_material = { + inherit: ["culling_base"] + state: { + } + } + + emissive = { + inherit: ["culling_base"] + state: { + z_func = "less_equal" + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_one" + src_blend = "blend_one" + } + } + + transparent = { + inherit: ["culling_base"] + state : { + z_write_enable = "false" + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + "defined(TRANSPARENT)" = { + src_blend = "blend_one" + } + "defined(TRANSPARENT_FADE)" = { + src_blend = "blend_src_alpha" + } + } + } + + wireframe = { + inherit: ["transparent"] + state: { + fill_mode = "fill_wireframe" + src_blend = "blend_src_alpha" + "on_renderer(D3D11, D3D12)" = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + } + + depth_only = { + inherit: ["culling_base"] + state: { + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + } + } + + shadow_caster = { + inherit: ["depth_only"] + state: { + "on_renderer(D3D11, D3D12)" = { + depth_bias = "0xff" + slope_scale_depth_bias = "1.0" + } + "on_renderer(GL)" = { + offset_factor = "1.0" + offset_units = "1024.0" + depth_bias_enable = "true" + } + } + } + + material_transfer = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + cull_mode = "cull_none" + z_write_enable = "false" + z_enable = "false" + } + } +} + +sampler_state = { + shadow_map = { + inherit: ["core/stingray_renderer/shader_libraries/common#clamp_point"] + states = { + "on_renderer(D3D11, D3D12)" = { + comparison_func = "less" + filter = "comparison_min_mag_linear_mip_point" + } + "on_renderer(GNM)" = { + comparison_func = "less" + filter = "min_mag_mip_linear" + + } + "on_renderer(GL)" = { + comparison_func = "less" + filter = "min_mag_linear" + } + } + } +} + +channels = { + "(defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL)) || defined(NEEDS_TANGENT_SPACE)": { + vertex_tangent = { type = "float3" semantic = "TANGENT" domain = "vertex" } + vertex_binormal = { type = "float3" semantic = "BINORMAL" domain = "vertex" } + + tsm0 = { type = "float3" domains = ["vertex", "pixel"] } + tsm1 = { type = "float3" domains = ["vertex", "pixel"] } + tsm2 = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(MOTION_BLUR)": { + last_clip_position = { type = "float3" domains = ["vertex", "pixel"] } + } + + vertex_position = { type = "float4" domain = "vertex" } + vertex_normal = { type = "float3" semantic = "NORMAL" domain = "vertex" } + + "defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING)": { + vertex_color1 = { type = "float4" semantic = "COLOR1" domains = ["vertex"] } + baked_light = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING)": { + lightmap_uv_input = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + lightmap_uv = { type = "float2" domains = ["vertex", "pixel"] } + } + + "defined(MATERIAL_TRANSFER)": { + lightmap_uv = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + } + + "defined(TRANSPARENT) || defined(TRANSPARENT_FADE)": { + world_pos = { type = "float3" domains = ["vertex", "pixel"] } + } +} + +//log_permutations = true +permutation_sets = { + vertex_modifiers = [ + { if: "num_skin_weights() == 4" define: { "macros": ["SKINNED_4WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 3" define: { "macros": ["SKINNED_3WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 2" define: { "macros": ["SKINNED_2WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 1" define: { "macros": ["SKINNED_1WEIGHT"] stages: ["vertex"] } } + { default = true } + ] + + instanced_modifiers = [ + { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] } + { default = true } + ] + + lightmap_modifiers = [ + { if: "lightmap_format() == directional_irradiance" define: ["HAS_DIRECTIONAL_LIGHTMAPS"] } + { default = true } + ] + + non_instanced_modifiers = [ + { if: "mesh_baked_lighting_type() == lightmap" define: ["HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING"] permute_with: "lightmap_modifiers" } + { if: "mesh_baked_lighting_type() == vertex" define: ["HAS_VERTEX_BAKED_DIFFUSE_LIGHTING"] } + { permute_with: "vertex_modifiers" } + ] + + instanced_and_non_instanced = [ + { if: "defined(INSTANCED)" permute_with: "instanced_modifiers" } + { if: "!defined(INSTANCED)" permute_with: "non_instanced_modifiers" } + ] + + default = [ + // FBX Standard material exclusive permutations, these will only compile of the + // 'USE_FBX_PERMUTATIONS' option is set, which it is in the shader used for imported fbx files. + { if: "defined(USE_FBX_PERMUTATIONS)" permute_with: [ + { if: "is_any_material_variable_set(use_emissive_map, emissive)" define: ["FBX_EMISSIVE"] permute_with: "instanced_and_non_instanced" } + ] } + + // Normal default permutation set + { permute_with: "instanced_and_non_instanced" } + ] + + shadow_caster = [ + { if: "defined(INSTANCED)" } + { if: "!defined(INSTANCED)" permute_with: "vertex_modifiers" } + ] +} + +shader_contexts = { + shadow_caster = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: "shadow_caster" } + ] + + passes = [ + { code_block="depth_only" render_state="shadow_caster" } + ] + } + + material_transfer = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12)" } + ] + + passes = [ + { code_block="gbuffer_base" defines=["MATERIAL_TRANSFER"] render_state="material_transfer" } + ] + } + + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: [ + { permute_with: "default" } + ] } + ] + + passes = [ + { if: "defined(TRANSPARENT) || defined(TRANSPARENT_FADE)" then: [ + { layer="hdr_transparent" code_block="gbuffer_base" render_state="transparent" } + ] else: [ + { layer="gbuffer" code_block="gbuffer_base" defines="MOTION_BLUR" render_state="gbuffer_material" } + + // This bit of logic is a bit complicated. The gist of it is that we want to disable this pass + // for materials that has a value connected on emissive for all permutations, but should have it + // discarded for all but the special permutations with the define FBX_EMISSIVE + { if: "defined(HAS_EMISSIVE) && (!defined(USE_FBX_PERMUTATIONS) || defined(FBX_EMISSIVE))" then: [ + { layer="emissive" code_block="gbuffer_base" defines={ macros: ["EMISSIVE_PASS"] stages: ["pixel"] } render_state="emissive" } + ]} + ]} + { if: "!on_renderer(GL)" then: [ + { layer="wireframe" code_block="depth_only" define="DRAW_WIREFRAME" render_state="wireframe" branch_key="dev_wireframe" } + ]} + ] + } +} + +code_blocks = { + gbuffer_base = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/common#skinning", + "core/stingray_renderer/shader_libraries/common#taa_offsets", + "core/stingray_renderer/shader_libraries/lighting_common#brdf", + "core/stingray_renderer/shader_libraries/common#fog", + "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_bias"] + + instance_data = { + "on_renderer(D3D11, D3D12) && !defined(MATERIAL_TRANSFER) && defined(INSTANCED)": { + world = { type = "matrix4x4" } + "defined(MOTION_BLUR)": { + last_world = { type = "matrix4x4" } + } + } + } + + stage_conditions = { + tessellation_control = "defined(DX11_DISPLACEMENT_MAPPING)" + } + + samplers = { + global_diffuse_map = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "global_diffuse_map" + type = "cube" + } + lightmap = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "lightmap" + type = "2d" + } + lightmap_ambient_term = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "lightmap_ambient_term" + type = "2d" + } + lightmap_directional_term = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "lightmap_directional_term" + type = "2d" + } + lightmap_dominant_direction = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "lightmap_dominant_direction" + type = "2d" + } + global_specular_map = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "global_specular_map" + type = "cube" + } + brdf_lut = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "brdf_lut" + type = "2d" + } + shadow_map_sampler = { + sampler_state = "core/stingray_renderer/shader_libraries/shadow_map_common#shadow_map" + slot_name = "shadow_map_sampler" + type = "2d" + } + + "!defined(DX10_STYLE_SAMPLERS)": { + sun_shadow_map = { + sampler_state = "shadow_map" + slot_name = "sun_shadow_map" + type = "2d" + } + } + } + + code = { + glsl = """ + #if (!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + #if defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL) + #define NEEDS_TANGENT_SPACE + #endif + + CBUFFER_START(c_per_object) + UNIFORM mat4 world_view_proj; + #if defined(NEEDS_WORLD_SPACE_POSITION) || defined(NEEDS_TANGENT_SPACE) || defined(NEEDS_WORLD_POSE) + UNIFORM mat4 world; + #if defined(NEEDS_WORLD_SPACE_POSITION) + UNIFORM mat4 view_proj; + #endif + #endif + #if defined(NEEDS_INVERSE_WORLD_POSE) + UNIFORM mat4 inv_world; + #endif + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + UNIFORM vec2 lightmap_ambient_term_uv_scale; + UNIFORM vec2 lightmap_ambient_term_uv_offset; + #else + UNIFORM vec2 lightmap_uv_scale; + UNIFORM vec2 lightmap_uv_offset; + #endif + #endif + CBUFFER_END + + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + CBUFFER_START(clustered_shading_data) + UNIFORM vec3 baked_diffuse_tint; + UNIFORM vec3 reflections_tint; + UNIFORM mat4 sun_world_to_shadow_slice0; + UNIFORM mat4 sun_world_to_shadow_slice1; + UNIFORM mat4 sun_world_to_shadow_slice2; + UNIFORM mat4 sun_world_to_shadow_slice3; + UNIFORM vec4 sphere_slice0; + UNIFORM vec4 sphere_slice1; + UNIFORM vec4 sphere_slice2; + UNIFORM vec4 sphere_slice3; + CBUFFER_END + + DECLARE_SAMPLER_CUBE(global_specular_map); + DECLARE_SAMPLER_2D(brdf_lut); + + uniform highp sampler2DShadow sun_shadow_map; + + float sun_shadow_intensity(float3 world_pos, float3 shadow_biased_pos) { + float3 wp_to_sphere0 = world_pos - sphere_slice0.xyz; + float dist_to_sphere0 = dot(wp_to_sphere0, wp_to_sphere0); + + float3 wp_to_sphere1 = world_pos - sphere_slice1.xyz; + float dist_to_sphere1 = dot(wp_to_sphere1, wp_to_sphere1); + + float3 wp_to_sphere2 = world_pos - sphere_slice2.xyz; + float dist_to_sphere2 = dot(wp_to_sphere2, wp_to_sphere2); + + float3 wp_to_sphere3 = world_pos - sphere_slice3.xyz; + float dist_to_sphere3 = dot(wp_to_sphere3, wp_to_sphere3); + + float4x4 world_to_sm; + if (dist_to_sphere0 <= sphere_slice0.w) + world_to_sm = sun_world_to_shadow_slice0; + else if (dist_to_sphere1 <= sphere_slice1.w) + world_to_sm = sun_world_to_shadow_slice1; + else if (dist_to_sphere2 <= sphere_slice2.w) + world_to_sm = sun_world_to_shadow_slice2; + else if (dist_to_sphere3 <= sphere_slice3.w) + world_to_sm = sun_world_to_shadow_slice3; + else + return 1.0; + + float4 sm_pos = mul(float4(shadow_biased_pos, 1.0), world_to_sm); + vec2 inv_size = vec2(1.f) / vec2(textureSize(sun_shadow_map, 0)); + vec4 scale = vec4(inv_size, 0.f, 0.f); + + highp float shadow = + textureProj(sun_shadow_map, sm_pos + vec4(-0.5, 0.5, 0, 0) * scale) + + textureProj(sun_shadow_map, sm_pos + vec4(0.5, 0.5, 0, 0) * scale) + + textureProj(sun_shadow_map, sm_pos + vec4(0.5, -0.5, 0, 0) * scale) + + textureProj(sun_shadow_map, sm_pos + vec4(-0.5, -0.5, 0, 0) * scale); + + return shadow * 0.25; + } + + void sun_lighting(float3 world_pos, float3 shadow_biased_pos, float3 N, float3 V, float roughness, float3 ambient, float3 diffuse_color, float3 specular_color, inout float3 acc_diff, inout float3 acc_spec) { + float3 L = normalize(-sun_direction); + float2 scale_bias = TEX2D(brdf_lut, float2(saturate(dot(N, V)), 1.0 - roughness)).xy; + + float mipmap_index = roughness * 7.0; + acc_diff += ambient * baked_diffuse_tint * diffuse_color; + acc_spec += rgbm_decode(TEXCUBELOD(global_specular_map, reflect(-V, N), mipmap_index)) * (specular_color * scale_bias.x + scale_bias.y) * reflections_tint; + + float shadow_intensity = saturate(sun_shadow_intensity(world_pos, shadow_biased_pos)); + bsdf(L, V, N, sun_color, diffuse_color, specular_color, roughness, shadow_intensity, acc_diff, acc_spec); + } + #endif + + #if defined(STAGE_VERTEX) + layout(location = POSITION0) in vec4 in_position; + + void main() { + GraphManualChannels params; + GraphResults graph; + + #if defined(SKINNED) + vec4 p = vec4(skin_point(in_position, blendindices, blendweights), 1); + mediump vec3 n = skin_vector(GRAPH_DATA(vertex_normal).xyz, blendindices, blendweights); + #if defined(NEEDS_TANGENT_SPACE) + mediump vec3 t = skin_vector(GRAPH_DATA(vertex_tangent).xyz, blendindices, blendweights); + mediump vec3 b = skin_vector(GRAPH_DATA(vertex_binormal).xyz, blendindices, blendweights); + #endif + #else + vec4 p = in_position; + mediump vec3 n = GRAPH_DATA(vertex_normal); + #if defined(NEEDS_TANGENT_SPACE) + mediump vec3 t = GRAPH_DATA(vertex_tangent); + mediump vec3 b = GRAPH_DATA(vertex_binormal); + #endif + #endif + + #if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_PARAM(params, world_space_normal) = n * mat3(world); + #endif + + GRAPH_PARAM(params, vertex_position) = p; + + #if defined(NEEDS_WORLD_SPACE_POSITION) || defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + vec4 wp = p * world; + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + GRAPH_PARAM(params, world_pos) = wp.xyz; + #endif + #endif + + #if defined(NEEDS_TANGENT_SPACE) + tspace_transform_transpose( + GRAPH_PARAM(params, tsm0), + GRAPH_PARAM(params, tsm1), + GRAPH_PARAM(params, tsm2), + t, b, n, + mat3(world)); + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_PARAM(params, eye_vector) = camera_pos - wp.rgb; + #endif + + #if defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING) + GRAPH_PARAM(params, baked_light) = rgbm_decode(decode_vertex_color(GRAPH_DATA(vertex_color1))); + #elif defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + mediump vec2 light_uv_in = GRAPH_DATA(lightmap_uv_input); + light_uv_in.y = 1.0 - light_uv_in.y; + + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + mediump vec2 luv = light_uv_in*lightmap_ambient_term_uv_scale + lightmap_ambient_term_uv_offset; + #else + mediump vec2 luv = light_uv_in*lightmap_uv_scale + lightmap_uv_offset; + #endif + + luv.y = 1.0 - luv.y; + GRAPH_PARAM(params, lightmap_uv) = luv; + #endif + + graph_evaluate(graph, params); + + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(HAS_VERTEX_OFFSET) + wp.xyz += graph.vertex_offset; + #endif + gl_Position = wp * view_proj; + #else + gl_Position = p * world_view_proj; + #endif + } + #elif defined(STAGE_FRAGMENT) + #if defined(EMISSIVE_PASS) + layout(location = 0) out mediump vec4 out_color; + + void main() { + GraphManualChannels params; + GraphResults graph; + graph_evaluate(graph, params); + + #if defined(EMISSIVE_PASS) + #if defined(HAS_OPACITY) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + discard; + #endif + out_color = vec4(graph.emissive, 0); + #endif + } + #else + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + layout(location = 0) out mediump vec4 out_color; + #else + GBUFFER_OUTPUT; + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + DECLARE_SAMPLER_2D(lightmap_ambient_term); + DECLARE_SAMPLER_2D(lightmap_directional_term); + DECLARE_SAMPLER_2D(lightmap_dominant_direction); + #else + DECLARE_SAMPLER_2D(lightmap); + #endif + #endif + + DECLARE_SAMPLER_CUBE(global_diffuse_map); + + void main() { + GraphManualChannels params; + GraphResults graph; + graph_evaluate(graph, params); + + #if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5; + #endif + if (graph.opacity < threshold) + discard; + #endif + + // Base color + lowp vec3 base_color = vec3(0); + #if defined(HAS_BASE_COLOR) + base_color = graph.base_color; + #else + base_color = vec3(0.5, 0.5, 0.5); + #endif + + // World space normal + #if defined(HAS_NORMAL) + #if defined(WORLD_SPACE_NORMAL) + mediump vec3 wn = normalize(graph.normal); + #if defined(CULL_NONE) || defined(CULL_FRONT) + wn = gl_FrontFacing ? wn : -wn; + #endif + #else + float3 tsm0 = GRAPH_DATA(tsm0).xyz; + float3 tsm1 = GRAPH_DATA(tsm1).xyz; + float3 tsm2 = GRAPH_DATA(tsm2).xyz; + #if defined(CULL_NONE) || defined(CULL_FRONT) + if (!gl_FrontFacing) { + tsm0.z = -tsm0.z; + tsm1.z = -tsm1.z; + tsm2.z = -tsm2.z; + } + #endif + mediump vec3 wn = rotate_vector3(graph.normal, tsm0, tsm1, tsm2); + #endif + #else + mediump vec3 wn = normalize(GRAPH_DATA(world_space_normal)); + #if defined(CULL_NONE) || defined(CULL_FRONT) + wn = gl_FrontFacing ? wn : -wn; + #endif + #endif + + // Metallic + lowp float metallic = 0.0; + #if defined(HAS_METALLIC) + metallic = graph.metallic; + #else + metallic = 0.0; + #endif + + // Roughness + half roughness = 0.0; + #if defined(HAS_ROUGHNESS) + roughness = max(graph.roughness, 1.f / 255.f); + #else + roughness = 0.5; + #endif + + // Ambient Diffuse + mediump vec3 ambient = vec3(0.0, 0.0, 0.0); + #if defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING) + ambient = GRAPH_DATA(baked_light); + #elif defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + ambient = TEX2D(lightmap_ambient_term, GRAPH_DATA(lightmap_uv)).rgb; + mediump vec3 dd = TEX2D(lightmap_dominant_direction, GRAPH_DATA(lightmap_uv)).rgb * 2.0 - vec3(1.0); + ambient += TEX2D(lightmap_directional_term, GRAPH_DATA(lightmap_uv)).rgb * max(0.0, dot(dd, wn)); + #else + ambient = TEX2D(lightmap, GRAPH_DATA(lightmap_uv)).rgb; + #endif + #else + ambient = gbuffer_decode_ambient_diffuse_light(TEXCUBELOD(global_diffuse_map, wn, 0.0)); + #endif + + #if !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + out_base_color = gbuffer_encode_base_color(base_color); + out_normal = gbuffer_encode_normal(wn); + out_metallic = gbuffer_encode_metallic_mask(metallic); + out_roughness = gbuffer_encode_roughness(roughness); + out_ambient_occlusion = gbuffer_encode_ambient_occlusion(1.0); + out_ambient_diffuse_light = gbuffer_encode_ambient_diffuse_light(ambient); + #else + #if defined(HAS_OPACITY) + half opacity = graph.opacity; + #else + half opacity = 0.5; + #endif + + float3 world_pos = GRAPH_DATA(world_pos); + float3 view_dir = float3(camera_world[0].w, camera_world[1].w, camera_world[2].w) - world_pos; + + float3 V = normalize(view_dir); + float3 N = normalize(wn); + + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + float3 specular_color = lerp(new_half3(0.04, 0.04, 0.04), base_color, metallic); + float3 diffuse_color = lerp(base_color, new_half3(0.0, 0.0, 0.0), metallic); + + float3 camera_dir = float3(camera_world[0].y, camera_world[1].y, camera_world[2].y); + float depth = dot(-view_dir, camera_dir); + float3 shadow_biased_pos = world_pos - (depth - apply_shadow_bias(depth)) * (-V); + + float3 acc_diff = new_half3(0.0, 0.0, 0.0); + float3 acc_spec = new_half3(0.0, 0.0, 0.0); + sun_lighting(world_pos, shadow_biased_pos, N, V, roughness, ambient, diffuse_color, specular_color, acc_diff, acc_spec); + + #if defined(TRANSPARENT) + float3 accumulated_color = acc_diff * opacity + acc_spec; + #else + float3 accumulated_color = acc_diff + acc_spec; + #endif + + out_color = apply_fog(new_half4_xyz(accumulated_color, opacity), world_pos, depth); + #endif + } + #endif + #endif + """ + + hlsl = """ + // We need to disable instancing for the material transfer context as it doesn't use the world transform. + #if defined(INSTANCED) && defined(MATERIAL_TRANSFER) + #undef INSTANCED + #endif + + #if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) || ((defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) && defined(INSTANCED)) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + #if defined(HAS_NORMAL) && !defined(WORLD_SPACE_NORMAL) + #define NEEDS_TANGENT_SPACE + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + DECLARE_SAMPLER_2D(lightmap_ambient_term); + DECLARE_SAMPLER_2D(lightmap_directional_term); + DECLARE_SAMPLER_2D(lightmap_dominant_direction); + #else + DECLARE_SAMPLER_2D(lightmap); + #endif + #endif + + DECLARE_SAMPLER_CUBE(global_diffuse_map); + + struct VS_INPUT { + float4 position : POSITION; + SKIN_INPUT + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_per_object) + #if defined(NEEDS_WORLD_SPACE_POSITION) + float4x4 view_proj; + #else + float4x4 world_view_proj; + #endif + float4x4 world; + #if defined(NEEDS_INVERSE_WORLD_POSE) + float4x4 inv_world; + #endif + float4x4 last_world; + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + float2 lightmap_ambient_term_uv_scale; + float2 lightmap_ambient_term_uv_offset; + #else + float2 lightmap_uv_scale; + float2 lightmap_uv_offset; + #endif + #endif + + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + CBUFFER_START(clustered_shading_data) + float4x4 cs_world_to_shadow_maps[64]; + float3 baked_diffuse_tint; + float3 reflections_tint; + float4x4 sun_world_to_shadow_slice0; + float4x4 sun_world_to_shadow_slice1; + float4x4 sun_world_to_shadow_slice2; + float4x4 sun_world_to_shadow_slice3; + float4 sphere_slice0; + float4 sphere_slice1; + float4 sphere_slice2; + float4 sphere_slice3; + float4 cs_cluster_size_in_pixels; + float4 cs_lights_data[1024]; + CBUFFER_END + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + Texture3D cluster_texture; + Texture2D light_index_texture; + + SamplerComparisonState shadow_map_sampler; + Texture2D sun_shadow_map; + Texture2D local_lights_shadow_atlas; + #endif + DECLARE_SAMPLER_CUBE(global_specular_map); + DECLARE_SAMPLER_2D(brdf_lut); + #endif + + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + Buffer idata; + float ioffset; + #endif + + PS_INPUT vs_main(VS_INPUT input + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + , uint instance_id : SV_InstanceId + #endif + ) + { + PS_INPUT o; + float4 p; + + GraphVertexParams params; + GraphVertexResults results; + + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + uint offset = (uint)ioffset + instance_id*IDATA_STRIDE; + world[0] = idata.Load(offset + IDATA_world + 0); + world[1] = idata.Load(offset + IDATA_world + 1); + world[2] = idata.Load(offset + IDATA_world + 2); + world[3] = idata.Load(offset + IDATA_world + 3); + + #if defined(MOTION_BLUR) + last_world[0] = idata.Load(offset + IDATA_last_world + 0); + last_world[1] = idata.Load(offset + IDATA_last_world + 1); + last_world[2] = idata.Load(offset + IDATA_last_world + 2); + last_world[3] = idata.Load(offset + IDATA_last_world + 3); + #endif + #endif + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + // Write output channels + #if defined(SKINNED) + float4 position = float4(skin_point(input.position, input.blendindices, input.blendweights), 1); + #if defined(MOTION_BLUR) + float4 last_position = float4(skin_point_last_frame(input.position, input.blendindices, input.blendweights), 1); + #endif + float3 normal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_normal).xyz, input.blendindices, input.blendweights); + #if defined(NEEDS_TANGENT_SPACE) + float3 tangent = skin_vector(GRAPH_VERTEX_DATA(input, vertex_tangent).xyz, input.blendindices, input.blendweights); + float3 binormal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_binormal).xyz, input.blendindices, input.blendweights); + #endif + #else + float4 position = input.position; + #if defined(MOTION_BLUR) + float4 last_position = position; + #endif + float3 normal = GRAPH_VERTEX_DATA(input, vertex_normal).xyz; + #if defined(NEEDS_TANGENT_SPACE) + float3 tangent = GRAPH_VERTEX_DATA(input, vertex_tangent).xyz; + float3 binormal = GRAPH_VERTEX_DATA(input, vertex_binormal).xyz; + #endif + #endif + + #if defined(NEEDS_WORLD_SPACE_POSITION) || defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + float4 wp = mul(position, world); + // TODO: Expose output channel here + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + GRAPH_VERTEX_PARAM(params, world_pos) = wp.xyz; + #endif + #endif + + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + #if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_VERTEX_PARAM(params, world_space_normal).rgb = mul(normal, (float3x3)world); + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp.rgb; + #endif + + #if defined(NEEDS_TANGENT_SPACE) + tspace_transform_transpose( + GRAPH_VERTEX_PARAM(params, tsm0), + GRAPH_VERTEX_PARAM(params, tsm1), + GRAPH_VERTEX_PARAM(params, tsm2), + tangent, binormal, normal, + (float3x3)world); + #endif + + #if defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING) + GRAPH_VERTEX_PARAM(params, baked_light) = rgbm_decode(decode_vertex_color(GRAPH_VERTEX_DATA(input, vertex_color1))); + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(HAS_VERTEX_OFFSET) + wp += float4(results.vertex_offset, 0); + #endif + p = mul(wp, view_proj); + #else + p = mul(position, world_view_proj); + #endif + + #if defined(MOTION_BLUR) + GRAPH_VERTEX_PARAM(params, last_clip_position) = float3(0.0, 0.0, 0.0); + #if defined(NEEDS_WORLD_SPACE_POSITION) + float4 cur_wp = wp; + float4 last_wp = mul(last_position, last_world); + + #if defined(HAS_VERTEX_OFFSET) + // TODO: Add _last_ position offset here to support vertex animation. + // The way it works now will only yield correct results of the offset is constant + last_wp += float4(results.vertex_offset, 0); + #endif + #else + float4 cur_wp = mul(position, last_world); + float4 last_wp = mul(last_position, last_world); + #endif + float4 last_clip_pos = mul(last_wp, camera_last_view_projection); + float4 last_view_space = last_clip_pos / last_clip_pos.w; + last_view_space.xy += get_vs_halton_offset(frame_number); + last_clip_pos = last_view_space * last_clip_pos.w; + GRAPH_VERTEX_PARAM(params, last_clip_position) = last_clip_pos.xyw; + #endif + + #if defined(MATERIAL_TRANSFER) + float2 unwrapped_uv = GRAPH_VERTEX_DATA(input, lightmap_uv); + float2 ndc = float2(unwrapped_uv.x, unwrapped_uv.y) * 2 - 1; + ndc.y *= -1; + p = float4(ndc, 0, 1); + #endif + + #if defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + GRAPH_VERTEX_PARAM(params, lightmap_uv) = GRAPH_VERTEX_DATA(input, lightmap_uv_input)*lightmap_ambient_term_uv_scale + lightmap_ambient_term_uv_offset; + #else + GRAPH_VERTEX_PARAM(params, lightmap_uv) = GRAPH_VERTEX_DATA(input, lightmap_uv_input)*lightmap_uv_scale + lightmap_uv_offset; + #endif + #endif + + #if defined(MATERIAL_TRANSFER) + o.position = p; + #else + float4 view_space = p / p.w; + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + #endif + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + #if defined(MATERIAL_TRANSFER) + struct MATERIAL_TRANSFER_OUT { + float4 albedo_op : SV_TARGET0; + float4 emissive : SV_TARGET1; + }; + + MATERIAL_TRANSFER_OUT ps_main(PS_INPUT input) : SV_TARGET0 + { + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + MATERIAL_TRANSFER_OUT o; + + #if defined(HAS_BASE_COLOR) + o.albedo_op = float4(graph.base_color, 0); + #else + o.albedo_op = float4(0.5, 0.5, 0.5, 0); + #endif + #if defined(HAS_EMISSIVE) + o.emissive = float4(graph.emissive, 1.0); + #else + o.emissive = float4(0.0, 0.0, 0.0, 0.0); + #endif + + return o; + } + #elif defined(EMISSIVE_PASS) + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(EMISSIVE_PASS) + #if defined(HAS_OPACITY) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + discard; + #endif + return float4(graph.emissive, 0); + #endif + } + #else + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + float sun_shadow_intensity(float3 world_pos, float3 shadow_biased_pos) { + float3 wp_to_sphere0 = world_pos - sphere_slice0.xyz; + float dist_to_sphere0 = dot(wp_to_sphere0, wp_to_sphere0); + + float3 wp_to_sphere1 = world_pos - sphere_slice1.xyz; + float dist_to_sphere1 = dot(wp_to_sphere1, wp_to_sphere1); + + float3 wp_to_sphere2 = world_pos - sphere_slice2.xyz; + float dist_to_sphere2 = dot(wp_to_sphere2, wp_to_sphere2); + + float3 wp_to_sphere3 = world_pos - sphere_slice3.xyz; + float dist_to_sphere3 = dot(wp_to_sphere3, wp_to_sphere3); + + float4x4 world_to_sm; + if (dist_to_sphere0 <= sphere_slice0.w) + world_to_sm = sun_world_to_shadow_slice0; + else if (dist_to_sphere1 <= sphere_slice1.w) + world_to_sm = sun_world_to_shadow_slice1; + else if (dist_to_sphere2 <= sphere_slice2.w) + world_to_sm = sun_world_to_shadow_slice2; + else if (dist_to_sphere3 <= sphere_slice3.w) + world_to_sm = sun_world_to_shadow_slice3; + else + return 1.0; + + float4 sm_pos = mul(float4(shadow_biased_pos, 1.0), world_to_sm); + float2 sm_resolution; + sun_shadow_map.GetDimensions(sm_resolution.x, sm_resolution.y); + float4 tscale = float4(1.f / sm_resolution.x, 1.f / sm_resolution.y, 0.f, 0.f); + + half shadow = 0.0; + for( float xx = -0.5; xx <= 0.5; xx += 1.0 ) { + for( float yy = -0.5; yy <= 0.5; yy += 1.0 ) { + #ifdef GNM + shadow += sun_shadow_map.SampleCmpLOD0(shadow_map_sampler, sm_pos.xy + (float2( xx, yy ) * tscale.xy), sm_pos.z); + #else + shadow += sun_shadow_map.SampleCmpLevelZero(shadow_map_sampler, sm_pos.xy + (float2( xx, yy ) * tscale.xy), sm_pos.z); + #endif + } + } + return shadow * 0.25; + } + + float local_light_shadow_intensity(float4 sm_pos) { + sm_pos.xyz /= sm_pos.w; + float2 sm_resolution; + local_lights_shadow_atlas.GetDimensions(sm_resolution.x, sm_resolution.y); + float2 tscale = float2(1.0f / sm_resolution.x, 1.0f / sm_resolution.y); + half shadow = 0; + [unroll] + for (float xx = -0.5; xx <= 0.5; xx += 1.0) { + [unroll] + for (float yy = -0.5; yy <= 0.5; yy += 1.0) + #if defined(RENDERER_GNM) + shadow += local_lights_shadow_atlas.SampleCmpLOD0(shadow_map_sampler, sm_pos.xy + (float2(xx, yy) * tscale), sm_pos.z); + #else + shadow += local_lights_shadow_atlas.SampleCmpLevelZero(shadow_map_sampler, sm_pos.xy + (float2(xx, yy) * tscale), sm_pos.z); + #endif + } + return shadow * 0.25; + } + + void sun_lighting(const float3 world_pos, const float3 shadow_biased_pos, const float3 N, const float3 V, const float roughness, const float3 ambient, const float3 diffuse_color, const float3 specular_color, inout float3 acc_diff, inout float3 acc_spec) { + const float3 L = normalize(-sun_direction); + const float2 scale_bias = TEX2D(brdf_lut, float2(saturate(dot(N, V)), roughness)).xy; + + const float mipmap_index = roughness * 7; + acc_diff += ambient * baked_diffuse_tint * diffuse_color; + acc_spec += rgbm_decode(TEXCUBELOD(global_specular_map, reflect(-V, N), mipmap_index)) * (specular_color * scale_bias.x + scale_bias.y) * reflections_tint; + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float shadow_intensity = saturate(sun_shadow_intensity(world_pos, shadow_biased_pos)); + #else + float shadow_intensity = 1.0f; + #endif + + bsdf(L, V, N, sun_color, diffuse_color, specular_color, roughness, shadow_intensity, acc_diff, acc_spec); + } + + struct PS_OUTPUT { + half4 color : SV_TARGET0; + }; + PS_OUTPUT ps_main(PS_INPUT input + #else + GBUFFER_OUT ps_main(PS_INPUT input + #endif + #if defined(CULL_NONE) || defined(CULL_FRONT) + #if defined(GNM) + , bool vface : S_FRONT_FACE + #else + , float vface : VFACE + #endif + #endif + ) + { + #if defined(TRANSPARENT) || defined(TRANSPARENT_FADE) + PS_OUTPUT o; + #else + GBUFFER_OUT o; + #endif + + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + if (graph.opacity < threshold) + discard; + #endif + + + // Base color + float3 base_color = float3(0,0,0); + #if defined(HAS_BASE_COLOR) + base_color = graph.base_color; + #else + base_color = float3(0.5, 0.5, 0.5); + #endif + + // World space normal + #if defined(HAS_NORMAL) + #if !defined(WORLD_SPACE_NORMAL) + float3 tsm0 = GRAPH_PIXEL_DATA(input, tsm0).xyz; + float3 tsm1 = GRAPH_PIXEL_DATA(input, tsm1).xyz; + float3 tsm2 = GRAPH_PIXEL_DATA(input, tsm2).xyz; + #if defined(CULL_NONE) || defined(CULL_FRONT) + if (!front_facing(vface)) { + tsm0.z = -tsm0.z; + tsm1.z = -tsm1.z; + tsm2.z = -tsm2.z; + } + #endif + float3 wn = rotate_vector3(graph.normal, tsm0, tsm1, tsm2); + #else + float3 wn = normalize(graph.normal); + #if defined(CULL_NONE) || defined(CULL_FRONT) + wn = !front_facing(vface) ? -wn : wn; + #endif + #endif + #else + float3 wn = normalize((float3)GRAPH_PIXEL_DATA(input, world_space_normal).rgb); + #if defined(CULL_NONE) || defined(CULL_FRONT) + wn = !front_facing(vface) ? -wn : wn; + #endif + #endif + + + // Metallic + half metallic_ = 0.f; + #if defined(HAS_METALLIC) + metallic_ = graph.metallic; + #else + metallic_ = 0.f; + #endif + + // Roughness + half roughness_ = 0.f; + #if defined(HAS_ROUGHNESS) + roughness_ = graph.roughness; + #else + roughness_ = 0.5; + #endif + + // Velocity vector + #if !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + #if defined(MOTION_BLUR) + VELOCITY(o) = float2(0.0, 0.0); + float3 last_clip_pos = GRAPH_PIXEL_DATA(input, last_clip_position); + float2 screen_pos = (input.position.xy / back_buffer_size); + float2 last_screen_pos = last_clip_pos.xy / last_clip_pos.z; + last_screen_pos = last_screen_pos * 0.5 + 0.5; + last_screen_pos.y = 1.0 - last_screen_pos.y; + VELOCITY(o) = encode_velocity(screen_pos - last_screen_pos); + #else + VELOCITY(o) = encode_velocity(0.0); + #endif + #endif + + // Ambient Diffuse + float3 ambient = float3(0, 0, 0); + #if defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING) + ambient = GRAPH_PIXEL_DATA(input, baked_light).rgb; + #elif defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) + #if defined(HAS_DIRECTIONAL_LIGHTMAPS) + ambient = TEX2D(lightmap_ambient_term, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb; + float3 dd = TEX2D(lightmap_dominant_direction, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb * 2 - 1; + ambient += TEX2D(lightmap_directional_term, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb * max(0, dot(dd, wn)); + #else + ambient = TEX2D(lightmap, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb; + #endif + #else + ambient = rgbm_decode(TEXCUBELOD(global_diffuse_map, wn, 0)); + #endif + + #if !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + BASE_COLOR(o) = gbuffer_encode_base_color(base_color); + NORMAL(o) = gbuffer_encode_normal(wn); + METALLIC(o) = gbuffer_encode_metallic_mask(metallic_); + ROUGHNESS(o) = gbuffer_encode_roughness(roughness_); + DENSITY(o) = 1.0; + // AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(ambient); + + #if defined(HAS_AMBIENT_OCCLUSION) + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(graph.ambient_occlusion); + #else + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(1.f); + #endif + #else + #if defined(HAS_OPACITY) + half opacity = graph.opacity; + #else + half opacity = 0.5; + #endif + const float3 world_pos = GRAPH_PIXEL_DATA(input, world_pos); + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 V = normalize(view_dir); + const float3 N = wn; + + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + const float3 specular_color = lerp(float3(0.04,0.04,0.04), base_color, metallic_); + const float3 diffuse_color = lerp(base_color, new_half3(0,0,0), metallic_); + + const float3 camera_dir = camera_world._m10_m11_m12; + const float depth = dot(-view_dir, camera_dir); + const float3 shadow_biased_pos = world_pos - (depth - apply_shadow_bias(depth)) * (-V); + + float3 acc_diff = 0; + float3 acc_spec = 0; + + sun_lighting(world_pos, shadow_biased_pos, N, V, roughness_, ambient, diffuse_color, specular_color, acc_diff, acc_spec); + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + const float max_depth = cs_cluster_size_in_pixels.w; + const float depth_per_cluster = max_depth / cs_cluster_size_in_pixels.z; + const uint layer = input.position.w / depth_per_cluster; + + const uint2 light_data = cluster_texture.Load(int4(input.position.xy / cs_cluster_size_in_pixels.xy, layer, 0)); + uint light_index = light_data.x; + + float2 li_resolution; + light_index_texture.GetDimensions(li_resolution.x, li_resolution.y); + + const uint point_light_count = light_data.y & 0x00FF; + for (uint pl = 0; pl < point_light_count; ++pl) { + uint2 index_uv = uint2(fmod(light_index, li_resolution.x), light_index / li_resolution.x); + const uint index = light_index_texture.Load(int3(index_uv.xy, 0)).r; + const float3 light_position = cs_lights_data[index].xyz; + const float3 light_color = cs_lights_data[index + 1].rgb; + const float3 light_falloff = cs_lights_data[index + 2].xyz; + + float3 L = light_position - world_pos; + const float len_L = length(L) + 0.00001f; + L /= len_L; + + const float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness_, attn, acc_diff, acc_spec); + + ++light_index; + } + + const uint shadow_casting_point_light_count = (light_data.y >> 8) & 0x00FF; + for (uint scpl = 0; scpl < shadow_casting_point_light_count; ++scpl) { + uint2 index_uv = uint2(fmod(light_index, li_resolution.x), light_index / li_resolution.x); + const uint index = light_index_texture.Load(int3(index_uv.xy, 0)).r; + const float3 light_position = cs_lights_data[index].xyz; + const float3 light_color = cs_lights_data[index + 1].rgb; + const float3 light_falloff = cs_lights_data[index + 2].xyz; + + float3 L = light_position - world_pos; + const float len_L = length(L) + 0.00001f; + L /= len_L; + + const float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + + // The indices to select the correct world to shadow map matrix are stored in the following components. + const float sm_indices[6] = { + float(cs_lights_data[index + 0].w), + float(cs_lights_data[index + 1].w), + float(cs_lights_data[index + 3].x), + float(cs_lights_data[index + 3].y), + float(cs_lights_data[index + 3].z), + float(cs_lights_data[index + 3].w) + }; + + // The shadows from the faces of an onmni light are populated in the following order and directions. + // float3(-1.0f, 0.0f, 0.0f), + // float3( 1.0f, 0.0f, 0.0f), + // float3( 0.0f, -1.0f, 0.0f), + // float3( 0.0f, 1.0f, 0.0f), + // float3( 0.0f, 0.0f, -1.0f) + // float3( 0.0f, 0.0f, 1.0f), + + // Based on the biggest component of the vector from the shaded position to the light source and its sign, chose the correct + // shadow map index to get the correct world position to shadow map matrix. + const float3 shadow_L = -L; + const int3 is_positive = shadow_L > 0; + const float3 abs_shadow_L = abs(shadow_L); + int test_index = (abs_shadow_L.x > abs_shadow_L.y && abs_shadow_L.x > abs_shadow_L.z) ? 0 + is_positive[0]: (abs_shadow_L.y > abs_shadow_L.z) ? 2 + is_positive[1] : 4 + is_positive[2]; + const float sm_index = sm_indices[test_index]; + + float shadow_intensity = 1.0f; + if (sm_index != -1.0f) { + float4x4 world_to_sm = cs_world_to_shadow_maps[uint(sm_index)]; + float4 sm_pos = mul(float4(shadow_biased_pos, 1.0), world_to_sm); + shadow_intensity = saturate(local_light_shadow_intensity(sm_pos)); + } + + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness_, attn * shadow_intensity, acc_diff, acc_spec); + + ++light_index; + } + + const uint spot_light_count = (light_data.y >> 16) & 0x00FF; + for (uint sl = 0; sl < spot_light_count; ++sl) { + uint2 index_uv = uint2(fmod(light_index, li_resolution.x), light_index / li_resolution.x); + const uint index = light_index_texture.Load(int3(index_uv.xy, 0)).r; + const float3 light_position = cs_lights_data[index].xyz; + const float3 light_color = cs_lights_data[index + 1].rgb; + const float3 light_falloff = cs_lights_data[index + 2].xyz; + const float3 spot_light_falloff = cs_lights_data[index + 3].xyz; + const float3 spot_dir = normalize(float3(cs_lights_data[index].w, cs_lights_data[index + 1].w, cs_lights_data[index + 2].w)); + + float3 L = light_position - world_pos; + const float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + + const float spot_angle = 1.0 - dot(L, -spot_dir); + const float spot_attenuation = (spot_angle > spot_light_falloff.x ? 1.0 - saturate((spot_angle - spot_light_falloff.x) * spot_light_falloff.y) : 1); + attn *= spot_attenuation; + + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness_, attn, acc_diff, acc_spec); + + ++light_index; + } + + const uint shadow_casting_spot_light_count = (light_data.y >> 24) & 0x00FF; + for (uint scsl = 0; scsl < shadow_casting_spot_light_count; ++scsl) { + uint2 index_uv = uint2(fmod(light_index, li_resolution.x), light_index / li_resolution.x); + const uint index = light_index_texture.Load(int3(index_uv.xy, 0)).r; + const float3 light_position = cs_lights_data[index].xyz; + const float3 light_color = cs_lights_data[index + 1].rgb; + const float3 light_falloff = cs_lights_data[index + 2].xyz; + const float3 spot_light_falloff = cs_lights_data[index + 3].xyz; + const uint sm_index = uint(cs_lights_data[index + 3].w); + const float3 spot_dir = normalize(float3(cs_lights_data[index].w, cs_lights_data[index + 1].w, cs_lights_data[index + 2].w)); + + float3 L = light_position - world_pos; + const float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + + const float spot_angle = 1.0 - dot(L, -spot_dir); + const float spot_attenuation = (spot_angle > spot_light_falloff.x ? 1.0 - saturate((spot_angle - spot_light_falloff.x) * spot_light_falloff.y) : 1); + attn *= spot_attenuation; + + float4x4 world_to_sm = cs_world_to_shadow_maps[sm_index]; + float4 sm_pos = mul(float4(shadow_biased_pos, 1.0), world_to_sm); + const float shadow_intensity = saturate(local_light_shadow_intensity(sm_pos)); + + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness_, attn * shadow_intensity, acc_diff, acc_spec); + + ++light_index; + } + #endif + + #if defined(TRANSPARENT) + float3 accumulated_color = acc_diff * opacity + acc_spec; + #else + float3 accumulated_color = acc_diff + acc_spec; + #endif + + o.color = apply_fog(half4(accumulated_color, opacity), world_pos, depth); + #endif + + return o; + } + #endif + """ + } + } + + depth_only = { + include: [ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#skinning"] + + instance_data = { + "on_renderer(D3D11, D3D12) && defined(INSTANCED)": { + world = { type = "matrix4x4" } + + "defined(DRAW_WIREFRAME)": { + dev_wireframe_color = { type = "vector4" } + } + } + + } + + code = { + glsl = """ + #if (!defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL)) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + CBUFFER_START(c_per_object) + UNIFORM mat4 world_view_proj; + #if defined(NEEDS_WORLD_SPACE_POSITION) + UNIFORM mat4 world; + UNIFORM mat4 view_proj; + #endif + #if defined(NEEDS_INVERSE_WORLD_POSE) + UNIFORM mat4 inv_world; + #endif + CBUFFER_END + + #if defined(STAGE_VERTEX) + layout(location = POSITION0) in vec4 in_position; + + void main() { + GraphManualChannels params; + GraphResults graph; + + #if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_PARAM(params, world_space_normal) = GRAPH_DATA(vertex_normal) * mat3(world); + #endif + + #if defined(SKINNED) + vec4 op = vec4(skin_point(in_position, blendindices, blendweights), 1); + #else + vec4 op = in_position; + #endif + GRAPH_PARAM(params, vertex_position) = op; + + #if defined(NEEDS_WORLD_SPACE_POSITION) + vec4 wp = op * world; + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_PARAM(params, eye_vector) = camera_pos - wp.rgb; + #endif + + graph_evaluate(graph, params); + + #if defined(HAS_VERTEX_OFFSET) + wp.xyz += graph.vertex_offset; + gl_Position = wp * view_proj; + #else + gl_Position = op * world_view_proj; + #endif + } + #elif defined(STAGE_FRAGMENT) + void main() { + #if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + GraphManualChannels params; + GraphResults graph; + graph_evaluate(graph, params); + + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + + if (graph.opacity < threshold) + discard; + #endif + } + #endif + """ + + hlsl = """ + #if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) || ((defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) && defined(INSTANCED)) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + struct VS_INPUT { + float4 position : POSITION; + SKIN_INPUT + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) && defined(INSTANCED) && defined(DRAW_WIREFRAME) + float4 instance_wireframe_color : COLOR0; + #endif + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_depth_only) + #if defined(NEEDS_WORLD_SPACE_POSITION) || defined(NEEDS_WORLD_POSE) + float4x4 world; + #endif + #if defined(NEEDS_WORLD_SPACE_POSITION) + float4x4 view_proj; + #else + float4x4 world_view_proj; + #endif + #if defined(NEEDS_INVERSE_WORLD_POSE) + float4x4 inv_world; + #endif + + float4 dev_wireframe_color; + + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + Buffer idata; + float ioffset; + #endif + + PS_INPUT vs_main(VS_INPUT input + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + , uint instance_id : SV_InstanceId + #endif + ) + { + PS_INPUT o; + float4 p; + + GraphVertexParams params; + GraphVertexResults results; + + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + uint offset = (uint)ioffset; + world[0] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 0)); + world[1] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 1)); + world[2] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 2)); + world[3] = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_world + 3)); + + #if defined(DRAW_WIREFRAME) + o.instance_wireframe_color = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_dev_wireframe_color)); + #endif + #endif + + #if defined(SKINNED) + float4 position = float4(skin_point(input.position, input.blendindices, input.blendweights), 1); + #else + float4 position = input.position; + #endif + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + // Write output channels + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + #if defined(NEEDS_WORLD_SPACE_POSITION) + float4 wp = mul(position, world); + // TODO: Expose output channel here + #endif + + // All members of the params struct has to be initialized, so assign dummy normal here. + #if !defined(HAS_NORMAL) || defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_VERTEX_PARAM(params, world_space_normal) = float4(0, 0, 0, 0); + #endif + + #if defined(NEEDS_TANGENT_SPACE) + GRAPH_VERTEX_PARAM(params, tsm0) = + GRAPH_VERTEX_PARAM(params, tsm1) = + GRAPH_VERTEX_PARAM(params, tsm2) = + float4(0, 0, 0, 0); + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = normalize(camera_pos - wp.rgb); + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(HAS_VERTEX_OFFSET) + wp += float4(results.vertex_offset, 0); + #endif + p = mul(wp, view_proj); + #else + p = mul(position, world_view_proj); + #endif + + o.position = p; + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + #if defined(DRAW_WIREFRAME) + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) && defined(INSTANCED) + return input.instance_wireframe_color; + #else + return dev_wireframe_color; + #endif + } + #else + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + #if defined(HAS_OPACITY) && !defined(TRANSPARENT) && !defined(TRANSPARENT_FADE) + GraphPixelParams params; + GraphPixelResults graph; + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + + if (graph.opacity < threshold) + discard; + #endif + + return float4(1, 1, 1, 1); + } + #endif + """ + } + } +} diff --git a/vmf_source/core/stingray_renderer/output_nodes/terrain_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/terrain_base.shader_node new file mode 100644 index 0000000..d681b8a --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/terrain_base.shader_node @@ -0,0 +1,764 @@ +group = "Output" +display_name = "Terrain Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "aca690cb-6305-4a2f-bf3d-69183a493db3" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "34259752-b962-4b65-92c3-903a57338519" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "7a9306c6-95ae-4cdb-9fef-0eedacce4e83" = { + name = "opacity_threshold" + is_required = false + display_name = "Opacity Threshold" + type = { scalar: ["HAS_OPACITY_THRESHOLD"] } + domain = "pixel" + } + + "b1c86408-aacb-4466-b754-ddcf37a3a2c8" = { + is_required = false + name = "normal" + display_name = "Normal" + type = { vector3: ["HAS_NORMAL"] } + domain = "pixel" + } + + "ad5e052f-d316-4a0f-8b79-53c38204d61b" = { + is_required = false + name = "metallic" + display_name = "Metallic" + type = { scalar: ["HAS_METALLIC"] } + domain = "pixel" + } + + "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" = { + is_required = false + name = "roughness" + display_name = "Roughness" + type = { scalar: ["HAS_ROUGHNESS"] } + domain = "pixel" + } + + "59fd1cf4-f736-470d-8510-1dd7c016639e" = { + is_required = false + name = "ambient_occlusion" + display_name = "Ambient Occlusion" + type = { scalar: ["HAS_AMBIENT_OCCLUSION"] } + domain = "pixel" + } + + "6ae3afc3-7a2d-4572-a376-f2649241c71d" = { + is_required = false + name = "density" + display_name = "Density" + type = { scalar: ["HAS_DENSITY"] } + domain = "pixel" + } +} + +options = { + "a84e242a-2ee2-4542-846a-3691a583e62c" = "USE_GLOBAL_ROUGHNESS_MULTIPLIER" +} + +ui = [ + { type = "checkbox" display_name = "Use Global Roughness Multiplier" option = "a84e242a-2ee2-4542-846a-3691a583e62c" } +] + +render_state = { + terrain = { + inherit: ["core/stingray_renderer/shader_libraries/common#gbuffer_material"] + state: { + fill_mode = "fill_solid" + cull_mode = "cull_ccw" + } + } + + + terrain_alpha_masked_with_prez = { + inherit: ["core/stingray_renderer/shader_libraries/common#gbuffer_material"] + state: { + + fill_mode = "fill_solid" + cull_mode = "cull_ccw" + z_func = "equal" + z_write_enable = "false" + z_enable = "true" + } + } + + depth_only = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + } + } + + terrain_depth_prepass = { + inherit: ["depth_only"] + state: { + fill_mode = "fill_solid" + cull_mode = "cull_ccw" + } + } + + wireframe = { + inherit: ["core/stingray_renderer/shader_libraries/common#opacity"] + state: { + fill_mode = "fill_wireframe" + "on_renderer(D3D11, D3D12)" = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + } + + shadow_caster = { + inherit: ["depth_only"] + state: { + "on_renderer(D3D11, D3D12)" = { + depth_bias = "0xff" + slope_scale_depth_bias = "1.0" + } + "on_renderer(GNM)" = { + offset_front = "0.0001" + offset_scale_front = "40.0" + offset_back = "0.0001" + offset_scale_back = "40.0" + } + "on_renderer(GL)" = { + offset_factor = "1.0" + offset_units = "1024.0" + depth_bias_enable = "true" + } + } + } +} + +sampler_state = { } + +channels = { + "defined(NEEDS_TANGENT_SPACE)": { + tsm0 = { type = "float3" domain = "vertex" } + tsm1 = { type = "float3" domain = "vertex" } + tsm2 = { type = "float3" domain = "vertex" } + } + + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "defined(MOTION_BLUR)": { + last_clip_position = { type = "float3" domains = ["vertex", "pixel"] } + } + + vertex_position = { type = "float4" domain = "vertex" } + terrain_uv = { type = "float2" domains=["vertex" "pixel"] } +} + +permutation_sets = { +} + +shader_contexts = { + shadow_caster = { + passes_sort_mode = "immediate" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM)" } + ] + + passes = [ + { code_block="terrain_depth_only" render_state="shadow_caster" } + ] + } + + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM)" permute_with: "default" } + ] + + passes = [ + { if: "defined(HAS_OPACITY)" then: [ + { layer="depth_prepass" code_block="terrain_depth_only" defines=["DEPTH_PREPASS"] render_state="terrain_depth_prepass" } // TODO: Make a depth shader + { layer="gbuffer_terrain" code_block="terrain" defines="MOTION_BLUR" render_state="terrain_alpha_masked_with_prez" } + //{ layer="gbuffer_terrain" code_block="terrain" defines=["MOTION_BLUR" "SINGLE_PASS"] render_state="terrain" } + ] else: [ + { layer="gbuffer_terrain" code_block="terrain" defines="MOTION_BLUR" render_state="terrain" } + ]} + { if: "!on_renderer(GL)" then: [ + { layer="wireframe" code_block="terrain_depth_only" defines=["DRAW_WIREFRAME"] render_state="wireframe" branch_key="dev_wireframe" } + ]} + ] + } +} + +code_blocks = { + terrain_shared = { + code = { + hlsl = """ + float2 morph(float2 uv, float2 wp, float t, float gsize, float psize) { + float3 grid_size = { gsize, gsize*0.5, 2.f/gsize }; + float2 frac_part = (frac(uv*grid_size.yy) * grid_size.zz) * psize.xx; + return wp - frac_part * t; + } + + float3 normal_from_hmap(Sampler2D height_map, float2 uv, float2 texel_size, float texel_aspect) { + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float4 h = { + TEX2D(height_map, uv + texel_size * float2(-1, 0)).r, + TEX2D(height_map, uv + texel_size * float2(1, 0)).r, + TEX2D(height_map, uv + texel_size * float2(0, -1)).r, + TEX2D(height_map, uv + texel_size * float2(0, 1)).r + }; + #endif + + h *= texel_aspect; + + float3 n = { + h[0] - h[1], + h[3] - h[2], + 2 + }; + + return normalize(n); + } + + float3 normal_from_hmap(Sampler2D height_map, float4 uv_lod, float2 texel_size, float texel_aspect) { + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float4 h = { + TEX2DLOD(height_map, uv_lod.xy + texel_size * float2(-1, 0), uv_lod.zw).r, + TEX2DLOD(height_map, uv_lod.xy + texel_size * float2(1, 0), uv_lod.zw).r, + TEX2DLOD(height_map, uv_lod.xy + texel_size * float2(0, -1), uv_lod.zw).r, + TEX2DLOD(height_map, uv_lod.xy + texel_size * float2(0, 1), uv_lod.zw).r + }; + #endif + + h *= texel_aspect; + + float3 n = { + h[0] - h[1], + h[3] - h[2], + 2 + }; + + return normalize(n); + } + """ + } + } + + terrain = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/common#taa_offsets", + "terrain_shared" ] + + samplers = { + //global_diffuse_map = { + // sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + // source = "resource_set" + // slot_name = "global_diffuse_map" + // type = "cube" + //} + hmap = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hmap" + type = "2d" + } + } + + code = { + hlsl = """ + #if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + DECLARE_SAMPLER_2D(hmap); + + struct VS_INPUT { + float4 position : POSITION; + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 color : COLOR; + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_per_object) + float4x4 world; + float4x4 world_view_proj; + float3 terrain_size; + float3 lod_camera_pos; + float4x4 last_world; + + #if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER) + float global_roughness_multiplier; + #endif + + float4 dev_wireframe_color; + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + Buffer idata; + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input, uint instance_id : SV_InstanceId) + { + PS_INPUT o; + float4 p; + + GraphVertexParams params; + GraphVertexResults results; + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float4 pos_scale = idata.Load(instance_id * 2 + 0); + float4 tile_info = idata.Load(instance_id * 2 + 1); + #endif + float2 half_size = terrain_size.xy * 0.5; + float2 mip = input.position.xy; + float2 pos = (mip.xy - 0.5) * pos_scale.zw + pos_scale.xy; + + float3 temp_wp = mul(float4(pos, 0, 1), world); + float t = 1-saturate((distance(temp_wp, camera_pos) - (tile_info.x - pos_scale.z*0.5)) / tile_info.y); + + float2 huv = ((pos.xy / half_size) * 0.5 + 0.5); + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + huv.y = 1-huv.y; + #endif + float h = TEX2DLOD(hmap, huv, 0).r * terrain_size.z; + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + // Write output channels + float4 position = float4(pos.xy, h, 1); + #if defined(NEEDS_WORLD_SPACE_POSITION) + float4 wp = mul(position, world); + // TODO: Expose output channel here + #endif + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp.rgb; + #endif + + GRAPH_VERTEX_PARAM(params, terrain_uv) = huv; + + #if defined(NEEDS_TANGENT_SPACE) + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float2 res; + TEXTURE_NAME(hmap).GetDimensions(res.x, res.y); + float2 inv_hmap_size = 1.0/res; + #endif + float3 normal = normal_from_hmap(hmap, float4(huv, 0, 0), inv_hmap_size, terrain_size.z); + + // create tangentspace vectors + normal = mul(normal, (float3x3)world); + float3 tangent = normalize(cross(world._m10_m11_m12, normal)); // TODO: redundant normalize? + float3 binormal = normalize(cross(normal, tangent)); // TODO: redundant normalize? + + tspace_transpose( + GRAPH_VERTEX_PARAM(params, tsm0), + GRAPH_VERTEX_PARAM(params, tsm1), + GRAPH_VERTEX_PARAM(params, tsm2), + tangent, binormal, normal); + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + p = mul(position, world_view_proj); + + #if defined(MOTION_BLUR) + float4 last_wp = mul(position, last_world); + float4 last_clip_pos = mul(last_wp, camera_last_view_projection); + float4 last_view_space = last_clip_pos / last_clip_pos.w; + last_view_space.xy += get_vs_halton_offset(frame_number); + last_view_space.xy = last_view_space.xy * 0.5 + 0.5; + last_view_space.y = 1.0 - last_view_space.y; + last_clip_pos = last_view_space * last_clip_pos.w; + GRAPH_VERTEX_PARAM(params, last_clip_position) = last_clip_pos.xyw; + #endif + + #if defined(DRAW_WIREFRAME) + o.position = p; + #else + float4 view_space = p / p.w; + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + #endif + + const float3 lod_cols[8] = { + float3(1,0,0), + float3(0,1,0), + float3(0,0,1), + float3(1,1,0), + float3(0,1,1), + float3(1,1,0.5), + float3(1,0,0.5), + float3(0,1,0.5) + }; + + o.color = float4(lod_cols[(uint)tile_info.w], t); + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + #if defined(DRAW_WIREFRAME) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + return dev_wireframe_color; + } + #elif defined(DEPTH_ONLY) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if defined(HAS_OPACITY) + GraphPixelParams params; + GraphPixelResults graph; + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + + if (graph.opacity < threshold) + discard; + #endif + + return float4(1,1,1,1); + } + #else + //DECLARE_SAMPLER_CUBE(global_diffuse_map); + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + GBUFFER_OUT ps_main(PS_INPUT input) + { + GBUFFER_OUT o; + + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY) && defined(SINGLE_PASS) + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + + if (graph.opacity < threshold) + discard; + #endif + + #if !defined(HAS_NORMAL) + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float2 res; + TEXTURE_NAME(hmap).GetDimensions(res.x, res.y); + float2 inv_hmap_size = 1.0/res; + #endif + float3 tnormal = normal_from_hmap(hmap, GRAPH_PIXEL_DATA(input, terrain_uv), inv_hmap_size, terrain_size.z); + #endif + + // Base color + float3 base_color = float3(0,0,0); + #if defined(HAS_BASE_COLOR) + base_color = graph.base_color; + #else + base_color = float3(0.5, 0.5, 0.5); + #endif + BASE_COLOR(o) = gbuffer_encode_base_color(base_color); + + // World space normal + #if defined(HAS_NORMAL) + float3 wn = graph.normal; + #else + float3 wn = tnormal; + #endif + wn = mul(wn, (float3x3)world); + + NORMAL(o) = gbuffer_encode_normal(wn); + + // Metallic + half metallic_ = 0.f; + #if defined(HAS_METALLIC) + metallic_ = graph.metallic; + #else + metallic_ = 0.f; + #endif + METALLIC(o) = gbuffer_encode_metallic_mask(metallic_); + + // Roughness + half roughness_ = 0.f; + #if defined(HAS_ROUGHNESS) + roughness_ = graph.roughness; + #else + roughness_ = 0.5; + #endif + #if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER) + roughness_ = saturate(roughness_ * global_roughness_multiplier); + #endif + ROUGHNESS(o) = gbuffer_encode_roughness(roughness_); + + #if defined(MOTION_BLUR) + float3 last_clip_pos = GRAPH_PIXEL_DATA(input, last_clip_position); + float2 screen_pos = (input.position.xy / back_buffer_size - viewport.zw) / viewport.xy; + float2 last_screen_pos = last_clip_pos.xy / last_clip_pos.z; + VELOCITY(o) = encode_velocity(viewport.xy*(screen_pos - last_screen_pos)); + #else + VELOCITY(o) = encode_velocity(float2(0.0, 0.0)); + #endif + + // Density + half density = 1.0; + #if defined(HAS_DENSITY) + density = graph.density; + #endif + DENSITY(o) = density; + + // Since we don't have baked lighting, we don't use this + // // Ambient Diffuse + // float3 ambient = float3(0, 0, 0); + // #if defined(HAS_VERTEX_BAKED_DIFFUSE_LIGHTING) + // ambient = GRAPH_PIXEL_DATA(input, baked_light).rgb; + // #elif defined(HAS_LIGHTMAP_BAKED_DIFFUSE_LIGHTING) && defined(HAS_UV_UNWRAP) + // ambient = TEX2D(lightmap, GRAPH_PIXEL_DATA(input, lightmap_uv)).rgb; + // #else + // ambient = rgbm_decode(TEXCUBELOD(global_diffuse_map, wn, 0)); + // #endif + + // AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(ambient); + + #if defined(HAS_AMBIENT_OCCLUSION) + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(graph.ambient_occlusion); + #else + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(1.f); + #endif + + return o; + } + #endif + """ + } + } + + terrain_depth_only = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/common#taa_offsets", + "terrain_shared" ] + + samplers = { + hmap = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hmap" + type = "2d" + } + } + + code = { + hlsl = """ + #if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + DECLARE_SAMPLER_2D(hmap); + + struct VS_INPUT { + float4 position : POSITION; + #if defined(HAS_OPACITY) + GRAPH_VERTEX_INPUT + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(HAS_OPACITY) + GRAPH_PIXEL_INPUT + #endif + }; + + // TODO: why does not this tinner buffer work? + /*CBUFFER_START(c_per_object_depth_only) + float4x4 world; + float4x4 world_view_proj; + float3 terrain_size; + + float4 dev_wireframe_color; + #if defined(HAS_OPACITY) + GRAPH_MATERIAL_EXPORTS + #endif + CBUFFER_END*/ + + CBUFFER_START(c_per_object) + float4x4 world; + float4x4 world_view_proj; + float3 terrain_size; + float3 lod_camera_pos; + float4x4 last_world; + + #if defined(USE_GLOBAL_ROUGHNESS_MULTIPLIER) + float global_roughness_multiplier; + #endif + + float4 dev_wireframe_color; + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + Buffer idata; + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input, uint instance_id : SV_InstanceId) + { + PS_INPUT o; + float4 p; + + #if defined(HAS_OPACITY) + GraphVertexParams params; + GraphVertexResults results; + #endif + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float4 pos_scale = idata.Load(instance_id * 2 + 0); + float4 tile_info = idata.Load(instance_id * 2 + 1); + #endif + float2 half_size = terrain_size.xy * 0.5; + float2 mip = input.position.xy; + float2 pos = (mip.xy - 0.5) * pos_scale.zw + pos_scale.xy; + + float2 huv = ((pos.xy / half_size) * 0.5 + 0.5); + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + huv.y = 1-huv.y; + #endif + float h = TEX2DLOD(hmap, huv, 0).r * terrain_size.z; + + float4 position = float4(pos.xy, h, 1); + + #if defined(HAS_OPACITY) + #if defined(NEEDS_WORLD_SPACE_POSITION) + float4 wp = mul(position, world); + // TODO: Expose output channel here + #endif + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + // Write output channels + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + GRAPH_VERTEX_PARAM(params, terrain_uv) = huv; + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp.rgb; + #endif + + #if defined(NEEDS_TANGENT_SPACE) + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float2 res; + TEXTURE_NAME(hmap).GetDimensions(res.x, res.y); + float2 inv_hmap_size = 1.0/res; + #endif + float3 normal = normal_from_hmap(hmap, float4(huv, 0, 0), inv_hmap_size, terrain_size.z); + + // create tangentspace vectors + normal = mul(normal, (float3x3)world); + float3 tangent = normalize(cross(world._m10_m11_m12, normal)); // TODO: redundant normalize? + float3 binormal = normalize(cross(normal, tangent)); // TODO: redundant normalize? + + tspace_transpose( + GRAPH_VERTEX_PARAM(params, tsm0), + GRAPH_VERTEX_PARAM(params, tsm1), + GRAPH_VERTEX_PARAM(params, tsm2), + tangent, binormal, normal); + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + #endif + + p = mul(position, world_view_proj); + + #if defined(DEPTH_PREPASS) + float4 view_space = p / p.w; + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + #else + o.position = p; + #endif + + #if defined(HAS_OPACITY) + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + #endif + + return o; + } + + #if defined(DRAW_WIREFRAME) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + return dev_wireframe_color; + } + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if defined(HAS_OPACITY) + GraphPixelParams params; + GraphPixelResults graph; + GRAPH_PIXEL_WRITE_PARAMS(params, input); + GRAPH_EVALUATE_PIXEL(graph, params); + + #if defined(HAS_OPACITY_THRESHOLD) + float threshold = graph.opacity_threshold; + #else + float threshold = 0.5f; + #endif + + if (graph.opacity < threshold) + discard; + #endif + + return float4(1,1,1,1); + } + #endif + """ + } + } +} diff --git a/vmf_source/core/stingray_renderer/output_nodes/unlit_base.shader_node b/vmf_source/core/stingray_renderer/output_nodes/unlit_base.shader_node new file mode 100644 index 0000000..3acdf67 --- /dev/null +++ b/vmf_source/core/stingray_renderer/output_nodes/unlit_base.shader_node @@ -0,0 +1,746 @@ +group = "Output" +display_name = "Unlit Base" +output_node = true +render_configs = ["core/stingray_renderer/renderer"] + +inputs = { + "aee6e47b-be7b-4d67-a123-2ab5d660b94e" = { + name = "vertex_offset" + display_name = "Position offset" + is_required = false + type = { vector3: ["HAS_VERTEX_OFFSET"] } + domain = "vertex" + } + + "aca690cb-6305-4a2f-bf3d-69183a493db3" = { + name = "base_color" + is_required = false + display_name = "Base Color" + type = { vector3: ["HAS_BASE_COLOR"] } + domain = "pixel" + } + + "34259752-b962-4b65-92c3-903a57338519" = { + name = "opacity" + is_required = false + display_name = "Opacity" + type = { scalar: ["HAS_OPACITY"] } + domain = "pixel" + } + + "c9a30d71-a8ad-4b9e-aedf-d785ae7e301f" = { + name = "additive" + is_required = false + display_name = "Additive" + type = { scalar: ["HAS_ADDITIVE"] } + domain = "pixel" + } +} + +options = { + "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" = "INSTANCED" + "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" = "DOUBLE_SIDED" + "34994d84-9d51-48ac-af85-bc053b2c65c3" = "SKIN" + "e1bfa889-2503-4ac3-9134-c08a7fa04568" = "PROJECT_TO_FAR_PLANE" + "6a6241cc-7d21-4e2e-87c8-8d9c7bdcd322" = "CAMERA_TRANSLATION_LOCK" + "435e14e4-556d-4ac1-af14-8dafe63aff8f" = "BLEND_TRANSPARENT" + "52c7ce01-ee57-4770-914e-727fc1966962" = "LAYER_EMISSIVE" + "182fabd6-9a4d-4cfc-8c8f-0c45ef09a138" = "LAYER_SKYDOME" + "4c3163d4-c086-4645-ba1c-0d68a98022a1" = "LAYER_HDR_TRANSPARENT" + "c8d8b754-c567-4c7b-9cbd-8acab22beff5" = "LAYER_TRANSPARENT" + "afe47c59-33c4-43b2-af4a-817085b1113c" = "DEPTH_TEST_INVERTED" + "774556cd-2d1e-4df8-8ae2-5e84800f0c04" = "DEPTH_TEST_DISABLED" + "7b8bc0bf-c453-49d2-9415-0e80fec1039f" = "DISABLE_DEPTH_WRITE" + "b2556764-e8e9-47cf-9ecc-f53b5d5d73c7" = "HAS_CUSTOM_FOV" +} + +ui = [ + { + type = "drop_down" + display_name = "Layer" + options = { + "Emissive" = "52c7ce01-ee57-4770-914e-727fc1966962" + "Skydome" = "182fabd6-9a4d-4cfc-8c8f-0c45ef09a138" + "HDR Transparent" = "4c3163d4-c086-4645-ba1c-0d68a98022a1" + "LDR Transparent" = "c8d8b754-c567-4c7b-9cbd-8acab22beff5" + } + default = "4c3163d4-c086-4645-ba1c-0d68a98022a1" + } + + { + type = "drop_down" + display_name = "Depth Testing" + options = { + "Normal" = "00000000-0000-0000-0000-000000000000" + "Inverted" = "afe47c59-33c4-43b2-af4a-817085b1113c" + "Disabled" = "774556cd-2d1e-4df8-8ae2-5e84800f0c04" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { + type = "drop_down" + display_name = "Blend Mode" + options = { + "Opaque" = "00000000-0000-0000-0000-000000000000" + "Transparent" = "435e14e4-556d-4ac1-af14-8dafe63aff8f" + } + default = "00000000-0000-0000-0000-000000000000" + } + + { type = "checkbox" display_name = "Disable Depth Writes" option = "7b8bc0bf-c453-49d2-9415-0e80fec1039f" } + { type = "checkbox" display_name = "Double Sided" option = "8df1b8f7-17c2-4ae4-8c4e-25517ec1df46" } + { type = "checkbox" display_name = "Instancing" option = "d1a42a54-0794-4d57-9aa0-eb35acb6b35c" } + { type = "checkbox" display_name = "Project to Far Plane" option = "e1bfa889-2503-4ac3-9134-c08a7fa04568" } + { type = "checkbox" display_name = "Camera Translation Lock" option = "6a6241cc-7d21-4e2e-87c8-8d9c7bdcd322" } + { type = "checkbox" display_name = "Custom FOV" option = "b2556764-e8e9-47cf-9ecc-f53b5d5d73c7" } +] + +render_state = { + unlit = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + "defined(BLEND_TRANSPARENT)" = { + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_one" + } + + "defined(DEPTH_TEST_DISABLED)" = { + z_enable = "false" + } + + "defined(DISABLE_DEPTH_WRITE)" = { + z_write_enable = "false" + } + + "defined(DEPTH_TEST_INVERTED)" = { + z_func = "greater_equal" + } + } + } + + material_transfer = { + inherit: ["core/stingray_renderer/shader_libraries/common#default"] + state: { + cull_mode = "cull_none" + z_write_enable = "false" + z_enable = "false" + } + } + + wireframe = { + inherit: ["core/stingray_renderer/shader_libraries/common#opacity"] + state: { + fill_mode = "fill_wireframe" + "on_renderer(D3D11, D3D12)" = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + } +} + +sampler_state = { } + +channels = { + "defined(NEEDS_EYE_VECTOR)": { + eye_vector = { type = "float3" domain = "vertex" } + } + + "defined(NEEDS_WORLD_SPACE_NORMAL)": { + world_space_normal = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(MOTION_BLUR)": { + last_clip_position = { type = "float3" domains = ["vertex", "pixel"] } + } + + "defined(MATERIAL_TRANSFER)": { + lightmap_uv = { type = "float2" semantic="TEXCOORD1" domains = ["vertex"] } + } + + "defined(NEEDS_SCREEN_POS)": { + screen_pos = { type = "float2" domain = "pixel" } + } + + "defined(NEEDS_PIXEL_DEPTH)": { + pixel_depth = { type = "float" domain = "pixel" } + } + + vertex_position = { type = "float4" domain = "vertex" } + vertex_normal = { type = "float3" semantic = "NORMAL" domain = "vertex" } +} + +//log_permutations = true +permutation_sets = { + vertex_modifiers = [ + { if: "num_skin_weights() == 4" define: { "macros": ["SKINNED_4WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 3" define: { "macros": ["SKINNED_3WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 2" define: { "macros": ["SKINNED_2WEIGHTS"] stages: ["vertex"] } } + { if: "num_skin_weights() == 1" define: { "macros": ["SKINNED_1WEIGHT"] stages: ["vertex"] } } + { default = true } + ] + + instanced_modifiers = [ + { default = true } + ] + + non_instanced_modifiers = [ + { permute_with: "vertex_modifiers" } + ] + + default = [ + { if: "defined(INSTANCED)" permute_with: "instanced_modifiers" } + { if: "!defined(INSTANCED)" permute_with: "non_instanced_modifiers" } + ] +} + +shader_contexts = { + default = { + passes_sort_mode = "deferred" + compile_with = [ + { if: "on_renderer(D3D11, D3D12, GNM, GL) && defined(LAYER_HDR_TRANSPARENT) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] permute_with: "default" } + { if: "on_renderer(D3D11, D3D12, GNM, GL)" permute_with: "default" } + ] + + passes = [ + { if: "defined(LAYER_EMISSIVE)" then: [ + { layer="emissive" code_block="unlit" defines="MOTION_BLUR" render_state="unlit" } + ]} + { if: "defined(LAYER_SKYDOME)" then: [ + { layer="skydome" code_block="unlit" render_state="unlit" } + ]} + { if: "defined(LAYER_HDR_TRANSPARENT)" then: [ + { layer="hdr_transparent" code_block="unlit" render_state="unlit" } + ]} + + { if: "defined(LAYER_TRANSPARENT)" then: [ + { layer="transparent" code_block="unlit" render_state="unlit" } + ] else: [ + { if: "!on_renderer(GL)" then: [ + { layer="wireframe" code_block="unlit" define="DRAW_WIREFRAME" render_state="wireframe" branch_key="dev_wireframe" } + ]} + ]} + ] + } +} + +code_blocks = { + macros = { + code = { + shared = """ + #define TAA_GUI_DEPTH_BIAS_RANGE 50.0 // The [0, TAA_GUI_DEPTH_BIAS_RANGE] depth range for which we lerp the min-max depth biases for line drawing + #define TAA_GUI_MIN_DEPTH_BIAS 0.001 // The depth offset to add for lines drawn at z = 0 + #define TAA_GUI_MAX_DEPTH_BIAS 0.05 // The depth offset to add for lines drawn at z >= GUI_DEPTH_BIAS_RANGE + + // We need to disable instancing for the material transfer context as it doesn't use the world transform. + #if defined(INSTANCED) && defined(MATERIAL_TRANSFER) + #undef INSTANCED + #endif + + #if defined(PS_NEEDS_WP) || defined(NEEDS_EYE_VECTOR) || defined(HAS_VERTEX_OFFSET) || ((defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) && defined(INSTANCED)) || defined(CAMERA_TRANSLATION_LOCK) || defined(BILLBOARD) + #define NEEDS_WORLD_SPACE_POSITION + #endif + """ + } + } + + unlit = { + include:[ + "core/stingray_renderer/shader_libraries/common#common", + "core/stingray_renderer/shader_libraries/common#gbuffer_access", + "core/stingray_renderer/shader_libraries/common#skinning", + "core/stingray_renderer/shader_libraries/common#taa_offsets", + "macros", + "core/stingray_renderer/shader_libraries/common#billboard_transformation"] + + instance_data = { + "on_renderer(D3D11, D3D12) && !defined(MATERIAL_TRANSFER) && defined(INSTANCED)": { + world = { type = "matrix4x4" } + "defined(MOTION_BLUR)": { + last_world = { type = "matrix4x4" } + } + "defined(DRAW_WIREFRAME)": { + dev_wireframe_color = { type = "vector4" } + } + } + } + + samplers = { + "defined(NEEDS_LINEAR_DEPTH)": { + linear_depth = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_point" + source = "resource_set" + slot_name = "linear_depth" + type = "2d" + } + } + "!defined(LOW_RES)": { + "defined(LOW_RES_ENABLED)": { + hdr_transparent_div4 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr_transparent_div4" + type = "2d" + } + + hdr_linear_depth_div4 = { + sampler_state = "core/stingray_renderer/shader_libraries/common#clamp_linear" + source = "resource_set" + slot_name = "hdr_linear_depth_div4" + type = "2d" + } + } + } + } + + code = { + glsl = """ + #if defined(STAGE_VERTEX) + layout(location = POSITION0) in highp vec4 in_pos; + + CBUFFER_START(c_per_object) + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(CAMERA_TRANSLATION_LOCK) + UNIFORM mat4 view; + UNIFORM mat4 proj; + #else + UNIFORM mat4 view_proj; + #endif + #else + UNIFORM mat4 world_view_proj; + #endif + UNIFORM mat4 world; + CBUFFER_END + + void main() + { + GraphManualChannels params; + GraphResults graph; + + // Write output channels + #if defined(SKINNED) + vec4 position = vec4(skin_point(in_pos, blendindices, blendweights), 1); + mediump vec3 normal = skin_vector(GRAPH_DATA(vertex_normal), blendindices, blendweights); + #if defined(NEEDS_TANGENT_SPACE) + mediump vec3 tangent = skin_vector(GRAPH_DATA(vertex_tangent), blendindices, blendweights); + mediump vec3 binormal = skin_vector(GRAPH_DATA(vertex_binormal), blendindices, blendweights); + #endif + #else + vec4 position = in_pos; + mediump vec3 normal = GRAPH_DATA(vertex_normal); + #if defined(NEEDS_TANGENT_SPACE) + mediump vec3 tangent = GRAPH_DATA(vertex_tangent); + mediump vec3 binormal = GRAPH_DATA(vertex_binormal); + #endif + #endif + + #if defined(NEEDS_WORLD_SPACE_POSITION) + vec4 wp = position * world; + #endif + + GRAPH_PARAM(params, vertex_position) = position; + + #if defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_PARAM(params, world_space_normal) = normal * mat3(world); + #endif + + #if defined(NEEDS_EYE_VECTOR) + GRAPH_PARAM(params, eye_vector) = camera_pos - wp.rgb; + #endif + + #if defined(NEEDS_TANGENT_SPACE) + tspace_transform_transpose( + GRAPH_PARAM(params, tsm0), + GRAPH_PARAM(params, tsm1), + GRAPH_PARAM(params, tsm2), + tangent, binormal, normal, + mat3(world)); + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + graph_evaluate(graph, params); + + vec4 p; + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(HAS_VERTEX_OFFSET) + wp += vec4(graph.vertex_offset, 0); + #endif + + #if defined(CAMERA_TRANSLATION_LOCK) + view[0][3] = 0.0; + view[1][3] = 0.0; + view[2][3] = 0.0; + p = (wp * view) * proj; + #else + p = wp * view_proj; + #endif + #else + p = position * world_view_proj; + #endif + + #if defined(PROJECT_TO_FAR_PLANE) + p.z = p.w; + #endif + + gl_Position = p; + } + #elif defined(STAGE_FRAGMENT) + layout(location = 0) out mediump vec4 out_color; + + void main() + { + #if defined(HAS_BASE_COLOR) || defined(HAS_OPACITY) + GraphManualChannels params; + GraphResults graph; + graph_evaluate(graph, params); + #endif + + vec3 color = vec3(0.5, 0.5, 0.5); + #if defined(HAS_BASE_COLOR) + color = graph.base_color; + #endif + + float op = 1.0; + #if defined(HAS_OPACITY) + op = graph.opacity; + #endif + + out_color = vec4(color.r, color.g, color.b, op); + } + #endif + """ + + hlsl = """ + #if defined(NEEDS_LINEAR_DEPTH) + DECLARE_SAMPLER_2D(linear_depth); + #define HAS_LINEAR_DEPTH + #endif + + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + DECLARE_SAMPLER_2D(hdr_transparent_div4); + DECLARE_SAMPLER_2D(hdr_linear_depth_div4); + #endif + + #if defined(NEEDS_PIXEL_DEPTH) || (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) + #define PS_NEEDS_WP + #endif + + #if defined(PS_NEEDS_WP) + #define NEEDS_WORLD_SPACE_POSITION + #endif + + struct VS_INPUT { + float4 position : POSITION; + SKIN_INPUT + GRAPH_VERTEX_INPUT + }; + + struct PS_INPUT { + #if (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) && defined(INSTANCED) && defined(DRAW_WIREFRAME) + float4 instance_wireframe_color : COLOR0; + #endif + float4 position : SV_POSITION; + #if defined(PS_NEEDS_WP) + float3 world_pos : TEXCOORD15; + #endif + GRAPH_PIXEL_INPUT + }; + + CBUFFER_START(c_per_object) + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(CAMERA_TRANSLATION_LOCK) + float4x4 view; + float4x4 proj; + #else + float4x4 view_proj; + #endif + #else + float4x4 world_view_proj; + #endif + #if defined(BILLBOARD) && defined(LAYER_SKYDOME_BILLBOARD) + #if defined(SECONDARY_SUN_DIRECTION) + float3 secondary_sun_direction; + #define billboard_direction secondary_sun_direction + #else + float3 sun_direction; + #define billboard_direction sun_direction + #endif + #endif + float4x4 world; + float4x4 last_world; + float4 dev_wireframe_color; + GRAPH_MATERIAL_EXPORTS + CBUFFER_END + + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + Buffer idata; + float ioffset; + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + , uint instance_id : SV_InstanceId + #endif + ) + { + PS_INPUT o; + float4 p; + + GraphVertexParams params; + GraphVertexResults results; + + #if defined(INSTANCED) && (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) + uint offset = (uint)ioffset + instance_id*IDATA_STRIDE; + world[0] = idata.Load(offset + IDATA_world + 0); + world[1] = idata.Load(offset + IDATA_world + 1); + world[2] = idata.Load(offset + IDATA_world + 2); + world[3] = idata.Load(offset + IDATA_world + 3); + + #if defined(MOTION_BLUR) + last_world[0] = idata.Load(offset + IDATA_last_world + 0); + last_world[1] = idata.Load(offset + IDATA_last_world + 1); + last_world[2] = idata.Load(offset + IDATA_last_world + 2); + last_world[3] = idata.Load(offset + IDATA_last_world + 3); + #endif + + #if defined(DRAW_WIREFRAME) + o.instance_wireframe_color = idata.Load(offset + (instance_id*IDATA_STRIDE + IDATA_dev_wireframe_color)); + #endif + #endif + + // Write automatic params + GRAPH_VERTEX_WRITE_PARAMS(params, input); + + // Write output channels + #if defined(SKINNED) + float4 position = float4(skin_point(input.position, input.blendindices, input.blendweights), 1); + #if defined(MOTION_BLUR) + float4 last_position = float4(skin_point_last_frame(input.position, input.blendindices, input.blendweights), 1); + #endif + #if !defined(BILLBOARD) + #if (defined(NEEDS_WORLD_SPACE_NORMAL) || defined(NEEDS_TANGENT_SPACE)) + float3 normal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_normal).xyz, input.blendindices, input.blendweights); + #endif + #if defined(NEEDS_TANGENT_SPACE) + float3 tangent = skin_vector(GRAPH_VERTEX_DATA(input, vertex_tangent).xyz, input.blendindices, input.blendweights); + float3 binormal = skin_vector(GRAPH_VERTEX_DATA(input, vertex_binormal).xyz, input.blendindices, input.blendweights); + #endif + #endif + #else + float4 position = input.position; + #if defined(MOTION_BLUR) + float4 last_position = position; + #endif + #if !defined(BILLBOARD) + #if (defined(NEEDS_WORLD_SPACE_NORMAL) || defined(NEEDS_TANGENT_SPACE)) + float3 normal = GRAPH_VERTEX_DATA(input, vertex_normal).xyz; + #endif + #if defined(NEEDS_TANGENT_SPACE) + float3 tangent = GRAPH_VERTEX_DATA(input, vertex_tangent).xyz; + float3 binormal = GRAPH_VERTEX_DATA(input, vertex_binormal).xyz; + #endif + #endif + #endif + + #if defined(BILLBOARD) + float4 wp; + float3 normal, tangent, binormal; + #if !defined(LAYER_SKYDOME_BILLBOARD) + get_billboard_data_from_position(world._m30_m31_m32, camera_world._m30_m31_m32, camera_view, position, wp, normal, tangent, binormal); + #else + get_billboard_data_from_direction(billboard_direction, position, wp, normal, tangent, binormal); + #endif + #elif defined(NEEDS_WORLD_SPACE_POSITION) + float4 wp = mul(position, world); + #endif + + GRAPH_VERTEX_PARAM(params, vertex_position) = position; + + #if defined(NEEDS_WORLD_SPACE_NORMAL) + GRAPH_VERTEX_PARAM(params, world_space_normal).rgb = mul(normal, (float3x3)world); + #endif + + #if defined(NEEDS_EYE_VECTOR) + #if defined(BILLBOARD) && defined(LAYER_SKYDOME_BILLBOARD) + // TODO: not correct length, we can't use length(eye_vector) to determine the distance + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = billboard_direction; + #else + GRAPH_VERTEX_PARAM(params, eye_vector).rgb = camera_pos - wp.rgb; + #endif + #endif + + #if defined(NEEDS_TANGENT_SPACE) + tspace_transform_transpose( + GRAPH_VERTEX_PARAM(params, tsm0), + GRAPH_VERTEX_PARAM(params, tsm1), + GRAPH_VERTEX_PARAM(params, tsm2), + tangent, binormal, normal, + (float3x3)world); + #endif + + #if defined(MOTION_BLUR) + GRAPH_VERTEX_PARAM(params, last_clip_position) = float3(0.0, 0.0, 0.0); + + #if defined(BILLBOARD) + float4 cur_wp = wp; + float4 last_wp; + #if !defined(LAYER_SKYDOME_BILLBOARD) + get_billboard_positions_from_position(last_world._m30_m31_m32, camera_last_world._m30_m31_m32, camera_last_view, last_position, last_wp); + #else + get_billboard_positions_from_direction(billboard_direction, last_position, last_wp); + #endif + #else + float4 cur_wp = mul(position, world); + float4 last_wp = mul(last_position, last_world); + #endif + if(length(cur_wp - last_wp) > 0.0) { + float4 last_clip_pos = mul(last_wp, camera_last_view_projection); + GRAPH_VERTEX_PARAM(params, last_clip_position) = last_clip_pos.xyw; + } + #endif + + // Evaluate all pieces of the graph that should run per-vertex. + GRAPH_EVALUATE_VERTEX(results, params); + + #if defined(NEEDS_WORLD_SPACE_POSITION) + #if defined(HAS_VERTEX_OFFSET) + wp += float4(results.vertex_offset, 0); + #endif + + #if defined(CAMERA_TRANSLATION_LOCK) + view._m30_m31_m32 = float3(0,0,0); + p = mul(mul(wp, view), proj); + #else + #if defined(HAS_CUSTOM_FOV) + p = mul(wp, camera_custom_fov_view_projection); + #else + p = mul(wp, view_proj); + #endif + #endif + #else + #if defined(HAS_CUSTOM_FOV) + // TODO: create camera_custom_fov_world_view_projection? + p = mul(mul(position, world), camera_custom_fov_view_projection); + #else + p = mul(position, world_view_proj); + #endif + #endif + + #if defined(MATERIAL_TRANSFER) + float2 unwrapped_uv = GRAPH_VERTEX_DATA(input, lightmap_uv); + float2 ndc = float2(unwrapped_uv.x, unwrapped_uv.y) * 2 - 1; + ndc.y *= -1; + p = float4(ndc, 0, 1); + #endif + + #if defined(PROJECT_TO_FAR_PLANE) + p.z = p.w; + #endif + + #if defined(LAYER_TRANSPARENT) && !defined(DEPTH_TEST_DISABLED) + p.z -= lerp(TAA_GUI_MIN_DEPTH_BIAS, TAA_GUI_MAX_DEPTH_BIAS, saturate((o.position.z / o.position.w) / TAA_GUI_DEPTH_BIAS_RANGE)) * taa_enabled; + #endif + + #if defined(PS_NEEDS_WP) + o.world_pos = wp.xyz; + #endif + + #if defined(DRAW_WIREFRAME) || defined(MATERIAL_TRANSFER) || defined(LAYER_TRANSPARENT) + o.position = p; + #else + float4 view_space = p / p.w; + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + #endif + + // Write results + GRAPH_VERTEX_WRITE(o, results, params); + + return o; + } + + #if defined(DRAW_WIREFRAME) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if (defined(RENDERER_D3D11) || defined(RENDERER_D3D12)) && defined(INSTANCED) + return input.instance_wireframe_color; + #else + return dev_wireframe_color; + #endif + } + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + #if defined(NEEDS_PIXEL_DEPTH) || (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) + const float3 world_pos = input.world_pos; + const float3 view_dir = camera_world._m30_m31_m32 - world_pos; + const float3 camera_dir = camera_world._m10_m11_m12; + const float depth = dot(-view_dir, camera_dir); + #endif + + #if defined(HAS_BASE_COLOR) || defined(HAS_OPACITY) || defined(HAS_ADDITIVE) + GraphPixelParams params; + GraphPixelResults graph; + + GRAPH_PIXEL_WRITE_PARAMS(params, input); + + #if defined(NEEDS_PIXEL_DEPTH) + GRAPH_PIXEL_PARAM(params, pixel_depth) = depth; + #endif + + #if defined(NEEDS_SCREEN_POS) + float2 screen_pos = (input.position.xy / back_buffer_size); + GRAPH_PIXEL_PARAM(params, screen_pos) = screen_pos; + #endif + + GRAPH_EVALUATE_PIXEL(graph, params); + #endif + + float3 color = float3(0.5, 0.5, 0.5); + #if defined(HAS_BASE_COLOR) + color = graph.base_color; + #endif + + float op = 1.f; + #if defined(HAS_OPACITY) + op = graph.opacity; + #endif + + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) + float2 screen_uv = input.position.xy / back_buffer_size; + + // Fade out high resolution particle if the low resolution particles is infront of the high resolution particle + float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg; + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + float background_depth = depth; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * max(depth_variance, 0.001))); + float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T); + op *= 1.0 - alpha_modifier; + #endif + + #ifdef LAYER_SKYDOME + return float4(color, 1.0); + #else + #if defined(HAS_ADDITIVE) + return float4(color * op, op * (1.0 - saturate(graph.additive))); + #else + return float4(color * op, op); + #endif + #endif + } + #endif + """ + } + } +} diff --git a/vmf_source/core/stingray_renderer/shader_import/global_textures.config b/vmf_source/core/stingray_renderer/shader_import/global_textures.config new file mode 100644 index 0000000..bef65d1 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_import/global_textures.config @@ -0,0 +1,5 @@ +texture_remap = { + ibl_brdf_lut = "core/stingray_renderer/lookup_tables/ibl_brdf_lut" + diffuse_cube = "core/stingray_renderer/environments/midday/diffuse_cube" + specular_cube = "core/stingray_renderer/environments/midday/specular_cube" +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_import/no_uvs.material b/vmf_source/core/stingray_renderer/shader_import/no_uvs.material new file mode 100644 index 0000000..79022f9 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_import/no_uvs.material @@ -0,0 +1,118 @@ + +shader = { + connections = [ + { + destination = { + connector_id = "aca690cb-6305-4a2f-bf3d-69183a493db3" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + } + } + ] + constants = [ + { + connector_id = "c4d6bc08-c489-430f-a836-ed490e59c3f9" + id = "964a0911-c691-405b-994d-5adf123e6f55" + instance_id = "7b49c63d-2d6b-4021-90f7-f0673a12d5e3" + value = [ + 1 + ] + } + ] + nodes = [ + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + options = [ + "2b136447-676e-4943-997b-04a28ae68497" + ] + position = [ + 1360 + 460 + ] + samplers = { + } + type = "core/stingray_renderer/output_nodes/standard_base" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "7b49c63d-2d6b-4021-90f7-f0673a12d5e3" + options = [ + ] + position = [ + 700 + 1320 + ] + samplers = { + } + title = "Ao" + type = "core/shader_nodes/constant_scalar" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Base Color" + name = "base_color" + type = "float3" + ui = { + max = [ + 1 + 1 + 1 + ] + min = [ + 0 + 0 + 0 + ] + order = 0 + step = [ + 0.001 + 0.001 + 0.001 + ] + ui_type = "Color" + } + value = [ + 0.5 + 0.5 + 0.5 + ] + } + } + id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + options = [ + ] + position = [ + 1120 + 300 + ] + samplers = { + } + title = "Base Color" + type = "core/shader_nodes/material_variable" + } + ] + version = 2 + is_editable = false +} +textures = { +} +variables = { +} diff --git a/vmf_source/core/stingray_renderer/shader_import/no_uvs_transparent.material b/vmf_source/core/stingray_renderer/shader_import/no_uvs_transparent.material new file mode 100644 index 0000000..5fbd7e1 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_import/no_uvs_transparent.material @@ -0,0 +1,162 @@ + +shader = { + connections = [ + { + destination = { + connector_id = "aca690cb-6305-4a2f-bf3d-69183a493db3" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + } + } + { + destination = { + connector_id = "34259752-b962-4b65-92c3-903a57338519" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "e56f936e-9a21-49fe-88ff-e92cab5e4aaf" + } + } + ] + constants = [ + { + connector_id = "c4d6bc08-c489-430f-a836-ed490e59c3f9" + id = "964a0911-c691-405b-994d-5adf123e6f55" + instance_id = "7b49c63d-2d6b-4021-90f7-f0673a12d5e3" + value = [ + 1 + ] + } + ] + nodes = [ + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "7b49c63d-2d6b-4021-90f7-f0673a12d5e3" + options = [ + ] + position = [ + 700 + 1320 + ] + samplers = { + } + title = "Ao" + type = "core/shader_nodes/constant_scalar" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Base Color" + name = "base_color" + type = "float3" + ui = { + max = [ + 1 + 1 + 1 + ] + min = [ + 0 + 0 + 0 + ] + order = 0 + step = [ + 0.001 + 0.001 + 0.001 + ] + ui_type = "Color" + } + value = [ + 0.5 + 0.5 + 0.5 + ] + } + } + id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + options = [ + ] + position = [ + 1120 + 300 + ] + samplers = { + } + title = "Base Color" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + options = [ + "2b136447-676e-4943-997b-04a28ae68497" + "dd7fcf97-0627-48ab-b29a-95b5685bb123" + ] + position = [ + 1360 + 460 + ] + samplers = { + } + type = "core/stingray_renderer/output_nodes/standard_base" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Opacity" + name = "opacity" + type = "float" + ui = { + max = 1 + min = 0 + step = 0.001 + } + value = 1 + } + } + id = "e56f936e-9a21-49fe-88ff-e92cab5e4aaf" + options = [ + ] + position = [ + 1120 + 400 + ] + samplers = { + } + title = "Opacity" + type = "core/shader_nodes/material_variable" + } + ] + version = 2 + is_editable = false +} +textures = { +} +variables = { + opacity = { + type = "scalar" + value = 0 + } +} diff --git a/vmf_source/core/stingray_renderer/shader_import/standard.material b/vmf_source/core/stingray_renderer/shader_import/standard.material new file mode 100644 index 0000000..f9f17fd --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_import/standard.material @@ -0,0 +1,1206 @@ + +shader = { + connections = [ + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + source = { + instance_id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + source = { + instance_id = "8a1e8286-ea11-49dc-b868-14945b416e78" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + select = [ + "rgb" + ] + source = { + instance_id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "c8bba500-e32f-4e86-b7c0-fe078eae5afd" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + source = { + instance_id = "828c6388-c6db-480d-a487-06b0685873ac" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + source = { + instance_id = "012a4e03-18b8-4652-91b2-5e448b2bb5fc" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + source = { + instance_id = "653f4884-93f1-454a-81f0-5d7f772cf038" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + source = { + instance_id = "fe8923f6-cf3f-4c3b-9608-b080628a027a" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + select = [ + "r" + ] + source = { + instance_id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + source = { + instance_id = "59a274c2-48fd-4caa-b08c-953861c45d40" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + select = [ + "rgb" + ] + source = { + instance_id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + source = { + instance_id = "5220b0c6-61da-4e63-ad44-f17250b6b465" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "ae781371-9e47-43dc-af01-6c8d47266b23" + } + } + { + destination = { + connector_id = "aca690cb-6305-4a2f-bf3d-69183a493db3" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + } + { + destination = { + connector_id = "b1c86408-aacb-4466-b754-ddcf37a3a2c8" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + } + { + destination = { + connector_id = "f72597c4-7487-419a-affb-df690e6582e1" + instance_id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + } + select = [ + "rgb" + ] + source = { + instance_id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + } + } + { + destination = { + connector_id = "ad5e052f-d316-4a0f-8b79-53c38204d61b" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + } + { + destination = { + connector_id = "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + select = [ + "r" + ] + source = { + instance_id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "829e0be2-93fb-4831-a842-5b7ea3944c08" + } + source = { + instance_id = "80b7af4c-895c-477d-ac56-de91383152dc" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "b960341a-d562-4b3b-a6b2-e99e1e0ce6fb" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "829e0be2-93fb-4831-a842-5b7ea3944c08" + } + select = [ + "r" + ] + source = { + instance_id = "b960341a-d562-4b3b-a6b2-e99e1e0ce6fb" + } + } + { + destination = { + connector_id = "59fd1cf4-f736-470d-8510-1dd7c016639e" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "829e0be2-93fb-4831-a842-5b7ea3944c08" + } + } + { + destination = { + connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" + instance_id = "f3df3545-d772-4f19-99e6-88f082b7d462" + } + source = { + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + } + { + destination = { + connector_id = "242d1648-a626-445b-9534-bccec094112f" + instance_id = "f3df3545-d772-4f19-99e6-88f082b7d462" + } + source = { + instance_id = "50edcdf8-0242-4cd3-8cb6-c0876fceed03" + } + } + { + destination = { + connector_id = "1164a5ef-4563-4795-b3b5-42825d6df037" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "f3df3545-d772-4f19-99e6-88f082b7d462" + } + } + ] + constants = [ + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "a333d901-0d47-4f7c-b9b0-e40a721126d7" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "d99c7c25-cf17-43f9-8683-988701e0e728" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "55c2b970-4eab-441d-b726-2131b4286bfc" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "58a4eb97-c9aa-431b-acd6-808e40038471" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "2a051598-c422-4ccb-8b95-5f4a9adb713d" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "ef2429ad-ed0a-4fe5-b6f4-6da091dd31c8" + instance_id = "829e0be2-93fb-4831-a842-5b7ea3944c08" + value = [ + 1 + ] + } + { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + id = "96ea35d4-ba50-4c3c-a467-831d714e6661" + instance_id = "829e0be2-93fb-4831-a842-5b7ea3944c08" + value = [ + 1 + ] + } + ] + groups = [ + ] + nodes = [ + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 240 + ] + samplers = { + } + title = "Metallic Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 740 + 780 + ] + samplers = { + } + title = "Emissive Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "4480f32a-9f95-4bee-8f67-f5567221534d" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + -340 + ] + samplers = { + } + title = "Color Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + -60 + ] + samplers = { + } + title = "Normal Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 540 + ] + samplers = { + } + title = "Roughness Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + options = [ + "2b136447-676e-4943-997b-04a28ae68497" + "b2c7c0d2-beff-4b1a-a9d4-068a507625a2" + ] + position = [ + 1360 + 260 + ] + samplers = { + } + type = "core/stingray_renderer/output_nodes/standard_base" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "829e0be2-93fb-4831-a842-5b7ea3944c08" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 1100 + ] + samplers = { + } + title = "Ao Map Swtich" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + options = [ + ] + position = [ + 40 + 440 + ] + samplers = { + } + type = "core/shader_nodes/texture_coordinate0" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "f669a3a6-0376-4187-840e-80000e2939d5" + "e94e53e6-49b6-4194-a747-8f064a5932e0" + ] + position = [ + 700 + 540 + ] + samplers = { + texture_map = { + display_name = "Roughness Map" + slot_name = "roughness_map" + ui = { + order = 4 + } + } + } + title = "Roughness Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + "1e067464-12d8-4826-9b72-cfd5765003e3" + ] + position = [ + 420 + 780 + ] + samplers = { + texture_map = { + display_name = "Emissive Map" + slot_name = "emissive_map" + ui = { + order = 5 + } + } + } + title = "Emissive Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "f3df3545-d772-4f19-99e6-88f082b7d462" + options = [ + ] + position = [ + 1020 + 840 + ] + samplers = { + } + type = "core/shader_nodes/mul" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Color Map" + name = "use_color_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 10 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "8a1e8286-ea11-49dc-b868-14945b416e78" + options = [ + ] + position = [ + 700 + -400 + ] + samplers = { + } + title = "Use Color Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "1e067464-12d8-4826-9b72-cfd5765003e3" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + ] + position = [ + 700 + -340 + ] + samplers = { + texture_map = { + display_name = "Color Map" + slot_name = "color_map" + ui = { + order = 1 + } + } + } + title = "Color Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Base Color" + name = "base_color" + type = "float3" + ui = { + max = [ + 1 + 1 + 1 + ] + min = [ + 0 + 0 + 0 + ] + order = 20 + step = [ + 0.001 + 0.001 + 0.001 + ] + ui_type = "Color" + } + value = [ + 0.5 + 0.5 + 0.5 + ] + } + } + id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + options = [ + ] + position = [ + 700 + -240 + ] + samplers = { + } + title = "Base Color" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Normal Map" + name = "use_normal_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 11 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "c8bba500-e32f-4e86-b7c0-fe078eae5afd" + options = [ + ] + position = [ + 700 + -120 + ] + samplers = { + } + title = "Use Normal Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "ae781371-9e47-43dc-af01-6c8d47266b23" + options = [ + ] + position = [ + 680 + 60 + ] + samplers = { + } + type = "core/shader_nodes/world_space_normal" + } + { + content_size = [ + 140 + 0 + ] + export = { + } + id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + options = [ + ] + position = [ + 740 + -60 + ] + samplers = { + } + type = "core/shader_nodes/tangent_to_world" + } + { + content_size = [ + 180 + 0 + ] + export = { + } + id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "1e067464-12d8-4826-9b72-cfd5765003e3" + "90e20826-8689-42fa-8e24-f484ec64c5c3" + ] + position = [ + 440 + -60 + ] + samplers = { + texture_map = { + display_name = "Normal Map" + slot_name = "normal_map" + ui = { + order = 2 + } + } + } + title = "Normal Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + options = [ + "e94e53e6-49b6-4194-a747-8f064a5932e0" + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "f669a3a6-0376-4187-840e-80000e2939d5" + ] + position = [ + 700 + 240 + ] + samplers = { + texture_map = { + display_name = "Metallic Map" + slot_name = "metallic_map" + ui = { + order = 3 + } + } + } + title = "Metallic Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Metallic Map" + name = "use_metallic_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 12 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "653f4884-93f1-454a-81f0-5d7f772cf038" + options = [ + ] + position = [ + 700 + 180 + ] + samplers = { + } + title = "Use Metallic Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Metallic" + name = "metallic" + type = "float" + ui = { + max = 1 + min = 0 + order = 21 + step = 0.001 + ui_type = "Number" + } + value = 0 + } + } + id = "fe8923f6-cf3f-4c3b-9608-b080628a027a" + options = [ + ] + position = [ + 700 + 340 + ] + samplers = { + } + title = "Metallic" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Roughness Map" + name = "use_roughness_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 13 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "828c6388-c6db-480d-a487-06b0685873ac" + options = [ + ] + position = [ + 700 + 480 + ] + samplers = { + } + title = "Use Roughness Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Roughness" + name = "roughness" + type = "float" + ui = { + max = 1 + min = 0 + order = 22 + step = 0.001 + ui_type = "Number" + } + value = 0.33 + } + } + id = "012a4e03-18b8-4652-91b2-5e448b2bb5fc" + options = [ + ] + position = [ + 700 + 640 + ] + samplers = { + } + title = "Roughness" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Emissive" + name = "emissive" + type = "float3" + ui = { + max = [ + 1 + 1 + 1 + ] + min = [ + 0 + 0 + 0 + ] + order = 23 + step = [ + 0.001 + 0.001 + 0.001 + ] + ui_type = "Color" + } + value = [ + 0 + 0 + 0 + ] + } + } + id = "5220b0c6-61da-4e63-ad44-f17250b6b465" + options = [ + ] + position = [ + 420 + 880 + ] + samplers = { + } + title = "Emissive" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Emissive Map" + name = "use_emissive_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 14 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "59a274c2-48fd-4caa-b08c-953861c45d40" + options = [ + ] + position = [ + 420 + 720 + ] + samplers = { + } + title = "Use Emissive Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Ao Map" + name = "use_ao_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 15 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "80b7af4c-895c-477d-ac56-de91383152dc" + options = [ + ] + position = [ + 700 + 1100 + ] + samplers = { + } + title = "Use Ao Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "b960341a-d562-4b3b-a6b2-e99e1e0ce6fb" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "f669a3a6-0376-4187-840e-80000e2939d5" + "e94e53e6-49b6-4194-a747-8f064a5932e0" + ] + position = [ + 700 + 1160 + ] + samplers = { + texture_map = { + display_name = "Ao Map" + slot_name = "ao_map" + ui = { + order = 6 + } + } + } + title = "Ao Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Emissive Intensity" + name = "emissive_intensity" + type = "float" + ui = { + max = 10 + min = 0 + order = 53 + step = 0.001 + } + value = 1 + } + } + id = "50edcdf8-0242-4cd3-8cb6-c0876fceed03" + options = [ + ] + position = [ + 740 + 920 + ] + samplers = { + } + title = "Emissive Intensity" + type = "core/shader_nodes/material_variable" + } + ] + is_editable = false + version = 3 +} +textures = { + ao_map = null + color_map = null + emissive_map = null + metallic_map = null + normal_map = null + roughness_map = null +} +variables = { + base_color = { + type = "vector3" + value = [ + 0.5 + 0.5 + 0.5 + ] + } + emissive = { + type = "vector3" + value = [ + 0 + 0 + 0 + ] + } + emissive_intensity = { + type = "scalar" + value = 0 + } + metallic = { + type = "scalar" + value = 0 + } + roughness = { + type = "scalar" + value = 0.33 + } + use_ao_map = { + type = "scalar" + value = 0 + } + use_color_map = { + type = "scalar" + value = 0 + } + use_emissive_map = { + type = "scalar" + value = 0 + } + use_metallic_map = { + type = "scalar" + value = 0 + } + use_normal_map = { + type = "scalar" + value = 0 + } + use_roughness_map = { + type = "scalar" + value = 0 + } +} diff --git a/vmf_source/core/stingray_renderer/shader_import/standard.shader_import b/vmf_source/core/stingray_renderer/shader_import/standard.shader_import new file mode 100644 index 0000000..bf60757 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_import/standard.shader_import @@ -0,0 +1,14 @@ +textures = { + diffuse_map = "color_map" + normal_map = "normal_map" + metallic_map = "metallic_map" + roughness_map = "roughness_map" + emissive_map = "emissive_map" +} +variables = { + diffuse_color = "base_color" + metallic_value = "metallic" + roughness_value = "roughness" + self_illumination_color = "emissive" + emissive_intensity = "emissive_intensity" +} diff --git a/vmf_source/core/stingray_renderer/shader_import/standard_masked.material b/vmf_source/core/stingray_renderer/shader_import/standard_masked.material new file mode 100644 index 0000000..62d8c04 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_import/standard_masked.material @@ -0,0 +1,1253 @@ + +shader = { + connections = [ + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + source = { + instance_id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + source = { + instance_id = "8a1e8286-ea11-49dc-b868-14945b416e78" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + select = [ + "rgb" + ] + source = { + instance_id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "c8bba500-e32f-4e86-b7c0-fe078eae5afd" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + source = { + instance_id = "828c6388-c6db-480d-a487-06b0685873ac" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + source = { + instance_id = "012a4e03-18b8-4652-91b2-5e448b2bb5fc" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + source = { + instance_id = "653f4884-93f1-454a-81f0-5d7f772cf038" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + source = { + instance_id = "fe8923f6-cf3f-4c3b-9608-b080628a027a" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + select = [ + "r" + ] + source = { + instance_id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + source = { + instance_id = "59a274c2-48fd-4caa-b08c-953861c45d40" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + select = [ + "rgb" + ] + source = { + instance_id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + source = { + instance_id = "5220b0c6-61da-4e63-ad44-f17250b6b465" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "ae781371-9e47-43dc-af01-6c8d47266b23" + } + } + { + destination = { + connector_id = "aca690cb-6305-4a2f-bf3d-69183a493db3" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + } + { + destination = { + connector_id = "b1c86408-aacb-4466-b754-ddcf37a3a2c8" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + } + { + destination = { + connector_id = "f72597c4-7487-419a-affb-df690e6582e1" + instance_id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + } + select = [ + "rgb" + ] + source = { + instance_id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + } + } + { + destination = { + connector_id = "ad5e052f-d316-4a0f-8b79-53c38204d61b" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + } + { + destination = { + connector_id = "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + select = [ + "r" + ] + source = { + instance_id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + } + source = { + instance_id = "f3e1742a-e7b2-4527-bb20-f572a9f9bddc" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + } + select = [ + "a" + ] + source = { + instance_id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + } + } + { + destination = { + connector_id = "34259752-b962-4b65-92c3-903a57338519" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + } + } + { + destination = { + connector_id = "242d1648-a626-445b-9534-bccec094112f" + instance_id = "4141e43d-03af-4433-91b9-a26dabfb3c31" + } + source = { + instance_id = "4552769c-1788-45ef-bfbf-9213a224293d" + } + } + { + destination = { + connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" + instance_id = "4141e43d-03af-4433-91b9-a26dabfb3c31" + } + source = { + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + } + { + destination = { + connector_id = "1164a5ef-4563-4795-b3b5-42825d6df037" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "4141e43d-03af-4433-91b9-a26dabfb3c31" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "23db6f79-706f-4d58-a6f8-dd65c7316891" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + } + select = [ + "r" + ] + source = { + instance_id = "23db6f79-706f-4d58-a6f8-dd65c7316891" + } + } + { + destination = { + connector_id = "7a9306c6-95ae-4cdb-9fef-0eedacce4e83" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "f8a51e15-78d6-4e06-b481-acc179ea087e" + } + } + ] + constants = [ + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "a333d901-0d47-4f7c-b9b0-e40a721126d7" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "d99c7c25-cf17-43f9-8683-988701e0e728" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "55c2b970-4eab-441d-b726-2131b4286bfc" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "58a4eb97-c9aa-431b-acd6-808e40038471" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "2a051598-c422-4ccb-8b95-5f4a9adb713d" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "ce1eaf9a-bed9-49ac-850b-7d41a937aad4" + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + value = [ + 1 + ] + } + ] + groups = [ + ] + nodes = [ + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + options = [ + ] + position = [ + -40 + 180 + ] + samplers = { + } + type = "core/shader_nodes/texture_coordinate0" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 760 + ] + samplers = { + } + title = "Roughness Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 200 + ] + samplers = { + } + title = "Normal Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 480 + ] + samplers = { + } + title = "Metallic Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 800 + 1060 + ] + samplers = { + } + title = "Emissive Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "4480f32a-9f95-4bee-8f67-f5567221534d" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + -340 + ] + samplers = { + } + title = "Color Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "ae781371-9e47-43dc-af01-6c8d47266b23" + options = [ + ] + position = [ + 700 + 300 + ] + samplers = { + } + type = "core/shader_nodes/world_space_normal" + } + { + content_size = [ + 140 + 0 + ] + export = { + } + id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + options = [ + ] + position = [ + 740 + 180 + ] + samplers = { + } + type = "core/shader_nodes/tangent_to_world" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + options = [ + "2b136447-676e-4943-997b-04a28ae68497" + "b2c7c0d2-beff-4b1a-a9d4-068a507625a2" + ] + position = [ + 1360 + 260 + ] + samplers = { + } + type = "core/stingray_renderer/output_nodes/standard_base" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + -100 + ] + samplers = { + } + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "1e067464-12d8-4826-9b72-cfd5765003e3" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + ] + position = [ + 700 + -340 + ] + samplers = { + texture_map = { + display_name = "Color Map" + slot_name = "color_map" + ui = { + order = 2 + } + } + } + title = "Color Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 180 + 0 + ] + export = { + } + id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "1e067464-12d8-4826-9b72-cfd5765003e3" + "90e20826-8689-42fa-8e24-f484ec64c5c3" + ] + position = [ + 440 + 180 + ] + samplers = { + texture_map = { + display_name = "Normal Map" + slot_name = "normal_map" + ui = { + order = 3 + } + } + } + title = "Normal Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + options = [ + "e94e53e6-49b6-4194-a747-8f064a5932e0" + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "f669a3a6-0376-4187-840e-80000e2939d5" + ] + position = [ + 700 + 480 + ] + samplers = { + texture_map = { + display_name = "Metallic Map" + slot_name = "metallic_map" + ui = { + order = 4 + } + } + } + title = "Metallic Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "f669a3a6-0376-4187-840e-80000e2939d5" + "e94e53e6-49b6-4194-a747-8f064a5932e0" + ] + position = [ + 700 + 760 + ] + samplers = { + texture_map = { + display_name = "Roughness Map" + slot_name = "roughness_map" + ui = { + order = 5 + } + } + } + title = "Roughness Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + "1e067464-12d8-4826-9b72-cfd5765003e3" + ] + position = [ + 480 + 1060 + ] + samplers = { + texture_map = { + display_name = "Emissive Map" + slot_name = "emissive_map" + ui = { + order = 6 + } + } + } + title = "Emissive Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "4141e43d-03af-4433-91b9-a26dabfb3c31" + options = [ + ] + position = [ + 1020 + 1140 + ] + samplers = { + } + type = "core/shader_nodes/mul" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Color Map" + name = "use_color_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 11 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "8a1e8286-ea11-49dc-b868-14945b416e78" + options = [ + ] + position = [ + 700 + -400 + ] + samplers = { + } + title = "Use Color Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Base Color" + name = "base_color" + type = "float3" + ui = { + max = [ + 1 + 1 + 1 + ] + min = [ + 0 + 0 + 0 + ] + order = 21 + step = [ + 0.001 + 0.001 + 0.001 + ] + ui_type = "Color" + } + value = [ + 0.5 + 0.5 + 0.5 + ] + } + } + id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + options = [ + ] + position = [ + 700 + -240 + ] + samplers = { + } + title = "Base Color" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Color Map Alpha" + name = "use_opacity_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 12 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "f3e1742a-e7b2-4527-bb20-f572a9f9bddc" + options = [ + ] + position = [ + 700 + -100 + ] + samplers = { + } + title = "Use Color Map Alpha" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Normal Map" + name = "use_normal_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 13 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "c8bba500-e32f-4e86-b7c0-fe078eae5afd" + options = [ + ] + position = [ + 700 + 120 + ] + samplers = { + } + title = "Use Normal Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Metallic Map" + name = "use_metallic_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 14 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "653f4884-93f1-454a-81f0-5d7f772cf038" + options = [ + ] + position = [ + 700 + 420 + ] + samplers = { + } + title = "Use Metallic Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Metallic" + name = "metallic" + type = "float" + ui = { + max = 1 + min = 0 + order = 23 + step = 0.001 + ui_type = "Number" + } + value = 0 + } + } + id = "fe8923f6-cf3f-4c3b-9608-b080628a027a" + options = [ + ] + position = [ + 700 + 580 + ] + samplers = { + } + title = "Metallic" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Roughness Map" + name = "use_roughness_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 15 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "828c6388-c6db-480d-a487-06b0685873ac" + options = [ + ] + position = [ + 700 + 700 + ] + samplers = { + } + title = "Use Roughness Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Roughness" + name = "roughness" + type = "float" + ui = { + max = 1 + min = 0 + order = 24 + step = 0.001 + ui_type = "Number" + } + value = 0.33 + } + } + id = "012a4e03-18b8-4652-91b2-5e448b2bb5fc" + options = [ + ] + position = [ + 700 + 860 + ] + samplers = { + } + title = "Roughness" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Emissive Map" + name = "use_emissive_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 16 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "59a274c2-48fd-4caa-b08c-953861c45d40" + options = [ + ] + position = [ + 480 + 1000 + ] + samplers = { + } + title = "Use Emissive Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Emissive" + name = "emissive" + type = "float3" + ui = { + max = [ + 1 + 1 + 1 + ] + min = [ + 0 + 0 + 0 + ] + order = 25 + step = [ + 0.001 + 0.001 + 0.001 + ] + ui_type = "Color" + } + value = [ + 0 + 0 + 0 + ] + } + } + id = "5220b0c6-61da-4e63-ad44-f17250b6b465" + options = [ + ] + position = [ + 480 + 1160 + ] + samplers = { + } + title = "Emissive" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Emissive Intensity" + name = "emissive_intensity" + type = "float" + ui = { + max = 10 + min = 0 + order = 26 + step = 0.001 + } + value = 1 + } + } + id = "4552769c-1788-45ef-bfbf-9213a224293d" + options = [ + ] + position = [ + 800 + 1240 + ] + samplers = { + } + title = "Emissive Intensity" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "23db6f79-706f-4d58-a6f8-dd65c7316891" + options = [ + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "1e067464-12d8-4826-9b72-cfd5765003e3" + ] + position = [ + 700 + -20 + ] + samplers = { + texture_map = { + display_name = "Mask Map" + slot_name = "mask_map" + ui = { + order = 7 + } + } + } + title = "Mask Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Mask Threshold" + name = "mask_threshold" + type = "float" + ui = { + max = 1 + min = 0 + order = 30 + step = 0.001 + } + value = 0 + } + } + id = "f8a51e15-78d6-4e06-b481-acc179ea087e" + options = [ + ] + position = [ + 700 + 360 + ] + samplers = { + } + title = "Mask Threshold" + type = "core/shader_nodes/material_variable" + } + ] + is_editable = false + version = 3 +} +textures = { + color_map = null + emissive_map = null + mask_map = null + metallic_map = null + normal_map = null + roughness_map = null +} +variables = { + base_color = { + type = "vector3" + value = [ + 0.5 + 0.5 + 0.5 + ] + } + emissive = { + type = "vector3" + value = [ + 0 + 0 + 0 + ] + } + emissive_intensity = { + type = "scalar" + value = 0 + } + mask_threshold = { + type = "scalar" + value = 0.5 + } + metallic = { + type = "scalar" + value = 0 + } + roughness = { + type = "scalar" + value = 0.33 + } + use_color_map = { + type = "scalar" + value = 0 + } + use_color_map_alpha = { + type = "scalar" + value = 0 + } + use_emissive_map = { + type = "scalar" + value = 0 + } + use_metallic_map = { + type = "scalar" + value = 0 + } + use_normal_map = { + type = "scalar" + value = 0 + } + use_roughness_map = { + type = "scalar" + value = 0 + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_import/standard_transparent.material b/vmf_source/core/stingray_renderer/shader_import/standard_transparent.material new file mode 100644 index 0000000..8560deb --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_import/standard_transparent.material @@ -0,0 +1,1204 @@ + +shader = { + connections = [ + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + source = { + instance_id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + source = { + instance_id = "8a1e8286-ea11-49dc-b868-14945b416e78" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + select = [ + "rgb" + ] + source = { + instance_id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "c8bba500-e32f-4e86-b7c0-fe078eae5afd" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + source = { + instance_id = "828c6388-c6db-480d-a487-06b0685873ac" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + source = { + instance_id = "012a4e03-18b8-4652-91b2-5e448b2bb5fc" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + source = { + instance_id = "653f4884-93f1-454a-81f0-5d7f772cf038" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + source = { + instance_id = "fe8923f6-cf3f-4c3b-9608-b080628a027a" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + select = [ + "r" + ] + source = { + instance_id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + source = { + instance_id = "59a274c2-48fd-4caa-b08c-953861c45d40" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + select = [ + "rgb" + ] + source = { + instance_id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + } + } + { + destination = { + connector_id = "1ee9af1f-65f2-4739-ad28-5ea6a0e68fc3" + instance_id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + } + source = { + instance_id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + source = { + instance_id = "5220b0c6-61da-4e63-ad44-f17250b6b465" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "ae781371-9e47-43dc-af01-6c8d47266b23" + } + } + { + destination = { + connector_id = "aca690cb-6305-4a2f-bf3d-69183a493db3" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + } + } + { + destination = { + connector_id = "b1c86408-aacb-4466-b754-ddcf37a3a2c8" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + } + { + destination = { + connector_id = "f72597c4-7487-419a-affb-df690e6582e1" + instance_id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + } + select = [ + "rgb" + ] + source = { + instance_id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + } + source = { + instance_id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + } + } + { + destination = { + connector_id = "ad5e052f-d316-4a0f-8b79-53c38204d61b" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + } + } + { + destination = { + connector_id = "36ba46d2-f6ea-4e60-a428-fdc17c75bc62" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + } + select = [ + "r" + ] + source = { + instance_id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + } + } + { + destination = { + connector_id = "ced7bbf3-0b48-4335-b933-095a41ca0294" + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + } + source = { + instance_id = "f3e1742a-e7b2-4527-bb20-f572a9f9bddc" + } + } + { + destination = { + connector_id = "f2f74e58-402d-472b-87dd-331e00db416c" + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + } + source = { + instance_id = "0bfbd0e9-5490-4bed-8abd-d94aaf7152d6" + } + } + { + destination = { + connector_id = "4cbb4480-79e8-4ce7-ac0f-8b09baf12390" + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + } + select = [ + "a" + ] + source = { + instance_id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + } + } + { + destination = { + connector_id = "34259752-b962-4b65-92c3-903a57338519" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + } + } + { + destination = { + connector_id = "242d1648-a626-445b-9534-bccec094112f" + instance_id = "4141e43d-03af-4433-91b9-a26dabfb3c31" + } + source = { + instance_id = "4552769c-1788-45ef-bfbf-9213a224293d" + } + } + { + destination = { + connector_id = "c5823c75-4ae5-4c71-b070-315fa4d03e8e" + instance_id = "4141e43d-03af-4433-91b9-a26dabfb3c31" + } + source = { + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + } + } + { + destination = { + connector_id = "1164a5ef-4563-4795-b3b5-42825d6df037" + instance_id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + } + source = { + instance_id = "4141e43d-03af-4433-91b9-a26dabfb3c31" + } + } + ] + constants = [ + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "a333d901-0d47-4f7c-b9b0-e40a721126d7" + instance_id = "4480f32a-9f95-4bee-8f67-f5567221534d" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "d99c7c25-cf17-43f9-8683-988701e0e728" + instance_id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "55c2b970-4eab-441d-b726-2131b4286bfc" + instance_id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "58a4eb97-c9aa-431b-acd6-808e40038471" + instance_id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "2a051598-c422-4ccb-8b95-5f4a9adb713d" + instance_id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + value = [ + 1 + ] + } + { + connector_id = "39bc7619-2768-480b-acfd-63fa66ef6905" + id = "ce1eaf9a-bed9-49ac-850b-7d41a937aad4" + instance_id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + value = [ + 1 + ] + } + ] + groups = [ + ] + nodes = [ + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "a464c282-44a3-40ee-a808-8d9541b6ad70" + options = [ + ] + position = [ + -40 + 180 + ] + samplers = { + } + type = "core/shader_nodes/texture_coordinate0" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "af89d1a9-402d-49e0-a0a6-c1100c158f91" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 760 + ] + samplers = { + } + title = "Roughness Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "17d9eafb-0d2b-4efd-9130-bc44418d16f8" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 180 + ] + samplers = { + } + title = "Normal Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "3f9d5218-b649-4a59-bdb0-0ac51415ce46" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + 480 + ] + samplers = { + } + title = "Metallic Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "1c9a0cc2-86b3-471d-933d-ccc4fa94baf3" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 800 + 1060 + ] + samplers = { + } + title = "Emissive Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "4480f32a-9f95-4bee-8f67-f5567221534d" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + -340 + ] + samplers = { + } + title = "Color Map Switch" + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "ae781371-9e47-43dc-af01-6c8d47266b23" + options = [ + ] + position = [ + 680 + 300 + ] + samplers = { + } + type = "core/shader_nodes/world_space_normal" + } + { + content_size = [ + 140 + 0 + ] + export = { + } + id = "0105014e-0250-4fd5-8fca-da57df4bb2de" + options = [ + ] + position = [ + 740 + 180 + ] + samplers = { + } + type = "core/shader_nodes/tangent_to_world" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "f8bf5438-b06f-4d56-ae0c-e4abd31c1dc1" + options = [ + "2b136447-676e-4943-997b-04a28ae68497" + "b2c7c0d2-beff-4b1a-a9d4-068a507625a2" + "dd7fcf97-0627-48ab-b29a-95b5685bb123" + ] + position = [ + 1360 + 260 + ] + samplers = { + } + type = "core/stingray_renderer/output_nodes/standard_base" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "538bf17a-f927-4ecf-a284-3a5ede1eb697" + options = [ + "9a84282b-f1a2-46d4-9fc4-5a76fc9b30dd" + ] + position = [ + 1020 + -100 + ] + samplers = { + } + type = "core/shader_nodes/if" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "20461e9a-8cc0-4af3-a588-22191cc9cfcd" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "1e067464-12d8-4826-9b72-cfd5765003e3" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + ] + position = [ + 700 + -340 + ] + samplers = { + texture_map = { + display_name = "Color Map" + slot_name = "color_map" + ui = { + order = 2 + } + } + } + title = "Color Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 180 + 0 + ] + export = { + } + id = "0231d6f6-9bda-4f34-b016-9b7c7c371c1c" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "1e067464-12d8-4826-9b72-cfd5765003e3" + "90e20826-8689-42fa-8e24-f484ec64c5c3" + ] + position = [ + 440 + 180 + ] + samplers = { + texture_map = { + display_name = "Normal Map" + slot_name = "normal_map" + ui = { + order = 3 + } + } + } + title = "Normal Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "3e7dc014-b974-42e7-a316-4e5696ae6c8c" + options = [ + "e94e53e6-49b6-4194-a747-8f064a5932e0" + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "f669a3a6-0376-4187-840e-80000e2939d5" + ] + position = [ + 700 + 480 + ] + samplers = { + texture_map = { + display_name = "Metallic Map" + slot_name = "metallic_map" + ui = { + order = 4 + } + } + } + title = "Metallic Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "d65549bb-2c6b-4b39-86d4-210200c5529c" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "f669a3a6-0376-4187-840e-80000e2939d5" + "e94e53e6-49b6-4194-a747-8f064a5932e0" + ] + position = [ + 700 + 760 + ] + samplers = { + texture_map = { + display_name = "Roughness Map" + slot_name = "roughness_map" + ui = { + order = 5 + } + } + } + title = "Roughness Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "06e20b35-09bf-4f22-84dd-db8f9e5588da" + options = [ + "5dd59b3d-1762-4a14-9930-7500230ef3db" + "fb3f709b-a54a-4e93-ac9f-e9fc76fb8bcd" + "1e067464-12d8-4826-9b72-cfd5765003e3" + ] + position = [ + 480 + 1060 + ] + samplers = { + texture_map = { + display_name = "Emissive Map" + slot_name = "emissive_map" + ui = { + order = 6 + } + } + } + title = "Emissive Map" + type = "core/shader_nodes/sample_texture" + } + { + content_size = [ + 160 + 0 + ] + export = { + } + id = "4141e43d-03af-4433-91b9-a26dabfb3c31" + options = [ + ] + position = [ + 1020 + 1140 + ] + samplers = { + } + type = "core/shader_nodes/mul" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Color Map" + name = "use_color_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 11 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "8a1e8286-ea11-49dc-b868-14945b416e78" + options = [ + ] + position = [ + 700 + -400 + ] + samplers = { + } + title = "Use Color Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Base Color" + name = "base_color" + type = "float3" + ui = { + max = [ + 1 + 1 + 1 + ] + min = [ + 0 + 0 + 0 + ] + order = 21 + step = [ + 0.001 + 0.001 + 0.001 + ] + ui_type = "Color" + } + value = [ + 0.5 + 0.5 + 0.5 + ] + } + } + id = "525f7d4e-f6e6-4867-9bae-d2ebcdff4b9c" + options = [ + ] + position = [ + 700 + -240 + ] + samplers = { + } + title = "Base Color" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Color Map Alpha" + name = "use_opacity_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 12 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "f3e1742a-e7b2-4527-bb20-f572a9f9bddc" + options = [ + ] + position = [ + 700 + -100 + ] + samplers = { + } + title = "Use Opacity Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Opacity" + name = "opacity" + type = "float" + ui = { + max = 1 + min = 0 + order = 22 + step = 0.001 + ui_type = "Number" + } + value = 1 + } + } + id = "0bfbd0e9-5490-4bed-8abd-d94aaf7152d6" + options = [ + ] + position = [ + 700 + 20 + ] + samplers = { + } + title = "Opacity" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Normal Map" + name = "use_normal_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 13 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "c8bba500-e32f-4e86-b7c0-fe078eae5afd" + options = [ + ] + position = [ + 700 + 120 + ] + samplers = { + } + title = "Use Normal Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Metallic Map" + name = "use_metallic_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 14 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "653f4884-93f1-454a-81f0-5d7f772cf038" + options = [ + ] + position = [ + 700 + 420 + ] + samplers = { + } + title = "Use Metallic Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Metallic" + name = "metallic" + type = "float" + ui = { + max = 1 + min = 0 + order = 23 + step = 0.001 + ui_type = "Number" + } + value = 0 + } + } + id = "fe8923f6-cf3f-4c3b-9608-b080628a027a" + options = [ + ] + position = [ + 700 + 580 + ] + samplers = { + } + title = "Metallic" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Roughness Map" + name = "use_roughness_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 15 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "828c6388-c6db-480d-a487-06b0685873ac" + options = [ + ] + position = [ + 700 + 700 + ] + samplers = { + } + title = "Use Roughness Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Roughness" + name = "roughness" + type = "float" + ui = { + max = 1 + min = 0 + order = 24 + step = 0.001 + ui_type = "Number" + } + value = 0.33 + } + } + id = "012a4e03-18b8-4652-91b2-5e448b2bb5fc" + options = [ + ] + position = [ + 700 + 860 + ] + samplers = { + } + title = "Roughness" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Use Emissive Map" + name = "use_emissive_map" + type = "float" + ui = { + max = 1 + min = 0 + order = 16 + step = 1 + ui_type = "Checkbox" + } + value = 0 + } + } + id = "59a274c2-48fd-4caa-b08c-953861c45d40" + options = [ + ] + position = [ + 480 + 1000 + ] + samplers = { + } + title = "Use Emissive Map" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Emissive" + name = "emissive" + type = "float3" + ui = { + max = [ + 1 + 1 + 1 + ] + min = [ + 0 + 0 + 0 + ] + order = 25 + step = [ + 0.001 + 0.001 + 0.001 + ] + ui_type = "Color" + } + value = [ + 0 + 0 + 0 + ] + } + } + id = "5220b0c6-61da-4e63-ad44-f17250b6b465" + options = [ + ] + position = [ + 480 + 1160 + ] + samplers = { + } + title = "Emissive" + type = "core/shader_nodes/material_variable" + } + { + content_size = [ + 160 + 0 + ] + export = { + material_variable = { + display_name = "Emissive Intensity" + name = "emissive_intensity" + type = "float" + ui = { + max = 10 + min = 0 + order = 26 + step = 0.001 + } + value = 1 + } + } + id = "4552769c-1788-45ef-bfbf-9213a224293d" + options = [ + ] + position = [ + 800 + 1240 + ] + samplers = { + } + title = "Emissive Intensity" + type = "core/shader_nodes/material_variable" + } + ] + is_edtiable = false + version = 3 +} +textures = { + color_map = null + emissive_map = null + metallic_map = null + normal_map = null + roughness_map = null +} +variables = { + base_color = { + type = "vector3" + value = [ + 0.5 + 0.5 + 0.5 + ] + } + emissive = { + type = "vector3" + value = [ + 0 + 0 + 0 + ] + } + emissive_intensity = { + type = "scalar" + value = 0 + } + metallic = { + type = "scalar" + value = 0 + } + opacity = { + type = "scalar" + value = 1 + } + roughness = { + type = "scalar" + value = 0.33 + } + use_color_map = { + type = "scalar" + value = 0 + } + use_emissive_map = { + type = "scalar" + value = 0 + } + use_metallic_map = { + type = "scalar" + value = 0 + } + use_normal_map = { + type = "scalar" + value = 0 + } + use_opacity_map = { + type = "scalar" + value = 0 + } + use_roughness_map = { + type = "scalar" + value = 0 + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/common.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/common.shader_source new file mode 100644 index 0000000..ffc83b7 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/common.shader_source @@ -0,0 +1,2016 @@ + +render_states = { + // Stencil mask bit allocation: + // |3221 1000| + // 3 = Outline (1 = outline, 0 = no outline) + // 2 = Decal projection mask (00 = not receiving, 01 = decal_group_1, 10 = decal_group_2, 11 = decal_group3) + // 1 = Material mask (00 = default, 01 = SKIN) + // 0 = Temporary / Scratch pad (used for Cascaded Shadow Map slice cut-out) + + + default = { + states = { + defined_DOUBLE_SIDED = { + cull_mode = "cull_none" + } + ndefined_DOUBLE_SIDED = { + cull_mode = "cull_cw" + } + + z_enable = "true" + z_write_enable = "true" + z_func = "less_equal" + blend_enable = "false" + write_mask0 = "red|green|blue|alpha" + write_mask1 = "red|green|blue|alpha" + write_mask2 = "red|green|blue|alpha" + write_mask3 = "red|green|blue|alpha" + + stencil_enable = "false" + stencil_func = "always" + stencil_fail = "stencil_op_keep" + stencil_mask = "0xff" + stencil_pass = "stencil_op_keep" + stencil_ref = "0x1" + stencil_write_mask = "0xff" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "always" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + defined_RENDERER_D3D11 = { + depth_bias = "0" + slope_scale_depth_bias = "0.0" + + independent_blend_enable = "false" + sample_mask = "0xffffffff" + nv_dbt_enable = "false" + multisample_antialias = "true" + + write_mask4 = "red|green|blue|alpha" + write_mask5 = "red|green|blue|alpha" + write_mask6 = "red|green|blue|alpha" + write_mask7 = "red|green|blue|alpha" + + defined_ALPHA_TO_COVERAGE = { + alpha_to_coverage_enable = "true" + } + ndefined_ALPHA_TO_COVERAGE = { + alpha_to_coverage_enable = "false" + } + srgb0 = "false" + } + defined_RENDERER_D3D12 = { + depth_bias = "0" + slope_scale_depth_bias = "0.0" + + independent_blend_enable = "false" + sample_mask = "0xffffffff" + nv_dbt_enable = "false" + multisample_antialias = "true" + + write_mask4 = "red|green|blue|alpha" + write_mask5 = "red|green|blue|alpha" + write_mask6 = "red|green|blue|alpha" + write_mask7 = "red|green|blue|alpha" + + defined_ALPHA_TO_COVERAGE = { + alpha_to_coverage_enable = "true" + } + ndefined_ALPHA_TO_COVERAGE = { + alpha_to_coverage_enable = "false" + } + srgb0 = "false" + } + + defined_RENDERER_GL = { + offset_factor = "0" + offset_units = "0" + depth_bias_enable = "false" + srgb = "false" + depth_bounds_test_enable = "false" + depth_bounds_near = "0.0" + depth_bounds_far = "0.0" + } + + defined_RENDERER_GNM = { + offset_clamp = "0" + offset_scale_front = "0" + offset_front = "0" + } + } + } + + + ambient = { + inherits = "default" + } + + gbuffer_ambient = { + inherits = "default" + } + + gbuffer_material = { + inherits = "default" + + states = { + // TODO: this does not work of some reason. + //defined_RENDERER_D3D11 = { + //defined_D3D11 = { // We need to enable this for the ps4 + stencil_enable = "true" + stencil_mask = "0x78" + stencil_write_mask = "0x78" + + stencil_func = "always" + stencil_pass = "stencil_op_replace" + stencil_fail = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "always" + stencil_pass_back_side = "stencil_op_replace" + stencil_fail_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + // Observation: If we had a way to do sequenctial bit manipulation of state values this would be soo much cleaner, easier and more powerful! + defined_DEFERRED_DECALS_GROUP_1 = { + defined_SKIN = { + stencil_ref = "0x28" + } + ndefined_SKIN = { + stencil_ref = "0x20" + } + } + ndefined_DEFERRED_DECALS_GROUP_1 = { + defined_DEFERRED_DECALS_GROUP_2 = { + defined_SKIN = { + stencil_ref = "0x48" + } + ndefined_SKIN = { + stencil_ref = "0x40" + } + } + ndefined_DEFERRED_DECALS_GROUP_2 = { + defined_DEFERRED_DECALS_GROUP_3 = { + defined_SKIN = { + stencil_ref = "0x68" + } + ndefined_SKIN = { + stencil_ref = "0x60" + } + } + ndefined_DEFERRED_DECALS_GROUP_3 = { + defined_SKIN = { + stencil_ref = "0x8" + } + ndefined_SKIN = { + stencil_ref = "0x0" + } + } + } + } + //} + } + } + + depth_only = { + inherits = "default" + states = { + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + } + } + + shadow_caster = { + inherits = "depth_only" + states = { + defined_RENDERER_D3D11 = { + depth_bias = "0xff" + slope_scale_depth_bias = "2.5" + } + defined_RENDERER_D3D12 = { + depth_bias = "0xff" + slope_scale_depth_bias = "2.5" + } + defined_RENDERER_GL = { + offset_factor = "1.1" + offset_units = "4.0" + depth_bias_enable = "true" + } + defined_RENDERER_GNM = { + offset_clamp = "0.01" + offset_scale_front = "16.0" + offset_front = "0.0001" + offset_z_format = "z_format_32" + } + } + } + + opacity = { + inherits = "default" + states = { + z_write_enable = "false" + cull_mode = "cull_none" + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_src_alpha" + } + } + + opacity_add = { + inherits = "opacity" + states = { + dest_blend = "blend_one" + src_blend = "blend_src_alpha" + } + } + + opacity_mul = { + inherits = "opacity" + states = { + dest_blend = "blend_one" + src_blend = "blend_dest_color" + } + } +} + +sampler_states = { + wrap_anisotropic = { + states = { + address_u = "address_wrap" + address_v = "address_wrap" + ndefined_RENDERER_GL = { + filter = "anisotropic" + max_anisotropy = "4" + } + defined_RENDERER_GL = { + max_anisotropy = "2.0" + filter = "min_mag_linear_mip_point" + } + + srgb = "false" + } + } + + wrap_linear = { + states = { + address_u = "address_wrap" + address_v = "address_wrap" + defined_RENDERER_GL = { + filter = "min_mag_linear_mip_point" + } + ndefined_RENDERER_GL = { + filter = "min_mag_mip_linear" + } + srgb = "false" + } + } + + wrap_point = { + states = { + address_u = "address_wrap" + address_v = "address_wrap" + filter = "min_mag_mip_point" + srgb = "false" + } + } + + wrap_anisotropic_srgb = { + inherits = "wrap_anisotropic" + states = { + srgb = "true" + } + } + + wrap_linear_srgb = { + inherits = "wrap_linear" + states = { + srgb = "true" + } + } + + wrap_point_srgb = { + inherits="wrap_point" + states = { + srgb = "true" + } + } + + clamp = { + states = { + srgb = "false" + defined_RENDERER_GL = { + address_u = "address_clamp_to_edge" + address_v = "address_clamp_to_edge" + address_w = "address_clamp_to_edge" + } + defined_RENDERER_D3D11 = { + address_u = "address_clamp" + address_v = "address_clamp" + address_w = "address_clamp" + } + defined_RENDERER_D3D12 = { + address_u = "address_clamp" + address_v = "address_clamp" + address_w = "address_clamp" + } + defined_RENDERER_GNM = { + address_u = "address_clamp" + address_v = "address_clamp" + address_w = "address_clamp" + } + } + } + + clamp_point = { + inherits="clamp" + states = { + filter = "min_mag_mip_point" + } + } + + clamp_linear = { + inherits="clamp" + states = { + defined_RENDERER_GL = { + filter = "min_mag_linear_mip_point" + } + ndefined_RENDERER_GL = { + filter = "min_mag_mip_linear" + } + } + } + + clamp_point_srgb = { + inherits = "clamp_point" + states = { + srgb = "true" + } + } + + clamp_linear_srgb = { + inherits = "clamp_linear" + states = { + srgb = "true" + } + } + + clamp_anisotropic_srgb = { + inherits = "clamp" + states = { + srgb = "true" + ndefined_RENDERER_GL = { + filter = "anisotropic" + max_anisotropy = "4" + } + defined_RENDERER_GL = { + max_anisotropy = "2.0" + filter = "min_mag_linear_mip_point" + } + } + } + + clamp_anisotropic = { + inherits = "clamp_anisotropic_srgb" + states = { + srgb = "false" + } + } +} + +hlsl_shaders = { + common = { + // Compatibility stuff for old library compiler + vp_code = { ref = "code" } + ep_code = { ref = "code" } + cp_code = { ref = "code" } + gp_code = { ref = "code" } + fp_code = { ref = "code" } + + code = """ + #if defined(RENDERER_GL) + #define new_fixed(x) float((x)) + #define new_fixed2(x, y) vec2((x), (y)) + + #define new_fixed3_xy(xy, z) vec3((xy), (z)) + #define new_fixed3(x, y, z) vec3((x), (y), (z)) + + #define new_fixed4_xyz(xyz, w) vec4((xyz), (w)) + #define new_fixed4_xy(xy, z, w) vec4((xy), (z), (w)) + #define new_fixed4(x, y, z, w) vec4((x), (y), (z), (w)) + + #define new_half(x) float((x)) + #define new_half2(x, y) vec2((x), (y)) + + #define new_half3_xy(xy, z) vec3((xy), (z)) + #define new_half3(x, y, z) vec3((x), (y), (z)) + #define new_half3_xyz(xyz) vec3((xyz)) + + #define new_half4_xyz(xyz, w) vec4((xyz), (w)) + #define new_half4_xy(xy, z, w) vec4((xy), (z), (w)) + #define new_half4(x, y, z, w) vec4((x), (y), (z), (w)) + + #define new_float(x) float((x)) + #define new_float2(x, y) vec2((x), (y)) + + #define new_float3_xy(xy, z) vec3((xy), (z)) + #define new_float3(x, y, z) vec3((x), (y), (z)) + + #define new_float4_xyz(xyz, w) vec4((xyz), (w)) + #define new_float4_xy(xy, z, w) vec4((xy), (z), (w)) + #define new_float4(x, y, z, w) vec4((x), (y), (z), (w)) + #else + #define new_fixed half + #define new_fixed2 fixed2 + #define new_fixed3 fixed3 + #define new_fixed3_xy fixed3 + #define new_fixed4 fixed4 + #define new_fixed4_xy fixed4 + #define new_fixed4_xyz fixed4 + + #define new_half half + #define new_half2 half2 + #define new_half3 float3 + #define new_half3_xy float3 + #define new_half3_xyz float3 + #define new_half4 half4 + #define new_half4_xy half4 + #define new_half4_xyz half4 + + #define new_float float + #define new_float2 float2 + #define new_float3 float3 + #define new_float3_xy float3 + #define new_float4 float4 + #define new_float4_xy float4 + #define new_float4_xyz float4 + + #define equal(a,b) (a)==(b) + #define lessThan(a,b) (a)<(b) + #define lessThanEqual(a,b) (a)<=(b) + #define greaterThan(a,b) (a)>(b) + #define greaterThanEqual(a,b) (a)>=(b) + #define notEqual(a,b) (a)!=(b) + #endif + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) + #define CBUFFER_START(name) cbuffer name { + #define CBUFFER_END }; + #define DX10_STYLE_SAMPLERS + #define NO_INTERPOLATION nointerpolation + + #elif defined(RENDERER_GNM) + #pragma warning(disable: 5609) // On ps4, halfs are treated as floats. Let's disable the warning. + #pragma warning(disable: 5203) // Todo: Remove this warning when GAME-6418 is closed. + + #pragma warning(disable: 5202) + #pragma warning(disable: 5206) + #pragma warning(disable: 6002) + + #define CBUFFER_START(name) ConstantBuffer name { + #define CBUFFER_END }; + #define SV_TARGET0 S_TARGET_OUTPUT0 + #define SV_TARGET1 S_TARGET_OUTPUT1 + #define SV_TARGET2 S_TARGET_OUTPUT2 + #define SV_TARGET3 S_TARGET_OUTPUT3 + #define SV_POSITION S_POSITION + #define SV_DEPTH S_DEPTH_OUTPUT + + #define Buffer DataBuffer + #define SV_InstanceId S_INSTANCE_ID + + #define reversebits ReverseBits + #define DX10_STYLE_SAMPLERS + + #define SV_TessFactor S_EDGE_TESS_FACTOR + #define SV_InsideTessFactor S_INSIDE_TESS_FACTOR + #define SV_OutputControlPointID S_OUTPUT_CONTROL_POINT_ID + #define SV_DomainLocation S_DOMAIN_LOCATION + #define domain DOMAIN_PATCH_TYPE + #define partitioning PARTITIONING_TYPE + #define outputtopology OUTPUT_TOPOLOGY_TYPE + #define outputcontrolpoints OUTPUT_CONTROL_POINTS + #define patchconstantfunc PATCH_CONSTANT_FUNC + #define maxtessfactor MAX_TESS_FACTOR + #if defined(STAGE_VERTEX) + #define WORLDPOS S_POSITION + #else + #define WORLDPOS POSITION + #endif + #define SV_PrimitiveID S_PRIMITIVE_ID + #define NO_INTERPOLATION nointerp + + #elif defined(RENDERER_GL) + // Default to highp to imitate hlsl closer + precision highp float; + precision highp int; + precision highp usampler2D; + + #define PRECISION precision + #define LOWP lowp + #define MEDIUMP mediump + #define HIGHP highp + + #define inline + #define static + #define half mediump float + #define half2 mediump vec2 + #define float3 mediump vec3 + #define half4 mediump vec4 + + #define fixed lowp float + #define fixed2 lowp vec2 + #define fixed3 lowp vec3 + #define fixed4 lowp vec4 + + #define float2 vec2 + #define float3 vec3 + #define float4 vec4 + + #define int2 ivec2 + #define int3 ivec3 + #define int4 ivec4 + + #define uint2 uvec2 + #define uint3 uvec3 + #define uint4 uvec4 + + #define float2x2 mat2 + #define float3x3 mat3 + #define float4x4 mat4 + + #define mul(a, b) ((a) * (b)) + #define frac(a) fract(a) + #define lerp(a, b, c) mix(a, b, c) + #define modf mod + + // fmod() != mod() for negative numbers + // % works on float for HLSL, but GLSL requires integral + #define fmod(a, b) (a - b * trunc(a/b)) + + #define ddx dFdx + #define ddy dFdy + #define rsqrt inversesqrt + + float saturate(float v) { return clamp(v, 0.f, 1.f); } + vec2 saturate(vec2 v) { return clamp(v, vec2(0.f), vec2(1.f)); } + vec3 saturate(vec3 v) { return clamp(v, vec3(0.f), vec3(1.f)); } + vec4 saturate(vec4 v) { return clamp(v, vec4(0.f), vec4(1.f)); } + + #define CBUFFER_START(name)// uniform name { + #define CBUFFER_END// }; + + // Attribute mappings taken from the GL backend. + // {0, 15, -1, -1, -1, -1, -1, -1, -1, -1}, // POSITION + // {1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // NORMAL + // {2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // TANGENT + // {3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // BINORMAL + // {8, 9, 10, 11, 12, 13, 6, 7, 14, 15}, // TEXCOORD + // {4, 5, 6, 7, -1, -1, -1, -1, -1, -1}, // COLOR + // {15, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // BLENDINDICES + // {14, -1, -1, -1, -1, -1, -1, -1, -1, -1}, // BLENDWEIGHTS + + #define POSITION0 0 + #define POSITION1 15 + + #define NORMAL 1 + #define TANGENT 2 + #define BINORMAL 3 + + #define TEXCOORD0 8 + #define TEXCOORD1 9 + #define TEXCOORD2 10 + #define TEXCOORD3 11 + #define TEXCOORD4 12 + #define TEXCOORD5 13 + #define TEXCOORD6 6 + #define TEXCOORD7 7 + #define TEXCOORD8 14 + #define TEXCOORD9 15 + + #define COLOR0 4 + #define COLOR1 5 + #define COLOR2 6 + #define COLOR3 7 + + #define BLENDINDICES 15 + #define BLENDWEIGHTS 14 + #endif + + #if defined(RENDERER_D3D12) + + #define RS1 "RootFlags (ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT), " \ + "CBV(b0, visibility = SHADER_VISIBILITY_VERTEX), " \ + "CBV(b1, visibility = SHADER_VISIBILITY_VERTEX), " \ + "CBV(b0, visibility = SHADER_VISIBILITY_PIXEL), " \ + "CBV(b1, visibility = SHADER_VISIBILITY_PIXEL), " \ + "DescriptorTable( CBV(b2, numDescriptors = 6), visibility = SHADER_VISIBILITY_VERTEX), " \ + "DescriptorTable( CBV(b2, numDescriptors = 6), visibility = SHADER_VISIBILITY_PIXEL), " \ + "DescriptorTable( SRV(t0, numDescriptors = 24), visibility = SHADER_VISIBILITY_VERTEX), " \ + "DescriptorTable( SRV(t0, numDescriptors = 24), visibility = SHADER_VISIBILITY_PIXEL), " \ + "DescriptorTable( Sampler(s0, numDescriptors = 24), visibility = SHADER_VISIBILITY_VERTEX), " \ + "DescriptorTable( Sampler(s0, numDescriptors = 24), visibility = SHADER_VISIBILITY_PIXEL) " + + + #define ROOT_SIGNATURE_ATTRIBUTE(root_signature) [RootSignature(root_signature)] + #define DEFAULT_ROOT_SIGNATURE_ATTRIBUTE ROOT_SIGNATURE_ATTRIBUTE(RS1) + #else + #define ROOT_SIGNATURE_ATTRIBUTE(root_signature) + #define DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + #endif + + + #if defined(RENDERER_GL) + #define DECLARE_SAMPLER_2D(name) uniform sampler2D name + #define Sampler2D sampler2D + #define TEX2D texture + #define TEX2DLOD textureLod + + #define DECLARE_SAMPLER_CUBE(name) uniform samplerCube name + #define SamplerCube samplerCube + #define TEXCUBE texture + #define TEXCUBELOD textureLod + + #define DECLARE_SAMPLER_3D(name) uniform sampler3D name + #define Sampler3D sampler3D + #define TEX3D texture + #define TEX3DLOD textureLod + + #define DECLARE_COMPARISON_SAMPLER_2D(name) uniform highp sampler2DShadow name + #define ComparisonSampler2D highp sampler2DShadow + #define TEX2DCMP texture + #define TEX2DCMPLOD0 textureLod + + #define DECLARE_COMPARISON_SAMPLER_CUBE(name) uniform highp samplerCubeShadow name + #define ComparisonSamplerCube highp samplerCubeShadow + #define TEXCUBECMP texture + #define TEXCUBECMPLOD0 textureLod + #elif defined(DX10_STYLE_SAMPLERS) + #define CONCAT_(prefix, name) prefix ## name + #define CONCAT(prefix, name) CONCAT_(prefix, name) + #define TEXTURE_NAME(name) CONCAT(TEXTURE_PREFIX, name) + #define SAMPLER_NAME(name) CONCAT(SAMPLER_PREFIX, name) + + struct Sampler2D { Texture2D tex; SamplerState state; }; + + #define TEX2D(s, uv) s.tex.Sample(s.state, uv) + #if defined(GNM) + #define TEX2DLOD(s, uv, lod) s.tex.SampleLOD(s.state, uv, lod) + #else + #define TEX2DLOD(s, uv, lod) s.tex.SampleLevel(s.state, uv, lod) + #endif + + #define DECLARE_SAMPLER_2D(name) \ + Texture2D TEXTURE_NAME(name); \ + SamplerState SAMPLER_NAME(name); \ + static Sampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(name) }; + + #define DECLARE_SAMPLER_2D_SHARED(name, share_from) \ + Texture2D TEXTURE_NAME(name); \ + static Sampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(share_from) }; + + + + struct SamplerCube { TextureCube tex; SamplerState state; }; + + #define TEXCUBE(s, uv) s.tex.Sample(s.state, uv) + #if defined(GNM) + #define TEXCUBELOD(s, uv, lod) s.tex.SampleLOD(s.state, uv, lod) + #else + #define TEXCUBELOD(s, uv, lod) s.tex.SampleLevel(s.state, uv, lod) + #endif + + #define DECLARE_SAMPLER_CUBE(name) \ + TextureCube TEXTURE_NAME(name); \ + SamplerState SAMPLER_NAME(name); \ + static SamplerCube name = { TEXTURE_NAME(name), SAMPLER_NAME(name) }; + + #define DECLARE_SAMPLER_CUBE_SHARED(name, share_from) \ + TextureCube TEXTURE_NAME(name); \ + static SamplerCube name = { TEXTURE_NAME(name), SAMPLER_NAME(share_from) }; + + + struct Sampler3D { Texture3D tex; SamplerState state; }; + + #define TEX3D(s, uv) s.tex.Sample(s.state, uv) + #if defined(GNM) + #define TEX3DLOD(s, uv, lod) s.tex.SampleLOD(s.state, uv, lod) + #else + #define TEX3DLOD(s, uv, lod) s.tex.SampleLevel(s.state, uv, lod) + #endif + + #define DECLARE_SAMPLER_3D(name) \ + Texture3D TEXTURE_NAME(name); \ + SamplerState SAMPLER_NAME(name); \ + static Sampler3D name = { TEXTURE_NAME(name), SAMPLER_NAME(name) }; + + #define DECLARE_SAMPLER_3D_SHARED(name, share_from) \ + Texture3D TEXTURE_NAME(name); \ + static Sampler3D name = { TEXTURE_NAME(name), SAMPLER_NAME(share_from) }; + + + struct ComparisonSampler2D { Texture2D tex; SamplerComparisonState state; }; + + #define TEX2DCMP(s, uv, cmp_value) s.tex.SampleCmp(s.state, uv, cmp_value) + #if defined(GNM) + #define TEX2DCMPLOD0(s, uv, cmp_value) s.tex.SampleCmpLOD0(s.state, uv, cmp_value) + #else + #define TEX2DCMPLOD0(s, uv, cmp_value) s.tex.SampleCmpLevelZero(s.state, uv, cmp_value) + #endif + + #define DECLARE_COMPARISON_SAMPLER_2D(name) \ + Texture2D TEXTURE_NAME(name); \ + SamplerComparisonState SAMPLER_NAME(name); \ + static ComparisonSampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(name) }; + + #define DECLARE_COMPARISON_SAMPLER_2D_SHARED(name, share_from) \ + Texture2D TEXTURE_NAME(name); \ + static ComparisonSampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(share_from) }; + + struct ComparisonSamplerCube { TextureCube tex; SamplerComparisonState state; }; + + #define TEXCUBECMP(s, uv, cmp_value) s.tex.SampleCmp(s.state, uv, cmp_value) + #if defined(GNM) + #define TEXCUBECMPLOD0(s, uv, cmp_value) s.tex.SampleCmpLOD0(s.state, uv, cmp_value) + #else + #define TEXCUBECMPLOD0(s, uv, cmp_value) s.tex.SampleCmpLevelZero(s.state, uv, cmp_value) + #endif + + #define DECLARE_COMPARISON_SAMPLER_CUBE(name) \ + TextureCube TEXTURE_NAME(name); \ + SamplerComparisonState SAMPLER_NAME(name); \ + static ComparisonSamplerCube name = { TEXTURE_NAME(name), SAMPLER_NAME(name) }; + + inline float4 Sample(Sampler2D s, float2 uv, int2 offset) + { + return s.tex.Sample(s.state, uv, offset); + } + + inline float4 Sample(Sampler3D s, float3 uvw, int3 offset) + { + return s.tex.Sample(s.state, uvw, offset); + } + + inline float4 Sample(Sampler2D s, float2 uv) + { + return s.tex.Sample(s.state, uv); + } + + inline float4 Sample(Sampler3D s, float3 uvw) + { + return s.tex.Sample(s.state, uvw); + } + #else + #define DECLARE_SAMPLER_2D(name) sampler2D name; + #define Sampler2D sampler2D + #define TEX2D tex2D + #define TEX2DLOD(s, uv, lod) tex2Dlod(s, float4(uv, 0, lod)) + + #define DECLARE_SAMPLER_CUBE(name) samplerCUBE name; + #define SamplerCube samplerCUBE + #define TEXCUBELOD(s, uv, lod) texCUBElod(s, float4(uv, lod)) + #define TEXCUBE(s, uv) texCUBE(s, uv) + + #define DECLARE_SAMPLER_3D(name) sampler3D name; + #define Sampler3D sampler3D + #define TEX3D(s, uv) tex3D(s, uv) + #define TEX3DLOD(s, uv, lod) tex3Dlod(s, float4(uv, lod)) + #endif + + #if defined(RENDERER_GL) + #define UNIFORM uniform + #else + #define UNIFORM + #endif + + #define MAX_RIMLIGHT_EXPONENT 8.0 + #define MAX_GLOSSINESS 500.0 + #define ONE_BIT_ALPHA_REF 0.5 + #define PI 3.14159265358979323846 + + inline float3x3 to_mat3(float4x4 m) + { + #if defined(RENDERER_GL) + return mat3(m); + #else + return (float3x3)m; + #endif + } + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) || (defined(RENDERER_GL) && defined(STAGE_FRAGMENT)) + inline void one_bit_alpha_mask(float alpha, float ref) { + if (alpha < ref) + discard; + } + #endif + + #if defined(RENDERER_GNM) + inline bool front_facing(bool vface) { + return vface; + } + #else + inline bool front_facing(float vface) { + return vface > 0.0; + } + #endif + + //remap color to RGBA using swizzling + inline float4 decode_vertex_color(float4 col) { + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) || defined(RENDERER_GL) + return col.bgra; + #else + return col; + #endif + } + + inline float3 decode_signed_normal(float3 v) { + return 2.0*(v - 0.5); + } + + inline float3 encode_signed_normal(float3 v) { + return v*0.5 + 0.5; + } + + inline float3 decode_normal_map(half4 v) { + half4 temp = 2.0*(v - 0.5); + return new_half3(temp.x, temp.y, sqrt(max(1.0 - dot(temp.xy, temp.xy), 0.0))); + } + + inline half2 decode_normal_grad(half4 v) { + half4 temp = 2.0*(v - 0.5); + #if defined(RENDERER_GNM) + return half2(temp.w, temp.y); + #else + return new_half2(temp.x, temp.y); + #endif + } + + inline half4 rgbm_encode(float3 color) { + half4 rgbm; + color = new_half3_xyz(sqrt(color)); + color *= new_half(1.0 / 6.0); + rgbm.a = clamp(max(max(color.r, color.g ), color.b), 0.0, 1.0); + rgbm.a = new_half(ceil(rgbm.a * 255.0) / 255.0); + rgbm.rgb = color / rgbm.a; + return rgbm; + } + + inline float3 rgbm_decode(half4 rgbm) { + float3 c = 6.0 * rgbm.rgb * rgbm.a; + return c * c; + } + + inline void tspace_transform_transpose(out float3 tangent_out, out float3 binormal_out, out float3 normal_out, in float3 tangent, in float3 binormal, in float3 normal, float3x3 mat) { + float3 t = normalize(mul(tangent, mat)); + float3 b = normalize(mul(binormal, mat)); + float3 n = normalize(mul(normal, mat)); + + // find transpose of 3x3matrix + tangent_out.x = t.x; tangent_out.y = b.x; tangent_out.z = n.x; + binormal_out.x = t.y; binormal_out.y = b.y; binormal_out.z = n.y; + normal_out.x = t.z; normal_out.y = b.z; normal_out.z = n.z; + } + + inline void tspace_transform(out float3 tangent_out, out float3 binormal_out, out float3 normal_out, in float3 tangent, in float3 binormal, in float3 normal, float3x3 mat) { + tangent_out = normalize(mul(tangent, mat)); + binormal_out = normalize(mul(binormal, mat)); + normal_out = normalize(mul(normal, mat)); + } + + inline void tspace_transpose(out float3 tsm0, out float3 tsm1, out float3 tsm2, in float3 t, in float3 b, in float3 n) { + tsm0 = float3(t.x, b.x, n.x); + tsm1 = float3(t.y, b.y, n.y); + tsm2 = float3(t.z, b.z, n.z); + } + + // half and float types are considered the same on gl and gnm platforms. + #if !(defined(RENDERER_GL) || defined(RENDERER_GNM)) + inline half3 rotate_vector3(half3 v, half3 x, half3 y, half3 z) { + return new_half3_xyz(normalize(half3(dot(v, x), dot(v, y), dot(v, z)))); + } + #endif + + inline float3 rotate_vector3(float3 v, float3 x, float3 y, float3 z) { + return normalize(new_float3(dot(v, x), dot(v, y), dot(v, z))); + } + + inline float3 rotate_point3(float3 v, float3 x, float3 y, float3 z) { + return new_float3(dot(v, x), dot(v, y), dot(v, z)); + } + + inline float3 fast_gamma_to_linear_rgb(float3 c) { + return c * c; + } + + inline float4 fast_gamma_to_linear_rgb(float4 c) { + return new_float4_xyz(c.rgb * c.rgb, c.a); + } + + inline float4 fast_gamma_to_linear_rgba(float4 c) { + return c * c; + } + + inline float3 fast_linear_to_gamma_rgb(float3 c) { + return sqrt(c); + } + + inline float4 fast_linear_to_gamma_rgb(float4 c) { + return new_float4_xyz(sqrt(c.rgb), c.a); + } + + inline float4 fast_linear_to_gamma_rgba(float4 c) { + return sqrt(c); + } + + CBUFFER_START(global_viewport) + UNIFORM float3 camera_unprojection; // Deprecated uniform + UNIFORM float3 camera_pos; + UNIFORM float4x4 camera_view; + UNIFORM float4x4 camera_projection; + UNIFORM float4x4 camera_inv_view; + UNIFORM float4x4 camera_inv_projection; + UNIFORM float4x4 camera_world; + UNIFORM float4x4 camera_last_world; + UNIFORM float4x4 camera_last_view; + UNIFORM float4x4 camera_last_view_projection; + UNIFORM float4x4 camera_custom_fov_view_projection; // TODO: Add a define in engine to only set this if we need it + UNIFORM float4x4 camera_custom_fov_last_view_projection; // TODO: Add a define in engine to only set this if we need it + UNIFORM float time; + UNIFORM float delta_time; + UNIFORM float frame_number; + UNIFORM float2 back_buffer_size; + UNIFORM float taa_enabled; + UNIFORM float debug_rendering; + UNIFORM float gamma; + // Screen space coordinate calculation + // X = (X + 1) * Viewport.Width * 0.5 + Viewport.TopLeftX + // Y = (1 - Y) * Viewport.Height * 0.5 + Viewport.TopLeftY + // Z = Viewport.MinDepth + Z * (Viewport.MaxDepth - Viewport.MinDepth) + // X, Y clip space coordinate range [-1, 1] =>transforms to screen space coordinate [0, 1920] and [0, 1080]. + UNIFORM float4 viewport; // width, height, top.x, top.y + #if defined(D3D11) + float capture_cubemap; + #endif + //float g_particle_lighting; + float sun_shadows; + float skin_material_enabled; + CBUFFER_END + + // will soon merge with global_viewport.. + CBUFFER_START(global_camera) + UNIFORM float3 camera_near_far; + CBUFFER_END + + CBUFFER_START(c_environment_settings) + #if defined(HAS_SUN_DIRECTION) + UNIFORM float3 sun_direction; + #endif + #if defined(HAS_SUN_COLOR) + UNIFORM float3 sun_color; + #endif + CBUFFER_END + """ + } + + gbuffer_access = { + // Compatibility stuff for old library compiler + fp_code = { ref = "code" } + vp_code = { ref = "code" } + + code = """ + // Materials + inline float2 encode_float_rg(float v) { + float2 enc = new_float2(1.0, 255.0) * v; + enc = frac(enc); + enc -= enc.y * new_float2(1.0/255.0, 1.0/255.0); + return enc; + } + + inline float decode_float_rg(float2 rg) { + return dot(rg, new_float2(1.0, 1.0/255.0)); + } + + inline float4 encode_float_rgba(float v) { + float4 enc = new_float4(1.0, 255.0, 65025.0, 160581375.0) * v; + enc = frac(enc); + enc -= enc.yzww * new_float4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; + } + + inline float decode_float_rgba(float4 rgba) { + return dot(rgba, new_float4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/160581375.0)); + } + + #define DEFAULT_MATERIAL 0 + #define CLOTH_MATERIAL 1.0/255.0 + #define SKIN_MATERIAL 2.0/255.0 + #define COLORED_SPECULAR_MATERIAL 3.0/255.0 + + #if defined(RENDERER_GL) && defined(STAGE_FRAGMENT) + #define GBUFFER_OUTPUT \ + layout(location = 0) out mediump vec4 out_gbuffer0; \ + layout(location = 1) out mediump vec4 out_gbuffer1; \ + layout(location = 2) out mediump vec4 out_gbuffer2; \ + layout(location = 3) out mediump vec4 out_gbuffer3 + + #define out_base_color out_gbuffer0.rgb + #define out_metallic out_gbuffer0.a + #define out_normal out_gbuffer1.rgb + #define out_roughness out_gbuffer1.a + #define out_velocity out_gbuffer2.ba + #define out_ambient_diffuse_light out_gbuffer3 + #define out_ambient_occlusion out_gbuffer2.r + #define out_density out_gbuffer2.g + #elif !defined(RENDERER_GL) + struct GBUFFER_OUT { + half4 buffer0 : SV_TARGET0; + half4 buffer1 : SV_TARGET1; + half2 buffer2 : SV_TARGET2; + // half4 buffer3 : SV_TARGET3; // Since we don't have baked lighting, we don't use this + half2 buffer4 : SV_TARGET3; + }; + #define BASE_COLOR(gbuffer) gbuffer.buffer0.rgb + #define METALLIC(gbuffer) gbuffer.buffer0.a + #define NORMAL(gbuffer) gbuffer.buffer1.rgb + #define ROUGHNESS(gbuffer) gbuffer.buffer1.a + // #define AMBIENT_DIFFUSE_LIGHT(gbuffer) gbuffer.buffer3.rgba + #define VELOCITY(gbuffer) gbuffer.buffer4.rg + #define AMBIENT_OCCLUSION(gbuffer) gbuffer.buffer2.r + #define DENSITY(gbuffer) gbuffer.buffer2.g + #endif + + #if defined(RENDERER_D3D12) + inline float3 gbuffer_encode_base_color(float3 color) { return color; } + inline float3 gbuffer_decode_base_color(half4 c) { return c.rgb; } + #else + inline float3 gbuffer_encode_base_color(float3 color) { return new_half3_xyz(sqrt(color.xyz)); } + inline float3 gbuffer_decode_base_color(half4 c) { return c.rgb * c.rgb; } + #endif + + inline float3 gbuffer_encode_normal_hiquality(float3 normal, Sampler2D nft) { + normal = new_half3_xyz(normalize(normal)); + float3 abs_normal = abs(normal); + half max_abs_normal = max(abs_normal.x, max(abs_normal.y, abs_normal.z)); + float2 tcoord = abs_normal.z < max_abs_normal ? (abs_normal.y < max_abs_normal ? abs_normal.yz : abs_normal.xz) : abs_normal.xy; + tcoord = tcoord.x < tcoord.y ? tcoord.yx : tcoord.xy; + tcoord.y /= tcoord.x; + normal /= max_abs_normal; + half fitting_scale = new_half(TEX2D(nft, tcoord).a); + normal *= fitting_scale; + return encode_signed_normal(normal); + } + + inline float3 gbuffer_encode_normal(float3 normal) { return encode_signed_normal(normal); } + inline float3 gbuffer_decode_normal(half4 c) { return decode_signed_normal(c.rgb); } + inline half gbuffer_encode_metallic_mask(half s) { return s; } + inline half gbuffer_decode_metallic_mask(half4 c) { return saturate(c.a); } + inline half gbuffer_encode_roughness(half c) { return c; } + inline half gbuffer_decode_roughness(half4 c) { return saturate(c.a); } + inline half4 gbuffer_encode_ambient_diffuse_light(float3 col) { return rgbm_encode(new_half3_xyz(col)); } + inline float3 gbuffer_decode_ambient_diffuse_light(half4 c) { return rgbm_decode(c); } + inline half gbuffer_encode_ambient_occlusion(half c) { return c; } + inline half gbuffer_decode_ambient_occlusion(half4 c) { return saturate(c.r); } + inline half gbuffer_encode_density(half c) { return c; } + inline half gbuffer_decode_density(half4 c) { return saturate(c.g); } + + inline float linearize_depth(float clip_depth) { + return (camera_near_far.y*camera_near_far.x)/(camera_near_far.y - clip_depth*(camera_near_far.y-camera_near_far.x)); + } + + // SCALE_VELOCITY represents the maximum component size of a motion vector (in pixels). + #define SCALE_VELOCITY 50.0 + #define HALF_ONE 127.0/255.0 + inline float2 encode_velocity(float2 v) { + // On high-end platforms we don't encode velocity vectors (stored as 16F precision) + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + return v; + #else + v *= back_buffer_size/(SCALE_VELOCITY * 2.0); + return clamp(v * 0.5 + HALF_ONE, 1.0/255.0, 1.0); + #endif + } + + inline float2 decode_velocity(float2 v) { + // On high-end platforms we don't encode velocity vectors (stored as 16F precision) + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + return v; + #else + v = 2.0 * (v - HALF_ONE); + return v / (back_buffer_size / (SCALE_VELOCITY * 2.0)); + #endif + } + + #if defined(RENDERER_GL) + float gbuffer_decode_depth(uvec4 d) { + return uintBitsToFloat(d.r); + } + #else + inline float gbuffer_decode_depth(float4 c) { + return c.r; + } + #endif + + inline float4 encode_world_pos(float4 p) { + return new_float4_xyz(mul(mul(p,camera_inv_projection).xyz,to_mat3(camera_inv_view)),p.w); + } + + // Function is deprecated. Use encode_world_pos(float4) instead. + inline float4 encode_world_pos(float4 p, float3 unprojection) { + return encode_world_pos(p); + } + + inline float3 decode_world_pos(float4 p, float depth) { + return (p.xyz / p.w) * depth + camera_pos; + } + """ + } + + space_conversion = { + + // Compatibility stuff for old library compiler + fp_code = { ref = "code" } + vp_code = { ref = "code" } + + // Normalized Display Coordinates (variable prefix "ndc_") + // (1,1) + // .-----------. (Z=1) + // | | / + // | 0 | / + // | | / + // .-----------.(Z=0) + // (-1,-1) + + // Screen Space Coordinates (variable prefix "ss_") + // (1,0) + // 0-----------. (Z=1) + // | | / + // | | / + // | | / + // .-----------.(Z=0) + // (0,1) + + // View Space Coordinates (variable prefix "view_") + // (+,+) + // .-----------. (Z=Far Plane) + // | | / + // | 0 | / + // | | / + // .-----------.(Z=Near Plane) + // (-,-) + + // World Space Coordinates (variable prefix "world_") + // + // (Z+) (Y+) + // | / + // | / + // |/ + // -------0------(X+) + // / + // / + + code = """ + float3 ss_to_view(float3 ss_vector, bool is_point) { + float x = ss_vector.x * 2 - 1; + float y = (1 - ss_vector.y) * 2 - 1; + float4 view_vector = mul(float4(x, y, ss_vector.z, is_point), camera_inv_projection); + view_vector.xyz /= view_vector.w; + return view_vector.xyz; + } + + float3 view_to_ss(float3 view_vector, bool is_point) { + float4 ss_vector = mul(float4(view_vector, is_point), camera_projection); + ss_vector.xyz /= ss_vector.w; + ss_vector.xy = ss_vector.xy * float2(0.5, -0.5) + float2(0.5, 0.5); + return ss_vector.xyz; + } + + float3 world_to_view(float3 world_vector, bool is_point) { + float4 view_vector = mul(float4(world_vector, is_point), camera_view); + return view_vector.xyz; + } + + float3 world_to_prev_view(float3 world_vector, bool is_point, bool lock_xyz = 0) { + camera_last_view[3][0] *= !lock_xyz; + camera_last_view[3][1] *= !lock_xyz; + camera_last_view[3][2] *= !lock_xyz; + float4 view_vector = mul(float4(world_vector, is_point), camera_last_view); + return view_vector.xyz; + } + + """ + } + + skinning = { + // This block uses the 'new' syntax as code sharing is very low between languages and it won't be included from any uber shaders. + code = { + glsl = """ + #if defined(SKINNED_1WEIGHT) || defined(SKINNED_2WEIGHTS) || defined(SKINNED_3WEIGHTS) || defined(SKINNED_4WEIGHTS) + #define SKINNED + #endif + + // TODO: Workaround long compile times, since all shaders are currently compiled with skinned option. + #if defined(SKINNED) && defined(SKINNED_DISABLED) + #undef SKINNED + #endif + + #if defined(SKINNED) + #define MAX_BONES 50 + CBUFFER_START(c_skin_matrices) + UNIFORM highp mat4 bones[MAX_BONES]; + #if defined(MOTION_BLUR) + UNIFORM highp mat4 last_bones[MAX_BONES]; + #endif + CBUFFER_END + + #if defined(SKINNED_1WEIGHT) + #define IndexType LOWP float + #define WeightType LOWP float + + vec3 skin_point(vec4 p, IndexType bi, WeightType bw) { + mat4 bone = bones[int(bi)]; + return (p * bone).xyz; + } + + #if defined(MOTION_BLUR) + vec3 skin_point_last_frame(vec4 p, IndexType bi, WeightType bw) { + mat4 bone = last_bones[int(bi)]; + return (p * bone).xyz; + } + #endif + + vec3 skin_vector(vec3 v, IndexType bi, WeightType bw) { + mat4 bone = bones[int(bi)]; + return normalize(v * mat3(bone[0].xyz, bone[1].xyz, bone[2].xyz)); + } + vec3 skin_displacement_delta(vec3 v, IndexType bi, WeightType bw) { + mat4 bone = bones[int(bi)]; + return v * mat3(bone[0].xyz, bone[1].xyz, bone[2].xyz); + } + #elif defined(SKINNED_2WEIGHTS) + #define IndexType LOWP vec2 + #define WeightType LOWP vec2 + + vec3 skin_point(vec4 p, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + return (bw.x * (p * bone1) + bw.y * (p * bone2)).xyz; + } + #if defined(MOTION_BLUR) + vec3 skin_point_last_frame(vec4 p, IndexType bi, WeightType bw) { + mat4 bone1 = last_bones[int(bi.x)]; + mat4 bone2 = last_bones[int(bi.y)]; + return (bw.x * (p * bone1) + bw.y * (p * bone2)).xyz; + } + #endif + vec3 skin_vector(vec3 v, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + return normalize( bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz)) + + bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz))); + } + vec3 skin_displacement_delta(vec3 v, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + return bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz)) + + bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz)); + } + #elif defined(SKINNED_3WEIGHTS) + #define IndexType LOWP vec3 + #define WeightType LOWP vec3 + + vec3 skin_point(vec4 p, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + mat4 bone3 = bones[int(bi.z)]; + return (bw.x * (p * bone1) + bw.y * (p * bone2) + bw.z * (p * bone3)).xyz; + } + #if defined(MOTION_BLUR) + vec3 skin_point_last_frame(vec4 p, IndexType bi, WeightType bw) { + mat4 bone1 = last_bones[int(bi.x)]; + mat4 bone2 = last_bones[int(bi.y)]; + mat4 bone3 = last_bones[int(bi.z)]; + return (bw.x * (p * bone1) + bw.y * (p * bone2) + bw.z * (p * bone3)).xyz; + } + #endif + vec3 skin_vector(vec3 v, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + mat4 bone3 = bones[int(bi.z)]; + return normalize( bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz)) + + bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz)) + + bw.z * (v * mat3(bone3[0].xyz, bone3[1].xyz, bone3[2].xyz))); + } + vec3 skin_displacement_delta(vec3 v, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + mat4 bone3 = bones[int(bi.z)]; + return bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz)) + + bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz)) + + bw.z * (v * mat3(bone3[0].xyz, bone3[1].xyz, bone3[2].xyz)); + } + #elif defined(SKINNED_4WEIGHTS) + #define IndexType LOWP vec4 + #define WeightType LOWP vec4 + + vec3 skin_point(vec4 p, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + mat4 bone3 = bones[int(bi.z)]; + mat4 bone4 = bones[int(bi.w)]; + return (bw.x * (p * bone1) + bw.y * (p * bone2) + bw.z * (p * bone3) + bw.w * (p * bone4)).xyz; + } + #if defined(MOTION_BLUR) + vec3 skin_point_last_frame(vec4 p, IndexType bi, WeightType bw) { + mat4 bone1 = last_bones[int(bi.x)]; + mat4 bone2 = last_bones[int(bi.y)]; + mat4 bone3 = last_bones[int(bi.z)]; + mat4 bone4 = last_bones[int(bi.w)]; + return (bw.x * (p * bone1) + bw.y * (p * bone2) + bw.z * (p * bone3) + bw.w * (p * bone4)).xyz; + } + #endif + vec3 skin_vector(vec3 v, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + mat4 bone3 = bones[int(bi.z)]; + mat4 bone4 = bones[int(bi.w)]; + return normalize( bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz)) + + bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz)) + + bw.z * (v * mat3(bone3[0].xyz, bone3[1].xyz, bone3[2].xyz)) + + bw.w * (v * mat3(bone4[0].xyz, bone4[1].xyz, bone4[2].xyz))); + } + vec3 skin_displacement_delta(vec3 v, IndexType bi, WeightType bw) { + mat4 bone1 = bones[int(bi.x)]; + mat4 bone2 = bones[int(bi.y)]; + mat4 bone3 = bones[int(bi.z)]; + mat4 bone4 = bones[int(bi.w)]; + return bw.x * (v * mat3(bone1[0].xyz, bone1[1].xyz, bone1[2].xyz)) + + bw.y * (v * mat3(bone2[0].xyz, bone2[1].xyz, bone2[2].xyz)) + + bw.z * (v * mat3(bone3[0].xyz, bone3[1].xyz, bone3[2].xyz)) + + bw.z * (v * mat3(bone4[0].xyz, bone4[1].xyz, bone4[2].xyz)); + } + #endif + + in IndexType blendindices; + in WeightType blendweights; + #endif + """ + + hlsl = """ + #if defined(SKINNED_1WEIGHT) || defined(SKINNED_2WEIGHTS) || defined(SKINNED_3WEIGHTS) || defined(SKINNED_4WEIGHTS) + #define SKINNED + #endif + + // TODO: Workaround long compile times, since all shaders are currently compiled with skinned option. + #if defined(SKINNED) && defined(SKINNED_DISABLED) + #undef SKINNED + #endif + + #if defined(SKINNED) + CBUFFER_START(c_skin_matrices) + #if defined(RENDERER_GL2) + #define MAX_BONES 50 + #else + #define MAX_BONES 100 + #endif + float4x4 bones[MAX_BONES]; + + #if defined(MOTION_BLUR) + float4x4 last_bones[MAX_BONES]; + #endif + CBUFFER_END + + #if defined(SKINNED_1WEIGHT) + #define IndexType float1 + #define WeightType float1 + + #if defined(GNM) + // Avoids 'unreferenced variable' warning on ps4 + #define WEIGHT_PARAM_NAME + #else + #define WEIGHT_PARAM_NAME bw + #endif + + inline float3 skin_point(in float4 p, in IndexType bi, in WeightType WEIGHT_PARAM_NAME) { + return mul(p, (bones[(int)bi.x])).xyz; + } + #if defined(MOTION_BLUR) + inline float3 skin_point_last_frame(in float4 p, in IndexType bi, in WeightType WEIGHT_PARAM_NAME) { + return mul(p, (last_bones[(int)bi.x])).xyz; + } + #endif + inline float3 skin_vector(in float3 v, in IndexType bi, in WeightType WEIGHT_PARAM_NAME) { + return normalize(mul(v, (float3x3)(bones[(int)bi.x]))); + } + inline float3 skin_displacement_delta(in float3 v, in IndexType bi, in WeightType WEIGHT_PARAM_NAME) { + return mul(v, (float3x3)(bones[(int)bi.x])); + } + #elif defined(SKINNED_2WEIGHTS) + #define IndexType float2 + #define WeightType float2 + + inline float3 skin_point(in float4 p, in IndexType bi, in WeightType bw) { + return bw.x * mul(p, (bones[(int)bi.x])).xyz + bw.y * mul(p, (bones[(int)bi.y])).xyz; + } + #if defined(MOTION_BLUR) + inline float3 skin_point_last_frame(in float4 p, in IndexType bi, in WeightType bw) { + return bw.x * mul(p, (last_bones[(int)bi.x])).xyz + bw.y * mul(p, (last_bones[(int)bi.y])).xyz; + } + #endif + inline float3 skin_vector(in float3 v, in IndexType bi, in WeightType bw) { + return normalize(bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y]))); + } + inline float3 skin_displacement_delta(in float3 v, in IndexType bi, in WeightType bw) { + return bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])).xyz; + } + #elif defined(SKINNED_3WEIGHTS) + #define IndexType float3 + #define WeightType float3 + + inline float3 skin_point(in float4 p, in IndexType bi, in WeightType bw) { + return bw.x * mul(p, (bones[(int)bi.x])).xyz + bw.y * mul(p, (bones[(int)bi.y])).xyz + bw.z * mul(p, (bones[(int)bi.z])).xyz; + } + #if defined(MOTION_BLUR) + inline float3 skin_point_last_frame(in float4 p, in IndexType bi, in WeightType bw) { + return bw.x * mul(p, (last_bones[(int)bi.x])).xyz + bw.y * mul(p, (last_bones[(int)bi.y])).xyz + bw.z * mul(p, (last_bones[(int)bi.z])).xyz; + } + #endif + inline float3 skin_vector(in float3 v, in IndexType bi, in WeightType bw) { + return normalize(bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])) + bw.z * mul(v, (float3x3)(bones[(int)bi.z]))); + } + inline float3 skin_displacement_delta(in float3 v, in IndexType bi, in WeightType bw) { + return bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])) + bw.z * mul(v, (float3x3)(bones[(int)bi.z])); + } + #elif defined(SKINNED_4WEIGHTS) + #define IndexType float4 + #define WeightType float4 + + inline float3 skin_point(in float4 p, in IndexType bi, in WeightType bw) { + return bw.x * mul(p, (bones[(int)bi.x])).xyz + bw.y * mul(p, (bones[(int)bi.y])).xyz + bw.z * mul(p, (bones[(int)bi.z])).xyz + bw.w * mul(p, (bones[(int)bi.w])).xyz; + } + #if defined(MOTION_BLUR) + inline float3 skin_point_last_frame(in float4 p, in IndexType bi, in WeightType bw) { + return bw.x * mul(p, (last_bones[(int)bi.x])).xyz + bw.y * mul(p, (last_bones[(int)bi.y])).xyz + bw.z * mul(p, (last_bones[(int)bi.z])).xyz + bw.w * mul(p, (last_bones[(int)bi.w])).xyz; + } + #endif + inline float3 skin_vector(in float3 v, in IndexType bi, in WeightType bw) { + return normalize(bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])) + bw.z * mul(v, (float3x3)(bones[(int)bi.z])) + bw.w * mul(v, (float3x3)(bones[(int)bi.w]))); + } + inline float3 skin_displacement_delta(in float3 v, in IndexType bi, in WeightType bw) { + return bw.x * mul(v, (float3x3)(bones[(int)bi.x])) + bw.y * mul(v, (float3x3)(bones[(int)bi.y])) + bw.z * mul(v, (float3x3)(bones[(int)bi.z])) + bw.w * mul(v, (float3x3)(bones[(int)bi.w])); + } + #endif + + #define BLENDINDEXSEMANTIC BLENDINDICES + + #define SKIN_INPUT \ + IndexType blendindices : BLENDINDEXSEMANTIC; \ + WeightType blendweights : BLENDWEIGHTS; + #else + #define SKIN_INPUT + #endif + """ + } + } + + fog = { + vp_code = { ref = "code" } + fp_code = { ref = "code" } + code=""" + CBUFFER_START(c_fog) + UNIFORM float2 fog_depth_range; + float2 height_fog_offset_falloff; // TODO: merge this param with fog_depth_range? + float fog_type; + UNIFORM float3 fog_color; + #if !defined(HAS_SUN_DIRECTION) + UNIFORM float3 sun_direction; + #endif + #if !defined(HAS_SUN_COLOR) + UNIFORM float3 sun_color; + #endif + UNIFORM float3 fog_sun_blend; + UNIFORM float fog_enabled; + CBUFFER_END + + #define DEFAULT_EXP_HEIGHT 0 + #define EXP_HEIGHT 1 + #define EXP 2 + #define LINEAR 3 + + half4 apply_fog(half4 c, float3 wp, float d) { + if (fog_enabled < 1.0) + return c; + + float start = fog_depth_range.x; + float end = fog_depth_range.y; + float b = 1.f / (end-start); + + #if defined(RENDERER_GL) + float3 view_dir = normalize(new_half3(camera_world[0].z, camera_world[1].z, camera_world[2].z) - wp); + float camera_world_m32 = camera_world[2].z; + #else + float3 view_dir = normalize(camera_world._m30_m31_m32 - wp); + float camera_world_m32 = camera_world._m32; + #endif + + float dist = d - start; + + float a = 0.0; + [branch] + if (fog_type == DEFAULT_EXP_HEIGHT) { + a = saturate(exp(-camera_world._m32 * b) * (1.0 - exp(-dist * -view_dir.z * b)) / -view_dir.z); + } else if (fog_type == EXP_HEIGHT) { + a = saturate(exp((height_fog_offset_falloff.x - wp.z)/height_fog_offset_falloff.y) * (1.0 - exp(-dist * b))); + } else if (fog_type == EXP) { + a = 1.0 - saturate(exp(-dist * b)); + } else { + a = d > start ? saturate((d-start) * b) : 0; + } + + half sa = fog_sun_blend.x * pow(saturate(dot(view_dir, sun_direction)), fog_sun_blend.y); + float3 fog_c = lerp(fog_color, fog_sun_blend.z * sun_color, sa); + return new_half4_xyz(lerp(c.rgb, fog_c, a), c.a); + } + + float4 calc_fog_vs(float3 wp, float d) { + if (fog_enabled < 1.0) + return float4(0,0,0,0); + + float start = fog_depth_range.x; + float end = fog_depth_range.y; + float b = 1.f / (end-start); + + #if defined(RENDERER_GL) + float3 view_dir = normalize(new_half3(camera_world[0].z, camera_world[1].z, camera_world[2].z) - wp); + float camera_world_m32 = camera_world[2].z; + #else + float3 view_dir = normalize(camera_world._m30_m31_m32 - wp); + float camera_world_m32 = camera_world._m32; + #endif + + float dist = d - start; + + float a = 0.0; + [branch] + if (fog_type == DEFAULT_EXP_HEIGHT) { + a = saturate(exp(-camera_world._m32 * b) * (1.0 - exp(-dist * -view_dir.z * b)) / -view_dir.z); + } else if (fog_type == EXP_HEIGHT) { + a = saturate(exp((height_fog_offset_falloff.x - wp.z)/height_fog_offset_falloff.y) * (1.0 - exp(-dist * b))); + } else if (fog_type == EXP) { + a = 1.0 - saturate(exp(-dist * b)); + } else { + a = d > start ? saturate((d-start) * b) : 0; + } + + half sa = fog_sun_blend.x * pow(saturate(dot(view_dir, sun_direction)), fog_sun_blend.y); + float3 fog_c = lerp(fog_color, fog_sun_blend.z * sun_color, sa); + return float4(fog_c, a); + + } + + #undef DEFAULT_EXP_HEIGHT + #undef EXP_HEIGHT + #undef EXP + #undef LINEAR + """ + } + + /*shadow_bias = { + code = """ + float apply_shadow_bias(float depth) { + const float min_bias = 0.005; + const float max_bias = 0.2; + const float blend_distance = 50.0; + depth -= lerp(min_bias, max_bias, saturate(depth / blend_distance)); + return depth; + } + """ + fp_code = { ref = "code" } + } + */ + color_management = { + code=""" + #if defined(RENDERER_GL) + #define splat3(c) vec3(c) + #else + #define splat3(c) c + #endif + + static const float3 luminance_vector = float3(0.2127, 0.7152, 0.0721); + static const half min_positive_f16 = 0.000061; + + float3 filmic_tone_mapping(float3 lin_x) { + float3 x = max(splat3(0.f), lin_x - splat3(0.004f)); + return (x*(6.2f*x + splat3(0.5f)))/(x*(6.2f*x + splat3(1.7f)) + splat3(0.06f)); + } + + float3 safe_range_tone_map(float3 value) { + float lum = max(dot(value.rgb, luminance_vector), min_positive_f16); + value.rgb /= (1.0 + lum); + + return value; + } + + float3 inv_safe_range_tone_map(float3 value) { + float lum = dot(value.rgb, luminance_vector); + value.rgb /= max((1.0 - lum), min_positive_f16); + + return value; + } + + float3 safe_range_tone_map_offset(float3 value, float offset) { + float lum = max(dot(value.rgb, luminance_vector), min_positive_f16); + value.rgb /= (offset + lum); + + return value; + } + + float3 inv_safe_range_tone_map_offset(float3 value, float offset) { + float lum = dot(value.rgb, luminance_vector); + value.rgb /= max((offset - lum), min_positive_f16); + + return value; + } + + float luminance(float3 value) { + return dot(value, luminance_vector); + } + + #undef splat3 + """ + fp_code = { ref = "code" } + } + + billboard_transformation = { + code = """ + // TODO: reuse code + void get_billboard_data_from_position(in float3 world_pos, in float3 camera_pos, in float4x4 camera_view, inout float4 position, out float4 wp, out float3 normal, out float3 tangent, out float3 binormal) { + wp = float4(world_pos, 1.0); + #if defined(BILLBOARD_TANGENT_ALIGNED) || defined(BILLBOARD_CAMERA_ALIGNED) + float3 eye_dir = normalize(camera_pos - wp.rgb); + #endif + #if defined(BILLBOARD_TANGENT_ALIGNED) + #if defined(BILLBOARD_TANGENT_ALIGNED_X_LOCKED) + float3 x_axis = float3(1, 0, 0); + float3 y_axis = normalize(cross(eye_dir, x_axis)); + #elif defined(BILLBOARD_TANGENT_ALIGNED_Y_LOCKED) + float3 x_axis = float3(0, 1, 0); + float3 y_axis = normalize(cross(eye_dir, x_axis)); + #else + float3 y_axis = float3(0, 0, 1); + float3 x_axis = normalize(cross(y_axis, eye_dir)); + #endif + #elif defined(BILLBOARD_CAMERA_ALIGNED) + float3 y_axis = float3(0, 0, 1); + float3 x_axis = normalize(cross(y_axis, eye_dir)); + y_axis = cross(eye_dir, x_axis); + #else + float3 x_axis = camera_view._m00_m10_m20; + float3 y_axis = camera_view._m02_m12_m22; + #endif + position = float4(x_axis * position.x + y_axis * position.y, 1.0); + wp.xyz += position.xyz; + + // Calculate normals and tangent info + #if defined(BILLBOARD_TANGENT_ALIGNED) + normal = cross(x_axis, y_axis); + #elif defined(BILLBOARD_CAMERA_ALIGNED) + normal = eye_dir; // TODO: is this same as -camera_view._m01_m11_m21? + #else + normal = -camera_view._m01_m11_m21; + #endif + #if defined(BILLBOARD_SPHERICAL_NORMAL) + // TODO: re-calculate tangent and binormal + normal = normalize(lerp(position, normal, 0.5)); + #endif + tangent = x_axis; + binormal = y_axis; + } + + void get_billboard_positions_from_position(in float3 world_pos, in float3 camera_pos, in float4x4 camera_view, inout float4 position, out float4 wp) { + wp = float4(world_pos, 1.0); + #if defined(BILLBOARD_TANGENT_ALIGNED) || defined(BILLBOARD_CAMERA_ALIGNED) + float3 eye_dir = normalize(camera_pos - wp.rgb); + #endif + #if defined(BILLBOARD_TANGENT_ALIGNED) + #if defined(BILLBOARD_TANGENT_ALIGNED_X_LOCKED) + float3 x_axis = float3(1, 0, 0); + float3 y_axis = normalize(cross(eye_dir, x_axis)); + #elif defined(BILLBOARD_TANGENT_ALIGNED_Y_LOCKED) + float3 x_axis = float3(0, 1, 0); + float3 y_axis = normalize(cross(eye_dir, x_axis)); + #else + float3 y_axis = float3(0, 0, 1); + float3 x_axis = normalize(cross(y_axis, eye_dir)); + #endif + #elif defined(BILLBOARD_CAMERA_ALIGNED) + float3 y_axis = float3(0, 0, 1); + float3 x_axis = normalize(cross(y_axis, eye_dir)); + y_axis = cross(eye_dir, x_axis); + #else + float3 x_axis = camera_view._m00_m10_m20; + float3 y_axis = camera_view._m02_m12_m22; + #endif + position = float4(x_axis * position.x + y_axis * position.y, 1.0); + wp.xyz += position.xyz; + } + + void get_billboard_data_from_direction(in float3 direction, inout float4 position, out float4 wp, out float3 normal, out float3 tangent, out float3 binormal) { + wp = float4(-direction, 1.0); + float3 y_axis = float3(0, 0, 1); + float3 x_axis = normalize(cross(y_axis, direction)); + y_axis = cross(direction, x_axis); + position = float4(x_axis * position.x + y_axis * position.y, 1.0); + wp.xyz += position.xyz; + + // Calculate normals and tangent info + normal = direction; + #if defined(BILLBOARD_SPHERICAL_NORMAL) + // TODO: re-calculate tangent and binormal + normal = normalize(lerp(position, normal, 0.5)); + #endif + tangent = x_axis; + binormal = y_axis; + } + + void get_billboard_positions_from_direction(in float3 direction, inout float4 position, out float4 wp) { + wp = float4(-direction, 1.0); + float3 y_axis = float3(0, 0, 1); + float3 x_axis = normalize(cross(y_axis, direction)); + y_axis = cross(direction, x_axis); + position = float4(x_axis * position.x + y_axis * position.y, 1.0); + wp.xyz += position.xyz; + } + """ + fp_code = { ref = "code" } + } + + taa_offsets = { + code=""" + #if !defined(RENDERER_GL) + CBUFFER_START(temporal_effects) + UNIFORM float temporal_effects_enabled; + CBUFFER_END + + // Halton offsets + #define NUM_HALTON_OFFSETS 8 + #define BLACKMAN_HARRIS_SHARPNESS 2.0 + + static const float2 halton_offsets[] = { + float2((1.f/2.f - 0.5f), (1.f/3.f - 0.5f)), + float2((1.f/4.f - 0.5f), (2.f/3.f - 0.5f)), + float2((3.f/4.f - 0.5f), (1.f/9.f - 0.5f)), + float2((1.f/8.f - 0.5f), (4.f/9.f - 0.5f)), + float2((5.f/8.f - 0.5f), (7.f/9.f - 0.5f)), + float2((3.f/8.f - 0.5f), (2.f/9.f - 0.5f)), + float2((7.f/8.f - 0.5f), (5.f/9.f - 0.5f)), + float2((1.f/16.f - 0.5f), (8.f/9.f - 0.5f)) + }; + + // Blackman-Harris pre calculated weights + float gaussian_blackman_harris(float2 pos) { + float x = pos.x * BLACKMAN_HARRIS_SHARPNESS; + float y = pos.y * BLACKMAN_HARRIS_SHARPNESS; + return exp(-2.29f * (x * x + y * y)); + } + + static const int2 neighbor_offsets_i[9] = { + int2(-1, -1), + int2( 0, -1), + int2( 1, -1), + int2(-1, 0), + int2( 0, 0), + int2( 1, 0), + int2(-1, 1), + int2( 0, 1), + int2( 1, 1) + }; + + static const float2 neighbor_offsets[9] = { + neighbor_offsets_i[0], + neighbor_offsets_i[1], + neighbor_offsets_i[2], + neighbor_offsets_i[3], + neighbor_offsets_i[4], + neighbor_offsets_i[5], + neighbor_offsets_i[6], + neighbor_offsets_i[7], + neighbor_offsets_i[8] + }; + + static const float blackman_harris_sum0 = + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[0]) + + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[0]) + + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[0]) + + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[0]) + + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[0]) + + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[0]) + + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[0]) + + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[0]) + + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[0]); + + static const float blackman_harris_weights0[9] = { + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[0]) / blackman_harris_sum0, + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[0]) / blackman_harris_sum0, + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[0]) / blackman_harris_sum0, + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[0]) / blackman_harris_sum0, + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[0]) / blackman_harris_sum0, + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[0]) / blackman_harris_sum0, + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[0]) / blackman_harris_sum0, + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[0]) / blackman_harris_sum0, + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[0]) / blackman_harris_sum0 + }; + + static const float blackman_harris_sum1 = + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[1]) + + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[1]) + + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[1]) + + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[1]) + + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[1]) + + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[1]) + + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[1]) + + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[1]) + + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[1]); + + static const float blackman_harris_weights1[9] = { + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[1]) / blackman_harris_sum1, + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[1]) / blackman_harris_sum1, + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[1]) / blackman_harris_sum1, + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[1]) / blackman_harris_sum1, + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[1]) / blackman_harris_sum1, + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[1]) / blackman_harris_sum1, + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[1]) / blackman_harris_sum1, + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[1]) / blackman_harris_sum1, + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[1]) / blackman_harris_sum1 + }; + + static const float blackman_harris_sum2 = + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[2]) + + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[2]) + + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[2]) + + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[2]) + + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[2]) + + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[2]) + + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[2]) + + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[2]) + + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[2]); + + static const float blackman_harris_weights2[9] = { + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[2]) / blackman_harris_sum2, + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[2]) / blackman_harris_sum2, + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[2]) / blackman_harris_sum2, + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[2]) / blackman_harris_sum2, + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[2]) / blackman_harris_sum2, + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[2]) / blackman_harris_sum2, + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[2]) / blackman_harris_sum2, + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[2]) / blackman_harris_sum2, + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[2]) / blackman_harris_sum2 + }; + + static const float blackman_harris_sum3 = + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[3]) + + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[3]) + + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[3]) + + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[3]) + + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[3]) + + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[3]) + + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[3]) + + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[3]) + + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[3]); + + static const float blackman_harris_weights3[9] = { + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[3]) / blackman_harris_sum3, + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[3]) / blackman_harris_sum3, + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[3]) / blackman_harris_sum3, + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[3]) / blackman_harris_sum3, + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[3]) / blackman_harris_sum3, + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[3]) / blackman_harris_sum3, + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[3]) / blackman_harris_sum3, + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[3]) / blackman_harris_sum3, + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[3]) / blackman_harris_sum3 + }; + + static const float blackman_harris_sum4 = + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[4]) + + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[4]) + + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[4]) + + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[4]) + + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[4]) + + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[4]) + + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[4]) + + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[4]) + + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[4]); + + static const float blackman_harris_weights4[9] = { + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[4]) / blackman_harris_sum4, + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[4]) / blackman_harris_sum4, + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[4]) / blackman_harris_sum4, + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[4]) / blackman_harris_sum4, + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[4]) / blackman_harris_sum4, + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[4]) / blackman_harris_sum4, + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[4]) / blackman_harris_sum4, + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[4]) / blackman_harris_sum4, + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[4]) / blackman_harris_sum4 + }; + + static const float blackman_harris_sum5 = + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[5]) + + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[5]) + + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[5]) + + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[5]) + + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[5]) + + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[5]) + + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[5]) + + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[5]) + + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[5]); + + static const float blackman_harris_weights5[9] = { + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[5]) / blackman_harris_sum5, + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[5]) / blackman_harris_sum5, + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[5]) / blackman_harris_sum5, + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[5]) / blackman_harris_sum5, + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[5]) / blackman_harris_sum5, + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[5]) / blackman_harris_sum5, + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[5]) / blackman_harris_sum5, + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[5]) / blackman_harris_sum5, + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[5]) / blackman_harris_sum5 + }; + + static const float blackman_harris_sum6 = + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[6]) + + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[6]) + + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[6]) + + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[6]) + + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[6]) + + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[6]) + + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[6]) + + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[6]) + + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[6]); + + static const float blackman_harris_weights6[9] = { + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[6]) / blackman_harris_sum6, + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[6]) / blackman_harris_sum6, + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[6]) / blackman_harris_sum6, + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[6]) / blackman_harris_sum6, + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[6]) / blackman_harris_sum6, + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[6]) / blackman_harris_sum6, + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[6]) / blackman_harris_sum6, + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[6]) / blackman_harris_sum6, + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[6]) / blackman_harris_sum6 + }; + + static const float blackman_harris_sum7 = + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[7]) + + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[7]) + + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[7]) + + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[7]) + + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[7]) + + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[7]) + + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[7]) + + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[7]) + + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[7]); + + static const float blackman_harris_weights7[9] = { + gaussian_blackman_harris(neighbor_offsets[0] + halton_offsets[7]) / blackman_harris_sum7, + gaussian_blackman_harris(neighbor_offsets[1] + halton_offsets[7]) / blackman_harris_sum7, + gaussian_blackman_harris(neighbor_offsets[2] + halton_offsets[7]) / blackman_harris_sum7, + gaussian_blackman_harris(neighbor_offsets[3] + halton_offsets[7]) / blackman_harris_sum7, + gaussian_blackman_harris(neighbor_offsets[4] + halton_offsets[7]) / blackman_harris_sum7, + gaussian_blackman_harris(neighbor_offsets[5] + halton_offsets[7]) / blackman_harris_sum7, + gaussian_blackman_harris(neighbor_offsets[6] + halton_offsets[7]) / blackman_harris_sum7, + gaussian_blackman_harris(neighbor_offsets[7] + halton_offsets[7]) / blackman_harris_sum7, + gaussian_blackman_harris(neighbor_offsets[8] + halton_offsets[7]) / blackman_harris_sum7 + }; + + static const float blackman_harris_weights[NUM_HALTON_OFFSETS][9] = { + blackman_harris_weights0, + blackman_harris_weights1, + blackman_harris_weights2, + blackman_harris_weights3, + blackman_harris_weights4, + blackman_harris_weights5, + blackman_harris_weights6, + blackman_harris_weights7 + }; + + + float2 get_vs_halton_offset(uint frame_number) + { + // Multiply the halton offset by 2 since we are adding the offset in view space (non projected frustrum width and height ranges [-1, 1]) + #if !defined(RENDERER_GL) + float enabled = temporal_effects_enabled * taa_enabled * !debug_rendering; + #if defined(D3D11)/* || defined(D3D12)*/ // we don't have baking support for D3D12 + enabled *= !capture_cubemap; + #endif + return (halton_offsets[frame_number % NUM_HALTON_OFFSETS] / back_buffer_size) / viewport.xy * 2 * enabled; + #else + return float2(0,0); + #endif + } + #endif + """ + } +} diff --git a/vmf_source/core/stingray_renderer/shader_libraries/compute.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/compute.shader_source new file mode 100644 index 0000000..34eff58 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/compute.shader_source @@ -0,0 +1,609 @@ +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source"] + +hlsl_shaders = { + + decompress_linearize = { + + stage_conditions = { + compute = "true" + } + + + + code = """ + + #define Depth __depth_input_texture0 + #define HTile __htile_input_texture1 + #define LinearZ input_texture2 + + Texture2D Depth : register(t0); + ByteAddressBuffer HTile : register(t1); + RWTexture2D LinearZ : register(u0); + + cbuffer global_camera + { + float3 camera_near_far; + } + + + //cbuffer ConstantBuffer : register(b0) + //{ + // float ZMagic; // (zFar - zNear) / zNear + // float ZClear; + // uint2 TiledDimensions; // For 1080p, this is 240x136 + //} + + #define ZMagic ((camera_near_far.y / camera_near_far.x) / camera_near_far.x) + #define TiledDimensions uint2(208, 120) + #define ZClear (1.f) + + #define DEPTH_UINT_COUNT (64 * 4) + groupshared uint sharedDepthData[DEPTH_UINT_COUNT]; + + + // the coord for the given micro tile index + uint2 PixelCoordFromLinearIndex(uint element) + { + return uint2( + (element & 1) >> 0 | (element & 4) >> 1 | (element & 16) >> 2, + (element & 2) >> 1 | (element & 8) >> 2 | (element & 32) >> 3); + } + + uint2 SwizzleLinearIndex(uint element) + { + const uint2 lookup[64] = + { + PixelCoordFromLinearIndex(8 * 0 + 0), + PixelCoordFromLinearIndex(8 * 0 + 1), + PixelCoordFromLinearIndex(8 * 0 + 2), + PixelCoordFromLinearIndex(8 * 0 + 3), + PixelCoordFromLinearIndex(8 * 0 + 4), + PixelCoordFromLinearIndex(8 * 0 + 5), + PixelCoordFromLinearIndex(8 * 0 + 6), + PixelCoordFromLinearIndex(8 * 0 + 7), + + PixelCoordFromLinearIndex(8 * 1 + 0), + PixelCoordFromLinearIndex(8 * 1 + 1), + PixelCoordFromLinearIndex(8 * 1 + 2), + PixelCoordFromLinearIndex(8 * 1 + 3), + PixelCoordFromLinearIndex(8 * 1 + 4), + PixelCoordFromLinearIndex(8 * 1 + 5), + PixelCoordFromLinearIndex(8 * 1 + 6), + PixelCoordFromLinearIndex(8 * 1 + 7), + + PixelCoordFromLinearIndex(8 * 2 + 0), + PixelCoordFromLinearIndex(8 * 2 + 1), + PixelCoordFromLinearIndex(8 * 2 + 2), + PixelCoordFromLinearIndex(8 * 2 + 3), + PixelCoordFromLinearIndex(8 * 2 + 4), + PixelCoordFromLinearIndex(8 * 2 + 5), + PixelCoordFromLinearIndex(8 * 2 + 6), + PixelCoordFromLinearIndex(8 * 2 + 7), + + PixelCoordFromLinearIndex(8 * 3 + 0), + PixelCoordFromLinearIndex(8 * 3 + 1), + PixelCoordFromLinearIndex(8 * 3 + 2), + PixelCoordFromLinearIndex(8 * 3 + 3), + PixelCoordFromLinearIndex(8 * 3 + 4), + PixelCoordFromLinearIndex(8 * 3 + 5), + PixelCoordFromLinearIndex(8 * 3 + 6), + PixelCoordFromLinearIndex(8 * 3 + 7), + + PixelCoordFromLinearIndex(8 * 4 + 0), + PixelCoordFromLinearIndex(8 * 4 + 1), + PixelCoordFromLinearIndex(8 * 4 + 2), + PixelCoordFromLinearIndex(8 * 4 + 3), + PixelCoordFromLinearIndex(8 * 4 + 4), + PixelCoordFromLinearIndex(8 * 4 + 5), + PixelCoordFromLinearIndex(8 * 4 + 6), + PixelCoordFromLinearIndex(8 * 4 + 7), + + PixelCoordFromLinearIndex(8 * 5 + 0), + PixelCoordFromLinearIndex(8 * 5 + 1), + PixelCoordFromLinearIndex(8 * 5 + 2), + PixelCoordFromLinearIndex(8 * 5 + 3), + PixelCoordFromLinearIndex(8 * 5 + 4), + PixelCoordFromLinearIndex(8 * 5 + 5), + PixelCoordFromLinearIndex(8 * 5 + 6), + PixelCoordFromLinearIndex(8 * 5 + 7), + + PixelCoordFromLinearIndex(8 * 6 + 0), + PixelCoordFromLinearIndex(8 * 6 + 1), + PixelCoordFromLinearIndex(8 * 6 + 2), + PixelCoordFromLinearIndex(8 * 6 + 3), + PixelCoordFromLinearIndex(8 * 6 + 4), + PixelCoordFromLinearIndex(8 * 6 + 5), + PixelCoordFromLinearIndex(8 * 6 + 6), + PixelCoordFromLinearIndex(8 * 6 + 7), + + PixelCoordFromLinearIndex(8 * 7 + 0), + PixelCoordFromLinearIndex(8 * 7 + 1), + PixelCoordFromLinearIndex(8 * 7 + 2), + PixelCoordFromLinearIndex(8 * 7 + 3), + PixelCoordFromLinearIndex(8 * 7 + 4), + PixelCoordFromLinearIndex(8 * 7 + 5), + PixelCoordFromLinearIndex(8 * 7 + 6), + PixelCoordFromLinearIndex(8 * 7 + 7), + }; + + return lookup[element]; + } + + // The whole pixel coordinate, ignoring subsampling, ranges from 0 to 7 + // (in two dimensions.) + // The fixed point grid ranges from -64 to +63 with whole values representing + // sample locations. Pixels are 16 samples wide with tiles being 8 pixels wide. + // Pixel centers are naturally spaced 16 samples apart in the range [-56, +56]. + int2 GetTileRelativeCoord( uint2 pixelCoord ) + { + return pixelCoord * 16 - 56; + } + + uint ZLayoutForMask( uint zMask, uint sampleCount = 1 ) + { + uint zPlaneCount = zMask; + if (zPlaneCount >= 11) + zPlaneCount += 2; + else if (zPlaneCount >= 9) + zPlaneCount += 1; + + uint zPlaneChunkSize = zPlaneCount * 12; + + uint pMaskElementSizeBits = zMask == 0 ? 0 : firstbithigh(zMask - 1) + 1; + + uint pMaskChunkSize = 8 * pMaskElementSizeBits * sampleCount; + uint paddedZPlaneChunkSize = (zPlaneChunkSize + 0x1f) & ~0x1f; + uint paddedPMaskChunkSize = (pMaskChunkSize + 0x1f) & ~0x1f; + uint packedSize = (zPlaneChunkSize + pMaskChunkSize + 0x1f) & ~0x1f; + uint paddedSize = paddedZPlaneChunkSize + paddedPMaskChunkSize; + bool pack = packedSize < paddedSize; + + uint pMaskOffset = pack ? zPlaneChunkSize : paddedZPlaneChunkSize; + if ((pMaskOffset & 0xff) + pMaskChunkSize - 1 > 0xff) + pMaskOffset = (pMaskOffset + pMaskChunkSize - 1) & ~0xff; + else + pMaskOffset = (pMaskOffset + 0x7) & ~0x7; + + uint maxLoadIndex_WithoutZDataOffset = (pMaskOffset + pMaskChunkSize) >> 2; + + //uint zPlaneCount : 5; + //uint pMaskElementSizeBits : 3; + //uint is32ByteChunk : 1; + //uint pMaskOffsetElement : 7; + //uint maxLoadIndex_WithoutZDataOffset : 8; + + uint ld = zPlaneCount | pMaskElementSizeBits << 5; + ld |= (packedSize <= 32 ? 256 : 0); + ld |= (pMaskOffset / 4) << 9; + ld |= maxLoadIndex_WithoutZDataOffset << 16; + + return ld; + } + + + uint getZDataLayout( uint zMask, uint sampleCount = 1 ) + { + const uint lookup[16] = + { + ZLayoutForMask(0x0, sampleCount), // Clear + ZLayoutForMask(0x1, sampleCount), + ZLayoutForMask(0x2, sampleCount), + ZLayoutForMask(0x3, sampleCount), + ZLayoutForMask(0x4, sampleCount), + ZLayoutForMask(0x5, sampleCount), + ZLayoutForMask(0x6, sampleCount), + ZLayoutForMask(0x7, sampleCount), + ZLayoutForMask(0x8, sampleCount), + ZLayoutForMask(0x9, sampleCount), + ZLayoutForMask(0xA, sampleCount), + ZLayoutForMask(0xB, sampleCount), + ZLayoutForMask(0xC, sampleCount), + ZLayoutForMask(0xD, sampleCount), + ZLayoutForMask(0xE, sampleCount), + ZLayoutForMask(0xF, sampleCount), // Uncompressed + }; + + return lookup[zMask]; + } + + + // P4_16x16 + uint GetHTileAddress(uint2 tileCoord, uint2 numTiles) + { + const uint pipeCount = 4; + // cache line tiling? + const bool linearMode = true; + + // in units of tiles + const uint clWidth = 64; + const uint clHeight = 32; + const uint clSize = 512; // per pipe + + const uint clCountX = numTiles.x / clWidth; // clPitch + //const uint clCountY = numTiles.y / clHeight; + + const uint slicePitch = numTiles.x * numTiles.y / pipeCount; + const uint macroPitch = (linearMode ? numTiles.x : clWidth) / 4; + + // cache line + uint clX = tileCoord.x / clWidth; + uint clY = tileCoord.y / clHeight; + + // for 2D array and 3D textures (including cube maps) + uint tileSlice = 0; // tileZ + uint sliceOffset = slicePitch * tileSlice; + + // cache line tiling (unused for linearMode) + uint clOffset = linearMode ? 0 : (clX + clCountX * clY) * clSize; + + uint tileX0 = tileCoord.x & 1; + uint tileY0 = tileCoord.y & 1; + uint tileX1 = (tileCoord.x >> 1) & 1; + uint tileY1 = (tileCoord.y >> 1) & 1; + // specific to pipe config + uint tileOffset = (tileX1 ^ tileY0) | (tileX1 << 1); + //uint tileOffset = (tileX1 ^ tileY0) | (tileCoord.x & 2); + + // specific to pipe config + uint pipe = (tileX0 ^ tileY0 ^ tileX1) | ((tileX1 ^ tileY1) << 1); + //uint pipe = (tileX0 | (tileCoord.y & 2)) ^ tileOffset; + //uint pipe = (tileX0 | tileY1 << 1) ^ tileOffset; + + // macro (4x4 tile) tiling + uint macroX = (linearMode ? tileCoord.x : (tileCoord.x % clWidth)) / 4; + uint macroY = (linearMode ? tileCoord.y : (tileCoord.y % clHeight)) / 4; + uint macroOffset = (macroX + macroY * macroPitch) << 2 | tileOffset; + + uint tileIndex = sliceOffset + clOffset + macroOffset; + uint tileByteOffset = tileIndex << 2; + + // The 2-bit pipe value gets inserted into the tileByteOffset at bits 8 and 9 + return (tileByteOffset & ~0xff) << 2 | (pipe << 8) | (tileByteOffset & 0xff); + } + + + uint2 GetZMaskAndStencilState(ByteAddressBuffer HTileBuffer, uint2 NumTiles, uint2 TileCoord) + { + uint HTileValue = HTileBuffer.Load(GetHTileAddress(TileCoord, NumTiles)); + uint ZMask = __XB_UBFE(4, 0, HTileValue); + uint StencilState = __XB_UBFE(2, 8, HTileValue); + return uint2(ZMask, StencilState); + } + + uint GetZMask(ByteAddressBuffer HTileBuffer, uint2 NumTiles, uint2 TileCoord) + { + return GetZMaskAndStencilState(HTileBuffer, NumTiles, TileCoord).x; + } + + // If extendedPrecision = true, uses the full 28 bits of the x and y slope values + float GetDepthFromZPlane(uint3 zPlane, int2 pixelPos, bool extendedPrecision = false ) + { + int depth = __XB_IBFE(31, 0, zPlane.z); + depth = __XB_MadI24(zPlane.x >> 4, pixelPos.x, depth); + depth = __XB_MadI24(zPlane.y, pixelPos.y, depth); + + if (extendedPrecision) + { + // Use the four remaining bits of slope + int slopeX = __XB_BFI(0xF, zPlane.x, __XB_IBFE(1, 27, zPlane.x)); + int slopeY = __XB_BFI(0xF, zPlane.x >> 28, __XB_IBFE(1, 23, zPlane.y)); + depth += (int)__XB_MadI24(slopeX, pixelPos.x, (int)__XB_MulI24(slopeY, pixelPos.y)) >> 4; + } + + float scale = asfloat(((zPlane.y >> 24) - 22) << 23); + return saturate(scale * (float)depth); + } + + float GetZValue(uint zDataLayout, uint2 globalPixel, uint threadIndex, + uint numSamples = 1, uint sampleIdx = 0, int2 sampleOffset = 0 ) + { + uint zPlaneCount = __XB_UBFE(5, 0, zDataLayout); + uint pMaskElementSizeBits = __XB_UBFE(3, 5, zDataLayout); + uint is32ByteChunk = __XB_UBFE(1, 8, zDataLayout); + uint pMaskOffsetElement = __XB_UBFE(7, 9, zDataLayout); + + uint zDataOffset = (is32ByteChunk << 3) & globalPixel.x; + + GroupMemoryBarrierWithGroupSync(); + + uint zPlaneIndex = 0; + if (zPlaneCount > 1) + { + // not sure why this is needed (but it is) + if (numSamples > 1) + pMaskElementSizeBits = max(pMaskElementSizeBits, 2); + + uint element = __XB_MadU24(threadIndex, numSamples, sampleIdx); + uint bitOffset = __XB_MulU24(element, pMaskElementSizeBits); + uint PlaneIndexOffset = zDataOffset + pMaskOffsetElement + (bitOffset >> 5); + + bitOffset &= 31; + + zPlaneIndex = sharedDepthData[PlaneIndexOffset] >> bitOffset; + uint bitsRead = 32 - bitOffset; + if (pMaskElementSizeBits > bitsRead) + zPlaneIndex |= sharedDepthData[PlaneIndexOffset + 1] << bitsRead; + + zPlaneIndex &= __XB_BFM(pMaskElementSizeBits, 0); + } + + uint PlaneOffset = __XB_MadU24(zPlaneIndex, 3, zDataOffset); + uint3 zPlane = uint3(sharedDepthData[PlaneOffset], sharedDepthData[PlaneOffset + 1], sharedDepthData[PlaneOffset + 2]); + + // pixelCoord is [0, 7] + // pixelPos is [-4.0, 4.0) represented as S3.4 fixed precision + // Pixel centers are at -3.5, -2.5, ..., 2.5, 3.5 + int2 pixelPos = GetTileRelativeCoord(globalPixel & 0x7); + + // Sub-sample offsets are in fractional terms ranging from -0.5f to +0.5f. + // They must be converted to fixed precision with 4 fractional bits. + if (numSamples > 1 && (int)zPlane.z < 0) // The high bit indicates MSAA is enabled + pixelPos += sampleOffset; + + return GetDepthFromZPlane(zPlane, pixelPos); + } + + float DecompressDepth(Texture2D depthSrc, + uint zMask, uint2 st, uint threadIndex, float depthClear = 0.0 ) + { + float PreFetchDepth = depthSrc[st]; + + // Clear + if (zMask == 0) + return depthClear; + + // Uncompressed + if (zMask == 15) + return PreFetchDepth; + + // Compressed + sharedDepthData[threadIndex] = asuint(PreFetchDepth); + + return GetZValue(getZDataLayout(zMask), st, threadIndex); + } + + + inline float linearize_depth(float clip_depth) { + return (camera_near_far.y*camera_near_far.x)/(camera_near_far.y - clip_depth*(camera_near_far.y-camera_near_far.x)); + } + + float DecompressAndLinearize( uint2 tile, uint2 localCoord, uint linearIndex ) + { + uint zMask = GetZMask(HTile, TiledDimensions, tile); + uint2 st = tile << 3 | localCoord; + float depth = DecompressDepth(Depth, zMask, st, linearIndex, ZClear ); + //float dist = 1.0 / (ZMagic * depth + 1.0); + float dist = linearize_depth(depth); + LinearZ[st] = dist; + return dist; + } + + + + + [numthreads( 8, 8, 1 )] + void cs_main( uint3 Gid : SV_GroupID, uint GI : SV_GroupIndex, uint3 GTid : SV_GroupThreadID, uint3 DTid : SV_DispatchThreadID ) + { + uint linearIndex = GI & 63; + uint2 pixelCoord = SwizzleLinearIndex(linearIndex); + uint destIdx = pixelCoord.x + pixelCoord.y * 16; + uint2 tile1 = Gid.xy << 1; + + DecompressAndLinearize(tile1 | uint2(0, 0), pixelCoord, linearIndex); + DecompressAndLinearize(tile1 | uint2(1, 0), pixelCoord, linearIndex); + DecompressAndLinearize(tile1 | uint2(0, 1), pixelCoord, linearIndex); + DecompressAndLinearize(tile1 | uint2(1, 1), pixelCoord, linearIndex); + } + + """ + } + + + cs_downsample = { + + includes = [ "common" ] + + stage_conditions = { + compute = "true" + } + + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + } + + code = """ + #if defined(THREADS_4) + #define THREADX 4 + #define THREADY 4 + #endif + #if defined(THREADS_8) + #define THREADX 8 + #define THREADY 8 + #endif + #if defined(THREADS_16) + #define THREADX 16 + #define THREADY 16 + #endif + + #define GROUPTHREADS THREADX * THREADY + + #define GATHER_RED(s, uv, offset) s.tex.GatherRed(s.state, uv, offset) + #define GATHER_GREEN(s, uv, offset) s.tex.GatherGreen(s.state, uv, offset) + #define GATHER_BLUE(s, uv, offset) s.tex.GatherBlue(s.state, uv, offset) + #define GATHER_ALPHA(s, uv, offset) s.tex.GatherAlpha(s.state, uv, offset) + + #define Input input_texture0 + #define Output input_texture1 + + #define InTex __tex_input_texture0 + #define Samp __samp_input_texture0 + + #define DECLARE_SAMPLER_2D_REG(name, tr, sr) \ + Texture2D TEXTURE_NAME(name) : register(tr); \ + SamplerState SAMPLER_NAME(name) : register(sr); \ + static Sampler2D name = { TEXTURE_NAME(name), SAMPLER_NAME(name) }; + + + cbuffer c0 + { + float2 input_texture0_size_inv; + }; + + #if defined(RENDERER_D3D11) + DECLARE_SAMPLER_2D(input_texture0); + #else + #if defined(POINT) + DECLARE_SAMPLER_2D_REG(input_texture0, t0, s0); + #endif + #if defined(LINEAR) + DECLARE_SAMPLER_2D_REG(input_texture0, t0, s1); + #endif + #endif + + RWTexture2D Output: register(u0); + + #if defined(GATHER) + groupshared float4 shared_mem[GROUPTHREADS]; + #else + groupshared float3 shared_mem[GROUPTHREADS]; + #endif + + static const float inv_div = 1.0 / 16.0; + + [numthreads(THREADX, THREADY, 1)] + void cs_main(uint3 Gid : SV_GroupID, uint3 DTId : SV_DispatchThreadID, uint3 GTid : SV_GroupThreadID, uint GI : SV_GroupIndex ) + { + const float4 one = float4(1,1,1,1); + + #if defined(WRITE1) + // Every thread writes, 4x4 Threads, 100x225 Thread Groups + #if defined(LOAD) + float3 rgb = InTex.Load(DTId * 4); + #endif + #if defined(SAMPLE) + float2 uv = float2(DTId.xy * 4) * input_texture0_size_inv; + float3 rgb = TEX2DLOD(input_texture0, uv, 0); + #endif + Output[DTId.xy] = float4(rgb, 0); + #endif + + + #if defined(WRITE2) + // Every other thread writes, avarage every other pixel, Load, 8x8 Threads, 100x225 Thread Groups + #if defined(LOAD) + float3 rgb = InTex.Load(DTId * 2); + #endif + #if defined(SAMPLE) + float2 uv = float2(DTId.xy * 2) * input_texture0_size_inv; + float3 rgb = TEX2DLOD(input_texture0, uv, 0); + #endif + shared_mem[GI] = rgb; + GroupMemoryBarrierWithGroupSync(); + bool2 mask = (GTid.xy & uint2(1,1)); + shared_mem[GI] += mask.y ? 0 : shared_mem[GI + THREADX]; + shared_mem[GI] += mask.x ? 0 : shared_mem[GI + 1]; + + if(!any(mask)) { + Output[DTId.xy * 0.5] = float4(shared_mem[GI] * (1.0 / 4.0), 0); + } + #endif + + #if defined(WRITE4) + // Every forth thread writes, avarage every pixel, 16x16 Threads, 100x225 Thread Groups + #if defined(LOAD) + float3 rgb = InTex.Load(DTId); + #endif + #if defined(SAMPLE) + float2 uv = float2(DTId.xy) * input_texture0_size_inv; + float3 rgb = TEX2DLOD(input_texture0, uv, 0); + #endif + shared_mem[GI] = rgb; + GroupMemoryBarrierWithGroupSync(); + bool4 mask = (GTid.xyxy & uint4(1,1,3,3)); + shared_mem[GI] += mask.y ? 0 : shared_mem[GI + THREADX * 2]; + shared_mem[GI] += mask.w ? 0 : shared_mem[GI + THREADX]; + shared_mem[GI] += mask.x ? 0 : shared_mem[GI + 1]; + shared_mem[GI] += mask.z ? 0 : shared_mem[GI + 2]; + if(!any(mask)) { + Output[DTId.xy / 4] = float4(shared_mem[GI] * (1.0 / 16.0), 0); + } + #endif + + #if defined(GATHER) + // gather 2x2 pixels per thread, 8x8 Thread groups, 100x225 + float2 uv = float2(DTId.xy * 2) * input_texture0_size_inv; + float4 reds = GATHER_RED(input_texture0, uv, uint2(1,1)); + float4 greens = GATHER_GREEN(input_texture0, uv, uint2(1,1)); + float4 blues = GATHER_BLUE(input_texture0, uv, uint2(1,1)); + float4 alphas = GATHER_ALPHA(input_texture0, uv, uint2(1,1)); + shared_mem[GI] = float4(dot(reds, one), dot(greens, one), dot(blues, one), dot(alphas, one)); + GroupMemoryBarrierWithGroupSync(); + bool2 mask = (GTid.xy & uint2(1,1)); + shared_mem[GI] += mask.y ? 0 : shared_mem[GI + THREADX]; + shared_mem[GI] += mask.x ? 0 : shared_mem[GI + 1]; + if(!any(mask)) { + Output[DTId.xy / 2] = shared_mem[GI] * (1.0 / 16.0); + } + #endif + } + """ + } + + +} + +shaders = { + + decompress_linearize = { + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="decompress_linearize" } + ] + } + } + + compile = { + default = [ + { defines="" platforms = "D3D12" } + ] + } + } + + cs_downsample = { + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="cs_downsample" } + ] + } + } + + compile = { + default = [ + { defines="" } + ] + } + } +} + +static_compile= [ + { shader="decompress_linearize" } + + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_4" "WRITE1" "LOAD" "POINT"] } + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_4" "WRITE1" "SAMPLE" "POINT"] } + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_4" "WRITE1" "SAMPLE" "LINEAR"] } + + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "WRITE2" "LOAD" "POINT"] } + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "WRITE2" "SAMPLE" "POINT"] } + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "WRITE2" "SAMPLE" "LINEAR"] } + + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_16" "WRITE4" "LOAD" "POINT"] } + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_16" "WRITE4" "SAMPLE" "POINT"] } + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_16" "WRITE4" "SAMPLE" "LINEAR"] } + + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "GATHER" "SAMPLE" "POINT"] } + { if: "on_renderer(D3D11, D3D12)" shader="cs_downsample" defines=["THREADS_8" "GATHER" "SAMPLE" "LINEAR"] } +] \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/decals.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/decals.shader_source new file mode 100644 index 0000000..c569e9d --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/decals.shader_source @@ -0,0 +1,704 @@ +// in this context include refers to another shader file +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source" ] + +/* + TODO + Write to AMBIENT_DIFFUSE_LIGHT? + Write to AMBIENT_OCCLUSION? +*/ + +render_states = { + decal_opacity = { + inherits = "opacity" + states = { + write_mask0 = "red|green|blue" + } + } + + decal_projector_debug_draw = { + inherits = "default" + } + + decal_base_color_normal = { + inherits = "opacity" + states = { + z_func = "greater_equal" + write_mask0 = "red|green|blue" + + defined_GROUP_STENCIL_MASK = { + stencil_enable = "true" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_write_mask = "0x0" + + defined_GROUP_0 = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x60" + stencil_ref = "0x0" + } + defined_GROUP_1 = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x60" + stencil_ref = "0x20" + } + defined_GROUP_2 = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x60" + stencil_ref = "0x40" + } + defined_GROUP_3 = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x60" + stencil_ref = "0x60" + } + defined_GROUP_01 = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x40" + stencil_ref = "0x0" + } + defined_GROUP_02 = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x20" + stencil_ref = "0x0" + } + defined_GROUP_13 = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x20" + stencil_ref = "0x60" + } + defined_GROUP_23 = { + stencil_func = "equal" + stencil_func_back_side = "equal" + stencil_mask = "0x40" + stencil_ref = "0x60" + } + defined_GROUP_012 = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + stencil_mask = "0x60" + stencil_ref = "0x60" + } + defined_GROUP_123 = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + stencil_mask = "0x60" + stencil_ref = "0x0" + } + defined_GROUP_023 = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + stencil_mask = "0x60" + stencil_ref = "0x20" + } + defined_GROUP_013 = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + stencil_mask = "0x60" + stencil_ref = "0x40" + } + } + } + } + + decal_pre_pass = { + inherits = "opacity" + states = { + z_enable = "true" + z_func = "greater_equal" + write_mask0 = "" + stencil_enable = "true" + + stencil_write_mask = "0x60" + stencil_mask = "0x60" + stencil_ref = "0x0" + + stencil_func = "always" + stencil_func_back_side = "always" + + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_replace" + + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_replace" + } + } + + decal_roughness_metallic_additive = { + inherits = "decal_base_color_normal" + states = { + write_mask0 = "alpha" + blend_op = "blend_op_add" + + defined_WRITES_ROUGHNESS = { + defined_ROUGHNESS_THRESHOLD = { + blend_enable = "false" + } + ndefined_ROUGHNESS_THRESHOLD = { + src_blend = "blend_one" + dest_blend = "blend_one" + } + } + defined_WRITES_METALLIC = { + src_blend = "blend_one" + dest_blend = "blend_one" + } + } + } +} + +hlsl_shaders = { + decal = { + includes = [ "common", "gbuffer_access" ] + + samplers = { + defined_DIFFUSE_MAP = { + diffuse_map = { sampler_states = "wrap_anisotropic_srgb" } + } + + defined_WRITES_NORMAL = { + normal_map = { sampler_states = "wrap_anisotropic" } + } + } + + code=""" + #if defined(DIFFUSE_MAP) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + #endif + + #if defined(WRITES_NORMAL) + DECLARE_SAMPLER_2D(normal_map); // exports={ name="Normal Map" type="resource" } + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + float decal_instance: TEXCOORD1; + #if defined(WRITES_NORMAL) + float3 tangent : TANGENT; + float3 binormal : BINORMAL; + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float3 uv_fade : TEXCOORD0; + #if defined(WRITES_NORMAL) + float3 tsm0 : TEXCOORD1; + float3 tsm1 : TEXCOORD2; + float3 tsm2 : TEXCOORD3; + #endif + }; + + #define MAX_INSTANCES_PER_BATCH 50 + CBUFFER_START(c0) + float4x4 world_view_proj; + float4x4 instance_data[MAX_INSTANCES_PER_BATCH]; + float2 sustain_release; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + int decal_instance = (int)floor(((frac((float)input.decal_instance / (float)MAX_INSTANCES_PER_BATCH)) * MAX_INSTANCES_PER_BATCH) + 0.5); + float4x4 decal_tm = instance_data[decal_instance]; + float t = decal_tm._m33; + //float3 custom = decal_tm._m03_m13_m23; + decal_tm._m03_m13_m23 = float3(0,0,0); + decal_tm._m33 = t == 0 ? 0 : 1; + + float4 position = input.position; + position = mul(position, decal_tm); + o.position = mul(position, world_view_proj); + o.position.z -= 0.001f; + + float fade = (t > sustain_release.x ? (t < (sustain_release.x + sustain_release.y) ? 1-saturate((t-sustain_release.x) / sustain_release.y) : 0) : 1); + o.uv_fade = float3(input.uv, fade); + + #if defined(WRITES_NORMAL) + float3 tangent = input.tangent; + float3 binormal = input.binormal; + float3 normal = cross(binormal, tangent); + tangent = -tangent; + + tspace_transform_transpose(o.tsm0, o.tsm1, o.tsm2, tangent, binormal, normal, (float3x3)decal_tm); + #endif + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + { + float2 uv = input.uv_fade.xy; + float alpha = input.uv_fade.z; + + #if defined(DIFFUSE_MAP) + half4 base_color_alpha = TEX2D(diffuse_map, uv); + float3 base_color = base_color_alpha.rgb; + alpha *= base_color_alpha.a; + #endif + + #if defined(WRITES_NORMAL) + float3 tnormal = decode_normal_map(TEX2D(normal_map, uv)); + float3 world_space_normal = rotate_vector3(tnormal, (float3)input.tsm0, (float3)input.tsm1, (float3)input.tsm2); + return half4(gbuffer_encode_normal(world_space_normal), alpha); + #elif defined(WRITES_BASE_COLOR) + return half4(gbuffer_encode_base_color(base_color), alpha); + #endif + } + """ + } + + decal_projector_fatshark = { + includes = [ "common", "gbuffer_access" ] + + samplers = { + linear_depth = { sampler_states = "clamp_point" } + + defined_DIFFUSE_MAP = { + diffuse_map = { sampler_states = "clamp_anisotropic" } + } + defined_NORMAL_MAP = { + normal_map = { sampler_states = "clamp_anisotropic" } + } + defined_ANGULAR_THRESHOLD = { + //defined_PRE_PASS = { + normals_copy = { sampler_states = "clamp_linear" } + //} + } + } + + code=""" + DECLARE_SAMPLER_2D(linear_depth); + #if defined(DIFFUSE_MAP) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" sort_tag="0_DIFFUSE_MAP"} + #endif + + #if defined(NORMAL_MAP) + DECLARE_SAMPLER_2D(normal_map); // exports={ name="Normal Map" type="resource" sort_tag="0_NORMAL_MAP"} + #endif + + #if defined(ANGULAR_THRESHOLD) //&& defined(PRE_PASS) + DECLARE_SAMPLER_2D(normals_copy); + #endif + + struct VS_INPUT { + float4 position : POSITION; + #if defined(WRITES_NORMAL) + float3 tangent : TANGENT; + float3 binormal : BINORMAL; + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 w : TEXCOORD0; + #if defined(WRITES_NORMAL) + float3 tsm0 : TEXCOORD1; + float3 tsm1 : TEXCOORD2; + float3 tsm2 : TEXCOORD3; + #endif + }; + + CBUFFER_START(c0) + float4x4 inv_world; + float4x4 world_view_proj; + float4x4 world; + + float4x4 bounding_volume; + + #if defined(COLOR_ALPHA_FADE) + float alpha; + #endif + + #if defined(DIFFUSE_MAP) || defined(NORMAL_MAP) + float2 uv_scale; // exports={ name="UV Scale" type="vector2" value=[1 1] min=[0 0] max=[10 10] step=[0.01 0.01] } + float2 uv_offset; // exports={ name="UV Offset" type="vector2" value=[0 0] min=[0 0] max=[1 1] step=[0.01 0.01] } + float diffuse_alpha; // exports={ name="Diffuse Alpha" type="scalar" value=1 min=0 max=1 step=0.01 } + float normal_alpha; // exports={ name="Normal Alpha" type="scalar" value=1 min=0 max=1 step=0.01 } + + #if defined(UV_ROTATION_ANIM) + float2 uv_rotation_pivot; // exports={ name="UV Rotation Pivot" type="vector2" value=[0.5 0.5] min=[-1.0 -1.0] max=[1.0 1.0] step=[0.001 0.001] } + float2 uv_rotation_speed; // exports={ name="UV Rotation Speed" type="scalar" value=1.57 min=-6.28 max=6.28 step=0.001 } + #elif defined(TEXTURE_FILM) + float2 frame_range; // exports={ name="Frame range" type="vector2" value=[0.0 4.0] min=[0.0 0.0] max=[128.0 128.0] step=[1.0 1.0] } + float2 frame_size; // exports={ name="Frame size" type="vector2" value=[0.25 0.25] min=[0.005 0.005] max=[1.0 1.0] step=[0.001 0.001] } + float frame_fps; // exports={ name="Frames per second" type="scalar" value=1.0 min=1.0 max=120.0 step=1.0 } + #endif + #endif + + #if defined(ANGULAR_THRESHOLD) + float angular_threshold; // exports={ name="Angular Threshold Value [radians]" type="scalar" value=0.35 min=0.0 max=1.0 step=0.001 } + #endif + + #if defined(HEIGHT_THRESHOLD) + float height_threshold; // exports={ name="Height Threshold Value [meter]" type="scalar" value=0.5 min=-0.5 max=1.0 step=0.001 } + #endif + + #if defined(DIFFUSE_MAP) && defined(TINT_COLOR) + float3 color; // exports = { name="Diffuse Tint" type="vector3" min=[0 0 0] max=[1 1 1] step=[0.01 0.01 0.01] value=[0.5 0.5 0.5] } + #endif + + #if defined(GLOSSINESS_SPECULAR) + float2 gloss_spec; // exports={ name="Roughness/Metallic" type="vector2" value=[0.5 0.04] min=[0.0 0.04] max=[1.0 1.0] step=[0.001 0.96] } + + #if defined( ROUGHNESS_THRESHOLD ) + float roughness_threshold; // exports={ name="Roughness Threshold" type="scalar" value=0.1 min=0.0 max=1.0 step=0.001 } + #endif + #endif + CBUFFER_END + + float2 uv_pass_through_modifier(float2 uv) { + return uv; + } + + #if defined(UV_ROTATION_ANIM) + float2 uv_rotation_anim_modifier(float2 uv) { + float a = uv_rotation_speed * time; + float c = cos(a); + float s = sin(a); + float2 center = uv - uv_rotation_pivot; + return float2( (center.x * c + center.y * s) + uv_rotation_pivot.x, + (center.y * c - center.x * s) + uv_rotation_pivot.y); + } + #elif defined(TEXTURE_FILM) + float2 uv_texture_film_modifier(float2 uv) { + float frames_x = floor((1/frame_size.x) + 0.5); + float frames_y = floor((1/frame_size.y) + 0.5); + + float frame_time = 1.0f/frame_fps; + + float current_frame = frame_range.x + fmod(floor(time/frame_time + 0.5), (frame_range.y - frame_range.x)); + + float uv_x = floor(fmod(current_frame,frames_x) + 0.5) * frame_size.x + (1.0 - uv.x)/frames_x; + float uv_y = floor(current_frame/frames_y) * frame_size.y + uv.y/frames_y; + + return float2(uv_x, uv_y); + + } + #endif + + #if defined(DIFFUSE_MAP) || defined(NORMAL_MAP) + #if defined(TEXTURE_FILM) + #define uv_modifier uv_texture_film_modifier + #elif defined(UV_ROTATION_ANIM) + #define uv_modifier uv_rotation_anim_modifier + #else + #define uv_modifier uv_pass_through_modifier + #endif + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + o.position = mul(input.position, world_view_proj); + o.w = encode_world_pos(o.position, camera_unprojection); + + #if defined(WRITES_NORMAL) + float3 tangent = float3(1,0,0); + float3 binormal = float3(0,0,1); + float3 normal = float3(0,-1,0); + + tspace_transform_transpose(o.tsm0, o.tsm1, o.tsm2, tangent, binormal, normal, (float3x3)world); + #endif + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input + #if defined(GL2) + , float4 wpos : WPOS + #endif + ) : SV_TARGET0 + { + #if defined(GL2) + half2 screen_uv = wpos.xy / back_buffer_size; + #else + half2 screen_uv = input.position.xy / back_buffer_size; + #endif + + float3 wp = decode_world_pos(input.w, gbuffer_decode_depth(TEX2D(linear_depth, screen_uv))); + float3 op = mul(float4(wp, 1), inv_world); + float3 containment = (op > bounding_volume._m00_m01_m02) * (op < bounding_volume._m10_m11_m12); + float mask = dot(containment, containment) == 3; + + [branch] + if( mask == 0 ) + discard; + + #if defined(COLOR_ALPHA_FADE) + mask *= alpha; + #endif + + float2 uv = (op.xz / (bounding_volume._m20_m22*0.5))*0.5+0.5; + uv.y = 1 - uv.y; + uv = uv_modifier(uv); + uv = uv * uv_scale + uv_offset; + + #if defined(ANGULAR_THRESHOLD) || defined(HEIGHT_THRESHOLD) + float3 up_vector = normalize(-world._m10_m11_m12); + #endif + + float threshold_multiplier = 1; + #if defined(ANGULAR_THRESHOLD) + float2 sample_uv = input.position.xy / back_buffer_size; + float4 world_normal_data = TEX2D(normals_copy, sample_uv); + float3 world_normal = normalize(gbuffer_decode_normal(world_normal_data)); + #if defined(ANGULAR_THRESHOLD_SOFT_FADE) + // TODO: add options to decrease the fade area. + threshold_multiplier *= saturate((dot(up_vector, world_normal) - angular_threshold) / (1.0 - angular_threshold)); + #else + threshold_multiplier *= dot(up_vector, world_normal) <= angular_threshold ? 0.0 : 1.0; + #endif + #endif + #if defined(HEIGHT_THRESHOLD) + // TODO: in progress + float3 wp_bottom = world._m30_m31_m32; + float height = dot(wp - wp_bottom, up_vector); + threshold_multiplier *= height > height_threshold ? 0.0 : 1.0; + #endif + + + #if defined(WRITES_BASE_COLOR) + float4 base_color = TEX2D(diffuse_map, uv); + + #if defined(USE_NORMAL_ALPHA) + base_color.a = TEX2D(normal_map, uv).a; + #endif + + base_color.a *= diffuse_alpha; + + #if defined(TINT_COLOR) + base_color.rgb *= color.rgb; + #endif + + base_color.rgb = gbuffer_encode_base_color(base_color.rgb); + return base_color * float4(1, 1, 1, mask * threshold_multiplier); + #endif + + #if defined(WRITES_NORMAL) + float4 normal_data = TEX2D(normal_map, uv); + float3 tnormal = decode_normal_map(normal_data); + #if defined(INVERT_NORMAL_G) + tnormal.g = -tnormal.g; + #endif + + float alpha = 1; + #if defined(USE_NORMAL_ALPHA) + alpha = normal_data.a; + #elif defined(DIFFUSE_MAP) + alpha = TEX2D( diffuse_map, uv).a; + #endif + + float3 world_space_normal = rotate_vector3(tnormal, (float3)input.tsm0, (float3)input.tsm1, (float3)input.tsm2); + + return half4(gbuffer_encode_normal(world_space_normal), alpha * normal_alpha * mask * threshold_multiplier); + #endif + + #if defined(WRITES_ROUGHNESS) + float roughness = 1.0 - gloss_spec.x; // Note: Roughness ~ 1 - glossniess + + float alpha = 1.0; + #if defined(USE_NORMAL_ALPHA) && defined(NORMAL_MAP) + alpha = TEX2D(normal_map, uv).a; + #elif defined(DIFFUSE_MAP) + alpha = TEX2D(diffuse_map, uv).a; + #endif + + #if defined(ROUGHNESS_THRESHOLD) + [branch] + if( (threshold_multiplier * alpha) < roughness_threshold ) + discard; + #endif + + #if defined(ANGULAR_THRESHOLD) + return half4( 0, 0, 0, gbuffer_encode_roughness( lerp(saturate(world_normal_data.a), roughness, alpha * mask * threshold_multiplier ) ) ); + #else + return half4( 0, 0, 0, gbuffer_encode_roughness( alpha * roughness * mask * threshold_multiplier ) ); + #endif + #endif + + #if defined(WRITES_METALLIC) + float alpha = 1.0; + #if defined(USE_NORMAL_ALPHA) && defined(NORMAL_MAP) + alpha = TEX2D(normal_map, uv).a; + #elif defined(DIFFUSE_MAP) + alpha = TEX2D(diffuse_map, uv).a; + #endif + + return half4( 0, 0, 0, gbuffer_encode_metallic_mask( alpha * gloss_spec.y * mask * threshold_multiplier ) ); + #endif + } + """ + } +} + +shaders = { + decal = { + editor_options = [ + { + name="Texture Layers" + options = [ + { name="Base Color Map" define="DIFFUSE_MAP" } + { name="Normal Map" define="NORMAL_MAP" } + ] + } + ] + + contexts = { + default = { + passes = [ + { + defined="DIFFUSE_MAP" + pass = [ + { layer="decal_base_color_metallic" hlsl_shader="decal" defines="WRITES_BASE_COLOR" render_states="decal_opacity" } + ] + } + { + defined="NORMAL_MAP" + pass = [ + { layer="decal_normal_roughness" hlsl_shader="decal" defines="WRITES_NORMAL" render_states="decal_opacity" } + ] + } + ] + } + } + + compile = { + default = [ + { defines="" platforms = "D3D11 D3D12 GL2 GNM"} + ] + } + } + + decal_projector_fatshark = { + editor_options = [ + { + name="Texture Layers" + options = [ + { name="Base Color Map" define="DIFFUSE_MAP" } + { name="Normal Map" define="NORMAL_MAP" } + { name="Roughness/Metallic" define="GLOSSINESS_SPECULAR" } + { name="Roughness Threshold" define="ROUGHNESS_THRESHOLD" condition="GLOSSINESS_SPECULAR"} + { name="Use Normal Alpha as Alpha" define="USE_NORMAL_ALPHA" } + ] + } + + { + name = "Color" + options = [ + { name="Base Color Tint" define="TINT_COLOR" } + ] + } + + { + name="UV Manipulation" + options = [ + { name="Texture Film" define="TEXTURE_FILM" } + { name="UV Rotation" define="UV_ROTATION_ANIM" } + ] + } + + { + name="Settings" + options = [ + { name="Debug Draw Projection Box" define="DEBUG_DRAW" } + { name="Invert Normal Green" define="INVERT_NORMAL_G" } + { name="Angular Threshold" define="ANGULAR_THRESHOLD" } + { name=" - Soft Fade" define="ANGULAR_THRESHOLD_SOFT_FADE" condition="ANGULAR_THRESHOLD" } + { name="Height Threshold" define="HEIGHT_THRESHOLD" } + ] + } + + { + name = "Particle Options" + options = [ + { name="Fade with Color Alpha" define="COLOR_ALPHA_FADE" } + ] + } + + { + name="Material Settings" + options = [ + { name="Use Group Mask" define="GROUP_STENCIL_MASK" } + { name="Decal Group 0" define="GROUP_0" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 1" define="GROUP_1" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 2" define="GROUP_2" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 3" define="GROUP_3" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 0, 1" define="GROUP_01" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 0, 2" define="GROUP_02" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 1, 3" define="GROUP_13" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 2, 3" define="GROUP_23" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 0, 1, 2" define="GROUP_012" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 1, 2, 3" define="GROUP_123" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 0, 2, 3" define="GROUP_023" condition="GROUP_STENCIL_MASK" } + { name="Decal Group 0, 1, 3" define="GROUP_013" condition="GROUP_STENCIL_MASK" } + ] + } + ] + + contexts = { + default = { + passes = [ + { + defined = "DEBUG_DRAW" + pass = [ + { layer="decal_base_color_metallic" hlsl_shader="decal_projector_fatshark" defines="WRITES_BASE_COLOR" render_states="decal_projector_debug_draw" } + ] + fail = [ + //{ layer="decal_pre_pass" hlsl_shader="decal_projector_fatshark" defines="PRE_PASS" render_states="decal_pre_pass" } + + { + defined="DIFFUSE_MAP" + pass = [ + { layer="decal_base_color_metallic" hlsl_shader="decal_projector_fatshark" defines="WRITES_BASE_COLOR" render_states="decal_base_color_normal" } + ] + } + { + defined="NORMAL_MAP" + pass = [ + { layer="decal_normal_roughness" hlsl_shader="decal_projector_fatshark" defines="WRITES_NORMAL" render_states="decal_base_color_normal" } + ] + } + { + defined="GLOSSINESS_SPECULAR" + pass = [ + { layer="decal_normal_roughness" hlsl_shader="decal_projector_fatshark" defines="WRITES_ROUGHNESS" render_states="decal_roughness_metallic_additive" } + //{ layer="decal_base_color_metallic" hlsl_shader="decal_projector_fatshark" defines="WRITES_METALLIC" render_states="decal_roughness_metallic_additive" } + ] + } + ] + } + ] + } + } + + compile = { + default = [ + { defines="" platforms = "D3D11 D3D12 GL2 GNM"} + ] + } + } +} + +static_compile= [ + { shader="decal" defines="DIFFUSE_MAP NORMAL_MAP" } +] diff --git a/vmf_source/core/stingray_renderer/shader_libraries/default_shaders.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/default_shaders.shader_source new file mode 100644 index 0000000..190e0d1 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/default_shaders.shader_source @@ -0,0 +1,512 @@ +includes = ["core/stingray_renderer/shader_libraries/common.shader_source"] + +render_states = { + line_object = { + inherits = "opacity" + states = { + defined_DEPTH_TEST_DISABLED = { + z_enable = "false" + } + } + } + + gui = { + inherits = "opacity" + states = { + ndefined_DEPTH_TEST_ENABLED = { + z_enable = "false" + } + z_write_enable = "false" + defined_WRITE_MASK = { + blend_enable = "false" + write_mask0 = "alpha" + } + defined_MASKED = { + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_dest_alpha" + src_blend = "blend_dest_alpha" + } + } + } + + + missing_shader = { + inherits = "opacity" + states = { + cull_mode = "cull_none" + } + } + +} + +hlsl_shaders = { + gui = { + includes = [ "common" ] + + samplers = { + defined_DIFFUSE_MAP = { + defined_TILE_DIFFUSE_MAP = { + defined_POINT_SAMPLE = { + diffuse_map = { sampler_states = "wrap_point" } + } + ndefined_POINT_SAMPLE = { + diffuse_map = { sampler_states = "wrap_linear" } + } + } + ndefined_TILE_DIFFUSE_MAP = { + defined_POINT_SAMPLE = { + diffuse_map = { sampler_states = "clamp_point" } + } + ndefined_POINT_SAMPLE = { + diffuse_map = { sampler_states = "clamp_linear" } + } + } + } + defined_YUV_VIDEO = { + diffuse_map = { sampler_states = "wrap_linear" } + normal_map = { sampler_states = "wrap_linear" } + } + defined_BINK_VIDEO = { + diffuse_map = { sampler_states = "wrap_linear" } + normal_map = { sampler_states = "wrap_linear" } + material_map = { sampler_states = "wrap_linear" } + } + } + + vp_code = """ + PRECISION HIGHP float; + + uniform mat4 world_view_proj; + + in vec4 position0; + in lowp vec4 color0; + out lowp vec4 v_color; + + #ifdef DIFFUSE_MAP + in vec2 texcoord0; + out vec2 v_texcoord; + #endif + + void main() { + #ifdef DIFFUSE_MAP + v_texcoord = texcoord0; + #endif + v_color = color0.bgra; + + gl_Position = position0 * world_view_proj; + } + """ + + fp_code = """ + #ifdef DIFFUSE_MAP + uniform sampler2D diffuse_map; + in vec2 v_texcoord; + #endif + + in LOWP vec4 v_color; + + #ifdef ONE_BIT_ALPHA + #define ONE_BIT_ALPHA_REF 0.5 + #endif + + layout(location = 0) out lowp vec4 color; + + void main() { + LOWP vec4 col = v_color; + #ifdef DIFFUSE_MAP + LOWP vec4 diffuse = texture(diffuse_map, v_texcoord); + #ifdef ONE_BIT_ALPHA + if (diffuse.a < ONE_BIT_ALPHA_REF) + discard; + + col.rgb *= diffuse.rgb; + #else + col *= diffuse; + #endif + + #endif + + #if defined(MASKED) + col.rgb *= col.a; + #endif + + color = col; + } + """ + + code=""" + #define RWGT 0.3086 + #define GWGT 0.6094 + #define BWGT 0.0820 + #define SAT_MAT(s) float3x3( \ + (1.0 - s) * RWGT + s, \ + (1.0 - s) * RWGT, \ + (1.0 - s) * RWGT, \ + (1.0 - s) * GWGT, \ + (1.0 - s) * GWGT + s, \ + (1.0 - s) * GWGT, \ + (1.0 - s) * BWGT, \ + (1.0 - s) * BWGT, \ + (1.0 - s) * BWGT + s \ + ) + + #if defined(DIFFUSE_MAP) || defined(YUV_VIDEO) || defined(BINK_VIDEO) + #define UV0 + #endif + + #if defined(DIFFUSE_MAP) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + #endif + + #if defined(YUV_VIDEO) || defined(BINK_VIDEO) + DECLARE_SAMPLER_2D(diffuse_map); + DECLARE_SAMPLER_2D(normal_map); + #endif + + #if defined(BINK_VIDEO) + DECLARE_SAMPLER_2D(material_map); + #endif + + #define TAA_GUI_DEPTH_BIAS_RANGE 50.0 // The [0, TAA_GUI_DEPTH_BIAS_RANGE] depth range for which we lerp the min-max depth biases for line drawing + #define TAA_GUI_MIN_DEPTH_BIAS 0.001 // The depth offset to add for lines drawn at z = 0 + #define TAA_GUI_MAX_DEPTH_BIAS 0.05 // The depth offset to add for lines drawn at z >= GUI_DEPTH_BIAS_RANGE + + struct VS_INPUT { + float4 position : POSITION; + float4 color : COLOR; + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(SELECTABLE) + float4 color : TEXCOORD1; + #else + float4 color : COLOR; + #endif + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float threshold_fade; // exports = { name="Threshold Fade" type="scalar" value=0.05 min=0.0 max=1 step=0.001 } + #if defined(CLOCK_MASK) + float start_angle; // exports = { name="Start angle" type="scalar" value=0.0 min=0.0 max=360.0 step=0.1 } + float masked_angle; // exports = { name="Masked angle" type="scalar" value=0.0 min=-360.0 max=360.0 step=0.1 } + float2 origin; // exports = { name="Origin" type="vector2" value=[0 0] min=[-256 -256] max=[256 256] step=[1 1] } + #endif + #if defined(SATURATE) + float2 saturate_params; // exports = { name="Saturate Amount/Intensity" type="vector2" value=[0 1] min=[0 0] max=[1 10] step=[0.001 0.001] } + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + + // When using temporal antialiasing we add a z offset to all drawn gui. This is done to minimize the flickering that can + // happen when compositing non jittered gui elements with a jittered depth buffer. + #if defined(LINE_OBJECT) && !defined(DEPTH_TEST_DISABLED) + o.position.z -= lerp(TAA_GUI_MIN_DEPTH_BIAS, TAA_GUI_MAX_DEPTH_BIAS, saturate((o.position.z / o.position.w) / TAA_GUI_DEPTH_BIAS_RANGE)) * taa_enabled; + #endif + o.color = decode_vertex_color(input.color); + #if defined(SELECTABLE) + o.color *= float4(PI * 2.0 * 255.0/256.0, 4.0, 4.0, 1.0); + #endif + #if defined(UV0) + o.uv = input.uv; + #endif + return o; + } + + float3 yuv_to_rgb(float3 yuv) { + float y = 1.1643*(yuv.x-0.0625); + float u = yuv.y - 0.5; + float v = yuv.z - 0.5; + float r = y + 1.5958*v; + float g = y - 0.39173*u-0.81290*v; + float b = y + 2.017*u; + return float3(r,g,b); + } + + float3 ycbcr_to_rgb(float3 ycbcr) { + float y = 1.164 * (ycbcr.x - 16.0 / 256.0); + float r = y + 1.596 * (ycbcr.z - 0.5); + float g = y - 0.813 * (ycbcr.z - 0.5) - 0.391 * (ycbcr.y - 0.5); + float b = y + 2.018 * (ycbcr.y - 0.5); + return float3(r,g,b); + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + #if defined(WRITE_MASK) + #if defined(DIFFUSE_MAP) + float mask = TEX2D(diffuse_map, input.uv).a; + #else + float mask = input.color.a; + #endif + #if defined(THRESHOLD_MASK) + mask = input.color.a > mask ? saturate((input.color.a - mask) / threshold_fade) : 0; + #endif + return float4(0,0,0,mask); + #else + #if defined(SELECTABLE) + float4 c = float4( 1, 1, 1, input.color.a ); + #else + float4 c = input.color; + #endif + + #if defined(YUV_VIDEO) + float y = TEX2D(diffuse_map, input.uv).r; + float2 uv = TEX2D(normal_map, input.uv).rg; + c *= float4(yuv_to_rgb(float3(y,uv)), 1); + #elif defined(BINK_VIDEO) + float y = TEX2D(diffuse_map, input.uv).r; + float b = TEX2D(normal_map, input.uv).r; + float r = TEX2D(material_map, input.uv).r; + + c *= float4(ycbcr_to_rgb(float3(y,b,r)), 1); + #elif defined(DIFFUSE_MAP) + #ifdef NORMAL_TO_DIFFUSE + float4 diffuse = TEX2D(diffuse_map, input.uv).rgaa; + #else + float4 diffuse = TEX2D(diffuse_map, input.uv); + #endif + #if defined(ONE_BIT_ALPHA) + one_bit_alpha_mask(diffuse.a, 0.5); + c.rgb *= diffuse.rgb; + #else + c *= diffuse; + #if defined(ALPHA_MUL_COLOR_INTENSITY) + c.a *= saturate(length(diffuse.rgb)); + #endif + #endif + #endif + + #if defined(MASKED) + c.rgb *= c.a; + #endif + + #if defined(GRAYSCALE) + float intensity = (c.r + c.g + c.b) / 3.0f; + c.rgb = intensity; + #elif defined(SATURATE) + float intensity = dot(c.rgb, 0.33 * saturate_params.y); + c.rgb = lerp(c.rgb, intensity, saturate_params.x); + #endif + + #if defined(CLOCK_MASK) + float2 center = float2( 0.5, 0.5 ); + float2 pos = input.uv; + + float2 delta = pos - center; + float dist = length( delta ); + float2 dir = delta / dist; + + if (dist > 0.0000001) + { + float angle = atan2( dir.y, dir.x ); + angle *= 180 / PI; + angle += 720; + angle -= start_angle; + #if defined(GL2) + angle = frac(angle/360) * 360; + #else + angle %= 360; + #endif + if (masked_angle >= 0) + c.a *= (angle >= masked_angle); + else + c.a *= (360-angle >= -masked_angle); + } + #endif + + #if defined(SELECTABLE) + // TODO: Add Hue rotation if demand high enough. + c.rgb = mul( c.rgb, SAT_MAT(input.color.y) ) * input.color.z; + #endif + + return c; + #endif + } + """ + } + + missing_shader = { + includes = ["common"] + + code=""" + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 color : COLOR; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + o.position = mul(input.position, world_view_proj); + o.color = float4(1, 0, 1, 0.4 + 0.6 * (sin(time*5.0)*0.5 + 0.5)); + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return input.color; + } + """ + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + + out vec4 v_col; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + void main() { + gl_Position = in_pos * world_view_proj; + v_col = vec4(1, 0, 1, 0.4 + 0.6*(sin(time*5.0)*0.5 + 0.5)); + } + """ + + fp_code = """ + layout(location = 0) out vec4 out_color; + + in vec4 v_col; + void main() { + out_color = v_col; + } + """ + } +} + +shaders = { + line_object = { + editor_advanced_mode = true + + editor_options = [ + { + name="Depth Testing" + options = [ + { name="Disable Depth Testing" define="DEPTH_TEST_DISABLED" } + ] + } + ] + + contexts = { + default = { + passes = [ + { layer="transparent" hlsl_shader="gui" render_states="line_object" defines="LINE_OBJECT" } + ] + } + } + + compile = { + default = [ + { if: "on_renderer(D3D11, D3D12, GL, GNM)" } + ] + } + } + + + gui = { + editor_options = [ + { + name="Pixel Modifiers" + options = [ + { name="Diffuse Map" define="DIFFUSE_MAP" } + { name="YUV Video Decode" define="YUV_VIDEO" tool_tip="Does YUV->RGB conversion (Y in diffuse_map slot, UV in normal_map slot).\nTo be used together with VideoPlayer" } + { name="Bink Video Decode" define="BINK_VIDEO" tool_tip="Use together with VideoPlayer" } + { name="One Bit Alpha" define="ONE_BIT_ALPHA" } + { name="Normal Map to Diffuse Map" define="NORMAL_TO_DIFFUSE" tool_tip="Sample RGAA to display normal maps as diffuse maps."} + { name="Grayscale" define="GRAYSCALE" } + { name="Saturate" define="SATURATE" } + ] + } + { + name="Masking" + options = [ + { name="Write Transparency Mask" define="WRITE_MASK" } + { name="Threshold Mask" define="THRESHOLD_MASK" } + { name="Transparency Masked" define="MASKED" } + ] + } + { + name="Depth Testing" + options = [ + { name="Depth Testing Enabled" define="DEPTH_TEST_ENABLED" } + ] + } + ] + + contexts = { + default = { + passes = [ + { layer="transparent" hlsl_shader="gui" render_states="gui" } + ] + } + } + + compile = { + default = [ + { if: "on_renderer(D3D11, D3D12, GL, GNM)" } + ] + } + } + + missing_shader = { + editor_advanced_mode = true + + contexts = { + default = { + passes = [ + { layer="transparent" hlsl_shader="missing_shader" render_states="missing_shader" } + ] + } + shadow_caster = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="missing_shader" render_states="shadow_caster" } + ] + } + } + + compile = { + default = [ + { if: "on_renderer(D3D11, D3D12, GL, GNM)" } + ] + + shadow_caster = [ + { if: "on_renderer(D3D11, D3D12, GL, GNM)" } + ] + } + } +} + +static_compile = [ + { shader="line_object" defines=[""] } + { shader="line_object" defines=["DEPTH_TEST_DISABLED"] } + { shader="gui" defines=[""] } + { shader="gui" defines=["DIFFUSE_MAP"] } + { shader="gui" defines=["DEPTH_TEST_ENABLED"] } + { shader="gui" defines=["DEPTH_TEST_ENABLED" "DIFFUSE_MAP"] } + { shader="gui" defines=["DIFFUSE_MAP" "ONE_BIT_ALPHA"] } + { shader="gui" defines=["DEPTH_TEST_ENABLED" "DIFFUSE_MAP" "ONE_BIT_ALPHA"] } + { shader="missing_shader" } +] diff --git a/vmf_source/core/stingray_renderer/shader_libraries/default_shaders_mobile.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/default_shaders_mobile.shader_source new file mode 100644 index 0000000..17d6216 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/default_shaders_mobile.shader_source @@ -0,0 +1,160 @@ +hlsl_shaders = { + mobile_line_object = { + includes = [ "common" ] + + vp_code = """ + uniform mat4 world; + uniform mat4 view_proj; + + in vec3 position0; + in lowp vec3 color0; + + out lowp vec4 v_color; + + void main() { + v_color = color0.bgra; + gl_Position = vec4(position0.x, position0.y, position0.z, 1.0) * world * view_proj; + } + """ + + fp_code = """ + in lowp vec4 v_color; + + layout(location = 0) out lowp vec4 color; + void main() { + color = v_color; + } + """ + } + + mobile_gui = { + includes = [ "common" ] + + samplers = { + defined_DIFFUSE_MAP = { + diffuse_map = { sampler_states = "clamp_linear" } + } + } + + vp_code = """ + PRECISION HIGHP float; + + uniform mat4 world_view_proj; + + in vec4 position0; + in lowp vec4 color0; + out lowp vec4 v_color; + + #ifdef DIFFUSE_MAP + in vec2 texcoord0; + out vec2 v_texcoord; + #endif + + void main() { + #ifdef DIFFUSE_MAP + v_texcoord = texcoord0; + #endif + v_color = color0.bgra; + + gl_Position = position0 * world_view_proj; + } + """ + + fp_code = """ + #ifdef DIFFUSE_MAP + uniform sampler2D diffuse_map; + in vec2 v_texcoord; + #endif + + in LOWP vec4 v_color; + + #ifdef ONE_BIT_ALPHA + #define ONE_BIT_ALPHA_REF 0.5 + #endif + + layout(location = 0) out lowp vec4 color; + + void main() { + LOWP vec4 col = v_color; + #ifdef DIFFUSE_MAP + LOWP vec4 diffuse = texture(diffuse_map, v_texcoord); + #ifdef ONE_BIT_ALPHA + if (diffuse.a < ONE_BIT_ALPHA_REF) + discard; + + col.rgb *= diffuse.rgb; + #else + col *= diffuse; + #endif + + #endif + + #if defined(MASKED) + col.rgb *= col.a; + #endif + + color = col; + } + """ + + code=""" + #if defined(DIFFUSE_MAP) + #define UV0 + #endif + + #if defined(DIFFUSE_MAP) + sampler2D diffuse_map; // exports={ name="Diffuse Map" type="resource" } + #endif + + struct VS_INPUT { + float4 position : POSITION; + float4 color : COLOR; + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 color : COLOR; + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.color = decode_vertex_color(input.color); + #if defined(UV0) + o.uv = input.uv; + #endif + return o; + } + + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float4 c = input.color; + + #if defined(DIFFUSE_MAP) + float4 diffuse = tex2D(diffuse_map, input.uv); + #if defined(ONE_BIT_ALPHA) + one_bit_alpha_mask(diffuse.a, 0.5); + c.rgb * diffuse.rgb; + #else + c *= diffuse; + #endif + #endif + + #if defined(MASKED) + c.rgb *= c.a; + #endif + + return c; + } + """ + } +} diff --git a/vmf_source/core/stingray_renderer/shader_libraries/development.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/development.shader_source new file mode 100644 index 0000000..1a01fef --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/development.shader_source @@ -0,0 +1,1009 @@ +includes = ["core/stingray_renderer/shader_libraries/common.shader_source", + "core/stingray_renderer/shader_libraries/lighting_common.shader_source", + "core/stingray_renderer/shader_libraries/post_processing_common.shader_source", + "core/stingray_renderer/shader_libraries/shadow_map_common.shader_source", + "core/stingray_renderer/shader_libraries/sampling_common.shader_source"] + +render_states = { + filter = { + inherits = "default" + states = { + ndefined_PROJECT_TO_FAR_PLANE = { + z_enable = "false" + } + z_write_enable = "false" + } + } + + gbuffer_debug = { + inherits = "filter" + states = { + defined_FILL_ALBEDO = { + write_mask0 = "red|green|blue" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + } + defined_FILL_UNTOUCHED_PIXELS = { + z_enable = "true" + z_func = "less_equal" + } + defined_OUTLINE_INSIDE = { + stencil_enable = "true" + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_ref = "0x80" + stencil_mask = "0x80" + stencil_write_mask = "0x0" + } + defined_OUTLINE_OUTSIDE = { + stencil_enable = "true" + stencil_func = "not_equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "not_equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_ref = "0x80" + stencil_mask = "0x80" + stencil_write_mask = "0x0" + } + + defined_DENSITY_VISUALIZATION = { + stencil_enable = "true" + stencil_ref = "0x0" + stencil_mask = "0x18" + stencil_write_mask = "0x0" + + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + } + } + } +} + +sampler_states = { +} + +hlsl_shaders = { + gbuffer_debug = { + includes = [ "common", "gbuffer_access" "color_management", "post_processing_common"] + samplers = { + gbuffer0 = { sampler_states = "clamp_linear" } + gbuffer1 = { sampler_states = "clamp_linear" } + gbuffer2 = { sampler_states = "clamp_linear" } + hdr3_rg = { sampler_states = "clamp_linear" } + // gbuffer3 = { sampler_states = "clamp_linear" } + global_diffuse_map = { sampler_states = "clamp_linear"} + ldr0 = { sampler_states = "clamp_linear" } + ldr3_div2 = { sampler_states = "clamp_linear" } + linear_depth = { sampler_states = "clamp_linear" } + hdr1 = { sampler_states = "clamp_linear" } + hdr2 = { sampler_states = "clamp_linear" } + hdr0_div2_mip6 = { sampler_states = "clamp_linear" } + ldr4_div2 = { sampler_states = "clamp_point" } + luminance_adaptation = { sampler_states = "clamp_linear" } + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + out vec2 v_uv0; + + void main() { + v_uv0 = in_uv0; + + vec4 p = in_pos * world_view_proj; + #if defined(FILL_UNTOUCHED_PIXELS) + p.z = p.w; + #endif + + gl_Position = p; + } + """ + + fp_code = """ + in vec2 v_uv0; + layout(location = 0) out vec4 out_color; + + DECLARE_SAMPLER_2D(gbuffer0); + DECLARE_SAMPLER_2D(gbuffer1); + DECLARE_SAMPLER_2D(gbuffer2); + DECLARE_SAMPLER_2D(gbuffer3); + DECLARE_SAMPLER_2D(ldr0); + DECLARE_SAMPLER_2D(ldr3_div2); + + void main() { + half4 gbuffer_0 = TEX2D(gbuffer0, v_uv0); + half4 gbuffer_1 = TEX2D(gbuffer1, v_uv0); + half4 gbuffer_2 = TEX2D(gbuffer2, v_uv0); + half4 gbuffer_3 = TEX2D(gbuffer3, v_uv0); + half4 ldr_0 = TEX2D(ldr0, v_uv0); + half4 ldr3_div2 = TEX2D(ldr3_div2, v_uv0); + + float3 base_color = gbuffer_decode_base_color(gbuffer_0); + half metallic = gbuffer_decode_metallic_mask(gbuffer_0); + float3 diffuse_color = lerp(base_color, new_half3(0, 0, 0), metallic); + float3 specular_color = lerp(new_half3(0.04, 0.04, 0.04), base_color, metallic); + half shadow = ldr_0.r; + half2 velocity = gbuffer_2.zw; + half ao = gbuffer_2.x; + + float3 N = normalize(gbuffer_decode_normal(gbuffer_1)); + half roughness = gbuffer_decode_roughness(gbuffer_1); + + #if defined(ALBEDO_VISUALIZATION) + out_color = vec4(pow(diffuse_color, vec3(1.0/2.2)), 0); + #elif defined(NORMAL_VISUALIZATION) + out_color = vec4(gbuffer_encode_normal(N), 0); + #elif defined(ROUGHNESS_VISUALIZATION) + out_color = vec4(roughness, roughness, roughness, 0); + #elif defined(SPECULAR_VISUALIZATION) + out_color = vec4(specular_color, 0); + #elif defined(METALLIC_VISUALIZATION) + out_color = vec4(metallic, metallic, metallic, 0); + #elif defined(AMBIENT_DIFFUSE_VISUALIZATION) + out_color = vec4(gbuffer_decode_ambient_diffuse_light(gbuffer_3), 0); + #elif defined(SUN_SHADOW_VISUALIZATION) + out_color = vec4(shadow, shadow, shadow, 0); + #elif defined(VELOCITY_VISUALIZATION) + out_color = vec4(velocity * vec2(100), 0, 0); + #elif defined(AO_VISUALIZATION) + out_color = vec4(ao, ao, ao, 0); + #elif defined(FILL_UNTOUCHED_PIXELS) + out_color = vec4(0, 0, 0, 0); + #else + out_color = vec4(0, 0, 0, 0); + #endif + } + """ + + code=""" + DECLARE_SAMPLER_2D(gbuffer0); + DECLARE_SAMPLER_2D(gbuffer1); + DECLARE_SAMPLER_2D(gbuffer2); + DECLARE_SAMPLER_2D(hdr3_rg); + //DECLARE_SAMPLER_2D(gbuffer3); + DECLARE_SAMPLER_CUBE(global_diffuse_map); + DECLARE_SAMPLER_2D(ldr0); + DECLARE_SAMPLER_2D(ldr3_div2); + DECLARE_SAMPLER_2D(linear_depth); + DECLARE_SAMPLER_2D(hdr1); + DECLARE_SAMPLER_2D(hdr2); + DECLARE_SAMPLER_2D(hdr0_div2_mip6); + DECLARE_SAMPLER_2D(ldr4_div2); + DECLARE_SAMPLER_2D(luminance_adaptation); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 inv_input_texture0_size; + float ao_enabled; + float ssr_enabled; + float eye_adaptation_enabled; + float exposure; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + o.position = mul(input.position, world_view_proj); + #ifdef FILL_UNTOUCHED_PIXELS + o.position.z = o.position.w; + #endif + o.uv = input.uv; + + return o; + } + + float3 rgb2xyz(float3 rgb) { + float3x3 m = { + 0.4124, 0.3576, 0.1805, + 0.2126, 0.7152, 0.0722, + 0.0193, 0.1192, 0.9505 + }; + + return mul(m, rgb); + } + + float3 xyz2lab(float3 xyz) { + float3 ref = float3(0.9505, 1.0000, 1.0888) * 100.0; // D65 white point + xyz /= ref; + xyz = (xyz > pow(6.0/29.0, 3.0)) ? pow(xyz, 1.0/3.0) : 1.0/3.0*pow(29.0/6.0, 2.0)*xyz + 4.0/29.0; + return float3(116.0, 500.0, 200.0) * float3(xyz.y, xyz.x - xyz.y, xyz.y - xyz.z) - float3(16.0, 0.0, 0.0); + } + + // simple srgb approximation + float3 rgb2srgb(float3 rgb) { + return pow(rgb, 1.0/2.2); + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + + // #pragma warning(push) // PS4 note: why does the warnings fail to be disable if we push and pop the pragma warning state? + #pragma warning(disable:5206) // muting 'local variable unreferenced' warning + #pragma warning(disable:6205) // muting 'code after this return statement is unreachable' warning + + half4 gbuffer_0 = TEX2D(gbuffer0, input.uv); + half4 gbuffer_1 = TEX2D(gbuffer1, input.uv); + half4 gbuffer_2 = TEX2D(gbuffer2, input.uv); + float4 gbuffer_4 = TEX2D(hdr3_rg, input.uv); + //half4 gbuffer_3 = TEX2D(gbuffer3, input.uv); + half4 ldr_0 = TEX2D(ldr0, input.uv); + half4 ldr_1_div2 = TEX2D(ldr3_div2, input.uv); + float d = gbuffer_decode_depth(TEX2D(linear_depth, input.uv)); + half4 local_reflections_data = TEX2D(hdr1, input.uv); + half4 local_radiation_data = TEX2D(hdr2, input.uv); + + float3 base_color = gbuffer_decode_base_color(gbuffer_0); + half metallic = gbuffer_decode_metallic_mask(gbuffer_0); + float3 diffuse_color = lerp(base_color, new_half3(0,0,0), metallic); + float3 specular_color = lerp(new_half3(0.04,0.04,0.04), base_color, metallic); + half shadow = ldr_0.r; + half2 velocity = decode_velocity(gbuffer_4.VELOCITY_COMPONENTS); + half ao = gbuffer_decode_ambient_occlusion(gbuffer_2); + if (ao_enabled) { + #if defined(SSAO_ENABLED) + half ssao = ldr_1_div2.r; + ao = min(ssao, ao); + #elif defined(SSAO_ONLY) + ao = ldr_1_div2.r; + #endif + } + + float3 N = normalize(gbuffer_decode_normal(gbuffer_1)); + half roughness = gbuffer_decode_roughness(gbuffer_1); + + half density = gbuffer_decode_density(gbuffer_2); + + float ssr_mip_level = TEX2DLOD(ldr4_div2, input.uv, 0).r * SSR_MIPMAP_LEVELS; + float ssr_weight = TEX2DLOD(hdr0_div2_mip6, input.uv, ssr_mip_level).a * ssr_enabled; + + float local_reflection_probe_weight = local_reflections_data.w; + local_reflection_probe_weight = min(local_reflection_probe_weight, 1.0 - ssr_weight); + float global_probe_weight = 1.0 - (ssr_weight + local_reflection_probe_weight); + + float3 local_reflection_probe_influence = local_reflections_data.rgb * (1.0 - max(local_reflections_data.w + ssr_weight - 1.0, 0.0)); + + #if defined(ALBEDO_VISUALIZATION) + return float4(rgb2srgb(base_color),0); + #elif defined(DIFFUSE_VISUALIZATION) + return float4(rgb2srgb(diffuse_color),0); + #elif defined(NORMAL_VISUALIZATION) + return float4(gbuffer_encode_normal(N),0); + #elif defined(ROUGHNESS_VISUALIZATION) + return float4(roughness,roughness,roughness,0); + #elif defined(SPECULAR_VISUALIZATION) + return float4(rgb2srgb(specular_color),0); + #elif defined(METALLIC_VISUALIZATION) + return float4(metallic,metallic,metallic,0); + #elif defined(AMBIENT_DIFFUSE_VISUALIZATION) + //return float4(gbuffer_decode_ambient_diffuse_light(gbuffer_3), 0); + return half4(rgbm_decode(TEXCUBELOD(global_diffuse_map, N, 0)), 1.0); + #elif defined(SUN_SHADOW_VISUALIZATION) + return float4(shadow, shadow, shadow, 0); + #elif defined(VELOCITY_VISUALIZATION) + return float4(velocity * 100, 0, 0); + #elif defined(NORMALIZED_VELOCITY_VISUALIZATION) + if (length(velocity) > 0.001) { + return float4(normalize(velocity)*0.5 + 0.5, 0, 0); + } else { + return float4(0, 0, 0, 0); + } + #elif defined(AO_VISUALIZATION) + return float4(ao, ao, ao, 0); + #elif defined(FILL_UNTOUCHED_PIXELS) + return float4(0,0,0,0); + #elif defined(ALBEDO_XYZ_LUMINANCE_VISUALIZATION) + float lum = pow(dot(diffuse_color + specular_color, luminance_vector), 1.0/2.2); + return float4(lum,lum,lum,0); + #elif defined(ALBEDO_XYZ_LUMINANCE_CLIPPING_VISUALIZATION) + float lum = pow(dot(diffuse_color + specular_color, luminance_vector), 1.0/2.2); + float min_value = 0.23; // charcoal + half max_value = 0.95; // snow + float3 albedo_luminance = float3(lum,lum,lum); + if (lum < min_value) { + float alpha = saturate(saturate((min_value - lum)/min_value)); + albedo_luminance = lerp(float3(0.0, 1.0, 1.0), float3(0.0, 0.0, 1.0), alpha); + } else if (lum > max_value) { + float alpha = saturate(saturate((lum - max_value)/(1.0 - max_value))); + albedo_luminance = lerp(float3(1.0, 1.0, 0.0), float3(1.0, 0.0, 0.0), alpha); + } + return float4(albedo_luminance, 0); + #elif defined(ALBEDO_LAB_LUMINANCE_VISUALIZATION) + float3 lab = xyz2lab(rgb2xyz(diffuse_color + specular_color)); + return float4(pow(float3(lab.x, lab.x, lab.x)*0.1, 1/2.2), 0); + #elif defined(ALBEDO_LAB_LUMINANCE_CLIPPING_VISUALIZATION) + float3 lab = xyz2lab(rgb2xyz(diffuse_color + specular_color)); + float lum = lab.x; + float min_value = xyz2lab(rgb2xyz(float3(0.04, 0.04, 0.04))).x; // charcoal + half max_value = xyz2lab(rgb2xyz(float3(0.9, 0.9, 0.9))).x; // snow + float3 albedo_luminance = float3(lum,lum,lum)*0.1; + if (lum < min_value) { + float alpha = saturate(saturate((min_value - lum)/min_value)); + albedo_luminance = lerp(float3(0.0, 1.0, 1.0), float3(0.0, 0.0, 1.0), alpha); + } else if (lum > max_value) { + float alpha = saturate(saturate((lum - max_value)/(1.0 - max_value))); + albedo_luminance = lerp(float3(1.0, 1.0, 0.0), float3(1.0, 0.0, 0.0), alpha); + } + return float4(pow(albedo_luminance, 1/2.2), 0); + #elif defined(LINEAR_DEPTH) + return saturate(d/20.0); + #elif defined(OUTLINE_INSIDE) + return float4(1, 0, 0, 0); + #elif defined(OUTLINE_OUTSIDE) + return float4(0, 0, 0, 0); + #elif defined(RADIATION_PROBE_VISUALIZATION) + float3 color = local_radiation_data.rgb; + color *= exposure / (eye_adaptation_enabled ? TEX2D(luminance_adaptation, input.uv).r : 1.0); + return float4(color, 1.0); + #elif defined(RADIATION_WEIGHT_VISUALIZATION) + return float4(1.0 - local_radiation_data.w, local_radiation_data.w, 0.0, 1.0); + #elif defined(REFLECTION_PROBE_VISUALIZATION) + float3 color = local_reflection_probe_influence; + color *= exposure / (eye_adaptation_enabled ? TEX2D(luminance_adaptation, input.uv).r : 1.0); + return float4(color, 1.0); + #elif defined(REFLECTION_WEIGHT_VISUALIZATION) + return float4(global_probe_weight, local_reflection_probe_weight, ssr_weight, 1.0); + #elif defined(DENSITY_VISUALIZATION) + return float4(density, density, density, 0); + #elif defined(SKIN_SSS_VISUALIZATION) + return float4(density, density, density, 0); + #endif + + return float4(0, 0, 0, 0); + + // #pragma warning(pop) + + } + """ + } + + clustered_shading_debug = { + includes = [ "common", "gbuffer_access", "brdf", "taa_offsets", "shadow_bias", "shadow_map_filtering", "clustered_shading" ] + samplers = { + linear_depth = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(linear_depth); + + DECLARE_CLUSTER_DATA(cs_cluster_buffer); + DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer); + DECLARE_LIGHT_DATA(cs_light_data_buffer); + DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer); + DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float4 w : TEXCOORD1; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + o.w = encode_world_pos(o.position); + o.position.z = o.position.w; + + // When using temporal antialiasing we try to cancel out the jitter + // that was introduced in the depth buffer. This is to minimize self occlusion + // that can arrise when performing a depth test beween the jittered depth buffer + // and a non jittered shadow map. + float4 tmp = o.position; + float4 view_space = tmp / tmp.w; + view_space.xy -= get_vs_halton_offset(frame_number); + tmp = view_space * tmp.w; + o.w = encode_world_pos(tmp); + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float depth = gbuffer_decode_depth(TEX2D(linear_depth, input.uv)); + float3 wp = decode_world_pos(input.w, depth); + + if (depth > cs_cluster_max_depth_inv_max_depth.x) + return float4(0, 0, 0, 1); + + uint2 cluster_info; + sample_cluster_data(cs_cluster_buffer, input.position.xy, depth, cluster_info); + + uint light_index = cluster_info.x; + int point_light_count = int(cluster_info.y & 0x00FFU); + int shadow_casting_point_light_count = int((cluster_info.y >> 8U) & 0x00FFU); + int spot_light_count = int((cluster_info.y >> 16U) & 0x00FFU); + int shadow_casting_spot_light_count = int((cluster_info.y >> 24U) & 0x00FFU); + float total = point_light_count + shadow_casting_point_light_count + spot_light_count + shadow_casting_spot_light_count; + + return float4(saturate(total / 32.0), 0, 0, 1); + } + """ + } + + coc_visualization = { + includes = [ "common", "gbuffer_access", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + const float signed_coc = decode_coc(TEX2D(input_texture0, input.uv).r); + return signed_coc > 0 ? float4(signed_coc, 0, 0, 1) : float4(0, 0, -signed_coc, 1.0); + } + """ + } + + copy = { + includes = [ "common", "gbuffer_access" ] + samplers = { + ndefined_INTERLEAVE_BUFFER = { + defined_POINT_SAMPLER = { + input_texture0 = { sampler_states = "wrap_point" } + } + ndefined_POINT_SAMPLER = { + input_texture0 = { sampler_states = "wrap_linear" } + } + + } + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + out vec2 v_uv0; + + void main() { + #if defined(FLIP_Y) + v_uv0 = vec2(in_uv0.x, 1.0 - in_uv0.y); + #else + v_uv0 = in_uv0; + #endif + + gl_Position = in_pos * world_view_proj; + } + """ + + fp_code = """ + in vec2 v_uv0; + layout(location = 0) out vec4 out_color; + + DECLARE_SAMPLER_2D(input_texture0); + + void main() { + vec4 c = TEX2D(input_texture0, v_uv0); + #if defined(ALPHA_TO_RGB) + c.rgb = c.aaa; + #endif + #if defined(ENCODE_RGBM) + c = rgbm_encode(c.rgb); + #endif + out_color = c; + } + """ + + code=""" + #if defined(INTERLEAVE_BUFFER) + StructuredBuffer input_texture0; + #else + DECLARE_SAMPLER_2D(input_texture0); + #endif + //samplerCUBE input_texture0; + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float exposure; + #if defined(INTERLEAVE_BUFFER) + float2 input_texture0_size; + #else + float2 inv_input_texture0_size; + #endif + #if defined(CUBE_CAPTURE) + float2 uv_flip; + #endif + CBUFFER_END + + #if defined(RENDERER_GL) + #define splat3(c) vec3(c) + #else + #define splat3(c) c + #endif + + static const float3 luminance_vector = float3(0.2127, 0.7152, 0.0721); + static const half min_positive_f16 = 0.000061; + + float3 inv_filmic_tone_mapping(float3 lin_x) { + float3 x = max(splat3(0.f), lin_x - splat3(0.004f)); + return (0.00322581 * (25.0f - 88.0f * x)) / (x - 1.0f); + } + + float3 filmic_tone_mapping(float3 lin_x) { + float3 x = max(splat3(0.f), lin_x - splat3(0.004f)); + return (x*(6.2f*x + splat3(0.5f)))/(x*(6.2f*x + splat3(1.7f)) + splat3(0.06f)); + } + + + float3 inv_safe_range_tone_map(float3 value) { + float lum = dot(value.rgb, luminance_vector); + value.rgb /= max((1.0 - lum), min_positive_f16); + + return value; + } + + float3 inv_safe_range_tone_map_offset(float3 value, float offset) { + float lum = dot(value.rgb, luminance_vector); + value.rgb /= max((offset - lum), min_positive_f16); + + return value; + } + + + //return (max(0, x - 0.004)*(6.2*max(0, x - 0.004) + 0.5))/(max(0, x - 0.004)*(6.2*max(0, x - 0.004) + 1.7) + 0.06); + //(x*(6.2*x + 0.5))/(x*(6.2*x + 1.7) + 0.06) + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + #if defined(PROJECT_TO_FAR_PLANE) + o.position.z = o.position.w; + #endif + + #if defined(FLIP_Y) + o.uv = float2(input.uv.x, 1.f - input.uv.y); + #else + o.uv = input.uv; + #endif + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + #if defined(INTERLEAVE_BUFFER) + float2 res = input_texture0_size; + res.y /= 4; + + uint2 pixel_pos = input.position.xy; + uint buff_index = pixel_pos.y*res.x + pixel_pos.x; + uint stride = res.x * res.y; + + return float4(input_texture0[buff_index + 0], input_texture0[buff_index + 1 * stride], input_texture0[buff_index + 2 * stride], input_texture0[buff_index + 3 * stride]); + #else + float2 uv = input.uv; + #if defined(CUBE_CAPTURE) + uv = uv * 2 - 1; + uv *= uv_flip; + uv = uv * 0.5 + 0.5; + #endif + + float4 c = TEX2D(input_texture0, uv); + + #ifdef ALPHA_TO_RGB + c.rgb = c.aaa; + #endif + #ifdef RED_TO_RGB + c.rgb = c.rrr; + #endif + #ifdef ENCODE_RGBM + c = rgbm_encode(c.rgb); + #endif + #ifdef CLEAR + c.argb = float4(0,0,0,0); + #endif + #if defined(PROJECT_TO_FAR_PLANE) + c.rgb = pow(c.rgb, gamma/2.2); + #if !defined(D3D11) + const bool capture_cubemap = false; + #endif + if (!capture_cubemap) { + c.rgb /= exposure; + } + #endif + return c; + #endif + } + """ + } + + filter_cubemap = { + includes = [ "common", "sampling" ] + + stage_conditions = { + compute = "true" + } + + samplers = { + cubemap_sampler = { sampler_states="clamp_linear" } + } + code=""" + RWTexture2D output; + TextureCube input; + SamplerState cubemap_sampler; + + CBUFFER_START(c0) + float face_index; + float mip_roughness; + CBUFFER_END + + static const float3x3 face_mapping[] = { + // +X + { + 0, 0, -1, + 0, -1, 0, + 1, 0, 0 + }, + // -X + { + 0, 0, 1, + 0, -1, 0, + -1, 0, 0 + }, + // +Y + { + 1, 0, 0, + 0, 0, 1, + 0, 1, 0 + }, + // -Y + { + 1, 0, 0, + 0, 0, -1, + 0, -1, 0 + }, + // +Z + { + 1, 0, 0, + 0, -1, 0, + 0, 0, 1 + }, + // -Z + { + -1, 0, 0, + 0, -1, 0, + 0, 0, -1 + }, + }; + + float3 face_to_cube(float2 uv, float face) { + float2 st = uv * 2 - 1; + return normalize(mul(float3(st, 1), face_mapping[face])); + } + + // TODO: optimize somehow?... + float2 cube_to_face(float3 cube) { + const float3 abs_cube = abs(cube); + + float sc, tc, ma; + if (abs_cube.x >= abs_cube.y && abs_cube.x >= abs_cube.z) { + if (cube.x > 0) { + sc = -cube.z; + tc = -cube.y; + } else { + sc = cube.z; + tc = -cube.y; + } + ma = cube.x; + } else if (abs_cube.y >= abs_cube.x && abs_cube.y >= abs_cube.z) { + if (cube.y > 0) { + sc = cube.x; + tc = cube.z; + } else { + sc = cube.x; + tc = -cube.z; + } + ma = cube.y; + } else { + if (cube.z > 0) { + sc = cube.x; + tc = -cube.y; + } else { + sc = -cube.x; + tc = -cube.y; + } + ma = cube.z; + } + + return (float2(sc / abs(ma), tc / abs(ma)) + 1) / 2; + } + + #if defined(DIFFUSE) + #define N_SAMPLES 32768 + #elif defined(SPECULAR) + #define N_SAMPLES 2048 + #elif defined(RADIANCE) + #define N_SAMPLES 8 + #endif + + float4 prefilter(float3 R, float roughness, float size) { + float3 N = R; + float3 V = R; + + float3 color = 0; + float weight = 0; + + [loop] + for (uint i = 0; i < N_SAMPLES; ++i) { + float2 E = hammersley_sequence_2d(i, N_SAMPLES); + float3 H = importance_sample_ggx(E, roughness, N); + float3 L = 2 * dot(V, H) * H - V; + float NL = saturate(dot(N, L)); + + if (NL > 0) { + color += rgbm_decode(input.SampleLevel(cubemap_sampler, L, 0)) * NL; + //color += input.SampleLevel(cubemap_sampler, L, 0) * NL; + weight += NL; + } + } + return float4(color / weight, 1); + } + + [numthreads(16, 16, 1)] + void cs_main(uint3 tid : SV_DispatchThreadID) { + uint2 st = tid.xy; + uint2 face_size; + output.GetDimensions(face_size.x, face_size.y); + if (st.x > face_size.x || st.y > face_size.y) + return; + #ifndef RADIANCE + float2 face_uv = (float2(st) + float2(0.5,0.5)) / float2(face_size); + float3 dir = face_to_cube(face_uv, face_index); + float4 filtered = prefilter(dir, mip_roughness, face_size); + #else + float4 filtered = float4(0,0,0,0); + [unroll] + for (uint fy = 0; fy < N_SAMPLES; fy++) { + for (uint fx = 0; fx < N_SAMPLES; fx++) { + float2 ofs = float2((fx+0.5)/float(N_SAMPLES), (fy+0.5)/float(N_SAMPLES)); + float2 face_uv = (float2(st) + ofs) / float2(face_size); + float3 dir = face_to_cube(face_uv, face_index); + filtered += rgbm_decode(input.SampleLevel(cubemap_sampler, dir, 0)); + //filtered += input.SampleLevel(cubemap_sampler, dir, 0); + } + } + filtered /= N_SAMPLES * N_SAMPLES; + #endif + output[st] = rgbm_encode(filtered.rgb); + } + """ + } +} + +shaders = { + gbuffer_debug = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="gbuffer_debug" render_states="gbuffer_debug" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + clustered_shading_debug = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="clustered_shading_debug" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + coc_visualization = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="coc_visualization" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + copy = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="copy" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + filter_cubemap = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="filter_cubemap" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } +} + +static_compile= [ + { shader="gbuffer_debug" } + { shader="gbuffer_debug" defines=["ALBEDO_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["NORMAL_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["ROUGHNESS_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["SPECULAR_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["METALLIC_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["FILL_UNTOUCHED_PIXELS"] } + { shader="gbuffer_debug" defines=["AMBIENT_DIFFUSE_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["SUN_SHADOW_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["VELOCITY_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["AO_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["SSAO_ENABLED" "AO_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["SSR_VISUALIZATION"] } + + { shader="copy" } + { shader="copy" defines=["FLIP_Y"] } + { shader="copy" defines=["CLEAR"] } + { shader="copy" defines=["ENCODE_RGBM"] } + { shader="copy" defines=["CUBE_CAPTURE","ENCODE_RGBM"] } + { shader="copy" defines=["CUBE_CAPTURE"] } + { shader="copy" defines=["POINT_SAMPLER"] } + { shader="copy" defines=["ALPHA_TO_RGB"] } + { shader="copy" defines=["PROJECT_TO_FAR_PLANE"] } + + { if: "on_renderer(D3D11, D3D12)" shader="filter_cubemap" defines=["DIFFUSE"] } + { if: "on_renderer(D3D11, D3D12)" shader="filter_cubemap" defines=["SPECULAR"] } + + // Fatshark + { shader="gbuffer_debug" defines=["ALBEDO_XYZ_LUMINANCE_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["ALBEDO_XYZ_LUMINANCE_CLIPPING_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["ALBEDO_LAB_LUMINANCE_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["ALBEDO_LAB_LUMINANCE_CLIPPING_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["LINEAR_DEPTH"] } + { shader="gbuffer_debug" defines=["NORMALIZED_VELOCITY_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["OUTLINE_INSIDE"] } + { shader="gbuffer_debug" defines=["OUTLINE_OUTSIDE"] } + { shader="gbuffer_debug" defines=["RADIATION_PROBE_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["RADIATION_WEIGHT_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["REFLECTION_PROBE_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["REFLECTION_WEIGHT_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["SSAO_ONLY" "AO_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["DENSITY_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["SKIN_SSS_VISUALIZATION"] } + { shader="gbuffer_debug" defines=["DIFFUSE_VISUALIZATION"] } + + { shader="copy" defines=["RED_TO_RGB"] } + + { shader="coc_visualization" } + { shader="clustered_shading_debug" } +] diff --git a/vmf_source/core/stingray_renderer/shader_libraries/extra.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/extra.shader_source new file mode 100644 index 0000000..60d557b --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/extra.shader_source @@ -0,0 +1,2266 @@ +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source", "core/stingray_renderer/shader_libraries/post_processing_common.shader_source" ] + +/* + TODO sort out code blocks later + Put this code in a fatshark_postprocessing file and inherit filter from post_processing. +*/ + +render_states = { + skydome_billboard = { + inherits = "default" + states = { + z_write_enable = "false" + + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_src_alpha" + } + } + + filter = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "false" + } + } + + filter_far = { + inherits = "default" + states = { + z_enable = "true" + z_func = "less_equal" + z_write_enable = "false" + } + } + + filter_depth = { + inherits = "default" + states = { + z_write_enable = "true" + z_enable = "true" + z_func = "always" + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + } + } + + init_linear_depth_div4 = { + inherits = "filter" + states = { + write_mask0 = "red|green" // TODO: write_mask0 = "red", there is a bug when write_mask1 is the same as write_mask0 + write_mask1 = "red|green" + } + } + + filter_alpha = { + inherits = "filter" + states = { + write_mask0 = "alpha" + } + } + + filter_specular_aa = { + inherits = "filter" + states = { + defined_SECOND_PASS = { + write_mask0 = "alpha" + } + } + } + + filter_hdr_transparency = { + inherits = "filter" + states = { + write_mask0 = "red|green|blue" + blend_enable = "true" + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_alpha" + } + } + + filter_blend = { + inherits = "filter" + states = { + blend_enable = "true" + blend_op = "blend_op_add" + src_blend = "blend_one" + + defined_PREMULTIPLIED = { + dest_blend = "blend_inv_src_alpha" + } + ndefined_PREMULTIPLIED = { + dest_blend = "blend_one" + } + + defined_OUTLINE = { + stencil_enable = "true" + + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_ref = "0x80" + stencil_mask = "0x80" + } + + defined_SKIN = { + defined_D3D11 = { + stencil_enable = "true" + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_ref = "0x8" + stencil_mask = "0x18" + stencil_write_mask = "0x18" + } + defined_D3D12 = { + stencil_enable = "true" + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_ref = "0x8" + stencil_mask = "0x18" + stencil_write_mask = "0x18" + } + } + } + } + + skin_filter = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "false" + + stencil_enable = "true" + stencil_ref = "0x8" + stencil_mask = "0x18" + stencil_write_mask = "0x0" + + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + defined_STEP0 = { + blend_factor_r = "0.3251" + blend_factor_g = "0.45" + blend_factor_b = "0.3583" + } + defined_STEP1 = { + blend_factor_r = "0.34" + blend_factor_g = "0.1864" + blend_factor_b = "0.0" + } + defined_STEP2 = { + blend_factor_r = "0.46" + blend_factor_g = "0.0" + blend_factor_b = "0.0402" + } + + defined_INDEPENDENT_BLEND = { + defined_DIRECTION_X = { + independent_blend_enable = "false" + } + defined_DIRECTION_Y = { + independent_blend_enable = "true" + // blend_enable0 should be set to false but due to a bug in the latest nvidia driver that causes blending to get disabled for MRT1 as well..?! + //blend_enable0 = "true" + //src_blend0 = "blend_one" + //dest_blend0 = "blend_zero" + blend_enable0 = "false" // I'm pretty sure this bug has been resolved + blend_enable1 = "true" + src_blend1 = "blend_blend_factor" + dest_blend1 = "blend_inv_blend_factor" + } + } + + ndefined_INDEPENDENT_BLEND = { + defined_BLEND = { + blend_enable = "true" + src_blend = "blend_blend_factor" + dest_blend = "blend_inv_blend_factor" + } + } + } + } + + skin_debug = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "false" + + stencil_enable = "true" + defined_SKIN = { + stencil_ref = "0x8" + } + ndefined_SKIN = { + stencil_ref = "0x0" + } + stencil_mask = "0x18" + stencil_write_mask = "0x8" + + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + } + } + + outline_stencil = { + inherits = "filter" + states = { + stencil_enable = "true" + + defined_FILL = { + stencil_func = "equal" + stencil_func_back_side = "equal" + } + ndefined_FILL = { + stencil_func = "not_equal" + stencil_func_back_side = "not_equal" + + ndefined_OPAQUE = { + blend_enable = "true" + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_alpha" + } + } + + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_ref = "0x80" + stencil_mask = "0x80" + stencil_write_mask = "0x0" + } + } + + fog_plane = { + inherits = "opacity" + states = { + defined_INVERTED = { + z_func = "greater" + } + + z_enable = "true" + + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_alpha" + + defined_LOW_RES = { + write_mask0 = "red|green|blue|alpha" + write_mask1 = "red|green" + } + ndefined_LOW_RES = { + write_mask0 = "red|green|blue" + } + } + } + + + premultiplied_alpha = { + inherits = "opacity" + states = { + src_blend = "blend_one" + dest_blend = "blend_inv_src_alpha" + } + } + + opacity_volume = { + inherits = "opacity" + states = { + defined_ENABLE_INSIDE= { + cull_mode = "cull_ccw" + z_enable = "false" + } + ndefined_ENABLE_INSIDE= { + cull_mode = "cull_cw" + z_enable = "true" + } + + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_alpha" + + defined_LOW_RES = { + write_mask0 = "red|green|blue|alpha" + write_mask1 = "red|green" + } + ndefined_LOW_RES = { + write_mask0 = "red|green|blue" + } + } + } +} + +sampler_states = { +} + +hlsl_shaders = { + error_debug = { + includes = [ "common" ] + + code=""" + Texture2D input_texture0; + + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + float4 texel = input_texture0.Load(int3(input.position.xy, 0)); + float4 result = 0.0; + if(any(isinf(texel))) + result.r = 1.0; + if(any(isnan(texel))) + result.g = 1.0; + if(any(texel < 0.0)) + result.b = 1.0; + if(!any(isfinite(texel))) + result.a = 1.0; + return result; + } + """ + } + + init_luminance_adaptation = { + includes = [ "common" ] + + code=""" + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + return half4(0.5,0.5,0.5,0.5); + } + """ + } + + average_luminance_feedback = { + includes = [ "common", "gbuffer_access" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_linear" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 inv_input_texture0_size; + float3 eye_adaptation_speed_min_max; + CBUFFER_END + + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float d = inv_input_texture0_size.x; + // TODO: expose a luminance to exposure curve (see Cryteks solution). + float current_avg_luminance = exp(( + TEX2D(input_texture0, input.uv + float2(-d, -d)).a + + TEX2D(input_texture0, input.uv + float2( d, -d)).a + + TEX2D(input_texture0, input.uv + float2(-d, d)).a + + TEX2D(input_texture0, input.uv + float2( d, d)).a) * 0.25); + + //float adapted_luminance = TEX2D(input_texture1, input.uv).r; + float2 eye_adaption_uv = viewport.zw + viewport.xy * 0.5; + float adapted_luminance = TEX2D(input_texture1, eye_adaption_uv).r; + + // isfinite is a safety check if there is protection if there is incorrect data in the luminance pass. Might consider to do this in the bright pass. + return isfinite(current_avg_luminance) ? (adapted_luminance + (clamp(current_avg_luminance, eye_adaptation_speed_min_max.y, eye_adaptation_speed_min_max.z) - adapted_luminance) * (1.0 - exp(-delta_time * eye_adaptation_speed_min_max.x))).rrrr : adapted_luminance.rrrr; + } + """ + } + + + volume_height_fog = { + includes = [ "common", "gbuffer_access", "fog"] + samplers = { + diffuse_map = { sampler_states ="wrap_linear" } + } + + code=""" + #ifdef LOW_RES + Texture2D linear_depth_div4; + #else + Texture2D linear_depth; + #endif + + #if defined(DENSITY_MAP) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Density Map" type="resource" } + #endif + + struct VS_INPUT { + float4 position : POSITION; + float3 normal : NORMAL0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 w : TEXCOORD0; + float wz_root : TEXCOORD1; + float linear_depth : TEXCOORD2; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float4x4 world; + float4x4 inv_world; + + #if defined(SOFT_VOLUME) + float4x4 bounding_volume; + #endif + + float3 height_fog_settings; // exports={ name="Fog Settings [Edge Falloff, Falloff Z, Density]" type="vector3" value=[0.1 0.747 1.0] min=[0 0 0] max=[2 10 1] step=[0.001 0.001 0.001] } + float3 height_fog_color; // exports={ name="Fog Color" type="vector3" value=[0.5 0.6 0.7] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] } + float3 height_fog_sun_blend; // exports={ name="Fog Sun [Blend, Exponent, Strength]" type="vector3" value=[1 4 1] min=[0 1 0] max=[1 16 1] step=[0.001 0.001 0.001] } + #if defined(DENSITY_MAP) + float3 height_fog_map_scale; // exports={ name="Density Map Scale #1" type="vector3" value=[0.7 0.7 10.0] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] } + float height_fog_map_strength; // exports={ name="Density Map Strength #1" type="scalar" value=0.6 min=0 max=1 step=0.001 } + float3 height_fog_map_wind; // exports={ name="Density Map Wind #1" type="vector3" value=[0.015 0.0 0.07] min=[-3 -3 -3] max=[3 3 3] step=[0.001 0.001 0.001] } + float3 height_fog_map_scale2; // exports={ name="Density Map Scale #2" type="vector3" value=[0.4 0.4 0.6] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] } + float height_fog_map_strength2; // exports={ name="Density Map Strength #2" type="scalar" value=1.0 min=0 max=1 step=0.001 } + float3 height_fog_map_wind2; // exports={ name="Density Map Wind #2" type="vector3" value=[0.045 0.03 -0.07] min=[-3 -3 -3] max=[3 3 3] step=[0.001 0.001 0.001] } + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + o.position = mul(input.position, world_view_proj); + o.w = encode_world_pos(o.position, camera_unprojection); + o.wz_root = world._m32; // wp.z + #if defined(GL2) + o.linear_depth = linearize_depth(o.position.z*0.5 / o.position.w + 0.5); + #else + o.linear_depth = linearize_depth(o.position.z / o.position.w); + #endif + + return o; + } + + float density_scale(float3 wp, float3 view_dir, float3 wind, Sampler2D map, float3 scale) { + float bias = 0.0; + [unroll] + for (int i = 0; i < 4; ++i) { + float3 sample_pos = wp + view_dir * 0.25 * i; + float3 uv = (sample_pos * scale + time * wind); + + bias += (TEX2D(map, uv.xy).r + TEX2D(map, uv.xz).r) * 0.5; + } + return bias * 0.25; + } + + // N = plane normal + // O = ray origin point + // d = distance of the plane from the origin + // D = normalized ray direction vector + float4 ray_plane_intersection(float3 N, float3 O, float d, float3 D, float cull) { + if(cull == -1 && dot(N, D) > 0 || cull == 1 && dot(N, D) < 0) + return float4(0, 0, 0, camera_near_far.y); + + float t = -(dot(N, O) + d) / (dot(N, D)); // t = distance from origin to intersection + + return float4(O + t * D, t); + } + + #ifdef LOW_RES + struct PS_OUTPUT { + half4 color : SV_TARGET0; + half4 depth : SV_TARGET1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + #endif + { + #ifdef LOW_RES + float d = gbuffer_decode_depth(linear_depth_div4.Load(int3(input.position.xy, 0))); + #else + float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy, 0))); + #endif + + float3 wp = decode_world_pos(input.w, d); + float3 op = mul(float4(wp, 1), inv_world); + float3 wp_cam = camera_world._m30_m31_m32; + float3 half_volume = float3(10,10,10) * 0.5; + float falloff_xy = height_fog_settings.x; + float falloff_z = height_fog_settings.y; + float density = height_fog_settings.z; + + // Inside fog volume? + bool is_inside = abs(op.x) < half_volume.x && abs(op.y) < half_volume.y; + #if defined(ENABLE_INSIDE) || defined(LOW_RES) + if (!is_inside && input.linear_depth > d) + discard; + #endif + + #if defined(SOFT_VOLUME) + float4 op_cam = mul(float4(wp_cam, 1), inv_world); + float3 o_view_dir = normalize(op - op_cam); + + if (!is_inside) { + // TODO: we already have the linear depth, we don't need these calculations if the length( wp_start - wp) calculation is done in linear depth space. + float4 p1 = ray_plane_intersection(float3(-1, 0, 0), op_cam.xyz, half_volume.x, o_view_dir, -1); + float4 p2 = ray_plane_intersection(float3( 1, 0, 0), op_cam.xyz, half_volume.x, o_view_dir, -1); + float4 p3 = ray_plane_intersection(float3( 0,-1, 0), op_cam.xyz, half_volume.y, o_view_dir, -1); + float4 p4 = ray_plane_intersection(float3( 0, 1, 0), op_cam.xyz, half_volume.y, o_view_dir, -1); + float4 p5 = ray_plane_intersection(float3( 0, 0,-1), op_cam.xyz, half_volume.z, o_view_dir, -1); + + float4 p = p1; + p = p.w < p2.w ? p : p2; + p = p.w < p3.w ? p : p3; + p = p.w < p4.w ? p : p4; + p = p.w < p5.w ? p : p5; + + wp = mul(float4(p.xyz, 1), world); + op = p.xyz; + } + + float4 p1 = ray_plane_intersection(float3(-1, 0, 0), op_cam.xyz, half_volume.x, -o_view_dir, -1); + float4 p2 = ray_plane_intersection(float3( 1, 0, 0), op_cam.xyz, half_volume.x, -o_view_dir, -1); + float4 p3 = ray_plane_intersection(float3( 0,-1, 0), op_cam.xyz, half_volume.y, -o_view_dir, -1); + float4 p4 = ray_plane_intersection(float3( 0, 1, 0), op_cam.xyz, half_volume.y, -o_view_dir, -1); + float4 p5 = ray_plane_intersection(float3( 0, 0,-1), op_cam.xyz, half_volume.z, -o_view_dir, -1); + + float4 start = p1; + start = start.w < p2.w ? start : p2; + start = start.w < p3.w ? start : p3; + start = start.w < p4.w ? start : p4; + start = start.w < p5.w ? start : p5; + + float radius = length((bounding_volume._m00_m01_m02 - bounding_volume._m10_m11_m12) * 0.5); + float3 wp_start = mul(float4(start.xyz, 1), world); + + density *= clamp( (length( wp_start - wp)/radius) * falloff_xy, 0, 1.0); + #else + if(!is_inside) { + float4 op_cam = mul(float4(wp_cam, 1), inv_world); + float3 o_view_dir = normalize(op - op_cam); + + float4 p1 = ray_plane_intersection(float3(-1, 0, 0), op_cam.xyz, half_volume.x, o_view_dir, -1); + float4 p2 = ray_plane_intersection(float3( 1, 0, 0), op_cam.xyz, half_volume.x, o_view_dir, -1); + float4 p3 = ray_plane_intersection(float3( 0,-1, 0), op_cam.xyz, half_volume.y, o_view_dir, -1); + float4 p4 = ray_plane_intersection(float3( 0, 1, 0), op_cam.xyz, half_volume.y, o_view_dir, -1); + float4 p5 = ray_plane_intersection(float3( 0, 0,-1), op_cam.xyz, half_volume.z, o_view_dir, -1); + + float4 p = p1; + p = p.w < p2.w ? p : p2; + p = p.w < p3.w ? p : p3; + p = p.w < p4.w ? p : p4; + p = p.w < p5.w ? p : p5; + + wp = mul(float4(p.xyz, 1), world); + op = p.xyz; + } + #endif + + float3 view_vec = wp - wp_cam; + float view_dist = length(view_vec); + float3 view_dir = normalize(view_vec); + + // Fade edges + /*if(is_inside && falloff_xy > 0){ + density *= saturate((half_volume.x - abs(op.x)) / falloff_xy) * saturate((half_volume.y - abs(op.y)) / falloff_xy); + + float2 uv = (op.xy/half_volume) * 0.5 + 0.5; + #if defined(DENSITY_MAP) + density *= TEX2D(diffuse_map, uv).b; + #endif + }*/ + + // Density map + #if defined(DENSITY_MAP) + float ds1 = density_scale(wp, view_dir, height_fog_map_wind, diffuse_map, height_fog_map_scale) * height_fog_map_strength; + float ds2 = density_scale(wp, view_dir, height_fog_map_wind2, diffuse_map, height_fog_map_scale2) * height_fog_map_strength2; + density *= (ds1 + ds2) * 0.5; + #endif + + #if defined(Z_INVERSION) + if( wp_cam.z > wp.z ) + discard; + float fog_a = density * saturate( 1.0 - exp((-wp_cam.z + input.wz_root) * falloff_z) * (exp(-view_dist * view_dir.z * falloff_z)) / view_dir.z ); + #else + // Fog alpha + float fog_a = density * saturate( exp((-wp_cam.z + input.wz_root) * falloff_z) * (1.0-exp(-view_dist * view_dir.z * falloff_z)) / view_dir.z ); + #endif + + // Sun alpha + half sun_a = height_fog_sun_blend.x * pow(saturate(dot(view_dir, -sun_direction)), height_fog_sun_blend.y); + + // Mix sun & fog color + float3 c = lerp(height_fog_color, height_fog_sun_blend.z * sun_color, sun_a); + half4 result = apply_fog(float4(c, fog_a), wp, input.linear_depth); + + #ifdef LOW_RES + const float3 camera_dir = camera_world._m10_m11_m12; + float back_depth = dot(view_vec, camera_dir); + + PS_OUTPUT o; + o.color = half4(result.rgb * result.a, result.a); + float alpha_depth = back_depth * result.a; + o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0, result.a); + return o; + #else + return half4(result.rgb * result.a, result.a); + #endif + } + """ + } + + fog_plane = { + includes = [ "common", "gbuffer_access", "fog"] + + samplers = { + diffuse_map = { sampler_states ="wrap_linear" } + ndefined_LOW_RES = { + defined_LOW_RES_ENABLED = { + hdr_transparent_div4 = { sampler_states = "clamp_linear" } + hdr_linear_depth_div4 = { sampler_states = "clamp_linear" } + } + } + defined_AMBIENT_TINT = { + global_diffuse_map = { sampler_states = "clamp_linear"} + } + } + + code=""" + #ifdef LOW_RES + Texture2D linear_depth_div4; + #else + Texture2D linear_depth; + #if defined(LOW_RES_ENABLED) + DECLARE_SAMPLER_2D(hdr_transparent_div4); + DECLARE_SAMPLER_2D(hdr_linear_depth_div4); + #endif + #endif + + #if defined(AMBIENT_TINT) + DECLARE_SAMPLER_CUBE(global_diffuse_map); + #endif + + + struct VS_INPUT { + float4 position : POSITION; + float3 normal : NORMAL; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 plane_eq : TEXCOORD0; + float4 w : TEXCOORD1; + float linear_depth : TEXCOORD2; + #if defined(AMBIENT_TINT) + float3 ambient_color : TEXCOORD3; + #endif + }; + + CBUFFER_START(c0) + float4x4 world; + float4x4 view_proj; + float3 fog_plane_color; // exports={ name="Fog Color" type="vector3" value=[0.5 0.5 0.5] min=[0 0 0] max=[8 8 8] step=[0.001 0.001 0.001] } + float fog_distance; // exports={ name="Fog Distance" type="scalar" value=10 min=0.01 max=200 step=0.01 } + #if defined(AMBIENT_TINT) + float3 ambient_tint; + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + float3 wp = mul(input.position, world); + o.position = mul(float4(wp,1), view_proj); + + float3 wnormal = mul(input.normal, (float3x3)world); + o.plane_eq = float4(wnormal, dot(wnormal, wp)); + + o.w = encode_world_pos(o.position, camera_unprojection); + + #if defined(GL2) + o.linear_depth = linearize_depth(o.position.z*0.5 / o.position.w + 0.5); + #else + o.linear_depth = linearize_depth(o.position.z / o.position.w); + #endif + + #if defined(AMBIENT_TINT) + #if defined(D3D11) + ambient_tint = (capture_cubemap == 1) ? 1.0 : ambient_tint; + #endif + o.ambient_color = rgbm_decode(TEXCUBELOD(global_diffuse_map, normalize(wnormal), 0)) * ambient_tint * fog_plane_color; + #endif + + return o; + } + + #ifdef LOW_RES + struct PS_OUTPUT { + half4 color : SV_TARGET0; + half4 depth : SV_TARGET1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + #endif + { + #ifdef LOW_RES + float d = gbuffer_decode_depth(linear_depth_div4.Load(int3(input.position.xy, 0))); + #else + float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy, 0))); + #endif + + #if defined(MASK_OUT_SKYDOME) + if (d > camera_near_far.y) discard; + #endif + + float3 wp = decode_world_pos(input.w, d); + + float distance_to_plane = abs(dot(wp, input.plane_eq.xyz) - input.plane_eq.w); + + #if defined(AMBIENT_TINT) + float3 color = input.ambient_color; + #else + float3 color = fog_plane_color; + #endif + half4 result = apply_fog(float4(color, saturate(distance_to_plane / fog_distance)), wp, input.linear_depth); + + #ifdef LOW_RES + PS_OUTPUT o; + o.color = half4(result.rgb * result.a, result.a); + float alpha_depth = input.linear_depth * result.a; + o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0, result.a); + return o; + #else + #if defined(LOW_RES_ENABLED) + // Fade out high resolution particle if the low resolution particles is infront of the high resolution particle + float2 screen_uv = input.position.xy / back_buffer_size; + float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg; + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + float background_depth = input.linear_depth; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * max(depth_variance, 0.001))); + float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T); + result.a *= 1.0 - alpha_modifier; + #endif + + return half4(result.rgb * result.a, result.a); + #endif + } + """ + } + + light_shafts = { + includes = [ "common", "gbuffer_access", "color_management" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + linear_depth = { sampler_states = "clamp_point" } + luminance_adaptation = { sampler_states = "clamp_linear" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(linear_depth); + #if defined(EYE_ADAPTATION) + DECLARE_SAMPLER_2D(luminance_adaptation); + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float2 sun_screen_pos : TEXCOORD2; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float3 sun_direction; + #if defined(BRIGHT_PASS) + float3 sun_color; + float exposure; + float2 light_shaft_settings; + float2 input_texture0_size; + #else + float light_shaft_weigth; + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + float4 sun_pos = float4(-sun_direction, 1.0); + camera_view._m30_m31_m32 = float3(0,0,0); + sun_pos = mul( mul( sun_pos, camera_view ), camera_projection ); + sun_pos /= sun_pos.w; + o.sun_screen_pos = sun_pos.xy * float2(0.5, -0.5) + 0.5; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + half2 uv = input.uv; + float3 c = TEX2D( input_texture0, uv ); + + #if defined(BRIGHT_PASS) + #if !defined(D3D11) + const bool capture_cubemap = false; + #endif + if (!capture_cubemap) { + #ifdef EYE_ADAPTATION + c *= exposure / TEX2D(luminance_adaptation, uv).r; + #else + c *= exposure; + #endif + } + + float d = gbuffer_decode_depth(TEX2D(linear_depth, uv)); + + // Render occluding objects black + if(d < camera_near_far.y*0.99 ){ + return float4( 0, 0, 0, 1 ); + } + + c.rgb *= light_shaft_settings.x; + c = safe_range_tone_map(c); + + // Render light source (sun) + float2 delta = uv - input.sun_screen_pos; + float dist = length( delta * float2( input_texture0_size.x/input_texture0_size.y, 1.0 ) ); + + c *= sun_color * (1.0 - saturate(dist/light_shaft_settings.y)); + #else + uint NUM_SAMPLES = 50; + float density = 0.90; + float decay = 0.992; + float weight = light_shaft_weigth; + + // Calculate vector from pixel to light source in screen space + + half2 delta_uv = uv - input.sun_screen_pos; + + // Divide by number of samples and scale by control factor. + delta_uv *= 1.0f / NUM_SAMPLES * density; + + // Set up illumination decay factor. + half illumination_decay = 1.0f; + + // Retrieve samples at new location. + const float weights[] = {0.25, 0.5, 0.25}; + + // Evaluate summation from Equation 3 NUM_SAMPLES iterations. + for (uint i = 0; i < NUM_SAMPLES; i++) + { + + float4 s = TEX2D(input_texture0, uv - delta_uv / 3) * weights[0]; + s += TEX2D(input_texture0, uv) * weights[1]; + s += TEX2D(input_texture0, uv + delta_uv / 3) * weights[2]; + + // Apply attenuation decay and weight factor + s *= illumination_decay * weight; + + // Accumulate combined color. + c += s.rgb; + + // Update exponential decay factor. + illumination_decay *= decay; + + // Step sample location along ray. + uv -= delta_uv; + } + #endif + + return float4(c, 1.0); + } + """ + } + + skydome_billboard = { + includes = [ "common", "gbuffer_access" ] + samplers = { + diffuse_map = { sampler_states = "wrap_linear" } + } + + code=""" + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 view; + float4x4 proj; + #if defined(SECONDARY_SUN) + float3 secondary_sun_direction; + #define direction secondary_sun_direction + #else + float3 sun_direction; + #define direction sun_direction + #endif + float scale; // exports={ name="Scale" type="scalar" value=1 min=0 max=2 step=0.001 } + CBUFFER_END + + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + float4 position = input.position * scale; + + float4 wp = float4(-direction, 1.0); + float3 y_axis = float3(0, 0, 1); + float3 x_axis = normalize(cross(y_axis, direction)); + y_axis = cross(direction, x_axis); + wp.rgb += x_axis * position.x + y_axis * position.y; + + o.uv = input.uv; + + view._m30_m31_m32 = float3(0,0,0); + o.position = mul(mul(wp, view), proj); + o.position.z = o.position.w; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + return TEX2D(diffuse_map, input.uv); + } + """ + } + + fixed_function_blend = { + includes = [ "common", "gbuffer_access" ] + samplers = { + defined_POINT_SAMPLER = { + input_texture0 = { sampler_states = "wrap_point" } + } + ndefined_POINT_SAMPLER = { + input_texture0 = { sampler_states = "wrap_linear" } + } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float4 color = TEX2D(input_texture0, input.uv); + #if defined(OUTLINE) + color.a = max(color.r, max(color.g, color.b)); + #endif + return color; + } + """ + } + + skin_filter = { + includes = [ "common", "gbuffer_access" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + #if defined(BLEND) + float4 blend_result(PS_INPUT input) { + return TEX2D(input_texture0, input.uv); + } + #else + float4 skin_blur(PS_INPUT input, uniform float2 step_value) : SV_TARGET0 { + // Gaussian weights for the six samples around the current pixel: + // -3 -2 -1 +1 +2 +3 + float w[6] = { 0.006, 0.061, 0.242, 0.242, 0.061, 0.006 }; + float o[6] = { -1.0, -0.6667, -0.3333, 0.3333, 0.6667, 1.0 }; + + // Fetch color and linear depth for current pixel: + float4 colorM = TEX2D(input_texture0, input.uv); + float depthM = TEX2D(input_texture1, input.uv); + + // Accumulate center sample, multiplying it with its gaussian weight: + float4 colorBlurred = colorM; + colorBlurred.rgb *= 0.382; + + // Calculate the step that we will use to fetch the surrounding pixels, + // where "step" is: + // step = sssStrength * gaussianWidth * pixelSize * dir + // The closer the pixel, the stronger the effect needs to be, hence + // the factor 1.0 / depthM. + //colorM.a = 1; + float2 finalStep = colorM.a * step_value / depthM; + float correction = 1600; //8000; + + // Accumulate the other samples: + [unroll] + for (int i = 0; i < 6; i++) { + // Fetch color and depth for current sample: + float2 offset = input.uv + o[i] * finalStep; + float3 color = TEX2D(input_texture0, offset).rgb; + float depth = TEX2D(input_texture1, offset).r; + + // If the difference in depth is huge, we lerp color back to "colorM": + float s = min(0.0125 * correction * abs(depthM - depth), 1.0); + color = lerp(color, colorM.rgb, s); + + // Accumulate: + colorBlurred.rgb += w[i] * color; + } + + // The result will be alpha blended with current buffer by using specific + // RGB weights. For more details, I refer you to the GPU Pro chapter :) + return colorBlurred; + } + + float4 skin_main(PS_INPUT input) { + float sss_strength = 12.5; //31.5; + float maxdd = 0.001; + + #ifdef STEP0 + float gaussian_width = sqrt((0.0516 - 0.0064)); + #elif defined(STEP1) + float gaussian_width = sqrt((0.2719 - 0.0516)); + #elif defined(STEP2) + float gaussian_width = sqrt((2.0062 - 0.2719)); + #endif + + #ifdef DIRECTION_X + float2 dir = float2(1,0); + #else + float2 dir = float2(0,1); + #endif + + float2 step_val = sss_strength * gaussian_width * (float2(1,1) / back_buffer_size.xy) * dir; + + float4 c = skin_blur(input, step_val); + + return c; + } + #endif + + #if defined(DIRECTION_X) || (defined(DIRECTION_Y) && !defined(INDEPENDENT_BLEND)) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return skin_main(input); + } + #elif defined(BLEND) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return blend_result(input); + } + #else + struct PS_OUTPUT { + float4 acc : SV_TARGET0; + float4 final : SV_TARGET1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) { + PS_OUTPUT o; + float4 c = skin_main(input); + o.acc = c; + o.final = c; + return o; + } + #endif + """ + } + + add_colored_outline = { + includes = [ "common", "gbuffer_access" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_point" } + } + + code=""" + #ifndef FILL + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + #ifndef FILL + float4 outline_thickness; + + float3 outline_color_red; + float3 outline_color_green; + float3 outline_color_blue; + float3 outline_color_alpha; + + float outline_multiplier_red; + float outline_multiplier_green; + float outline_multiplier_blue; + float outline_multiplier_alpha; + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + #ifdef FILL + return float4(0, 0, 0, 0); + #else + // float mask = 1.0 - TEX2D(input_texture1, input.uv).r; + float4 color = TEX2D(input_texture0, input.uv); + + color = pow(color, outline_thickness); + + float3 final_color = color.r * outline_color_red * outline_multiplier_red; + final_color += color.g * outline_color_green * outline_multiplier_green; + final_color += color.b * outline_color_blue * outline_multiplier_blue; + final_color += color.a * outline_color_alpha * outline_multiplier_alpha; + + // final_color *= mask; + + // We assume that the colors was authered in gamma 2.2 + final_color = pow(final_color, 2.2/gamma); + float alpha = saturate(max(final_color.r, max(final_color.g, final_color.b))); + return float4(final_color, alpha); + #endif + } + """ + } + + specular_aa = { + includes = [ "common", "gbuffer_access" ] + samplers = { + } + + code=""" + #define NUM_GAUSSIAN_WEIGHTS 4 + + // Gaussian coefficients + static float gaussian_weights[] = + // { 0.398943, 0.241971, 0.053991, 0.004432, 0.000134 }; // stddev = 1.0 + { 0.153170, 0.144893, 0.122649, 0.092902, 0.062970 }; // stddev = 2.0 + // { 0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108 }; // stddev = 3.0 + + // First pass: + // Decode and blur normals + // Second pass: + // Blur normals and encode new roughness + #if defined(FIRST_PASS) + static int2 axis = int2(1,0); + #define NORMALIZE(x) normalize(x) + #define DECODE_NORMAL(x) gbuffer_decode_normal(x) + #define DECODE_ROUGHNESS(x) gbuffer_decode_roughness(x) + #else + static int2 axis = int2(0,1); + #define NORMALIZE(x) x + #define DECODE_NORMAL(x) x.rgb + #define DECODE_ROUGHNESS(x) x.a + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + Texture2D input_texture0; + Texture2D linear_depth; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + int2 position = input.position.xy; + + half4 texel = input_texture0.Load(int3(position, 0)); + float3 ref_N = NORMALIZE(DECODE_NORMAL(texel)); + float ref_roughness = DECODE_ROUGHNESS(texel); + float ref_depth = gbuffer_decode_depth(linear_depth.Load(int3(position, 0))); + + // Base weight for depth falloff. Decrease this for more blurriness, + // increase it for better edge discrimination + float total_weight = gaussian_weights[0] * 0.5; + float3 Na = ref_N * total_weight; + + const float DEPTH_THRESHOLD = 0.2; + const float inv_denominator = 1.0 / (-DEPTH_THRESHOLD); + + position -= axis * NUM_GAUSSIAN_WEIGHTS; + [unroll] + for (int r = NUM_GAUSSIAN_WEIGHTS; r > 0 ; --r) { + // Sample data + float3 N = NORMALIZE(DECODE_NORMAL(input_texture0.Load(int3(position, 0)))); + float depth = gbuffer_decode_depth(linear_depth.Load(int3(position, 0))); + float weight = gaussian_weights[r]; + + // Range domain (the "bilateral" weight). As depth difference increases, decrease weight. + weight *= saturate(abs(ref_depth - depth) * inv_denominator + 1.0); //linearstep is cheaper, smoothstep(DEPTH_THRESHOLD, 0, abs(ref_depth - depth)); + + Na += N * weight; + total_weight += weight; + + position += axis; + } + + // The case where r == 0 is handled above the for loop, just change the sample pos here and go into the next loop. + position = input.position.xy + axis; + [unroll] + for (int r = 1; r <= NUM_GAUSSIAN_WEIGHTS; ++r) { + // Sample data + float3 N = NORMALIZE(DECODE_NORMAL(input_texture0.Load(int3(position, 0)))); + float depth = gbuffer_decode_depth(linear_depth.Load(int3(position, 0))); + float weight = gaussian_weights[r]; + + // Range domain (the "bilateral" weight). As depth difference increases, decrease weight. + weight *= saturate(abs(ref_depth - depth) * inv_denominator + 1.0); //linearstep is cheaper, smoothstep(DEPTH_THRESHOLD, 0, abs(ref_depth - depth)); + + Na += N * weight; + total_weight += weight; + + position += axis; + } + + Na = Na / total_weight; + + #if defined(FIRST_PASS) + return half4(Na, ref_roughness); + #else + // Specular AA + // http://blog.selfshadow.com/publications/s2013-shading-course/rad/s2013_pbs_rad_notes.pdf + float l = length(Na); + [flatten] + if (l < 0.999) { + float ll = l * l ; + float kappa = (3.0 * l - l * ll) / (1.0 - ll) ; + float variance = 1.0 / kappa; + ref_roughness = sqrt(ref_roughness * ref_roughness + variance); + } + + return half4(0, 0, 0, gbuffer_encode_roughness(ref_roughness)); + #endif + } + """ + } + + apply_hdr_transparent = { + includes = [ "common", "gbuffer_access" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_linear" } + } + + compiler_options = { + D3D11 = { + pixel_shader = { + instruction_set = "ps_5_0" + } + } + D3D12 = { + pixel_shader = { + instruction_set = "ps_5_0" + } + } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + + Texture2D linear_depth; + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + // Upsampling method based on Destiny VDP + // http://advances.realtimerendering.com/destiny/i3d_2015/I3D_Tatarchuk_keynote_2015_for_web.pdf + // http://advances.realtimerendering.com/s2013/Tatarchuk-Destiny-SIGGRAPH2013.pdf + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + int2 position = input.position.xy + 1; + int2 offset = (((position & 3) >> 1) << 1) - 1; // same as position%4 < 2 ? -1 : 1; but faster + + float background_depth = linear_depth.Load(int3(input.position.xy, 0)).r; + + // TODO: is the result of d the same for both branches? + #if defined(DX10_CAPS) || defined(RENDERER_GNM) || defined(PLATFORM_XB1) // Todo: Implement compile_options for consoles + // TODO: optmize sample pattern + float d0 = input_texture1.tex.Load(int3(input.position.xy/4, 0)).r; + float d1 = input_texture1.tex.Load(int3(input.position.xy/4 + int2(offset.x, 0), 0)).r; + float d2 = input_texture1.tex.Load(int3(input.position.xy/4 + int2(0, offset.y), 0)).r; + float d3 = input_texture1.tex.Load(int3(input.position.xy/4 + offset, 0)).r; + float4 d = float4(d0, d1, d2, d3); + #else + float4 d = input_texture1.tex.Gather(input_texture1.state, input.uv); + #endif + + // Based on nearest-depth upsampling + // https://mynameismjp.wordpress.com/2015/09/13/programmable-sample-points/ + float2 particle_depth_values = 0; + half4 color = 0; + [branch] + if (all(d < background_depth)) { + particle_depth_values = TEX2D(input_texture1, input.uv).rg; + color = TEX2D(input_texture0, input.uv); + } else { + particle_depth_values = input_texture1.tex.Load(int3(input.position.xy/4, 0)).rg; + color = input_texture0.tex.Load(int3(input.position.xy/4, 0)); + } + + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + depth_variance = depth_variance > 0.0 ? sqrt(depth_variance) : 0.001; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * depth_variance)); + + return color * (1.0 - T); + } + """ + } + + depth_filter = { + includes = [ "common" ] + samplers = { + } + + code=""" + Texture2D input_texture0; + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 inv_input_texture0_size; + float2 output_target_base_size; + CBUFFER_END + + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + return o; + } + + #if defined(GNM) + #define DEPTH S_DEPTH_OUTPUT + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float ps_main(PS_INPUT input) : DEPTH { + #define load_depth(coordinate) input_texture0.Load(coordinate).r + + int2 coord = input.position.xy*4.0 + 0.5; + float4 depth1 = float4(load_depth(int3(coord + int2(-2, -2), 0.0)).r, load_depth(int3(coord + int2(-1, -2), 0.0)).r, load_depth(int3(coord + int2(0, -2), 0.0)).r, load_depth(int3(coord + int2(1, -2), 0.0)).r); + float4 depth2 = float4(load_depth(int3(coord + int2(-2, -1), 0.0)).r, load_depth(int3(coord + int2(-1, -1), 0.0)).r, load_depth(int3(coord + int2(0, -1), 0.0)).r, load_depth(int3(coord + int2(1, -1), 0.0)).r); + float4 depth3 = float4(load_depth(int3(coord + int2(-2, 0), 0.0)).r, load_depth(int3(coord + int2(-1, 0), 0.0)).r, load_depth(int3(coord + int2(0, 0), 0.0)).r, load_depth(int3(coord + int2(1, 0), 0.0)).r); + float4 depth4 = float4(load_depth(int3(coord + int2(-2, 1), 0.0)).r, load_depth(int3(coord + int2(-1, 1), 0.0)).r, load_depth(int3(coord + int2(0, 1), 0.0)).r, load_depth(int3(coord + int2(1, 1), 0.0)).r); + + #if defined(MIN_FILTER) + float4 min_column = min(min(depth1, depth2), min(depth3, depth4)); + float depth = min(min(min_column.x, min_column.y), min(min_column.z, min_column.w)); + #elif defined(MAX_FILTER) + float4 max_column = max(max(depth1, depth2), max(depth3, depth4)); + float depth = max(max(max_column.x, max_column.y), max(max_column.z, max_column.w)); + #else + float depth = dot(depth1 + depth2 + depth3 + depth4, float4(0.0625, 0.0625, 0.0625, 0.0625)); + #endif + + return depth; + } + """ + } + + init_linear_depth_div4 = { + includes = [ "common", "gbuffer_access" ] + samplers = { + } + + code=""" + Texture2D input_texture0; + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + struct PS_OUTPUT { + float4 linear_depth : SV_TARGET0; + float4 linear_depth_squared : SV_TARGET1; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 inv_input_texture0_size; + float2 output_target_base_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + return o; + } + + #ifdef RENDERER_GNM + #pragma PSSL_target_output_format (target 0 FMT_32_R) + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) { + float clip_depth = input_texture0.Load(int3(input.position.xy, 0)); + float depth = linearize_depth(clip_depth); + + PS_OUTPUT o; + o.linear_depth = float4(depth, 0, 0, 0); + // o.linear_depth_squared = float4(depth, depth * depth, 0, 0); // TODO: this coulde be interesting, however it results in bad edge artefacts atm. + o.linear_depth_squared = float4(0, 0, 0, 0); + return o; + } + """ + } + + skin_debug = { + includes = [ "common" ] + samplers = { + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + + Texture2D linear_depth; + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + // Upsampling method based on Destiny VDP + // http://advances.realtimerendering.com/destiny/i3d_2015/I3D_Tatarchuk_keynote_2015_for_web.pdf + // http://advances.realtimerendering.com/s2013/Tatarchuk-Destiny-SIGGRAPH2013.pdf + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + #if defined(SKIN) + return float4(0, 1, 0, 0.8); + #else + return float4(0, 0, 0, 0.5); + #endif + } + """ + } + + fill_far_plane = { + includes = [ "common" ] + + code=""" + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.position.z = o.position.w; + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return float4(0, 0, 0, 0); + } + """ + } + + rgba_temporal_aa = { + includes = [ "common", "gbuffer_access", "taa_offsets", "color_management", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_linear" } + input_texture2 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + DECLARE_SAMPLER_2D(input_texture2); + + #define TAA_SIMPLE_BLEND_FACTOR 0.05 + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + // Current fragment generic info + float4 result = 0; + float2 pixel_size = 1.0/back_buffer_size; + + float2 uv = input.uv; + float3 ss_pos = float3(uv, 0); + float3 ss_front_most_neighbor = ss_pos; + + // Reprojection info + float2 motion_vector = decode_velocity(TEX2D(input_texture2, ss_front_most_neighbor.xy).VELOCITY_COMPONENTS); + float2 ss_prev_pos = ss_pos.xy - motion_vector; + + float3 prev_sample = TEX2D(input_texture1, ss_prev_pos).rgb; + + float2 nd_prev_pos = ss_prev_pos * 2.0 - 1.0; + bool reprojection_is_offscreen = max(abs(nd_prev_pos.x), abs(nd_prev_pos.y)) >= 1.0; + + // 3x3 neighbors info + int3 st = input.position; + float4 reconstructed_sample = input_texture0.tex.Load(st, + neighbor_offsets[4]); + + float3 sample0 = input_texture0.tex.Load(st, + neighbor_offsets[0]).rgb; + float3 sample1 = input_texture0.tex.Load(st, + neighbor_offsets[1]).rgb; + float3 sample2 = input_texture0.tex.Load(st, + neighbor_offsets[2]).rgb; + float3 sample3 = input_texture0.tex.Load(st, + neighbor_offsets[3]).rgb; + float3 sample4 = reconstructed_sample.rgb; + float3 sample5 = input_texture0.tex.Load(st, + neighbor_offsets[5]).rgb; + float3 sample6 = input_texture0.tex.Load(st, + neighbor_offsets[6]).rgb; + float3 sample7 = input_texture0.tex.Load(st, + neighbor_offsets[7]).rgb; + float3 sample8 = input_texture0.tex.Load(st, + neighbor_offsets[8]).rgb; + + // Shaped Neighorhood clamp info + // We want the clamped of the min/max values to appear filtered. + // We split the samples into two "neighborhoods" and average + // them together (Karis 2014) + // ________________ ________________ + // Neighborhood '1' Neighborhood '2' + // 0 1 2 - 1 - + // 3 4 5 3 4 5 + // 6 7 8 - 7 - + float3 neighborhood_1_min = min(min(sample0, sample2), min(sample6, sample8)); + float3 neighborhood_1_max = max(max(sample0, sample2), max(sample6, sample8)); + float3 neighborhood_2_min = min(min(min(sample1, sample3), min(sample4, sample5)), sample7); + float3 neighborhood_2_max = max(max(max(sample1, sample3), max(sample4, sample5)), sample7); + neighborhood_1_min = min(neighborhood_1_min, neighborhood_2_min); + neighborhood_1_max = max(neighborhood_1_max, neighborhood_2_max); + + prev_sample = clamp(prev_sample, neighborhood_1_min, neighborhood_1_max); + result = reprojection_is_offscreen ? reconstructed_sample : float4(lerp(prev_sample, reconstructed_sample.rgb, TAA_SIMPLE_BLEND_FACTOR), reconstructed_sample.a); + + return result; + } + """ + } + +} + +shaders = { + error_debug = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="error_debug" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + init_luminance_adaptation = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="init_luminance_adaptation" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + average_luminance_feedback = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="average_luminance_feedback" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + + volume_height_fog = { + editor_options = [ + { + name="Fog Density" + options = [ + { name="Density Map" define="DENSITY_MAP" } + ] + } + { + name="Special" + options = [ + { name="Flip Z" define="Z_INVERSION" } + { name="Soft Volume" define="SOFT_VOLUME" } + { name="Enable Camera Inside Volume" define="ENABLE_INSIDE" tooltip="Extra computations to make correct fog calculations when camera is inside the volume." } + ] + } + ] + + contexts = { + default = { + passes = [ + { + defined="LOW_RES" + pass = [ + { layer="hdr_transparent_low_res" hlsl_shader="volume_height_fog" render_states="opacity_volume" } + ] + fail = [ + { layer="hdr_transparent" hlsl_shader="volume_height_fog" render_states="opacity_volume" } + ] + } + ] + } + } + + compile = { + default = [ + { if: "on_renderer(D3D11, D3D12, GNM) && render_setting(low_res_transparency)" defines=["LOW_RES"] } + { defines="" platforms = "D3D11 D3D12 GNM"} + ] + } + } + + fog_plane = { + editor_options = [ + { + name="Quality" + options = [ + { name="High Resolution" define="HIGH_RESOLUTION" tool_tip="Fog planes are low resolution by default. If this flag is enabled the particle will be rendered in full resolution." } + ] + } + { + name="Settings" + options = [ + { name="Inverted: Fog pixels in-front of plane" define="INVERTED" } + { name="Mask out skydome" define="MASK_OUT_SKYDOME" } + { name="Ambient tint" define="AMBIENT_TINT" } + ] + } + ] + + contexts = { + default = { + passes = [ + { + defined="LOW_RES_ENABLED" + pass = [ + //{ + //defined="HIGH_RESOLUTION" + //pass = [ + { layer="hdr_transparent" hlsl_shader="fog_plane" render_states="fog_plane" } + //] + //fail = [ + // { layer="hdr_transparent_low_res" hlsl_shader="fog_plane" defines=["LOW_RES"] render_states="fog_plane" } + //] + //} + ] + fail = [ + { layer="hdr_transparent" hlsl_shader="fog_plane" render_states="fog_plane" } + ] + } + ] + } + } + + compile = { + default = [ + { if: "on_renderer(D3D11, D3D12, GNM) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] } + { defines="" platforms = "D3D11 D3D12 GNM"} + ] + } + } + + light_shafts = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="light_shafts" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + skydome_billboard = { + editor_advanced_mode = false + editor_options = [ + { + name="Sun Direction" + options = [ + { name="Use Secondary Sun" define="SECONDARY_SUN" } + ] + } + ] + + contexts = { + default = { + passes = [ + { layer="skydome_billboard" hlsl_shader="skydome_billboard" render_states="skydome_billboard" } + ] + } + + // TODO: material transfer + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + fixed_function_blend = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="fixed_function_blend" render_states="filter_blend" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + skin_filter = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="skin_filter" render_states="skin_filter" } + ] + } + } + + compile = { + default = [ + { defines="INDEPENDENT_BLEND" platforms="GNM" } + { defines="INDEPENDENT_BLEND" render_caps={ feature_level="DX11_0" } platforms="D3D11" } + { defines="INDEPENDENT_BLEND" render_caps={ feature_level="DX12_1" } platforms="D3D12" } + { defines="INDEPENDENT_BLEND DX10_1" render_caps={ feature_level="DX10_1" } platforms="D3D11" } + { defines="SEPARATE_BLEND" platforms="D3D11 D3D12 GNM" } + ] + } + } + + add_colored_outline = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="add_colored_outline" render_states="outline_stencil" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + specular_aa = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="specular_aa" render_states="filter_specular_aa" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + apply_hdr_transparent = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="apply_hdr_transparent" render_states="filter_hdr_transparency" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + depth_filter = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="depth_filter" render_states="filter_depth" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + skin_debug = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="skin_debug" render_states="skin_debug" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + init_linear_depth_div4 = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="init_linear_depth_div4" render_states="init_linear_depth_div4" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + fill_far_plane = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="fill_far_plane" render_states="filter_far" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + rgba_temporal_aa = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="rgba_temporal_aa" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } +} + +static_compile= [ + { shader="error_debug" } + { shader="init_luminance_adaptation" } + { shader="average_luminance_feedback" } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="light_shafts" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="light_shafts" defines=["BRIGHT_PASS"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="light_shafts" defines=["BRIGHT_PASS" "EYE_ADAPTATION"] } + + { shader="fixed_function_blend" } + { shader="fixed_function_blend" defines=["POINT_SAMPLER"] } + { shader="fixed_function_blend" defines=["PREMULTIPLIED"] } + { shader="fixed_function_blend" defines=["PREMULTIPLIED" "OUTLINE"] } + { if: "!on_renderer(GL)" shader="fixed_function_blend" defines=["POINT_SAMPLER" "SKIN"] } + + { if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_X" "STEP0"] } + { if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_Y" "STEP0"] } + { if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_X" "STEP1"] } + { if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_Y" "STEP1"] } + { if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_X" "STEP2"] } + { if: "!on_renderer(GL)" shader="skin_filter" defines=["DIRECTION_Y" "STEP2"] } + { if: "!on_renderer(GL)" shader="skin_filter" defines=["BLEND STEP0"] } + { if: "!on_renderer(GL)" shader="skin_filter" defines=["BLEND STEP1"] } + { if: "!on_renderer(GL)" shader="skin_filter" defines=["BLEND STEP2"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="add_colored_outline" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="add_colored_outline" defines=["FILL"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="add_colored_outline" defines=["OPAQUE"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="specular_aa" defines=["FIRST_PASS"]} + { if: "on_renderer(D3D11, D3D12, GNM)" shader="specular_aa" defines=["SECOND_PASS"]} + + { shader="apply_hdr_transparent" } + + { shader="depth_filter" defines=["MIN_FILTER"] } + { shader="depth_filter" defines=["MAX_FILTER"] } + { shader="depth_filter" } + + { shader="skin_debug" } + { shader="skin_debug" defines=["SKIN"] } + + { shader="init_linear_depth_div4" } + + { shader="fill_far_plane" } + + { shader="rgba_temporal_aa" } +] diff --git a/vmf_source/core/stingray_renderer/shader_libraries/fxaa.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/fxaa.shader_source new file mode 100644 index 0000000..623ecc7 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/fxaa.shader_source @@ -0,0 +1,2276 @@ +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source" ] + +render_states = { + fxaa = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "false" + } + } +} + +sampler_states = { + fxaa_sampler = { + inherits = "clamp_linear_srgb" + states = { + srgb = "false" + defined_D3D11 = { + max_anisotropy = "0x4" + min_lod = "0.0" + max_lod = "0.0" + } + defined_D3D12 = { + max_anisotropy = "0x4" + min_lod = "0.0" + max_lod = "0.0" + } + defined_GL2 = { + max_anisotropy = "4.0" + } + } + } +} + +hlsl_shaders = { + fxaa_source = { + includes = [ "common" ] + + samplers = { + defined_D3D11 = { + anisotropic_sampler = { sampler_states = "fxaa_sampler" } + } + defined_D3D12 = { + anisotropic_sampler = { sampler_states = "fxaa_sampler" } + } + defined_GNM = { + anisotropic_sampler = { sampler_states = "fxaa_sampler" } + } + defined_GL2 = { + input_texture0 = { sampler_states = "fxaa_sampler" } + } + } + + fp_code = { ref = "code" } + + code=""" + #define FXAA_PC 1 + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) + #define FXAA_HLSL_4 1 + #elif defined(GL2) + #extension GL_EXT_gpu_shader4 : enable + #extension GL_ARB_gpu_shader5 : enable + #define FXAA_GLSL_120 1 + #elif defined(RENDERER_GNM) + #define GNM_HLSL 1 + #endif + #define FXAA_QUALITY__PRESET 12 + #define FXAA_GREEN_AS_LUMA 1 + + /*============================================================================ + + + NVIDIA FXAA 3.11 by TIMOTHY LOTTES + + + ------------------------------------------------------------------------------ + COPYRIGHT (C) 2010, 2011 NVIDIA CORPORATION. ALL RIGHTS RESERVED. + ------------------------------------------------------------------------------ + TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THIS SOFTWARE IS PROVIDED + *AS IS* AND NVIDIA AND ITS SUPPLIERS DISCLAIM ALL WARRANTIES, EITHER EXPRESS + OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL NVIDIA + OR ITS SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR + CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR + LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION, + OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE + THIS SOFTWARE, EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + DAMAGES. + + ------------------------------------------------------------------------------ + INTEGRATION CHECKLIST + ------------------------------------------------------------------------------ + (1.) + In the shader source, setup defines for the desired configuration. + When providing multiple shaders (for different presets), + simply setup the defines differently in multiple files. + Example, + + #define FXAA_PC 1 + #define FXAA_HLSL_5 1 + #define FXAA_QUALITY__PRESET 12 + + Or, + + #define FXAA_360 1 + + Or, + + #define FXAA_PS3 1 + + Etc. + + (2.) + Then include this file, + + #include "Fxaa3_11.h" + + (3.) + Then call the FXAA pixel shader from within your desired shader. + Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + As for FXAA 3.11 all inputs for all shaders are the same + to enable easy porting between platforms. + + return FxaaPixelShader(...); + + (4.) + Insure pass prior to FXAA outputs RGBL (see next section). + Or use, + + #define FXAA_GREEN_AS_LUMA 1 + + (5.) + Setup engine to provide the following constants + which are used in the FxaaPixelShader() inputs, + + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + + Look at the FXAA Quality FxaaPixelShader() for docs on inputs. + + (6.) + Have FXAA vertex shader run as a full screen triangle, + and output "pos" and "fxaaConsolePosPos" + such that inputs in the pixel shader provide, + + // {xy} = center of pixel + FxaaFloat2 pos, + + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 fxaaConsolePosPos, + + (7.) + Insure the texture sampler(s) used by FXAA are set to bilinear filtering. + + + ------------------------------------------------------------------------------ + INTEGRATION - RGBL AND COLORSPACE + ------------------------------------------------------------------------------ + FXAA3 requires RGBL as input unless the following is set, + + #define FXAA_GREEN_AS_LUMA 1 + + In which case the engine uses green in place of luma, + and requires RGB input is in a non-linear colorspace. + + RGB should be LDR (low dynamic range). + Specifically do FXAA after tonemapping. + + RGB data as returned by a texture fetch can be non-linear, + or linear when FXAA_GREEN_AS_LUMA is not set. + Note an "sRGB format" texture counts as linear, + because the result of a texture fetch is linear data. + Regular "RGBA8" textures in the sRGB colorspace are non-linear. + + If FXAA_GREEN_AS_LUMA is not set, + luma must be stored in the alpha channel prior to running FXAA. + This luma should be in a perceptual space (could be gamma 2.0). + Example pass before FXAA where output is gamma 2.0 encoded, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + return color; + + To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.rgb = sqrt(color.rgb); // gamma 2.0 color output + color.a = dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114)); // compute luma + return color; + + Another example where output is linear encoded, + say for instance writing to an sRGB formated render target, + where the render target does the conversion back to sRGB after blending, + + color.rgb = ToneMap(color.rgb); // linear color output + return color; + + To use FXAA, + + color.rgb = ToneMap(color.rgb); // linear color output + color.a = sqrt(dot(color.rgb, FxaaFloat3(0.299, 0.587, 0.114))); // compute luma + return color; + + Getting luma correct is required for the algorithm to work correctly. + + + ------------------------------------------------------------------------------ + BEING LINEARLY CORRECT? + ------------------------------------------------------------------------------ + Applying FXAA to a framebuffer with linear RGB color will look worse. + This is very counter intuitive, but happends to be true in this case. + The reason is because dithering artifacts will be more visiable + in a linear colorspace. + + + ------------------------------------------------------------------------------ + COMPLEX INTEGRATION + ------------------------------------------------------------------------------ + Q. What if the engine is blending into RGB before wanting to run FXAA? + + A. In the last opaque pass prior to FXAA, + have the pass write out luma into alpha. + Then blend into RGB only. + FXAA should be able to run ok + assuming the blending pass did not any add aliasing. + This should be the common case for particles and common blending passes. + + A. Or use FXAA_GREEN_AS_LUMA. + + ============================================================================*/ + + /*============================================================================ + + INTEGRATION KNOBS + + ============================================================================*/ + // + // FXAA_PS3 and FXAA_360 choose the console algorithm (FXAA3 CONSOLE). + // FXAA_360_OPT is a prototype for the new optimized 360 version. + // + // 1 = Use API. + // 0 = Don't use API. + // + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_PS3 + #define FXAA_PS3 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_360 + #define FXAA_360 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_360_OPT + #define FXAA_360_OPT 0 + #endif + /*==========================================================================*/ + #ifndef FXAA_PC + // + // FXAA Quality + // The high quality PC algorithm. + // + #define FXAA_PC 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_PC_CONSOLE + // + // The console algorithm for PC is included + // for developers targeting really low spec machines. + // Likely better to just run FXAA_PC, and use a really low preset. + // + #define FXAA_PC_CONSOLE 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_GLSL_120 + #define FXAA_GLSL_120 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_GLSL_130 + #define FXAA_GLSL_130 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_HLSL_3 + #define FXAA_HLSL_3 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_HLSL_4 + #define FXAA_HLSL_4 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_HLSL_5 + #define FXAA_HLSL_5 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef GNM_HLSL + #define GNM_HLSL 0 + #endif + /*==========================================================================*/ + #ifndef FXAA_GREEN_AS_LUMA + // + // For those using non-linear color, + // and either not able to get luma in alpha, or not wanting to, + // this enables FXAA to run using green as a proxy for luma. + // So with this enabled, no need to pack luma in alpha. + // + // This will turn off AA on anything which lacks some amount of green. + // Pure red and blue or combination of only R and B, will get no AA. + // + // Might want to lower the settings for both, + // fxaaConsoleEdgeThresholdMin + // fxaaQualityEdgeThresholdMin + // In order to insure AA does not get turned off on colors + // which contain a minor amount of green. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_GREEN_AS_LUMA 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_EARLY_EXIT + // + // Controls algorithm's early exit path. + // On PS3 turning this ON adds 2 cycles to the shader. + // On 360 turning this OFF adds 10ths of a millisecond to the shader. + // Turning this off on console will result in a more blurry image. + // So this defaults to on. + // + // 1 = On. + // 0 = Off. + // + #define FXAA_EARLY_EXIT 1 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_DISCARD + // + // Only valid for PC OpenGL currently. + // Probably will not work when FXAA_GREEN_AS_LUMA = 1. + // + // 1 = Use discard on pixels which don't need AA. + // For APIs which enable concurrent TEX+ROP from same surface. + // 0 = Return unchanged color on pixels which don't need AA. + // + #define FXAA_DISCARD 0 + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_FAST_PIXEL_OFFSET + // + // Used for GLSL 120 only. + // + // 1 = GL API supports fast pixel offsets + // 0 = do not use fast pixel offsets + // + #ifdef GL_EXT_gpu_shader4 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_FAST_PIXEL_OFFSET 1 + #endif + #ifndef FXAA_FAST_PIXEL_OFFSET + #define FXAA_FAST_PIXEL_OFFSET 0 + #endif + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_GATHER4_ALPHA + // + // 1 = API supports gather4 on alpha channel. + // 0 = API does not support gather4 on alpha channel. + // + #if (FXAA_HLSL_5 == 1) + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_ARB_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifdef GL_NV_gpu_shader5 + #define FXAA_GATHER4_ALPHA 1 + #endif + #ifndef FXAA_GATHER4_ALPHA + #define FXAA_GATHER4_ALPHA 0 + #endif + #endif + + /*============================================================================ + FXAA CONSOLE PS3 - TUNING KNOBS + ============================================================================*/ + #ifndef FXAA_CONSOLE__PS3_EDGE_SHARPNESS + // + // Consoles the sharpness of edges on PS3 only. + // Non-PS3 tuning is done with shader input. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 8.0 is sharper + // 4.0 is softer + // 2.0 is really soft (good for vector graphics inputs) + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 8.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 4.0 + #endif + #if 0 + #define FXAA_CONSOLE__PS3_EDGE_SHARPNESS 2.0 + #endif + #endif + /*--------------------------------------------------------------------------*/ + #ifndef FXAA_CONSOLE__PS3_EDGE_THRESHOLD + // + // Only effects PS3. + // Non-PS3 tuning is done with shader input. + // + // The minimum amount of local contrast required to apply algorithm. + // The console setting has a different mapping than the quality setting. + // + // This only applies when FXAA_EARLY_EXIT is 1. + // + // Due to the PS3 being ALU bound, + // there are only two safe values here: 0.25 and 0.125. + // These options use the shaders ability to a free *|/ by 2|4|8. + // + // 0.125 leaves less aliasing, but is softer + // 0.25 leaves more aliasing, and is sharper + // + #if 1 + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.125 + #else + #define FXAA_CONSOLE__PS3_EDGE_THRESHOLD 0.25 + #endif + #endif + + /*============================================================================ + FXAA QUALITY - TUNING KNOBS + ------------------------------------------------------------------------------ + NOTE the other tuning knobs are now in the shader function inputs! + ============================================================================*/ + #ifndef FXAA_QUALITY__PRESET + // + // Choose the quality preset. + // This needs to be compiled into the shader as it effects code. + // Best option to include multiple presets is to + // in each shader define the preset, then include this file. + // + // OPTIONS + // ----------------------------------------------------------------------- + // 10 to 15 - default medium dither (10=fastest, 15=highest quality) + // 20 to 29 - less dither, more expensive (20=fastest, 29=highest quality) + // 39 - no dither, very expensive + // + // NOTES + // ----------------------------------------------------------------------- + // 12 = slightly faster then FXAA 3.9 and higher edge quality (default) + // 13 = about same speed as FXAA 3.9 and better than 12 + // 23 = closest to FXAA 3.9 visually and performance wise + // _ = the lowest digit is directly related to performance + // _ = the highest digit is directly related to style + // + #define FXAA_QUALITY__PRESET 12 + #endif + + + /*============================================================================ + + FXAA QUALITY - PRESETS + + ============================================================================*/ + + /*============================================================================ + FXAA QUALITY - MEDIUM DITHER PRESETS + ============================================================================*/ + #if (FXAA_QUALITY__PRESET == 10) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 3.0 + #define FXAA_QUALITY__P2 12.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 11) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 3.0 + #define FXAA_QUALITY__P3 12.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 12) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 4.0 + #define FXAA_QUALITY__P4 12.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 13) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 4.0 + #define FXAA_QUALITY__P5 12.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 14) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 4.0 + #define FXAA_QUALITY__P6 12.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 15) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 12.0 + #endif + + /*============================================================================ + FXAA QUALITY - LOW DITHER PRESETS + ============================================================================*/ + #if (FXAA_QUALITY__PRESET == 20) + #define FXAA_QUALITY__PS 3 + #define FXAA_QUALITY__P0 1.5 + #define FXAA_QUALITY__P1 2.0 + #define FXAA_QUALITY__P2 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 21) + #define FXAA_QUALITY__PS 4 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 22) + #define FXAA_QUALITY__PS 5 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 23) + #define FXAA_QUALITY__PS 6 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 24) + #define FXAA_QUALITY__PS 7 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 3.0 + #define FXAA_QUALITY__P6 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 25) + #define FXAA_QUALITY__PS 8 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 4.0 + #define FXAA_QUALITY__P7 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 26) + #define FXAA_QUALITY__PS 9 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 4.0 + #define FXAA_QUALITY__P8 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 27) + #define FXAA_QUALITY__PS 10 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 4.0 + #define FXAA_QUALITY__P9 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 28) + #define FXAA_QUALITY__PS 11 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 4.0 + #define FXAA_QUALITY__P10 8.0 + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PRESET == 29) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.5 + #define FXAA_QUALITY__P2 2.0 + #define FXAA_QUALITY__P3 2.0 + #define FXAA_QUALITY__P4 2.0 + #define FXAA_QUALITY__P5 2.0 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 + #endif + + /*============================================================================ + FXAA QUALITY - EXTREME QUALITY + ============================================================================*/ + #if (FXAA_QUALITY__PRESET == 39) + #define FXAA_QUALITY__PS 12 + #define FXAA_QUALITY__P0 1.0 + #define FXAA_QUALITY__P1 1.0 + #define FXAA_QUALITY__P2 1.0 + #define FXAA_QUALITY__P3 1.0 + #define FXAA_QUALITY__P4 1.0 + #define FXAA_QUALITY__P5 1.5 + #define FXAA_QUALITY__P6 2.0 + #define FXAA_QUALITY__P7 2.0 + #define FXAA_QUALITY__P8 2.0 + #define FXAA_QUALITY__P9 2.0 + #define FXAA_QUALITY__P10 4.0 + #define FXAA_QUALITY__P11 8.0 + #endif + + + + /*============================================================================ + + API PORTING + + ============================================================================*/ + #if (FXAA_GLSL_120 == 1) || (FXAA_GLSL_130 == 1) + #define FxaaBool bool + #define FxaaDiscard discard + #define FxaaFloat float + #define FxaaFloat2 vec2 + #define FxaaFloat3 vec3 + #define FxaaFloat4 vec4 + #define FxaaHalf float + #define FxaaHalf2 vec2 + #define FxaaHalf3 vec3 + #define FxaaHalf4 vec4 + #define FxaaInt2 ivec2 + #define FxaaSat(x) clamp(x, 0.0, 1.0) + #define FxaaTex sampler2D + #else + #define FxaaBool bool + #define FxaaDiscard clip(-1) + #define FxaaFloat float + #define FxaaFloat2 float2 + #define FxaaFloat3 float3 + #define FxaaFloat4 float4 + #define FxaaHalf half + #define FxaaHalf2 half2 + #define FxaaHalf3 float3 + #define FxaaHalf4 half4 + #define FxaaSat(x) saturate(x) + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_GLSL_120 == 1) + // Requires, + // #version 120 + // And at least, + // #extension GL_EXT_gpu_shader4 : enable + // (or set FXAA_FAST_PIXEL_OFFSET 1 to work like DX9) + #define FxaaTexTop(t, p) texture2DLod(t, p, 0.0) + #if (FXAA_FAST_PIXEL_OFFSET == 1) + #define FxaaTexOff(t, p, o, r) texture2DLodOffset(t, p, 0.0, o) + #else + #define FxaaTexOff(t, p, o, r) texture2DLod(t, p + (o * r), 0.0) + #endif + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_GLSL_130 == 1) + // Requires "#version 130" or better + #define FxaaTexTop(t, p) textureLod(t, p, 0.0) + #define FxaaTexOff(t, p, o, r) textureLodOffset(t, p, 0.0, o) + #if (FXAA_GATHER4_ALPHA == 1) + // use #extension GL_ARB_gpu_shader5 : enable + #define FxaaTexAlpha4(t, p) textureGather(t, p, 3) + #define FxaaTexOffAlpha4(t, p, o) textureGatherOffset(t, p, o, 3) + #define FxaaTexGreen4(t, p) textureGather(t, p, 1) + #define FxaaTexOffGreen4(t, p, o) textureGatherOffset(t, p, o, 1) + #endif + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_HLSL_3 == 1) || (FXAA_360 == 1) || (FXAA_PS3 == 1) + #define FxaaInt2 float2 + #define FxaaTex sampler2D + #define FxaaTexTop(t, p) tex2Dlod(t, float4(p, 0.0, 0.0)) + #define FxaaTexOff(t, p, o, r) tex2Dlod(t, float4(p + (o * r), 0, 0)) + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_HLSL_4 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_HLSL_5 == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLevel(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLevel(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) + #endif + #if (GNM_HLSL == 1) + #define FxaaInt2 int2 + struct FxaaTex { SamplerState smpl; Texture2D tex; }; + #define FxaaTexTop(t, p) t.tex.SampleLOD(t.smpl, p, 0.0) + #define FxaaTexOff(t, p, o, r) t.tex.SampleLOD(t.smpl, p, 0.0, o) + #define FxaaTexAlpha4(t, p) t.tex.GatherAlpha(t.smpl, p) + #define FxaaTexOffAlpha4(t, p, o) t.tex.GatherAlpha(t.smpl, p, o) + #define FxaaTexGreen4(t, p) t.tex.GatherGreen(t.smpl, p) + #define FxaaTexOffGreen4(t, p, o) t.tex.GatherGreen(t.smpl, p, o) + #endif + + + /*============================================================================ + GREEN AS LUMA OPTION SUPPORT FUNCTION + ============================================================================*/ + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.w; } + #else + FxaaFloat FxaaLuma(FxaaFloat4 rgba) { return rgba.y; } + #endif + + + #if !defined(RENDERER_GNM) + #define FXAACONSOLEPOSPOS fxaaConsolePosPos + #define FXAACONSOLE360TEXEXPBIASNEGONE fxaaConsole360TexExpBiasNegOne + #define FXAACONSOLE360TEXEXPBIASNEGTWO fxaaConsole360TexExpBiasNegTwo + #define FXAACONSOLERCPFRAMEOPT fxaaConsoleRcpFrameOpt + #define FXAACONSOLERCPFRAMEOPT2 fxaaConsoleRcpFrameOpt2 + #define FXAACONSOLE360RCPFRAMEOPT2 fxaaConsole360RcpFrameOpt2 + #define FXAACONSOLEEDGESHARPNESS fxaaConsoleEdgeSharpness + #define FXAACONSOLEEDGETHRESHOLD fxaaConsoleEdgeThreshold + #define FXAACONSOLEEDGETHRESHOLDMIN fxaaConsoleEdgeThresholdMin + #define FXAACONSOLE360CONSTDIR fxaaConsole360ConstDir + #else + #define FXAACONSOLEPOSPOS + #define FXAACONSOLE360TEXEXPBIASNEGONE + #define FXAACONSOLE360TEXEXPBIASNEGTWO + #define FXAACONSOLERCPFRAMEOPT + #define FXAACONSOLERCPFRAMEOPT2 + #define FXAACONSOLE360RCPFRAMEOPT2 + #define FXAACONSOLEEDGESHARPNESS + #define FXAACONSOLEEDGETHRESHOLD + #define FXAACONSOLEEDGETHRESHOLDMIN + #define FXAACONSOLE360CONSTDIR + #endif + + /*============================================================================ + + FXAA3 QUALITY - PC + + ============================================================================*/ + #if (FXAA_PC == 1) + /*--------------------------------------------------------------------------*/ + FxaaFloat4 FxaaPixelShader( + // + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy} = center of pixel + FxaaFloat2 pos, + // + // Used only for FXAA Console, and not used on the 360 version. + // Use noperspective interpolation here (turn off perspective interpolation). + // {xy__} = upper left of pixel + // {__zw} = lower right of pixel + FxaaFloat4 FXAACONSOLEPOSPOS, + // + // Input color texture. + // {rgb_} = color in linear or perceptual color space + // if (FXAA_GREEN_AS_LUMA == 0) + // {___a} = luma in perceptual color space (not linear) + FxaaTex tex, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 2nd sampler. + // This sampler needs to have an exponent bias of -1. + FxaaTex FXAACONSOLE360TEXEXPBIASNEGONE, + // + // Only used on the optimized 360 version of FXAA Console. + // For everything but 360, just use the same input here as for "tex". + // For 360, same texture, just alias with a 3nd sampler. + // This sampler needs to have an exponent bias of -2. + FxaaTex FXAACONSOLE360TEXEXPBIASNEGTWO, + // + // Only used on FXAA Quality. + // This must be from a constant/uniform. + // {x_} = 1.0/screenWidthInPixels + // {_y} = 1.0/screenHeightInPixels + FxaaFloat2 fxaaQualityRcpFrame, + // + // Only used on FXAA Console. + // This must be from a constant/uniform. + // This effects sub-pixel AA quality and inversely sharpness. + // Where N ranges between, + // N = 0.50 (default) + // N = 0.33 (sharper) + // {x___} = -N/screenWidthInPixels + // {_y__} = -N/screenHeightInPixels + // {__z_} = N/screenWidthInPixels + // {___w} = N/screenHeightInPixels + FxaaFloat4 FXAACONSOLERCPFRAMEOPT, + // + // Only used on FXAA Console. + // Not used on 360, but used on PS3 and PC. + // This must be from a constant/uniform. + // {x___} = -2.0/screenWidthInPixels + // {_y__} = -2.0/screenHeightInPixels + // {__z_} = 2.0/screenWidthInPixels + // {___w} = 2.0/screenHeightInPixels + FxaaFloat4 FXAACONSOLERCPFRAMEOPT2, + // + // Only used on FXAA Console. + // Only used on 360 in place of fxaaConsoleRcpFrameOpt2. + // This must be from a constant/uniform. + // {x___} = 8.0/screenWidthInPixels + // {_y__} = 8.0/screenHeightInPixels + // {__z_} = -4.0/screenWidthInPixels + // {___w} = -4.0/screenHeightInPixels + FxaaFloat4 FXAACONSOLE360RCPFRAMEOPT2, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__SUBPIX define. + // It is here now to allow easier tuning. + // Choose the amount of sub-pixel aliasing removal. + // This can effect sharpness. + // 1.00 - upper limit (softer) + // 0.75 - default amount of filtering + // 0.50 - lower limit (sharper, less sub-pixel aliasing removal) + // 0.25 - almost off + // 0.00 - completely off + FxaaFloat fxaaQualitySubpix, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // The minimum amount of local contrast required to apply algorithm. + // 0.333 - too little (faster) + // 0.250 - low quality + // 0.166 - default + // 0.125 - high quality + // 0.063 - overkill (slower) + FxaaFloat fxaaQualityEdgeThreshold, + // + // Only used on FXAA Quality. + // This used to be the FXAA_QUALITY__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // 0.0833 - upper limit (default, the start of visible unfiltered edges) + // 0.0625 - high quality (faster) + // 0.0312 - visible limit (slower) + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat fxaaQualityEdgeThresholdMin, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_SHARPNESS define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_SHARPNESS for PS3. + // Due to the PS3 being ALU bound, + // there are only three safe values here: 2 and 4 and 8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // For all other platforms can be a non-power of two. + // 8.0 is sharper (default!!!) + // 4.0 is softer + // 2.0 is really soft (good only for vector graphics inputs) + FxaaFloat FXAACONSOLEEDGESHARPNESS, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD define. + // It is here now to allow easier tuning. + // This does not effect PS3, as this needs to be compiled in. + // Use FXAA_CONSOLE__PS3_EDGE_THRESHOLD for PS3. + // Due to the PS3 being ALU bound, + // there are only two safe values here: 1/4 and 1/8. + // These options use the shaders ability to a free *|/ by 2|4|8. + // The console setting has a different mapping than the quality setting. + // Other platforms can use other values. + // 0.125 leaves less aliasing, but is softer (default!!!) + // 0.25 leaves more aliasing, and is sharper + FxaaFloat FXAACONSOLEEDGETHRESHOLD, + // + // Only used on FXAA Console. + // This used to be the FXAA_CONSOLE__EDGE_THRESHOLD_MIN define. + // It is here now to allow easier tuning. + // Trims the algorithm from processing darks. + // The console setting has a different mapping than the quality setting. + // This only applies when FXAA_EARLY_EXIT is 1. + // This does not apply to PS3, + // PS3 was simplified to avoid more shader instructions. + // 0.06 - faster but more aliasing in darks + // 0.05 - default + // 0.04 - slower and less aliasing in darks + // Special notes when using FXAA_GREEN_AS_LUMA, + // Likely want to set this to zero. + // As colors that are mostly not-green + // will appear very dark in the green channel! + // Tune by looking at mostly non-green content, + // then start at zero and increase until aliasing is a problem. + FxaaFloat FXAACONSOLEEDGETHRESHOLDMIN, + // + // Extra constants for 360 FXAA Console only. + // Use zeros or anything else for other platforms. + // These must be in physical constant registers and NOT immedates. + // Immedates will result in compiler un-optimizing. + // {xyzw} = float4(1.0, -1.0, 0.25, -0.25) + FxaaFloat4 FXAACONSOLE360CONSTDIR + ) { + /*--------------------------------------------------------------------------*/ + FxaaFloat2 posM; + posM.x = pos.x; + posM.y = pos.y; + #if (FXAA_GATHER4_ALPHA == 1) + #if (FXAA_DISCARD == 0) + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + #endif + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat4 luma4A = FxaaTexAlpha4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffAlpha4(tex, posM, FxaaInt2(-1, -1)); + #else + FxaaFloat4 luma4A = FxaaTexGreen4(tex, posM); + FxaaFloat4 luma4B = FxaaTexOffGreen4(tex, posM, FxaaInt2(-1, -1)); + #endif + #if (FXAA_DISCARD == 1) + #define lumaM luma4A.w + #endif + #define lumaE luma4A.z + #define lumaS luma4A.x + #define lumaSE luma4A.y + #define lumaNW luma4B.w + #define lumaN luma4B.z + #define lumaW luma4B.x + #else + FxaaFloat4 rgbyM = FxaaTexTop(tex, posM); + #if (FXAA_GREEN_AS_LUMA == 0) + #define lumaM rgbyM.w + #else + #define lumaM rgbyM.y + #endif + FxaaFloat lumaS = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 0), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaN = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 0,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 0), fxaaQualityRcpFrame.xy)); + #endif + /*--------------------------------------------------------------------------*/ + FxaaFloat maxSM = max(lumaS, lumaM); + FxaaFloat minSM = min(lumaS, lumaM); + FxaaFloat maxESM = max(lumaE, maxSM); + FxaaFloat minESM = min(lumaE, minSM); + FxaaFloat maxWN = max(lumaN, lumaW); + FxaaFloat minWN = min(lumaN, lumaW); + FxaaFloat rangeMax = max(maxWN, maxESM); + FxaaFloat rangeMin = min(minWN, minESM); + FxaaFloat rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; + FxaaFloat range = rangeMax - rangeMin; + FxaaFloat rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); + FxaaBool earlyExit = range < rangeMaxClamped; + /*--------------------------------------------------------------------------*/ + if(earlyExit) + #if (FXAA_DISCARD == 1) + FxaaDiscard; + #else + return rgbyM; + #endif + /*--------------------------------------------------------------------------*/ + #if (FXAA_GATHER4_ALPHA == 0) + FxaaFloat lumaNW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1, 1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2( 1,-1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #else + FxaaFloat lumaNE = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(1, -1), fxaaQualityRcpFrame.xy)); + FxaaFloat lumaSW = FxaaLuma(FxaaTexOff(tex, posM, FxaaInt2(-1, 1), fxaaQualityRcpFrame.xy)); + #endif + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaNS = lumaN + lumaS; + FxaaFloat lumaWE = lumaW + lumaE; + FxaaFloat subpixRcpRange = 1.0/range; + FxaaFloat subpixNSWE = lumaNS + lumaWE; + FxaaFloat edgeHorz1 = (-2.0 * lumaM) + lumaNS; + FxaaFloat edgeVert1 = (-2.0 * lumaM) + lumaWE; + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaNESE = lumaNE + lumaSE; + FxaaFloat lumaNWNE = lumaNW + lumaNE; + FxaaFloat edgeHorz2 = (-2.0 * lumaE) + lumaNESE; + FxaaFloat edgeVert2 = (-2.0 * lumaN) + lumaNWNE; + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaNWSW = lumaNW + lumaSW; + FxaaFloat lumaSWSE = lumaSW + lumaSE; + FxaaFloat edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); + FxaaFloat edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); + FxaaFloat edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; + FxaaFloat edgeVert3 = (-2.0 * lumaS) + lumaSWSE; + FxaaFloat edgeHorz = abs(edgeHorz3) + edgeHorz4; + FxaaFloat edgeVert = abs(edgeVert3) + edgeVert4; + /*--------------------------------------------------------------------------*/ + FxaaFloat subpixNWSWNESE = lumaNWSW + lumaNESE; + FxaaFloat lengthSign = fxaaQualityRcpFrame.x; + FxaaBool horzSpan = edgeHorz >= edgeVert; + FxaaFloat subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; + /*--------------------------------------------------------------------------*/ + if(!horzSpan) lumaN = lumaW; + if(!horzSpan) lumaS = lumaE; + if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; + FxaaFloat subpixB = (subpixA * (1.0/12.0)) - lumaM; + /*--------------------------------------------------------------------------*/ + FxaaFloat gradientN = lumaN - lumaM; + FxaaFloat gradientS = lumaS - lumaM; + FxaaFloat lumaNN = lumaN + lumaM; + FxaaFloat lumaSS = lumaS + lumaM; + FxaaBool pairN = abs(gradientN) >= abs(gradientS); + FxaaFloat gradient = max(abs(gradientN), abs(gradientS)); + if(pairN) lengthSign = -lengthSign; + FxaaFloat subpixC = FxaaSat(abs(subpixB) * subpixRcpRange); + /*--------------------------------------------------------------------------*/ + FxaaFloat2 posB; + posB.x = posM.x; + posB.y = posM.y; + FxaaFloat2 offNP; + offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; + offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; + if(!horzSpan) posB.x += lengthSign * 0.5; + if( horzSpan) posB.y += lengthSign * 0.5; + /*--------------------------------------------------------------------------*/ + FxaaFloat2 posN; + posN.x = posB.x - offNP.x * FXAA_QUALITY__P0; + posN.y = posB.y - offNP.y * FXAA_QUALITY__P0; + FxaaFloat2 posP; + posP.x = posB.x + offNP.x * FXAA_QUALITY__P0; + posP.y = posB.y + offNP.y * FXAA_QUALITY__P0; + FxaaFloat subpixD = ((-2.0)*subpixC) + 3.0; + FxaaFloat lumaEndN = FxaaLuma(FxaaTexTop(tex, posN)); + FxaaFloat subpixE = subpixC * subpixC; + FxaaFloat lumaEndP = FxaaLuma(FxaaTexTop(tex, posP)); + /*--------------------------------------------------------------------------*/ + if(!pairN) lumaNN = lumaSS; + FxaaFloat gradientScaled = gradient * 1.0/4.0; + FxaaFloat lumaMM = lumaM - lumaNN * 0.5; + FxaaFloat subpixF = subpixD * subpixE; + FxaaBool lumaMLTZero = lumaMM < 0.0; + /*--------------------------------------------------------------------------*/ + lumaEndN -= lumaNN * 0.5; + lumaEndP -= lumaNN * 0.5; + FxaaBool doneN = abs(lumaEndN) >= gradientScaled; + FxaaBool doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P1; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P1; + FxaaBool doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P1; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P1; + /*--------------------------------------------------------------------------*/ + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P2; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P2; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P2; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P2; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 3) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P3; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P3; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P3; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P3; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 4) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P4; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P4; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P4; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P4; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 5) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P5; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P5; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P5; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P5; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 6) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P6; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P6; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P6; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P6; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 7) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P7; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P7; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P7; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P7; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 8) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P8; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P8; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P8; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P8; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 9) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P9; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P9; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P9; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P9; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 10) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P10; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P10; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P10; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P10; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 11) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P11; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P11; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P11; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P11; + /*--------------------------------------------------------------------------*/ + #if (FXAA_QUALITY__PS > 12) + if(doneNP) { + if(!doneN) lumaEndN = FxaaLuma(FxaaTexTop(tex, posN.xy)); + if(!doneP) lumaEndP = FxaaLuma(FxaaTexTop(tex, posP.xy)); + if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; + if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; + doneN = abs(lumaEndN) >= gradientScaled; + doneP = abs(lumaEndP) >= gradientScaled; + if(!doneN) posN.x -= offNP.x * FXAA_QUALITY__P12; + if(!doneN) posN.y -= offNP.y * FXAA_QUALITY__P12; + doneNP = (!doneN) || (!doneP); + if(!doneP) posP.x += offNP.x * FXAA_QUALITY__P12; + if(!doneP) posP.y += offNP.y * FXAA_QUALITY__P12; + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + #endif + /*--------------------------------------------------------------------------*/ + } + /*--------------------------------------------------------------------------*/ + FxaaFloat dstN = posM.x - posN.x; + FxaaFloat dstP = posP.x - posM.x; + if(!horzSpan) dstN = posM.y - posN.y; + if(!horzSpan) dstP = posP.y - posM.y; + /*--------------------------------------------------------------------------*/ + FxaaBool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; + FxaaFloat spanLength = (dstP + dstN); + FxaaBool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; + FxaaFloat spanLengthRcp = 1.0/spanLength; + /*--------------------------------------------------------------------------*/ + FxaaBool directionN = dstN < dstP; + FxaaFloat destination = min(dstN, dstP); + FxaaBool goodSpan = directionN ? goodSpanN : goodSpanP; + FxaaFloat subpixG = subpixF * subpixF; + FxaaFloat pixelOffset = (destination * (-spanLengthRcp)) + 0.5; + FxaaFloat subpixH = subpixG * fxaaQualitySubpix; + /*--------------------------------------------------------------------------*/ + FxaaFloat pixelOffsetGood = goodSpan ? pixelOffset : 0.0; + FxaaFloat pixelOffsetSubpix = max(pixelOffsetGood, subpixH); + if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; + if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; + #if (FXAA_DISCARD == 1) + return FxaaTexTop(tex, posM); + #else + return FxaaFloat4(FxaaTexTop(tex, posM).xyz, lumaM); + #endif + } + /*==========================================================================*/ + #endif + + + + + /*============================================================================ + + FXAA3 CONSOLE - PC VERSION + + ------------------------------------------------------------------------------ + Instead of using this on PC, I'd suggest just using FXAA Quality with + #define FXAA_QUALITY__PRESET 10 + Or + #define FXAA_QUALITY__PRESET 20 + Either are higher qualilty and almost as fast as this on modern PC GPUs. + ============================================================================*/ + #if (FXAA_PC_CONSOLE == 1) + /*--------------------------------------------------------------------------*/ + FxaaFloat4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + ) { + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaNw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xy)); + FxaaFloat lumaSw = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.xw)); + FxaaFloat lumaNe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zy)); + FxaaFloat lumaSe = FxaaLuma(FxaaTexTop(tex, fxaaConsolePosPos.zw)); + /*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyM = FxaaTexTop(tex, pos.xy); + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaFloat lumaM = rgbyM.w; + #else + FxaaFloat lumaM = rgbyM.y; + #endif + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNwSw = max(lumaNw, lumaSw); + lumaNe += 1.0/384.0; + FxaaFloat lumaMinNwSw = min(lumaNw, lumaSw); + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxNeSe = max(lumaNe, lumaSe); + FxaaFloat lumaMinNeSe = min(lumaNe, lumaSe); + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaMax = max(lumaMaxNeSe, lumaMaxNwSw); + FxaaFloat lumaMin = min(lumaMinNeSe, lumaMinNwSw); + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaMaxScaled = lumaMax * fxaaConsoleEdgeThreshold; + /*--------------------------------------------------------------------------*/ + FxaaFloat lumaMinM = min(lumaMin, lumaM); + FxaaFloat lumaMaxScaledClamped = max(fxaaConsoleEdgeThresholdMin, lumaMaxScaled); + FxaaFloat lumaMaxM = max(lumaMax, lumaM); + FxaaFloat dirSwMinusNe = lumaSw - lumaNe; + FxaaFloat lumaMaxSubMinM = lumaMaxM - lumaMinM; + FxaaFloat dirSeMinusNw = lumaSe - lumaNw; + if(lumaMaxSubMinM < lumaMaxScaledClamped) return rgbyM; + /*--------------------------------------------------------------------------*/ + FxaaFloat2 dir; + dir.x = dirSwMinusNe + dirSeMinusNw; + dir.y = dirSwMinusNe - dirSeMinusNw; + /*--------------------------------------------------------------------------*/ + FxaaFloat2 dir1 = normalize(dir.xy); + FxaaFloat4 rgbyN1 = FxaaTexTop(tex, pos.xy - dir1 * fxaaConsoleRcpFrameOpt.zw); + FxaaFloat4 rgbyP1 = FxaaTexTop(tex, pos.xy + dir1 * fxaaConsoleRcpFrameOpt.zw); + /*--------------------------------------------------------------------------*/ + FxaaFloat dirAbsMinTimesC = min(abs(dir1.x), abs(dir1.y)) * fxaaConsoleEdgeSharpness; + FxaaFloat2 dir2 = clamp(dir1.xy / dirAbsMinTimesC, -2.0, 2.0); + /*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyN2 = FxaaTexTop(tex, pos.xy - dir2 * fxaaConsoleRcpFrameOpt2.zw); + FxaaFloat4 rgbyP2 = FxaaTexTop(tex, pos.xy + dir2 * fxaaConsoleRcpFrameOpt2.zw); + /*--------------------------------------------------------------------------*/ + FxaaFloat4 rgbyA = rgbyN1 + rgbyP1; + FxaaFloat4 rgbyB = ((rgbyN2 + rgbyP2) * 0.25) + (rgbyA * 0.25); + /*--------------------------------------------------------------------------*/ + #if (FXAA_GREEN_AS_LUMA == 0) + FxaaBool twoTap = (rgbyB.w < lumaMin) || (rgbyB.w > lumaMax); + #else + FxaaBool twoTap = (rgbyB.y < lumaMin) || (rgbyB.y > lumaMax); + #endif + if(twoTap) rgbyB.xyz = rgbyA.xyz * 0.5; + return rgbyB; } + /*==========================================================================*/ + #endif + + + + /*============================================================================ + + FXAA3 CONSOLE - 360 PIXEL SHADER + + ------------------------------------------------------------------------------ + This optimized version thanks to suggestions from Andy Luedke. + Should be fully tex bound in all cases. + As of the FXAA 3.11 release, I have still not tested this code, + however I fixed a bug which was in both FXAA 3.9 and FXAA 3.10. + And note this is replacing the old unoptimized version. + If it does not work, please let me know so I can fix it. + ============================================================================*/ + #if (FXAA_360 == 1) + /*--------------------------------------------------------------------------*/ + [reduceTempRegUsage(4)] + float4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + ) { + /*--------------------------------------------------------------------------*/ + float4 lumaNwNeSwSe; + #if (FXAA_GREEN_AS_LUMA == 0) + asm { + tfetch2D lumaNwNeSwSe.w___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._w__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__w_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___w, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #else + asm { + tfetch2D lumaNwNeSwSe.y___, tex, pos.xy, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe._y__, tex, pos.xy, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.__y_, tex, pos.xy, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false + tfetch2D lumaNwNeSwSe.___y, tex, pos.xy, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false + }; + #endif + /*--------------------------------------------------------------------------*/ + lumaNwNeSwSe.y += 1.0/384.0; + float2 lumaMinTemp = min(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float2 lumaMaxTemp = max(lumaNwNeSwSe.xy, lumaNwNeSwSe.zw); + float lumaMin = min(lumaMinTemp.x, lumaMinTemp.y); + float lumaMax = max(lumaMaxTemp.x, lumaMaxTemp.y); + /*--------------------------------------------------------------------------*/ + float4 rgbyM = tex2Dlod(tex, float4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + float lumaMinM = min(lumaMin, rgbyM.w); + float lumaMaxM = max(lumaMax, rgbyM.w); + #else + float lumaMinM = min(lumaMin, rgbyM.y); + float lumaMaxM = max(lumaMax, rgbyM.y); + #endif + if((lumaMaxM - lumaMinM) < max(fxaaConsoleEdgeThresholdMin, lumaMax * fxaaConsoleEdgeThreshold)) return rgbyM; + /*--------------------------------------------------------------------------*/ + float2 dir; + dir.x = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.yyxx); + dir.y = dot(lumaNwNeSwSe, fxaaConsole360ConstDir.xyxy); + dir = normalize(dir); + /*--------------------------------------------------------------------------*/ + float4 dir1 = dir.xyxy * fxaaConsoleRcpFrameOpt.xyzw; + /*--------------------------------------------------------------------------*/ + float4 dir2; + float dirAbsMinTimesC = min(abs(dir.x), abs(dir.y)) * fxaaConsoleEdgeSharpness; + dir2 = saturate(fxaaConsole360ConstDir.zzww * dir.xyxy / dirAbsMinTimesC + 0.5); + dir2 = dir2 * fxaaConsole360RcpFrameOpt2.xyxy + fxaaConsole360RcpFrameOpt2.zwzw; + /*--------------------------------------------------------------------------*/ + float4 rgbyN1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.xy, 0.0, 0.0)); + float4 rgbyP1 = tex2Dlod(fxaaConsole360TexExpBiasNegOne, float4(pos.xy + dir1.zw, 0.0, 0.0)); + float4 rgbyN2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.xy, 0.0, 0.0)); + float4 rgbyP2 = tex2Dlod(fxaaConsole360TexExpBiasNegTwo, float4(pos.xy + dir2.zw, 0.0, 0.0)); + /*--------------------------------------------------------------------------*/ + float4 rgbyA = rgbyN1 + rgbyP1; + float4 rgbyB = rgbyN2 + rgbyP2 * 0.5 + rgbyA; + /*--------------------------------------------------------------------------*/ + float4 rgbyR = ((rgbyB.w - lumaMax) > 0.0) ? rgbyA : rgbyB; + rgbyR = ((rgbyB.w - lumaMin) > 0.0) ? rgbyR : rgbyA; + return rgbyR; } + /*==========================================================================*/ + #endif + + + + /*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (NO EARLY EXIT) + + ============================================================================== + The code below does not exactly match the assembly. + I have a feeling that 12 cycles is possible, but was not able to get there. + Might have to increase register count to get full performance. + Note this shader does not use perspective interpolation. + + Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + + ------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT + ------------------------------------------------------------------------------ + For reference and to aid in debug, output of NVShaderPerf should match this, + + Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.z(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h0.w(TRUE), v5.xwxx, #0 + 6: addh h0.z(TRUE), -h2, h0.w + 7: texpkb h1.w(TRUE), v5, #0 + 9: addh h0.x(TRUE), h0.z, -h1.w + 10: addh h3.w(TRUE), h0.z, h1 + 11: texpkb h2.w(TRUE), v5.zwzz, #0 + 13: addh h0.z(TRUE), h3.w, -h2.w + 14: addh h0.x(TRUE), h2.w, h0 + 15: nrmh h1.xz(TRUE), h0_n + 16: minh_m8 h0.x(TRUE), |h1|, |h1.z| + 17: maxh h4.w(TRUE), h0, h1 + 18: divx h2.xy(TRUE), h1_n.xzzw, h0_n + 19: movr r1.zw(TRUE), v4.xxxy + 20: madr r2.xz(TRUE), -h1, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zzww, r1.zzww + 22: minh h5.w(TRUE), h0, h1 + 23: texpkb h0(TRUE), r2.xzxx, #0 + 25: madr r0.zw(TRUE), h1.xzxz, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w), r1 + 27: maxh h4.x(TRUE), h2.z, h2.w + 28: texpkb h1(TRUE), r0.zwzz, #0 + 30: addh_d2 h1(TRUE), h0, h1 + 31: madr r0.xy(TRUE), -h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 33: texpkb h0(TRUE), r0, #0 + 35: minh h4.z(TRUE), h2, h2.w + 36: fenct TRUE + 37: madr r1.xy(TRUE), h2, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 39: texpkb h2(TRUE), r1, #0 + 41: addh_d2 h0(TRUE), h0, h2 + 42: maxh h2.w(TRUE), h4, h4.x + 43: minh h2.x(TRUE), h5.w, h4.z + 44: addh_d2 h0(TRUE), h0, h1 + 45: slth h2.x(TRUE), h0.w, h2 + 46: sgth h2.w(TRUE), h0, h2 + 47: movh h0(TRUE), h0 + 48: addx.c0 rc(TRUE), h2, h2.w + 49: movh h0(c0.NE.x), h1 + + IPU0 ------ Simplified schedule: -------- + Pass | Unit | uOp | PC: Op + -----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB1 | add | 2: ADDh h2.z, h0.--w-, const.--x-; + | | | + 2 | SCT0/1 | mov | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h0.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB1 | add | 6: ADDh h0.z,-h2, h0.--w-; + | | | + 3 | SCT0/1 | mov | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 7: TXLr h1.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 9: ADDh h0.x, h0.z---,-h1.w---; + | SCB1 | add | 10: ADDh h3.w, h0.---z, h1; + | | | + 4 | SCT0/1 | mov | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 11: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h2.w---, h0; + | SCB1 | add | 13: ADDh h0.z, h3.--w-,-h2.--w-; + | | | + 5 | SCT1 | mov | 15: NRMh h1.xz, h0; + | SRB | nrm | 15: NRMh h1.xz, h0; + | SCB0 | min | 16: MINh*8 h0.x, |h1|, |h1.z---|; + | SCB1 | max | 17: MAXh h4.w, h0, h1; + | | | + 6 | SCT0 | div | 18: DIVx h2.xy, h1.xz--, h0; + | SCT1 | mov | 19: MOVr r1.zw, g[TEX0].--xy; + | SCB0 | mad | 20: MADr r2.xz,-h1, const.z-w-, r1.z-w-; + | SCB1 | min | 22: MINh h5.w, h0, h1; + | | | + 7 | SCT0/1 | mov | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | TEX | txl | 23: TXLr h0, r2.xzxx, const.xxxx, TEX0; + | SCB0 | max | 27: MAXh h4.x, h2.z---, h2.w---; + | SCB1 | mad | 25: MADr r0.zw, h1.--xz, const, r1; + | | | + 8 | SCT0/1 | mov | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | TEX | txl | 28: TXLr h1, r0.zwzz, const.xxxx, TEX0; + | SCB0/1 | add | 30: ADDh/2 h1, h0, h1; + | | | + 9 | SCT0 | mad | 31: MADr r0.xy,-h2, const.xy--, r1.zw--; + | SCT1 | mov | 33: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 33: TXLr h0, r0, const.zzzz, TEX0; + | SCB1 | min | 35: MINh h4.z, h2, h2.--w-; + | | | + 10 | SCT0 | mad | 37: MADr r1.xy, h2, const.xy--, r1.zw--; + | SCT1 | mov | 39: TXLr h2, r1, const.zzzz, TEX0; + | TEX | txl | 39: TXLr h2, r1, const.zzzz, TEX0; + | SCB0/1 | add | 41: ADDh/2 h0, h0, h2; + | | | + 11 | SCT0 | min | 43: MINh h2.x, h5.w---, h4.z---; + | SCT1 | max | 42: MAXh h2.w, h4, h4.---x; + | SCB0/1 | add | 44: ADDh/2 h0, h0, h1; + | | | + 12 | SCT0 | set | 45: SLTh h2.x, h0.w---, h2; + | SCT1 | set | 46: SGTh h2.w, h0, h2; + | SCB0/1 | mul | 47: MOVh h0, h0; + | | | + 13 | SCT0 | mad | 48: ADDxc0_s rc, h2, h2.w---; + | SCB0/1 | mul | 49: MOVh h0(NE0.xxxx), h1; + + Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 0% 0% 50% + 6: 100% 0% 75% + 7: 0% 100% 75% + 8: 0% 100% 100% + 9: 0% 100% 25% + 10: 0% 100% 100% + 11: 50% 0% 100% + 12: 50% 0% 100% + 13: 25% 0% 100% + + MEAN: 17% 61% 67% + + Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 0% 100% + 2: 0% 0% 100% 0% 100% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 0% 0% 0% 100% 100% + 6: 100% 100% 0% 100% 100% + 7: 0% 0% 100% 100% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 0% 100% + 10: 0% 0% 100% 100% 100% + 11: 100% 100% 0% 100% 100% + 12: 100% 100% 0% 100% 100% + 13: 100% 0% 0% 100% 100% + + MEAN: 30% 23% 61% 76% 100% + Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 + Results 13 cycles, 3 r regs, 923,076,923 pixels/s + ============================================================================*/ + #if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 0) + /*--------------------------------------------------------------------------*/ + #pragma regcount 7 + #pragma disablepc all + #pragma option O3 + #pragma option OutColorPrec=fp16 + #pragma texformat default RGBA8 + /*==========================================================================*/ + half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + ) { + /*--------------------------------------------------------------------------*/ + // (1) + half4 dir; + half4 lumaNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + lumaNe.w += half(1.0/512.0); + dir.x = -lumaNe.w; + dir.z = -lumaNe.w; + #else + lumaNe.y += half(1.0/512.0); + dir.x = -lumaNe.y; + dir.z = -lumaNe.y; + #endif + /*--------------------------------------------------------------------------*/ + // (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSw.w; + dir.z += lumaSw.w; + #else + dir.x += lumaSw.y; + dir.z += lumaSw.y; + #endif + /*--------------------------------------------------------------------------*/ + // (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x -= lumaNw.w; + dir.z += lumaNw.w; + #else + dir.x -= lumaNw.y; + dir.z += lumaNw.y; + #endif + /*--------------------------------------------------------------------------*/ + // (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x += lumaSe.w; + dir.z -= lumaSe.w; + #else + dir.x += lumaSe.y; + dir.z -= lumaSe.y; + #endif + /*--------------------------------------------------------------------------*/ + // (5) + half4 dir1_pos; + dir1_pos.xy = normalize(dir.xyz).xz; + half dirAbsMinTimesC = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); + /*--------------------------------------------------------------------------*/ + // (6) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimesC, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + /*--------------------------------------------------------------------------*/ + // (7) + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + /*--------------------------------------------------------------------------*/ + // (8) + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; + /*--------------------------------------------------------------------------*/ + // (9) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); + /*--------------------------------------------------------------------------*/ + // (10) + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; + /*--------------------------------------------------------------------------*/ + // (11) + // compilier moves these scalar ops up to other cycles + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMin = min(min(lumaNw.w, lumaSw.w), min(lumaNe.w, lumaSe.w)); + half lumaMax = max(max(lumaNw.w, lumaSw.w), max(lumaNe.w, lumaSe.w)); + #else + half lumaMin = min(min(lumaNw.y, lumaSw.y), min(lumaNe.y, lumaSe.y)); + half lumaMax = max(max(lumaNw.y, lumaSw.y), max(lumaNe.y, lumaSe.y)); + #endif + rgby2 = (rgby2 + rgby1) * 0.5; + /*--------------------------------------------------------------------------*/ + // (12) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif + /*--------------------------------------------------------------------------*/ + // (13) + if(twoTapLt || twoTapGt) rgby2 = rgby1; + /*--------------------------------------------------------------------------*/ + return rgby2; } + /*==========================================================================*/ + #endif + + + + /*============================================================================ + + FXAA3 CONSOLE - OPTIMIZED PS3 PIXEL SHADER (WITH EARLY EXIT) + + ============================================================================== + The code mostly matches the assembly. + I have a feeling that 14 cycles is possible, but was not able to get there. + Might have to increase register count to get full performance. + Note this shader does not use perspective interpolation. + + Use the following cgc options, + + --fenable-bx2 --fastmath --fastprecision --nofloatbindings + + Use of FXAA_GREEN_AS_LUMA currently adds a cycle (16 clks). + Will look at fixing this for FXAA 3.12. + ------------------------------------------------------------------------------ + NVSHADERPERF OUTPUT + ------------------------------------------------------------------------------ + For reference and to aid in debug, output of NVShaderPerf should match this, + + Shader to schedule: + 0: texpkb h0.w(TRUE), v5.zyxx, #0 + 2: addh h2.y(TRUE), h0.w, constant(0.001953, 0.000000, 0.000000, 0.000000).x + 4: texpkb h1.w(TRUE), v5.xwxx, #0 + 6: addh h0.x(TRUE), h1.w, -h2.y + 7: texpkb h2.w(TRUE), v5.zwzz, #0 + 9: minh h4.w(TRUE), h2.y, h2 + 10: maxh h5.x(TRUE), h2.y, h2.w + 11: texpkb h0.w(TRUE), v5, #0 + 13: addh h3.w(TRUE), -h0, h0.x + 14: addh h0.x(TRUE), h0.w, h0 + 15: addh h0.z(TRUE), -h2.w, h0.x + 16: addh h0.x(TRUE), h2.w, h3.w + 17: minh h5.y(TRUE), h0.w, h1.w + 18: nrmh h2.xz(TRUE), h0_n + 19: minh_m8 h2.w(TRUE), |h2.x|, |h2.z| + 20: divx h4.xy(TRUE), h2_n.xzzw, h2_n.w + 21: movr r1.zw(TRUE), v4.xxxy + 22: maxh h2.w(TRUE), h0, h1 + 23: fenct TRUE + 24: madr r0.xy(TRUE), -h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 26: texpkb h0(TRUE), r0, #0 + 28: maxh h5.x(TRUE), h2.w, h5 + 29: minh h5.w(TRUE), h5.y, h4 + 30: madr r1.xy(TRUE), h2.xzzw, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).zwzz, r1.zwzz + 32: texpkb h2(TRUE), r1, #0 + 34: addh_d2 h2(TRUE), h0, h2 + 35: texpkb h1(TRUE), v4, #0 + 37: maxh h5.y(TRUE), h5.x, h1.w + 38: minh h4.w(TRUE), h1, h5 + 39: madr r0.xy(TRUE), -h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 41: texpkb h0(TRUE), r0, #0 + 43: addh_m8 h5.z(TRUE), h5.y, -h4.w + 44: madr r2.xy(TRUE), h4, constant(cConst5.x, cConst5.y, cConst5.z, cConst5.w).xyxx, r1.zwzz + 46: texpkb h3(TRUE), r2, #0 + 48: addh_d2 h0(TRUE), h0, h3 + 49: addh_d2 h3(TRUE), h0, h2 + 50: movh h0(TRUE), h3 + 51: slth h3.x(TRUE), h3.w, h5.w + 52: sgth h3.w(TRUE), h3, h5.x + 53: addx.c0 rc(TRUE), h3.x, h3 + 54: slth.c0 rc(TRUE), h5.z, h5 + 55: movh h0(c0.NE.w), h2 + 56: movh h0(c0.NE.x), h1 + + IPU0 ------ Simplified schedule: -------- + Pass | Unit | uOp | PC: Op + -----+--------+------+------------------------- + 1 | SCT0/1 | mov | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | TEX | txl | 0: TXLr h0.w, g[TEX1].zyxx, const.xxxx, TEX0; + | SCB0 | add | 2: ADDh h2.y, h0.-w--, const.-x--; + | | | + 2 | SCT0/1 | mov | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | TEX | txl | 4: TXLr h1.w, g[TEX1].xwxx, const.xxxx, TEX0; + | SCB0 | add | 6: ADDh h0.x, h1.w---,-h2.y---; + | | | + 3 | SCT0/1 | mov | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | TEX | txl | 7: TXLr h2.w, g[TEX1].zwzz, const.xxxx, TEX0; + | SCB0 | max | 10: MAXh h5.x, h2.y---, h2.w---; + | SCB1 | min | 9: MINh h4.w, h2.---y, h2; + | | | + 4 | SCT0/1 | mov | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | TEX | txl | 11: TXLr h0.w, g[TEX1], const.xxxx, TEX0; + | SCB0 | add | 14: ADDh h0.x, h0.w---, h0; + | SCB1 | add | 13: ADDh h3.w,-h0, h0.---x; + | | | + 5 | SCT0 | mad | 16: ADDh h0.x, h2.w---, h3.w---; + | SCT1 | mad | 15: ADDh h0.z,-h2.--w-, h0.--x-; + | SCB0 | min | 17: MINh h5.y, h0.-w--, h1.-w--; + | | | + 6 | SCT1 | mov | 18: NRMh h2.xz, h0; + | SRB | nrm | 18: NRMh h2.xz, h0; + | SCB1 | min | 19: MINh*8 h2.w, |h2.---x|, |h2.---z|; + | | | + 7 | SCT0 | div | 20: DIVx h4.xy, h2.xz--, h2.ww--; + | SCT1 | mov | 21: MOVr r1.zw, g[TEX0].--xy; + | SCB1 | max | 22: MAXh h2.w, h0, h1; + | | | + 8 | SCT0 | mad | 24: MADr r0.xy,-h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 26: TXLr h0, r0, const.xxxx, TEX0; + | TEX | txl | 26: TXLr h0, r0, const.xxxx, TEX0; + | SCB0 | max | 28: MAXh h5.x, h2.w---, h5; + | SCB1 | min | 29: MINh h5.w, h5.---y, h4; + | | | + 9 | SCT0 | mad | 30: MADr r1.xy, h2.xz--, const.zw--, r1.zw--; + | SCT1 | mov | 32: TXLr h2, r1, const.xxxx, TEX0; + | TEX | txl | 32: TXLr h2, r1, const.xxxx, TEX0; + | SCB0/1 | add | 34: ADDh/2 h2, h0, h2; + | | | + 10 | SCT0/1 | mov | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | TEX | txl | 35: TXLr h1, g[TEX0], const.xxxx, TEX0; + | SCB0 | max | 37: MAXh h5.y, h5.-x--, h1.-w--; + | SCB1 | min | 38: MINh h4.w, h1, h5; + | | | + 11 | SCT0 | mad | 39: MADr r0.xy,-h4, const.xy--, r1.zw--; + | SCT1 | mov | 41: TXLr h0, r0, const.zzzz, TEX0; + | TEX | txl | 41: TXLr h0, r0, const.zzzz, TEX0; + | SCB0 | mad | 44: MADr r2.xy, h4, const.xy--, r1.zw--; + | SCB1 | add | 43: ADDh*8 h5.z, h5.--y-,-h4.--w-; + | | | + 12 | SCT0/1 | mov | 46: TXLr h3, r2, const.xxxx, TEX0; + | TEX | txl | 46: TXLr h3, r2, const.xxxx, TEX0; + | SCB0/1 | add | 48: ADDh/2 h0, h0, h3; + | | | + 13 | SCT0/1 | mad | 49: ADDh/2 h3, h0, h2; + | SCB0/1 | mul | 50: MOVh h0, h3; + | | | + 14 | SCT0 | set | 51: SLTh h3.x, h3.w---, h5.w---; + | SCT1 | set | 52: SGTh h3.w, h3, h5.---x; + | SCB0 | set | 54: SLThc0 rc, h5.z---, h5; + | SCB1 | add | 53: ADDxc0_s rc, h3.---x, h3; + | | | + 15 | SCT0/1 | mul | 55: MOVh h0(NE0.wwww), h2; + | SCB0/1 | mul | 56: MOVh h0(NE0.xxxx), h1; + + Pass SCT TEX SCB + 1: 0% 100% 25% + 2: 0% 100% 25% + 3: 0% 100% 50% + 4: 0% 100% 50% + 5: 50% 0% 25% + 6: 0% 0% 25% + 7: 100% 0% 25% + 8: 0% 100% 50% + 9: 0% 100% 100% + 10: 0% 100% 50% + 11: 0% 100% 75% + 12: 0% 100% 100% + 13: 100% 0% 100% + 14: 50% 0% 50% + 15: 100% 0% 100% + + MEAN: 26% 60% 56% + + Pass SCT0 SCT1 TEX SCB0 SCB1 + 1: 0% 0% 100% 100% 0% + 2: 0% 0% 100% 100% 0% + 3: 0% 0% 100% 100% 100% + 4: 0% 0% 100% 100% 100% + 5: 100% 100% 0% 100% 0% + 6: 0% 0% 0% 0% 100% + 7: 100% 100% 0% 0% 100% + 8: 0% 0% 100% 100% 100% + 9: 0% 0% 100% 100% 100% + 10: 0% 0% 100% 100% 100% + 11: 0% 0% 100% 100% 100% + 12: 0% 0% 100% 100% 100% + 13: 100% 100% 0% 100% 100% + 14: 100% 100% 0% 100% 100% + 15: 100% 100% 0% 100% 100% + + MEAN: 33% 33% 60% 86% 80% + Fragment Performance Setup: Driver RSX Compiler, GPU RSX, Flags 0x5 + Results 15 cycles, 3 r regs, 800,000,000 pixels/s + ============================================================================*/ + #if (FXAA_PS3 == 1) && (FXAA_EARLY_EXIT == 1) + /*--------------------------------------------------------------------------*/ + #pragma regcount 7 + #pragma disablepc all + #pragma option O2 + #pragma option OutColorPrec=fp16 + #pragma texformat default RGBA8 + /*==========================================================================*/ + half4 FxaaPixelShader( + // See FXAA Quality FxaaPixelShader() source for docs on Inputs! + FxaaFloat2 pos, + FxaaFloat4 fxaaConsolePosPos, + FxaaTex tex, + FxaaTex fxaaConsole360TexExpBiasNegOne, + FxaaTex fxaaConsole360TexExpBiasNegTwo, + FxaaFloat2 fxaaQualityRcpFrame, + FxaaFloat4 fxaaConsoleRcpFrameOpt, + FxaaFloat4 fxaaConsoleRcpFrameOpt2, + FxaaFloat4 fxaaConsole360RcpFrameOpt2, + FxaaFloat fxaaQualitySubpix, + FxaaFloat fxaaQualityEdgeThreshold, + FxaaFloat fxaaQualityEdgeThresholdMin, + FxaaFloat fxaaConsoleEdgeSharpness, + FxaaFloat fxaaConsoleEdgeThreshold, + FxaaFloat fxaaConsoleEdgeThresholdMin, + FxaaFloat4 fxaaConsole360ConstDir + ) { + /*--------------------------------------------------------------------------*/ + // (1) + half4 rgbyNe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaNe = rgbyNe.w + half(1.0/512.0); + #else + half lumaNe = rgbyNe.y + half(1.0/512.0); + #endif + /*--------------------------------------------------------------------------*/ + // (2) + half4 lumaSw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaSwNegNe = lumaSw.w - lumaNe; + #else + half lumaSwNegNe = lumaSw.y - lumaNe; + #endif + /*--------------------------------------------------------------------------*/ + // (3) + half4 lumaNw = h4tex2Dlod(tex, half4(fxaaConsolePosPos.xy, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNwSw = max(lumaNw.w, lumaSw.w); + half lumaMinNwSw = min(lumaNw.w, lumaSw.w); + #else + half lumaMaxNwSw = max(lumaNw.y, lumaSw.y); + half lumaMinNwSw = min(lumaNw.y, lumaSw.y); + #endif + /*--------------------------------------------------------------------------*/ + // (4) + half4 lumaSe = h4tex2Dlod(tex, half4(fxaaConsolePosPos.zw, 0, 0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half dirZ = lumaNw.w + lumaSwNegNe; + half dirX = -lumaNw.w + lumaSwNegNe; + #else + half dirZ = lumaNw.y + lumaSwNegNe; + half dirX = -lumaNw.y + lumaSwNegNe; + #endif + /*--------------------------------------------------------------------------*/ + // (5) + float3 dir; + dir.y = 0.0; + #if (FXAA_GREEN_AS_LUMA == 0) + dir.x = lumaSe.w + dirX; + dir.z = -lumaSe.w + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.w); + #else + dir.x = lumaSe.y + dirX; + dir.z = -lumaSe.y + dirZ; + half lumaMinNeSe = min(lumaNe, lumaSe.y); + #endif + /*--------------------------------------------------------------------------*/ + // (6) + half4 dir1_pos; + dir1_pos.xy = normalize(dir).xz; + half dirAbsMinTimes8 = min(abs(dir1_pos.x), abs(dir1_pos.y)) * half(FXAA_CONSOLE__PS3_EDGE_SHARPNESS); + /*--------------------------------------------------------------------------*/ + // (7) + half4 dir2_pos; + dir2_pos.xy = clamp(dir1_pos.xy / dirAbsMinTimes8, half(-2.0), half(2.0)); + dir1_pos.zw = pos.xy; + dir2_pos.zw = pos.xy; + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxNeSe = max(lumaNe, lumaSe.w); + #else + half lumaMaxNeSe = max(lumaNe, lumaSe.y); + #endif + /*--------------------------------------------------------------------------*/ + // (8) + half4 temp1N; + temp1N.xy = dir1_pos.zw - dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + temp1N = h4tex2Dlod(tex, half4(temp1N.xy, 0.0, 0.0)); + half lumaMax = max(lumaMaxNwSw, lumaMaxNeSe); + half lumaMin = min(lumaMinNwSw, lumaMinNeSe); + /*--------------------------------------------------------------------------*/ + // (9) + half4 rgby1; + rgby1.xy = dir1_pos.zw + dir1_pos.xy * fxaaConsoleRcpFrameOpt.zw; + rgby1 = h4tex2Dlod(tex, half4(rgby1.xy, 0.0, 0.0)); + rgby1 = (temp1N + rgby1) * 0.5; + /*--------------------------------------------------------------------------*/ + // (10) + half4 rgbyM = h4tex2Dlod(tex, half4(pos.xy, 0.0, 0.0)); + #if (FXAA_GREEN_AS_LUMA == 0) + half lumaMaxM = max(lumaMax, rgbyM.w); + half lumaMinM = min(lumaMin, rgbyM.w); + #else + half lumaMaxM = max(lumaMax, rgbyM.y); + half lumaMinM = min(lumaMin, rgbyM.y); + #endif + /*--------------------------------------------------------------------------*/ + // (11) + half4 temp2N; + temp2N.xy = dir2_pos.zw - dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + temp2N = h4tex2Dlod(tex, half4(temp2N.xy, 0.0, 0.0)); + half4 rgby2; + rgby2.xy = dir2_pos.zw + dir2_pos.xy * fxaaConsoleRcpFrameOpt2.zw; + half lumaRangeM = (lumaMaxM - lumaMinM) / FXAA_CONSOLE__PS3_EDGE_THRESHOLD; + /*--------------------------------------------------------------------------*/ + // (12) + rgby2 = h4tex2Dlod(tex, half4(rgby2.xy, 0.0, 0.0)); + rgby2 = (temp2N + rgby2) * 0.5; + /*--------------------------------------------------------------------------*/ + // (13) + rgby2 = (rgby2 + rgby1) * 0.5; + /*--------------------------------------------------------------------------*/ + // (14) + #if (FXAA_GREEN_AS_LUMA == 0) + bool twoTapLt = rgby2.w < lumaMin; + bool twoTapGt = rgby2.w > lumaMax; + #else + bool twoTapLt = rgby2.y < lumaMin; + bool twoTapGt = rgby2.y > lumaMax; + #endif + bool earlyExit = lumaRangeM < lumaMax; + bool twoTap = twoTapLt || twoTapGt; + /*--------------------------------------------------------------------------*/ + // (15) + if(twoTap) rgby2 = rgby1; + if(earlyExit) rgby2 = rgbyM; + /*--------------------------------------------------------------------------*/ + return rgby2; } + /*==========================================================================*/ + #endif + """ + } + + fxaa = { + includes = [ "common", "fxaa_source" ] + + vp_code = """ + uniform mat4 world_view_proj; + + attribute vec4 sem_position; + attribute vec2 sem_texcoord0; + + varying vec2 uv; + + void main() { + uv = sem_texcoord0; + gl_Position = sem_position * world_view_proj; + } + """ + + fp_code = """ + uniform sampler2D input_texture0; + uniform vec2 inv_input_texture0_size; + varying vec2 uv; + + void main() { + gl_FragColor = FxaaPixelShader( + uv, + vec4(0.0, 0.0, 0.0, 0.0), + input_texture0, + input_texture0, + input_texture0, + inv_input_texture0_size, + vec4(0.0, 0.0, 0.0, 0.0), + vec4(-2.0*inv_input_texture0_size.xy, 2.0*inv_input_texture0_size.xy), + vec4(0.0, 0.0, 0.0, 0.0), + 0.25, + 0.166, + 0.0833, + 8.0, + 0.125, + 0.05, + vec4(1.0, -1.0, 0.25, -0.25)); + } + """ + + code = """ + struct FxaaVS_Output { + float4 Pos : SV_POSITION; + float2 Tex : TEXCOORD0; + }; + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 inv_input_texture0_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + FxaaVS_Output vs_main(VS_INPUT input) { + FxaaVS_Output o; + o.Pos = mul(input.position, world_view_proj); + o.Tex = input.uv; + + return o; + } + + SamplerState anisotropic_sampler; + Texture2D input_texture0; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(FxaaVS_Output Input) : SV_TARGET0 { + FxaaTex tex = { anisotropic_sampler, input_texture0 }; + //return float4(FxaaPixelShader(Input.Tex.xy, tex, inv_input_texture0_size), 1.0f); + return FxaaPixelShader( + Input.Tex.xy, + float4(0,0,0,0), + tex, + tex, + tex, + inv_input_texture0_size, + float4(0,0,0,0), + float4(-2*inv_input_texture0_size.xy, 2*inv_input_texture0_size.xy), + float4(0,0,0,0), + 0.25, + 0.166, + 0.0833, + 8.0, + 0.125, + 0.05, + float4(1.0, -1.0, 0.25, -0.25) ); + } + """ + } +} + +shaders = { + fxaa = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="fxaa" render_states="fxaa" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } +} + +static_compile= [ + { if: "!on_renderer(GL)" shader="fxaa" } +] diff --git a/vmf_source/core/stingray_renderer/shader_libraries/gui.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/gui.shader_source new file mode 100644 index 0000000..e98ff09 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/gui.shader_source @@ -0,0 +1,613 @@ +includes = ["core/stingray_renderer/shader_libraries/common.shader_source"] + +render_states = { + gui_gradient = { + inherits = "opacity" + states = { + z_enable = "false" + z_write_enable = "false" + defined_WRITE_MASK = { + defined_ADDITIVE_BLEND = { + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_one" + src_blend = "blend_one" + } + ndefined_ADDITIVE_BLEND = { + blend_enable = "false" + } + write_mask0 = "red" + } + defined_MASKED = { + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_src_alpha" + } + } + } +} + +hlsl_shaders = { + gui_gradient = { + includes = [ "common" ] + + samplers = { + defined_DIFFUSE_MAP = { + defined_ATLAS_COLOR_LOOKUP = { + defined_ANISOTROPIC = { + diffuse_map = { sampler_states = "clamp_anisotropic" } + } + ndefined_ANISOTROPIC = { + diffuse_map = { sampler_states = "clamp_point" } + } + } + ndefined_ATLAS_COLOR_LOOKUP = { + defined_CLAMP_POINT ={ + diffuse_map = { sampler_states = "clamp_point" } + } + ndefined_CLAMP_POINT = { + diffuse_map = { sampler_states = "clamp_linear" } + } + } + } + defined_DIFFUSE_MAP_TWO = { + diffuse_map = { sampler_states = "clamp_linear" } + diffuse_map_two = { sampler_states = "clamp_linear" } + } + defined_DIFFUSE_MAP_THREE = { + diffuse_map = { sampler_states = "clamp_linear" } + diffuse_map_two = { sampler_states = "clamp_linear" } + diffuse_map_three = { sampler_states = "clamp_linear" } + } + defined_DIFFUSE_MAP_FOUR = { + diffuse_map = { sampler_states = "clamp_linear" } + diffuse_map_two = { sampler_states = "clamp_linear" } + diffuse_map_three = { sampler_states = "clamp_linear" } + diffuse_map_four = { sampler_states = "clamp_linear" } + } + defined_DIFFUSE_MAP_FIVE = { + diffuse_map = { sampler_states = "clamp_linear" } + diffuse_map_two = { sampler_states = "clamp_linear" } + diffuse_map_three = { sampler_states = "clamp_linear" } + diffuse_map_four = { sampler_states = "clamp_linear" } + diffuse_map_five = { sampler_states = "clamp_linear" } + } + defined_DIFFUSE_MAP_SIX = { + diffuse_map = { sampler_states = "clamp_linear" } + diffuse_map_two = { sampler_states = "clamp_linear" } + diffuse_map_three = { sampler_states = "clamp_linear" } + diffuse_map_four = { sampler_states = "clamp_linear" } + diffuse_map_five = { sampler_states = "clamp_linear" } + diffuse_map_six = { sampler_states = "clamp_linear" } + } + defined_GRADIENT = { + gradient_map = { sampler_states = "clamp_anisotropic" } + } + defined_MASKED = { + overlay_mask = { sampler_states = "clamp_point" } + } + defined_PATTERN_TINTING = { + color_atlas = { sampler_states = "clamp_point" } + material_map = { sampler_states = "clamp_point" } + } + defined_DETAIL_TEXTURE = { + detail_texture = { sampler_states = "wrap_linear" } + } + defined_COLOR_TINT_MAP = { + color_tint_map = { sampler_states = "clamp_linear" } + } + defined_DISTORTION = { + distortion_map = { sampler_states = "wrap_linear" } + } + } + + code=""" + #if defined(DIFFUSE_MAP) || defined(DIVISION) || defined(PATTERN_TINTING) || defined(ATLAS_COLOR_LOOKUP) || defined(CIRCULAR_MASK) || defined(CIRCULAR_GRADIENT) || defined(DIFFUSE_MAP_TWO) || defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + #define UV0 + #endif + + #if defined(MASKED) + DECLARE_SAMPLER_2D(overlay_mask); + #endif + + #if defined(DETAIL_TEXTURE) + DECLARE_SAMPLER_2D(detail_texture); // exports={ name="Detail Map" type="resource" } + #endif + + #if defined(DIFFUSE_MAP) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + #endif + + #if defined(DIFFUSE_MAP_TWO) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_two); // exports={ name="Diffuse Map Two" type="resource" } + #endif + + #if defined(DIFFUSE_MAP_THREE) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_two); // exports={ name="Diffuse Map Two" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_three); // exports={ name="Diffuse Map Three" type="resource" } + #endif + + #if defined(DIFFUSE_MAP_FOUR) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_two); // exports={ name="Diffuse Map Two" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_three); // exports={ name="Diffuse Map Three" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_four); // exports={ name="Diffuse Map Four" type="resource" } + #endif + + #if defined(DIFFUSE_MAP_FIVE) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_two); // exports={ name="Diffuse Map Two" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_three); // exports={ name="Diffuse Map Three" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_four); // exports={ name="Diffuse Map Four" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_five); // exports={ name="Diffuse Map Five" type="resource" } + #endif + + #if defined(DIFFUSE_MAP_SIX) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_two); // exports={ name="Diffuse Map Two" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_three); // exports={ name="Diffuse Map Three" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_four); // exports={ name="Diffuse Map Four" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_five); // exports={ name="Diffuse Map Five" type="resource" } + DECLARE_SAMPLER_2D(diffuse_map_six); // exports={ name="Diffuse Map Six" type="resource" } + #endif + + #if defined(GRADIENT) + DECLARE_SAMPLER_2D(gradient_map); // exports={ name="Gradient Map" type="resource" } + #endif + + #if defined(DISTORTION) + DECLARE_SAMPLER_2D(distortion_map); // exports={ name="Distortion Map" type="resource" } + #endif + + #if defined(PATTERN_TINTING) + DECLARE_SAMPLER_2D(color_atlas); // exports={ name="Color Atlas" type="resource" } + DECLARE_SAMPLER_2D(material_map); // exports={ name="Material Map" type="resource" } + #endif + + #if defined(COLOR_TINT_MAP) + DECLARE_SAMPLER_2D(color_tint_map); // exports={ name="Color Tint Map" type="resource" } + #endif + + struct VS_INPUT { + float4 position : POSITION; + #if defined(DIFFUSE_MAP_TWO) || defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + float3 normal : NORMAL; + #endif + float4 color : COLOR; + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 color : COLOR; + #if defined(DIFFUSE_MAP_TWO) || defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + float page : TEXCOORD1; + #endif + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + }; + + inline float2 scale_material_uv( float2 material_uv, float pattern_variation ) { + float pattern_u = fmod(pattern_variation,2.0f) * 0.5f; + float pattern_v = floor(pattern_variation / 2.0f) * 0.5; + return float2((material_uv.x * 0.5) + pattern_u, (material_uv.y * 0.5) + pattern_v); + } + + inline float2 scale_uv_coat_of_arms( float2 uv, float variation, float2 atlas_fields ) { + float step_u = (1.0f/atlas_fields.x); + float step_v = (1.0f/atlas_fields.y); + float pattern_u = fmod( variation, atlas_fields.x) * step_u; + float pattern_v = floor( variation / atlas_fields.x ) * step_v; + + return float2(uv.x * step_u + pattern_u, uv.y * step_v + pattern_v); + } + + inline float2 scale_uv_coat_of_arms_atlas( float2 uv, float2 offset, float2 scale) + { + float2 scaled_uv = uv * scale + offset; + return scaled_uv; + } + + + CBUFFER_START(c0) + float4x4 world_view_proj; + float threshold_fade; // exports = { name="Threshold Fade" type="scalar" value=0.05 min=0.0 max=1 step=0.001 } + #if defined(GRADIENT) + float gradient_threshold; // exports = { Name="Gradient threshold" type="scalar" value=0.37 min=0 max=1 step=0.00001 } + #endif + #if defined(PATTERN_TINTING) + float pattern_variation; // exports = { name="Pattern Variation" type="scalar" value=0.0 min=0.0 max=3.0 step=1.0 } + float atlas_variation; // exports = { name="Armour Atlas Variation" type="scalar" value=0.0 min=0.0 max=31.0 step=1.0 } + float use_grayscale = 0; + #endif + #if defined(ATLAS_COLOR_LOOKUP) + float2 atlas_tiles; // exports = { name="Atlas tiles" type="vector2" value=[128.0 1.0] min=[1.0 1.0] max=[255.0 255.0] step=[1.0 1.0] } + #endif + #if defined(CIRCULAR_MASK) + float radial_threshold; // exports = { name="Radial Threshold" type="scalar" value=0.5 min=0.0 max=1.0 step=0.0001 } + float fade_threshold; // exports = { name="Fade Threshold" type="scalar" value=0.5 min=0.0001 max=1.0 step=0.0001 } + #if defined(UV_SCALE) + float2 uv_scale; // exports = { name="UV Scale" type="vector2" value=[0.0 0.0] min=[0.001 0.001] max=[1 1] step=[0.0001 0.0001] } + #endif + #endif + #if defined(CIRCULAR_GRADIENT) + float4 from_color; // exports = { name="Gradient From Color" type="vector4" value=[0.0 0.0 0.0 0.0] min=[0 0 0 0] max=[1 1 1 1] step=[0.0001 0.0001 0.0001 0.0001] } + float4 to_color; // exports = { name="Gradient To Color" type="vector4" value=[0.0 0.0 0.0 0.0] min=[0 0 0 0] max=[1 1 1 1] step=[0.0001 0.0001 0.0001 0.0001] } + float exponent; // exports = { name="Gradient exponent" type="scalar" value=1 min=0 max=10.0 step=0.0001 } + float scale; // exports = { name="Gradient scale" type="scalar" value=1 min=0 max=10.0 step=0.0001 } + #endif + #if defined(DISTANCE_FIELD) + float smoothing_denominator; // exports = { name="Smoothing Scale" type="scalar" value=18 min=2 max=100.0 step=0.001 } + float outline_color; // exports = { name="Outline Color" type="vector3" value=[0.0 0.0 0.0] min=[0.0 0.0 0.0] max=[1 1 1] step=[0.0001 0.0001 0.0001] } + float2 min_value; // exports = { name="Min Outline" type="vector2" value=[0.0 0.0] min=[0.0 0.0] max=[1 1] step=[0.0001 0.0001] } + float2 max_value; // exports = { name="Max Outline" type="vector2" value=[0.0 0.0] min=[0.0 0.0] max=[1 1] step=[0.0001 0.0001] } + #endif + #if defined(DETAIL_TEXTURE_DIR) + float2 detail_dir; // exports = { name="Detail Texture scroll direction" type="vector2" value=[0.0 0.0] min=[-1 -1] max=[1 1] step=[0.0001 0.0001] } + float detail_speed; // exports = { name="Detail Texture scroll speed" type="scalar" value=0.5 min=0.0 max=10.0 step=0.0001 } + #endif + #if defined(DETAIL_TEXTURE) + float2 sample_tiling; // exports = { name="Detail Texture Tiling" type="vector2" value=[1.0 1.0] min=[0 0] max=[100 100] step=[0.0001 0.0001] } + float detail_offset; // exports = { name="Detail Texture color offset" type="scalar" value=0.0 min=-1 max=1 step=0.0001 } + #endif + #if defined(COLOR_TINT_MAP) + float2 color_tint_uv; // exports = { name="Color Tint UV" type="vector2" value=[0.5 0.5] min=[0.0 0.0] max=[1 1] step=[0.0001 0.0001] } + #endif + #if defined(DISTORTION) + float2 distortion_strength; // exports = { name = "Distortion strength" type = "vector2" value=[0.5 0.5] min=[0.0 0.0] max=[10 10] step=[0.0001 0.0001] } + float2 distortion_speed; // exports = { name = "Distortion speed" type = "vector2" value=[0.0 0.0] min=[-10.0 -10.0] max=[10.0 10.0] step=[0.0001 0.0001] } + float distortion_offset_top; // exports = { name="Distortion offset top" type="scalar" value=0.0 min=-0 max=1 step=0.0001 } + #endif + #if defined(DESATURATION) + float desaturation; // exports = { name="Desaturation Value" type="scalar" value=0.5 min=0.0 max=1.0 step=0.0001 } + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.color = decode_vertex_color(input.color); + #if defined(DIFFUSE_MAP_TWO) || defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + o.page = input.normal.z; + #endif + #if defined(UV0) + o.uv = input.uv; + #endif + return o; + } + + float4 sample_diffuse(float2 uv, float page) + { + #if !defined(DIFFUSE_MAP) && !defined(DIFFUSE_MAP_TWO) && !defined(DIFFUSE_MAP_THREE) && !defined(DIFFUSE_MAP_FOUR) && !defined(DIFFUSE_MAP_FIVE) && !defined(DIFFUSE_MAP_SIX) + return float4(0,0,0,0); + #endif + + + #if defined(DIFFUSE_MAP) || defined(DIFFUSE_MAP_TWO) || defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + [branch] + if( page == 0) + return TEX2D(diffuse_map, uv); + #endif + #if defined(DIFFUSE_MAP_TWO) || defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + else if( page == 1) + return TEX2D(diffuse_map_two, uv); + #endif + #if defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + else if( page == 2) + return TEX2D(diffuse_map_three, uv); + #endif + #if defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + else if( page == 3) + return TEX2D(diffuse_map_four, uv); + #endif + #if defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + else if( page == 4) + return TEX2D(diffuse_map_five, uv); + #endif + #if defined(DIFFUSE_MAP_SIX) + else + return TEX2D(diffuse_map_six, uv); + #endif + + return float4(0,0,0,0); + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + #if defined(WRITE_MASK) + #if defined(DIFFUSE_MAP) + float mask = TEX2D(diffuse_map, input.uv).a; + #if defined(COLOR_MASK) + mask *= input.color.a; + #endif + #else + float mask = input.color.a; + #endif + + #if defined(THRESHOLD_MASK) + mask = input.color.a > mask ? saturate((input.color.a - mask) / threshold_fade) : 0; + #endif + + #if defined(ADDITIVE_BLEND) + return float4(mask, 0, 0, mask); + #else + return float4(mask, 0, 0, 0); + #endif + #elif defined(DIVISION) + float intensity = 1.0; + intensity = pow(max(pow(((input.uv.x * 2.0) - 1.0),2.0), pow(((input.uv.y * 2.0) - 1.0),2.0)),2.0); + + float4 color = input.color; + color.rgb *= intensity; + color.a *= intensity; + return color; + #elif defined(CLEAR_MASK) + #if defined(CLEAR_WITH_ALPHA) + return float4(1,1,1,input.color.a); + #else + return float4(1,1,1,1); + #endif + #elif defined(CIRCULAR_GRADIENT) + float2 uv = input.uv; + float2 offset_uv = uv - float2(0.5,0.5); + float hypotenuse = sqrt(offset_uv.x * offset_uv.x + offset_uv.y * offset_uv.y); + return lerp(to_color, from_color, pow(hypotenuse * scale, exponent)); + #else + + float4 c = input.color; + #if defined(DIFFUSE_MAP) || defined(DIFFUSE_MAP_TWO) || defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + #if defined(DISTANCE_FIELD) + float smoothing = 1.0/smoothing_denominator; + #if defined(DIFFUSE_MAP_TWO) || defined(DIFFUSE_MAP_THREE) || defined(DIFFUSE_MAP_FOUR) || defined(DIFFUSE_MAP_FIVE) || defined(DIFFUSE_MAP_SIX) + float4 diffuse = sample_diffuse(input.uv, input.page); + #else + float4 diffuse = TEX2D(diffuse_map, input.uv); + #endif + float alpha = smoothstep(0.5 - smoothing, 0.5 + smoothing, diffuse.a); + + float dist_mask = diffuse.a; + if( dist_mask >= min_value.x && dist_mask <= max_value.y ) { + float factor = 1.0; + if( dist_mask <= min_value.y ) + factor = smoothstep(min_value.x, min_value.y, dist_mask); + else + factor = smoothstep(max_value.y, max_value.x, dist_mask); + + float3 color = lerp(diffuse.rgb * c.rgb, outline_color, factor); + c = float4(color, alpha * c.a); + } + else + c = saturate(float4(diffuse.rgb * c.rgb, alpha * c.a)); + #elif defined(DISTORTION) + half4 normal_distortion = TEX2D(distortion_map, input.uv + distortion_speed * time); + half2 distortion = (normal_distortion.xy * 2.0 - 1.0) * clamp(pow(input.uv.y,2) * distortion_offset_top, 0, 1); + float4 diffuse = TEX2D(diffuse_map, input.uv + distortion * distortion_strength); + c *= diffuse; + c.rgb *= length(float2(1.0,1.0) - distortion); + //c = float4(distortion, 1, 1); + #else + #if defined(ATLAS_COLOR_LOOKUP) + float atlas_index = floor((input.color.a * 255.0f) + 0.5f); + float2 atlas_uv = scale_uv_coat_of_arms( input.uv, atlas_index , atlas_tiles ); + float4 diffuse = TEX2D( diffuse_map, atlas_uv ); + c.a = 1.0f; + #else + float4 diffuse = TEX2D(diffuse_map, input.uv); + #endif + #if defined(ONE_BIT_ALPHA) + one_bit_alpha_mask(diffuse.a, 0.5); + c.rgb * diffuse.rgb; + #else + c *= diffuse; + #endif + + #if defined(GAMMA_CORRECTED) + c.rgb = pow(c.rgb, 2.2/gamma); + #endif + #endif + #endif + + #if defined(COLOR_TINT_MAP) + float3 tint_color = TEX2D(color_tint_map, color_tint_uv).rgb; + c.rgb *= tint_color; + #endif + + #if defined(DETAIL_TEXTURE) + float2 sample_uv = (input.position.xy / back_buffer_size) * sample_tiling; + #if defined(DETAIL_TEXTURE_DIR) + float detail_value = TEX2D(detail_texture, sample_uv + detail_dir * detail_speed * time).r; + #else + float detail_value = TEX2D(detail_texture, sample_uv).r; + #endif + #if defined(MASK_WITH_DETAIL_TEXTURE) + c.rgba += detail_offset + (detail_value - 0.5); + #else + c.rgb += detail_offset + (detail_value - 0.5); + #endif + #endif + + #if defined(PATTERN_TINTING) + //float4 pattern_sample_uv = float4( scale_material_uv( input.uv, pattern_variation ), 0.0f, 0.0f ); + float4 pattern_sample_uv = float4( input.uv, 0.0f, 0.0f ); + float pattern_value = TEX2D( material_map, pattern_sample_uv ).a; + + float4 armour_tint_sample_uv = float4( pattern_value, (atlas_variation / 32.0f), 0.0f, 0.0f); + half4 armour_tint_color = TEX2D( color_atlas, armour_tint_sample_uv ); + + c.rgb *= armour_tint_color.rgb * 2; + if( use_grayscale ) + c.rgb = (c.r + c.g + c.b) / 3; + #endif + + #if defined(CIRCULAR_MASK) + float2 uv = input.uv; + #if defined(UV_SCALE) + uv = fmod(input.uv, uv_scale) * floor((1.0/uv_scale) + 0.5); + #endif + float2 offset_uv = uv - float2(0.5,0.5); + float hypotenuse_squared = offset_uv.x * offset_uv.x + offset_uv.y * offset_uv.y; + float radial_threshold_squared = (radial_threshold * radial_threshold); + float fade_threshold_squared = (fade_threshold*fade_threshold); + if( hypotenuse_squared > radial_threshold_squared) + { + float diff = hypotenuse_squared - radial_threshold_squared; + c.a *= lerp(1.0,0.0, diff/fade_threshold_squared); + } + #endif + + #if defined(MASKED) + float2 masked_uv = input.position.xy / back_buffer_size; + half mask = TEX2D(overlay_mask, masked_uv).r; + c.a *= mask; + #endif + + #if defined(RADIAL_DISSOLVE) + float value = sqrt( pow((input.uv.x * 2.0) - 1.0,2.0) + pow((input.uv.y * 2.0) - 1.0,2.0) ); + if( value < 0.9 ) + c.a *= lerp( 1.0, 0.0, (0.9 - value)/0.4); + #endif + + #if defined(GRADIENT) + float4 gradient_value = TEX2D(gradient_map, input.uv); + if( gradient_value.r > gradient_threshold) + discard; + #endif + + #if defined(GRAYSCALE) + float intensity = (c.r + c.g + c.b) / 3.0f; + c.rgb = intensity; + #endif + + #if defined(DESATURATION) + float intensity = (c.r + c.g + c.b) / 3.0f; + c.rgb = lerp(c.rgb, float3(intensity, intensity, intensity), desaturation); + #endif + + return c; + #endif + } + """ + } +} + +shaders = { + gui_gradient = { + editor_options = [ + { + name="Pixel Modifiers" + options = [ + { name="Diffuse Map" define="DIFFUSE_MAP" condition="!DIFFUSE_MAP_TWO && !DIFFUSE_MAP_THREE && !DIFFUSE_MAP_FOUR && !DIFFUSE_MAP_FIVE && !DIFFUSE_MAP_SIX" } + { name="Gradient Map" define="GRADIENT" } + { name="One Bit Alpha" define="ONE_BIT_ALPHA" } + { name="Grayscale" define="GRAYSCALE" } + { name="Atlas lookup from alpha" define="ATLAS_COLOR_LOOKUP" } + { name="- Anisotropic Filtering" defined="ANISOTROPIC" } + { name="Circular Gradient" define="CIRCULAR_GRADIENT" } + { name="Distance Field" define="DISTANCE_FIELD" } + { name="Detail Texture" define="DETAIL_TEXTURE" } + { name="Detail Texture scroll direction" define="DETAIL_TEXTURE_DIR" condition="DETAIL_TEXTURE"} + { name="Mask with Detail Texture" define="MASK_WITH_DETAIL_TEXTURE" condition="DETAIL_TEXTURE"} + { name="Tint Color Map" define="COLOR_TINT_MAP" } + { name="Gamma Correction" define="GAMMA_CORRECTED" } + { name="Normal Map Distortion" define="DISTORTION" } + { name="Desaturation" define="DESATURATION" } + { name="Two Font Maps" define="DIFFUSE_MAP_TWO" condition="DISTANCE_FIELD && !DIFFUSE_MAP && !DIFFUSE_MAP_THREE && !DIFFUSE_MAP_FOUR && !DIFFUSE_MAP_FIVE && !DIFFUSE_MAP_SIX" } + { name="Three Font Maps" define="DIFFUSE_MAP_THREE" condition="DISTANCE_FIELD && !DIFFUSE_MAP && !DIFFUSE_MAP_TWO && !DIFFUSE_MAP_FOUR && !DIFFUSE_MAP_FIVE && !DIFFUSE_MAP_SIX" } + { name="Four Font Maps" define="DIFFUSE_MAP_FOUR" condition="DISTANCE_FIELD && !DIFFUSE_MAP && !DIFFUSE_MAP_THREE && !DIFFUSE_MAP_TWO && !DIFFUSE_MAP_FIVE && !DIFFUSE_MAP_SIX" } + { name="Five Font Maps" define="DIFFUSE_MAP_FIVE" condition="DISTANCE_FIELD && !DIFFUSE_MAP && !DIFFUSE_MAP_THREE && !DIFFUSE_MAP_FOUR && !DIFFUSE_MAP_TWO && !DIFFUSE_MAP_SIX" } + { name="Six Font Maps" define="DIFFUSE_MAP_SIX" condition="DISTANCE_FIELD && !DIFFUSE_MAP && !DIFFUSE_MAP_THREE && !DIFFUSE_MAP_FOUR && !DIFFUSE_MAP_FIVE && !DIFFUSE_MAP_TWO" } + ] + } + { + name="Masking" + options = [ + { name="Write Transparency Mask" define="WRITE_MASK" condition="!MASKED"} + { name=" - Additive Blend" define="ADDITIVE_BLEND" condition="WRITE_MASK"} + { name="Diffuse and Color Mask" define="COLOR_MASK" condition="WRITE_MASK && DIFFUSE_MAP"} + { name="Threshold Mask" define="THRESHOLD_MASK" } + { name="Transparency Masked" define="MASKED" condition="!WRITE_MASK && !CLEAR_MASK"} + { name="Circular Mask" defined="CIRCULAR_MASK" } + { name="- UV Scale" defined="UV_SCALE" } + ] + } + { + name="Depth Testing" + options = [ + { name="Depth Testing Enabled" define="DEPTH_TEST_ENABLED" } + ] + } + ] + + contexts = { + default = { + passes = [ + { + defined = "OES2" + pass = [ + { layer="transparent" hlsl_shader="mobile_gui" render_states="gui_gradient" } + ] + fail = [ + { + defined = "CLEAR_MASK" + pass = [ + { layer="transparent_mask" hlsl_shader="gui_gradient" render_states="gui_gradient" } + ] + fail = [ + { + defined = "WRITE_MASK" + pass = [ + { layer="transparent_mask" hlsl_shader="gui_gradient" render_states="gui_gradient" } + ] + fail = [ + { layer="transparent" hlsl_shader="gui_gradient" render_states="gui_gradient" } + ] + } + ] + } + ] + } + ] + } + } + + compile = { + default = [ + { defines="" platforms = "D3D11 D3D12 GL2 GNM"} + ] + } + } + +} + +static_compile = [ + { shader = "gui_gradient" defines = "MASKED" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP ANISOTROPIC CIRCULAR_MASK MASKED " } + { shader = "gui_gradient" defines = "ATLAS_COLOR_LOOKUP DIFFUSE_MAP" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP CIRCULAR_MASK UV_SCALE" } + { shader = "gui_gradient" defines = "WRITE_MASK CLEAR_WITH_ALPHA" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP PATTERN_TINTING" } + { shader = "gui_gradient" defines = "ATLAS_COLOR_LOOKUP DIFFUSE_MAP MASKED ANISOTROPIC" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP MASKED" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP MATERIAL_TINT_RGB_1 MATERIAL_TINT_RGB_2 MATERIAL_TINT_RGB_3" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP MASKED CIRCULAR_MASK UV_SCALE" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP PATTERN_TINTING MASKED" } + { shader = "gui_gradient" defines = "DIVISION" } + { shader = "gui_gradient" defines = "WRITE_MASK" } + { shader = "gui_gradient" defines = "ATLAS_COLOR_LOOKUP DIFFUSE_MAP ANISOTROPIC" } + { shader = "gui_gradient" defines = "ATLAS_COLOR_LOOKUP DIFFUSE_MAP MASKED" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP MASKED GRAYSCALE" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP WRITE_MASK" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP GRAYSCALE" } + { shader = "gui_gradient" defines = "CLEAR_MASK" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP GRADIENT" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP DISTANCE_FIELD" } + { shader = "gui_gradient" defines = "DIFFUSE_MAP DISTORTION" } +] diff --git a/vmf_source/core/stingray_renderer/shader_libraries/heatmap.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/heatmap.shader_source new file mode 100644 index 0000000..aec6c1f --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/heatmap.shader_source @@ -0,0 +1,216 @@ +includes = ["core/stingray_renderer/shader_libraries/common.shader_source"] + +render_states = { + heatmap = { + inherits = "opacity" + states = { + z_enable = "false" + defined_HEATMAP_WRITE = { + defined_HEATMAP_CLEAR = { + blend_enable = "false" + } + ndefined_HEATMAP_CLEAR = { + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_dest_alpha" + src_blend = "blend_src_alpha" + } + } + } + } + + heatmap_apply = { + inherits = "default" + states = { + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_src_alpha" + } + } +} + +hlsl_shaders = { + heatmap = { + includes = [ "common" ] + + samplers = { + defined_HEATMAP = { + diffuse_map = { sampler_states = "clamp_linear" } + heatmap = { sampler_states = "clamp_linear" } + } + defined_HEATMAP_WRITE = { + heatmap = { sampler_states = "clamp_linear" } + } + } + + code=""" + #if defined(HEATMAP) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + #endif + + DECLARE_SAMPLER_2D(heatmap); + + struct VS_INPUT { + float4 position : POSITION; + float4 color : COLOR; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 color : COLOR; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.color = decode_vertex_color(input.color); + o.uv = input.uv; + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float4 c = input.color; + + #if defined(HEATMAP) + float value = TEX2D(heatmap, input.uv).r; + float4 color = TEX2D(diffuse_map, float2(0.5, value)); + color.a = value; + c *= color; + #elif defined(HEATMAP_WRITE) + #if !defined(HEATMAP_CLEAR) + float value = 1-(distance(input.uv, float2(0.5, 0.5)) * 2); + c *= value; + #endif + #endif + + return c; + } + """ + } + + heatmap_apply = { + includes = [ "common", "gbuffer_access" ] + + samplers = { + linear_depth = { sampler_states = "clamp_point" } + heatmap = { sampler_states = "clamp_linear" } + heatmap_legend = { sampler_states = "clamp_linear" } + } + + code=""" + DECLARE_SAMPLER_2D(linear_depth); + DECLARE_SAMPLER_2D(heatmap); + DECLARE_SAMPLER_2D(heatmap_legend); + + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 w : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 heatmap_world_position; + float2 heatmap_world_extents; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) + { + PS_INPUT o; + + o.position = mul(input.position, world_view_proj); + o.w = encode_world_pos(o.position, camera_unprojection); + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + half2 screen_uv = input.position.xy / back_buffer_size; + + float d = gbuffer_decode_depth(TEX2D(linear_depth, screen_uv)); + float3 world_pos = decode_world_pos(input.w, d); + float2 calculated_uv = (world_pos - heatmap_world_position) / heatmap_world_extents; + if (calculated_uv.x >= 0 && calculated_uv.x <= 1 + && calculated_uv.y >= 0 && calculated_uv.y <= 1) + { + calculated_uv.y = 1 - calculated_uv.y; + float value = TEX2D(heatmap, calculated_uv).r; + float4 color = TEX2D(heatmap_legend, float2(0.5, value)); + color.a = value; + return color; + } + return 0; + } + """ + } +} + +shaders = { + heatmap = { + editor_advanced_mode = true + + contexts = { + default = { + passes = [ + { + defined = "HEATMAP_WRITE" + pass = [ + { layer="transparent_heatmap" hlsl_shader="heatmap" render_states="heatmap" } + ] + fail = [ + { layer="transparent" hlsl_shader="heatmap" render_states="heatmap" } + ] + } + ] + } + } + + compile = { + default = [ + { defines="" } + ] + } + } + + heatmap_apply = { + editor_advanced_mode = true + + contexts = { + default = { + passes = [ + { hlsl_shader="heatmap_apply" render_states="heatmap_apply" } + ] + } + } + + compile = { + default = [ + { defines="" } + ] + } + } +} + +static_compile = [ + { shader="heatmap_apply" } + + { shader="heatmap" } + { shader="heatmap" defines=["HEATMAP"] } + { shader="heatmap" defines=["HEATMAP_WRITE"] } + { shader="heatmap" defines=["HEATMAP_WRITE" "HEATMAP_CLEAR"] } +] \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/lighting.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/lighting.shader_source new file mode 100644 index 0000000..b2147f5 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/lighting.shader_source @@ -0,0 +1,2005 @@ +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source", + "core/stingray_renderer/shader_libraries/lighting_common.shader_source", + "core/stingray_renderer/shader_libraries/shadow_map_common.shader_source", + "core/stingray_renderer/shader_libraries/post_processing_common.shader_source" ] + +render_states = { + filter = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "false" + } + } + + light = { + inherits = "default" + states = { + z_enable = "true" + z_write_enable = "false" + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_one" + src_blend = "blend_one" + + cull_mode = "cull_ccw" + z_func = "greater_equal" + + defined_SKIN_MATERIAL_ENABLED = { + stencil_enable = "true" + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_mask = "0x18" + stencil_write_mask = "0x0" + defined_SKIN = { + stencil_ref = "0x8" + } + ndefined_SKIN = { + stencil_ref = "0x0" + } + } + write_mask0 = "red|green|blue" + } + } + + light_stencil = { + inherits = "default" + states = { + z_enable = "true" + z_write_enable = "false" + z_func = "less_equal" + + cull_mode = "cull_none" + + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + + stencil_enable = "true" + stencil_func = "always" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_ref = "0x0" + stencil_func_back_side = "always" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail = "stencil_op_incr" + stencil_z_fail_back_side = "stencil_op_decr" + } + } + + global_lighting = { + inherits = "filter" + states = { + z_enable = "true" + z_func = "greater" + defined_SKIN_MATERIAL_ENABLED = { + stencil_enable = "true" + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_z_fail = "stencil_op_keep" + + stencil_func_back_side = "equal" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_keep" + + stencil_mask = "0x18" + stencil_write_mask = "0x0" + defined_SKIN = { + stencil_ref = "0x8" + write_mask1 = "red|green|blue|alpha" + } + ndefined_SKIN = { + stencil_ref = "0x0" + } + } + // alpha channel is not used for blending anymore + write_mask0 = "red|green|blue|alpha" + } + } + + sun_shadow_mask = { + inherits = "filter" + states = { + write_mask0 = "red" + stencil_enable = "true" + stencil_func = "equal" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + defined_FILL = { + stencil_ref = "0x0" + } + ndefined_FILL = { + defined_FILL_SHADOW = { + stencil_ref = "0x0" + } + ndefined_FILL_SHADOW = { + stencil_ref = "0x1" + } + } + + stencil_z_fail = "stencil_op_keep" + stencil_mask = "0x7" + stencil_write_mask = "0x7" + } + } + + sun_shadow_cutter = { + inherits = "default" + states = { + cull_mode="cull_ccw" + + z_enable = "true" + z_write_enable = "false" + + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + + stencil_enable = "true" + stencil_func = "always" + stencil_fail = "stencil_op_keep" + stencil_pass = "stencil_op_keep" + stencil_ref = "0x0" + stencil_z_fail = "stencil_op_incr" + + stencil_func_back_side = "always" + stencil_fail_back_side = "stencil_op_keep" + stencil_pass_back_side = "stencil_op_keep" + stencil_z_fail_back_side = "stencil_op_incr" + + stencil_mask = "0x7" + stencil_write_mask = "0x7" + } + } + + ao_source = { + inherits = "default" + states = { + z_enable = "true" + z_func = "greater_equal" + z_write_enable = "false" + cull_mode = "cull_ccw" + + write_mask0 = "red" + blend_enable = "true" + blend_op = "blend_op_min" + dest_blend = "blend_one" + src_blend = "blend_one" + } + } + + global_indirect_specular_lighting = { + inherits = "filter" + states = { + z_enable = "true" + z_func = "greater" + + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_one" + src_blend = "blend_dest_alpha" // The ao and transparency should be in the hdr0 alpha + } + } + + global_indirect_specular_lighting_and_fog = { + inherits = "filter" + states = { + z_enable = "true" + z_func = "greater" + + blend_enable = "true" + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_one" + + write_mask0 = "red|green|blue" + } + } +} + +sampler_states = { + clamp_point_no_mip = { + inherits="clamp" + states = { + defined_RENDERER_GL = { + filter = "min_mag_point" + } + ndefined_RENDERER_GL = { + filter = "min_mag_mip_point" + } + } + } +} + +hlsl_shaders = { + global_lighting = { + includes = [ "common", "gbuffer_access", "brdf", "color_management", "post_processing_common", "taa_offsets", "shadow_bias", "shadow_map_filtering", "clustered_shading" ] + samplers = { + gbuffer0 = { sampler_states = "clamp_point" } + gbuffer1 = { sampler_states = "clamp_point" } + gbuffer2 = { sampler_states = "clamp_point" } + // gbuffer3 = { sampler_states = "clamp_point" } + linear_depth = { sampler_states = "clamp_point" } + global_diffuse_map = { sampler_states = "clamp_linear"} + global_specular_map = { sampler_states = "clamp_linear"} + brdf_lut = { sampler_states = "clamp_linear"} + hdr1 = { sampler_states = "clamp_point" } + hdr2 = { sampler_states = "clamp_point" } + defined_SSR_ENABLED = { + hdr0_div2_mip6 = { sampler_states = "clamp_linear" } + ldr4_div2 = { sampler_states = "clamp_point" } + } + defined_SSAO_ENABLED = { + ldr3_div2 = { sampler_states = "clamp_linear" } + } + defined_SUN = { + ldr0 = { sampler_states = "clamp_point" } + } + defined_RENDERER_GL = { + depth_stencil_buffer = { sampler_states = "clamp_point" } + } + + // + defined_CLUSTERED_SHADING = { + local_lights_shadow_atlas = { sampler_states = "shadow_map" } + // cs_cluster_buffer = { sampler_states = "clamp_point_no_mip" } + // cs_light_index_buffer = { sampler_states = "clamp_point_no_mip" } + // cs_light_data_buffer = { sampler_states = "clamp_point_no_mip" } + // cs_light_shadow_matrices_buffer = { sampler_states = "clamp_point_no_mip" } + } + } + + vp_code = """ + layout(location = POSITION0) in highp vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + out vec2 v_uv0; + out vec4 v_w; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + void main() { + v_uv0 = in_uv0; + vec4 p = in_pos * world_view_proj; + v_w = encode_world_pos(p); + p.z = p.w; + gl_Position = p; + } + """ + + fp_code = """ + DECLARE_SAMPLER_2D(gbuffer0); + DECLARE_SAMPLER_2D(gbuffer1); + DECLARE_SAMPLER_2D(gbuffer2); + DECLARE_SAMPLER_2D(gbuffer3); + DECLARE_SAMPLER_2D(ldr0); + DECLARE_SAMPLER_CUBE(global_diffuse_map); + uniform mediump samplerCube global_specular_map; + DECLARE_SAMPLER_2D(brdf_lut); + + uniform highp usampler2D linear_depth; + + CBUFFER_START(c1) + #if defined(SUN) + UNIFORM vec3 sun_color; + UNIFORM vec3 sun_direction; + #endif + UNIFORM vec3 baked_diffuse_tint; + UNIFORM vec3 reflections_tint; + CBUFFER_END + + #if defined(CLUSTERED_SHADING) + DECLARE_CLUSTER_DATA(cs_cluster_buffer); + DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer); + DECLARE_LIGHT_DATA(cs_light_data_buffer); + DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer); + DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas); + #endif + + in vec2 v_uv0; + in highp vec4 v_w; + layout(location = 0) out mediump vec4 out_base; + + void main() { + mediump vec4 gbuffer_0 = TEX2D(gbuffer0, v_uv0); + mediump vec4 gbuffer_1 = TEX2D(gbuffer1, v_uv0); + mediump vec4 gbuffer_2 = TEX2D(gbuffer2, v_uv0); + mediump vec4 gbuffer_3 = TEX2D(gbuffer3, v_uv0); + highp float depth = uintBitsToFloat(texture(linear_depth, v_uv0).r); + + mediump vec3 N = normalize(gbuffer_decode_normal(gbuffer_1)); + mediump float roughness = gbuffer_decode_roughness(gbuffer_1); + mediump float ao = gbuffer_decode_ambient_occlusion(gbuffer_2); + mediump vec3 base_color = gbuffer_decode_base_color(gbuffer_0); + mediump float metallic = gbuffer_decode_metallic_mask(gbuffer_0); + mediump vec3 diffuse_color = lerp(base_color, vec3(0, 0, 0), metallic); + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + mediump vec3 specular_color = lerp(vec3(0.04, 0.04, 0.04), base_color, metallic); + + highp vec3 wp = decode_world_pos(v_w, depth); + highp vec3 V = normalize(vec3(camera_world[0].w, camera_world[1].w, camera_world[2].w) - wp); + + mediump vec3 ambient = gbuffer_decode_ambient_diffuse_light(gbuffer_3) * baked_diffuse_tint * diffuse_color; + mediump float mipmap_index = roughness * 7.0; + + mediump vec2 brdf_uv = float2(clamp(dot(N, V), 0.0, 1.0), roughness); + mediump vec2 env_brdf = TEX2D(brdf_lut, brdf_uv).xy; + + mediump vec4 global_spec_rgbm = TEXCUBELOD(global_specular_map, reflect(-V, N), mipmap_index); + mediump vec3 global_spec = rgbm_decode(global_spec_rgbm); + ambient += global_spec * (specular_color * env_brdf.x + env_brdf.y) * reflections_tint; + ambient *= ao; + + mediump vec3 col = ambient; + mediump vec3 acc_diff = vec3(0,0,0); + mediump vec3 acc_spec = vec3(0,0,0); + mediump vec3 translucency = vec3(0.0f, 0.0f, 0.0f); + half density = gbuffer_decode_density(gbuffer_2); + #if defined(SUN) + mediump vec3 L = normalize(-sun_direction); + mediump vec4 aux = TEX2D(ldr0, v_uv0); + bsdf(L, V, N, sun_color, diffuse_color, specular_color, roughness, aux.r, acc_diff, acc_spec); + + translucency = calculate_translucency(L, V, N, lerp(lerp(0.5, 1.0, aux.r), saturate(aux.r * TRANSLUCENCY_DIRECTIONAL_SCALE), density), base_color, sun_color, density); + #endif + #if defined(CLUSTERED_SHADING) + clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, cs_light_shadow_matrices_buffer, local_lights_shadow_atlas, wp, V, N, diffuse_color, specular_color, roughness, gl_FragCoord.xy, depth, density, base_color, acc_diff, acc_spec, translucency); + #endif + col += (acc_diff + acc_spec + translucency); + + out_base = vec4(col.x, + col.y, col.z, 1.0); + } + """ + + code=""" + DECLARE_SAMPLER_2D(gbuffer0); + DECLARE_SAMPLER_2D(gbuffer1); + DECLARE_SAMPLER_2D(gbuffer2); + // DECLARE_SAMPLER_2D(gbuffer3); + DECLARE_SAMPLER_2D(linear_depth); + DECLARE_SAMPLER_CUBE(global_diffuse_map); + DECLARE_SAMPLER_CUBE(global_specular_map); + DECLARE_SAMPLER_2D(brdf_lut); + DECLARE_SAMPLER_2D(hdr1); + DECLARE_SAMPLER_2D(hdr2); + #if defined(SSR_ENABLED) + DECLARE_SAMPLER_2D(hdr0_div2_mip6); + DECLARE_SAMPLER_2D(ldr4_div2); + #endif + #if defined(SSAO_ENABLED) + DECLARE_SAMPLER_2D(ldr3_div2); + #endif + #if defined(SUN) + DECLARE_SAMPLER_2D(ldr0); + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float4 w : TEXCOORD1; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + #if defined(SUN) + // sun parameters from shading environment + float3 sun_color; + float3 sun_direction; + #endif + float3 ambient_tint; + float ao_enabled; + + float3 ambient_diffuse_fade; // min multiplier, height offset, falloff + CBUFFER_END + + #if defined(CLUSTERED_SHADING) + DECLARE_CLUSTER_DATA(cs_cluster_buffer); + DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer); + DECLARE_LIGHT_DATA(cs_light_data_buffer); + DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer); + DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas); + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + o.w = encode_world_pos(o.position); + o.position.z = o.position.w; + + #if defined(CLUSTERED_SHADING) + // When using temporal antialiasing we try to cancel out the jitter + // that was introduced in the depth buffer. This is to minimize self occlusion + // that can arrise when performing a depth test beween the jittered depth buffer + // and a non jittered shadow map. + float4 tmp = o.position; + float4 view_space = tmp / tmp.w; + view_space.xy -= get_vs_halton_offset(frame_number); + tmp = view_space * tmp.w; + o.w = encode_world_pos(tmp); + #endif + + return o; + } + + struct PS_OUTPUT { + #if defined(SKIN) + half4 specular : SV_TARGET0; + half4 base : SV_TARGET1; + #else + half4 base : SV_TARGET0; + #endif + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) { + PS_OUTPUT o; + + float4 gbuffer_0 = TEX2D(gbuffer0, input.uv); + float4 gbuffer_1 = TEX2D(gbuffer1, input.uv); + float4 gbuffer_2 = TEX2D(gbuffer2, input.uv); + // float4 gbuffer_3 = TEX2D(gbuffer3, input.uv); + float ao = gbuffer_decode_ambient_occlusion(gbuffer_2); + #if defined(SSAO_ENABLED) + #if defined(D3D11) + const bool ssao_enabled = ao_enabled && capture_cubemap == 0.0; + #else + const bool ssao_enabled = ao_enabled; + #endif + if (ssao_enabled) { + float ssao = TEX2D(ldr3_div2, input.uv).r; + ao = min(ao, ssao); + } + #endif + float depth = gbuffer_decode_depth(TEX2D(linear_depth, input.uv)); + + float3 N = normalize(gbuffer_decode_normal(gbuffer_1)); + float roughness = gbuffer_decode_roughness(gbuffer_1); + + float3 base_color = gbuffer_decode_base_color(gbuffer_0); + float metallic = gbuffer_decode_metallic_mask(gbuffer_0); + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + float3 specular_color = lerp(float3(0.04,0.04,0.04), base_color, metallic); + float3 diffuse_color = lerp(base_color, float3(0,0,0), metallic); + + #if defined(D3D11) + float specular_toggle = 1-capture_cubemap; + // If we are currently capturing a reflection probe, use specular F0 as diffuse color for metallics + diffuse_color = (specular_toggle == 1) ? diffuse_color : lerp(diffuse_color, specular_color, metallic); + + // Remove ambient tint from cube map baking, since will have an exponential effect on the result. + ambient_tint = (specular_toggle == 1) ? ambient_tint : 1.0; + #else + float specular_toggle = 1.f; + #endif + + float3 wp = decode_world_pos(input.w, depth); + float3 V = normalize(camera_world._m30_m31_m32 - wp); + + #if defined(AMBIENT_FADE_UP) + float3 indirect_light_tint = max(ambient_diffuse_fade.x, 1.0 - exp((wp.z - ambient_diffuse_fade.y)/ambient_diffuse_fade.z)) * ambient_tint; + #elif defined(AMBIENT_FADE_DOWN) + float3 indirect_light_tint = max(ambient_diffuse_fade.x, 1.0 - exp((ambient_diffuse_fade.y - wp.z)/ambient_diffuse_fade.z)) * ambient_tint; + #elif defined(AMBIENT_FADE_MIRROR) + float3 indirect_light_tint = max(ambient_diffuse_fade.x, 1.0 - exp(-abs(ambient_diffuse_fade.y - wp.z)/ambient_diffuse_fade.z)) * ambient_tint; + #else + float3 indirect_light_tint = ambient_tint; + #endif + + float4 local_radiation_data = TEX2D(hdr2, input.uv) * specular_toggle; + float3 global_radiation_probe_data = rgbm_decode(TEXCUBELOD(global_diffuse_map, N, 0)); + float3 diffuse_ambient = (local_radiation_data.rgb + (1.0 - local_radiation_data.w) * global_radiation_probe_data) * indirect_light_tint * diffuse_color; + + float2 scale_bias = TEX2D(brdf_lut, float2(saturate(dot(N, V)), roughness)).xy; + + // Remove energy from the diffuse ambient term from the Fresnel reflections + float inv_fresnel = 1.f - scale_bias.y; + float diff_attn = lerp(inv_fresnel, inv_fresnel * 0.04, metallic); + diffuse_ambient *= diff_attn; + + // Read all the reflection data + float mipmap_index = roughness * 7; + float4 local_reflections_data = TEX2D(hdr1, input.uv); + float3 global_probe_data = rgbm_decode(TEXCUBELOD(global_specular_map, reflect(-V, N), mipmap_index)); + + // Prepare the reflection weight data + float local_probe_weight = local_reflections_data.w; + + #if defined(SSR_ENABLED) + float ssr_mip_level = TEX2DLOD(ldr4_div2, input.uv, 0).r * SSR_MIPMAP_LEVELS; + float4 ssr_data = TEX2DLOD(hdr0_div2_mip6, input.uv, ssr_mip_level); + ssr_data.rgb = inv_safe_range_tone_map(ssr_data.rgb); + + float ssr_weight = ssr_data.a; + float3 ssr_influence = ssr_data.rgb * ssr_weight; + + // Distribute the reflection weight (ssr -> local probes -> global probe) + local_probe_weight = min(local_probe_weight, 1.0 - ssr_weight); + const float global_probe_weight = 1.0 - (ssr_weight + local_probe_weight); + + // adjust local reflection weight with the ssr modification + local_reflections_data.rgb = local_reflections_data.rgb * (local_reflections_data.w > 0.0 ? (local_probe_weight / local_reflections_data.w) : 1.0); + #else + float ssr_weight = 0.0; + float3 ssr_influence = float3(0.0, 0.0, 0.0); + + // Distribute the reflection weight (local probes -> global probe) + float global_probe_weight = 1.0 - local_probe_weight; + #endif + + // Final reflection influence terms + float3 local_probe_influence = local_reflections_data.rgb; + float3 global_probe_influence = global_probe_data.rgb * global_probe_weight; + + float3 specular_ambient = (ssr_influence + local_probe_influence + global_probe_influence) * (specular_color * scale_bias.x + scale_bias.y) * indirect_light_tint; + + // If we are currently capturing a reflection probe, mask out all IBL based relfections to avoid feedback loops. + specular_ambient *= specular_toggle; + + #if !defined(SKIN) + float3 ambient = diffuse_ambient + specular_ambient; + ambient *= ao; + + float3 col = ambient; + float3 acc_diff = 0; + float3 acc_spec = 0; + float3 translucency = 0; + half density = gbuffer_decode_density(gbuffer_2); + #if defined(SUN) + float3 L = normalize(-sun_direction); + float4 aux = TEX2D(ldr0, input.uv); + bsdf(L, V, N, sun_color, diffuse_color, specular_color, roughness, aux.r, acc_diff, acc_spec); + + translucency = calculate_translucency(L, V, N, lerp(lerp(0.5, 1.0, aux.r), saturate(aux.r * TRANSLUCENCY_DIRECTIONAL_SCALE), density), base_color, sun_color, density); + #endif + #if defined(CLUSTERED_SHADING) + clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, cs_light_shadow_matrices_buffer, local_lights_shadow_atlas, wp, V, N, diffuse_color, specular_color, roughness, input.position.xy, depth, density, base_color, acc_diff, acc_spec, translucency); + #endif + col += acc_diff + acc_spec * specular_toggle + translucency; + o.base = float4(col, 1); + #else + float3 diffuse_lighting = diffuse_ambient * ao; + float3 specular_lighting = specular_ambient * ao; + float3 acc_diff = 0; + float3 acc_spec = 0; + float3 translucency = 0; + half density = gbuffer_decode_density(gbuffer_2); + #if defined(SUN) + float3 L = normalize(-sun_direction); + float4 aux = TEX2D(ldr0, input.uv); + bsdf(L, V, N, sun_color, diffuse_color, specular_color, roughness, aux.r, acc_diff, acc_spec); + + // TODO: translucency is not supported for skin materials atm, since they share gbuffer channel + // translucency = calculate_translucency(L, V, N, lerp(lerp(0.5, 1.0, aux.r), saturate(aux.r * TRANSLUCENCY_DIRECTIONAL_SCALE), density), base_color, sun_color, density); + #endif + #if defined(CLUSTERED_SHADING) + clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, cs_light_shadow_matrices_buffer, local_lights_shadow_atlas, wp, V, N, diffuse_color, specular_color, roughness, input.position.xy, depth, density, base_color, acc_diff, acc_spec, translucency); + #endif + + diffuse_lighting += acc_diff;// + translucency; + specular_lighting += acc_spec * specular_toggle; + + o.specular = float4(specular_lighting, 1.0); + o.base = float4(diffuse_lighting, density); + #endif + + return o; + } + """ + } + + light_source = { + includes = [ "common", "gbuffer_access", "brdf", "shadow_bias", "taa_offsets", "shadow_map_filtering" ] + + samplers = { + defined_SHADOW_MAPPING = { + local_lights_shadow_atlas = { sampler_states = "shadow_map" } + } + gbuffer0 = { sampler_states = "clamp_point" } + gbuffer1 = { sampler_states = "clamp_point" } + gbuffer2 = { sampler_states = "clamp_point" } + linear_depth = { sampler_states = "clamp_point" } + defined_SPOT = { + defined_COOKIE_PROJECTION = { + defined_COOKIE_CLAMP_SAMPLER = { + ndefined_COOKIE_UV_OFFSET_ANIM = { + diffuse_map = { sampler_states = "clamp_linear" } + } + defined_COOKIE_UV_OFFSET_ANIM = { + diffuse_map = { sampler_states = "wrap_linear" } + } + } + ndefined_COOKIE_CLAMP_SAMPLER = { + diffuse_map = { sampler_states = "wrap_linear" } + } + } + } + } + + vp_code = """ + layout(location = POSITION0) in highp vec4 in_pos; + + out vec4 v_w; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + CBUFFER_START(light) + UNIFORM vec3 light_proxy_scale; + CBUFFER_END + + void main() { + vec4 p = new_half4_xyz(in_pos.xyz * light_proxy_scale, 1.0) * world_view_proj; + v_w = encode_world_pos(p); + gl_Position = p; + } + """ + + fp_code = """ + DECLARE_SAMPLER_2D(gbuffer0); + DECLARE_SAMPLER_2D(gbuffer1); + DECLARE_SAMPLER_2D(gbuffer2); + + #if defined(SHADOW_MAPPING) + DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas); + #endif + + uniform highp usampler2D linear_depth; + + #if defined(SPOT) + CBUFFER_START(c1) + UNIFORM mat4 world; + CBUFFER_END + #endif + + CBUFFER_START(light) + UNIFORM vec3 light_position; + UNIFORM vec3 light_color; + // start, 1.f/(end-start), exp + UNIFORM vec3 light_falloff; + #if defined(SPOT) + // scale, bias + UNIFORM vec2 light_spot_falloff; + #endif + + #if defined(SHADOW_MAPPING) + #if defined(OMNI) + UNIFORM float4x4 world_to_shadow_maps[6]; + #else + UNIFORM float4x4 world_to_shadow_map; + #endif + #endif + CBUFFER_END + + in highp vec4 v_w; + layout(location = 0) out vec4 out_base; + + void main() { + half2 uv = gl_FragCoord.xy / back_buffer_size; + float depth = uintBitsToFloat(texture(linear_depth, uv).r); + + float3 wp = decode_world_pos(v_w, depth); + float3 V = normalize(float3(camera_world[0].w, camera_world[1].w, camera_world[2].w) - wp); + + float3 L = light_position - wp; + float len_L = length(L) + 0.00001f; + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + L *= 1.0 / len_L; + + float normalized_distance = (len_L - light_falloff.x) * light_falloff.y; + if (normalized_distance > 1.0f) discard; + + #if defined(OMNI) && defined(SHADOW_MAPPING) + // The shadows from the faces of an onmni light are populated in the following order and directions. + // float3(-1.0f, 0.0f, 0.0f), + // float3( 1.0f, 0.0f, 0.0f), + // float3( 0.0f, -1.0f, 0.0f), + // float3( 0.0f, 1.0f, 0.0f), + // float3( 0.0f, 0.0f, -1.0f) + // float3( 0.0f, 0.0f, 1.0f), + + // Based on the biggest component of the vector from the shaded position to the light source and its sign, chose the correct + // shadow map index to get the correct world position to shadow map matrix. + float3 shadow_L = -L; + int3 is_positive = int3(greaterThan(shadow_L, float3(0.0, 0.0, 0.0))); + float3 abs_shadow_L = abs(shadow_L); + int test_index = (abs_shadow_L.x > abs_shadow_L.y && abs_shadow_L.x > abs_shadow_L.z) ? 0 + is_positive[0]: (abs_shadow_L.y > abs_shadow_L.z) ? 2 + is_positive[1] : 4 + is_positive[2]; + + // On the cpu side, the whole matrix will be full of zeroes if the face is not casting any shadows. Just test the first element. + float shadow_active = world_to_shadow_maps[uint(test_index)][0].x != 0.0f? 1.0f : 0.0f; + float4 ls = mul(float4(wp, 1), world_to_shadow_maps[uint(test_index)]); + #elif defined(SPOT) + float3 spot_dir = float3(world[0].y, world[1].y, world[2].y); + float spot_attenuation = spot_angle_attenuation(dot(-L, spot_dir), light_spot_falloff.x, light_spot_falloff.y); + if (spot_attenuation == 0.0f) discard; + attn *= spot_attenuation; + #endif + + float translucency_attn = attn; + float shadow_mask = 0.0; + #if defined(SHADOW_MAPPING) + #if !defined(OMNI) + float4 ls = mul(float4(wp, 1), world_to_shadow_map); + #endif + + ls.xyz /= ls.w; + ls.z = apply_shadow_depth_comparison_bias(depth, ls.z, local_shadow_map_bias); + float2 sm_resolution = float2(textureSize(local_lights_shadow_atlas, 0)); + #if defined(OMNI) + half shadow = shadow_active > 0.0f? shadow_intensity_2d(local_lights_shadow_atlas, sm_resolution, ls.xy, ls.z) : 1.0f; + #else + half shadow = shadow_intensity_2d(local_lights_shadow_atlas, sm_resolution, ls.xy, ls.z); + #endif + shadow_mask = shadow; + attn *= shadow_mask; + #endif + + half4 gbuffer_0 = TEX2D(gbuffer0, uv); + half4 gbuffer_1 = TEX2D(gbuffer1, uv); + half4 gbuffer_2 = TEX2D(gbuffer2, uv); + + float3 N = normalize(gbuffer_decode_normal(gbuffer_1)); + half roughness = gbuffer_decode_roughness(gbuffer_1); + + float3 base_color = gbuffer_decode_base_color(gbuffer_0); + half metallic = gbuffer_decode_metallic_mask(gbuffer_0); + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + float3 specular_color = lerp(new_half3(0.04, 0.04, 0.04), base_color, metallic); + float3 diffuse_color = lerp(base_color, new_half3(0.0, 0.0, 0.0), metallic); + + float3 acc_diff = new_half3(0.0, 0.0, 0.0); + float3 acc_spec = new_half3(0.0, 0.0, 0.0); + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness, attn, acc_diff, acc_spec); + + half density = gbuffer_decode_density(gbuffer_2); + float3 translucency = calculate_translucency(L, V, N, translucency_attn * lerp(lerp(0.5, 1.0, shadow_mask), shadow_mask, density), base_color, light_color, density); + + float3 col = acc_diff + acc_spec + translucency; + out_base = float4(col, 0.0); + } + """ + + code=""" + DECLARE_SAMPLER_2D(gbuffer0); + DECLARE_SAMPLER_2D(gbuffer1); + #if !defined(SKIN) + DECLARE_SAMPLER_2D(gbuffer2); + #endif + DECLARE_SAMPLER_2D(linear_depth); + #if defined(COOKIE_PROJECTION) && defined(SPOT) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Cookie Projection Map" type="resource" sort_tag="0_DIFFUSE_MAP"} + #endif + + #if defined(SHADOW_MAPPING) && (defined(D3D11) || defined(D3D12) || defined(GNM)) + DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas); + #endif + + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if !defined(STENCIL_MARK) + float4 w : TEXCOORD1; + #endif + }; + + CBUFFER_START(c0) + #if defined(SPOT) || defined(BOX) + float4x4 world; + float4x4 inv_world; + #endif + float4x4 world_view_proj; + CBUFFER_END + + CBUFFER_START(light) + float3 light_position; + float3 light_color; + + #if defined(OMNI) || defined(SPOT) + // start, 1.f/(end-start), exp + float3 light_falloff; + #endif + #if defined(SPOT) + #if defined(COOKIE_PROJECTION) + // scale, bias, projection_scale + float3 light_spot_falloff; + #else + // scale, bias + float2 light_spot_falloff; + #endif + #endif + + #if defined(SHADOW_MAPPING) + #if defined(OMNI) + float4x4 world_to_shadow_maps[6]; + #else + float4x4 world_to_shadow_map; + #endif + float shadow_intensity; + #endif + + #if defined(BOX) + float3 light_box_min; + float3 light_box_max; + #endif + + float3 light_proxy_scale; + #if defined(COOKIE_PROJECTION) && defined(SPOT) + #if defined(COOKIE_UV_OFFSET_ANIM) + float2 cookie_uv_speed; // exports={ name="Cookie UV Offset Speed" type="vector2" value=[0.1 0.1] min=[-10.0 -10.0] max=[10.0 10.0] step=[0.001 0.001] } + #endif + #if defined(COOKIE_UV_OFFSET) + float2 cookie_uv_offset; // exports={ name="Cookie UV Offset" type="vector2" value=[0.5 0.5] min=[0 0] max=[1 1] step=[0.001 0.001] } + #endif + #if defined(COOKIE_UV_ROTATION_ANIM) + float2 cookie_uv_rotation_pivot; // exports={ name="Cookie UV Rotation Pivot" type="vector2" value=[0.5 0.5] min=[-1.0 -1.0] max=[1.0 1.0] step=[0.001 0.001] } + float2 cookie_uv_rotation_speed; // exports={ name="Cookie UV Rotation Speed" type="scalar" value=1.0 min=-50 max=50 step=0.001 } + #endif + #if defined(COOKIE_UV_SCALE) + float2 cookie_uv_scale; // exports={ name="Cookie UV Scale" type="vector2" value=[1 1] min=[0.001 0.001] max=[100 100] step=[0.001 0.001] } + #endif + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + #if defined(BOX) + float3 p = input.position.xyz * light_proxy_scale; + p += (light_box_max + light_box_min) * 0.5; + o.position = mul(float4(p, 1), world_view_proj); + #else + o.position = mul(float4(input.position.xyz * light_proxy_scale, 1), world_view_proj); + #endif + + + #if !defined(STENCIL_MARK) + #if defined(SHADOW_MAPPING) + // When using temporal antialiasing we try to cancel out the jitter + // that was introduced in the depth buffer. This is to minimize self occlusion + // that can arrise when performing a depth test beween the jittered depth buffer + // and a non jittered shadow map. + float4 tmp = o.position; + float4 view_space = tmp / tmp.w; + view_space.xy -= get_vs_halton_offset(frame_number); + tmp = view_space * tmp.w; + o.w = encode_world_pos(tmp); + #else + o.w = encode_world_pos(o.position); + #endif + #endif + return o; + } + + struct PS_OUTPUT { + #if defined(SKIN) + half4 specular : SV_TARGET0; + half4 base : SV_TARGET1; + #else + half4 base : SV_TARGET0; + #endif + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + #if defined(STENCIL_MARK) + half4 ps_main() : SV_TARGET0 { + return half4(0,0,0,0); + } + #else + PS_OUTPUT ps_main(PS_INPUT input) + { + PS_OUTPUT o; + o.base = half4(0,0,0,0); + + half2 uv = input.position.xy / back_buffer_size; + float depth = gbuffer_decode_depth(TEX2D(linear_depth, uv)); + + #if defined(D3D11) + float specular_toggle = 1-capture_cubemap; + #else + float specular_toggle = 1.f; + #endif + + float3 wp = decode_world_pos(input.w, depth); + float3 L = float3(0,0,1); + float attn = 1.f; + #if defined(OMNI) || defined(SPOT) + L = light_position - wp; + float len_L = length(L) + 0.00001f; + attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + L *= 1.0/len_L; + + float normalized_distance = (len_L - light_falloff.x) * light_falloff.y; + [branch] + if (normalized_distance > 1.0f) discard; + + #if defined(OMNI) && defined(SHADOW_MAPPING) + // The shadows from the faces of an onmni light are populated in the following order and directions. + // float3(-1.0f, 0.0f, 0.0f), + // float3( 1.0f, 0.0f, 0.0f), + // float3( 0.0f, -1.0f, 0.0f), + // float3( 0.0f, 1.0f, 0.0f), + // float3( 0.0f, 0.0f, -1.0f) + // float3( 0.0f, 0.0f, 1.0f), + + // Based on the biggest component of the vector from the shaded position to the light source and its sign, chose the correct + // shadow map index to get the correct world position to shadow map matrix. + const float3 shadow_L = -L; + const int3 is_positive = shadow_L > 0; + const float3 abs_shadow_L = abs(shadow_L); + int test_index = (abs_shadow_L.x > abs_shadow_L.y && abs_shadow_L.x > abs_shadow_L.z) ? 0 + is_positive[0]: (abs_shadow_L.y > abs_shadow_L.z) ? 2 + is_positive[1] : 4 + is_positive[2]; + + // On the cpu side, the whole matrix will be full of zeroes if the face is not casting any shadows. Just test the first element. + const float shadow_active = world_to_shadow_maps[uint(test_index)]._m00 != 0.0f? 1.0f : 0.0f; + float4 ls = mul(float4(wp, 1), world_to_shadow_maps[uint(test_index)]); + #elif defined(SPOT) + float spot_attenuation = spot_angle_attenuation(dot(-L, world._m10_m11_m12), light_spot_falloff.x, light_spot_falloff.y); + if (spot_attenuation == 0.0f) discard; + attn *= spot_attenuation; + #endif + #elif defined(BOX) + L = -world._m10_m11_m12; + float3 op = mul(float4(wp, 1), inv_world).xyz; + float3 containment = (op > light_box_min) * (op < light_box_max); + float mask = dot(containment, containment) == 3; + attn = light_attenuation(op.y, light_box_min.y, 1.f / (light_box_max.y - light_box_min.x)) * mask; + #endif + + #if defined(COOKIE_PROJECTION) && defined(SPOT) + float2 cookie_uv = mul(L, (float3x3)inv_world).xz * light_spot_falloff.z; + + #if defined(COOKIE_UV_SCALE) + cookie_uv *= cookie_uv_scale; + #endif + + #if defined(COOKIE_UV_OFFSET) + #if defined(GL2) + cookie_uv = cookie_uv * float2(0.5, -0.5) + float2(cookie_uv_offset.x, -cookie_uv_offset.y); + #else + cookie_uv = cookie_uv * float2(0.5, -0.5) + cookie_uv_offset; + #endif + #else + cookie_uv = cookie_uv * float2(0.5, -0.5) + 0.5; + #endif + + #if defined(COOKIE_UV_ROTATION_ANIM) + float A = cookie_uv_rotation_speed * time; + float C = cos(A); + float S = sin(A); + float2 center = cookie_uv - cookie_uv_rotation_pivot; + float2 result = float2( (center.x * C + center.y * S) + cookie_uv_rotation_pivot.x, + (center.y * C - center.x * S) + cookie_uv_rotation_pivot.y); + #if defined(GL2) + cookie_uv = float2(result.x, -result.y); + #else + cookie_uv = result; + #endif + #endif + + #if defined(COOKIE_UV_OFFSET_ANIM) + #if defined(GL2) + cookie_uv += float2(cookie_uv_speed.x, -cookie_uv_speed.y) * time; + #else + cookie_uv += cookie_uv_speed * time; + #endif + #endif + + light_color *= TEX2D(diffuse_map, cookie_uv); + #endif + + float translucency_attn = attn; + float shadow_mask = 0.0; + #if defined(SHADOW_MAPPING) + #if !defined(OMNI) + float4 ls = mul(float4(wp,1), world_to_shadow_map); + #endif + + #if defined(D3D11) || defined(D3D12) || defined(GNM) + ls.xyz /= ls.w; + ls.z = apply_shadow_depth_comparison_bias(depth, ls.z, local_shadow_map_bias); + float2 sm_resolution; + local_lights_shadow_atlas.tex.GetDimensions(sm_resolution.x, sm_resolution.y); + #if defined(OMNI) + half shadow = shadow_active > 0.0f? shadow_intensity_2d(local_lights_shadow_atlas, sm_resolution, ls.xy, ls.z) : 1.0f; + #else + half shadow = shadow_intensity_2d(local_lights_shadow_atlas, sm_resolution, ls.xy, ls.z); + #endif + shadow_mask = shadow; + attn *= saturate((shadow_mask - 1.0) * shadow_intensity + 1.0); + #endif + #endif + + half4 gbuffer_0 = TEX2D(gbuffer0, uv); + half4 gbuffer_1 = TEX2D(gbuffer1, uv); + #if !defined(SKIN) + half4 gbuffer_2 = TEX2D(gbuffer2, uv); + #endif + + float3 N = normalize(gbuffer_decode_normal(gbuffer_1)); + half roughness = gbuffer_decode_roughness(gbuffer_1); + + float3 base_color = gbuffer_decode_base_color(gbuffer_0); + half metallic = gbuffer_decode_metallic_mask(gbuffer_0); + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + float3 specular_color = lerp(float3(0.04,0.04,0.04), base_color, metallic); + float3 diffuse_color = lerp(base_color, new_half3(0,0,0), metallic); + #if defined(D3D11) || defined(D3D12) + // If we are currently capturing a reflection probe, use specular F0 as diffuse color for metallics + diffuse_color = (specular_toggle == 1) ? diffuse_color : lerp(diffuse_color, specular_color, metallic); + #endif + + float3 V = normalize(camera_world._m30_m31_m32 - wp); + float3 acc_diff = 0; + float3 acc_spec = 0; + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness, attn, acc_diff, acc_spec); + + #if defined(SKIN) + o.specular = half4(acc_spec * specular_toggle, 0); + o.base = half4(acc_diff, 0); + #else + half density = gbuffer_decode_density(gbuffer_2); + float3 translucency = calculate_translucency(L, V, N, translucency_attn * lerp(lerp(0.5, 1.0, shadow_mask), shadow_mask, density), base_color, light_color, density); + o.base = half4(acc_diff + acc_spec * specular_toggle + translucency, 0); + #endif + return o; + } + #endif + """ + } + + sun_shadow_mask = { + includes = [ "common", "gbuffer_access", "shadow_bias", "shadow_map_filtering", "taa_offsets" ] + samplers = { + linear_depth = { sampler_states = "clamp_point" } + + input_texture0 = { sampler_states = "shadow_map" } + defined_BILLBOARD_SHADOW_CASTING = { + shadow_map_color = { sampler_states = "clamp_linear" } + } + + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + #if !defined(FILL) + out vec4 v_w; + out vec2 v_uv; + #endif + + void main() { + vec4 pos = in_pos * world_view_proj; + gl_Position = pos; + + #if !defined(FILL) + v_uv = in_uv; + v_w = encode_world_pos(pos); + #endif + } + """ + + fp_code = """ + out lowp vec4 out_shadow; + #if !defined(FILL) + uniform highp usampler2D linear_depth; + DECLARE_COMPARISON_SAMPLER_2D(input_texture0); + + in highp vec4 v_w; + in highp vec2 v_uv; + + CBUFFER_START(c0) + UNIFORM mat4 world_to_shadow_map; + UNIFORM vec2 input_texture0_size; + CBUFFER_END + #endif + + void main() { + #if defined(FILL) + out_shadow = vec4(1.0); + #else + highp float d = uintBitsToFloat(texture(linear_depth, v_uv).r); + + highp vec3 wp = decode_world_pos(v_w, d); + highp vec4 sm_pos = vec4(wp, 1.0) * world_to_shadow_map; + sm_pos.z = apply_sun_shadow_depth_comparison_bias(d, sm_pos.z); + lowp float shadow = shadow_intensity_2d(input_texture0, input_texture0_size, sm_pos.xy, sm_pos.z); + out_shadow = vec4(shadow, shadow, shadow, 0); + #endif + } + """ + + code=""" + DECLARE_SAMPLER_2D(linear_depth); + + DECLARE_COMPARISON_SAMPLER_2D(input_texture0); + #ifdef BILLBOARD_SHADOW_CASTING + DECLARE_SAMPLER_2D(shadow_map_color); + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if !defined(FILL) && !defined(NUKE_HI_STENCIL) + float2 uv : TEXCOORD0; + float4 w : TEXCOORD1; + #endif + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + + #if defined(FILL) + float shadow_fallback_intensity; // exports={ name="Shadow Intensity Fallback" type="scalar" value=1 min=0 max=1 step=0.003 } + #else + float4x4 world_to_shadow_map; + #endif + float2 input_texture0_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + #if !defined(FILL) + o.uv = input.uv; + // When using temporal antialiasing we try to cancel out the jitter + // that was introduced in the depth buffer. This is to minimize self occlusion + // that can arrise when performing a depth test beween the jittered depth buffer + // and a non jittered shadow map. + float4 tmp = o.position; + float4 view_space = tmp / tmp.w; + view_space.xy -= get_vs_halton_offset(frame_number); + tmp = view_space * tmp.w; + o.w = encode_world_pos(tmp); + #endif + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + #if defined(FILL) + half4 ps_main() : SV_TARGET0 { + return half4(1,1,1,1); + } + #else + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + float d = gbuffer_decode_depth(TEX2D(linear_depth, input.uv)); + float3 wp = decode_world_pos(input.w, d); + float4 sm_pos = mul(float4(wp, 1), world_to_shadow_map); + + #if defined(FILL_SHADOW) + sm_pos.z = apply_shadow_depth_comparison_bias(d, sm_pos.z, ssm_shadow_map_bias); + #else + sm_pos.z = apply_shadow_depth_comparison_bias(d, sm_pos.z, sun_shadow_map_bias); + #endif + half shadow = shadow_intensity_2d(input_texture0, input_texture0_size, sm_pos.xy, sm_pos.z); + #if defined(FILL_SHADOW) + if(sm_pos.x < 0.0 || sm_pos.x > 1.0 || + sm_pos.y < 0.0 || sm_pos.y > 1.0) { + shadow = 1.0; + } + #endif + return half4(shadow, shadow, shadow, 1); + } + #endif + """ + } + + sun_shadow_cutter = { + includes = [ "common", "gbuffer_access" ] + + vp_code=""" + layout(location = POSITION0) in vec4 in_pos; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + UNIFORM mat4 box_wtm; + UNIFORM vec3 box_scale; + UNIFORM vec3 box_translation; + CBUFFER_END + + void main() { + vec4 p = vec4(vec3(2, 1, 2) * box_scale * in_pos.xyz, in_pos.w); + gl_Position = p * box_wtm * world_view_proj; + } + """ + + fp_code=""" + void main() {} + """ + + code=""" + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + + float4x4 box_wtm; + float3 box_scale; + float3 box_translation; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = input.position; + p.xyz *= float3(2,1,2); + p.xyz = p.xyz * box_scale; // + box_translation; + o.position = mul(mul(p, box_wtm), world_view_proj); + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main() : SV_TARGET0 { + return half4(1,0,0,0.5); + } + """ + } + + linearize_depth = { + includes = [ "common", "gbuffer_access" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + } + + vp_code=""" + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv; + out vec2 v_uv; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + void main() { + v_uv = in_uv; + gl_Position = in_pos * world_view_proj; + } + """ + fp_code=""" + in vec2 v_uv; + out highp uint out_depth; + uniform highp sampler2D input_texture0; + + void main() { + highp float clip_depth = TEX2D(input_texture0, v_uv).r; + out_depth = floatBitsToUint(linearize_depth(clip_depth)); + } + """ + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + #if defined(GNM) + #pragma PSSL_target_output_format(default FMT_32_ABGR) + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float clip_depth = TEX2D(input_texture0, input.uv).r; + float d = linearize_depth(clip_depth); + return float4(d,0,0,0); + } + """ + } + + brdf_lut = { + includes = [ "common", "brdf" ] + + code = """ + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + float2 preintegrate_gf(float NV, float roughness) { + float3 V = float3(sqrt(1.0f - NV * NV), 0.0f, NV); + + float scale = 0.0f; + float bias = 0.0f; + + const uint samples = 1024; + for (uint i = 0; i < samples; ++i) { + float2 E = hammersley_sequence_2d(i, samples); + float3 H = importance_sample_ggx(E, roughness, float3(0,0,1)); + + float3 L = 2.0f * dot(V, H) * H - V; + + float NL = saturate(L.z); + float NH = saturate(H.z); + float VH = saturate(dot(V, H)); + + if (NL > 0.0f) { + float G = g_visibility_schlick_smith(roughness, NV, NL) * (4 * NL * NV); + float G_vis = G * VH / (NH * NV); + float F = pow(1 - VH, 5); + scale += (1 - F) * G_vis; + bias += F * G_vis; + } + } + + return float2(scale, bias) / samples; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float NV = input.uv.x; + float roughness = input.uv.y; + float2 lut = preintegrate_gf(NV, roughness); + return float4(lut, 0, 0); + } + """ + } + + // TODO: update using the new local light code + ao_source = { + includes = [ "common", "gbuffer_access", "brdf" ] + + samplers = { + defined_LAMBERTIAN_WEIGHT = { + gbuffer1 = { sampler_states = "clamp_point" } + } + linear_depth = { sampler_states = "clamp_point" } + } + + code=""" + #if defined(LAMBERTIAN_WEIGHT) + DECLARE_SAMPLER_2D(gbuffer1); + #endif + DECLARE_SAMPLER_2D(linear_depth); + + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 w : TEXCOORD1; + }; + + CBUFFER_START(c0) + #if defined(SPOT) + float4x4 world; + #endif + float4x4 world_view_proj; + CBUFFER_END + + CBUFFER_START(Light) + float3 light_position; + float3 light_color; + float3 light_falloff; + float3 light_proxy_scale; + #if defined(SPOT) + float2 light_spot_falloff; + #endif + #if defined(MIN_WEIGHT) + float min_weight; // exports = { name="Min Weight" type="scalar" value=0.0 min=0 max=1 step=0.001 } + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(float4(input.position.xyz * light_proxy_scale, 1), world_view_proj); + o.w = encode_world_pos(o.position, camera_unprojection); + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input + #if defined(GL2) + , float2 wpos : VPOS + #endif + ) : SV_TARGET0 + { + #if defined(GL2) + half2 uv = wpos.xy / back_buffer_size; + #else + half2 uv = input.position.xy / back_buffer_size; + #endif + // Sample textures + float d = gbuffer_decode_depth(TEX2D(linear_depth, uv)); + #if defined(LAMBERTIAN_WEIGHT) + half4 gbuffer_1 = TEX2D(gbuffer1, uv); + #endif + + float3 wp = decode_world_pos(input.w, d); + + float3 V = normalize(camera_world._m30_m31_m32 - wp); + float3 L = light_position - wp; + float len_L = length(L) + 0.00001f; + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + + #if defined(SPOT) + float spot_angle = 1.0 - dot(L, -world._m10_m11_m12); + attn *= (spot_angle > light_spot_falloff.x ? 1-saturate((spot_angle-light_spot_falloff.x)*light_spot_falloff.y) : 1); + #endif + + #if defined(LAMBERTIAN_WEIGHT) + float3 N = gbuffer_decode_normal(gbuffer_1); + #if !defined(HARD_CUT_OFF) + L *= 1.0/len_L; // normalize L + N = normalize(N); + #endif + float NL = dot(N, L); + + #if defined(HARD_CUT_OFF) + NL = (NL >= 0.0) ? 1.0 : 0.0; + #elif defined(MIN_WEIGHT) + NL = max(NL, min_weight); + #else + NL = saturate(NL); + #endif + attn *= NL; + #endif + + attn *= length(light_color); + + return half4(1.0-attn, 0, 0, 0); + } + """ + } + + global_indirect_specular_lighting_and_fog = { + includes = [ "common", "gbuffer_access", "brdf", "color_management", "post_processing_common" ] + samplers = { + gbuffer0 = { sampler_states = "clamp_point" } + gbuffer1 = { sampler_states = "clamp_point" } + gbuffer2 = { sampler_states = "clamp_point" } + defined_SSAO_ENABLED = { + ldr3_div2 = { sampler_states = "clamp_linear" } + } + linear_depth = { sampler_states = "clamp_point" } + global_specular_map = { sampler_states = "clamp_linear"} + brdf_lut = { sampler_states = "clamp_linear"} + hdr1 = { sampler_states = "clamp_point" } + defined_SSR_ENABLED = { + hdr0_div2_mip6 = { sampler_states = "clamp_linear" } + ldr4_div2 = { sampler_states = "clamp_point" } + } + water_mask = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(gbuffer0); + DECLARE_SAMPLER_2D(gbuffer1); + DECLARE_SAMPLER_2D(gbuffer2); + #if defined(SSAO_ENABLED) + DECLARE_SAMPLER_2D(ldr3_div2); + #endif + DECLARE_SAMPLER_2D(linear_depth); + DECLARE_SAMPLER_2D(hdr1); + DECLARE_SAMPLER_CUBE(global_specular_map); + DECLARE_SAMPLER_2D(brdf_lut); + #ifdef SSR_ENABLED + DECLARE_SAMPLER_2D(hdr0_div2_mip6); + DECLARE_SAMPLER_2D(ldr4_div2); + #endif + DECLARE_SAMPLER_2D(water_mask); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float4 w : TEXCOORD1; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float3 ambient_tint; + float ambient_diffuse_fade_type; + float3 ambient_diffuse_fade; // min multiplier, height offset, falloff + #if defined(FOG_ENABLED) + float2 fog_depth_range; + float2 height_fog_offset_falloff; + float fog_type; + float3 fog_color; + float3 fog_sun_blend; + float3 sun_direction; + float3 sun_color; + #if defined(CUSTOM_FOG_BLEND) + float3 custom_fog_blend_direction; + float3 custom_fog_blend_color; + float3 custom_fog_blend; + #endif + #if defined(SECONDARY_SUN_BLEND) + float3 secondary_sun_direction; + float3 secondary_sun_color; + float3 secondary_sun_blend; + #endif + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + o.w = encode_world_pos(o.position); + o.position.z = o.position.w; + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + float4 gbuffer_0 = TEX2D(gbuffer0, input.uv); + float4 gbuffer_1 = TEX2D(gbuffer1, input.uv); + float4 gbuffer_2 = TEX2D(gbuffer2, input.uv); + float transparency_mask = TEX2D(water_mask, input.uv).r; // this can be used for other transparent materials aswell. + + float ao = gbuffer_decode_ambient_occlusion(gbuffer_2); + #if defined(SSAO_ENABLED) + float ssao = TEX2D(ldr3_div2, input.uv).r; + ao = min(ao, ssao); + #endif + + float depth = gbuffer_decode_depth(TEX2D(linear_depth, input.uv)); + + float3 N = normalize(gbuffer_decode_normal(gbuffer_1)); + float roughness = gbuffer_decode_roughness(gbuffer_1); + + float3 base_color = gbuffer_decode_base_color(gbuffer_0); + float metallic = gbuffer_decode_metallic_mask(gbuffer_0); + + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + float3 specular_color = lerp(float3(0.04,0.04,0.04), base_color, metallic); + + #if defined(D3D11) + float specular_toggle = 1-capture_cubemap; + #else + float specular_toggle = 1.f; + #endif + + float3 wp = decode_world_pos(input.w, depth); + float3 V = normalize(camera_world._m30_m31_m32 - wp); + + float2 scale_bias = TEX2D(brdf_lut, float2(saturate(dot(N, V)), roughness)).xy; + + float mipmap_index = roughness * 7; + + // Read all the reflection data + float4 local_reflections_data = TEX2D(hdr1, input.uv); + float3 global_probe_data = rgbm_decode(TEXCUBELOD(global_specular_map, reflect(-V, N), mipmap_index)); + + // Prepare the reflection weight data + float local_probe_weight = local_reflections_data.w; + + #if defined(SSR_ENABLED) + float ssr_mip_level = TEX2DLOD(ldr4_div2, input.uv, 0).r * SSR_MIPMAP_LEVELS; + float4 ssr_data = TEX2DLOD(hdr0_div2_mip6, input.uv, ssr_mip_level); + ssr_data.rgb = inv_safe_range_tone_map(ssr_data.rgb); + + float ssr_weight = ssr_data.a; + float3 ssr_influence = ssr_data.rgb * ssr_weight; + + // Distribute the reflection weight (ssr -> local probes -> global probe) + local_probe_weight = min(local_probe_weight, 1.0 - ssr_weight); + float global_probe_weight = 1.0 - (ssr_weight + local_probe_weight); + #else + float3 ssr_influence = float3(0.0, 0.0, 0.0); + + // Distribute the reflection weight (local probes -> global probe) + float global_probe_weight = 1.0 - local_probe_weight; + #endif + + // Final reflection influence terms + float3 local_probe_influence = local_reflections_data.rgb * local_probe_weight; + float3 global_probe_influence = global_probe_data.rgb * global_probe_weight; + + #if defined(D3D11) + ambient_tint = (capture_cubemap == 1) ? 1.0 : ambient_tint; + #endif + + float3 indirect_light_tint = ambient_tint; + float fade_type = ambient_diffuse_fade_type; + float3 fade_params = ambient_diffuse_fade; + [branch] + if(fade_type != 0.0) { + if(fade_type == 1.0) { + indirect_light_tint *= max(fade_params.x, 1.0 - exp((wp.z - fade_params.y)/fade_params.z)); + } else if(fade_type == 2.0) { + indirect_light_tint *= max(fade_params.x, 1.0 - exp((fade_params.y - wp.z)/fade_params.z)); + } else { + indirect_light_tint *= max(fade_params.x, 1.0 - exp(-abs(fade_params.y - wp.z)/fade_params.z)); + } + } + float3 specular_indirect_lighting = (ssr_influence + local_probe_influence + global_probe_influence) * (specular_color * scale_bias.x + scale_bias.y) * indirect_light_tint * ao * transparency_mask; + + // If we are currently capturing a reflection probe, mask out all IBL based relfections to avoid feedback loops. + specular_indirect_lighting *= specular_toggle; + + #if defined(FOG_ENABLED) + float start = fog_depth_range.x; + float end = fog_depth_range.y; + + float inv_fog_range = 1.0 / (end-start); + float fog_strength = (depth - start) * inv_fog_range; + + float sun_blend = 0.0; + float3 color = fog_color; + + #if defined(SECONDARY_SUN_BLEND) + sun_blend = secondary_sun_blend.x * pow(saturate(dot(V, secondary_sun_direction)), secondary_sun_blend.y); + color = lerp(color, secondary_sun_blend.z * secondary_sun_color, sun_blend); + #endif + + #if defined(CUSTOM_FOG_BLEND) + sun_blend = custom_fog_blend.x * pow(saturate(dot(V, custom_fog_blend_direction)), custom_fog_blend.y); + color = lerp(color, custom_fog_blend.z * custom_fog_blend_color, sun_blend); + #endif + + #define DEFAULT_EXP_HEIGHT 0 + #define EXP_HEIGHT 1 + #define EXP 2 + #define LINEAR 3 + + // TODO: branch in render config + float fog_blend = 0.0; + [branch] + if (fog_type == DEFAULT_EXP_HEIGHT) { + fog_blend = saturate(exp(-camera_world._m32 * inv_fog_range) * (1.0 - exp(-fog_strength * -V.z)) / -V.z); + } else if (fog_type == EXP_HEIGHT) { + fog_blend = saturate(exp((height_fog_offset_falloff.x - wp.z)/height_fog_offset_falloff.y) * (1.0 - exp(-fog_strength))); + } else if (fog_type == EXP) { + fog_blend = 1.0 - saturate(exp(-fog_strength)); + } else { // linear + fog_blend = saturate(fog_strength); + } + fog_blend = depth > start ? fog_blend : 0.0; + + sun_blend = fog_sun_blend.x * pow(saturate(dot(V, sun_direction)), fog_sun_blend.y); + color = lerp(color, fog_sun_blend.z * sun_color, sun_blend); + + return half4(lerp(specular_indirect_lighting, color, fog_blend), fog_blend); + #else + return half4(specular_indirect_lighting, 0.0); + #endif + } + """ + } +} + + +shaders = { + global_lighting = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="global_lighting" render_states="global_lighting" } + { defined="SKIN_MATERIAL_ENABLED" + pass = [ + { hlsl_shader="global_lighting" defines=["SKIN"] render_states="global_lighting" } + ] + } + ] + } + } + + compile = { + default = [ + { if: "on_renderer(D3D11, D3D12, GNM) && render_setting(ao_enabled, skin_material_enabled)" defines=["SSAO_ENABLED" "SKIN_MATERIAL_ENABLED"] } + { if: "on_renderer(D3D11, D3D12, GNM) && render_setting(skin_material_enabled)" defines=["SKIN_MATERIAL_ENABLED"] } + { if: "on_renderer(D3D11, D3D12, GNM) && render_setting(ao_enabled)" defines=["SSAO_ENABLED"] } + { defines=[""] } + ] + } + } + + sun_shadow_mask = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="sun_shadow_mask" render_states="sun_shadow_mask" } + ] + } + } + + compile = { + default = [ + { if: "on_renderer(D3D11, D3D12, GNM)" render_settings={ sun_shadow_map_filter_quality="high" } defines=["PCF_5X5"] } + { defines=[] } + ] + } + } + + sun_shadow_cutter = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="sun_shadow_cutter" render_states="sun_shadow_cutter" } + ] + } + } + + compile = { + default = [ + { defines="" } + ] + } + } + + brdf_lut = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="brdf_lut" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines="" } + ] + } + } + + light_source = { + editor_advanced_mode = false + + editor_options = [ + { + name="Light Type" + options = [ + { name="Omni" define="OMNI" } + { name="Spot" define="SPOT" } + ] + } + { + name="Cookie Projection" + options = [ + { name="Enable Cookie Projection" define="COOKIE_PROJECTION" condition="SPOT" } + { name="Clamp Texture Sampler" define="COOKIE_CLAMP_SAMPLER" condition="SPOT" } + { name="UV Offset" define="COOKIE_UV_OFFSET" condition="SPOT" } + { name="UV Scale" define="COOKIE_UV_SCALE" condition="SPOT" } + { name="UV Offset Animation" define="COOKIE_UV_OFFSET_ANIM" condition="SPOT" } + { name="UV Rotation" define="COOKIE_UV_ROTATION_ANIM" condition="SPOT" } + ] + } + { + name="Properties" + options = [ + { name="Cast Shadows" define="SHADOW_MAPPING" } + ] + } + ] + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="light_source" render_states="light" } + { defined="SKIN_MATERIAL_ENABLED" + pass = [ + { hlsl_shader="light_source" defines=["SKIN"] render_states="light" } + ] + } + ] + } + } + + compile = { + default = [ + // TODO: this syntax is broken! + //{ if: "on_renderer(D3D11, GNM)" render_settings={ local_lights_shadow_map_filter_quality="high", skin_material_enabled=true } defines=["PCF_5X5" "SKIN_MATERIAL_ENABLED"] } + //{ if: "on_renderer(D3D11, GNM) && render_setting(skin_material_enabled)" defines=["SKIN_MATERIAL_ENABLED"] } + //{ if: "on_renderer(D3D11, GNM)" render_settings={ local_lights_shadow_map_filter_quality="high" } defines=["PCF_5X5"] } + { if: "on_renderer(D3D11, D3D12, GNM) && render_setting(skin_material_enabled)" defines=["SKIN_MATERIAL_ENABLED"] } + { defines=[""] } + ] + } + } + + linearize_depth = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="linearize_depth" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines = [] } + ] + } + } + + // TODO: Make spot light work too + ao_source = { + editor_options = [ + { + name="Other" + options = [ + { name="Lambertian Weight" define="LAMBERTIAN_WEIGHT" tool_tip="Uses the normal to remove ao from back-facing triangles." } + { name=" - Min Weight" define="MIN_WEIGHT" condition="LAMBERTIAN_WEIGHT && !HARD_CUT_OFF" } + { name=" - Hard Cut-Off" define="HARD_CUT_OFF" condition="LAMBERTIAN_WEIGHT && !MIN_WEIGHT" } + ] + } + ] + + contexts = { + default = { + passes = [ + { layer="ao_sources" hlsl_shader="ao_source" defines="OMNI" render_states="ao_source" } + ] + } + } + + compile = { + default = [ + { defines="" platforms = "D3D11 D3D12 GL2 GNM"} + ] + } + } + + global_indirect_specular_lighting_and_fog = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="global_indirect_specular_lighting_and_fog" render_states="global_indirect_specular_lighting_and_fog" } + ] + } + } + + compile = { + default = [ + { if: "on_renderer(D3D11, D3D12, GNM) && render_setting(ao_enabled)" defines=["SSAO_ENABLED"] } + { defines=[""] } + ] + } + } +} + +static_compile= [ + { shader="global_lighting" } + { shader="global_lighting" defines=["CLUSTERED_SHADING"] } + { shader="global_lighting" defines=["SUN"] } + { shader="global_lighting" defines=["SUN" "CLUSTERED_SHADING"] } + { shader="global_lighting" defines=["SSR_ENABLED"] } + { shader="global_lighting" defines=["SSR_ENABLED" "CLUSTERED_SHADING"] } + { shader="global_lighting" defines=["SUN" "SSR_ENABLED"] } + { shader="global_lighting" defines=["SUN" "SSR_ENABLED" "CLUSTERED_SHADING"] } + { shader="sun_shadow_mask" } + { shader="sun_shadow_mask" defines=["FILL"] } + { shader="sun_shadow_mask" defines=["FILL_SHADOW"] } + { shader="sun_shadow_cutter" } + { shader="linearize_depth" } + { if: "!on_renderer(GL)" shader="brdf_lut" } + + { shader="light_source" defines=["OMNI"] } + { shader="light_source" defines=["OMNI" "SHADOW_MAPPING"] } + { shader="light_source" defines=["SPOT"] } + { shader="light_source" defines=["SPOT" "SHADOW_MAPPING"] } + { shader="light_source" defines=["BOX"] } + { shader="light_source" defines=["BOX" "SHADOW_MAPPING"] } + + // Fatshark + { shader="global_lighting" defines=["AMBIENT_FADE_UP"] } + { shader="global_lighting" defines=["AMBIENT_FADE_DOWN"] } + { shader="global_lighting" defines=["AMBIENT_FADE_MIRROR"] } + { shader="global_lighting" defines=["SSR_ENABLED" "AMBIENT_FADE_UP"] } + { shader="global_lighting" defines=["SSR_ENABLED" "AMBIENT_FADE_DOWN"] } + { shader="global_lighting" defines=["SSR_ENABLED" "AMBIENT_FADE_MIRROR"] } + { shader="global_lighting" defines=["SUN" "AMBIENT_FADE_UP"] } + { shader="global_lighting" defines=["SUN" "AMBIENT_FADE_DOWN"] } + { shader="global_lighting" defines=["SUN" "AMBIENT_FADE_MIRROR"] } + { shader="global_lighting" defines=["SUN" "SSR_ENABLED" "AMBIENT_FADE_UP"] } + { shader="global_lighting" defines=["SUN" "SSR_ENABLED" "AMBIENT_FADE_DOWN"] } + { shader="global_lighting" defines=["SUN" "SSR_ENABLED" "AMBIENT_FADE_MIRROR"] } + { shader="global_lighting" defines=["CLUSTERED_SHADING" "AMBIENT_FADE_UP"] } + { shader="global_lighting" defines=["CLUSTERED_SHADING" "AMBIENT_FADE_DOWN"] } + { shader="global_lighting" defines=["CLUSTERED_SHADING" "AMBIENT_FADE_MIRROR"] } + { shader="global_lighting" defines=["SUN" "CLUSTERED_SHADING" "AMBIENT_FADE_UP"] } + { shader="global_lighting" defines=["SUN" "CLUSTERED_SHADING" "AMBIENT_FADE_DOWN"] } + { shader="global_lighting" defines=["SUN" "CLUSTERED_SHADING" "AMBIENT_FADE_MIRROR"] } + { shader="global_lighting" defines=["SSR_ENABLED" "CLUSTERED_SHADING" "AMBIENT_FADE_UP"] } + { shader="global_lighting" defines=["SSR_ENABLED" "CLUSTERED_SHADING" "AMBIENT_FADE_DOWN"] } + { shader="global_lighting" defines=["SSR_ENABLED" "CLUSTERED_SHADING" "AMBIENT_FADE_MIRROR"] } + { shader="global_lighting" defines=["SUN" "SSR_ENABLED" "CLUSTERED_SHADING" "AMBIENT_FADE_UP"] } + { shader="global_lighting" defines=["SUN" "SSR_ENABLED" "CLUSTERED_SHADING" "AMBIENT_FADE_DOWN"] } + { shader="global_lighting" defines=["SUN" "SSR_ENABLED" "CLUSTERED_SHADING" "AMBIENT_FADE_MIRROR"] } +] diff --git a/vmf_source/core/stingray_renderer/shader_libraries/lighting_common.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/lighting_common.shader_source new file mode 100644 index 0000000..4069ddc --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/lighting_common.shader_source @@ -0,0 +1,669 @@ +hlsl_shaders = { + brdf = { + vp_code = { ref = "code" } + fp_code = { ref = "code" } + code=""" + #if defined(RENDERER_GL) + #define splat(c) vec3(c) + #else + #define splat(c) c + #endif + + float3 diffuse_lambert(float3 diffuse_color) { + return diffuse_color / splat(PI); + } + + // Microfacet distribution function + // Trowbridge-Reitz (GGX) NDF + float d_ggx(float roughness, float NH) { + float alpha = roughness * roughness; + float alpha2 = alpha * alpha; + float d = (NH * NH) * (alpha2 - 1.f) + 1.f; + const float eps = 0.0000001f; + return alpha2 / ((d * d * PI) + eps); + } + + float d_ggx_anisotropic(float roughness, float anisotropy, float TH, float BH, float NH) { + const float eps = 0.0000001f; + float alpha = max(roughness * roughness, eps); + float t_alpha = alpha; + float b_alpha = lerp(alpha, eps, anisotropy); + float dt = TH/t_alpha; + float db = BH/b_alpha; + float d = dt*dt + db*db + NH*NH; + + return 1.0 / ((PI * t_alpha * b_alpha * d * d) + eps); + } + + // Visibility term + // Schlick approximation of Smith for GGX + float g_visibility_schlick_smith(float roughness, float NV, float NL) { + float a = max(roughness * roughness, 0.002); + float k = a * 0.5; + float G_L = NL * (1.0 - k) + k; + float G_V = NV * (1.0 - k) + k; + float G = 0.25 / ( G_L * G_V ); + return G; + } + + // Schlick Fresnel approximation + // Uses spherical gaussians approximation + float3 f_schlick(float3 F0, float VH) { + float F = exp2((-5.554730f * VH - 6.983160f) * VH); + return F0 + (splat(1.f) - F0) * F; + } + + void bsdf(float3 L, float3 V, float3 N, float3 light_color, float3 diffuse_color, float3 specular_color, half roughness, half attenuation, inout float3 acc_diff, inout float3 acc_spec) { + float3 H = new_half3_xyz(normalize(L + V)); + float NL = saturate(dot(N, L)); + float NV = saturate(dot(N, V)); + float NH = saturate(dot(N, H)); + float VH = saturate(dot(V, H)); + + // Distribution term + float D = d_ggx(roughness, NH); + + // Visibility term + float G_VIS = g_visibility_schlick_smith(roughness, NV, NL); + + // Fresnel term + float3 F = f_schlick(specular_color, VH); + + float3 atten_light_color = light_color * NL * attenuation; + + acc_diff += diffuse_lambert(diffuse_color) * atten_light_color; + acc_spec += D * G_VIS * F * atten_light_color; + } + + void bsdf(float3 L, float3 V, float3 T, float3 B, float3 N, float3 light_color, float3 diffuse_color, float3 specular_color, half roughness, float anisotropy, half attenuation, inout float3 acc_diff, inout float3 acc_spec) { + float3 H = new_half3_xyz(normalize(L + V)); + float NL = saturate(dot(N, L)); + float NV = saturate(dot(N, V)); + float NH = saturate(dot(N, H)); + float VH = saturate(dot(V, H)); + float TH = dot(T, H); + float BH = dot(B, H); + + // Distribution term + float D = d_ggx_anisotropic(roughness, anisotropy, TH, BH, NH); + + // Visibility term + float G_VIS = g_visibility_schlick_smith(roughness, NV, NL); + + // Fresnel term + float3 F = f_schlick(specular_color, VH); + + float3 atten_light_color = light_color * NL * attenuation; + + acc_diff += diffuse_lambert(diffuse_color) * atten_light_color; + acc_spec += D * G_VIS * F * atten_light_color; + } + + // Subsurface approximation + // Ref: C. Barre-Brisebois, M. Bouchard, "Approximating translucency for a fast, cheap and convincing + // subsurface scattering look", GDC 2011 + // + #define TRANSLUCENCY_AMBIENT 0.35 + #define TRANSLUCENCY_DISTORTION 0.016 + #define TRANSLUCENCY_POWER 11.5 + #define TRANSLUCENCY_LIGHT_SCALE 0.3 + #define TRANSLUCENCY_DIRECTIONAL_SCALE 0.1 + + float3 calculate_translucency(float3 L, float3 V, float3 N, float attn, float3 base_color, float3 light_color, half density, half translucency_ambient, half translucency_distortion, half translucency_power, half translucency_light_scale) { + float3 light_vec = L + N * translucency_distortion; + half v_inv_l_dot = pow(saturate(dot(V, -light_vec)), translucency_power) * translucency_light_scale; + return attn * (v_inv_l_dot + translucency_ambient) * saturate(1.0 - density) * light_color * base_color; + } + + float3 calculate_translucency(float3 L, float3 V, float3 N, float attn, float3 base_color, float3 light_color, half density) { + return calculate_translucency(L, V, N, attn, base_color, light_color, density, TRANSLUCENCY_AMBIENT, TRANSLUCENCY_DISTORTION, TRANSLUCENCY_POWER, TRANSLUCENCY_LIGHT_SCALE); + } + + half light_attenuation(half d, half near, half inv_far_minus_near) { + half dist = d-near; + half a = dist*inv_far_minus_near; + half a2 = a*a; + half num = new_half(saturate(1.0 - a2*a2)); + return d < near ? 1.0 : (num*num) / (dist*dist + 1.0); + } + + float spot_angle_attenuation(float angle, float scale, float bias) { + // scale=1.f / max(eps, (cos(start_angle*0.5) - cos(stop_angle*0.5))) + // bias = -cos(stop_angle*0.5) * scale; + + float a = saturate(angle * scale + bias); + // squared for smoother falloff. + return a*a; + } + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float2 hammersley_sequence_2d(uint i, uint n) { + return float2((float)i / n, reversebits(i) * 2.3283064365386963e-10); + } + + float3 importance_sample_ggx(float2 E, float roughness, float3 N) { + float a = roughness*roughness; + float a2 = a*a; + + float phi = 2 * PI * E.x; + float cos_phi = cos(phi); + float sin_phi = sin(phi); + float cos_theta = sqrt((1 - E.y) / ((a2 - 1) * E.y + 1)); + float sin_theta = sqrt(1 - cos_theta * cos_theta); + + float3 H = float3(sin_theta * cos_phi, sin_theta * sin_phi, cos_theta); + + float3 up = abs(N.z) < 0.999 ? float3(0.0,0.0,1.0) : float3(1.0,0.0,0.0); + float3 tx = normalize(cross(up, N)); + float3 ty = cross(N, tx); + + return tx * H.x + ty * H.y + N * H.z; + } + #endif + """ + } + + lighting = { + vp_code = { ref = "code" } + fp_code = { ref = "code" } + code=""" + #if defined(CALCULATE_LIGHTING) + CBUFFER_START(lighting_data) + UNIFORM float3 ambient_tint; + + UNIFORM float4x4 shadow_rotation; + UNIFORM float3 shadow_scale_slice0; + UNIFORM float3 shadow_scale_slice1; + UNIFORM float3 shadow_scale_slice2; + UNIFORM float3 shadow_scale_slice3; + + UNIFORM float3 shadow_bias_slice0; + UNIFORM float3 shadow_bias_slice1; + UNIFORM float3 shadow_bias_slice2; + UNIFORM float3 shadow_bias_slice3; + + UNIFORM float3 vp_min_slice0; + UNIFORM float3 vp_min_slice1; + UNIFORM float3 vp_min_slice2; + UNIFORM float3 vp_min_slice3; + + UNIFORM float3 vp_max_slice0; + UNIFORM float3 vp_max_slice1; + UNIFORM float3 vp_max_slice2; + UNIFORM float3 vp_max_slice3; + + UNIFORM float sun_shadows_enabled; + UNIFORM float sun_enabled; + + UNIFORM float ssm_enabled; + UNIFORM float4x4 ssm_shadow_rotation; + + UNIFORM float ambient_diffuse_fade_type; + UNIFORM float3 ambient_diffuse_fade; // min multiplier, height offset, falloff + CBUFFER_END + + float sample_shadow_map(ComparisonSampler2D shadow_map, float3 sm_pos, float depth, float3 shadow_map_bias) + { + sm_pos.z = apply_shadow_depth_comparison_bias(depth, sm_pos.z, shadow_map_bias); + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float2 sm_resolution; + // TODO: get rid of GetDimension, since this a cost on GCN + shadow_map.tex.GetDimensions(sm_resolution.x, sm_resolution.y); + #else + float2 sm_resolution = float2(textureSize(shadow_map, 0)); + #endif + return shadow_intensity_2d(shadow_map, sm_resolution, sm_pos.xy, sm_pos.z); + } + + float calculate_static_shadow_intensity(ComparisonSampler2D static_sun_shadow_map, const float3 world_pos, float depth) + { + float3 sm_pos = mul(float4(world_pos, 1.0), ssm_shadow_rotation); + return sample_shadow_map(static_sun_shadow_map, sm_pos, depth, ssm_shadow_map_bias); + } + + float calculate_shadow_intensity(ComparisonSampler2D sun_shadow_map, ComparisonSampler2D static_sun_shadow_map, const float3 world_pos, float depth) + { + float3 wp_shadow_space = mul(world_pos, to_mat3(shadow_rotation)); + float3 sm0_pos = wp_shadow_space * shadow_scale_slice0 + shadow_bias_slice0; + float3 sm1_pos = wp_shadow_space * shadow_scale_slice1 + shadow_bias_slice1; + float3 sm2_pos = wp_shadow_space * shadow_scale_slice2 + shadow_bias_slice2; + float3 sm3_pos = wp_shadow_space * shadow_scale_slice3 + shadow_bias_slice3; + + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float3 sm0_test_pos = sm0_pos; + float3 sm1_test_pos = sm1_pos; + float3 sm2_test_pos = sm2_pos; + float3 sm3_test_pos = sm3_pos; + #else + // Need to flip z because of upside down rendering. + float3 sm0_test_pos = float3(sm0_pos.x, sm0_pos.y, 1.0f - sm0_pos.z); + float3 sm1_test_pos = float3(sm1_pos.x, sm1_pos.y, 1.0f - sm1_pos.z); + float3 sm2_test_pos = float3(sm2_pos.x, sm2_pos.y, 1.0f - sm2_pos.z); + float3 sm3_test_pos = float3(sm3_pos.x, sm3_pos.y, 1.0f - sm3_pos.z); + #endif + if (dot(float3(lessThan(sm0_test_pos, vp_min_slice0)) + float3(greaterThan(sm0_test_pos, vp_max_slice0)), float3(1.0f, 1.0f, 1.0f)) == 0.0f) + return sample_shadow_map(sun_shadow_map, sm0_pos.xzy, depth, sun_shadow_map_bias); + if (dot(float3(lessThan(sm1_test_pos, vp_min_slice1)) + float3(greaterThan(sm1_test_pos, vp_max_slice1)), float3(1.0f, 1.0f, 1.0f)) == 0.0f) + return sample_shadow_map(sun_shadow_map, sm1_pos.xzy, depth, sun_shadow_map_bias); + if (dot(float3(lessThan(sm2_test_pos, vp_min_slice2)) + float3(greaterThan(sm2_test_pos, vp_max_slice2)), float3(1.0f, 1.0f, 1.0f)) == 0.0f) + return sample_shadow_map(sun_shadow_map, sm2_pos.xzy, depth, sun_shadow_map_bias); + if (dot(float3(lessThan(sm3_test_pos, vp_min_slice3)) + float3(greaterThan(sm3_test_pos, vp_max_slice3)), float3(1.0f, 1.0f, 1.0f)) == 0.0f) + return sample_shadow_map(sun_shadow_map, sm3_pos.xzy, depth, sun_shadow_map_bias); + + if (ssm_enabled) { + return calculate_static_shadow_intensity(static_sun_shadow_map, world_pos, depth); + } + + return 1.0f; + } + + void calculate_lighting(const float3 world_pos, float depth, ComparisonSampler2D shadow_map, ComparisonSampler2D static_sun_shadow_map, Sampler2D brdf_lut, SamplerCube global_specular_map, const float3 N, const float3 V, const float roughness, const float3 ambient, const float3 diffuse_color, const float3 specular_color, const float density, const float3 base_color, inout float3 acc_diff, inout float3 acc_spec, inout float3 translucency) + { + float3 L = normalize(-sun_direction); + float2 scale_bias = TEX2D(brdf_lut, float2(saturate(dot(N, V)), roughness)).xy; + + #if defined(D3D11) + const float specular_toggle = 1-capture_cubemap; + // Remove ambient tint from cube map baking, since will have an exponential effect on the result. + ambient_tint = (specular_toggle == 1) ? ambient_tint : 1.0; + #else + const float specular_toggle = 1.0; + #endif + + // TODO: make a branchless solution? Do we use this? + float3 indirect_light_tint = ambient_tint; + float fade_type = ambient_diffuse_fade_type; + float3 fade_params = ambient_diffuse_fade; + [branch] + if(fade_type != 0.0) { + if(fade_type == 1.0) { + indirect_light_tint *= max(fade_params.x, 1.0 - exp((world_pos.z - fade_params.y)/fade_params.z)); + } else if(fade_type == 2.0) { + indirect_light_tint *= max(fade_params.x, 1.0 - exp((fade_params.y - world_pos.z)/fade_params.z)); + } else { + indirect_light_tint *= max(fade_params.x, 1.0 - exp(-abs(fade_params.y - world_pos.z)/fade_params.z)); + } + } + + float mipmap_index = roughness * 7.0; + acc_diff += ambient * indirect_light_tint * diffuse_color; + acc_spec += rgbm_decode(TEXCUBELOD(global_specular_map, reflect(-V, N), mipmap_index)) * (specular_color * scale_bias.x + scale_bias.y) * indirect_light_tint * specular_toggle; + + if (sun_enabled == 1.0) { + float shadow = 1.0; + if(sun_shadows_enabled && sun_shadows) { + shadow = saturate(calculate_shadow_intensity(shadow_map, static_sun_shadow_map, world_pos, depth)); + } else if(ssm_enabled) { + shadow = saturate(calculate_static_shadow_intensity(static_sun_shadow_map, world_pos, depth)); + } + bsdf(L, V, N, sun_color, diffuse_color, specular_color, roughness, shadow, acc_diff, acc_spec); + translucency = calculate_translucency(L, V, N, lerp(lerp(0.5, 1.0, shadow), saturate(shadow * TRANSLUCENCY_DIRECTIONAL_SCALE), density), base_color, sun_color, density); + } + } + #endif + """ + } + + clustered_shading = { + vp_code = { ref = "code" } + fp_code = { ref = "code" } + code = """ + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + #define USE_RAW_BUFFER_FOR_LIGHT_INDEX_DATA + #endif + + #if defined(USE_RAW_BUFFER_FOR_LIGHT_INDEX_DATA) + #define CLUSTER_DATA_TYPE Buffer + #define LIGHT_INDEX_DATA_TYPE Buffer + #define LIGHT_DATA_TYPE Buffer + #define LIGHT_SHADOW_MATRICES_TYPE Buffer + + #define DECLARE_CLUSTER_DATA(name) CLUSTER_DATA_TYPE name + #define DECLARE_LIGHT_INDEX_DATA(name) LIGHT_INDEX_DATA_TYPE name + #define DECLARE_LIGHT_DATA(name) LIGHT_DATA_TYPE name + #define DECLARE_LIGHT_DATA(name) LIGHT_DATA_TYPE name + #define DECLARE_LIGHT_SHADOW_MATRICES(name) LIGHT_SHADOW_MATRICES_TYPE name + #else + #define CLUSTER_DATA_TYPE highp usampler2D + #define LIGHT_INDEX_DATA_TYPE highp usampler2D + #define LIGHT_DATA_TYPE highp sampler2D + #define LIGHT_SHADOW_MATRICES_TYPE highp sampler2D + + #define DECLARE_CLUSTER_DATA(name) uniform CLUSTER_DATA_TYPE name + #define DECLARE_LIGHT_INDEX_DATA(name) uniform LIGHT_INDEX_DATA_TYPE name + #define DECLARE_LIGHT_DATA(name) uniform LIGHT_DATA_TYPE name + #define DECLARE_LIGHT_SHADOW_MATRICES(name) uniform LIGHT_SHADOW_MATRICES_TYPE name + #endif + + // Don't know why, but using texelFetch instead of textureLod is slower, but this define is here in case we want to start using texelFetch. + //#define USE_TEXEL_FETCH + + CBUFFER_START(clustered_shading_data) + UNIFORM float4 cs_cluster_size_in_pixels; + UNIFORM float4 cs_cluster_sizes; + UNIFORM float4 cs_cluster_data_size; + UNIFORM float4 cs_light_index_data_size; + UNIFORM float4 cs_light_data_size; + UNIFORM float4 cs_light_shadow_matrices_size; + UNIFORM float2 cs_cluster_max_depth_inv_max_depth; + UNIFORM float4 cs_shadow_atlas_size; + CBUFFER_END + + void calculate_sample_uv(uint index, float2 inv_tex_size, out float2 uv) + { + float normalized_uv = float(index) * inv_tex_size.x; + uv.x = frac(normalized_uv); + uv.y = normalized_uv * inv_tex_size.y; + } + + void calculate_sample_position(uint index, uint2 tex_size, out uint2 position) + { + position.x = index % tex_size.x; + position.y = index / tex_size.x; + } + + void sample_cluster_data(CLUSTER_DATA_TYPE cluster_data, float2 screen_pos, float depth, out uint2 light_data) + { + float inv_depth_per_cluster = cs_cluster_size_in_pixels.z; + float layer = (depth - camera_near_far.x) * inv_depth_per_cluster; + int3 pos_3d = int3(screen_pos * cs_cluster_size_in_pixels.xy, layer); + int pos_1d = pos_3d.z * int(cs_cluster_sizes.y) + pos_3d.y * int(cs_cluster_sizes.x) + pos_3d.x; + #if defined(USE_RAW_BUFFER_FOR_LIGHT_INDEX_DATA) + light_data = cluster_data.Load(pos_1d).xy; + #else + #if defined(USE_TEXEL_FETCH) + uint2 p; + calculate_sample_position(uint(pos_1d), uint2(cs_cluster_data_size.xy), p); + light_data = texelFetch(cluster_data, int2(p), 0).xy; + #else + float2 uv; + calculate_sample_uv(uint(pos_1d), cs_cluster_data_size.zw, uv); + light_data = textureLod(cluster_data, uv, 0.0).xy; + #endif + #endif + } + + void sample_light_index_data(LIGHT_INDEX_DATA_TYPE light_index_data, uint light_index, out uint index) + { + #if defined(USE_RAW_BUFFER_FOR_LIGHT_INDEX_DATA) + index = light_index_data.Load(light_index).r; + #else + #if defined(USE_TEXEL_FETCH) + uint2 p; + calculate_sample_position(light_index, uint2(cs_light_index_data_size.xy), p); + index = texelFetch(light_index_data, int2(p), 0).x; + #else + float2 uv; + calculate_sample_uv(light_index, cs_light_index_data_size.zw, uv); + index = textureLod(light_index_data, uv, 0.0).x; + #endif + #endif + } + + void sample_light_data(LIGHT_DATA_TYPE light_data, uint index, out float3 light_position, out float3 light_color, out float3 light_falloff, out float3 spot_light_falloff, out float3 spot_dir, out float sm_index0, out float sm_index1, out float sm_index2, out float sm_index3, out float sm_index4, out float sm_index5, out float shadow_intensity) + { + #if defined(USE_RAW_BUFFER_FOR_LIGHT_INDEX_DATA) + float4 light_data_0 = light_data.Load(index); + float4 light_data_1 = light_data.Load(index + 1); + float4 light_data_2 = light_data.Load(index + 2); + float4 light_data_3 = light_data.Load(index + 3); + float4 light_data_4 = light_data.Load(index + 4); + #else + #if defined(USE_TEXEL_FETCH) + uint2 p0, p1, p2, p3; + calculate_sample_position(index + 0U, uint2(cs_light_data_size.xy), p0); + calculate_sample_position(index + 1U, uint2(cs_light_data_size.xy), p1); + calculate_sample_position(index + 2U, uint2(cs_light_data_size.xy), p2); + calculate_sample_position(index + 3U, uint2(cs_light_data_size.xy), p3); + float4 light_data_0 = texelFetch(light_data, int2(p0), 0); + float4 light_data_1 = texelFetch(light_data, int2(p1), 0); + float4 light_data_2 = texelFetch(light_data, int2(p2), 0); + float4 light_data_3 = texelFetch(light_data, int2(p3), 0); + #else + float2 uv0, uv1, uv2, uv3; + uint index0 = index + uint(0); + uint index1 = index + uint(1); + uint index2 = index + uint(2); + uint index3 = index + uint(3); + calculate_sample_uv(index0, cs_light_data_size.zw, uv0); + calculate_sample_uv(index1, cs_light_data_size.zw, uv1); + calculate_sample_uv(index2, cs_light_data_size.zw, uv2); + calculate_sample_uv(index3, cs_light_data_size.zw, uv3); + float4 light_data_0 = textureLod(light_data, uv0, 0.0); + float4 light_data_1 = textureLod(light_data, uv1, 0.0); + float4 light_data_2 = textureLod(light_data, uv2, 0.0); + float4 light_data_3 = textureLod(light_data, uv3, 0.0); + #endif + #endif + + light_position = light_data_0.xyz; + light_color = light_data_1.xyz; + light_falloff = light_data_2.xyz; + spot_light_falloff = light_data_3.xyz; + spot_dir = normalize(float3(light_data_0.w, light_data_1.w, light_data_2.w)); + + sm_index0 = light_data_0.w; + sm_index1 = light_data_1.w; + sm_index2 = light_data_3.x; + sm_index3 = light_data_3.y; + sm_index4 = light_data_3.z; + sm_index5 = light_data_3.w; + + shadow_intensity = light_data_4.x; + } + + void sample_light_shadow_matrix(LIGHT_SHADOW_MATRICES_TYPE light_shadow_matrices, uint index, out float4x4 shadow_matrix) + { + #if defined(USE_RAW_BUFFER_FOR_LIGHT_INDEX_DATA) + shadow_matrix[0] = light_shadow_matrices.Load(index * 4 + 0); + shadow_matrix[1] = light_shadow_matrices.Load(index * 4 + 1); + shadow_matrix[2] = light_shadow_matrices.Load(index * 4 + 2); + shadow_matrix[3] = light_shadow_matrices.Load(index * 4 + 3); + #else + #if defined(USE_TEXEL_FETCH) + uint2 p0, p1, p2, p3; + calculate_sample_position(index * 4U + 0U, uint2(cs_light_shadow_matrices_size.xy), p0); + calculate_sample_position(index * 4U + 1U, uint2(cs_light_shadow_matrices_size.xy), p1); + calculate_sample_position(index * 4U + 2U, uint2(cs_light_shadow_matrices_size.xy), p2); + calculate_sample_position(index * 4U + 3U, uint2(cs_light_shadow_matrices_size.xy), p3); + shadow_matrix[0] = texelFetch(light_shadow_matrices, int2(p0), 0); + shadow_matrix[1] = texelFetch(light_shadow_matrices, int2(p1), 0); + shadow_matrix[2] = texelFetch(light_shadow_matrices, int2(p2), 0); + shadow_matrix[3] = texelFetch(light_shadow_matrices, int2(p3), 0); + shadow_matrix = transpose(shadow_matrix); + #else + float2 uv0, uv1, uv2, uv3; + uint index0 = index * uint(4); + uint index1 = index * uint(4); + uint index2 = index * uint(4); + uint index3 = index * uint(4); + index0 += uint(0); + index1 += uint(1); + index2 += uint(2); + index3 += uint(3); + + calculate_sample_uv(index0, cs_light_shadow_matrices_size.zw, uv0); + calculate_sample_uv(index1, cs_light_shadow_matrices_size.zw, uv1); + calculate_sample_uv(index2, cs_light_shadow_matrices_size.zw, uv2); + calculate_sample_uv(index3, cs_light_shadow_matrices_size.zw, uv3); + shadow_matrix[0] = textureLod(light_shadow_matrices, uv0, 0.0); + shadow_matrix[1] = textureLod(light_shadow_matrices, uv1, 0.0); + shadow_matrix[2] = textureLod(light_shadow_matrices, uv2, 0.0); + shadow_matrix[3] = textureLod(light_shadow_matrices, uv3, 0.0); + shadow_matrix = transpose(shadow_matrix); + #endif + #endif + } + + void clustered_shading(CLUSTER_DATA_TYPE cluster_data, + LIGHT_INDEX_DATA_TYPE light_index_data, + LIGHT_DATA_TYPE light_data, + LIGHT_SHADOW_MATRICES_TYPE light_shadow_matrices, + ComparisonSampler2D local_lights_shadow_atlas, + const float3 world_pos, + const float3 V, + const float3 N, + const float3 diffuse_color, + const float3 specular_color, + const float roughness, + const float2 screen_pos, + const float depth, + const float density, + const float3 base_color, + inout float3 acc_diff, + inout float3 acc_spec, + inout float3 translucency) + { + if (depth > cs_cluster_max_depth_inv_max_depth.x) + return; + + const float inv_fade_distance = 0.1; + const float fade_attn = min((cs_cluster_max_depth_inv_max_depth.x - depth) * inv_fade_distance, 1.0); + + uint2 cluster_info; + sample_cluster_data(cluster_data, screen_pos, depth, cluster_info); + + uint light_index = cluster_info.x; + int point_light_count = int(cluster_info.y & 0x00FFU); + for (int pl = 0; pl < point_light_count; ++pl) { + uint index; + sample_light_index_data(light_index_data, light_index, index); + float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir; + float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5; + float shadow_fade; + sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade); + + float3 L = light_position - world_pos; + float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y) * fade_attn; + #ifdef HAS_ANISOTROPY + bsdf(L, V, T, B, N, light_color, diffuse_color, specular_color, roughness, anisotropy_, attn, acc_diff, acc_spec); + #else + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness, attn, acc_diff, acc_spec); + #endif + + translucency += calculate_translucency(L, V, N, attn, base_color, light_color, density); + ++light_index; + } + + int spot_light_count = int((cluster_info.y >> 16U) & 0x00FFU); + for (int sl = 0; sl < spot_light_count; ++sl) { + uint index; + sample_light_index_data(light_index_data, light_index, index); + float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir; + float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5; + float shadow_fade; + sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade); + + float3 L = light_position - world_pos; + float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y) * fade_attn; + attn *= spot_angle_attenuation(dot(-L, spot_dir), spot_light_falloff.x, spot_light_falloff.y); + #ifdef HAS_ANISOTROPY + bsdf(L, V, T, B, N, light_color, diffuse_color, specular_color, roughness, anisotropy_, attn, acc_diff, acc_spec); + #else + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness, attn, acc_diff, acc_spec); + #endif + translucency += calculate_translucency(L, V, N, attn, base_color, light_color, density); + + ++light_index; + } + + int shadow_casting_point_light_count = int((cluster_info.y >> 8U) & 0x00FFU); + for (int scpl = 0; scpl < shadow_casting_point_light_count; ++scpl) { + uint index; + sample_light_index_data(light_index_data, light_index, index); + float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir; + float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5; + float shadow_fade; + sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade); + + float3 L = light_position - world_pos; + float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y) * fade_attn; + + // Based on the biggest component of the vector from the shaded position to the light source and its sign, chose the correct + // shadow map index to get the correct world position to shadow map matrix. + float3 shadow_L = -L; + int3 is_positive = int3(greaterThan(shadow_L, float3(0.0, 0.0, 0.0))); + float3 abs_shadow_L = abs(shadow_L); + int test_index = (abs_shadow_L.x > abs_shadow_L.y && abs_shadow_L.x > abs_shadow_L.z) ? 0 + is_positive[0]: (abs_shadow_L.y > abs_shadow_L.z) ? 2 + is_positive[1] : 4 + is_positive[2]; + + // The shadows from the faces of an onmni light are populated in the following order and directions. + // float3(-1.0f, 0.0f, 0.0f), + // float3( 1.0f, 0.0f, 0.0f), + // float3( 0.0f, -1.0f, 0.0f), + // float3( 0.0f, 1.0f, 0.0f), + // float3( 0.0f, 0.0f, -1.0f) + // float3( 0.0f, 0.0f, 1.0f), + //fix this crap about the sm_index potentially being -1.0 which indicates that the light isn't casting any shadows.... + float sm_index = test_index == 0? sm_index0 : + test_index == 1? sm_index1 : + test_index == 2? sm_index2 : + test_index == 3? sm_index3 : + test_index == 4? sm_index4 : + sm_index5; + + float shadow_intensity = 1.0f; + if (sm_index != -1.0f) { + float4x4 world_to_sm; + sample_light_shadow_matrix(light_shadow_matrices, uint(sm_index), world_to_sm); + float4 sm_pos = mul(float4(world_pos, 1.0), world_to_sm); + sm_pos.xyz /= sm_pos.w; + sm_pos.z = apply_shadow_depth_comparison_bias(depth, sm_pos.z, local_shadow_map_bias); + shadow_intensity = shadow_intensity_2d(local_lights_shadow_atlas, cs_shadow_atlas_size.xy, sm_pos.xy, sm_pos.z); + shadow_intensity = (shadow_intensity - 1.0) * shadow_fade + 1.0; + } + #ifdef HAS_ANISOTROPY + bsdf(L, V, T, B, N, light_color, diffuse_color, specular_color, roughness, anisotropy_, attn * shadow_intensity, acc_diff, acc_spec); + #else + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness, attn * shadow_intensity, acc_diff, acc_spec); + #endif + translucency += calculate_translucency(L, V, N, attn * lerp(lerp(0.5, 1.0, shadow_intensity), shadow_intensity, density), base_color, light_color, density); + + ++light_index; + } + + int shadow_casting_spot_light_count = int((cluster_info.y >> 24U) & 0x00FFU); + for (int scsl = 0; scsl < shadow_casting_spot_light_count; ++scsl) { + uint index; + sample_light_index_data(light_index_data, light_index, index); + float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir; + float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5; + float shadow_fade; + sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade); + + float3 L = light_position - world_pos; + float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y) * fade_attn; + attn *= spot_angle_attenuation(dot(-L, spot_dir), spot_light_falloff.x, spot_light_falloff.y); + + float4x4 world_to_sm; + sample_light_shadow_matrix(light_shadow_matrices, uint(sm_index5), world_to_sm); + float4 sm_pos = mul(float4(world_pos, 1.0), world_to_sm); + sm_pos.xyz /= sm_pos.w; + sm_pos.z = apply_shadow_depth_comparison_bias(depth, sm_pos.z, local_shadow_map_bias); + float shadow_intensity = shadow_intensity_2d(local_lights_shadow_atlas, cs_shadow_atlas_size.xy, sm_pos.xy, sm_pos.z); + shadow_intensity = (shadow_intensity - 1.0) * shadow_fade + 1.0; + + #ifdef HAS_ANISOTROPY + bsdf(L, V, T, B, N, light_color, diffuse_color, specular_color, roughness, anisotropy_, attn * shadow_intensity, acc_diff, acc_spec); + #else + bsdf(L, V, N, light_color, diffuse_color, specular_color, roughness, attn * shadow_intensity, acc_diff, acc_spec); + #endif + translucency += calculate_translucency(L, V, N, attn * lerp(lerp(0.5, 1.0, shadow_intensity), shadow_intensity, density), base_color, light_color, density); + + ++light_index; + } + } + """ + } +} diff --git a/vmf_source/core/stingray_renderer/shader_libraries/noise.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/noise.shader_source new file mode 100644 index 0000000..d74ddb5 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/noise.shader_source @@ -0,0 +1,2659 @@ +render_states = { + +} + +sampler_states = { + +} + +hlsl_shaders = { + noise_functions = { + code=""" + // ---------------------------------------------------------------------------------------------------------------------- + // + // Structure of document: + // 1. License for simplex noise and classic noise + // 2. License for worley noise + // 3. GLSL to HLSL helper functions + // 4. Ashima Arts Simplex Noise 4D + // 5. Brian Sharpe's noise library, https://github.com/BrianSharpe + // + // Code is ported from GLSL to HLSL + // + // ---------------------------------------------------------------------------------------------------------------------- + // + // Description : Array and textureless GLSL 2D/3D/4D simplex noise function. + // noise functions. + // Author : Ian McEwan, Ashima Arts. + // Maintainer : ijm + // Lastmod : 20110822 (ijm) + // License : Copyright (C) 2011 Ashima Arts. All rights reserved. + // Distributed under the MIT License. See LICENSE_Simplex_Noise below for details. + // https://github.com/ashima/webgl-noise + // + + // --------------------------------- + // LICENSE_Simplex_Noise + // --------------------------------- + // Copyright (C) 2011 by Ashima Arts (Simplex noise) + // Copyright (C) 2011 by Stefan Gustavson (Classic noise) + // + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + // + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + // + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + // THE SOFTWARE. + // --------------------------------- + + // ---------------------------------------------------------------------------------------------------------------------- + // Cellular noise ("Worley noise") in 2D in GLSL. + // Copyright (c) Stefan Gustavson 2011-04-19. All rights reserved. + // This code is released under the conditions of the MIT license. + // See LICENSE_Cellular_Noise below for details. + // https://github.com/ashima/webgl-noise + + // --------------------------------- + // LICENSE_Cellular_Noise + // --------------------------------- + // GLSL 2D and 3D cellular noise + // Copyright (c) 2011 by Stefan Gustavson (stefan.gustavson@liu.se) + + // Permission is hereby granted, free of charge, to any person obtaining a copy + // of this software and associated documentation files (the "Software"), to deal + // in the Software without restriction, including without limitation the rights + // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + // copies of the Software, and to permit persons to whom the Software is + // furnished to do so, subject to the following conditions: + + // The above copyright notice and this permission notice shall be included in + // all copies or substantial portions of the Software. + + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + // THE SOFTWARE. + // --------------------------------- + + // ---------------------------------------------------------------------------------------------------------------------- + // HLSL port functions + #define splat4(x) float4(x, x, x, x) + #define splat3(x) float3(x, x, x) + #define splat2(x) float2(x, x) + // TODO: use ternary operator instead of sign and step + //----------------------------------------------------------------------------------------------------------------------- + + // + // Description : Array and textureless GLSL 2D/3D/4D simplex noise function. + // noise functions. + // Author : Ian McEwan, Ashima Arts. + // Maintainer : ijm + // Lastmod : 20110822 (ijm) + // License : Copyright (C) 2011 Ashima Arts. All rights reserved. + // Distributed under the MIT License. See LICENSE_Simplex_Noise below for details. + // https://github.com/ashima/webgl-noise + // + + float mod289(float x) { + return x - floor(x * (1.0 / 289.0)) * 289.0; + } + + float3 mod289(float3 x) { + return x - floor(x * (1.0 / 289.0)) * 289.0; + } + + float4 mod289(float4 x) { + return x - floor(x * (1.0 / 289.0)) * 289.0; + } + + float permute(float x) { + return mod289(((x * 34.0) + 1.0) * x); + } + + float4 permute(float4 x) { + return mod289(((x * 34.0) + 1.0) * x); + } + + float taylorInvSqrt(float r) + { + return 1.79284291400159 - 0.85373472095314 * r; + } + + float4 taylorInvSqrt(float4 r) + { + return 1.79284291400159 - 0.85373472095314 * r; + } + + float4 grad4(float j, float4 ip) + { + const float4 ones = float4(1.0, 1.0, 1.0, -1.0); + float4 p,s; + + p.xyz = floor( frac(j * ip.xyz) * 7.0) * ip.z - 1.0; + p.w = 1.5 - dot(abs(p.xyz), ones.xyz); + s = float4(p < float4(0.0, 0.0, 0.0, 0.0)); + p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; + + return p; + } + + // returns a value in range of [-1, 1] + float simplex_noise4D(float4 v) + { + // (sqrt(5) - 1)/4 = F4, used once below + const float F4 = 0.309016994374947451; + + const float4 C = float4( 0.138196601125011, // (5 - sqrt(5))/20 G4 + 0.276393202250021, // 2 * G4 + 0.414589803375032, // 3 * G4 + -0.447213595499958); // -1 + 4 * G4 + + // First corner + float4 i = floor(v + dot(v, float4(F4, F4, F4, F4))); + float4 x0 = v - i + dot(i, C.xxxx); + + // Other corners + + // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) + float4 i0; + float3 isX = step( x0.yzw, x0.xxx ); + float3 isYZ = step( x0.zww, x0.yyz ); + // i0.x = dot( isX, float3( 1.0 ) ); + i0.x = isX.x + isX.y + isX.z; + i0.yzw = 1.0 - isX; + // i0.y += dot( isYZ.xy, float2( 1.0 ) ); + i0.y += isYZ.x + isYZ.y; + i0.zw += 1.0 - isYZ.xy; + i0.z += isYZ.z; + i0.w += 1.0 - isYZ.z; + + // i0 now contains the unique values 0,1,2,3 in each channel + float4 i3 = clamp( i0, 0.0, 1.0 ); + float4 i2 = clamp( i0-1.0, 0.0, 1.0 ); + float4 i1 = clamp( i0-2.0, 0.0, 1.0 ); + + // x0 = x0 - 0.0 + 0.0 * C.xxxx + // x1 = x0 - i1 + 1.0 * C.xxxx + // x2 = x0 - i2 + 2.0 * C.xxxx + // x3 = x0 - i3 + 3.0 * C.xxxx + // x4 = x0 - 1.0 + 4.0 * C.xxxx + float4 x1 = x0 - i1 + C.xxxx; + float4 x2 = x0 - i2 + C.yyyy; + float4 x3 = x0 - i3 + C.zzzz; + float4 x4 = x0 + C.wwww; + + // Permutations + i = mod289(i); + float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x); + float4 j1 = permute( permute( permute( permute ( + i.w + float4(i1.w, i2.w, i3.w, 1.0 )) + + i.z + float4(i1.z, i2.z, i3.z, 1.0 )) + + i.y + float4(i1.y, i2.y, i3.y, 1.0 )) + + i.x + float4(i1.x, i2.x, i3.x, 1.0 )); + + // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope + // 7*7*6 = 294, which is close to the ring size 17*17 = 289. + float4 ip = float4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; + + float4 p0 = grad4(j0, ip); + float4 p1 = grad4(j1.x, ip); + float4 p2 = grad4(j1.y, ip); + float4 p3 = grad4(j1.z, ip); + float4 p4 = grad4(j1.w, ip); + + // Normalise gradients + float4 norm = taylorInvSqrt(float4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); + p0 *= norm.x; + p1 *= norm.y; + p2 *= norm.z; + p3 *= norm.w; + p4 *= taylorInvSqrt(dot(p4, p4)); + + // Mix contributions from the five corners + float3 m0 = max(0.6 - float3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); + float2 m1 = max(0.6 - float2(dot(x3,x3), dot(x4,x4) ), 0.0); + m0 = m0 * m0; + m1 = m1 * m1; + return 49.0 * ( dot(m0*m0, float3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 ))) + + dot(m1*m1, float2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; + + } + + //----------------------------------------------------------------------------------------------------------------------- + + // + // Code repository for GPU noise development blog + // http://briansharpe.wordpress.com + // https://github.com/BrianSharpe + // + // I'm not one for copyrights. Use the code however you wish. + // All I ask is that credit be given back to the blog or myself when appropriate. + // And also to let me know if you come up with any changes, improvements, thoughts or interesting uses for this stuff. :) + // Thanks! + // + // Brian Sharpe + // brisharpe CIRCLE_A yahoo DOT com + // http://briansharpe.wordpress.com + // https://github.com/BrianSharpe + // + + + // + // NoiseLib TODO + // + // 1) Ensure portability across different cards + // 2) 16bit and 24bit implementations of hashes and noises + // 3) Lift various noise implementations out to individual self-contained files + // 4) Implement texture-based versions + // + + + // + // Permutation polynomial idea is from Stefan Gustavson's and Ian McEwan's work at... + // http://github.com/ashima/webgl-noise + // http://www.itn.liu.se/~stegu/GLSL-cellular + // + // http://briansharpe.wordpress.com/2011/10/01/gpu-texture-free-noise/ + // + float4 SGPP_coord_prepare(float4 x) { return x - floor(x * ( 1.0 / 289.0 )) * 289.0; } + float3 SGPP_coord_prepare(float3 x) { return x - floor(x * ( 1.0 / 289.0 )) * 289.0; } + float4 SGPP_permute(float4 x) { return frac( x * ( ( 34.0 / 289.0 ) * x + ( 1.0 / 289.0 ) ) ) * 289.0; } + float4 SGPP_resolve(float4 x) { return frac( x * ( 7.0 / 288.0 ) ); } + float4 SGPP_hash_2D( float2 gridcell ) // generates a random number for each of the 4 cell corners + { + // gridcell is assumed to be an integer coordinate + float4 hash_coord = SGPP_coord_prepare( float4( gridcell.xy, gridcell.xy + 1.0 ) ); + return SGPP_resolve( SGPP_permute( SGPP_permute( hash_coord.xzxz ) + hash_coord.yyww ) ); + } + void SGPP_hash_2D( float2 gridcell, out float4 hash_0, out float4 hash_1 ) // generates 2 random numbers for each of the 4 cell corners + { + // gridcell is assumed to be an integer coordinate + float4 hash_coord = SGPP_coord_prepare( float4( gridcell.xy, gridcell.xy + 1.0 ) ); + hash_0 = SGPP_permute( SGPP_permute( hash_coord.xzxz ) + hash_coord.yyww ); + hash_1 = SGPP_resolve( SGPP_permute( hash_0 ) ); + hash_0 = SGPP_resolve( hash_0 ); + } + void SGPP_hash_3D( float3 gridcell, out float4 lowz_hash, out float4 highz_hash ) // generates a random number for each of the 8 cell corners + { + // gridcell is assumed to be an integer coordinate + gridcell = SGPP_coord_prepare( gridcell ); + float3 gridcell_inc1 = step( gridcell, splat3( 287.5 ) ) * ( gridcell + 1.0 ); + highz_hash = SGPP_permute( SGPP_permute( float2( gridcell.x, gridcell_inc1.x ).xyxy ) + float2( gridcell.y, gridcell_inc1.y ).xxyy ); + lowz_hash = SGPP_resolve( SGPP_permute( highz_hash + gridcell.zzzz ) ); + highz_hash = SGPP_resolve( SGPP_permute( highz_hash + gridcell_inc1.zzzz ) ); + } + void SGPP_hash_3D( float3 gridcell, + float3 v1_mask, // user definable v1 and v2. ( 0's and 1's ) + float3 v2_mask, + out float4 hash_0, + out float4 hash_1, + out float4 hash_2 ) // generates 3 random numbers for each of the 4 3D cell corners. cell corners: v0=0,0,0 v3=1,1,1 the other two are user definable + { + float3 coords0 = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / 289.0 )) * 289.0; + float3 coords3 = step( coords0, splat3( 287.5 ) ) * ( coords0 + 1.0 ); + float3 coords1 = lerp( coords0, coords3, v1_mask ); + float3 coords2 = lerp( coords0, coords3, v2_mask ); + hash_2 = SGPP_permute( SGPP_permute( SGPP_permute( float4( coords0.x, coords1.x, coords2.x, coords3.x ) ) + float4( coords0.y, coords1.y, coords2.y, coords3.y ) ) + float4( coords0.z, coords1.z, coords2.z, coords3.z ) ); + hash_0 = SGPP_resolve( hash_2 ); + hash_1 = SGPP_resolve( hash_2 = SGPP_permute( hash_2 ) ); + hash_2 = SGPP_resolve( SGPP_permute( hash_2 ) ); + } + void SGPP_hash_3D( float3 gridcell, + out float4 lowz_hash_0, + out float4 lowz_hash_1, + out float4 lowz_hash_2, + out float4 highz_hash_0, + out float4 highz_hash_1, + out float4 highz_hash_2 ) // generates 3 random numbers for each of the 8 cell corners + { + // gridcell is assumed to be an integer coordinate + gridcell = SGPP_coord_prepare( gridcell ); + float3 gridcell_inc1 = step( gridcell, splat3( 287.5 ) ) * ( gridcell + 1.0 ); + highz_hash_2 = SGPP_permute( SGPP_permute( float2( gridcell.x, gridcell_inc1.x ).xyxy ) + float2( gridcell.y, gridcell_inc1.y ).xxyy ); + lowz_hash_0 = SGPP_resolve( lowz_hash_2 = SGPP_permute( highz_hash_2 + gridcell.zzzz ) ); + highz_hash_0 = SGPP_resolve( highz_hash_2 = SGPP_permute( highz_hash_2 + gridcell_inc1.zzzz ) ); + lowz_hash_1 = SGPP_resolve( lowz_hash_2 = SGPP_permute( lowz_hash_2 ) ); + highz_hash_1 = SGPP_resolve( highz_hash_2 = SGPP_permute( highz_hash_2 ) ); + lowz_hash_2 = SGPP_resolve( SGPP_permute( lowz_hash_2 ) ); + highz_hash_2 = SGPP_resolve( SGPP_permute( highz_hash_2 ) ); + } + + + // + // implementation of the blumblumshub hash + // as described in MNoise paper http://www.cs.umbc.edu/~olano/papers/mNoise.pdf + // + // http://briansharpe.wordpress.com/2011/10/01/gpu-texture-free-noise/ + // + float4 BBS_coord_prepare(float4 x) { return x - floor(x * ( 1.0 / 61.0 )) * 61.0; } + float3 BBS_coord_prepare(float3 x) { return x - floor(x * ( 1.0 / 61.0 )) * 61.0; } + float4 BBS_permute(float4 x) { return frac( x * x * ( 1.0 / 61.0 )) * 61.0; } + float4 BBS_permute_and_resolve(float4 x) { return frac( x * x * ( 1.0 / 61.0 ) ); } + float4 BBS_hash_2D( float2 gridcell ) // generates a random number for each of the 4 cell corners + { + // gridcell is assumed to be an integer coordinate + float4 hash_coord = BBS_coord_prepare( float4( gridcell.xy, gridcell.xy + 1.0 ) ); + float4 p = BBS_permute( hash_coord.xzxz /* * 7.0 */ ); // * 7.0 will increase variance close to origin + return BBS_permute_and_resolve( p + hash_coord.yyww ); + } + float4 BBS_hash_hq_2D( float2 gridcell ) // generates a hq random number for each of the 4 cell corners + { + // gridcell is assumed to be an integer coordinate + float4 hash_coord = BBS_coord_prepare( float4( gridcell.xy, gridcell.xy + 1.0 ) ); + float4 p = BBS_permute( hash_coord.xzxz /* * 7.0 */ ); // * 7.0 will increase variance close to origin + p = BBS_permute( p + hash_coord.yyww ); + return BBS_permute_and_resolve( p + hash_coord.xzxz ); + } + void BBS_hash_3D( float3 gridcell, out float4 lowz_hash, out float4 highz_hash ) // generates a random number for each of the 8 cell corners + { + // gridcell is assumed to be an integer coordinate + + // was having precision issues here with 61.0 60.0 fixes it. need to test on other cards. + const float DOMAIN = 60.0; + gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN; + float3 gridcell_inc1 = step( gridcell, splat3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + float4 p = BBS_permute( float2( gridcell.x, gridcell_inc1.x ).xyxy /* * 7.0 */ ); // * 7.0 will increase variance close to origin + p = BBS_permute( p + float2( gridcell.y, gridcell_inc1.y ).xxyy ); + lowz_hash = BBS_permute_and_resolve( p + gridcell.zzzz ); + highz_hash = BBS_permute_and_resolve( p + gridcell_inc1.zzzz ); + } + + + // + // FAST32_hash + // A very fast hashing function. Requires 32bit support. + // http://briansharpe.wordpress.com/2011/11/15/a-fast-and-simple-32bit-floating-point-hash-function/ + // + // The 2D hash formula takes the form.... + // hash = mod( coord.x * coord.x * coord.y * coord.y, SOMELARGEFLOAT ) / SOMELARGEFLOAT + // We truncate and offset the domain to the most interesting part of the noise. + // SOMELARGEFLOAT should be in the range of 400.0->1000.0 and needs to be hand picked. Only some give good results. + // A 3D hash is achieved by offsetting the SOMELARGEFLOAT value by the Z coordinate + // + float4 FAST32_hash_2D( float2 gridcell ) // generates a random number for each of the 4 cell corners + { + // gridcell is assumed to be an integer coordinate + const float2 OFFSET = float2( 26.0, 161.0 ); + const float DOMAIN = 71.0; + const float SOMELARGEFLOAT = 951.135664; + float4 P = float4( gridcell.xy, gridcell.xy + 1.0 ); + P = P - floor(P * ( 1.0 / DOMAIN )) * DOMAIN; // truncate the domain + P += OFFSET.xyxy; // offset to interesting part of the noise + P *= P; // calculate and return the hash + return frac( P.xzxz * P.yyww * ( 1.0 / SOMELARGEFLOAT ) ); + } + void FAST32_hash_2D( float2 gridcell, out float4 hash_0, out float4 hash_1 ) // generates 2 random numbers for each of the 4 cell corners + { + // gridcell is assumed to be an integer coordinate + const float2 OFFSET = float2( 26.0, 161.0 ); + const float DOMAIN = 71.0; + const float2 SOMELARGEFLOATS = float2( 951.135664, 642.949883 ); + float4 P = float4( gridcell.xy, gridcell.xy + 1.0 ); + P = P - floor(P * ( 1.0 / DOMAIN )) * DOMAIN; + P += OFFSET.xyxy; + P *= P; + P = P.xzxz * P.yyww; + hash_0 = frac( P * ( 1.0 / SOMELARGEFLOATS.x ) ); + hash_1 = frac( P * ( 1.0 / SOMELARGEFLOATS.y ) ); + } + void FAST32_hash_2D( float2 gridcell, + out float4 hash_0, + out float4 hash_1, + out float4 hash_2 ) // generates 3 random numbers for each of the 4 cell corners + { + // gridcell is assumed to be an integer coordinate + const float2 OFFSET = float2( 26.0, 161.0 ); + const float DOMAIN = 71.0; + const float3 SOMELARGEFLOATS = float3( 951.135664, 642.949883, 803.202459 ); + float4 P = float4( gridcell.xy, gridcell.xy + 1.0 ); + P = P - floor(P * ( 1.0 / DOMAIN )) * DOMAIN; + P += OFFSET.xyxy; + P *= P; + P = P.xzxz * P.yyww; + hash_0 = frac( P * ( 1.0 / SOMELARGEFLOATS.x ) ); + hash_1 = frac( P * ( 1.0 / SOMELARGEFLOATS.y ) ); + hash_2 = frac( P * ( 1.0 / SOMELARGEFLOATS.z ) ); + } + float4 FAST32_hash_2D_Cell( float2 gridcell ) // generates 4 different random numbers for the single given cell point + { + // gridcell is assumed to be an integer coordinate + const float2 OFFSET = float2( 26.0, 161.0 ); + const float DOMAIN = 71.0; + const float4 SOMELARGEFLOATS = float4( 951.135664, 642.949883, 803.202459, 986.973274 ); + float2 P = gridcell - floor(gridcell * ( 1.0 / DOMAIN )) * DOMAIN; + P += OFFSET.xy; + P *= P; + return frac( (P.x * P.y) * ( 1.0 / SOMELARGEFLOATS.xyzw ) ); + } + float4 FAST32_hash_3D_Cell( float3 gridcell ) // generates 4 different random numbers for the single given cell point + { + // gridcell is assumed to be an integer coordinate + + // TODO: these constants need tweaked to find the best possible noise. + // probably requires some kind of brute force computational searching or something.... + const float2 OFFSET = float2( 50.0, 161.0 ); + const float DOMAIN = 69.0; + const float4 SOMELARGEFLOATS = float4( 635.298681, 682.357502, 668.926525, 588.255119 ); + const float4 ZINC = float4( 48.500388, 65.294118, 63.934599, 63.279683 ); + + // truncate the domain + gridcell.xyz = gridcell - floor(gridcell * ( 1.0 / DOMAIN )) * DOMAIN; + gridcell.xy += OFFSET.xy; + gridcell.xy *= gridcell.xy; + return frac( ( gridcell.x * gridcell.y ) * ( 1.0 / ( SOMELARGEFLOATS + gridcell.zzzz * ZINC ) ) ); + } + void FAST32_hash_3D( float3 gridcell, out float4 lowz_hash, out float4 highz_hash ) // generates a random number for each of the 8 cell corners + { + // gridcell is assumed to be an integer coordinate + + // TODO: these constants need tweaked to find the best possible noise. + // probably requires some kind of brute force computational searching or something.... + const float2 OFFSET = float2( 50.0, 161.0 ); + const float DOMAIN = 69.0; + const float SOMELARGEFLOAT = 635.298681; + const float ZINC = 48.500388; + + // truncate the domain + gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN; + float3 gridcell_inc1 = step( gridcell, splat3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + // calculate the noise + float4 P = float4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy; + P *= P; + P = P.xzxz * P.yyww; + highz_hash.xy = float2( 1.0 / ( SOMELARGEFLOAT + float2( gridcell.z, gridcell_inc1.z ) * ZINC ) ); + lowz_hash = frac( P * highz_hash.xxxx ); + highz_hash = frac( P * highz_hash.yyyy ); + } + void FAST32_hash_3D( float3 gridcell, + float3 v1_mask, // user definable v1 and v2. ( 0's and 1's ) + float3 v2_mask, + out float4 hash_0, + out float4 hash_1, + out float4 hash_2 ) // generates 3 random numbers for each of the 4 3D cell corners. cell corners: v0=0,0,0 v3=1,1,1 the other two are user definable + { + // gridcell is assumed to be an integer coordinate + + // TODO: these constants need tweaked to find the best possible noise. + // probably requires some kind of brute force computational searching or something.... + const float2 OFFSET = float2( 50.0, 161.0 ); + const float DOMAIN = 69.0; + const float3 SOMELARGEFLOATS = float3( 635.298681, 682.357502, 668.926525 ); + const float3 ZINC = float3( 48.500388, 65.294118, 63.934599 ); + + // truncate the domain + gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN; + float3 gridcell_inc1 = step( gridcell, splat3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + // compute x*x*y*y for the 4 corners + float4 P = float4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy; + P *= P; + float4 V1xy_V2xy = lerp( P.xyxy, P.zwzw, float4( v1_mask.xy, v2_mask.xy ) ); // apply mask for v1 and v2 + P = float4( P.x, V1xy_V2xy.xz, P.z ) * float4( P.y, V1xy_V2xy.yw, P.w ); + + // get the lowz and highz mods + float3 lowz_mods = float3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) ); + float3 highz_mods = float3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) ); + + // apply mask for v1 and v2 mod values + v1_mask = ( v1_mask.z < 0.5 ) ? lowz_mods : highz_mods; + v2_mask = ( v2_mask.z < 0.5 ) ? lowz_mods : highz_mods; + + // compute the final hash + hash_0 = frac( P * float4( lowz_mods.x, v1_mask.x, v2_mask.x, highz_mods.x ) ); + hash_1 = frac( P * float4( lowz_mods.y, v1_mask.y, v2_mask.y, highz_mods.y ) ); + hash_2 = frac( P * float4( lowz_mods.z, v1_mask.z, v2_mask.z, highz_mods.z ) ); + } + float4 FAST32_hash_3D( float3 gridcell, + float3 v1_mask, // user definable v1 and v2. ( 0's and 1's ) + float3 v2_mask ) // generates 1 random number for each of the 4 3D cell corners. cell corners: v0=0,0,0 v3=1,1,1 the other two are user definable + { + // gridcell is assumed to be an integer coordinate + + // TODO: these constants need tweaked to find the best possible noise. + // probably requires some kind of brute force computational searching or something.... + const float2 OFFSET = float2( 50.0, 161.0 ); + const float DOMAIN = 69.0; + const float SOMELARGEFLOAT = 635.298681; + const float ZINC = 48.500388; + + // truncate the domain + gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN; + float3 gridcell_inc1 = step( gridcell, splat3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + // compute x*x*y*y for the 4 corners + float4 P = float4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy; + P *= P; + float4 V1xy_V2xy = lerp( P.xyxy, P.zwzw, float4( v1_mask.xy, v2_mask.xy ) ); // apply mask for v1 and v2 + P = float4( P.x, V1xy_V2xy.xz, P.z ) * float4( P.y, V1xy_V2xy.yw, P.w ); + + // get the z mod vals + float2 V1z_V2z = float2( v1_mask.z < 0.5 ? gridcell.z : gridcell_inc1.z, v2_mask.z < 0.5 ? gridcell.z : gridcell_inc1.z ); + float4 mod_vals = float4( 1.0 / ( SOMELARGEFLOAT + float4( gridcell.z, V1z_V2z, gridcell_inc1.z ) * ZINC ) ); + + // compute the final hash + return frac( P * mod_vals ); + } + void FAST32_hash_3D( float3 gridcell, + out float4 lowz_hash_0, + out float4 lowz_hash_1, + out float4 lowz_hash_2, + out float4 highz_hash_0, + out float4 highz_hash_1, + out float4 highz_hash_2 ) // generates 3 random numbers for each of the 8 cell corners + { + // gridcell is assumed to be an integer coordinate + + // TODO: these constants need tweaked to find the best possible noise. + // probably requires some kind of brute force computational searching or something.... + const float2 OFFSET = float2( 50.0, 161.0 ); + const float DOMAIN = 69.0; + const float3 SOMELARGEFLOATS = float3( 635.298681, 682.357502, 668.926525 ); + const float3 ZINC = float3( 48.500388, 65.294118, 63.934599 ); + + // truncate the domain + gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN; + float3 gridcell_inc1 = step( gridcell, splat3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + // calculate the noise + float4 P = float4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy; + P *= P; + P = P.xzxz * P.yyww; + float3 lowz_mod = float3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell.zzz * ZINC.xyz ) ); + float3 highz_mod = float3( 1.0 / ( SOMELARGEFLOATS.xyz + gridcell_inc1.zzz * ZINC.xyz ) ); + lowz_hash_0 = frac( P * lowz_mod.xxxx ); + highz_hash_0 = frac( P * highz_mod.xxxx ); + lowz_hash_1 = frac( P * lowz_mod.yyyy ); + highz_hash_1 = frac( P * highz_mod.yyyy ); + lowz_hash_2 = frac( P * lowz_mod.zzzz ); + highz_hash_2 = frac( P * highz_mod.zzzz ); + } + void FAST32_hash_3D( float3 gridcell, + out float4 lowz_hash_0, + out float4 lowz_hash_1, + out float4 lowz_hash_2, + out float4 lowz_hash_3, + out float4 highz_hash_0, + out float4 highz_hash_1, + out float4 highz_hash_2, + out float4 highz_hash_3 ) // generates 4 random numbers for each of the 8 cell corners + { + // gridcell is assumed to be an integer coordinate + + // TODO: these constants need tweaked to find the best possible noise. + // probably requires some kind of brute force computational searching or something.... + const float2 OFFSET = float2( 50.0, 161.0 ); + const float DOMAIN = 69.0; + const float4 SOMELARGEFLOATS = float4( 635.298681, 682.357502, 668.926525, 588.255119 ); + const float4 ZINC = float4( 48.500388, 65.294118, 63.934599, 63.279683 ); + + // truncate the domain + gridcell.xyz = gridcell.xyz - floor(gridcell.xyz * ( 1.0 / DOMAIN )) * DOMAIN; + float3 gridcell_inc1 = step( gridcell, splat3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + // calculate the noise + float4 P = float4( gridcell.xy, gridcell_inc1.xy ) + OFFSET.xyxy; + P *= P; + P = P.xzxz * P.yyww; + lowz_hash_3.xyzw = float4( 1.0 / ( SOMELARGEFLOATS.xyzw + gridcell.zzzz * ZINC.xyzw ) ); + highz_hash_3.xyzw = float4( 1.0 / ( SOMELARGEFLOATS.xyzw + gridcell_inc1.zzzz * ZINC.xyzw ) ); + lowz_hash_0 = frac( P * lowz_hash_3.xxxx ); + highz_hash_0 = frac( P * highz_hash_3.xxxx ); + lowz_hash_1 = frac( P * lowz_hash_3.yyyy ); + highz_hash_1 = frac( P * highz_hash_3.yyyy ); + lowz_hash_2 = frac( P * lowz_hash_3.zzzz ); + highz_hash_2 = frac( P * highz_hash_3.zzzz ); + lowz_hash_3 = frac( P * lowz_hash_3.wwww ); + highz_hash_3 = frac( P * highz_hash_3.wwww ); + } + + + // + // FastHash32_2 + // + // An alternative to FastHash32 + // - slightly slower + // - can have a larger domain + // - allows for a 4D implementation + // + // (eg)4D is computed like so.... + // coord = mod( coord, DOMAIN ); + // coord = ( coord * SCALE ) + OFFSET; + // coord *= coord; + // hash = mod( coord.x * coord.y * coord.z * coord.w, SOMELARGEFLOAT ) / SOMELARGEFLOAT; + // + float4 FAST32_2_hash_2D( float2 gridcell ) // generates a random number for each of the 4 cell corners + { + // gridcell is assumed to be an integer coordinate + const float2 OFFSET = float2( 403.839172, 377.242706 ); + const float DOMAIN = 69.0; // NOTE: this can most likely be extended with some tweaking of the other parameters + const float SOMELARGEFLOAT = 32745.708984; + const float2 SCALE = float2( 2.009842, 1.372549 ); + + float4 P = float4( gridcell.xy, gridcell.xy + 1.0 ); + P = P - floor(P * ( 1.0 / DOMAIN )) * DOMAIN; + P = ( P * SCALE.xyxy ) + OFFSET.xyxy; + P *= P; + return frac( P.xzxz * P.yyww * ( 1.0 / SOMELARGEFLOAT ) ); + } + void FAST32_2_hash_3D( float3 gridcell, + out float4 z0_hash, // float4 == ( x0y0, x1y0, x0y1, x1y1 ) + out float4 z1_hash ) // generates a random number for each of the 8 cell corners + { + // gridcell is assumed to be an integer coordinate + const float3 OFFSET = float3( 55.882355, 63.167774, 52.941177 ); + const float DOMAIN = 69.0; // NOTE: this can most likely be extended with some tweaking of the other parameters + const float SOMELARGEFLOAT = 69412.070313; + const float3 SCALE = float3( 0.235142, 0.205890, 0.216449 ); + + // truncate the domain + gridcell = gridcell - floor(gridcell * ( 1.0 / DOMAIN )) * DOMAIN; + float3 gridcell_inc1 = step( gridcell, splat3( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + // calculate the noise + gridcell = ( gridcell * SCALE ) + OFFSET; + gridcell_inc1 = ( gridcell_inc1 * SCALE ) + OFFSET; + gridcell *= gridcell; + gridcell_inc1 *= gridcell_inc1; + + float4 x0y0_x1y0_x0y1_x1y1 = float4( gridcell.x, gridcell_inc1.x, gridcell.x, gridcell_inc1.x ) * float4( gridcell.yy, gridcell_inc1.yy ); + + z0_hash = frac( x0y0_x1y0_x0y1_x1y1 * gridcell.zzzz * ( 1.0 / SOMELARGEFLOAT ) ); + z1_hash = frac( x0y0_x1y0_x0y1_x1y1 * gridcell_inc1.zzzz * ( 1.0 / SOMELARGEFLOAT ) ); + } + void FAST32_2_hash_4D( float4 gridcell, + out float4 z0w0_hash, // float4 == ( x0y0, x1y0, x0y1, x1y1 ) + out float4 z1w0_hash, + out float4 z0w1_hash, + out float4 z1w1_hash ) + { + // gridcell is assumed to be an integer coordinate + + // TODO: these constants need tweaked to find the best possible noise. + // probably requires some kind of brute force computational searching or something.... + const float4 OFFSET = float4( 16.841230, 18.774548, 16.873274, 13.664607 ); + const float DOMAIN = 69.0; + const float SOMELARGEFLOAT = 47165.636719; + const float4 SCALE = float4( 0.102007, 0.114473, 0.139651, 0.084550 ); + + // truncate the domain + gridcell = gridcell - floor(gridcell * ( 1.0 / DOMAIN )) * DOMAIN; + float4 gridcell_inc1 = step( gridcell, splat4( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + // calculate the noise + gridcell = ( gridcell * SCALE ) + OFFSET; + gridcell_inc1 = ( gridcell_inc1 * SCALE ) + OFFSET; + gridcell *= gridcell; + gridcell_inc1 *= gridcell_inc1; + + float4 x0y0_x1y0_x0y1_x1y1 = float4( gridcell.x, gridcell_inc1.x, gridcell.x, gridcell_inc1.x ) * float4( gridcell.yy, gridcell_inc1.yy ); + float4 z0w0_z1w0_z0w1_z1w1 = float4( gridcell.z, gridcell_inc1.z, gridcell.z, gridcell_inc1.z ) * float4( gridcell.ww, gridcell_inc1.ww ) * ( 1.0 / SOMELARGEFLOAT ); + + z0w0_hash = frac( x0y0_x1y0_x0y1_x1y1 * z0w0_z1w0_z0w1_z1w1.xxxx ); + z1w0_hash = frac( x0y0_x1y0_x0y1_x1y1 * z0w0_z1w0_z0w1_z1w1.yyyy ); + z0w1_hash = frac( x0y0_x1y0_x0y1_x1y1 * z0w0_z1w0_z0w1_z1w1.zzzz ); + z1w1_hash = frac( x0y0_x1y0_x0y1_x1y1 * z0w0_z1w0_z0w1_z1w1.wwww ); + } + void FAST32_2_hash_4D( float4 gridcell, + out float4 z0w0_hash_0, // float4 == ( x0y0, x1y0, x0y1, x1y1 ) + out float4 z0w0_hash_1, + out float4 z0w0_hash_2, + out float4 z0w0_hash_3, + out float4 z1w0_hash_0, + out float4 z1w0_hash_1, + out float4 z1w0_hash_2, + out float4 z1w0_hash_3, + out float4 z0w1_hash_0, + out float4 z0w1_hash_1, + out float4 z0w1_hash_2, + out float4 z0w1_hash_3, + out float4 z1w1_hash_0, + out float4 z1w1_hash_1, + out float4 z1w1_hash_2, + out float4 z1w1_hash_3 ) + { + // gridcell is assumed to be an integer coordinate + + // TODO: these constants need tweaked to find the best possible noise. + // probably requires some kind of brute force computational searching or something.... + const float4 OFFSET = float4( 16.841230, 18.774548, 16.873274, 13.664607 ); + const float DOMAIN = 69.0; + const float4 SOMELARGEFLOATS = float4( 56974.746094, 47165.636719, 55049.667969, 49901.273438 ); + const float4 SCALE = float4( 0.102007, 0.114473, 0.139651, 0.084550 ); + + // truncate the domain + gridcell = gridcell - floor(gridcell * ( 1.0 / DOMAIN )) * DOMAIN; + float4 gridcell_inc1 = step( gridcell, splat4( DOMAIN - 1.5 ) ) * ( gridcell + 1.0 ); + + // calculate the noise + gridcell = ( gridcell * SCALE ) + OFFSET; + gridcell_inc1 = ( gridcell_inc1 * SCALE ) + OFFSET; + gridcell *= gridcell; + gridcell_inc1 *= gridcell_inc1; + + float4 x0y0_x1y0_x0y1_x1y1 = float4( gridcell.x, gridcell_inc1.x, gridcell.x, gridcell_inc1.x ) * float4( gridcell.yy, gridcell_inc1.yy ); + float4 z0w0_z1w0_z0w1_z1w1 = float4( gridcell.z, gridcell_inc1.z, gridcell.z, gridcell_inc1.z ) * float4( gridcell.ww, gridcell_inc1.ww ); + + float4 hashval = x0y0_x1y0_x0y1_x1y1 * z0w0_z1w0_z0w1_z1w1.xxxx; + z0w0_hash_0 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.x ) ); + z0w0_hash_1 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.y ) ); + z0w0_hash_2 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.z ) ); + z0w0_hash_3 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.w ) ); + hashval = x0y0_x1y0_x0y1_x1y1 * z0w0_z1w0_z0w1_z1w1.yyyy; + z1w0_hash_0 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.x ) ); + z1w0_hash_1 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.y ) ); + z1w0_hash_2 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.z ) ); + z1w0_hash_3 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.w ) ); + hashval = x0y0_x1y0_x0y1_x1y1 * z0w0_z1w0_z0w1_z1w1.zzzz; + z0w1_hash_0 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.x ) ); + z0w1_hash_1 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.y ) ); + z0w1_hash_2 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.z ) ); + z0w1_hash_3 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.w ) ); + hashval = x0y0_x1y0_x0y1_x1y1 * z0w0_z1w0_z0w1_z1w1.wwww; + z1w1_hash_0 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.x ) ); + z1w1_hash_1 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.y ) ); + z1w1_hash_2 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.z ) ); + z1w1_hash_3 = frac( hashval * ( 1.0 / SOMELARGEFLOATS.w ) ); + } + + + // + // Interpolation functions + // ( smoothly increase from 0.0 to 1.0 as x increases linearly from 0.0 to 1.0 ) + // http://briansharpe.wordpress.com/2011/11/14/two-useful-interpolation-functions-for-noise-development/ + // + float Interpolation_C1( float x ) { return x * x * (3.0 - 2.0 * x); } // 3x^2-2x^3 ( Hermine Curve. Same as SmoothStep(). As used by Perlin in Original Noise. ) + float2 Interpolation_C1( float2 x ) { return x * x * (3.0 - 2.0 * x); } + float3 Interpolation_C1( float3 x ) { return x * x * (3.0 - 2.0 * x); } + float4 Interpolation_C1( float4 x ) { return x * x * (3.0 - 2.0 * x); } + + float Interpolation_C2( float x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } // 6x^5-15x^4+10x^3 ( Quintic Curve. As used by Perlin in Improved Noise. http://mrl.nyu.edu/~perlin/paper445.pdf ) + float2 Interpolation_C2( float2 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } + float3 Interpolation_C2( float3 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } + float4 Interpolation_C2( float4 x ) { return x * x * x * (x * (x * 6.0 - 15.0) + 10.0); } + float4 Interpolation_C2_InterpAndDeriv( float2 x ) { return x.xyxy * x.xyxy * ( x.xyxy * ( x.xyxy * ( x.xyxy * float2( 6.0, 0.0 ).xxyy + float2( -15.0, 30.0 ).xxyy ) + float2( 10.0, -60.0 ).xxyy ) + float2( 0.0, 30.0 ).xxyy ); } + float3 Interpolation_C2_Deriv( float3 x ) { return x * x * (x * (x * 30.0 - 60.0) + 30.0); } + + float Interpolation_C2_Fast( float x ) { float x3 = x*x*x; return ( 7.0 + ( x3 - 7.0 ) * x ) * x3; } // 7x^3-7x^4+x^7 ( Faster than Perlin Quintic. Not quite as good shape. ) + float2 Interpolation_C2_Fast( float2 x ) { float2 x3 = x*x*x; return ( 7.0 + ( x3 - 7.0 ) * x ) * x3; } + float3 Interpolation_C2_Fast( float3 x ) { float3 x3 = x*x*x; return ( 7.0 + ( x3 - 7.0 ) * x ) * x3; } + float4 Interpolation_C2_Fast( float4 x ) { float4 x3 = x*x*x; return ( 7.0 + ( x3 - 7.0 ) * x ) * x3; } + + float Interpolation_C3( float x ) { float xsq = x*x; float xsqsq = xsq*xsq; return xsqsq * ( 25.0 - 48.0 * x + xsq * ( 25.0 - xsqsq ) ); } // 25x^4-48x^5+25x^6-x^10 ( C3 Interpolation function. If anyone ever needs it... :) ) + float2 Interpolation_C3( float2 x ) { float2 xsq = x*x; float2 xsqsq = xsq*xsq; return xsqsq * ( 25.0 - 48.0 * x + xsq * ( 25.0 - xsqsq ) ); } + float3 Interpolation_C3( float3 x ) { float3 xsq = x*x; float3 xsqsq = xsq*xsq; return xsqsq * ( 25.0 - 48.0 * x + xsq * ( 25.0 - xsqsq ) ); } + float4 Interpolation_C3( float4 x ) { float4 xsq = x*x; float4 xsqsq = xsq*xsq; return xsqsq * ( 25.0 - 48.0 * x + xsq * ( 25.0 - xsqsq ) ); } + + + // + // Falloff defined in XSquared + // ( smoothly decrease from 1.0 to 0.0 as xsq increases from 0.0 to 1.0 ) + // http://briansharpe.wordpress.com/2011/11/14/two-useful-interpolation-functions-for-noise-development/ + // + float Falloff_Xsq_C1( float xsq ) { xsq = 1.0 - xsq; return xsq*xsq; } // ( 1.0 - x*x )^2 ( Used by Humus for lighting falloff in Just Cause 2. GPUPro 1 ) + float Falloff_Xsq_C2( float xsq ) { xsq = 1.0 - xsq; return xsq*xsq*xsq; } // ( 1.0 - x*x )^3. NOTE: 2nd derivative is 0.0 at x=1.0, but non-zero at x=0.0 + float4 Falloff_Xsq_C2( float4 xsq ) { xsq = 1.0 - xsq; return xsq*xsq*xsq; } + + // + // Value Noise 2D + // Return value range of 0.0->1.0 + // http://briansharpe.files.wordpress.com/2011/11/valuesample1.jpg + // + float Value2D( float2 P ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash = FAST32_hash_2D( Pi ); + //float4 hash = FAST32_2_hash_2D( Pi ); + //float4 hash = BBS_hash_2D( Pi ); + //float4 hash = SGPP_hash_2D( Pi ); + //float4 hash = BBS_hash_hq_2D( Pi ); + + // blend the results and return + float2 blend = Interpolation_C2( Pf ); + float4 blend2 = float4( blend, float2( 1.0 - blend ) ); + return dot( hash, blend2.zxzx * blend2.wwyy ); + } + + // + // Value Noise 3D + // Return value range of 0.0->1.0 + // http://briansharpe.files.wordpress.com/2011/11/valuesample1.jpg + // + float Value3D( float3 P ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_lowz, hash_highz; + FAST32_hash_3D( Pi, hash_lowz, hash_highz ); + //FAST32_2_hash_3D( Pi, hash_lowz, hash_highz ); + //BBS_hash_3D( Pi, hash_lowz, hash_highz ); + //SGPP_hash_3D( Pi, hash_lowz, hash_highz ); + + // blend the results and return + float3 blend = Interpolation_C2( Pf ); + float4 res0 = lerp( hash_lowz, hash_highz, blend.z ); + float4 blend2 = float4( blend.xy, float2( 1.0 - blend.xy ) ); + return dot( res0, blend2.zxzx * blend2.wwyy ); + } + + // + // Value Noise 4D + // Return value range of 0.0->1.0 + // + float Value4D( float4 P ) + { + // establish our grid cell and unit position + float4 Pi = floor(P); + float4 Pf = P - Pi; + + // calculate the hash. + float4 z0w0_hash; // float4 == ( x0y0, x1y0, x0y1, x1y1 ) + float4 z1w0_hash; + float4 z0w1_hash; + float4 z1w1_hash; + FAST32_2_hash_4D( Pi, z0w0_hash, z1w0_hash, z0w1_hash, z1w1_hash ); + + // blend the results and return + float4 blend = Interpolation_C2( Pf ); + float4 res0 = z0w0_hash + ( z0w1_hash - z0w0_hash ) * blend.wwww; + float4 res1 = z1w0_hash + ( z1w1_hash - z1w0_hash ) * blend.wwww; + res0 = res0 + ( res1 - res0 ) * blend.zzzz; + blend.zw = float2( 1.0 - blend.xy ); + return dot( res0, blend.zxzx * blend.wwyy ); + } + + // + // Perlin Noise 2D ( gradient noise ) + // Return value range of -1.0->1.0 + // http://briansharpe.files.wordpress.com/2011/11/perlinsample.jpg + // + float Perlin2D( float2 P ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float4 Pf_Pfmin1 = P.xyxy - float4( Pi, Pi + 1.0 ); + + #if 1 + // + // classic noise looks much better than improved noise in 2D, and with an efficent hash function runs at about the same speed. + // requires 2 random numbers per point. + // + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y; + FAST32_hash_2D( Pi, hash_x, hash_y ); + //SGPP_hash_2D( Pi, hash_x, hash_y ); + + // calculate the gradient results + float4 grad_x = hash_x - 0.49999; + float4 grad_y = hash_y - 0.49999; + float4 grad_results = rsqrt( grad_x * grad_x + grad_y * grad_y ) * ( grad_x * Pf_Pfmin1.xzxz + grad_y * Pf_Pfmin1.yyww ); + + #if 1 + // Classic Perlin Interpolation + grad_results *= 1.4142135623730950488016887242097; // (optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.5) + float2 blend = Interpolation_C2( Pf_Pfmin1.xy ); + float4 blend2 = float4( blend, float2( 1.0 - blend ) ); + return dot( grad_results, blend2.zxzx * blend2.wwyy ); + #else + // Classic Perlin Surflet + // http://briansharpe.wordpress.com/2012/03/09/modifications-to-classic-perlin-noise/ + grad_results *= 2.3703703703703703703703703703704; // (optionally) scale things to a strict -1.0->1.0 range *= 1.0/cube(0.75) + float4 vecs_len_sq = Pf_Pfmin1 * Pf_Pfmin1; + vecs_len_sq = vecs_len_sq.xzxz + vecs_len_sq.yyww; + return dot( Falloff_Xsq_C2( min( float4( 1.0 ), vecs_len_sq ) ), grad_results ); + #endif + + #else + // + // 2D improved perlin noise. + // requires 1 random value per point. + // does not look as good as classic in 2D due to only a small number of possible cell types. But can run a lot faster than classic perlin noise if the hash function is slow + // + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash = FAST32_hash_2D( Pi ); + //float4 hash = BBS_hash_2D( Pi ); + //float4 hash = SGPP_hash_2D( Pi ); + //float4 hash = BBS_hash_hq_2D( Pi ); + // + // evaulate the gradients + // choose between the 4 diagonal gradients. ( slightly slower than choosing the axis gradients, but shows less grid artifacts ) + // NOTE: diagonals give us a nice strict -1.0->1.0 range without additional scaling + // [1.0,1.0] [-1.0,1.0] [1.0,-1.0] [-1.0,-1.0] + // + hash -= 0.5; + float4 grad_results = Pf_Pfmin1.xzxz * sign( hash ) + Pf_Pfmin1.yyww * sign( abs( hash ) - 0.25 ); + // blend the results and return + float2 blend = Interpolation_C2( Pf_Pfmin1.xy ); + float4 blend2 = float4( blend, float2( 1.0 - blend ) ); + return dot( grad_results, blend2.zxzx * blend2.wwyy ); + #endif + + } + + // + // Perlin Noise 3D ( gradient noise ) + // Return value range of -1.0->1.0 + // http://briansharpe.files.wordpress.com/2011/11/perlinsample.jpg + // + float Perlin3D( float3 P ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + float3 Pf_min1 = Pf - 1.0; + + #if 1 + // + // classic noise. + // requires 3 random values per point. with an efficent hash function will run faster than improved noise + // + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1; + FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 ); + //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 ); + + // calculate the gradients + float4 grad_x0 = hashx0 - 0.49999; + float4 grad_y0 = hashy0 - 0.49999; + float4 grad_z0 = hashz0 - 0.49999; + float4 grad_x1 = hashx1 - 0.49999; + float4 grad_y1 = hashy1 - 0.49999; + float4 grad_z1 = hashz1 - 0.49999; + float4 grad_results_0 = rsqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 ); + float4 grad_results_1 = rsqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 ); + + #if 1 + // Classic Perlin Interpolation + float3 blend = Interpolation_C2( Pf ); + float4 res0 = lerp( grad_results_0, grad_results_1, blend.z ); + float4 blend2 = float4( blend.xy, float2( 1.0 - blend.xy ) ); + float final = dot( res0, blend2.zxzx * blend2.wwyy ); + final *= 1.1547005383792515290182975610039; // (optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75) + return final; + #else + // Classic Perlin Surflet + // http://briansharpe.wordpress.com/2012/03/09/modifications-to-classic-perlin-noise/ + Pf *= Pf; + Pf_min1 *= Pf_min1; + float4 vecs_len_sq = float4( Pf.x, Pf_min1.x, Pf.x, Pf_min1.x ) + float4( Pf.yy, Pf_min1.yy ); + float final = dot( Falloff_Xsq_C2( min( float4( 1.0 ), vecs_len_sq + Pf.zzzz ) ), grad_results_0 ) + dot( Falloff_Xsq_C2( min( float4( 1.0 ), vecs_len_sq + Pf_min1.zzzz ) ), grad_results_1 ); + final *= 2.3703703703703703703703703703704; // (optionally) scale things to a strict -1.0->1.0 range *= 1.0/cube(0.75) + return final; + #endif + + #else + // + // improved noise. + // requires 1 random value per point. Will run faster than classic noise if a slow hashing function is used + // + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_lowz, hash_highz; + FAST32_hash_3D( Pi, hash_lowz, hash_highz ); + //BBS_hash_3D( Pi, hash_lowz, hash_highz ); + //SGPP_hash_3D( Pi, hash_lowz, hash_highz ); + // + // "improved" noise using 8 corner gradients. Faster than the 12 mid-edge point method. + // Ken mentions using diagonals like this can cause "clumping", but we'll live with that. + // [1,1,1] [-1,1,1] [1,-1,1] [-1,-1,1] + // [1,1,-1] [-1,1,-1] [1,-1,-1] [-1,-1,-1] + // + hash_lowz -= 0.5; + float4 grad_results_0_0 = float2( Pf.x, Pf_min1.x ).xyxy * sign( hash_lowz ); + hash_lowz = abs( hash_lowz ) - 0.25; + float4 grad_results_0_1 = float2( Pf.y, Pf_min1.y ).xxyy * sign( hash_lowz ); + float4 grad_results_0_2 = Pf.zzzz * sign( abs( hash_lowz ) - 0.125 ); + float4 grad_results_0 = grad_results_0_0 + grad_results_0_1 + grad_results_0_2; + hash_highz -= 0.5; + float4 grad_results_1_0 = float2( Pf.x, Pf_min1.x ).xyxy * sign( hash_highz ); + hash_highz = abs( hash_highz ) - 0.25; + float4 grad_results_1_1 = float2( Pf.y, Pf_min1.y ).xxyy * sign( hash_highz ); + float4 grad_results_1_2 = Pf_min1.zzzz * sign( abs( hash_highz ) - 0.125 ); + float4 grad_results_1 = grad_results_1_0 + grad_results_1_1 + grad_results_1_2; + // blend the gradients and return + float3 blend = Interpolation_C2( Pf ); + float4 res0 = lerp( grad_results_0, grad_results_1, blend.z ); + float4 blend2 = float4( blend.xy, float2( 1.0 - blend.xy ) ); + return dot( res0, blend2.zxzx * blend2.wwyy ) * (2.0 / 3.0); // (optionally) mult by (2.0/3.0) to scale to a strict -1.0->1.0 range + #endif + + } + + + // + // Perlin Noise 4D ( gradient noise ) + // Return value range of -1.0->1.0 + // + float Perlin4D( float4 P ) + { + // establish our grid cell and unit position + float4 Pi = floor(P); + float4 Pf = P - Pi; + float4 Pf_min1 = Pf - 1.0; + + // calculate the hash. + float4 lowz_loww_hash_0, lowz_loww_hash_1, lowz_loww_hash_2, lowz_loww_hash_3; + float4 highz_loww_hash_0, highz_loww_hash_1, highz_loww_hash_2, highz_loww_hash_3; + float4 lowz_highw_hash_0, lowz_highw_hash_1, lowz_highw_hash_2, lowz_highw_hash_3; + float4 highz_highw_hash_0, highz_highw_hash_1, highz_highw_hash_2, highz_highw_hash_3; + FAST32_2_hash_4D( + Pi, + lowz_loww_hash_0, lowz_loww_hash_1, lowz_loww_hash_2, lowz_loww_hash_3, + highz_loww_hash_0, highz_loww_hash_1, highz_loww_hash_2, highz_loww_hash_3, + lowz_highw_hash_0, lowz_highw_hash_1, lowz_highw_hash_2, lowz_highw_hash_3, + highz_highw_hash_0, highz_highw_hash_1, highz_highw_hash_2, highz_highw_hash_3 ); + + // calculate the gradients + lowz_loww_hash_0 -= 0.49999; + lowz_loww_hash_1 -= 0.49999; + lowz_loww_hash_2 -= 0.49999; + lowz_loww_hash_3 -= 0.49999; + highz_loww_hash_0 -= 0.49999; + highz_loww_hash_1 -= 0.49999; + highz_loww_hash_2 -= 0.49999; + highz_loww_hash_3 -= 0.49999; + lowz_highw_hash_0 -= 0.49999; + lowz_highw_hash_1 -= 0.49999; + lowz_highw_hash_2 -= 0.49999; + lowz_highw_hash_3 -= 0.49999; + highz_highw_hash_0 -= 0.49999; + highz_highw_hash_1 -= 0.49999; + highz_highw_hash_2 -= 0.49999; + highz_highw_hash_3 -= 0.49999; + + float4 grad_results_lowz_loww = rsqrt( lowz_loww_hash_0 * lowz_loww_hash_0 + lowz_loww_hash_1 * lowz_loww_hash_1 + lowz_loww_hash_2 * lowz_loww_hash_2 + lowz_loww_hash_3 * lowz_loww_hash_3 ); + grad_results_lowz_loww *= ( float2( Pf.x, Pf_min1.x ).xyxy * lowz_loww_hash_0 + float2( Pf.y, Pf_min1.y ).xxyy * lowz_loww_hash_1 + Pf.zzzz * lowz_loww_hash_2 + Pf.wwww * lowz_loww_hash_3 ); + + float4 grad_results_highz_loww = rsqrt( highz_loww_hash_0 * highz_loww_hash_0 + highz_loww_hash_1 * highz_loww_hash_1 + highz_loww_hash_2 * highz_loww_hash_2 + highz_loww_hash_3 * highz_loww_hash_3 ); + grad_results_highz_loww *= ( float2( Pf.x, Pf_min1.x ).xyxy * highz_loww_hash_0 + float2( Pf.y, Pf_min1.y ).xxyy * highz_loww_hash_1 + Pf_min1.zzzz * highz_loww_hash_2 + Pf.wwww * highz_loww_hash_3 ); + + float4 grad_results_lowz_highw = rsqrt( lowz_highw_hash_0 * lowz_highw_hash_0 + lowz_highw_hash_1 * lowz_highw_hash_1 + lowz_highw_hash_2 * lowz_highw_hash_2 + lowz_highw_hash_3 * lowz_highw_hash_3 ); + grad_results_lowz_highw *= ( float2( Pf.x, Pf_min1.x ).xyxy * lowz_highw_hash_0 + float2( Pf.y, Pf_min1.y ).xxyy * lowz_highw_hash_1 + Pf.zzzz * lowz_highw_hash_2 + Pf_min1.wwww * lowz_highw_hash_3 ); + + float4 grad_results_highz_highw = rsqrt( highz_highw_hash_0 * highz_highw_hash_0 + highz_highw_hash_1 * highz_highw_hash_1 + highz_highw_hash_2 * highz_highw_hash_2 + highz_highw_hash_3 * highz_highw_hash_3 ); + grad_results_highz_highw *= ( float2( Pf.x, Pf_min1.x ).xyxy * highz_highw_hash_0 + float2( Pf.y, Pf_min1.y ).xxyy * highz_highw_hash_1 + Pf_min1.zzzz * highz_highw_hash_2 + Pf_min1.wwww * highz_highw_hash_3 ); + + // Classic Perlin Interpolation + float4 blend = Interpolation_C2( Pf ); + float4 res0 = grad_results_lowz_loww + ( grad_results_lowz_highw - grad_results_lowz_loww ) * blend.wwww; + float4 res1 = grad_results_highz_loww + ( grad_results_highz_highw - grad_results_highz_loww ) * blend.wwww; + res0 = res0 + ( res1 - res0 ) * blend.zzzz; + blend.zw = splat2( 1.0 ) - blend.xy; + return dot( res0, blend.zxzx * blend.wwyy ); + } + + + // + // ValuePerlin Noise 2D ( value gradient noise ) + // A uniform blend between value and perlin noise + // Return value range of -1.0->1.0 + // http://briansharpe.files.wordpress.com/2011/11/valueperlinsample.jpg + // + float ValuePerlin2D( float2 P, float blend_val ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float4 Pf_Pfmin1 = P.xyxy - float4( Pi, Pi + 1.0 ); + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_value, hash_x, hash_y; + FAST32_hash_2D( Pi, hash_value, hash_x, hash_y ); + + // calculate the gradient results + float4 grad_x = hash_x - 0.49999; + float4 grad_y = hash_y - 0.49999; + float4 grad_results = rsqrt( grad_x * grad_x + grad_y * grad_y ) * ( grad_x * Pf_Pfmin1.xzxz + grad_y * Pf_Pfmin1.yyww ); + grad_results *= 1.4142135623730950488016887242097; // scale the perlin component to a -1.0->1.0 range *= 1.0/sqrt(0.5) + grad_results = lerp( (hash_value * 2.0 - 1.0), grad_results, blend_val ); + + // blend the results and return + float2 blend = Interpolation_C2( Pf_Pfmin1.xy ); + float4 blend2 = float4( blend, float2( 1.0 - blend ) ); + return dot( grad_results, blend2.zxzx * blend2.wwyy ); + } + + + // + // ValuePerlin Noise 3D ( value gradient noise ) + // A uniform blend between value and perlin noise + // Return value range of -1.0->1.0 + // http://briansharpe.files.wordpress.com/2011/11/valueperlinsample.jpg + // + float ValuePerlin3D( float3 P, float blend_val ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + float3 Pf_min1 = Pf - 1.0; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_value0, hashx0, hashy0, hashz0, hash_value1, hashx1, hashy1, hashz1; + FAST32_hash_3D( Pi, hash_value0, hashx0, hashy0, hashz0, hash_value1, hashx1, hashy1, hashz1 ); + + // calculate the gradients + float4 grad_x0 = hashx0 - 0.49999; + float4 grad_y0 = hashy0 - 0.49999; + float4 grad_z0 = hashz0 - 0.49999; + float4 grad_x1 = hashx1 - 0.49999; + float4 grad_y1 = hashy1 - 0.49999; + float4 grad_z1 = hashz1 - 0.49999; + float4 grad_results_0 = rsqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 ); + float4 grad_results_1 = rsqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 ); + grad_results_0 *= 1.1547005383792515290182975610039; // scale the perlin component to a -1.0->1.0 range *= 1.0/sqrt(0.75) + grad_results_1 *= 1.1547005383792515290182975610039; + grad_results_0 = lerp( (hash_value0 * 2.0 - 1.0), grad_results_0, blend_val ); + grad_results_1 = lerp( (hash_value1 * 2.0 - 1.0), grad_results_1, blend_val ); + + // blend the gradients and return + float3 blend = Interpolation_C2( Pf ); + float4 res0 = lerp( grad_results_0, grad_results_1, blend.z ); + float4 blend2 = float4( blend.xy, float2( 1.0 - blend.xy ) ); + return dot( res0, blend2.zxzx * blend2.wwyy ); + } + + + // + // Cubist Noise 2D + // http://briansharpe.files.wordpress.com/2011/12/cubistsample.jpg + // + // Generates a noise which resembles a cubist-style painting pattern. Final Range 0.0->1.0 + // NOTE: contains discontinuities. best used only for texturing. + // NOTE: Any serious game implementation should hard-code these parameter values for efficiency. + // + float Cubist2D( float2 P, float2 range_clamp ) // range_clamp.x = low, range_clamp.y = 1.0/(high-low). suggest value low=-2.0 high=1.0 + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float4 Pf_Pfmin1 = P.xyxy - float4( Pi, Pi + 1.0 ); + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y, hash_value; + FAST32_hash_2D( Pi, hash_x, hash_y, hash_value ); + + // calculate the gradient results + float4 grad_x = hash_x - 0.49999; + float4 grad_y = hash_y - 0.49999; + float4 grad_results = rsqrt( grad_x * grad_x + grad_y * grad_y ) * ( grad_x * Pf_Pfmin1.xzxz + grad_y * Pf_Pfmin1.yyww ); + + // invert the gradient to convert from perlin to cubist + grad_results = ( hash_value - 0.5 ) * ( 1.0 / grad_results ); + + // blend the results and return + float2 blend = Interpolation_C2( Pf_Pfmin1.xy ); + float4 blend2 = float4( blend, float2( 1.0 - blend ) ); + float final = dot( grad_results, blend2.zxzx * blend2.wwyy ); + + // the 1.0/grad calculation pushes the result to a possible to +-infinity. Need to clamp to keep things sane + return clamp( ( final - range_clamp.x ) * range_clamp.y, 0.0, 1.0 ); + //return smoothstep( 0.0, 1.0, ( final - range_clamp.x ) * range_clamp.y ); // experiments. smoothstep doesn't look as good, but does remove some discontinuities.... + } + + + // + // Cubist Noise 3D + // http://briansharpe.files.wordpress.com/2011/12/cubistsample.jpg + // + // Generates a noise which resembles a cubist-style painting pattern. Final Range 0.0->1.0 + // NOTE: contains discontinuities. best used only for texturing. + // NOTE: Any serious game implementation should hard-code these parameter values for efficiency. + // + float Cubist3D( float3 P, float2 range_clamp ) // range_clamp.x = low, range_clamp.y = 1.0/(high-low). suggest value low=-2.0 high=1.0 + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + float3 Pf_min1 = Pf - 1.0; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hashx0, hashy0, hashz0, hash_value0, hashx1, hashy1, hashz1, hash_value1; + FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hash_value0, hashx1, hashy1, hashz1, hash_value1 ); + + // calculate the gradients + float4 grad_x0 = hashx0 - 0.49999; + float4 grad_y0 = hashy0 - 0.49999; + float4 grad_z0 = hashz0 - 0.49999; + float4 grad_x1 = hashx1 - 0.49999; + float4 grad_y1 = hashy1 - 0.49999; + float4 grad_z1 = hashz1 - 0.49999; + float4 grad_results_0 = rsqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0 ); + float4 grad_results_1 = rsqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ) * ( float2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1 ); + + // invert the gradient to convert from perlin to cubist + grad_results_0 = ( hash_value0 - 0.5 ) * ( 1.0 / grad_results_0 ); + grad_results_1 = ( hash_value1 - 0.5 ) * ( 1.0 / grad_results_1 ); + + // blend the gradients and return + float3 blend = Interpolation_C2( Pf ); + float4 res0 = lerp( grad_results_0, grad_results_1, blend.z ); + float4 blend2 = float4( blend.xy, float2( 1.0 - blend.xy ) ); + float final = dot( res0, blend2.zxzx * blend2.wwyy ); + + // the 1.0/grad calculation pushes the result to a possible to +-infinity. Need to clamp to keep things sane + return clamp( ( final - range_clamp.x ) * range_clamp.y, 0.0, 1.0 ); + //return smoothstep( 0.0, 1.0, ( final - range_clamp.x ) * range_clamp.y ); // experiments. smoothstep doesn't look as good, but does remove some discontinuities.... + } + + + // convert a 0.0->1.0 sample to a -1.0->1.0 sample weighted towards the extremes + float4 Cellular_weight_samples( float4 samples ) + { + samples = samples * 2.0 - 1.0; + //return (1.0 - samples * samples) * sign(samples); // square + return (samples * samples * samples) - sign(samples); // cubic (even more variance) + } + + // + // Cellular Noise 2D + // Based off Stefan Gustavson's work at http://www.itn.liu.se/~stegu/GLSL-cellular + // http://briansharpe.files.wordpress.com/2011/12/cellularsample.jpg + // + // Speed up by using 2x2 search window instead of 3x3 + // produces a range of 0.0->1.0 + // + float Cellular2D(float2 P) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y; + FAST32_hash_2D( Pi, hash_x, hash_y ); + //SGPP_hash_2D( Pi, hash_x, hash_y ); + + // generate the 4 random points + #if 1 + // restrict the random point offset to eliminate artifacts + // we'll improve the variance of the noise by pushing the points to the extremes of the jitter window + const float JITTER_WINDOW = 0.25; // 0.25 will guarentee no artifacts. 0.25 is the intersection on x of graphs f(x)=( (0.5+(0.5-x))^2 + (0.5-x)^2 ) and f(x)=( (0.5+x)^2 + x^2 ) + hash_x = Cellular_weight_samples( hash_x ) * JITTER_WINDOW + float4(0.0, 1.0, 0.0, 1.0); + hash_y = Cellular_weight_samples( hash_y ) * JITTER_WINDOW + float4(0.0, 0.0, 1.0, 1.0); + #else + // non-weighted jitter window. jitter window of 0.4 will give results similar to Stefans original implementation + // nicer looking, faster, but has minor artifacts. ( discontinuities in signal ) + const float JITTER_WINDOW = 0.4; + hash_x = hash_x * JITTER_WINDOW * 2.0 + float4(-JITTER_WINDOW, 1.0-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW); + hash_y = hash_y * JITTER_WINDOW * 2.0 + float4(-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW); + #endif + + // return the closest squared distance + float4 dx = Pf.xxxx - hash_x; + float4 dy = Pf.yyyy - hash_y; + float4 d = dx * dx + dy * dy; + d.xy = min(d.xy, d.zw); + return min(d.x, d.y) * ( 1.0 / 1.125 ); // scale return value from 0.0->1.125 to 0.0->1.0 ( 0.75^2 * 2.0 == 1.125 ) + } + + + + // + // Cellular Noise 3D + // Based off Stefan Gustavson's work at http://www.itn.liu.se/~stegu/GLSL-cellular + // http://briansharpe.files.wordpress.com/2011/12/cellularsample.jpg + // + // Speed up by using 2x2x2 search window instead of 3x3x3 + // produces range of 0.0->1.0 + // + float Cellular3D(float3 P) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1; + FAST32_hash_3D( Pi, hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1 ); + //SGPP_hash_3D( Pi, hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1 ); + + // generate the 8 random points + #if 1 + // restrict the random point offset to eliminate artifacts + // we'll improve the variance of the noise by pushing the points to the extremes of the jitter window + const float JITTER_WINDOW = 0.166666666; // 0.166666666 will guarentee no artifacts. It is the intersection on x of graphs f(x)=( (0.5 + (0.5-x))^2 + 2*((0.5-x)^2) ) and f(x)=( 2 * (( 0.5 + x )^2) + x * x ) + hash_x0 = Cellular_weight_samples( hash_x0 ) * JITTER_WINDOW + float4(0.0, 1.0, 0.0, 1.0); + hash_y0 = Cellular_weight_samples( hash_y0 ) * JITTER_WINDOW + float4(0.0, 0.0, 1.0, 1.0); + hash_x1 = Cellular_weight_samples( hash_x1 ) * JITTER_WINDOW + float4(0.0, 1.0, 0.0, 1.0); + hash_y1 = Cellular_weight_samples( hash_y1 ) * JITTER_WINDOW + float4(0.0, 0.0, 1.0, 1.0); + hash_z0 = Cellular_weight_samples( hash_z0 ) * JITTER_WINDOW + float4(0.0, 0.0, 0.0, 0.0); + hash_z1 = Cellular_weight_samples( hash_z1 ) * JITTER_WINDOW + float4(1.0, 1.0, 1.0, 1.0); + #else + // non-weighted jitter window. jitter window of 0.4 will give results similar to Stefans original implementation + // nicer looking, faster, but has minor artifacts. ( discontinuities in signal ) + const float JITTER_WINDOW = 0.4; + hash_x0 = hash_x0 * JITTER_WINDOW * 2.0 + float4(-JITTER_WINDOW, 1.0-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW); + hash_y0 = hash_y0 * JITTER_WINDOW * 2.0 + float4(-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW); + hash_x1 = hash_x1 * JITTER_WINDOW * 2.0 + float4(-JITTER_WINDOW, 1.0-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW); + hash_y1 = hash_y1 * JITTER_WINDOW * 2.0 + float4(-JITTER_WINDOW, -JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW); + hash_z0 = hash_z0 * JITTER_WINDOW * 2.0 + float4(-JITTER_WINDOW, -JITTER_WINDOW, -JITTER_WINDOW, -JITTER_WINDOW); + hash_z1 = hash_z1 * JITTER_WINDOW * 2.0 + float4(1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW, 1.0-JITTER_WINDOW); + #endif + + // return the closest squared distance + float4 dx1 = Pf.xxxx - hash_x0; + float4 dy1 = Pf.yyyy - hash_y0; + float4 dz1 = Pf.zzzz - hash_z0; + float4 dx2 = Pf.xxxx - hash_x1; + float4 dy2 = Pf.yyyy - hash_y1; + float4 dz2 = Pf.zzzz - hash_z1; + float4 d1 = dx1 * dx1 + dy1 * dy1 + dz1 * dz1; + float4 d2 = dx2 * dx2 + dy2 * dy2 + dz2 * dz2; + d1 = min(d1, d2); + d1.xy = min(d1.xy, d1.wz); + return min(d1.x, d1.y) * ( 9.0 / 12.0 ); // scale return value from 0.0->1.333333 to 0.0->1.0 (2/3)^2 * 3 == (12/9) == 1.333333 + } + + // + // PolkaDot Noise 2D + // http://briansharpe.files.wordpress.com/2011/12/polkadotsample.jpg + // http://briansharpe.files.wordpress.com/2012/01/polkaboxsample.jpg + // TODO, these images have random intensity and random radius. This noise now has intensity as proportion to radius. Images need updated. TODO + // + // Generates a noise of smooth falloff polka dots. + // Allow for control on radius. Intensity is proportional to radius + // Return value range of 0.0->1.0 + // + float PolkaDot2D( float2 P, + float radius_low, // radius range is 0.0->1.0 + float radius_high ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash = FAST32_hash_2D_Cell( Pi ); + + // user variables + float RADIUS = max( 0.0, radius_low + hash.z * ( radius_high - radius_low ) ); + float VALUE = RADIUS / max( radius_high, radius_low ); // new keep value in proportion to radius. Behaves better when used for bumpmapping, distortion and displacement + + // calc the noise and return + RADIUS = 2.0/RADIUS; + Pf *= RADIUS; + Pf -= ( RADIUS - 1.0 ); + Pf += hash.xy * ( RADIUS - 2.0 ); + //Pf *= Pf; // this gives us a cool box looking effect + return Falloff_Xsq_C2( min( dot( Pf, Pf ), 1.0 ) ) * VALUE; + } + // PolkaDot2D_FixedRadius, PolkaDot2D_FixedValue, PolkaDot2D_FixedRadius_FixedValue TODO + + + // + // PolkaDot Noise 3D + // http://briansharpe.files.wordpress.com/2011/12/polkadotsample.jpg + // http://briansharpe.files.wordpress.com/2012/01/polkaboxsample.jpg + // TODO, these images have random intensity and random radius. This noise now has intensity as proportion to radius. Images need updated. TODO + // + // Generates a noise of smooth falloff polka dots. + // Allow for control on radius. Intensity is proportional to radius + // Return value range of 0.0->1.0 + // + float PolkaDot3D( float3 P, + float radius_low, // radius range is 0.0->1.0 + float radius_high ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + + // calculate the hash. + float4 hash = FAST32_hash_3D_Cell( Pi ); + + // user variables + float RADIUS = max( 0.0, radius_low + hash.w * ( radius_high - radius_low ) ); + float VALUE = RADIUS / max( radius_high, radius_low ); // new keep value in proportion to radius. Behaves better when used for bumpmapping, distortion and displacement + + // calc the noise and return + RADIUS = 2.0/RADIUS; + Pf *= RADIUS; + Pf -= ( RADIUS - 1.0 ); + Pf += hash.xyz * ( RADIUS - 2.0 ); + //Pf *= Pf; // this gives us a cool box looking effect + return Falloff_Xsq_C2( min( dot( Pf, Pf ), 1.0 ) ) * VALUE; + } + // PolkaDot3D_FixedRadius, PolkaDot3D_FixedValue, PolkaDot3D_FixedRadius_FixedValue TODO + + + // + // Stars2D + // http://briansharpe.files.wordpress.com/2011/12/starssample.jpg + // + // procedural texture for creating a starry background. ( looks good when combined with a nebula/space-like colour texture ) + // NOTE: Any serious game implementation should hard-code these parameter values for efficiency. + // + // Return value range of 0.0->1.0 + // + float Stars2D( float2 P, + float probability_threshold, // probability a star will be drawn ( 0.0->1.0 ) + float max_dimness, // the maximal dimness of a star ( 0.0->1.0 0.0 = all stars bright, 1.0 = maximum variation ) + float two_over_radius ) // fixed radius for the stars. radius range is 0.0->1.0 shader requires 2.0/radius as input. + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash = FAST32_hash_2D_Cell( Pi ); + //float4 hash = FAST32_hash_2D( Pi * 2.0 ); // Need to multiply by 2.0 here because we want to use all 4 corners once per cell. No sharing with other cells. It helps if the hash function has an odd domain. + //float4 hash = BBS_hash_2D( Pi * 2.0 ); + //float4 hash = SGPP_hash_2D( Pi * 2.0 ); + //float4 hash = BBS_hash_hq_2D( Pi * 2.0 ); + + // user variables + float VALUE = 1.0 - max_dimness * hash.z; + + // calc the noise and return + Pf *= two_over_radius; + Pf -= ( two_over_radius - 1.0 ); + Pf += hash.xy * ( two_over_radius - 2.0 ); + return ( hash.w < probability_threshold ) ? ( Falloff_Xsq_C1( min( dot( Pf, Pf ), 1.0 ) ) * VALUE ) : 0.0; + } + + + // + // SimplexPerlin2D ( simplex gradient noise ) + // Perlin noise over a simplex (triangular) grid + // Return value range of -1.0->1.0 + // http://briansharpe.files.wordpress.com/2012/01/simplexperlinsample.jpg + // + // Implementation originally based off Stefan Gustavson's and Ian McEwan's work at... + // http://github.com/ashima/webgl-noise + // + float SimplexPerlin2D( float2 P ) + { + // simplex math constants + const float SKEWFACTOR = 0.36602540378443864676372317075294; // 0.5*(sqrt(3.0)-1.0) + const float UNSKEWFACTOR = 0.21132486540518711774542560974902; // (3.0-sqrt(3.0))/6.0 + const float SIMPLEX_TRI_HEIGHT = 0.70710678118654752440084436210485; // sqrt( 0.5 ) height of simplex triangle + const float3 SIMPLEX_POINTS = float3( 1.0-UNSKEWFACTOR, -UNSKEWFACTOR, 1.0-2.0*UNSKEWFACTOR ); // vertex info for simplex triangle + + // establish our grid cell. + P *= SIMPLEX_TRI_HEIGHT; // scale space so we can have an approx feature size of 1.0 ( optional ) + float2 Pi = floor( P + dot( P, splat2( SKEWFACTOR ) ) ); + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y; + FAST32_hash_2D( Pi, hash_x, hash_y ); + //SGPP_hash_2D( Pi, hash_x, hash_y ); + + // establish vectors to the 3 corners of our simplex triangle + float2 v0 = Pi - dot( Pi, splat2( UNSKEWFACTOR ) ) - P; + float4 v1pos_v1hash = (v0.x < v0.y) ? float4(SIMPLEX_POINTS.xy, hash_x.y, hash_y.y) : float4(SIMPLEX_POINTS.yx, hash_x.z, hash_y.z); + float4 v12 = float4( v1pos_v1hash.xy, SIMPLEX_POINTS.zz ) + v0.xyxy; + + // calculate the dotproduct of our 3 corner vectors with 3 random normalized vectors + float3 grad_x = float3( hash_x.x, v1pos_v1hash.z, hash_x.w ) - 0.49999; + float3 grad_y = float3( hash_y.x, v1pos_v1hash.w, hash_y.w ) - 0.49999; + float3 grad_results = rsqrt( grad_x * grad_x + grad_y * grad_y ) * ( grad_x * float3( v0.x, v12.xz ) + grad_y * float3( v0.y, v12.yw ) ); + + // Normalization factor to scale the final result to a strict 1.0->-1.0 range + // x = ( sqrt( 0.5 )/sqrt( 0.75 ) ) * 0.5 + // NF = 1.0 / ( x * ( ( 0.5 – x*x ) ^ 4 ) * 2.0 ) + // http://briansharpe.wordpress.com/2012/01/13/simplex-noise/#comment-36 + const float FINAL_NORMALIZATION = 99.204334582718712976990005025589; + + // evaluate the surflet, sum and return + float3 m = float3( v0.x, v12.xz ) * float3( v0.x, v12.xz ) + float3( v0.y, v12.yw ) * float3( v0.y, v12.yw ); + m = max(0.5 - m, 0.0); // The 0.5 here is SIMPLEX_TRI_HEIGHT^2 + m = m*m; + return dot(m*m, grad_results) * FINAL_NORMALIZATION; + } + + // + // SimplexPolkaDot2D + // polkadots over a simplex (triangular) grid + // Return value range of 0.0->1.0 + // http://briansharpe.files.wordpress.com/2012/01/simplexpolkadotsample.jpg + // + float SimplexPolkaDot2D( float2 P, + float radius, // radius range is 0.0->1.0 + float max_dimness ) // the maximal dimness of a dot ( 0.0->1.0 0.0 = all dots bright, 1.0 = maximum variation ) + { + // simplex math based off Stefan Gustavson's and Ian McEwan's work at... + // http://github.com/ashima/webgl-noise + + // simplex math constants + const float SKEWFACTOR = 0.36602540378443864676372317075294; // 0.5*(sqrt(3.0)-1.0) + const float UNSKEWFACTOR = 0.21132486540518711774542560974902; // (3.0-sqrt(3.0))/6.0 + const float SIMPLEX_TRI_HEIGHT = 0.70710678118654752440084436210485; // sqrt( 0.5 ) height of simplex triangle + const float INV_SIMPLEX_TRI_HALF_EDGELEN = 2.4494897427831780981972840747059; // sqrt( 0.75 )/(2.0*sqrt( 0.5 )) + const float3 SIMPLEX_POINTS = float3( 1.0-UNSKEWFACTOR, -UNSKEWFACTOR, 1.0-2.0*UNSKEWFACTOR ); // vertex info for simplex triangle + + // establish our grid cell. + P *= SIMPLEX_TRI_HEIGHT; // scale space so we can have an approx feature size of 1.0 ( optional ) + float2 Pi = floor( P + dot( P, splat2( SKEWFACTOR ) ) ); + + // establish vectors to the 4 corners of our simplex triangle + float2 v0 = ( Pi - dot( Pi, splat2( UNSKEWFACTOR ) ) - P ); + float4 v0123_x = float4( 0.0, SIMPLEX_POINTS.xyz ) + v0.x; + float4 v0123_y = float4( 0.0, SIMPLEX_POINTS.yxz ) + v0.y; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash = FAST32_hash_2D( Pi ); + //float4 hash = BBS_hash_2D( Pi ); + //float4 hash = SGPP_hash_2D( Pi ); + //float4 hash = BBS_hash_hq_2D( Pi ); + + // apply user controls + radius = INV_SIMPLEX_TRI_HALF_EDGELEN/radius; // INV_SIMPLEX_TRI_HALF_EDGELEN here is to scale to a nice 0.0->1.0 range + v0123_x *= radius; + v0123_y *= radius; + + // return a smooth falloff from the closest point. ( we use a f(x)=(1.0-x*x)^3 falloff ) + float4 point_distance = max( splat4( 0.0 ), 1.0 - ( v0123_x*v0123_x + v0123_y*v0123_y ) ); + point_distance = point_distance*point_distance*point_distance; + return dot( 1.0 - hash * max_dimness, point_distance ); + } + + + // + // SimplexCellular2D + // cellular noise over a simplex (triangular) grid + // Return value range of 0.0->~1.0 + // http://briansharpe.files.wordpress.com/2012/01/simplexcellularsample.jpg + // + // TODO: scaling of return value to strict 0.0->1.0 range + // + float SimplexCellular2D( float2 P ) + { + // simplex math based off Stefan Gustavson's and Ian McEwan's work at... + // http://github.com/ashima/webgl-noise + + // simplex math constants + const float SKEWFACTOR = 0.36602540378443864676372317075294; // 0.5*(sqrt(3.0)-1.0) + const float UNSKEWFACTOR = 0.21132486540518711774542560974902; // (3.0-sqrt(3.0))/6.0 + const float SIMPLEX_TRI_HEIGHT = 0.70710678118654752440084436210485; // sqrt( 0.5 ) height of simplex triangle. + const float INV_SIMPLEX_TRI_HEIGHT = 1.4142135623730950488016887242097; // 1.0 / sqrt( 0.5 ) + const float3 SIMPLEX_POINTS = float3( 1.0-UNSKEWFACTOR, -UNSKEWFACTOR, 1.0-2.0*UNSKEWFACTOR ) * INV_SIMPLEX_TRI_HEIGHT; // vertex info for simplex triangle + + // establish our grid cell. + P *= SIMPLEX_TRI_HEIGHT; // scale space so we can have an approx feature size of 1.0 ( optional ) + float2 Pi = floor( P + dot( P, splat2( SKEWFACTOR ) ) ); + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y; + FAST32_hash_2D( Pi, hash_x, hash_y ); + //SGPP_hash_2D( Pi, hash_x, hash_y ); + + // push hash values to extremes of jitter window + const float JITTER_WINDOW = ( 0.10566243270259355887271280487451 * INV_SIMPLEX_TRI_HEIGHT ); // this will guarentee no artifacts. + hash_x = Cellular_weight_samples( hash_x ) * JITTER_WINDOW; + hash_y = Cellular_weight_samples( hash_y ) * JITTER_WINDOW; + + // calculate sq distance to closest point + float2 p0 = ( ( Pi - dot( Pi, splat2( UNSKEWFACTOR ) ) ) - P ) * INV_SIMPLEX_TRI_HEIGHT; + hash_x += p0.xxxx; + hash_y += p0.yyyy; + hash_x.yzw += SIMPLEX_POINTS.xyz; + hash_y.yzw += SIMPLEX_POINTS.yxz; + float4 distsq = hash_x*hash_x + hash_y*hash_y; + float2 tmp = min( distsq.xy, distsq.zw ); + return min( tmp.x, tmp.y ); + } + + // + // Given an arbitrary 3D point this calculates the 4 vectors from the corners of the simplex pyramid to the point + // It also returns the integer grid index information for the corners + // + void Simplex3D_GetCornerVectors( float3 P, // input point + out float3 Pi, // integer grid index for the origin + out float3 Pi_1, // offsets for the 2nd and 3rd corners. ( the 4th = Pi + 1.0 ) + out float3 Pi_2, + out float4 v1234_x, // vectors from the 4 corners to the intput point + out float4 v1234_y, + out float4 v1234_z ) + { + // + // Simplex math from Stefan Gustavson's and Ian McEwan's work at... + // http://github.com/ashima/webgl-noise + // + + // simplex math constants + const float SKEWFACTOR = 1.0/3.0; + const float UNSKEWFACTOR = 1.0/6.0; + const float SIMPLEX_CORNER_POS = 0.5; + const float SIMPLEX_PYRAMID_HEIGHT = 0.70710678118654752440084436210485; // sqrt( 0.5 ) height of simplex pyramid. + + P *= SIMPLEX_PYRAMID_HEIGHT; // scale space so we can have an approx feature size of 1.0 ( optional ) + + // Find the vectors to the corners of our simplex pyramid + Pi = floor( P + dot( P, splat3( SKEWFACTOR) ) ); + float3 x0 = P - Pi + dot(Pi, splat3( UNSKEWFACTOR ) ); + float3 g = step(x0.yzx, x0.xyz); + float3 l = 1.0 - g; + Pi_1 = min( g.xyz, l.zxy ); + Pi_2 = max( g.xyz, l.zxy ); + float3 x1 = x0 - Pi_1 + UNSKEWFACTOR; + float3 x2 = x0 - Pi_2 + SKEWFACTOR; + float3 x3 = x0 - SIMPLEX_CORNER_POS; + + // pack them into a parallel-friendly arrangement + v1234_x = float4( x0.x, x1.x, x2.x, x3.x ); + v1234_y = float4( x0.y, x1.y, x2.y, x3.y ); + v1234_z = float4( x0.z, x1.z, x2.z, x3.z ); + } + + // + // Calculate the weights for the 3D simplex surflet + // + float4 Simplex3D_GetSurfletWeights( float4 v1234_x, + float4 v1234_y, + float4 v1234_z ) + { + // perlins original implementation uses the surlet falloff formula of (0.6-x*x)^4. + // This is buggy as it can cause discontinuities along simplex faces. (0.5-x*x)^3 solves this and gives an almost identical curve + + // evaluate surflet. f(x)=(0.5-x*x)^3 + float4 surflet_weights = v1234_x * v1234_x + v1234_y * v1234_y + v1234_z * v1234_z; + surflet_weights = max(0.5 - surflet_weights, 0.0); // 0.5 here represents the closest distance (squared) of any simplex pyramid corner to any of its planes. ie, SIMPLEX_PYRAMID_HEIGHT^2 + return surflet_weights*surflet_weights*surflet_weights; + } + + + + // + // SimplexPerlin3D ( simplex gradient noise ) + // Perlin noise over a simplex (tetrahedron) grid + // Return value range of -1.0->1.0 + // http://briansharpe.files.wordpress.com/2012/01/simplexperlinsample.jpg + // + // Implementation originally based off Stefan Gustavson's and Ian McEwan's work at... + // http://github.com/ashima/webgl-noise + // + float SimplexPerlin3D(float3 P) + { + // calculate the simplex vector and index math + float3 Pi; + float3 Pi_1; + float3 Pi_2; + float4 v1234_x; + float4 v1234_y; + float4 v1234_z; + Simplex3D_GetCornerVectors( P, Pi, Pi_1, Pi_2, v1234_x, v1234_y, v1234_z ); + + // generate the random vectors + // ( various hashing methods listed in order of speed ) + float4 hash_0; + float4 hash_1; + float4 hash_2; + FAST32_hash_3D( Pi, Pi_1, Pi_2, hash_0, hash_1, hash_2 ); + //SGPP_hash_3D( Pi, Pi_1, Pi_2, hash_0, hash_1, hash_2 ); + hash_0 -= 0.49999; + hash_1 -= 0.49999; + hash_2 -= 0.49999; + + // evaluate gradients + float4 grad_results = rsqrt( hash_0 * hash_0 + hash_1 * hash_1 + hash_2 * hash_2 ) * ( hash_0 * v1234_x + hash_1 * v1234_y + hash_2 * v1234_z ); + + // Normalization factor to scale the final result to a strict 1.0->-1.0 range + // x = sqrt( 0.75 ) * 0.5 + // NF = 1.0 / ( x * ( ( 0.5 – x*x ) ^ 3 ) * 2.0 ) + // http://briansharpe.wordpress.com/2012/01/13/simplex-noise/#comment-36 + const float FINAL_NORMALIZATION = 37.837227241611314102871574478976; + + // sum with the surflet and return + return dot( Simplex3D_GetSurfletWeights( v1234_x, v1234_y, v1234_z ), grad_results ) * FINAL_NORMALIZATION; + } + + // + // SimplexCellular3D + // cellular noise over a simplex (tetrahedron) grid + // Return value range of 0.0->~1.0 + // http://briansharpe.files.wordpress.com/2012/01/simplexcellularsample.jpg + // + // TODO: scaling of return value to strict 0.0->1.0 range + // + float SimplexCellular3D( float3 P ) + { + // calculate the simplex vector and index math + float3 Pi; + float3 Pi_1; + float3 Pi_2; + float4 v1234_x; + float4 v1234_y; + float4 v1234_z; + Simplex3D_GetCornerVectors( P, Pi, Pi_1, Pi_2, v1234_x, v1234_y, v1234_z ); + + // generate the random vectors + // ( various hashing methods listed in order of speed ) + float4 hash_x; + float4 hash_y; + float4 hash_z; + FAST32_hash_3D( Pi, Pi_1, Pi_2, hash_x, hash_y, hash_z ); + //SGPP_hash_3D( Pi, Pi_1, Pi_2, hash_x, hash_y, hash_z ); + + // push hash values to extremes of jitter window + const float INV_SIMPLEX_PYRAMID_HEIGHT = 1.4142135623730950488016887242097; // 1.0 / sqrt( 0.5 ) This scales things so to a nice 0.0->1.0 range + const float JITTER_WINDOW = ( 0.0597865779345250670558198111 * INV_SIMPLEX_PYRAMID_HEIGHT) ; // this will guarentee no artifacts. + hash_x = Cellular_weight_samples( hash_x ) * JITTER_WINDOW; + hash_y = Cellular_weight_samples( hash_y ) * JITTER_WINDOW; + hash_z = Cellular_weight_samples( hash_z ) * JITTER_WINDOW; + + // offset the vectors. + v1234_x *= INV_SIMPLEX_PYRAMID_HEIGHT; + v1234_y *= INV_SIMPLEX_PYRAMID_HEIGHT; + v1234_z *= INV_SIMPLEX_PYRAMID_HEIGHT; + v1234_x += hash_x; + v1234_y += hash_y; + v1234_z += hash_z; + + // calc the distance^2 to the closest point + float4 distsq = v1234_x*v1234_x + v1234_y*v1234_y + v1234_z*v1234_z; + return min( min( distsq.x, distsq.y ), min( distsq.z, distsq.w ) ); + } + + + // + // SimplexPolkaDot3D + // polkadots over a simplex (tetrahedron) grid + // Return value range of 0.0->1.0 + // http://briansharpe.files.wordpress.com/2012/01/simplexpolkadotsample.jpg + // + float SimplexPolkaDot3D( float3 P, + float radius, // radius range is 0.0->1.0 + float max_dimness ) // the maximal dimness of a dot ( 0.0->1.0 0.0 = all dots bright, 1.0 = maximum variation ) + { + // calculate the simplex vector and index math + float3 Pi; + float3 Pi_1; + float3 Pi_2; + float4 v1234_x; + float4 v1234_y; + float4 v1234_z; + Simplex3D_GetCornerVectors( P, Pi, Pi_1, Pi_2, v1234_x, v1234_y, v1234_z ); + + // calculate the hash + float4 hash = FAST32_hash_3D( Pi, Pi_1, Pi_2 ); + + // apply user controls + const float INV_SIMPLEX_TRI_HALF_EDGELEN = 2.3094010767585030580365951220078; // scale to a 0.0->1.0 range. 2.0 / sqrt( 0.75 ) + radius = INV_SIMPLEX_TRI_HALF_EDGELEN/radius; + v1234_x *= radius; + v1234_y *= radius; + v1234_z *= radius; + + // return a smooth falloff from the closest point. ( we use a f(x)=(1.0-x*x)^3 falloff ) + float4 point_distance = max( splat4( 0.0 ), 1.0 - ( v1234_x*v1234_x + v1234_y*v1234_y + v1234_z*v1234_z ) ); + point_distance = point_distance*point_distance*point_distance; + return dot( 1.0 - hash * max_dimness, point_distance ); + } + + + // + // Quintic Hermite Interpolation + // http://www.rose-hulman.edu/~finn/CCLI/Notes/day09.pdf + // + // NOTE: maximum value of a hermitequintic interpolation with zero acceleration at the endpoints would be... + // f(x=0.5) = MAXPOS + MAXVELOCITY * ( ( x - 6x^3 + 8x^4 - 3x^5 ) - ( -4x^3 + 7x^4 -3x^5 ) ) = MAXPOS + MAXVELOCITY * 0.3125 + // + // variable naming conventions: + // val = value ( position ) + // grad = gradient ( velocity ) + // x = 0.0->1.0 ( time ) + // i = interpolation = a value to be interpolated + // e = evaluation = a value to be used to calculate the interpolation + // 0 = start + // 1 = end + // + float QuinticHermite( float x, float ival0, float ival1, float egrad0, float egrad1 ) // quintic hermite with start/end acceleration of 0.0 + { + const float3 C0 = float3( -15.0, 8.0, 7.0 ); + const float3 C1 = float3( 6.0, -3.0, -3.0 ); + const float3 C2 = float3( 10.0, -6.0, -4.0 ); + float3 h123 = ( ( ( C0 + C1 * x ) * x ) + C2 ) * ( x*x*x ); + return ival0 + dot( float3( (ival1 - ival0), egrad0, egrad1 ), h123.xyz + float3( 0.0, x, 0.0 ) ); + } + float4 QuinticHermite( float x, float4 ival0, float4 ival1, float4 egrad0, float4 egrad1 ) // quintic hermite with start/end acceleration of 0.0 + { + const float3 C0 = float3( -15.0, 8.0, 7.0 ); + const float3 C1 = float3( 6.0, -3.0, -3.0 ); + const float3 C2 = float3( 10.0, -6.0, -4.0 ); + float3 h123 = ( ( ( C0 + C1 * x ) * x ) + C2 ) * ( x*x*x ); + return ival0 + (ival1 - ival0) * h123.xxxx + egrad0 * splat4( h123.y + x ) + egrad1 * h123.zzzz; + } + float4 QuinticHermite( float x, float2 igrad0, float2 igrad1, float2 egrad0, float2 egrad1 ) // quintic hermite with start/end position and acceleration of 0.0 + { + const float3 C0 = float3( -15.0, 8.0, 7.0 ); + const float3 C1 = float3( 6.0, -3.0, -3.0 ); + const float3 C2 = float3( 10.0, -6.0, -4.0 ); + float3 h123 = ( ( ( C0 + C1 * x ) * x ) + C2 ) * ( x*x*x ); + return float4( egrad1, igrad0 ) * float4( h123.zz, 1.0, 1.0 ) + float4( egrad0, h123.xx ) * float4( splat2( h123.y + x ), (igrad1 - igrad0) ); // returns float4( out_ival.xy, out_igrad.xy ) + } + void QuinticHermite( float x, + float4 ival0, float4 ival1, // values are interpolated using the gradient arguments + float4 igrad_x0, float4 igrad_x1, // gradients are interpolated using eval gradients of 0.0 + float4 igrad_y0, float4 igrad_y1, + float4 egrad0, float4 egrad1, // our evaluation gradients + out float4 out_ival, out float4 out_igrad_x, out float4 out_igrad_y ) // quintic hermite with start/end acceleration of 0.0 + { + const float3 C0 = float3( -15.0, 8.0, 7.0 ); + const float3 C1 = float3( 6.0, -3.0, -3.0 ); + const float3 C2 = float3( 10.0, -6.0, -4.0 ); + float3 h123 = ( ( ( C0 + C1 * x ) * x ) + C2 ) * ( x*x*x ); + out_ival = ival0 + (ival1 - ival0) * h123.xxxx + egrad0 * splat4( h123.y + x ) + egrad1 * h123.zzzz; + out_igrad_x = igrad_x0 + (igrad_x1 - igrad_x0) * h123.xxxx; // NOTE: gradients of 0.0 + out_igrad_y = igrad_y0 + (igrad_y1 - igrad_y0) * h123.xxxx; // NOTE: gradients of 0.0 + } + void QuinticHermite( float x, + float4 igrad_x0, float4 igrad_x1, // gradients are interpolated using eval gradients of 0.0 + float4 igrad_y0, float4 igrad_y1, + float4 egrad0, float4 egrad1, // our evaluation gradients + out float4 out_ival, out float4 out_igrad_x, out float4 out_igrad_y ) // quintic hermite with start/end position and acceleration of 0.0 + { + const float3 C0 = float3( -15.0, 8.0, 7.0 ); + const float3 C1 = float3( 6.0, -3.0, -3.0 ); + const float3 C2 = float3( 10.0, -6.0, -4.0 ); + float3 h123 = ( ( ( C0 + C1 * x ) * x ) + C2 ) * ( x*x*x ); + out_ival = egrad0 * splat4( h123.y + x ) + egrad1 * h123.zzzz; + out_igrad_x = igrad_x0 + (igrad_x1 - igrad_x0) * h123.xxxx; // NOTE: gradients of 0.0 + out_igrad_y = igrad_y0 + (igrad_y1 - igrad_y0) * h123.xxxx; // NOTE: gradients of 0.0 + } + float QuinticHermiteDeriv( float x, float ival0, float ival1, float egrad0, float egrad1 ) // gives the derivative of quintic hermite with start/end acceleration of 0.0 + { + const float3 C0 = float3( 30.0, -15.0, -15.0 ); + const float3 C1 = float3( -60.0, 32.0, 28.0 ); + const float3 C2 = float3( 30.0, -18.0, -12.0 ); + float3 h123 = ( ( ( C1 + C0 * x ) * x ) + C2 ) * ( x*x ); + return dot( float3( (ival1 - ival0), egrad0, egrad1 ), h123.xyz + float3( 0.0, 1.0, 0.0 ) ); + } + + // + // Hermite2D + // Return value range of -1.0->1.0 + // http://briansharpe.files.wordpress.com/2012/01/hermitesample.jpg + // + float Hermite2D( float2 P ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_gradx, hash_grady; + FAST32_hash_2D( Pi, hash_gradx, hash_grady ); + + // scale the hash values + hash_gradx = ( hash_gradx - 0.49999); + hash_grady = ( hash_grady - 0.49999); + + #if 1 + // normalize gradients + float4 norm = rsqrt( hash_gradx * hash_gradx + hash_grady * hash_grady ); + hash_gradx *= norm; + hash_grady *= norm; + const float FINAL_NORM_VAL = 2.2627416997969520780827019587355; + #else + // unnormalized gradients + const float FINAL_NORM_VAL = 3.2; // 3.2 = 1.0 / ( 0.5 * 0.3125 * 2.0 ) + #endif + + // evaluate the hermite + float4 qh_results = QuinticHermite( Pf.y, hash_gradx.xy, hash_gradx.zw, hash_grady.xy, hash_grady.zw ); + return QuinticHermite( Pf.x, qh_results.x, qh_results.y, qh_results.z, qh_results.w ) * FINAL_NORM_VAL; + } + + // + // Hermite3D + // Return value range of -1.0->1.0 + // http://briansharpe.files.wordpress.com/2012/01/hermitesample.jpg + // + float Hermite3D( float3 P ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_gradx0, hash_grady0, hash_gradz0, hash_gradx1, hash_grady1, hash_gradz1; + FAST32_hash_3D( Pi, hash_gradx0, hash_grady0, hash_gradz0, hash_gradx1, hash_grady1, hash_gradz1 ); + + // scale the hash values + hash_gradx0 = ( hash_gradx0 - 0.49999); + hash_grady0 = ( hash_grady0 - 0.49999); + hash_gradz0 = ( hash_gradz0 - 0.49999); + hash_gradx1 = ( hash_gradx1 - 0.49999); + hash_grady1 = ( hash_grady1 - 0.49999); + hash_gradz1 = ( hash_gradz1 - 0.49999); + + #if 1 + // normalize gradients + float4 norm0 = rsqrt( hash_gradx0 * hash_gradx0 + hash_grady0 * hash_grady0 + hash_gradz0 * hash_gradz0 ); + hash_gradx0 *= norm0; + hash_grady0 *= norm0; + hash_gradz0 *= norm0; + float4 norm1 = rsqrt( hash_gradx1 * hash_gradx1 + hash_grady1 * hash_grady1 + hash_gradz1 * hash_gradz1 ); + hash_gradx1 *= norm1; + hash_grady1 *= norm1; + hash_gradz1 *= norm1; + const float FINAL_NORM_VAL = 1.8475208614068024464292760976063; + #else + // unnormalized gradients + const float FINAL_NORM_VAL = (1.0/0.46875); // = 1.0 / ( 0.5 * 0.3125 * 3.0 ) + #endif + + // evaluate the hermite + float4 ival_results, igrad_results_x, igrad_results_y; + QuinticHermite( Pf.z, hash_gradx0, hash_gradx1, hash_grady0, hash_grady1, hash_gradz0, hash_gradz1, ival_results, igrad_results_x, igrad_results_y ); + float4 qh_results = QuinticHermite( Pf.y, float4(ival_results.xy, igrad_results_x.xy), float4(ival_results.zw, igrad_results_x.zw), float4( igrad_results_y.xy, 0.0, 0.0 ), float4( igrad_results_y.zw, 0.0, 0.0 ) ); + return QuinticHermite( Pf.x, qh_results.x, qh_results.y, qh_results.z, qh_results.w ) * FINAL_NORM_VAL; + } + + // + // ValueHermite2D + // Return value range of -1.0->1.0 + // ( allows for a blend between value and hermite noise ) + // http://briansharpe.files.wordpress.com/2012/01/valuehermitesample.jpg + // + float ValueHermite2D( float2 P, + float value_scale, // value_scale = 2.0*MAXVALUE + float gradient_scale, // gradient_scale = 2.0*MAXGRADIENT + float normalization_val ) // normalization_val = ( 1.0 / ( MAXVALUE + MAXGRADIENT * 0.3125 * 2.0 ) ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_value, hash_gradx, hash_grady; + FAST32_hash_2D( Pi, hash_value, hash_gradx, hash_grady ); + + // scale the hash values + hash_gradx = ( hash_gradx - 0.49999) * gradient_scale; + hash_grady = ( hash_grady - 0.49999) * gradient_scale; // hmmm... should we normalize gradients? + hash_value = ( hash_value - 0.5) * value_scale; + + // evaluate the hermite + float4 qh_results = QuinticHermite( Pf.y, float4(hash_value.xy, hash_gradx.xy), float4(hash_value.zw, hash_gradx.zw), float4( hash_grady.xy, 0.0, 0.0 ), float4( hash_grady.zw, 0.0, 0.0 ) ); + return QuinticHermite( Pf.x, qh_results.x, qh_results.y, qh_results.z, qh_results.w ) * normalization_val; + } + + + // + // ValueHermite3D + // Return value range of -1.0->1.0 + // ( allows for a blend between value and hermite noise ) + // http://briansharpe.files.wordpress.com/2012/01/valuehermitesample.jpg + // + float ValueHermite3D( float3 P, + float value_scale, // value_scale = 2.0*MAXVALUE + float gradient_scale, // gradient_scale = 2.0*MAXGRADIENT + float normalization_val ) // normalization_val = ( 1.0 / ( MAXVALUE + MAXGRADIENT * 0.3125 * 3.0 ) ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_value0, hash_gradx0, hash_grady0, hash_gradz0, hash_value1, hash_gradx1, hash_grady1, hash_gradz1; + FAST32_hash_3D( Pi, hash_value0, hash_gradx0, hash_grady0, hash_gradz0, hash_value1, hash_gradx1, hash_grady1, hash_gradz1 ); + + // scale the hash values + hash_gradx0 = ( hash_gradx0 - 0.49999) * gradient_scale; + hash_grady0 = ( hash_grady0 - 0.49999) * gradient_scale; + hash_gradz0 = ( hash_gradz0 - 0.49999) * gradient_scale; + hash_gradx1 = ( hash_gradx1 - 0.49999) * gradient_scale; + hash_grady1 = ( hash_grady1 - 0.49999) * gradient_scale; + hash_gradz1 = ( hash_gradz1 - 0.49999) * gradient_scale; // hmmm... should we normalize gradients? + hash_value0 = ( hash_value0 - 0.5) * value_scale; + hash_value1 = ( hash_value1 - 0.5) * value_scale; + + // evaluate the hermite + float4 ival_results, igrad_results_x, igrad_results_y; + QuinticHermite( Pf.z, hash_value0, hash_value1, hash_gradx0, hash_gradx1, hash_grady0, hash_grady1, hash_gradz0, hash_gradz1, ival_results, igrad_results_x, igrad_results_y ); + float4 qh_results = QuinticHermite( Pf.y, float4(ival_results.xy, igrad_results_x.xy), float4(ival_results.zw, igrad_results_x.zw), float4( igrad_results_y.xy, 0.0, 0.0 ), float4( igrad_results_y.zw, 0.0, 0.0 ) ); + return QuinticHermite( Pf.x, qh_results.x, qh_results.y, qh_results.z, qh_results.w ) * normalization_val; + } + + + + // + // Derivative Noises + // + + // + // Value2D_Deriv + // Value2D noise with derivatives + // returns float3( value, xderiv, yderiv ) + // + float3 Value2D_Deriv( float2 P ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + float4 hash = FAST32_hash_2D( Pi ); + + // blend result and return + float4 blend = Interpolation_C2_InterpAndDeriv( Pf ); + float4 res0 = lerp( hash.xyxz, hash.zwyw, blend.yyxx ); + return float3( res0.x, 0.0, 0.0 ) + ( res0.yyw - res0.xxz ) * blend.xzw; + } + + // + // Value3D_Deriv + // Value3D noise with derivatives + // returns float4( value, xderiv, yderiv, zderiv ) + // + float4 Value3D_Deriv( float3 P ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_lowz, hash_highz; + FAST32_hash_3D( Pi, hash_lowz, hash_highz ); + + // blend the results and return + float3 blend = Interpolation_C2( Pf ); + float4 res0 = lerp( hash_lowz, hash_highz, blend.z ); + float4 res1 = lerp( res0.xyxz, res0.zwyw, blend.yyxx ); + float4 res3 = lerp( float4( hash_lowz.xy, hash_highz.xy ), float4( hash_lowz.zw, hash_highz.zw ), blend.y ); + float2 res4 = lerp( res3.xz, res3.yw, blend.x ); + return float4( res1.x, 0.0, 0.0, 0.0 ) + ( float4( res1.yyw, res4.y ) - float4( res1.xxz, res4.x ) ) * float4( blend.x, Interpolation_C2_Deriv( Pf ) ); + } + + + // + // Perlin2D_Deriv + // Classic Perlin 2D noise with derivatives + // returns float3( value, xderiv, yderiv ) + // + float3 Perlin2D_Deriv( float2 P ) + { + // https://github.com/BrianSharpe/Wombat/blob/master/Perlin2D_Deriv.glsl + + // establish our grid cell and unit position + float2 Pi = floor(P); + float4 Pf_Pfmin1 = P.xyxy - float4( Pi, Pi + 1.0 ); + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y; + FAST32_hash_2D( Pi, hash_x, hash_y ); + //SGPP_hash_2D( Pi, hash_x, hash_y ); + + // calculate the gradient results + float4 grad_x = hash_x - 0.49999; + float4 grad_y = hash_y - 0.49999; + float4 norm = rsqrt( grad_x * grad_x + grad_y * grad_y ); + grad_x *= norm; + grad_y *= norm; + float4 dotval = ( grad_x * Pf_Pfmin1.xzxz + grad_y * Pf_Pfmin1.yyww ); + + // Convert our data to a more parallel format + float3 dotval0_grad0 = float3( dotval.x, grad_x.x, grad_y.x ); + float3 dotval1_grad1 = float3( dotval.y, grad_x.y, grad_y.y ); + float3 dotval2_grad2 = float3( dotval.z, grad_x.z, grad_y.z ); + float3 dotval3_grad3 = float3( dotval.w, grad_x.w, grad_y.w ); + + // evaluate common constants + float3 k0_gk0 = dotval1_grad1 - dotval0_grad0; + float3 k1_gk1 = dotval2_grad2 - dotval0_grad0; + float3 k2_gk2 = dotval3_grad3 - dotval2_grad2 - k0_gk0; + + // C2 Interpolation + float4 blend = Interpolation_C2_InterpAndDeriv( Pf_Pfmin1.xy ); + + // calculate final noise + deriv + float3 results = dotval0_grad0 + + blend.x * k0_gk0 + + blend.y * ( k1_gk1 + blend.x * k2_gk2 ); + + results.yz += blend.zw * ( float2( k0_gk0.x, k1_gk1.x ) + blend.yx * k2_gk2.xx ); + + return results * 1.4142135623730950488016887242097; // scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.5) + } + + // + // Perlin3D_Deriv + // Classic Perlin 3D noise with derivatives + // returns float4( value, xderiv, yderiv, zderiv ) + // + float4 Perlin3D_Deriv( float3 P ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + float3 Pf_min1 = Pf - 1.0; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1; + FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 ); + //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 ); + + // calculate the gradients + float4 grad_x0 = hashx0 - 0.49999; + float4 grad_y0 = hashy0 - 0.49999; + float4 grad_z0 = hashz0 - 0.49999; + float4 grad_x1 = hashx1 - 0.49999; + float4 grad_y1 = hashy1 - 0.49999; + float4 grad_z1 = hashz1 - 0.49999; + float4 norm_0 = rsqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ); + float4 norm_1 = rsqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ); + grad_x0 *= norm_0; + grad_y0 *= norm_0; + grad_z0 *= norm_0; + grad_x1 *= norm_1; + grad_y1 *= norm_1; + grad_z1 *= norm_1; + + // calculate the dot products + float4 dotval_0 = float2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0; + float4 dotval_1 = float2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1; + + // + // NOTE: the following is based off Milo Yips derivation, but modified for parallel execution + // http://stackoverflow.com/a/14141774 + // + + // Convert our data to a more parallel format + float4 dotval0_grad0 = float4( dotval_0.x, grad_x0.x, grad_y0.x, grad_z0.x ); + float4 dotval1_grad1 = float4( dotval_0.y, grad_x0.y, grad_y0.y, grad_z0.y ); + float4 dotval2_grad2 = float4( dotval_0.z, grad_x0.z, grad_y0.z, grad_z0.z ); + float4 dotval3_grad3 = float4( dotval_0.w, grad_x0.w, grad_y0.w, grad_z0.w ); + float4 dotval4_grad4 = float4( dotval_1.x, grad_x1.x, grad_y1.x, grad_z1.x ); + float4 dotval5_grad5 = float4( dotval_1.y, grad_x1.y, grad_y1.y, grad_z1.y ); + float4 dotval6_grad6 = float4( dotval_1.z, grad_x1.z, grad_y1.z, grad_z1.z ); + float4 dotval7_grad7 = float4( dotval_1.w, grad_x1.w, grad_y1.w, grad_z1.w ); + + // evaluate common constants + float4 k0_gk0 = dotval1_grad1 - dotval0_grad0; + float4 k1_gk1 = dotval2_grad2 - dotval0_grad0; + float4 k2_gk2 = dotval4_grad4 - dotval0_grad0; + float4 k3_gk3 = dotval3_grad3 - dotval2_grad2 - k0_gk0; + float4 k4_gk4 = dotval5_grad5 - dotval4_grad4 - k0_gk0; + float4 k5_gk5 = dotval6_grad6 - dotval4_grad4 - k1_gk1; + float4 k6_gk6 = (dotval7_grad7 - dotval6_grad6) - (dotval5_grad5 - dotval4_grad4) - k3_gk3; + + // C2 Interpolation + float3 blend = Interpolation_C2( Pf ); + float3 blendDeriv = Interpolation_C2_Deriv( Pf ); + + // calculate final noise + deriv + float u = blend.x; + float v = blend.y; + float w = blend.z; + + float4 result = dotval0_grad0 + + u * ( k0_gk0 + v * k3_gk3 ) + + v * ( k1_gk1 + w * k5_gk5 ) + + w * ( k2_gk2 + u * ( k4_gk4 + v * k6_gk6 ) ); + + result.y += dot( float4( k0_gk0.x, k3_gk3.x * v, float2( k4_gk4.x, k6_gk6.x * v ) * w ), splat4( blendDeriv.x ) ); + result.z += dot( float4( k1_gk1.x, k3_gk3.x * u, float2( k5_gk5.x, k6_gk6.x * u ) * w ), splat4( blendDeriv.y ) ); + result.w += dot( float4( k2_gk2.x, k4_gk4.x * u, float2( k5_gk5.x, k6_gk6.x * u ) * v ), splat4( blendDeriv.z ) ); + + // normalize and return + result *= 1.1547005383792515290182975610039; // (optionally) scale things to a strict -1.0->1.0 range *= 1.0/sqrt(0.75) + return result; + } + + // + // PerlinSurflet2D_Deriv + // Perlin Surflet 2D noise with derivatives + // returns float3( value, xderiv, yderiv ) + // + float3 PerlinSurflet2D_Deriv( float2 P ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float4 Pf_Pfmin1 = P.xyxy - float4( Pi, Pi + 1.0 ); + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y; + FAST32_hash_2D( Pi, hash_x, hash_y ); + //SGPP_hash_2D( Pi, hash_x, hash_y ); + + // calculate the gradient results + float4 grad_x = hash_x - 0.49999; + float4 grad_y = hash_y - 0.49999; + float4 norm = rsqrt( grad_x * grad_x + grad_y * grad_y ); + grad_x *= norm; + grad_y *= norm; + float4 grad_results = grad_x * Pf_Pfmin1.xzxz + grad_y * Pf_Pfmin1.yyww; + + // eval the surflet + float4 m = Pf_Pfmin1 * Pf_Pfmin1; + m = m.xzxz + m.yyww; + m = max(1.0 - m, 0.0); + float4 m2 = m*m; + float4 m3 = m*m2; + + // calc the deriv + float4 temp = -6.0 * m2 * grad_results; + float xderiv = dot( temp, Pf_Pfmin1.xzxz ) + dot( m3, grad_x ); + float yderiv = dot( temp, Pf_Pfmin1.yyww ) + dot( m3, grad_y ); + + // sum the surflets and return all results combined in a float3 + const float FINAL_NORMALIZATION = 2.3703703703703703703703703703704; // scales the final result to a strict 1.0->-1.0 range + return float3( dot( m3, grad_results ), xderiv, yderiv ) * FINAL_NORMALIZATION; + } + + + // + // PerlinSurflet3D_Deriv + // Perlin Surflet 3D noise with derivatives + // returns float4( value, xderiv, yderiv, zderiv ) + // + float4 PerlinSurflet3D_Deriv( float3 P ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + float3 Pf_min1 = Pf - 1.0; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hashx0, hashy0, hashz0, hashx1, hashy1, hashz1; + FAST32_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 ); + //SGPP_hash_3D( Pi, hashx0, hashy0, hashz0, hashx1, hashy1, hashz1 ); + + // calculate the gradients + float4 grad_x0 = hashx0 - 0.49999; + float4 grad_y0 = hashy0 - 0.49999; + float4 grad_z0 = hashz0 - 0.49999; + float4 norm_0 = rsqrt( grad_x0 * grad_x0 + grad_y0 * grad_y0 + grad_z0 * grad_z0 ); + grad_x0 *= norm_0; + grad_y0 *= norm_0; + grad_z0 *= norm_0; + float4 grad_x1 = hashx1 - 0.49999; + float4 grad_y1 = hashy1 - 0.49999; + float4 grad_z1 = hashz1 - 0.49999; + float4 norm_1 = rsqrt( grad_x1 * grad_x1 + grad_y1 * grad_y1 + grad_z1 * grad_z1 ); + grad_x1 *= norm_1; + grad_y1 *= norm_1; + grad_z1 *= norm_1; + float4 grad_results_0 = float2( Pf.x, Pf_min1.x ).xyxy * grad_x0 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y0 + Pf.zzzz * grad_z0; + float4 grad_results_1 = float2( Pf.x, Pf_min1.x ).xyxy * grad_x1 + float2( Pf.y, Pf_min1.y ).xxyy * grad_y1 + Pf_min1.zzzz * grad_z1; + + // get lengths in the x+y plane + float3 Pf_sq = Pf*Pf; + float3 Pf_min1_sq = Pf_min1*Pf_min1; + float4 vecs_len_sq = float2( Pf_sq.x, Pf_min1_sq.x ).xyxy + float2( Pf_sq.y, Pf_min1_sq.y ).xxyy; + + // evaluate the surflet + float4 m_0 = vecs_len_sq + Pf_sq.zzzz; + m_0 = max(1.0 - m_0, 0.0); + float4 m2_0 = m_0*m_0; + float4 m3_0 = m_0*m2_0; + + float4 m_1 = vecs_len_sq + Pf_min1_sq.zzzz; + m_1 = max(1.0 - m_1, 0.0); + float4 m2_1 = m_1*m_1; + float4 m3_1 = m_1*m2_1; + + // calc the deriv + float4 temp_0 = -6.0 * m2_0 * grad_results_0; + float xderiv_0 = dot( temp_0, float2( Pf.x, Pf_min1.x ).xyxy ) + dot( m3_0, grad_x0 ); + float yderiv_0 = dot( temp_0, float2( Pf.y, Pf_min1.y ).xxyy ) + dot( m3_0, grad_y0 ); + float zderiv_0 = dot( temp_0, Pf.zzzz ) + dot( m3_0, grad_z0 ); + + float4 temp_1 = -6.0 * m2_1 * grad_results_1; + float xderiv_1 = dot( temp_1, float2( Pf.x, Pf_min1.x ).xyxy ) + dot( m3_1, grad_x1 ); + float yderiv_1 = dot( temp_1, float2( Pf.y, Pf_min1.y ).xxyy ) + dot( m3_1, grad_y1 ); + float zderiv_1 = dot( temp_1, Pf_min1.zzzz ) + dot( m3_1, grad_z1 ); + + const float FINAL_NORMALIZATION = 2.3703703703703703703703703703704; // scales the final result to a strict 1.0->-1.0 range + return float4( dot( m3_0, grad_results_0 ) + dot( m3_1, grad_results_1 ) , float3(xderiv_0,yderiv_0,zderiv_0) + float3(xderiv_1,yderiv_1,zderiv_1) ) * FINAL_NORMALIZATION; + } + + // + // Cellular2D_Deriv + // Cellular 2D noise with derivatives + // returns float3( value, xderiv, yderiv ) + // + float3 Cellular2D_Deriv(float2 P) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y; + FAST32_hash_2D( Pi, hash_x, hash_y ); + //SGPP_hash_2D( Pi, hash_x, hash_y ); + + // generate the 4 random points + // restrict the random point offset to eliminate artifacts + // we'll improve the variance of the noise by pushing the points to the extremes of the jitter window + const float JITTER_WINDOW = 0.25; // 0.25 will guarentee no artifacts. 0.25 is the intersection on x of graphs f(x)=( (0.5+(0.5-x))^2 + (0.5-x)^2 ) and f(x)=( (0.5+x)^2 + x^2 ) + hash_x = Cellular_weight_samples( hash_x ) * JITTER_WINDOW + float4(0.0, 1.0, 0.0, 1.0); + hash_y = Cellular_weight_samples( hash_y ) * JITTER_WINDOW + float4(0.0, 0.0, 1.0, 1.0); + + // return the closest squared distance ( + derivs ) + // thanks to Jonathan Dupuy for the initial implementation + float4 dx = Pf.xxxx - hash_x; + float4 dy = Pf.yyyy - hash_y; + float4 d = dx * dx + dy * dy; + float3 t1 = d.x < d.y ? float3( d.x, dx.x, dy.x ) : float3( d.y, dx.y, dy.y ); + float3 t2 = d.z < d.w ? float3( d.z, dx.z, dy.z ) : float3( d.w, dx.w, dy.w ); + return ( t1.x < t2.x ? t1 : t2 ) * float3( 1.0, 2.0, 2.0 ) * ( 1.0 / 1.125 ); // scale return value from 0.0->1.125 to 0.0->1.0 ( 0.75^2 * 2.0 == 1.125 ) + } + + // + // Cellular3D Deriv + // Cellular3D noise with derivatives + // returns float3( value, xderiv, yderiv ) + // + float4 Cellular3D_Deriv(float3 P) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1; + FAST32_hash_3D( Pi, hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1 ); + //SGPP_hash_3D( Pi, hash_x0, hash_y0, hash_z0, hash_x1, hash_y1, hash_z1 ); + + // generate the 8 random points + // restrict the random point offset to eliminate artifacts + // we'll improve the variance of the noise by pushing the points to the extremes of the jitter window + const float JITTER_WINDOW = 0.166666666; // 0.166666666 will guarentee no artifacts. It is the intersection on x of graphs f(x)=( (0.5 + (0.5-x))^2 + 2*((0.5-x)^2) ) and f(x)=( 2 * (( 0.5 + x )^2) + x * x ) + hash_x0 = Cellular_weight_samples( hash_x0 ) * JITTER_WINDOW + float4(0.0, 1.0, 0.0, 1.0); + hash_y0 = Cellular_weight_samples( hash_y0 ) * JITTER_WINDOW + float4(0.0, 0.0, 1.0, 1.0); + hash_x1 = Cellular_weight_samples( hash_x1 ) * JITTER_WINDOW + float4(0.0, 1.0, 0.0, 1.0); + hash_y1 = Cellular_weight_samples( hash_y1 ) * JITTER_WINDOW + float4(0.0, 0.0, 1.0, 1.0); + hash_z0 = Cellular_weight_samples( hash_z0 ) * JITTER_WINDOW + float4(0.0, 0.0, 0.0, 0.0); + hash_z1 = Cellular_weight_samples( hash_z1 ) * JITTER_WINDOW + float4(1.0, 1.0, 1.0, 1.0); + + // return the closest squared distance ( + derivs ) + // thanks to Jonathan Dupuy for the initial implementation + float4 dx1 = Pf.xxxx - hash_x0; + float4 dy1 = Pf.yyyy - hash_y0; + float4 dz1 = Pf.zzzz - hash_z0; + float4 dx2 = Pf.xxxx - hash_x1; + float4 dy2 = Pf.yyyy - hash_y1; + float4 dz2 = Pf.zzzz - hash_z1; + float4 d1 = dx1 * dx1 + dy1 * dy1 + dz1 * dz1; + float4 d2 = dx2 * dx2 + dy2 * dy2 + dz2 * dz2; + float4 r1 = d1.x < d1.y ? float4( d1.x, dx1.x, dy1.x, dz1.x ) : float4( d1.y, dx1.y, dy1.y, dz1.y ); + float4 r2 = d1.z < d1.w ? float4( d1.z, dx1.z, dy1.z, dz1.z ) : float4( d1.w, dx1.w, dy1.w, dz1.w ); + float4 r3 = d2.x < d2.y ? float4( d2.x, dx2.x, dy2.x, dz2.x ) : float4( d2.y, dx2.y, dy2.y, dz2.y ); + float4 r4 = d2.z < d2.w ? float4( d2.z, dx2.z, dy2.z, dz2.z ) : float4( d2.w, dx2.w, dy2.w, dz2.w ); + float4 t1 = r1.x < r2.x ? r1 : r2; + float4 t2 = r3.x < r4.x ? r3 : r4; + return ( t1.x < t2.x ? t1 : t2 ) * float4( 1.0, splat3( 2.0 ) ) * ( 9.0 / 12.0 ); // scale return value from 0.0->1.333333 to 0.0->1.0 (2/3)^2 * 3 == (12/9) == 1.333333 + } + + // + // SimplexPerlin2D_Deriv + // SimplexPerlin2D noise with derivatives + // returns float3( value, xderiv, yderiv ) + // + float3 SimplexPerlin2D_Deriv( float2 P ) + { + // simplex math constants + const float SKEWFACTOR = 0.36602540378443864676372317075294; // 0.5*(sqrt(3.0)-1.0) + const float UNSKEWFACTOR = 0.21132486540518711774542560974902; // (3.0-sqrt(3.0))/6.0 + const float SIMPLEX_TRI_HEIGHT = 0.70710678118654752440084436210485; // sqrt( 0.5 ) height of simplex triangle + const float3 SIMPLEX_POINTS = float3( 1.0-UNSKEWFACTOR, -UNSKEWFACTOR, 1.0-2.0*UNSKEWFACTOR ); // vertex info for simplex triangle + + // establish our grid cell. + P *= SIMPLEX_TRI_HEIGHT; // scale space so we can have an approx feature size of 1.0 ( optional ) + float2 Pi = floor( P + dot( P, splat2( SKEWFACTOR ) ) ); + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_x, hash_y; + FAST32_hash_2D( Pi, hash_x, hash_y ); + //SGPP_hash_2D( Pi, hash_x, hash_y ); + + // establish vectors to the 3 corners of our simplex triangle + float2 v0 = Pi - dot( Pi, splat2( UNSKEWFACTOR ) ) - P; + float4 v1pos_v1hash = (v0.x < v0.y) ? float4(SIMPLEX_POINTS.xy, hash_x.y, hash_y.y) : float4(SIMPLEX_POINTS.yx, hash_x.z, hash_y.z); + float4 v12 = float4( v1pos_v1hash.xy, SIMPLEX_POINTS.zz ) + v0.xyxy; + + // calculate the dotproduct of our 3 corner vectors with 3 random normalized vectors + float3 grad_x = float3( hash_x.x, v1pos_v1hash.z, hash_x.w ) - 0.49999; + float3 grad_y = float3( hash_y.x, v1pos_v1hash.w, hash_y.w ) - 0.49999; + float3 norm = rsqrt( grad_x * grad_x + grad_y * grad_y ); + grad_x *= norm; + grad_y *= norm; + float3 grad_results = grad_x * float3( v0.x, v12.xz ) + grad_y * float3( v0.y, v12.yw ); + + // evaluate the surflet + float3 m = float3( v0.x, v12.xz ) * float3( v0.x, v12.xz ) + float3( v0.y, v12.yw ) * float3( v0.y, v12.yw ); + m = max(0.5 - m, 0.0); // The 0.5 here is SIMPLEX_TRI_HEIGHT^2 + float3 m2 = m*m; + float3 m4 = m2*m2; + + // calc the deriv + float3 temp = 8.0 * m2 * m * grad_results; + float xderiv = dot( temp, float3( v0.x, v12.xz ) ) - dot( m4, grad_x ); + float yderiv = dot( temp, float3( v0.y, v12.yw ) ) - dot( m4, grad_y ); + + const float FINAL_NORMALIZATION = 99.204334582718712976990005025589; // scales the final result to a strict 1.0->-1.0 range + + // sum the surflets and return all results combined in a float3 + return float3( dot( m4, grad_results ), xderiv, yderiv ) * FINAL_NORMALIZATION; + } + + // + // SimplexPerlin3D_Deriv + // SimplexPerlin3D noise with derivatives + // returns float3( value, xderiv, yderiv, zderiv ) + // + float4 SimplexPerlin3D_Deriv(float3 P) + { + // calculate the simplex vector and index math + float3 Pi; + float3 Pi_1; + float3 Pi_2; + float4 v1234_x; + float4 v1234_y; + float4 v1234_z; + Simplex3D_GetCornerVectors( P, Pi, Pi_1, Pi_2, v1234_x, v1234_y, v1234_z ); + + // generate the random vectors + // ( various hashing methods listed in order of speed ) + float4 hash_0; + float4 hash_1; + float4 hash_2; + FAST32_hash_3D( Pi, Pi_1, Pi_2, hash_0, hash_1, hash_2 ); + //SGPP_hash_3D( Pi, Pi_1, Pi_2, hash_0, hash_1, hash_2 ); + hash_0 -= 0.49999; + hash_1 -= 0.49999; + hash_2 -= 0.49999; + + // normalize random gradient vectors + float4 norm = rsqrt( hash_0 * hash_0 + hash_1 * hash_1 + hash_2 * hash_2 ); + hash_0 *= norm; + hash_1 *= norm; + hash_2 *= norm; + + // evaluate gradients + float4 grad_results = hash_0 * v1234_x + hash_1 * v1234_y + hash_2 * v1234_z; + + // evaluate the surflet f(x)=(0.5-x*x)^3 + float4 m = v1234_x * v1234_x + v1234_y * v1234_y + v1234_z * v1234_z; + m = max(0.5 - m, 0.0); // The 0.5 here is SIMPLEX_PYRAMID_HEIGHT^2 + float4 m2 = m*m; + float4 m3 = m*m2; + + // calc the deriv + float4 temp = -6.0 * m2 * grad_results; + float xderiv = dot( temp, v1234_x ) + dot( m3, hash_0 ); + float yderiv = dot( temp, v1234_y ) + dot( m3, hash_1 ); + float zderiv = dot( temp, v1234_z ) + dot( m3, hash_2 ); + + const float FINAL_NORMALIZATION = 37.837227241611314102871574478976; // scales the final result to a strict 1.0->-1.0 range + + // sum with the surflet and return + return float4( dot( m3, grad_results ), xderiv, yderiv, zderiv ) * FINAL_NORMALIZATION; + } + + + // + // Hermite2D_Deriv + // Hermite2D noise with derivatives + // returns float3( value, xderiv, yderiv ) + // + float3 Hermite2D_Deriv( float2 P ) + { + // establish our grid cell and unit position + float2 Pi = floor(P); + float2 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_gradx, hash_grady; + FAST32_hash_2D( Pi, hash_gradx, hash_grady ); + + // scale the hash values + hash_gradx = ( hash_gradx - 0.49999); + hash_grady = ( hash_grady - 0.49999); + + #if 1 + // normalize gradients + float4 norm = rsqrt( hash_gradx * hash_gradx + hash_grady * hash_grady ); + hash_gradx *= norm; + hash_grady *= norm; + const float FINAL_NORM_VAL = 2.2627416997969520780827019587355; + #else + // unnormalized gradients + const float FINAL_NORM_VAL = 3.2; // 3.2 = 1.0 / ( 0.5 * 0.3125 * 2.0 ) + #endif + + // + // NOTE: This stuff can be optimized further. + // But it also appears the compiler is doing a lot of that automatically for us anyway + // + + float4 qh_results_x = QuinticHermite( Pf.y, hash_gradx.xy, hash_gradx.zw, hash_grady.xy, hash_grady.zw ); + float4 qh_results_y = QuinticHermite( Pf.x, hash_grady.xz, hash_grady.yw, hash_gradx.xz, hash_gradx.yw ); + float finalpos = QuinticHermite( Pf.x, qh_results_x.x, qh_results_x.y, qh_results_x.z, qh_results_x.w ); + float deriv_x = QuinticHermiteDeriv( Pf.x, qh_results_x.x, qh_results_x.y, qh_results_x.z, qh_results_x.w ); + float deriv_y = QuinticHermiteDeriv( Pf.y, qh_results_y.x, qh_results_y.y, qh_results_y.z, qh_results_y.w ); + return float3( finalpos, deriv_x, deriv_y ) * FINAL_NORM_VAL; + } + + // + // Hermite3D_Deriv + // Hermite3D noise with derivatives + // returns float3( value, xderiv, yderiv, zderiv ) + // + float4 Hermite3D_Deriv( float3 P ) + { + // establish our grid cell and unit position + float3 Pi = floor(P); + float3 Pf = P - Pi; + + // calculate the hash. + // ( various hashing methods listed in order of speed ) + float4 hash_gradx0, hash_grady0, hash_gradz0, hash_gradx1, hash_grady1, hash_gradz1; + FAST32_hash_3D( Pi, hash_gradx0, hash_grady0, hash_gradz0, hash_gradx1, hash_grady1, hash_gradz1 ); + + // scale the hash values + hash_gradx0 = ( hash_gradx0 - 0.49999); + hash_grady0 = ( hash_grady0 - 0.49999); + hash_gradz0 = ( hash_gradz0 - 0.49999); + hash_gradx1 = ( hash_gradx1 - 0.49999); + hash_grady1 = ( hash_grady1 - 0.49999); + hash_gradz1 = ( hash_gradz1 - 0.49999); + + #if 1 + // normalize gradients + float4 norm0 = rsqrt( hash_gradx0 * hash_gradx0 + hash_grady0 * hash_grady0 + hash_gradz0 * hash_gradz0 ); + hash_gradx0 *= norm0; + hash_grady0 *= norm0; + hash_gradz0 *= norm0; + float4 norm1 = rsqrt( hash_gradx1 * hash_gradx1 + hash_grady1 * hash_grady1 + hash_gradz1 * hash_gradz1 ); + hash_gradx1 *= norm1; + hash_grady1 *= norm1; + hash_gradz1 *= norm1; + const float FINAL_NORM_VAL = 1.8475208614068024464292760976063; + #else + // unnormalized gradients + const float FINAL_NORM_VAL = (1.0/0.46875); // = 1.0 / ( 0.5 * 0.3125 * 3.0 ) + #endif + + // + // NOTE: This stuff can be optimized further. + // But it also appears the compiler is doing a lot of that automatically for us anyway + // + + // drop things from three dimensions to two + float4 ival_results_z, igrad_results_x_z, igrad_results_y_z; + QuinticHermite( Pf.z, hash_gradx0, hash_gradx1, hash_grady0, hash_grady1, hash_gradz0, hash_gradz1, ival_results_z, igrad_results_x_z, igrad_results_y_z ); + + float4 ival_results_y, igrad_results_x_y, igrad_results_z_y; + QuinticHermite( Pf.y, float4( hash_gradx0.xy, hash_gradx1.xy ), float4( hash_gradx0.zw, hash_gradx1.zw ), + float4( hash_gradz0.xy, hash_gradz1.xy ), float4( hash_gradz0.zw, hash_gradz1.zw ), + float4( hash_grady0.xy, hash_grady1.xy ), float4( hash_grady0.zw, hash_grady1.zw ), + ival_results_y, igrad_results_x_y, igrad_results_z_y ); + + // drop things from two dimensions to one + float4 qh_results_x = QuinticHermite( Pf.y, float4(ival_results_z.xy, igrad_results_x_z.xy), float4(ival_results_z.zw, igrad_results_x_z.zw), float4( igrad_results_y_z.xy, 0.0, 0.0 ), float4( igrad_results_y_z.zw, 0.0, 0.0 ) ); + float4 qh_results_y = QuinticHermite( Pf.x, float4(ival_results_z.xz, igrad_results_y_z.xz), float4(ival_results_z.yw, igrad_results_y_z.yw), float4( igrad_results_x_z.xz, 0.0, 0.0 ), float4( igrad_results_x_z.yw, 0.0, 0.0 ) ); + float4 qh_results_z = QuinticHermite( Pf.x, float4(ival_results_y.xz, igrad_results_z_y.xz), float4(ival_results_y.yw, igrad_results_z_y.yw), float4( igrad_results_x_y.xz, 0.0, 0.0 ), float4( igrad_results_x_y.yw, 0.0, 0.0 ) ); + + // for each hermite curve calculate the derivative + float deriv_x = QuinticHermiteDeriv( Pf.x, qh_results_x.x, qh_results_x.y, qh_results_x.z, qh_results_x.w ); + float deriv_y = QuinticHermiteDeriv( Pf.y, qh_results_y.x, qh_results_y.y, qh_results_y.z, qh_results_y.w ); + float deriv_z = QuinticHermiteDeriv( Pf.z, qh_results_z.x, qh_results_z.y, qh_results_z.z, qh_results_z.w ); + + // and also the final noise value off any one of them + float finalpos = QuinticHermite( Pf.x, qh_results_x.x, qh_results_x.y, qh_results_x.z, qh_results_x.w ); + + // normalize and return results! :) + return float4( finalpos, deriv_x, deriv_y, deriv_z ) * FINAL_NORM_VAL; + } + + #undef splat4 + #undef splat3 + #undef splat2 + """ + } +} diff --git a/vmf_source/core/stingray_renderer/shader_libraries/particle_billboard.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/particle_billboard.shader_source new file mode 100644 index 0000000..2ed120c --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/particle_billboard.shader_source @@ -0,0 +1,1172 @@ +// in this context include refers to another shader file +includes = ["core/stingray_renderer/shader_libraries/common.shader_source", + "core/stingray_renderer/shader_libraries/lighting_common.shader_source", + "core/stingray_renderer/shader_libraries/particle_lighting_common.shader_source", + "core/stingray_renderer/shader_libraries/shadow_map_common.shader_source" ] + +sampler_states = { + shadow_map = { + inherits = "clamp_point" + states = { + defined_D3D11 = { + comparison_func = "less" + filter = "comparison_min_mag_linear_mip_point" + } + defined_D3D12 = { + comparison_func = "less" + filter = "comparison_min_mag_linear_mip_point" + } + defined_GNM = { + comparison_func = "less" + filter = "min_mag_mip_linear" + } + defined_GL2 = { + comparison_func = "less" + filter = "min_mag_linear" + } + } + } +} + +render_states = { + billboard_shadow_caster = { + inherits = "shadow_caster" + states = { + ndefined_GBUFFER_PARTICLES = { + z_write_enable = "false" + + write_mask0 = "red" + cull_mode = "cull_none" + blend_enable = "true" + + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_color" + } + } + } + + billboard_opacity = { + inherits = "opacity" + states = { + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_one" + + defined_WIREFRAME = { + fill_mode = "fill_wireframe" + } + defined_SCREEN_SPACE = { + cull_mode = "cull_none" + z_enable = "false" + } + ndefined_SCREEN_SPACE = { + z_enable = "true" + } + defined_LOW_RES = { + write_mask0 = "red|green|blue|alpha" + write_mask1 = "red|green" + } + ndefined_LOW_RES = { + write_mask0 = "red|green|blue" + } + } + } +} + +hlsl_shaders = { + billboard_depth_only = { + includes = [ "common", "gbuffer_access" ] + + samplers = { + defined_DIFFUSE_MAP = { + diffuse_map = { sampler_states = "wrap_anisotropic_srgb" } + } + } + + code=""" + #if defined(DIFFUSE_MAP) + #define UV0 + DECLARE_SAMPLER_2D(diffuse_map); + #endif + + struct VS_INPUT { + float4 position : POSITION; + #ifdef GL2 + float2 corner_info : COLOR1; + #else + float2 corner_info : POSITION1; + #endif + float2 size : TEXCOORD7; + #if defined(VERTEX_COLOR) + float4 color : COLOR; + #endif + #if defined(ROTATION) + float rotation : TEXCOORD1; + #endif + #if defined(UV_ANIMATION) + float frame : TEXCOORD0; + #endif + #if defined(PIVOT) + float2 pivot : TEXCOORD6; + #endif + #if defined(EXTERNAL_ROTATION) || defined(TANGENT_LOCKED) + float3 tangent : TANGENT; + #if defined(EXTERNAL_ROTATION) + float3 binormal : BINORMAL; + #endif + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + #if defined(VERTEX_COLOR) + float4 color : COLOR; + #endif + //float4 normal_depth : TEXCOORD1; + }; + + CBUFFER_START(c0) + float4x4 view; + float4x4 view_proj; + #if defined(UV_ANIMATION) + float2 animation_frame_size; // exports={ name="Frame Size" type="vector2" value=[0.1 0.1] min=[0 0] max=[1 1] step=[0.000244140625 0.000244140625]} + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT i) { + PS_INPUT o; + + #if defined(EXTERNAL_ROTATION) + float3 y = i.tangent; + float3 x = i.binormal; + #elif defined(TANGENT_LOCKED) + float3 y = i.tangent; + float3 x = normalize(cross(normalize(i.position.xyz - camera_pos), y)); + #else + float3 x = view._m00_m10_m20; + float3 y = view._m02_m12_m22; + #endif + + #if defined(ROTATION) + float c = cos(i.rotation); + float s = sin(i.rotation); + float3 x_axis = x * c + y * s; + float3 y_axis = y * c - x * s; + #else + float3 x_axis = x; + float3 y_axis = y; + #endif + + #if defined(PIVOT) + float2 ci = i.corner_info; + float2 corner = ci * ( (1-(ci*0.5+0.5)) * i.size + ci * (i.pivot * i.size) ); + #else + float2 corner = i.corner_info * (i.size * 0.5); + #endif + + float3 wp = i.position.xyz + (x_axis * corner.x + y_axis * corner.y); + #if defined(NEEDS_WORLD_POS) + o.wp = wp; + #endif + #if defined(HAS_CUSTOM_FOV) + float4 p = mul(float4(wp, 1), camera_custom_fov_view_projection); + #else + float4 p = mul(float4(wp, 1), view_proj); + #endif + o.position = p; + + float3 n = lerp(normalize(wp - i.position.xyz), -view._m01_m11_m21, 0.5); + n = mul(n, (half3x3)view); + //o.normal_depth = float4(n, p.z); + + #if defined(UV0) + float2 uv = (i.corner_info * float2(1,-1) * 0.5 + 0.5); + #if defined(UV_ANIMATION) + uv *= animation_frame_size; + float n_frames = 1.f / animation_frame_size.x; + int frame_x = fmod(i.frame, n_frames); + int frame_y = i.frame / n_frames; + float2 offset = float2(frame_x * animation_frame_size.x, frame_y * animation_frame_size.y); + uv += offset; + #endif + o.uv = uv; + #endif + + #if defined(VERTEX_COLOR) + o.color = decode_vertex_color(i.color); + #endif + + return o; + } + + float4 fast_gamma_to_linear(float4 c) { + return c * c; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT i) : SV_TARGET0 { + #if defined(DIFFUSE_MAP) + float4 c = TEX2D(diffuse_map, i.uv); + #else + float4 c = float4(1,1,1,1); + #endif + + #if defined(VERTEX_COLOR) + float4 vc = fast_gamma_to_linear(i.color); + #else + float4 vc = float4(1,1,1,1); + #endif + + #if defined(GBUFFER_PARTICLES) + #if defined(ONE_BIT_ALPHA) + float op = c.a; + #if defined(DISSOLVE_USING_VERTEX_ALPHA) + one_bit_alpha_mask(op, 1-vc.a); + #else + one_bit_alpha_mask(op, ONE_BIT_ALPHA_REF); + #endif + #endif + return float4(1,1,1,1); + #else + //float d = i.normal_depth.w - normalize(i.normal_depth.xyz) * 0.01; + //return float4(d,d,d,c.a); + c *= vc; + return float4(c.aaaa); + #endif + } + """ + } + + particle_lighting = { + vp_code = { ref = "code" } + fp_code = { ref = "code" } + + code=""" + #if !defined(BLEND_ADDITIVE) && !defined(GBUFFER_PARTICLES) && !defined(DISTORTION) && !defined(DISABLE_LIGHTING) + #define PARTICLE_LIGHTING + #define VS_FOG + + #if defined(D3D11) || defined(D3D12) || defined(GNM) + #define BACK_LIGHTING + #endif + + #if defined(LOCAL_LIGHTS) + #define CALCULATE_LIGHTING + #endif + #else + #if defined(TESSELLATION) + #undef TESSELLATION + #endif + #endif + + #if defined(DIFFUSE_MAP) || defined(NORMAL_MAP) || defined(MATERIAL_MAP) || defined(DISTORTION) + #define UV0 + #endif + + #if (defined(USE_DEPTH_RT) && defined(GBUFFER_PARTICLES)) || !defined(GBUFFER_PARTICLES) || (!defined(LOW_RES) && defined(LOW_RES_ENABLED)) + #define NEEDS_LINEAR_DEPTH + #endif + + #if defined(DIFFUSE_MAP) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" } + #endif + + #if defined(NORMAL_MAP) + DECLARE_SAMPLER_2D(normal_map); // exports={ name="Normal Map" type="resource" } + #endif + + #if defined(MATERIAL_MAP) + DECLARE_SAMPLER_2D(material_map); // exports={ name="Glossiness/Specular/Mask Map" type="resource" } + #endif + + #if defined(DISTORTION) + DECLARE_SAMPLER_2D(normal_map); // exports={ name="Distortion Normal Map" type="resource" } + DECLARE_SAMPLER_2D(hdr0); + #endif + + #ifndef GBUFFER_PARTICLES + #ifdef LOW_RES + Texture2D linear_depth_div4; + #else + Texture2D linear_depth; + #if defined(LOW_RES_ENABLED) + DECLARE_SAMPLER_2D(hdr_transparent_div4); + DECLARE_SAMPLER_2D(hdr_linear_depth_div4); + #endif + #endif + #endif + """ + } + + billboard = { + includes = [ "common", "gbuffer_access", "particle_lighting", "taa_offsets", "fog", "brdf", "shadow_bias", "shadow_map_filtering", "lighting", "clustered_shading", "radiosity_normal_mapping", "particle_debug" ] + + samplers = { + defined_DIFFUSE_MAP = { + diffuse_map = { sampler_states = "wrap_anisotropic_srgb" } + } + defined_NORMAL_MAP = { + normal_map = { sampler_states = "wrap_anisotropic" } + } + defined_MATERIAL_MAP = { + material_map = { sampler_states = "wrap_anisotropic_srgb" } + } + defined_DISTORTION = { + normal_map = { sampler_states = "wrap_anisotropic" } + hdr0 = { sampler_states = "clamp_linear" } + } + + global_diffuse_map = { sampler_states = "clamp_linear"} + + ndefined_GBUFFER_PARTICLES = { + ndefined_LOW_RES = { + defined_LOW_RES_ENABLED = { + hdr_transparent_div4 = { sampler_states = "clamp_linear" } + hdr_linear_depth_div4 = { sampler_states = "clamp_linear" } + } + } + + defined_SHADOW_RECEIVING = { + local_lights_shadow_atlas = { sampler_states = "shadow_map" } + sun_shadow_map = { sampler_states = "shadow_map" } + static_sun_shadow_map = { sampler_states = "shadow_map" } + } + } + } + + stage_conditions = { + hull = "!defined(BLEND_ADDITIVE) && !defined(GBUFFER_PARTICLES) && !defined(DISTORTION) && !defined(DISABLE_LIGHTING) && defined(TESSELLATION)" + domain = "!defined(BLEND_ADDITIVE) && !defined(GBUFFER_PARTICLES) && !defined(DISTORTION) && !defined(DISABLE_LIGHTING) && defined(TESSELLATION)" + } + + code=""" + struct VS_INPUT { + float4 position : POSITION; + #ifdef GL2 + float2 corner_info : COLOR1; + #else + float2 corner_info : POSITION1; + #endif + float2 size : TEXCOORD7; // exports={ name="Size" type="vector2" value=[0.1 0.1] min=[0 0] max=[10 10] step=[0.01 0.01] } + #if defined(VERTEX_COLOR) + float4 color : COLOR; + #endif + #if defined(ROTATION) + float rotation : TEXCOORD1; // exports = { name="Rotation" type="scalar" value=0 min=0 max=6.28319 step=0.0174533 } + #endif + #if defined(UV_SCALE) + #if defined(UV_ANIMATION) + float3 uv_data : TEXCOORD0; // exports = { name="UV Scale/Frame" type="vector3" value=[0 0 0] min=[0 0 0] max=[100 100 100] step=[0.1 0.1 1] } + #define uv_frame uv_data.z + #else + float2 uv_data : TEXCOORD0; + #endif + #define uv_scale uv_data.xy + #else + #if defined(UV_ANIMATION) + float uv_frame : TEXCOORD0; // exports = { name="UV Frame" type="scalar" value=0 min=0 max=100 step=1 } + #endif + #endif + #if defined(PIVOT) + float2 pivot : TEXCOORD6; // exports={ name="Pivot" type="vector2" value=[0.5 0.5] min=[0 0] max=[1 1] step=[0.001 0.001] } + #endif + #if defined(EXTERNAL_ROTATION) || defined(TANGENT_LOCKED) + float3 tangent : TANGENT; // exports={ name="Tangent" type="vector3" value=[0 0 1] min=[-1 -1 -1] max=[1 1 1] step=[0.001 0.001 0.001] } + #if defined(EXTERNAL_ROTATION) + float3 binormal : BINORMAL; // exports={ name="Binormal" type="vector3" value=[1 0 0] min=[-1 -1 -1] max=[1 1 1] step=[0.001 0.001 0.001] } + #endif + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + #if defined(VERTEX_COLOR) + float4 color : COLOR; + #endif + #if defined(NEEDS_LINEAR_DEPTH) + float linear_depth : TEXCOORD1; + #endif + + #if defined(NEEDS_WORLD_POS) + float4 wp : TEXCOORD5; + #endif + + #if defined(GBUFFER_PARTICLES) + #if defined(NORMAL_MAP) + float3 tsm0 : TEXCOORD2; + float3 tsm1 : TEXCOORD3; + float3 tsm2 : TEXCOORD4; + #else + float3 normal : TEXCOORD2; + #endif + #endif + + #if defined(PARTICLE_LIGHTING) + float4 basis0 : TEXCOORD2; + float4 basis1 : TEXCOORD3; + float4 basis2 : TEXCOORD4; + #if defined(BACK_LIGHTING) + float3 back_lighting : TEXCOORD6; + #endif + #endif + + #if defined(VS_FOG) + float4 fog_params : TEXCOORD7; + #endif + + }; + + CBUFFER_START(c_billboard) + #if defined(SCREEN_SPACE) + float4x4 proj; + float4x4 view; + #else + float4x4 view; + float4x4 view_proj; + #endif + #if defined(UV_ANIMATION) + float2 animation_frame_size; // exports={ name="Frame Size" type="vector2" value=[0.1 0.1] min=[0 0] max=[1 1] step=[0.000244140625 0.000244140625]} + #endif + #if defined(SOFT_PARTICLES) + float depth_fade_distance; // exports={ name="Depth Fade Distance" type="scalar" value=1 min=0.01 max=30 step=0.1 } + #endif + #if defined(GBUFFER_PARTICLES) && !defined(MATERIAL_MAP) + float specular; // exports={ name="Specular Mask" type="scalar" value=0.8 min=0.0 max=1.0 step=0.001 } + float glossiness; // exports={ name="Glossiness Amount" type="scalar" value=0.5 min=0.0 max=1.0 step=0.001 } + #endif + #if !defined(PARTICLE_LIGHTING) && !defined(GBUFFER_PARTICLES) + float emissive_particle_intensity; + #endif + #if defined(DISTORTION) + float2 distortion_strength; // exports={ name="Distortion Strength" type="vector2" value=[1.0 1.0] min=[ 1 1 ] max=[300 300] step=[1 1] } + #endif + CBUFFER_END + + #if defined(TESSELLATION) + #define NEEDS_WORLD_POS + #undef VS_FOG + + struct VS_OUTPUT_HS_INPUT { + float4 wp : WORLDPOS; + float3 normal : TEXCOORD2; + + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + #if defined(VERTEX_COLOR) + float4 color : COLOR; + #endif + #if defined(NEEDS_LINEAR_DEPTH) + float linear_depth : TEXCOORD1; + #endif + }; + + struct HS_CONSTANT_DATA_OUTPUT { + float edges[3] : SV_TessFactor; + float inside : SV_InsideTessFactor; + }; + + #define VS_OUTPUT VS_OUTPUT_HS_INPUT + + // hull shader is just a pipe through shader + #define HS_CONTROL_POINT_OUTPUT VS_OUTPUT_HS_INPUT + + HS_CONSTANT_DATA_OUTPUT hs_constants(InputPatch p) + { + HS_CONSTANT_DATA_OUTPUT output = (HS_CONSTANT_DATA_OUTPUT)0; + + float3 v0 = p[0].wp.xyz; + float3 v1 = p[1].wp.xyz; + float3 v2 = p[2].wp.xyz; + + #ifdef LOW_RES + float2 half_res = (back_buffer_size * 0.25) * 0.5; + #else + float2 half_res = back_buffer_size * 0.5; + #endif + const float eps = 0.0001; + const float wanted_tri_size = 16; + float4 clip_rect = float4(-half_res.x, -half_res.y, half_res.x, half_res.y); + + #if defined(SCREEN_SPACE) + float4 p0 = float4(v0, 1); + float4 p1 = float4(v1, 1); + float4 p2 = float4(v2, 1); + #else + #if defined(HAS_CUSTOM_FOV) + float4 p0 = mul(float4(v0, 1), camera_custom_fov_view_projection); + float4 p1 = mul(float4(v1, 1), camera_custom_fov_view_projection); + float4 p2 = mul(float4(v2, 1), camera_custom_fov_view_projection); + #else + float4 p0 = mul(float4(v0, 1), view_proj); + float4 p1 = mul(float4(v1, 1), view_proj); + float4 p2 = mul(float4(v2, 1), view_proj); + #endif + #endif + p0.xy = (p0.xy / (p0.w+eps)) * half_res; + p1.xy = (p1.xy / (p1.w+eps)) * half_res; + p2.xy = (p2.xy / (p2.w+eps)) * half_res; + + float4 tessellation_factors = float4(0,0,0,0); + + bool near_rejected = all(float3(p0.z,p1.z,p2.z) < 0.0f); + bool left_rejected = all(float3(p0.x,p1.x,p2.x) < clip_rect.x); + bool right_rejected = all(float3(p0.x,p1.x,p2.x) > clip_rect.z); + bool top_rejected = all(float3(p0.y,p1.y,p2.y) > clip_rect.w); + bool bottom_rejected = all(float3(p0.y, p1.y, p2.y) < clip_rect.y); + bool culled = (near_rejected || left_rejected || right_rejected || top_rejected || bottom_rejected); + if (!culled) { + float3 tf = float3(length(p2 - p1) / wanted_tri_size, + length(p2 - p0) / wanted_tri_size, + length(p1 - p0) / wanted_tri_size); + + tessellation_factors = float4(tf.xyz,max(tf.z, max(tf.x, tf.y))); + } + + //float4 tessellation_factors = float4(1,1,1,1); + + output.edges[0] = tessellation_factors.x; + output.edges[1] = tessellation_factors.y; + output.edges[2] = tessellation_factors.z; + output.inside = tessellation_factors.w; + + return output; + } + + [domain("tri")] + [partitioning("fractional_odd")] + [outputtopology("triangle_cw")] + [outputcontrolpoints(3)] + [patchconstantfunc("hs_constants")] + [maxtessfactor(14)] + HS_CONTROL_POINT_OUTPUT hs_main(InputPatch inputPatch, uint cp_id : SV_OutputControlPointID) + { + HS_CONTROL_POINT_OUTPUT o; + + o.wp = inputPatch[cp_id].wp; + o.normal = inputPatch[cp_id].normal.xyz; + #if defined(UV0) + o.uv = inputPatch[cp_id].uv; + #endif + #if defined(VERTEX_COLOR) + o.color = inputPatch[cp_id].color; + #endif + + #if defined(NEEDS_LINEAR_DEPTH) + o.linear_depth = inputPatch[cp_id].linear_depth; + #endif + + return o; + } + + [domain("tri")] + PS_INPUT ds_main( HS_CONSTANT_DATA_OUTPUT input, float3 barycentric_coordinates : SV_DomainLocation, const OutputPatch triangle_patch ) + { + PS_INPUT o; + + float3 wp = barycentric_coordinates.x * triangle_patch[0].wp.xyz + barycentric_coordinates.y * triangle_patch[1].wp.xyz + barycentric_coordinates.z * triangle_patch[2].wp.xyz; + #if defined(NEEDS_WORLD_POS) + o.wp = float4(wp, 1.0f); + #endif + + #if defined(SCREEN_SPACE) + o.position = float4(wp.xyz, 1.0); + #else + #if defined(HAS_CUSTOM_FOV) + float4 p = mul(float4(wp.xyz, 1), camera_custom_fov_view_projection); + #else + float4 p = mul(float4(wp.xyz, 1), view_proj); + #endif + + float4 proj_pos = p / p.w; + proj_pos.xy += get_vs_halton_offset(frame_number); + o.position = proj_pos * p.w; + #endif + float3 normal = normalize(barycentric_coordinates.x * triangle_patch[0].normal + barycentric_coordinates.y * triangle_patch[1].normal + barycentric_coordinates.z * triangle_patch[2].normal); + + #if defined(PARTICLE_LIGHTING) + float3 back_lighting; + calc_basis_lighting(o.basis0, o.basis1, o.basis2, back_lighting, wp, normal, view, o.position); + #if defined(BACK_LIGHTING) + o.back_lighting = back_lighting; + #endif + #endif + + #if defined(UV0) + float2 uv = barycentric_coordinates.x * triangle_patch[0].uv + barycentric_coordinates.y * triangle_patch[1].uv + barycentric_coordinates.z * triangle_patch[2].uv; + o.uv = uv; + #endif + + #if defined(VERTEX_COLOR) + float4 color = barycentric_coordinates.x * triangle_patch[0].color + barycentric_coordinates.y * triangle_patch[1].color + barycentric_coordinates.z * triangle_patch[2].color; + o.color = color; + #endif + + #if defined(NEEDS_LINEAR_DEPTH) + o.linear_depth = barycentric_coordinates.x * triangle_patch[0].linear_depth + barycentric_coordinates.y * triangle_patch[1].linear_depth + barycentric_coordinates.z * triangle_patch[2].linear_depth; + #endif + + return o; + } + #else + #define VS_OUTPUT PS_INPUT + #endif + + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + VS_OUTPUT vs_main(VS_INPUT i) { + VS_OUTPUT o; + + #if defined(EXTERNAL_ROTATION) + float3 y = i.tangent; + float3 x = i.binormal; + #elif defined(TANGENT_LOCKED) + float3 y = i.tangent; + float3 x = normalize(cross(normalize(i.position.xyz - camera_pos), y)); + #elif defined(SCREEN_SPACE) + float3 x = float3(1,0,0); + float3 y = float3(0,1,0); //float3(0,0,1); + #else + float3 x = view._m00_m10_m20; + float3 y = view._m02_m12_m22; + #endif + + #if defined(ROTATION) + float c = cos(i.rotation); + float s = sin(i.rotation); + float3 x_axis = x * c + y * s; + float3 y_axis = y * c - x * s; + #else + float3 x_axis = x; + float3 y_axis = y; + #endif + + #if defined(PIVOT) + float2 ci = i.corner_info; + float2 corner = ci * ( (1-(ci*0.5+0.5)) * i.size + ci * (i.pivot * i.size) ); + #else + float2 corner = i.corner_info * (i.size * 0.5); + #endif + + #if defined(SCREEN_SPACE) + float3 wp = i.position.xzy + (x_axis * corner.x + y_axis * corner.y) / float3(normalize(camera_unprojection.xz), 1); + #else + float3 wp = i.position.xyz + (x_axis * corner.x + y_axis * corner.y); + #endif + + #if defined(NEEDS_WORLD_POS) + o.wp = float4(wp, 1.0f); + #endif + + #if defined(SCREEN_SPACE) + float4 p = float4(wp, 1); //mul(float4(wp, 1), proj); + #else + #if defined(HAS_CUSTOM_FOV) + float4 p = mul(float4(wp, 1), camera_custom_fov_view_projection); + #else + float4 p = mul(float4(wp, 1), view_proj); + #endif + #endif + #if !defined(TESSELLATION) + float4 proj_pos = p / p.w; + proj_pos.xy += get_vs_halton_offset(frame_number); + o.position = proj_pos * p.w; + #endif + + #if defined(PARTICLE_LIGHTING) + #if defined(SCREEN_SPACE) + float3 n = view._m01_m11_m21; + #else + float3 n = normalize(lerp(wp - i.position.xyz, -view._m01_m11_m21, 0.5)); + #endif + n = mul(n, (float3x3)view); + + #if defined(TESSELLATION) + o.normal = n; + #else + float3 back_lighting; + calc_basis_lighting(o.basis0, o.basis1, o.basis2, back_lighting, wp, n, view, o.position); + #if defined(BACK_LIGHTING) + o.back_lighting = back_lighting; + #endif + #endif + #endif + + #if defined(UV0) + float2 uv = (i.corner_info * float2(1,-1) * 0.5 + 0.5); + #if defined(UV_ANIMATION) + uv *= animation_frame_size; + float n_frames = 1.f / animation_frame_size.x; + int frame_x = fmod(i.uv_frame, n_frames); + int frame_y = i.uv_frame / n_frames; + float2 offset = float2(frame_x * animation_frame_size.x, frame_y * animation_frame_size.y); + uv += offset; + #endif + + #if defined(GL2) + uv = float2(uv.x, 1-uv.y); + #endif + + #if defined(UV_SCALE) + uv *= i.uv_scale; + #endif + o.uv = uv; + #endif + + #if defined(VERTEX_COLOR) + o.color = decode_vertex_color(i.color); + #endif + + #if defined(NEEDS_LINEAR_DEPTH) || defined(VS_FOG) + float l_depth = linearize_depth(p.z / p.w); + #endif + + #if defined(NEEDS_LINEAR_DEPTH) + o.linear_depth = l_depth; + #endif + + #if defined(VS_FOG) + o.fog_params = calc_fog_vs(wp, l_depth); + #endif + + #if defined(GBUFFER_PARTICLES) + float3 n = cross(x_axis, y_axis); + + #if defined(NORMAL_MAP) + float3 t = x_axis; + float3 b = y_axis; + o.tsm0 = float3(t.x, b.x, n.x); + o.tsm1 = float3(t.y, b.y, n.y); + o.tsm2 = float3(t.z, b.z, n.z); + #else + o.normal = n; + #endif + #endif + + return o; + } + + // Note: Alpha channel stored as sqrt(opacity) to preserve precision + float4 fast_gamma_to_linear(float4 c) { + return c * c; + } + + #if defined(GBUFFER_PARTICLES) + //DECLARE_SAMPLER_CUBE(global_diffuse_map); + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + GBUFFER_OUT ps_main(PS_INPUT input) { + GBUFFER_OUT o; + + #if defined(USE_DEPTH_RT) + DEPTH(o) = gbuffer_encode_depth(input.linear_depth); + #endif + + #if defined(VERTEX_COLOR) + half4 vc = input.color; + vc = fast_gamma_to_linear(vc); + #endif + + half op = 1.f; + #ifdef DIFFUSE_MAP + half4 dtex = TEX2D(diffuse_map, input.uv); + float3 albedo = dtex.rgb; + op = dtex.a; + #else + float3 albedo = float3(0.6,0.6,0.6); + #endif + + #ifdef MATERIAL_MAP + float3 gsm = TEX2D(material_map, input.uv).rgb; + half2 specular_glossiness = float2(gsm.g, gsm.r); + #ifdef ONE_BIT_ALPHA_FROM_MATERIAL_B + op = gsm.b; + #endif + #else + half2 specular_glossiness = float2(specular, glossiness); + #endif + + #if defined(ONE_BIT_ALPHA) + #if defined(VERTEX_COLOR) && defined(DISSOLVE_USING_VERTEX_ALPHA) + one_bit_alpha_mask(op, 1-vc.a); + #else + one_bit_alpha_mask(op, ONE_BIT_ALPHA_REF); + #endif + #endif + + #if defined(VERTEX_COLOR) + albedo *= vc.rgb; + #endif + + BASE_COLOR(o) = gbuffer_encode_base_color(albedo); + + float3 world_space_normal = float3(0,0,0); + #ifdef NORMAL_MAP + float3 tnormal = decode_normal_map(TEX2D(normal_map, input.uv)); + world_space_normal = rotate_vector3(tnormal, input.tsm0, input.tsm1, input.tsm2); + #else + world_space_normal = normalize(input.normal); + #endif + + NORMAL(o) = gbuffer_encode_normal(world_space_normal); + ROUGHNESS(o) = gbuffer_encode_roughness(1.f - specular_glossiness.y); + METALLIC(o) = 0.f; + VELOCITY(o) = encode_velocity(0.0); + DENSITY(o) = 1.0; + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(1.f); + //AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(rgbm_decode(TEXCUBELOD(global_diffuse_map, world_space_normal, 0))); + + return o; + } + #elif defined(DISTORTION) + #ifdef LOW_RES + struct PS_OUTPUT { + half4 color : SV_TARGET0; + half4 depth : SV_TARGET1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + #endif + { + #ifdef LOW_RES + // TODO: correct sample position? + half2 screen_uv = input.position.xy / (back_buffer_size * 0.25); + #else + half2 screen_uv = input.position.xy / back_buffer_size; + #endif + + #if defined(LOW_RES) + float d = gbuffer_decode_depth(linear_depth_div4.Load(int3(input.position.xy, 0))); + #else + float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy, 0))); + #endif + + half4 tnormal = TEX2D(normal_map, input.uv); + half2 distortion = (tnormal.xy * 2.0 - 1.0) / (back_buffer_size * 0.25); + + half alpha = tnormal.a; + #if defined(VERTEX_COLOR) + half4 vcol = fast_gamma_to_linear(input.color); + alpha *= vcol.a; + #endif + #if defined(SOFT_PARTICLES) + alpha *= saturate(abs(d - input.linear_depth) / depth_fade_distance); + #endif + + distortion_strength *= alpha; + half4 color = half4(TEX2D(hdr0, screen_uv + (distortion * distortion_strength)).rgb, alpha); + + #if defined(VERTEX_COLOR) + color.rgb *= lerp(float3(1,1,1), vcol.rgb, color.a); + #endif + + #ifdef LOW_RES + PS_OUTPUT o; + o.color = half4(color.rgb * color.a, color.a); + float alpha_depth = input.linear_depth * color.a; + o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0, color.a); + return o; + #else + #if defined(LOW_RES_ENABLED) && !defined(SCREEN_SPACE) + // Fade out high resolution particle if the low resolution particles is infront of the high resolution particle + float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg; + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + float background_depth = input.linear_depth; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * max(depth_variance, 0.001))); + float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T); + color.a *= 1.0 - alpha_modifier; + #endif + #if defined(PARTICLE_DEBUG) + return DISTORTION_PARTICLES_DEBUG_COLOR; + #else + return half4(color.rgb * color.a, color.a); + #endif + #endif + } + #elif defined(WIREFRAME) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return float4(1,0,0,1); + } + #else + #ifdef LOW_RES + struct PS_OUTPUT { + half4 color : SV_TARGET0; + half4 depth : SV_TARGET1; + }; + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + #endif + + { + #if defined(DIFFUSE_MAP) + float4 c = TEX2D(diffuse_map, input.uv); + #else + float4 c = float4(1,1,1,1); + #endif + + #if defined(SOFT_PARTICLES) + #if defined(LOW_RES) + float d = gbuffer_decode_depth(linear_depth_div4.Load(int3(input.position.xy, 0))); + #else + float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy, 0))); + #endif + #endif + + #if defined(VERTEX_COLOR) + float4 vc = input.color; + vc = fast_gamma_to_linear(vc); + c *= vc; + #endif + + #if defined(SOFT_PARTICLES) + c.a *= saturate(abs(d - input.linear_depth) / depth_fade_distance); + #endif + + #if defined(PARTICLE_LIGHTING) + float3 n = normalize(float3(input.basis0.w, input.basis1.w, input.basis2.w)); + c.rgb = calc_lighting(c, n, input.back_lighting, input.basis0.xyz, input.basis1.xyz, input.basis2.xyz); + + + #if defined(VS_FOG) + // apply vs calculated fog + c = float4(lerp(c.rgb, input.fog_params.rgb, input.fog_params.a), c.a); + #else + c = apply_fog(c, input.wp.xyz, input.linear_depth); + #endif + #else + c.rgb *= emissive_particle_intensity; + #endif + + #if !defined(LOW_RES) && defined(LOW_RES_ENABLED) && !defined(SCREEN_SPACE) + // Fade out high resolution particle if the low resolution particles is infront of the high resolution particle + float2 screen_uv = input.position.xy / back_buffer_size; + float2 particle_depth_values = TEX2D(hdr_linear_depth_div4, screen_uv).rg; + float depth_mean = particle_depth_values.x; + float depth_variance = particle_depth_values.y - depth_mean*depth_mean; + float background_depth = input.linear_depth; + + float diff = max(background_depth - depth_mean, 0.0); + float C = 1.38629436112; // 2 * ln(2) + float T = exp2(-diff*diff / (C * max(depth_variance, 0.001))); + float alpha_modifier = TEX2D(hdr_transparent_div4, screen_uv).a * (1.0 - T); + c.a *= 1.0 - alpha_modifier; + #endif + + float alpha = c.a; + c.rgb *= alpha; + #ifdef BLEND_ADDITIVE + c.a = 0.0; + #endif + + #ifdef LOW_RES + PS_OUTPUT o; + o.color = c; + float alpha_depth = input.linear_depth * alpha; + o.depth = half4(alpha_depth, alpha_depth * alpha_depth, 0.0, alpha); + return o; + #else + #if defined(PARTICLE_DEBUG) && defined(PARTICLE_LIGHTING) + return LIT_PARTICLES_DEBUG_COLOR; + #elif defined(PARTICLE_DEBUG) + return EMISSIVE_PARTICLES_DEBUG_COLOR; + #else + return c; + #endif + #endif + } + #endif + """ + } +} + +shaders = { + billboard = { + // editor_advanced_mode = true + + editor_options = [ + { + name="Quality" + options = [ + { name="High Resolution" define="HIGH_RESOLUTION" tool_tip="Particles are low resolution by default. If this flag is enabled the particle will be rendered in full resolution." } + ] + } + { + name="Misc" + options = [ + { name="Custom FOV" define="HAS_CUSTOM_FOV" } + ] + } + { + name="Turn-up Algorithms" + options = [ + { name="Screen space" define="SCREEN_SPACE" } + { name="Tangent Locked" define="TANGENT_LOCKED" } + { name="External Rotation" define="EXTERNAL_ROTATION" } + ] + } + { + name="Vertex Modifiers" + options = [ + { name="Rotation" define="ROTATION" } + { name="Pivot" define="PIVOT" } + { name="UV Animation" define="UV_ANIMATION" } + { name="UV Scale" define="UV_SCALE" } + ] + } + { + name="Pixel Modifiers" + options = [ + { name="Diffuse Map" define="DIFFUSE_MAP" } + { name="Vertex Color" define="VERTEX_COLOR" } + { name="Soft Particles" define="SOFT_PARTICLES" } + { name="Distortion Particles" define="DISTORTION" } + ] + } + { + name="Lighting" + options = [ + { name="Disable" define="DISABLE_LIGHTING" } + { name="Disable Tessellation" define="DISABLE_TESSELLATION" } + { name="Disable Sun" define="DISABLE_SUN" } + { name="Disable Shadows" define="DISABLE_SHADOWS" } + { name="Disable Local Lights" define="DISABLE_LOCAL_LIGHTS" } + ] + } + { + name="GBuffer Particles" + options = [ + { name="Enable" define="LIT_PARTICLES" } + { name="Normal Map" define="NORMAL_MAP" } + { name="Material Map" define="MATERIAL_MAP" } + { name="One Bit Alpha" define="ONE_BIT_ALPHA" } + { name="Dissolve Using Vertex Alpha" define="DISSOLVE_USING_VERTEX_ALPHA" } + ] + } + { + name="Blending" + options = [ + { name="Additive Blend" define="BLEND_ADDITIVE" } + ] + } + ] + + contexts = { + shadow_caster = { + passes_sort_mode="immediate" + passes = [{ + defined="BLEND_ADDITIVE" + pass = [ + ] + fail = [{ + defined="LIT_PARTICLES" + pass = [ + { hlsl_shader="billboard_depth_only" defines=["GBUFFER_PARTICLES"] render_states="billboard_shadow_caster" } + ] + fail = [ + // { hlsl_shader="billboard_depth_only" render_states="billboard_shadow_caster" } + ] + }] + }] + } + + default = { + passes = [{ + defined="LIT_PARTICLES" + pass = [ + { layer="gbuffer" hlsl_shader="billboard" defines=["GBUFFER_PARTICLES"] render_states="gbuffer_material" } + ] + fail = [ + { + defined="PARTICLE_DEBUG" + pass = [ + { layer="hdr_transparent" hlsl_shader="billboard" render_states="billboard_opacity" } + ] + fail = [ + { + defined="LOW_RES_ENABLED" + pass = [ + { + defined="HIGH_RESOLUTION" + pass = [ + { + defined="SCREEN_SPACE" + pass = [ + { layer="hdr_transparent_screen_space" hlsl_shader="billboard" render_states="billboard_opacity" } + ] + fail = [ + { layer="hdr_transparent" hlsl_shader="billboard" render_states="billboard_opacity" } + ] + } + ] + fail = [ + { + defined="SCREEN_SPACE" + pass = [ + { layer="hdr_transparent_screen_space_low_res" hlsl_shader="billboard" defines=["LOW_RES"] render_states="billboard_opacity" } + ] + fail = [ + { layer="hdr_transparent_low_res" hlsl_shader="billboard" defines=["LOW_RES"] render_states="billboard_opacity" } + ] + } + ] + } + ] + fail = [ + { + defined="SCREEN_SPACE" + pass = [ + { layer="hdr_transparent_screen_space" hlsl_shader="billboard" render_states="billboard_opacity" } + ] + fail = [ + { layer="hdr_transparent" hlsl_shader="billboard" render_states="billboard_opacity" } + ] + } + ] + } + ] + } + ] + }] + } + } + + compile = { + shadow_caster = [ + { if: "on_renderer(D3D11, D3D12, GNM)" } + ] + default = [ + // TODO: tessellation is broken for ps4 atm + // TODO: we only need to create these many permutations for particles that recieve lighting. => PARTICLE_LIGHTING + { if: "on_renderer(D3D11, D3D12, GNM, GL) && render_cap(development) && render_setting(particle_visualization) && !defined(LIT_PARTICLES)" defines=["PARTICLE_DEBUG"] } + { if: "on_renderer(D3D11, D3D12) && on_platform(win32) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, low_res_transparency, particles_local_lighting, particles_receive_shadows, particles_tessellation)" defines=["LOW_RES_ENABLED", "LOCAL_LIGHTS" "SHADOW_RECEIVING" "TESSELLATION"] } + { if: "on_renderer(D3D11, D3D12) && on_platform(win32) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, low_res_transparency, particles_local_lighting, particles_tessellation)" defines=["LOW_RES_ENABLED", "LOCAL_LIGHTS" "TESSELLATION"] } + { if: "on_renderer(D3D11, D3D12, GNM) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, low_res_transparency, particles_local_lighting)" defines=["LOW_RES_ENABLED", "LOCAL_LIGHTS"] } + { if: "on_renderer(D3D11, D3D12, GNM) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && render_setting(low_res_transparency)" defines=["LOW_RES_ENABLED"] } + { if: "on_renderer(D3D11, D3D12) && on_platform(win32) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, particles_local_lighting, particles_receive_shadows, particles_tessellation)" defines=["LOCAL_LIGHTS" "SHADOW_RECEIVING" "TESSELLATION"] } + { if: "on_renderer(D3D11, D3D12) && on_platform(win32) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, particles_local_lighting, particles_tessellation)" defines=["LOCAL_LIGHTS" "TESSELLATION"] } + { if: "on_renderer(D3D11, D3D12, GNM) && !defined(LIT_PARTICLES) && !defined(DISTORTION) && !defined(BLEND_ADDITIVE) && !defined(DISABLE_LIGHTING) && render_setting(local_lights, particles_local_lighting)" defines=["LOCAL_LIGHTS"] } + { if: "on_renderer(D3D11, D3D12, GNM, GL)" defines=[] } + ] + } + } +} diff --git a/vmf_source/core/stingray_renderer/shader_libraries/particle_lighting_common.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/particle_lighting_common.shader_source new file mode 100644 index 0000000..3dbbcf1 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/particle_lighting_common.shader_source @@ -0,0 +1,551 @@ +hlsl_shaders = { + particle_debug = { + code=""" + #define LIT_PARTICLES_DEBUG_COLOR float4(0.01, 0.0, 0.0, 0.01) + #define DISTORTION_PARTICLES_DEBUG_COLOR float4(0.0, 0.01, 0.0, 0.01) + #define EMISSIVE_PARTICLES_DEBUG_COLOR float4(0.0, 0.0, 0.01, 0.01) + """ + } + + radiosity_normal_mapping = { + code=""" + #if defined(PARTICLE_LIGHTING) + #if defined(D3D11) || defined(D3D12) || defined(GNM) + #define BACK_LIGHTING + #endif + + #define NEEDS_WORLD_POS + + #if defined(TESSELLATION) && defined(DISABLE_TESSELLATION) + #undef TESSELLATION + #endif + + #if defined(SHADOW_RECEIVING) && (defined(DISABLE_SHADOWS)|| defined(DISABLE_SUN)) + #undef SHADOW_RECEIVING + #endif + + #if defined(LOCAL_LIGHTS) && defined(DISABLE_LOCAL_LIGHTS) + #undef LOCAL_LIGHTS + #endif + + #define INV_PI 0.31830988618379067153 + + #if 0 + CBUFFER_START(c_particle_lighting) + UNIFORM float ambient_diffuse_fade_type; + UNIFORM float3 ambient_diffuse_fade; + UNIFORM float3 ambient_tint; + #if !defined(DISABLE_SUN) + UNIFORM float sun_enabled; + #endif + #if defined(LOCAL_LIGHTS) + UNIFORM float n_frustum_lights; + UNIFORM float4x4 frustum_lights[50]; + #endif + #ifdef SHADOW_RECEIVING + UNIFORM float4x4 sun_world_to_shadow_slice3; + #endif + CBUFFER_END + + DECLARE_SAMPLER_CUBE(global_diffuse_map); + + #if defined(SHADOW_RECEIVING) + #if defined(DX10_STYLE_SAMPLERS) + SamplerComparisonState shadow_map_sampler; + Texture2D sun_shadow_map; + #else + DECLARE_SAMPLER_2D(sun_shadow_map); + #endif + + half shadow_intensity(float4 sm_pos) { + float2 sm_resolution; + #ifdef DX10_STYLE_SAMPLERS + sun_shadow_map.GetDimensions(sm_resolution.x, sm_resolution.y); + #else + sm_resolution = float2(1024.0, 1024.0); + #endif + float4 tscale = float4(1.f / sm_resolution.x, 1.f / sm_resolution.y, 0.f, 0.f); + + half a = 0.0; + for( float xx = -0.5; xx <= 0.5; xx += 1.0 ) { + for( float yy = -0.5; yy <= 0.5; yy += 1.0 ) { + #if defined(D3D11) || defined(D3D12) + a += sun_shadow_map.SampleCmpLevelZero(shadow_map_sampler, sm_pos.xy + (float2( xx, yy ) * tscale.xy), sm_pos.z); + #elif defined(GNM) + a += sun_shadow_map.SampleCmpLOD0(shadow_map_sampler, sm_pos.xy + (float2( xx, yy ) * tscale.xy), sm_pos.z); + #else + a += TEX2Dproj(sun_shadow_map, sm_pos + (float4(xx, yy, 0.0, 0.0) * tscale)); + #endif + } + } + + return a * 0.25; + } + #endif + + #ifdef SCREEN_SPACE + static const float3 hl2_basis0 = float3(0, 0.57735026918962576450914878050196, 0.81649658092772603273242802490196); // float3(0, 1/sqrt(3), sqrt(2/3)); + static const float3 hl2_basis1 = float3(0.70710678118654752440084436210485, 0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(1/sqrt(2), 1/sqrt(3), -1/sqrt(6)); + static const float3 hl2_basis2 = float3(-0.70710678118654752440084436210485, 0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(-1/sqrt(2), 1/sqrt(3), -1/sqrt(6)); + #else + static const float3 hl2_basis0 = float3(0, -0.57735026918962576450914878050196, 0.81649658092772603273242802490196); // float3(0, -1/sqrt(3), sqrt(2/3)); + static const float3 hl2_basis1 = float3(0.70710678118654752440084436210485, -0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(1/sqrt(2), -1/sqrt(3), -1/sqrt(6)); + static const float3 hl2_basis2 = float3(-0.70710678118654752440084436210485, -0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(-1/sqrt(2)), -1/sqrt(3), -1/sqrt(6)); + #endif + + void calc_basis_lighting(out float4 basis0, out float4 basis1, out float4 basis2, out float3 back_lighting, float3 wp, float3 normal, float4x4 view, float4 position) { + // Rotate hl2 basis into world space - move to CPU + float3 cs_hl2_basis0 = mul(hl2_basis0, to_mat3(camera_world)); + float3 cs_hl2_basis1 = mul(hl2_basis1, to_mat3(camera_world)); + float3 cs_hl2_basis2 = mul(hl2_basis2, to_mat3(camera_world)); + + basis0 = float4(0.0, 0.0, 0.0, 1.0); + basis1 = float4(0.0, 0.0, 0.0, 1.0); + basis2 = float4(0.0, 0.0, 0.0, 1.0); + back_lighting = float3(0.0, 0.0, 0.0); + + float3 n = normal; + basis0.w = n.x; + basis1.w = n.y; + basis2.w = n.z; + + #ifdef SCREEN_SPACE + //wp = camera_pos + view._m01_m11_m21 * camera_near_far.x + 0.3*mul(normalize(float3(wp.x, 1.f, wp.y)), to_mat3(camera_world)); + // do some form of spherical projection of the screen space particles onto the near plane + wp = camera_pos + camera_near_far.x *mul(normalize(new_half3(wp.x, 1.f, wp.y)), to_mat3(camera_world)); + #endif + + #if defined(D3D11) + ambient_tint = (capture_cubemap == 1) ? 1.0 : ambient_tint; + #endif + + // TODO: make a branchless solution? Do we use this? + float3 diffuse_tint = ambient_tint; + float fade_type = ambient_diffuse_fade_type; + float3 fade_params = ambient_diffuse_fade; + [branch] + if(fade_type != 0.0) { + if(fade_type == 1.0) { + diffuse_tint *= max(fade_params.x, 1.0 - exp((wp.z - fade_params.y)/fade_params.z)); + } else if(fade_type == 2.0) { + diffuse_tint *= max(fade_params.x, 1.0 - exp((fade_params.y - wp.z)/fade_params.z)); + } else { + diffuse_tint *= max(fade_params.x, 1.0 - exp(-abs(fade_params.y - wp.z)/fade_params.z)); + } + } + + #if defined(RENDERER_GL) + float3 V = new_half3(view[1].x, view[1].y, view[1].z); + #else + float3 V = view._m01_m11_m21; + #endif + + #if !defined(DISABLE_SUN) + if (sun_enabled == 1.0) { + #ifdef SHADOW_RECEIVING + float4 sm_pos = mul(float4(wp, 1), sun_world_to_shadow_slice3); + float sun_shadow = saturate(shadow_intensity(sm_pos)); + sun_color *= sun_shadow; + #endif + + // setup global lighting + float3 sdir = normalize(-sun_direction); + float3 sun_weights = saturate(new_half3(dot(sdir, cs_hl2_basis0), dot(sdir, cs_hl2_basis1), dot(sdir, cs_hl2_basis2))); + basis0.xyz = sun_weights.x * sun_color; + basis1.xyz = sun_weights.y * sun_color; + basis2.xyz = sun_weights.z * sun_color; + + #if defined(BACK_LIGHTING) + #if defined(SCREEN_SPACE) + back_lighting = saturate(dot(sdir, -V)) * sun_color; + #else + back_lighting = saturate(dot(sdir, V)) * sun_color; + #endif + + #if defined(SHADOW_RECEIVING) && defined(SUN_SCATTER_FALLOFF) + float sf = scatter_falloff(sm_pos); + back_lighting *= sun_shadow; + sf = pow(sf + 0.001, 0.3); + back_lighting *= sf; //saturate(0.05 + sf*0.95); + #else + back_lighting *= 0.25f; + #endif + #endif + } + #endif + + basis0.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis0, 0.0)) * diffuse_tint; + basis1.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis1, 0.0)) * diffuse_tint; + basis2.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis2, 0.0)) * diffuse_tint; + + // do local lights + #if defined(LOCAL_LIGHTS) + // const int type_omni = 0; + const int type_spot = 1; + int light_count = (int)n_frustum_lights; + for (int idx = 0; idx < light_count; ++idx) { + #if defined(RENDERER_GL) + float3 lpos = float3(frustum_lights[idx][0].x, frustum_lights[idx][1].x, frustum_lights[idx][2].x); + float3 ldir = float3(frustum_lights[idx][0].y, frustum_lights[idx][1].y, frustum_lights[idx][2].y); + int ltype = (int)frustum_lights[idx][3].x; + float3 lfalloff = float3(frustum_lights[idx][0].z, frustum_lights[idx][1].z, frustum_lights[idx][2].z); + float2 lspotfalloff = float2(frustum_lights[idx][3].y, frustum_lights[idx][3].z); + float3 lcol = float3(frustum_lights[idx][0].w, frustum_lights[idx][1].w, frustum_lights[idx][2].w); + #else + float3 lpos = frustum_lights[idx]._m00_m01_m02; + float3 ldir = frustum_lights[idx]._m10_m11_m12; + int ltype = (int)frustum_lights[idx]._m03; + float3 lfalloff = frustum_lights[idx]._m20_m21_m22; + float2 lspotfalloff = frustum_lights[idx]._m13_m23; + float3 lcol = frustum_lights[idx]._m30_m31_m32; + #endif + + float3 light_vector = lpos - wp; // This used to be center.. validate if not wp is better + float l = length(light_vector) + 0.00001; + float attn = light_attenuation(l, lfalloff.x, lfalloff.y); + light_vector /= l; + float spot_angle = 1.0 - dot(light_vector, -ldir); + attn *= ltype == type_spot ? (spot_angle > lspotfalloff.x ? 1.0 - saturate((spot_angle-lspotfalloff.x)*lspotfalloff.y) : 1.0) : 1.0; + + float3 lambert_atten = saturate(float3(dot(light_vector, cs_hl2_basis0), dot(light_vector, cs_hl2_basis1), dot(light_vector, cs_hl2_basis2))); + float3 col = attn * lcol; + basis0.xyz += lambert_atten.x * col; + basis1.xyz += lambert_atten.y * col; + basis2.xyz += lambert_atten.z * col; + #if defined(BACK_LIGHTING) + back_lighting += saturate(dot(light_vector, V)) * col;// * 0.25; + #endif + } + #endif + } + #else + #if defined(LOCAL_LIGHTS) + DECLARE_CLUSTER_DATA(cs_cluster_buffer); + DECLARE_LIGHT_INDEX_DATA(cs_light_index_buffer); + DECLARE_LIGHT_DATA(cs_light_data_buffer); + #if defined(SHADOW_RECEIVING) + DECLARE_LIGHT_SHADOW_MATRICES(cs_light_shadow_matrices_buffer); + + DECLARE_COMPARISON_SAMPLER_2D(local_lights_shadow_atlas); + #if !defined(DISABLE_SUN) + DECLARE_COMPARISON_SAMPLER_2D(sun_shadow_map); + DECLARE_COMPARISON_SAMPLER_2D(static_sun_shadow_map); + #endif + #endif + #endif + #if !defined(CALCULATE_LIGHTING) + CBUFFER_START(lighting_data) + UNIFORM float3 ambient_tint; + + #if !defined(DISABLE_SUN) + UNIFORM float sun_shadows_enabled; + UNIFORM float sun_enabled; + + #if defined(SHADOW_RECEIVING) + UNIFORM float ssm_enabled; + UNIFORM float4x4 ssm_shadow_rotation; + #endif + #endif + + UNIFORM float ambient_diffuse_fade_type; + UNIFORM float3 ambient_diffuse_fade; // min multiplier, height offset, falloff + CBUFFER_END + #endif + + DECLARE_SAMPLER_CUBE(global_diffuse_map); + + #ifdef SCREEN_SPACE + static const float3 hl2_basis0 = float3(0, 0.57735026918962576450914878050196, 0.81649658092772603273242802490196); // float3(0, 1/sqrt(3), sqrt(2/3)); + static const float3 hl2_basis1 = float3(0.70710678118654752440084436210485, 0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(1/sqrt(2), 1/sqrt(3), -1/sqrt(6)); + static const float3 hl2_basis2 = float3(-0.70710678118654752440084436210485, 0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(-1/sqrt(2), 1/sqrt(3), -1/sqrt(6)); + #else + static const float3 hl2_basis0 = float3(0, -0.57735026918962576450914878050196, 0.81649658092772603273242802490196); // float3(0, -1/sqrt(3), sqrt(2/3)); + static const float3 hl2_basis1 = float3(0.70710678118654752440084436210485, -0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(1/sqrt(2), -1/sqrt(3), -1/sqrt(6)); + static const float3 hl2_basis2 = float3(-0.70710678118654752440084436210485, -0.57735026918962576450914878050196, -0.40824829046386301636621401245098); // float3(-1/sqrt(2)), -1/sqrt(3), -1/sqrt(6)); + #endif + + // Rotate hl2 basis into world space - move to CPU + static const float3 cs_hl2_basis0 = mul(hl2_basis0, to_mat3(camera_world)); + static const float3 cs_hl2_basis1 = mul(hl2_basis1, to_mat3(camera_world)); + static const float3 cs_hl2_basis2 = mul(hl2_basis2, to_mat3(camera_world)); + + void accumulate_basis_lighting(float3 L, float3 V, float3 light_color, float attn, inout float4 basis0, inout float4 basis1, inout float4 basis2, inout float3 back_lighting) { + float3 lambert_atten = saturate(float3(dot(L, cs_hl2_basis0), dot(L, cs_hl2_basis1), dot(L, cs_hl2_basis2))); + float3 color = light_color * attn; + basis0.xyz += lambert_atten.x * color; + basis1.xyz += lambert_atten.y * color; + basis2.xyz += lambert_atten.z * color; + #if defined(BACK_LIGHTING) + back_lighting += saturate(dot(L, V)) * color;// * 0.25; + #endif + } + + void clustered_shading(CLUSTER_DATA_TYPE cluster_data, + LIGHT_INDEX_DATA_TYPE light_index_data, + LIGHT_DATA_TYPE light_data, + #if defined(SHADOW_RECEIVING) + LIGHT_SHADOW_MATRICES_TYPE light_shadow_matrices, + ComparisonSampler2D local_lights_shadow_atlas, + #endif + const float3 world_pos, + const float3 V, + const float3 N, + const float2 screen_pos, + const float depth, + inout float4 basis0, + inout float4 basis1, + inout float4 basis2, + inout float3 back_lighting) + { + if (depth > cs_cluster_max_depth_inv_max_depth.x) + return; + + uint2 cluster_info; + sample_cluster_data(cluster_data, screen_pos, depth, cluster_info); + + uint light_index = cluster_info.x; + int point_light_count = int(cluster_info.y & 0x00FFU); + for (int pl = 0; pl < point_light_count; ++pl) { + uint index; + sample_light_index_data(light_index_data, light_index, index); + float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir; + float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5; + float shadow_fade; + sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade); + + float3 L = light_position - world_pos; + float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + accumulate_basis_lighting(L, V, light_color, attn, basis0, basis1, basis2, back_lighting); + //translucency += calculate_translucency(L, V, N, attn * density, base_color, light_color, density); + ++light_index; + } + + int spot_light_count = int((cluster_info.y >> 16U) & 0x00FFU); + for (int sl = 0; sl < spot_light_count; ++sl) { + uint index; + sample_light_index_data(light_index_data, light_index, index); + float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir; + float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5; + float shadow_fade; + sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade); + + float3 L = light_position - world_pos; + float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + attn *= spot_angle_attenuation(dot(-L, spot_dir), spot_light_falloff.x, spot_light_falloff.y); + accumulate_basis_lighting(L, V, light_color, attn, basis0, basis1, basis2, back_lighting); + //translucency += calculate_translucency(L, V, N, attn * density, base_color, light_color, density); + + ++light_index; + } + + int shadow_casting_point_light_count = int((cluster_info.y >> 8U) & 0x00FFU); + for (int scpl = 0; scpl < shadow_casting_point_light_count; ++scpl) { + uint index; + sample_light_index_data(light_index_data, light_index, index); + float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir; + float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5; + float shadow_fade; + sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade); + + float3 L = light_position - world_pos; + float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + + #if defined(SHADOW_RECEIVING) + // Based on the biggest component of the vector from the shaded position to the light source and its sign, chose the correct + // shadow map index to get the correct world position to shadow map matrix. + float3 shadow_L = -L; + int3 is_positive = int3(greaterThan(shadow_L, float3(0.0, 0.0, 0.0))); + float3 abs_shadow_L = abs(shadow_L); + int test_index = (abs_shadow_L.x > abs_shadow_L.y && abs_shadow_L.x > abs_shadow_L.z) ? 0 + is_positive[0]: (abs_shadow_L.y > abs_shadow_L.z) ? 2 + is_positive[1] : 4 + is_positive[2]; + + // The shadows from the faces of an onmni light are populated in the following order and directions. + // float3(-1.0f, 0.0f, 0.0f), + // float3( 1.0f, 0.0f, 0.0f), + // float3( 0.0f, -1.0f, 0.0f), + // float3( 0.0f, 1.0f, 0.0f), + // float3( 0.0f, 0.0f, -1.0f) + // float3( 0.0f, 0.0f, 1.0f), + //fix this crap about the sm_index potentially being -1.0 which indicates that the light isn't casting any shadows.... + float sm_index = test_index == 0? sm_index0 : + test_index == 1? sm_index1 : + test_index == 2? sm_index2 : + test_index == 3? sm_index3 : + test_index == 4? sm_index4 : + sm_index5; + + float shadow_intensity = 1.0f; + if (sm_index != -1.0f) { + float4x4 world_to_sm; + sample_light_shadow_matrix(light_shadow_matrices, uint(sm_index), world_to_sm); + float4 sm_pos = mul(float4(world_pos, 1.0), world_to_sm); + sm_pos.xyz /= sm_pos.w; + sm_pos.z = apply_local_shadow_depth_comparison_bias(depth, sm_pos.z); + shadow_intensity = shadow_intensity_2d(local_lights_shadow_atlas, cs_shadow_atlas_size.xy, sm_pos.xy, sm_pos.z); + shadow_intensity = (shadow_intensity - 1.0) * shadow_fade + 1.0; + } + attn *= shadow_intensity; + #endif + accumulate_basis_lighting(L, V, light_color, attn, basis0, basis1, basis2, back_lighting); + //translucency += calculate_translucency(L, V, N, attn * lerp(lerp(0.5, 1.0, shadow_intensity), shadow_intensity, density), base_color, light_color, density); + + ++light_index; + } + + int shadow_casting_spot_light_count = int((cluster_info.y >> 24U) & 0x00FFU); + for (int scsl = 0; scsl < shadow_casting_spot_light_count; ++scsl) { + uint index; + sample_light_index_data(light_index_data, light_index, index); + float3 light_position, light_color, light_falloff, spot_light_falloff, spot_dir; + float sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5; + float shadow_fade; + sample_light_data(light_data, index, light_position, light_color, light_falloff, spot_light_falloff, spot_dir, sm_index0, sm_index1, sm_index2, sm_index3, sm_index4, sm_index5, shadow_fade); + + float3 L = light_position - world_pos; + float len_L = length(L) + 0.00001f; + L /= len_L; + + float attn = light_attenuation(len_L, light_falloff.x, light_falloff.y); + attn *= spot_angle_attenuation(dot(-L, spot_dir), spot_light_falloff.x, spot_light_falloff.y); + + #if defined(SHADOW_RECEIVING) + float4x4 world_to_sm; + sample_light_shadow_matrix(light_shadow_matrices, uint(sm_index5), world_to_sm); + float4 sm_pos = mul(float4(world_pos, 1.0), world_to_sm); + sm_pos.xyz /= sm_pos.w; + sm_pos.z = apply_local_shadow_depth_comparison_bias(depth, sm_pos.z); + float shadow_intensity = shadow_intensity_2d(local_lights_shadow_atlas, cs_shadow_atlas_size.xy, sm_pos.xy, sm_pos.z); + shadow_intensity = (shadow_intensity - 1.0) * shadow_fade + 1.0; + attn *= shadow_intensity; + #endif + + accumulate_basis_lighting(L, V, light_color, attn, basis0, basis1, basis2, back_lighting); + //translucency += calculate_translucency(L, V, N, attn * lerp(lerp(0.5, 1.0, shadow_intensity), shadow_intensity, density), base_color, light_color, density); + + ++light_index; + } + } + + void calc_basis_lighting(out float4 basis0, out float4 basis1, out float4 basis2, out float3 back_lighting, float3 wp, float3 N, float4x4 view, float4 position) { + basis0 = float4(0.0, 0.0, 0.0, 1.0); + basis1 = float4(0.0, 0.0, 0.0, 1.0); + basis2 = float4(0.0, 0.0, 0.0, 1.0); + back_lighting = float3(0.0, 0.0, 0.0); + + basis0.w = N.x; + basis1.w = N.y; + basis2.w = N.z; + + #ifdef SCREEN_SPACE + //wp = camera_pos + view._m01_m11_m21 * camera_near_far.x + 0.3*mul(normalize(float3(wp.x, 1.f, wp.y)), to_mat3(camera_world)); + // do some form of spherical projection of the screen space particles onto the near plane + wp = camera_pos + camera_near_far.x * mul(normalize(new_half3(wp.x, 1.f, wp.y)), to_mat3(camera_world)); + #endif + + #if defined(D3D11) + ambient_tint = (capture_cubemap == 1) ? 1.0 : ambient_tint; + #endif + + // TODO: make a branchless solution? Do we use this? + float3 diffuse_tint = ambient_tint; + float fade_type = ambient_diffuse_fade_type; + float3 fade_params = ambient_diffuse_fade; + [branch] + if(fade_type != 0.0) { + if(fade_type == 1.0) { + diffuse_tint *= max(fade_params.x, 1.0 - exp((wp.z - fade_params.y)/fade_params.z)); + } else if(fade_type == 2.0) { + diffuse_tint *= max(fade_params.x, 1.0 - exp((fade_params.y - wp.z)/fade_params.z)); + } else { + diffuse_tint *= max(fade_params.x, 1.0 - exp(-abs(fade_params.y - wp.z)/fade_params.z)); + } + } + + const float3 view_dir = camera_world._m30_m31_m32 - wp; + const float3 V = normalize(view_dir); + #if defined(SHADOW_RECEIVING) || defined(LOCAL_LIGHTS) + const float3 camera_dir = camera_world._m10_m11_m12; + const float depth = dot(-view_dir, camera_dir); + // const float3 shadow_biased_pos = wp - (depth - apply_shadow_bias(depth)) * (-V); + #endif + + #if !defined(DISABLE_SUN) + if (sun_enabled) { + #if defined(SHADOW_RECEIVING) + float shadow = 1.0; + if(sun_shadows_enabled && sun_shadows) { + shadow = saturate(calculate_shadow_intensity(sun_shadow_map, static_sun_shadow_map, wp, depth)); + } else if(ssm_enabled) { + shadow = saturate(calculate_static_shadow_intensity(static_sun_shadow_map, wp, depth)); + } + sun_color *= shadow; + #endif + + // setup global lighting + float3 L = normalize(-sun_direction); + float3 sun_weights = saturate(new_half3(dot(L, cs_hl2_basis0), dot(L, cs_hl2_basis1), dot(L, cs_hl2_basis2))); + basis0.xyz = sun_weights.x * sun_color; + basis1.xyz = sun_weights.y * sun_color; + basis2.xyz = sun_weights.z * sun_color; + + #if defined(BACK_LIGHTING) + #if defined(SCREEN_SPACE) + back_lighting = saturate(dot(L, -V)) * sun_color; + #else + back_lighting = saturate(dot(L, V)) * sun_color; + #endif + + #if defined(SHADOW_RECEIVING) && defined(SUN_SCATTER_FALLOFF) + float sf = scatter_falloff(sm_pos); + back_lighting *= sun_shadow; + sf = pow(sf + 0.001, 0.3); + back_lighting *= sf; //saturate(0.05 + sf*0.95); + #else + back_lighting *= 0.25f; + #endif + #endif + } + #endif + + basis0.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis0, 0.0)) * diffuse_tint; + basis1.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis1, 0.0)) * diffuse_tint; + basis2.xyz += rgbm_decode(TEXCUBELOD(global_diffuse_map, cs_hl2_basis2, 0.0)) * diffuse_tint; + + #if defined(LOCAL_LIGHTS) + // Transform Normalized Display Coordinates to view port coordinates + const float2 clip_space = (float2(position.x, -position.y)/position.w + 1.0)*0.5; + const float2 view_space = clip_space * (back_buffer_size * viewport.xy - 1.0) + 0.5; // I assume that SV_POSITION is in range of 0.5 to viewport.width - 0.5 + + clustered_shading(cs_cluster_buffer, cs_light_index_buffer, cs_light_data_buffer, + #if defined(SHADOW_RECEIVING) + cs_light_shadow_matrices_buffer, local_lights_shadow_atlas, + #endif + wp, V, N, view_space, depth, basis0, basis1, basis2, back_lighting); + #endif + } + #endif + + float3 calc_lighting(const float4 base_color_alpha, const float3 N, const float3 back_lighting, const float3 basis0, const float3 basis1, const float3 basis2) { + float3 weights = saturate(float3(dot(N, hl2_basis0), dot(N, hl2_basis1), dot(N, hl2_basis2))); + float3 light = weights.x * basis0 + weights.y * basis1 + weights.z * basis2; + + #if defined(BACK_LIGHTING) + float back_light_scale = saturate(1.0-(base_color_alpha.a*0.5+0.25)); + back_light_scale *= back_light_scale; + light += back_lighting * back_light_scale; + #endif + + return base_color_alpha.rgb * light * INV_PI; + } + #endif + """ + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/particle_ribbon.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/particle_ribbon.shader_source new file mode 100644 index 0000000..d1ae6c1 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/particle_ribbon.shader_source @@ -0,0 +1,171 @@ +includes = ["core/stingray_renderer/shader_libraries/common.shader_source" ] +render_states = { + + ribbon_opacity = { + inherits = "opacity" + states = { + blend_op = "blend_op_add" + dest_blend = "blend_inv_src_alpha" + src_blend = "blend_one" + + defined_WIREFRAME = { + fill_mode = "fill_wireframe" + } + defined_SCREEN_SPACE = { + cull_mode = "cull_none" + z_enable = "false" + } + ndefined_SCREEN_SPACE = { + z_enable = "true" + } + defined_LOW_RES = { + write_mask0 = "red|green|blue|alpha" + write_mask1 = "red|green" + } + ndefined_LOW_RES = { + write_mask0 = "red|green|blue" + } + } + } +} + +hlsl_shaders = { + + ribbon = { + includes = [ "common" ] + + samplers = { + defined_DIFFUSE_MAP = { + diffuse_map = { sampler_states = "wrap_anisotropic_srgb" } + } + } + + code = """ + + #if defined(DIFFUSE_MAP) + #define UV0 + DECLARE_SAMPLER_2D(diffuse_map); + #endif + + CBUFFER_START(c_billboard) + float4x4 view; + float4x4 view_proj; + CBUFFER_END + + + struct VS_INPUT { + float4 position : POSITION; + float strip_info : POSITION1; + float2 size : TEXCOORD7; + #if defined(VERTEX_COLOR) + float4 color : COLOR; + #endif + #if defined(TANGENT_TO_NEXT) + float3 tangent : TANGENT; + float ribbon_distance : TEXCOORD6; + #endif + + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(VERTEX_COLOR) + float4 color : COLOR; + #endif + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + #if defined(TANGENT_TO_NEXT) + float3 y = input.tangent; + float3 x = normalize(cross(normalize(input.position.xyz - camera_pos), y)); + #else + float3 x = view._m00_m10_m20; + float3 y = view._m02_m12_m22; + #endif + + float3 x_axis = x; + float3 y_axis = y; + + float corner = input.strip_info * (input.size.x * 0.5); + + float3 wp = input.position.xyz + (x_axis * corner); + + float4 p = mul(float4(wp, 1), view_proj); + o.position = p; + + #if defined(VERTEX_COLOR) + //#if defined(TANGENT_TO_NEXT) + // float4 dist = fmod(input.ribbon_distance, 2) / 2; // (input.ribbon_distance * 0.05f).xxxx; + // o.color = dist; + //#else + o.color = decode_vertex_color(input.color); + //#endif + #endif + + #if defined(UV0) + float2 uv = float2(input.ribbon_distance, input.strip_info * 0.5 + 0.5); + o.uv = uv; + #endif + + + return o; + } + + float4 fast_gamma_to_linear(float4 c) { + return c * c; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + + #if defined(DIFFUSE_MAP) + float4 c = TEX2D(diffuse_map, input.uv); + #else + float4 c = float4(1,1,1,1); + #endif + + #if defined(VERTEX_COLOR) + float4 vc = input.color; + vc = fast_gamma_to_linear(vc); + c *= vc; + #endif + + float alpha = c.a; + c.rgb *= alpha; + #ifdef BLEND_ADDITIVE + c.a = 0.0; + #endif + + return c; + } + + """ + } +} + +shaders = { + ribbon = { + + contexts = { + default = { + passes = [ + { layer="hdr_transparent" hlsl_shader="ribbon" render_states="ribbon_opacity" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/placeholders.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/placeholders.shader_source new file mode 100644 index 0000000..c73ac01 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/placeholders.shader_source @@ -0,0 +1,584 @@ +// This whole file consist of place holder shaders -- will be removed and authored using the graph based system soon. + +// In this context include refers to another shader file +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source" ] + +render_states = { + ambient_no_depth_write = { + inherits = "ambient" + states = { + z_write_enable = "false" + } + } + filter = { + inherits = "default" + states = { + cull_mode = "cull_none" + z_write_enable = "false" + z_enable = "false" + } + } + wireframe = { + inherits = "opacity" + states = { + fill_mode = "fill_wireframe" + z_write_enable = "true" + z_func = "less" + defined_D3D11 = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + defined_D3D12 = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + } +} + +hlsl_shaders = { + gbuffer_base = { + includes = [ "common", "gbuffer_access", "taa_offsets" ] + + samplers = { + // global_diffuse_map = { sampler_states = "clamp_linear"} + } + + glsl_code = """ + CBUFFER_START(c_per_object) + UNIFORM mat4 world_view_proj; + UNIFORM mat4 world; + UNIFORM vec3 diffuse_rgb; + CBUFFER_END + + #if defined(VC_TINT_RGB) + #define VERTEX_COLOR + #endif + + #if defined(STAGE_VERTEX) + layout(location = POSITION0) in vec4 in_position; + layout(location = NORMAL) in vec3 in_normal; + + #if defined(VERTEX_COLOR) + layout(location = COLOR0) in vec4 in_color; + out vec4 v_color; + #endif + + out vec3 v_normal; + + void main() { + v_normal = in_normal * mat3(world); + + #if defined(VERTEX_COLOR) + #if defined(VC_COMPRESSED) + v_color = decode_vertex_color(in_color); + #else + v_color = in_color; + #endif + #endif + + gl_Position = in_position * world_view_proj; + } + #elif defined(STAGE_FRAGMENT) + GBUFFER_OUTPUT; + in vec3 v_normal; + + #if defined(VERTEX_COLOR) + in vec4 v_color; + #endif + + DECLARE_SAMPLER_CUBE(global_diffuse_map); + + void main() { + // Base color + out_base_color = gbuffer_encode_base_color(diffuse_rgb); + #if defined(VC_TINT_RGB) + out_base_color *= fast_gamma_to_linear_rgb(v_color.rgb); + #endif + + // Normal + mediump vec3 wn = normalize(v_normal); + out_normal = gbuffer_encode_normal(wn); + + // Metallic + out_metallic = gbuffer_encode_metallic_mask(0.0); + + // Roughness + out_roughness = gbuffer_encode_roughness(0.75); + + // Ambient Diffuse + mediump vec3 ambient = rgbm_decode(TEXCUBELOD(global_diffuse_map, wn, 0.0)); + out_ambient_diffuse_light = gbuffer_encode_ambient_diffuse_light(ambient); + + out_ambient_occlusion = gbuffer_encode_ambient_occlusion(1.0f); + } + #endif + """ + + code = """ + #if defined(VC_TINT_RGB) + #define VERTEX_COLOR + #endif + + struct VS_INPUT { + float4 position : POSITION; + float3 normal : NORMAL0; + #if defined(VERTEX_COLOR) + float4 color : COLOR0; + #endif + + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float3 normal : TEXCOORD0; + #if defined(VERTEX_COLOR) + float4 color : COLOR0; + #endif + #if defined(MOTION_BLUR) + float3 last_clip_position : TEXCOORD1; + #endif + }; + + CBUFFER_START(c_per_object) + float4x4 world_view_proj; + float4x4 world; + float4x4 last_world; + float3 diffuse_rgb; // exports={ name="Diffuse RGB" type="vector3" value=[0.5 0.5 0.5] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001] } + float4 dev_wireframe_color; + CBUFFER_END + + // DECLARE_SAMPLER_CUBE(global_diffuse_map); + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + float4 p = mul(input.position, world_view_proj); + + #if defined(DRAW_WIREFRAME) + o.position = p; + #else + float4 view_space = p / p.w; + // Multiply the halton offset by 2 since we are adding the offset in view space (non projected frustrum width and height ranges [-1, 1]) + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + #endif + + o.normal = mul(input.normal, (float3x3)world); + + #if defined(VERTEX_COLOR) + #if defined(VC_COMPRESSED) + o.color = decode_vertex_color(input.color); + #else + o.color = input.color; + #endif + #endif + + #if defined(MOTION_BLUR) + float4 last_wp = mul(input.position, last_world); + float4 last_clip_pos = mul(last_wp, camera_last_view_projection); + float4 last_view_space = last_clip_pos / last_clip_pos.w; + last_view_space.xy += get_vs_halton_offset(frame_number); + last_view_space.xy = last_view_space.xy * 0.5 + 0.5; + last_view_space.y = 1.0 - last_view_space.y; + last_clip_pos = last_view_space * last_clip_pos.w; + o.last_clip_position = last_clip_pos.xyw; + #endif + + return o; + } + + #ifdef DRAW_WIREFRAME + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return dev_wireframe_color; + } + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + GBUFFER_OUT ps_main(PS_INPUT input) { + GBUFFER_OUT o; + + half4 diffuse = half4(diffuse_rgb, 1); + + #if defined(VC_TINT_RGB) + diffuse.rgb *= fast_gamma_to_linear_rgb(input.color.rgb); + #endif + + float3 wn = normalize(input.normal); + BASE_COLOR(o) = gbuffer_encode_base_color(diffuse.rgb); + NORMAL(o) = gbuffer_encode_normal(wn); + METALLIC(o) = gbuffer_encode_metallic_mask(0); + ROUGHNESS(o) = gbuffer_encode_roughness(0.75); + + // Since we don't have baked lighting, we don't use this + // float3 ambient = rgbm_decode(TEXCUBELOD(global_diffuse_map, wn, 0)); + // AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(ambient); + + #if defined(MOTION_BLUR) + float3 last_clip_pos = input.last_clip_position; + float2 screen_pos = (input.position.xy / back_buffer_size - viewport.zw) / viewport.xy; + float2 last_screen_pos = last_clip_pos.xy / last_clip_pos.z; + VELOCITY(o) = encode_velocity(viewport.xy*(screen_pos - last_screen_pos)); + #else + // Velocity and occlusion is written to gbuffer2. If not written to but + // writes to gbuffer3 are done the gpu on PS4 will crash. + VELOCITY(o) = encode_velocity(float2(0.0, 0.0)); + #endif + + AMBIENT_OCCLUSION(o) = 1.f; + DENSITY(o) = 1.0; + return o; + } + #endif + """ + } + + depth_only = { + includes = [ "common" ] + + samplers = { + } + + glsl_code = """ + CBUFFER_START(c_per_object) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + #if defined(STAGE_VERTEX) + layout(location = POSITION0) in vec4 in_position; + void main() { + gl_Position = in_position * world_view_proj; + } + #elif defined(STAGE_FRAGMENT) + void main() { } + #endif + """ + + code = """ + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main() : SV_TARGET0 { + return float4(1,1,1,1); + } + """ + } + + forward_emissive = { + includes = [ "common", "gbuffer_access" ] + + samplers = { + defined_SKYDOME = { + skydome_map = { sampler_states = "wrap_anisotropic" } + } + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + #if defined(SKYDOME) + out lowp vec2 v_uv; + #endif + + CBUFFER_START(c0) + UNIFORM mat4 world; + UNIFORM mat4 view; + UNIFORM mat4 proj; + CBUFFER_END + + void main() { + HIGHP mat4 v = view; + HIGHP vec4 wp = in_pos * world; + + #ifdef CAMERA_LOCK_XY + v[0][3] = 0.0; + v[1][3] = 0.0; + #ifdef CAMERA_LOCK_Z + v[2][3] = 0.0; + #endif + #endif + + wp = wp * v * proj; + #ifdef PROJECT_TO_FAR_PLANE + wp.z = wp.w; + #endif + #if defined(SKYDOME) + v_uv = in_uv0; + #endif + gl_Position = wp; + } + """ + + fp_code = """ + #if defined(SKYDOME) + in lowp vec2 v_uv; + DECLARE_SAMPLER_2D(skydome_map); + + CBUFFER_START(c1) + UNIFORM float skydome_intensity; + CBUFFER_END + #endif + + layout(location = 0) out vec4 color; + + void main() { + #if defined(SKYDOME) + lowp vec4 c = texture(skydome_map, v_uv); + c.rgb *= skydome_intensity; + color = c; + #else + color = vec4(0.5, 0.5, 0.5, 1); + #endif + } + """ + + code=""" + #if defined(SKYDOME) + #define UV0 + DECLARE_SAMPLER_2D(skydome_map); + #endif + + struct VS_INPUT { + float4 position : POSITION; + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(UV0) + float2 uv : TEXCOORD0; + #endif + #if defined(FOG_BLEND) + float4 w : TEXCOORD1; + #endif + }; + + CBUFFER_START(c0) + #ifdef CAMERA_LOCK_XY + float4x4 world; + float4x4 view; + float4x4 proj; + #endif + float4x4 world_view_proj; + #ifdef SKYDOME + float skydome_u_offset; + float skydome_intensity; + #ifdef TINT_COLOR + float3 skydome_tint_color; + #endif + #endif + CBUFFER_END + + #ifdef FOG_BLEND + CBUFFER_START(c_fog) + // float2 fog_depth_range; + float3 fog_color; + float3 sun_direction; + float3 sun_color; + float3 fog_sun_blend; + float2 skydome_fog_height_falloff; + CBUFFER_END + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) + { + PS_INPUT o; + + float4 position; + position = input.position; + + float4 p; + #if defined(CAMERA_LOCK_XY) + float3 wp = mul(position, world).xyz; + #ifdef CAMERA_LOCK_Z + view._m30_m31_m32 = float3(0,0,0); + #else + view._m30_m31 = float2(0,0); + #endif + p = mul(mul(float4(wp,1),view), proj); + #else + p = mul(position, world_view_proj); + #endif + + #ifdef PROJECT_TO_FAR_PLANE + p.z = p.w; + #endif + + o.position = p; + + #if defined(FOG_BLEND) + o.w = encode_world_pos(o.position, camera_unprojection); + #endif + + #if defined(UV0) + o.uv = input.uv; + #endif + #if defined(MATERIAL_TRANSFER) && defined(UV0) + float2 tmp = input.uv; + tmp.y = 1 - tmp.y; + o.position = float4(tmp * 2 - 1, 0, 1); + #endif + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 + { + half4 c = half4(0,0,0,0); + + #if defined(SKYDOME) + c = TEX2D(skydome_map, input.uv + float2(skydome_u_offset, 0)); + #if defined(SKYDOME_RGBM) + c.rgb = rgbm_decode(c); + #endif + c.rgb *= skydome_intensity; + + #if defined(TINT_COLOR) + c.rgb *= skydome_tint_color; + #endif + + #if defined(FOG_BLEND) + float3 wp = decode_world_pos(input.w, 1); + float3 view_dir = normalize(camera_world._m30_m31_m32 - wp); + float sa = fog_sun_blend.x * pow(saturate(dot(view_dir, sun_direction)), fog_sun_blend.y); + float3 fog_c = lerp(fog_color, fog_sun_blend.z * sun_color, sa); + + // Soft fog-skybox transition + // TODO: make better falloff, e.g. exp(max(x - skydome_fog_height_falloff.x, 0) * skydome_fog_height_falloff.y) + half inv_denom = 1.0 / (skydome_fog_height_falloff.y - skydome_fog_height_falloff.x); + half weight0 = 1.0 - saturate((1.0 - input.uv.g - skydome_fog_height_falloff.x) * inv_denom); + + // Skybox fog calculation + // TODO: would be nice if we could calculate correct fog on skydome two, but then I think we also need to account the height. + /* + float start = fog_depth_range.x; + float end = fog_depth_range.y; + float b = 1.f / (end-start); + half dist = 1000000.0; + half weight1 = clamp(exp(-camera_world._m32 * b) * (1-exp(-dist * -view_dir.z * b)) / -view_dir.z, 0.0, 1.0); + return half4(lerp(c.rgb, fog_c, max(weight0, weight1)), c.a); + */ + + return half4(lerp(c.rgb, fog_c, weight0), 1.0); + #endif + #endif + + //#if defined(MATERIAL_TRANSFER) + // c.rgb = pow(c.rgb, 1.f / 2.2f); + //#endif + + return c; + } + """ + } +} + +shaders = { + skydome = { + editor_options = [ + { + name="Vertex Modifiers" + options = [ + { name="Lock Camera in XY-plane" define="CAMERA_LOCK_XY" } + { name="Lock Camera in Z-plane" define="CAMERA_LOCK_Z" } + ] + } + { + name="Pixel Modifiers" + options = [ + { name="Skydome Texture RGBM encoded" define = "SKYDOME_RGBM" } + { name="Tint Color" define = "TINT_COLOR" } + { name="Blend in fog" define = "FOG_BLEND" } + ] + } + ] + + contexts = { + default = { + passes = [ + { layer="skydome" hlsl_shader="forward_emissive" defines=["PROJECT_TO_FAR_PLANE" "SKYDOME"] render_states="ambient_no_depth_write" } + ] + } + + material_transfer = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="forward_emissive" defines=["MATERIAL_TRANSFER" "SKYDOME"] render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + material_transfer = [ + { defines=[] } + ] + } + } + + base = { + editor_options = [ + ] + + contexts = { + shadow_caster = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="depth_only" render_states="shadow_caster" } + ] + } + + default = { + passes = [ + { layer="gbuffer" hlsl_shader="gbuffer_base" defines=["MOTION_BLUR"] render_states="gbuffer_material" } + { defined="RENDERER_D3D11" + pass = [ + { layer="wireframe" hlsl_shader="gbuffer_base" defines=["DRAW_WIREFRAME"] render_states="wireframe" branch_key="dev_wireframe" } + ] + } + { defined="RENDERER_D3D12" + pass = [ + { layer="wireframe" hlsl_shader="gbuffer_base" defines=["DRAW_WIREFRAME"] render_states="wireframe" branch_key="dev_wireframe" } + ] + } + ] + } + } + + compile = { + shadow_caster = [ + { defines=[] } + ] + default = [ + { defines=[] } + ] + } + } +} diff --git a/vmf_source/core/stingray_renderer/shader_libraries/post_processing.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/post_processing.shader_source new file mode 100644 index 0000000..7ad3a2b --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/post_processing.shader_source @@ -0,0 +1,4006 @@ +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source", + "core/stingray_renderer/shader_libraries/post_processing_common.shader_source" ] + +render_states = { + filter = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "false" + } + } + + filter_farplane = { + inherits = "filter" + states = { + z_enable = "true" + } + } + + filter_fog = { + inherits = "opacity" + states = { + z_write_enable = "false" + z_func = "greater" + } + } + + filter_premultiplied = { + inherits = "filter" + states = { + blend_enable = "true" + blend_op = "blend_op_add" + src_blend = "blend_one" + dest_blend = "blend_inv_src_alpha" + } + } + + merge_ssao = { + inherits = "filter" + states = { + write_mask0 = "red" + blend_enable = "true" + blend_op = "blend_op_min" + dest_blend = "blend_one" + src_blend = "blend_one" + } + } + + repopulate_hiz = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "true" + z_func = "greater" + } + } +} + +sampler_states = { + ssao_sample_mip_index = { + inherits = "clamp_point" + states = { + defined_D3D11 = { + mip_level_index = { variable = "sampler_input_mip_level" } + } + defined_D3D12 = { + mip_level_index = { variable = "sampler_input_mip_level" } + } + } + } + + hiz_sample_mip_index = { + inherits = "clamp_point" + states = { + defined_D3D11 = { + mip_level_index = { variable = "sampler_input_mip_level" } + } + defined_D3D12 = { + mip_level_index = { variable = "sampler_input_mip_level" } + } + } + } + + downsample_mip_index = { + inherits = "clamp_linear" + states = { + defined_D3D11 = { + mip_level_index = { variable = "sampler_input_mip_level" } + } + defined_D3D12 = { + mip_level_index = { variable = "sampler_input_mip_level" } + } + } + } +} + +hlsl_shaders = { + sampling_common = { + code = """ + #if defined(SAMPLE_RGBA) + #define SAMPLE_TYPE float4 + #define SAMPLE_CHANNELS rgba + #elif defined(SAMPLE_RGB) + #define SAMPLE_TYPE float3 + #define SAMPLE_CHANNELS rgb + #else + #error unsupported sample mode + #endif + """ + } + + bicubic_sampling = { + code = """ + SAMPLE_TYPE bicubic_sample_2d(Sampler2D tex, float2 uv, float2 texture_size) { + uv *= texture_size; + float2 inv_texture_size = 1.0/texture_size; + float2 tc = floor(uv-0.5) + 0.5; + + float2 f = uv - tc; + float2 f2 = f * f; + float2 f3 = f * f2; + float4x4 M = { + -1.0, 3.0, -3.0, 1.0, + 3.0, -6.0, 3.0, 0.0, + -3.0, 0.0, 3.0, 0.0, + 1.0, 4.0, 1.0, 0.0 + }; + M /= 6.0; + float4 wx = mul(float4(f3.x, f2.x, f.x, 1), M); + float4 wy = mul(float4(f3.y, f2.y, f.y, 1), M); + float2 w0 = float2(wx.x, wy.x); + float2 w1 = float2(wx.y, wy.y); + float2 w2 = float2(wx.z, wy.z); + float2 w3 = float2(wx.w, wy.w); + + float2 g0 = w0 + w1; + float2 g1 = w2 + w3; + float2 h0 = w1 / g0 - 1; + float2 h1 = w3 / g1 + 1; + + float2 c00 = (tc + h0) * inv_texture_size; + float2 c11 = (tc + h1) * inv_texture_size; + + SAMPLE_TYPE t00 = TEX2D(tex, c00).SAMPLE_CHANNELS; + SAMPLE_TYPE t10 = TEX2D(tex, float2(c11.x, c00.y)).SAMPLE_CHANNELS; + SAMPLE_TYPE t01 = TEX2D(tex, float2(c00.x, c11.y)).SAMPLE_CHANNELS; + SAMPLE_TYPE t11 = TEX2D(tex, c11).SAMPLE_CHANNELS; + + t00 = lerp(t01, t00, g0.y); + t10 = lerp(t11, t10, g0.y); + return lerp(t10, t00, g0.x); + } + """ + } + + lagrange_cubic_sampling = { + code = """ + // From http://mathworld.wolfram.com/LagrangeInterpolatingPolynomial.html + SAMPLE_TYPE lagrange_3rd_degree_interpolation(SAMPLE_TYPE y0, SAMPLE_TYPE y1, SAMPLE_TYPE y2, SAMPLE_TYPE y3, float x) + { + const float x_minus_x0 = x + 1.0; + const float x_minus_x1 = x; + const float x_minus_x2 = x - 1.0; + const float x_minus_x3 = x - 2.0; + const float x_minus_x0_mul_x_minus_x1 = x_minus_x0 * x_minus_x1; + const float x_minus_x2_mul_x_minus_x3 = x_minus_x2 * x_minus_x3; + + SAMPLE_TYPE t0 = y0 * x_minus_x1 * x_minus_x2_mul_x_minus_x3 * -0.1666666f; + SAMPLE_TYPE t1 = y1 * x_minus_x0 * x_minus_x2_mul_x_minus_x3 * 0.5f; + SAMPLE_TYPE t2 = y2 * x_minus_x0_mul_x_minus_x1 * x_minus_x3 * -0.5f; + SAMPLE_TYPE t3 = y3 * x_minus_x0_mul_x_minus_x1 * x_minus_x2 * 0.166666f; + + return t0 + t1 + t2 + t3; + } + + SAMPLE_TYPE lagrange_cubic_sample_2d(Sampler2D tex, float2 uv, float2 texture_size) + { + float2 pixel_size = 1.0 / texture_size; + float2 offseted_uv = uv * texture_size + 0.5; + float2 pixel_coordinate = frac(offseted_uv); + offseted_uv = floor(offseted_uv) / texture_size - pixel_size * 0.5; + int3 st = int3(offseted_uv * texture_size, 0); + + SAMPLE_TYPE c00 = tex.tex.Load(st, int2(-1,-1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c10 = tex.tex.Load(st, int2( 0,-1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c20 = tex.tex.Load(st, int2( 1,-1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c30 = tex.tex.Load(st, int2( 2,-1)).SAMPLE_CHANNELS; + + SAMPLE_TYPE c01 = tex.tex.Load(st, int2(-1, 0)).SAMPLE_CHANNELS; + SAMPLE_TYPE c11 = tex.tex.Load(st, int2( 0, 0)).SAMPLE_CHANNELS; + SAMPLE_TYPE c21 = tex.tex.Load(st, int2( 1, 0)).SAMPLE_CHANNELS; + SAMPLE_TYPE c31 = tex.tex.Load(st, int2( 2, 0)).SAMPLE_CHANNELS; + + SAMPLE_TYPE c02 = tex.tex.Load(st, int2(-1, 1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c12 = tex.tex.Load(st, int2( 0, 1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c22 = tex.tex.Load(st, int2( 1, 1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c32 = tex.tex.Load(st, int2( 2, 1)).SAMPLE_CHANNELS; + + SAMPLE_TYPE c03 = tex.tex.Load(st, int2(-1, 2)).SAMPLE_CHANNELS; + SAMPLE_TYPE c13 = tex.tex.Load(st, int2( 0, 2)).SAMPLE_CHANNELS; + SAMPLE_TYPE c23 = tex.tex.Load(st, int2( 1, 2)).SAMPLE_CHANNELS; + SAMPLE_TYPE c33 = tex.tex.Load(st, int2( 2, 2)).SAMPLE_CHANNELS; + + SAMPLE_TYPE l0 = lagrange_3rd_degree_interpolation(c00, c10, c20, c30, pixel_coordinate.x); + SAMPLE_TYPE l1 = lagrange_3rd_degree_interpolation(c01, c11, c21, c31, pixel_coordinate.x); + SAMPLE_TYPE l2 = lagrange_3rd_degree_interpolation(c02, c12, c22, c32, pixel_coordinate.x); + SAMPLE_TYPE l3 = lagrange_3rd_degree_interpolation(c03, c13, c23, c33, pixel_coordinate.x); + + return lagrange_3rd_degree_interpolation(l0, l1, l2, l3, pixel_coordinate.y); + } + + // Samples a texture with Catmull-Rom filtering, using 9 bilinear texture fetches instead of 16 point fetches. + // See http://vec3.ca/bicubic-filtering-in-fewer-taps/ for more details + // Credits: https://twitter.com/MyNameIsMJP/status/777783169835675648 + SAMPLE_TYPE catmull_rom_sample_2d(Sampler2D tex, float2 uv, float2 texture_size) + { + // We're going to sample a a 4x4 grid of texels surrounding the target UV coordinate. We'll do this by rounding + // down the sample location to get the exact center of our "starting" texel. The starting texel will be at + // location [1, 1] in the grid, where [0, 0] is the top left corner. + float2 sample_pos = uv * texture_size; + float2 tex_pos1 = floor(sample_pos - 0.5f) + 0.5f; + + // Compute the fractional offset from our starting texel to our original sample location, which we'll + // feed into the Catmull-Rom spline function to get our filter weights. + float2 f = sample_pos - tex_pos1; + + // Compute the Catmull-Rom weights using the fractional offset that we calculated earlier. + // These equations are pre-expanded based on our knowledge of where the texels will be located, + // which lets us avoid having to evaluate a piece-wise function. + float2 w0 = f * ( -0.5 + f * (1.0 - 0.5*f)); + float2 w1 = 1.0 + f * f * (1.5*f - 2.5); + float2 w2 = f * ( 0.5 + f * (2.0 - 1.5*f) ); + float2 w3 = f * f * (-0.5 + 0.5 * f); + + // Work out weighting factors and sampling offsets that will let us use bilinear filtering to + // simultaneously evaluate the middle 2 samples from the 4x4 grid. + float2 w12 = w1 + w2; + float2 offset12 = w2 / (w1 + w2); + + // Compute the final UV coordinates we'll use for sampling the texture + float2 tex_pos0 = tex_pos1 - 1; + float2 tex_pos3 = tex_pos1 + 2; + float2 tex_pos12 = tex_pos1 + offset12; + + tex_pos0 /= texture_size; + tex_pos3 /= texture_size; + tex_pos12 /= texture_size; + + SAMPLE_TYPE result = 0.0f; + result += TEX2DLOD(tex, float2(tex_pos0.x, tex_pos0.y), 0.0f).SAMPLE_CHANNELS * w0.x * w0.y; + result += TEX2DLOD(tex, float2(tex_pos12.x, tex_pos0.y), 0.0f).SAMPLE_CHANNELS * w12.x * w0.y; + result += TEX2DLOD(tex, float2(tex_pos3.x, tex_pos0.y), 0.0f).SAMPLE_CHANNELS * w3.x * w0.y; + + result += TEX2DLOD(tex, float2(tex_pos0.x, tex_pos12.y), 0.0f).SAMPLE_CHANNELS * w0.x * w12.y; + result += TEX2DLOD(tex, float2(tex_pos12.x, tex_pos12.y), 0.0f).SAMPLE_CHANNELS * w12.x * w12.y; + result += TEX2DLOD(tex, float2(tex_pos3.x, tex_pos12.y), 0.0f).SAMPLE_CHANNELS * w3.x * w12.y; + + result += TEX2DLOD(tex, float2(tex_pos0.x, tex_pos3.y), 0.0f).SAMPLE_CHANNELS * w0.x * w3.y; + result += TEX2DLOD(tex, float2(tex_pos12.x, tex_pos3.y), 0.0f).SAMPLE_CHANNELS * w12.x * w3.y; + result += TEX2DLOD(tex, float2(tex_pos3.x, tex_pos3.y), 0.0f).SAMPLE_CHANNELS * w3.x * w3.y; + + return result; + } + """ + } + + hermite_cubic_sampling = { + code = """ + SAMPLE_TYPE hermite_cubic(SAMPLE_TYPE A, SAMPLE_TYPE B, SAMPLE_TYPE C, SAMPLE_TYPE D, float t) + { + const float t2 = t*t; + const float t3 = t2*t; + SAMPLE_TYPE a = (-A + (3.0*B) - (3.0*C) + D)*0.5; + SAMPLE_TYPE b = (2.0*A - (5.0*B) + 4.0*C - D)*0.5; + SAMPLE_TYPE c = (-A + C)*0.5; + SAMPLE_TYPE d = B; + + return a*t3 + b*t2 + c*t + d; + } + + SAMPLE_TYPE hermite_cubic_sample_2d(Sampler2D tex, float2 uv, float2 texture_size) + { + float2 pixel_size = 1.0 / texture_size; + float2 offseted_uv = uv * texture_size + 0.5; + float2 pixel_coordinate = frac(offseted_uv); + offseted_uv = floor(offseted_uv) / texture_size - pixel_size * 0.5; + int3 st = int3(offseted_uv * texture_size, 0); + + SAMPLE_TYPE c00 = tex.tex.Load(st, int2(-1,-1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c10 = tex.tex.Load(st, int2( 0,-1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c20 = tex.tex.Load(st, int2( 1,-1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c30 = tex.tex.Load(st, int2( 2,-1)).SAMPLE_CHANNELS; + + SAMPLE_TYPE c01 = tex.tex.Load(st, int2(-1, 0)).SAMPLE_CHANNELS; + SAMPLE_TYPE c11 = tex.tex.Load(st, int2( 0, 0)).SAMPLE_CHANNELS; + SAMPLE_TYPE c21 = tex.tex.Load(st, int2( 1, 0)).SAMPLE_CHANNELS; + SAMPLE_TYPE c31 = tex.tex.Load(st, int2( 2, 0)).SAMPLE_CHANNELS; + + SAMPLE_TYPE c02 = tex.tex.Load(st, int2(-1, 1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c12 = tex.tex.Load(st, int2( 0, 1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c22 = tex.tex.Load(st, int2( 1, 1)).SAMPLE_CHANNELS; + SAMPLE_TYPE c32 = tex.tex.Load(st, int2( 2, 1)).SAMPLE_CHANNELS; + + SAMPLE_TYPE c03 = tex.tex.Load(st, int2(-1, 2)).SAMPLE_CHANNELS; + SAMPLE_TYPE c13 = tex.tex.Load(st, int2( 0, 2)).SAMPLE_CHANNELS; + SAMPLE_TYPE c23 = tex.tex.Load(st, int2( 1, 2)).SAMPLE_CHANNELS; + SAMPLE_TYPE c33 = tex.tex.Load(st, int2( 2, 2)).SAMPLE_CHANNELS; + + SAMPLE_TYPE c0 = hermite_cubic(c00, c10, c20, c30, pixel_coordinate.x); + SAMPLE_TYPE c1 = hermite_cubic(c01, c11, c21, c31, pixel_coordinate.x); + SAMPLE_TYPE c2 = hermite_cubic(c02, c12, c22, c32, pixel_coordinate.x); + SAMPLE_TYPE c3 = hermite_cubic(c03, c13, c23, c33, pixel_coordinate.x); + + return hermite_cubic(c0, c1, c2, c3, pixel_coordinate.y); + } + """ + } + + scene_combine = { + includes = [ "common", "color_management" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + defined_APPLY_BLOOM = { + input_texture1 = { sampler_states = "clamp_linear" } + defined_APPLY_LIGHT_SHAFTS = { + input_texture2 = { sampler_states = "clamp_linear" } + } + } + ndefined_APPLY_BLOOM = { + defined_APPLY_LIGHT_SHAFTS = { + input_texture1 = { sampler_states = "clamp_linear" } + } + } + defined_COLOR_GRADING = { + color_grading_map = { sampler_states="clamp_linear" } + } + defined_EYE_ADAPTATION = { + luminance_adaptation = { sampler_states="clamp_linear" } + } + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + out vec2 v_uv0; + + void main() { + v_uv0 = in_uv0; + gl_Position = in_pos * world_view_proj; + } + """ + + fp_code = """ + precision mediump sampler3D; + + in vec2 v_uv0; + layout(location = 0) out vec4 out_color; + + DECLARE_SAMPLER_2D(input_texture0); + #if defined(APPLY_BLOOM) + DECLARE_SAMPLER_2D(input_texture1); + #endif + #if defined(COLOR_GRADING) + DECLARE_SAMPLER_3D(color_grading_map); + #define COLOR_GRADING_LUT_TEXTURE_SIZE 16 + #endif + + CBUFFER_START(c1) + UNIFORM float exposure; + UNIFORM vec3 bloom_threshold_offset_falloff; + UNIFORM vec3 vignette_properties; + UNIFORM float vignette_enabled; + CBUFFER_END + + const half eps = 0.0001; + + void main() { + vec4 c = TEX2D(input_texture0, v_uv0); + c.rgb *= exposure; + #if defined(APPLY_BLOOM) + vec4 bloom = TEX2D(input_texture1, v_uv0); + bloom.rgb = inv_safe_range_tone_map_offset(bloom.rgb, bloom_threshold_offset_falloff.y); + c.rgb += bloom.rgb; + #endif + c.rgb = filmic_tone_mapping(c.rgb); + + float radius = length(v_uv0 - vec2(0.5)); + float vignette = smoothstep(vignette_properties.x, vignette_properties.x - vignette_properties.y, radius); + c.rgb = mix(c.rgb, c.rgb * vignette, vignette_properties.z * vignette_enabled); + + #if defined(COLOR_GRADING) + c.rgb = clamp(c.rgb, vec3(0.0), vec3(1.0)); + float s = float(COLOR_GRADING_LUT_TEXTURE_SIZE - 1) / float(COLOR_GRADING_LUT_TEXTURE_SIZE); + float b = 0.5 / float(COLOR_GRADING_LUT_TEXTURE_SIZE); + c.rgb = c.rgb * vec3(s) + vec3(b); + c.rgb = TEX3DLOD(color_grading_map, c.rgb, 0.0).rgb; + #endif + + out_color = c; + } + """ + + code = """ + DECLARE_SAMPLER_2D(input_texture0); + #if defined(APPLY_BLOOM) + DECLARE_SAMPLER_2D(input_texture1); + #if defined(APPLY_LIGHT_SHAFTS) + DECLARE_SAMPLER_2D(input_texture2); + #define light_shafts_map input_texture2 + #endif + #elif defined(APPLY_LIGHT_SHAFTS) + DECLARE_SAMPLER_2D(input_texture1); + #define light_shafts_map input_texture1 + #endif + #if defined(COLOR_GRADING) + DECLARE_SAMPLER_3D(color_grading_map); + #define COLOR_GRADING_LUT_TEXTURE_SIZE 16 + #endif + #if defined(EYE_ADAPTATION) + DECLARE_SAMPLER_2D(luminance_adaptation); + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float exposure; // exports={ name="Scene Exposure" type="scalar" value=1.0 min=0.0 max=5.0 step=0.001 } + float3 bloom_threshold_offset_falloff; + + #if defined(STINGRAY_VIGNETTE) + float3 vignette_properties; + #else + float3 vignette_scale_falloff_opacity; + float3 vignette_color; + #endif + float vignette_enabled; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + #if !defined(STINGRAY_VIGNETTE) + float approx_pow(float x, float n) { + n = n * 1.4427f + 1.4427f; // 1.4427f --> 1/ln(2) + return exp2(x * n - n); + } + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float4 c = TEX2D(input_texture0, input.uv); + #if !defined(D3D11) + const bool capture_cubemap = false; + #endif + if (!capture_cubemap) { + #ifdef EYE_ADAPTATION + // exposure here is grey value, thus grey_value / avg_luminance = exposure + float2 eye_adaption_uv = viewport.zw + viewport.xy * 0.5; + c.rgb *= exposure / TEX2D(luminance_adaptation, eye_adaption_uv).r; + #else + c.rgb *= exposure; + #endif + } + + #if defined(APPLY_BLOOM) + float3 bloom = TEX2D(input_texture1, input.uv).rgb; + bloom.rgb = inv_safe_range_tone_map_offset(bloom.rgb, bloom_threshold_offset_falloff.y); + c.rgb += bloom.rgb; + #endif + + #if defined(APPLY_LIGHT_SHAFTS) + float3 light_shafts = TEX2D(light_shafts_map, input.uv).rgb; + c.rgb += inv_safe_range_tone_map(light_shafts); + #endif + + #if !defined(NO_TONE_MAPPING) + c.rgb = filmic_tone_mapping(c.rgb); + #endif + + #if defined(STINGRAY_VIGNETTE) + float radius = length(input.uv - 0.5); + float vignette = smoothstep(vignette_properties.x, vignette_properties.x - vignette_properties.y, radius); + c.rgb = lerp(c.rgb, c.rgb * vignette, vignette_properties.z * vignette_enabled); + #else + half2 dist = (input.uv - 0.5f); + half vignette_mask = saturate(vignette_scale_falloff_opacity.x*approx_pow(1.f-dot(dist, dist), vignette_scale_falloff_opacity.y)); + float3 vignette_tint = 1.0f-((1.0f-vignette_color)*(1.0f-vignette_mask)); + c.rgb = lerp(c.rgb, c.rgb * vignette_tint, vignette_scale_falloff_opacity.z * vignette_enabled); + #endif + + #if defined(COLOR_GRADING) + c.rgb = saturate(c.rgb); + c.rgb = c.rgb * ((COLOR_GRADING_LUT_TEXTURE_SIZE - 1.0)/COLOR_GRADING_LUT_TEXTURE_SIZE) + 0.5 / COLOR_GRADING_LUT_TEXTURE_SIZE; + c.rgb = TEX3DLOD(color_grading_map, c.rgb, 0).rgb; + #endif + + #if defined(ADJUST_GAMMA) + // TODO: let the hardware do this for us, so the gamma is correct between the gui and the level. + c.rgb = pow(c.rgb, 2.2/gamma); + #endif + + return c; + } + """ + } + + bilateral_upsample = { + // TODO: needs optimization + includes = [ "common", "gbuffer_access" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_point" } + input_texture2 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + DECLARE_SAMPLER_2D(input_texture2); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 inv_input_texture0_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + const float4 bilinear_weights[4] = { + float4(9.0/16.0,3.0/16.0,3.0/16.0,1.0/16.0), + float4(3.0/16.0,9.0/16.0,1.0/16.0,3.0/16.0), + float4(3.0/16.0,1.0/16.0,9.0/16.0,3.0/16.0), + float4(1.0/16.0,3.0/16.0,3.0/16.0,9.0/16.0) + }; + const float eps = 0.001; + + int2 pos = int2(input.position.xy); + int idx = int((pos.y%2.0)*2.0) + int(pos.x%2.0); + + const float2 offsets[4*4] = { + { -0.5, -0.5 }, { 1.5, -0.5 }, { 1.5, 1.5 }, { -0.5, 1.5 }, + { -1.5, -0.5 }, { 0.5, -0.5 }, { 0.5, 1.5 }, { -1.5, 1.5 }, + { -0.5, -1.5 }, { 1.5, -1.5 }, { -0.5, 0.5 }, { 1.5, 0.5 }, + { -1.5, -1.5 }, { 0.5, -1.5 }, { -1.5, 0.5 }, { 0.5, 0.5 } + }; + + inv_input_texture0_size *= 0.5; + float4 shading_coarse[4] = { + TEX2D(input_texture0, input.uv + offsets[idx*4+0] * inv_input_texture0_size), + TEX2D(input_texture0, input.uv + offsets[idx*4+1] * inv_input_texture0_size), + TEX2D(input_texture0, input.uv + offsets[idx*4+2] * inv_input_texture0_size), + TEX2D(input_texture0, input.uv + offsets[idx*4+3] * inv_input_texture0_size) + }; + + float depth_coarse[4] = { + TEX2D(input_texture1, input.uv + offsets[idx*4+0] * inv_input_texture0_size).r, + TEX2D(input_texture1, input.uv + offsets[idx*4+1] * inv_input_texture0_size).r, + TEX2D(input_texture1, input.uv + offsets[idx*4+2] * inv_input_texture0_size).r, + TEX2D(input_texture1, input.uv + offsets[idx*4+3] * inv_input_texture0_size).r + }; + + float depth_hires = TEX2D(input_texture2, input.uv).r; + + float total_weight = 0; + float4 s = 0; + float d = 0; + for (int i=0;i<4;i++) { + float diff = abs(depth_hires - depth_coarse[i]); + float w = (1.0 / (eps+diff)) * bilinear_weights[idx][i]; + s += shading_coarse[i] * w; + d += depth_coarse[i] * w; + total_weight += w; + } + + s /= total_weight; + d /= total_weight; + + s.a = d; + return s; + } + """ + } + + mb_common = { + code=""" + // k in the paper + #define MAX_BLUR_RADIUS 10 + #define HALF_PIX 0.5 + #define MOTION_BLUR_CURVE_EXPONENT 2.0 + + float2 adjust_velocity(float2 v, out float radius, float motion_blur_amount) + { + float length_v = length(v); + + // Convert the velocity to be a radius instead of a diameter, and scale by motion blur amount. + radius = 0.5 * length_v * motion_blur_amount; + + // Dampens the blur filter for slow moving pixels + radius = pow(radius/MAX_BLUR_RADIUS, MOTION_BLUR_CURVE_EXPONENT) * MAX_BLUR_RADIUS; + + radius = clamp(radius, HALF_PIX, float(MAX_BLUR_RADIUS)); + + // Much larger than zero + if (length_v >= 0.01) + v = v * (radius / length_v); + + return v; + } + + float2 adjust_velocity(float2 v, float radius) + { + float length_v = length(v); + + // Much larger than zero + if (length_v >= 0.01) + v = v * (radius / length_v); + + return v; + } + + float calculate_radius(float2 v, float motion_blur_amount) + { + float length_v = length(v); + + // Convert the velocity to be a radius instead of a diameter, and scale by motion blur amount. + float radius = 0.5 * length_v * motion_blur_amount; + + // Dampens the blur filter for slow moving pixels + radius = pow(radius/MAX_BLUR_RADIUS, MOTION_BLUR_CURVE_EXPONENT) * MAX_BLUR_RADIUS; + + radius = clamp(radius, HALF_PIX, float(MAX_BLUR_RADIUS)); + + return radius; + } + """ + } + + merge_skydome_motion_vectors = { + includes = [ "common", "gbuffer_access", "space_conversion", "post_processing_common" ] + samplers = { + } + + code=""" + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float4 w : TEXCOORD1; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + o.w = encode_world_pos(o.position); + o.position.z = o.position.w; + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float2 ps_main(PS_INPUT input) : SV_TARGET0 { + float3 cur_world_pos = decode_world_pos(input.w, camera_near_far.y); + cur_world_pos -= camera_pos; + float3 prev_view_pos = world_to_prev_view(cur_world_pos, 1, 1); + float3 prev_ss_pos = view_to_ss(prev_view_pos, 1); + float2 curr_ss_pos = (input.uv - viewport.zw) / viewport.xy; + float2 velocity = (curr_ss_pos - prev_ss_pos.xy)*viewport.xy; + return float2(encode_velocity(velocity)); + } + """ + } + + + mb_tile_max = { + includes = [ "common", "gbuffer_access", "mb_common", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + } + + code=""" + #if defined(HORIZONTAL_PASS) + #define OFFSET_COMPONENT x + #else + #define OFFSET_COMPONENT y + #endif + + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float2 inv_input_texture0_size; + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float2 ps_main(PS_INPUT input) : SV_TARGET0 { + float2 largest_vector = float2(0,0); + float largest_magnitude = 0.0; + + // We need to start sampling at the start of a tile to preperly identify the local maximum velocity + // input.uv + // offset | + // | | + // v v + // | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | + float offset = 0.5 * inv_input_texture0_size.OFFSET_COMPONENT * (1.0 - MAX_BLUR_RADIUS); + #if defined(HORIZONTAL_PASS) + float2 sample_offset = float2(offset, 0); + #elif defined(VERTICAL_PASS) + float2 sample_offset = float2(0, offset); + #endif + + float2 sample_uv = input.uv; + for (int i = 0; i < MAX_BLUR_RADIUS; ++i) { + float2 v = TEX2DLOD(input_texture0, sample_uv + sample_offset, 0).VELOCITY_COMPONENTS; + + float v_magnitude = dot(v, v); + if (v_magnitude > largest_magnitude) { + largest_magnitude = v_magnitude; + largest_vector = v; + } + + #if defined(HORIZONTAL_PASS) + sample_uv.x += inv_input_texture0_size.x; + #elif defined(VERTICAL_PASS) + sample_uv.y += inv_input_texture0_size.y; + #endif + } + return largest_vector; + } + """ + } + + mb_neighbour_max = { + includes = [ "common", "gbuffer_access", "mb_common", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float2 inv_input_texture0_size; + float4x4 world_view_proj; + float motion_blur_amount; // exports={ name="Motion Blur Factor" type="scalar" value=1.0 min=0.0 max=5.0 step=0.001 } + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + void max_velocity_magnitude(float2 uv, float2 offset, inout float2 m, inout float largest_magnitude) + { + float2 tile_uv = uv + offset * inv_input_texture0_size.xy; + float2 tile_velocity = decode_velocity(TEX2DLOD(input_texture0, tile_uv, 0).VELOCITY_COMPONENTS); + float tile_velocity_magnitude = dot(tile_velocity, tile_velocity); + + // Reference http://sourceforge.net/p/g3d/code/HEAD/tree/G3D10/data-files/shader/MotionBlur/MotionBlur_neighborMinMax.pix + if (tile_velocity_magnitude > largest_magnitude) { + float displacement = abs(offset.x) + abs(offset.y); + float2 direction = sign(offset * tile_velocity); + float dist = direction.x + direction.y; + if (abs(dist) == displacement) { + largest_magnitude = tile_velocity_magnitude; + m = tile_velocity; + } + } + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + float2 m = decode_velocity(TEX2D(input_texture0, input.uv).VELOCITY_COMPONENTS); + float largest_magnitude = dot(m, m); + + max_velocity_magnitude(input.uv, float2(-1,-1), m, largest_magnitude); + max_velocity_magnitude(input.uv, float2(0,-1), m, largest_magnitude); + max_velocity_magnitude(input.uv, float2(1,-1), m, largest_magnitude); + max_velocity_magnitude(input.uv, float2(1,0), m, largest_magnitude); + max_velocity_magnitude(input.uv, float2(-1,0), m, largest_magnitude); + max_velocity_magnitude(input.uv, float2(-1,1), m, largest_magnitude); + max_velocity_magnitude(input.uv, float2(0,1), m, largest_magnitude); + max_velocity_magnitude(input.uv, float2(1,1), m, largest_magnitude); + + float radius = 0.0; + float2 velocity = adjust_velocity(m * back_buffer_size, radius, motion_blur_amount); + + return float4(velocity, radius, 0); + } + """ + } + + mb_bake_velocity_depth = { + includes = [ "common", "gbuffer_access", "mb_common", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float motion_blur_amount; // exports={ name="Motion Blur Factor" type="scalar" value=1.0 min=0.0 max=5.0 step=0.001 } + CBUFFER_END + + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 + { + float radius = 0.0; + float2 velocity = decode_velocity(TEX2D(input_texture0, input.uv).VELOCITY_COMPONENTS); + velocity = adjust_velocity(velocity * back_buffer_size, radius, motion_blur_amount); + float depth = gbuffer_decode_depth(TEX2D(input_texture1, input.uv)); + + return float4(velocity, radius, depth); + + } + """ + } + + mb_bake_radius_depth = { + includes = [ "common", "gbuffer_access", "mb_common", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float motion_blur_amount; // exports={ name="Motion Blur Factor" type="scalar" value=1.0 min=0.0 max=5.0 step=0.001 } + CBUFFER_END + + + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + float2 ps_main(PS_INPUT input) : SV_TARGET0 + { + float2 velocity = decode_velocity(TEX2D(input_texture0, input.uv).VELOCITY_COMPONENTS); + float radius = calculate_radius(velocity * back_buffer_size, motion_blur_amount); + float depth = gbuffer_decode_depth(TEX2D(input_texture1, input.uv)); + return float2(radius, depth); + + } + """ + } + + mb_reconstruct_filter_blur = { + includes = [ "common", "gbuffer_access", "mb_common", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_point" } + input_texture2 = { sampler_states = "clamp_point" } + input_texture3 = { sampler_states = "clamp_point" } + + noise = { sampler_states = "clamp_linear" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + DECLARE_SAMPLER_2D(input_texture2); + DECLARE_SAMPLER_2D(input_texture3); + DECLARE_SAMPLER_2D(noise); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 inv_input_texture0_size; + CBUFFER_END + + #define NUM_SAMPLES_ODD 9 + + // Measured in pixels + // Make this smaller to better hide tile boundaries + // Make this bigger to get smoother blur (less noise) + #define VARIANCE_THRESHOLD 1.5 + + float soft_depth_compare(float depth_a, float depth_b) { + // World space distance over which we are conservative about the classification + // of "foreground" vs. "background". Must be > 0. + // Increase if slanted surfaces aren't blurring enough. + // Decrease if the background is bleeding into the foreground. + // Fairly insensitive + const float SOFT_DEPTH_EXTENT = 0.02; + return saturate(1.0 - (depth_b - depth_a)/SOFT_DEPTH_EXTENT); + } + + float cone(float dist, float radius) { + return saturate(1- abs(dist)/radius); + } + + float fast_cone(float dist, float inv_radius) { + return saturate(1- abs(dist) * inv_radius); + } + + float cylinder(float dist, float radius) { + // Optimized implementation + return sign(radius - abs(dist)) * 0.5 + 0.5; + + //return 1.0 - smoothstep(radius * 0.95, radius * 1.05, abs(dist)); + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + // TODO: change this to a screen tiled random texture + float jitter(int2 c) { + return float(int(c.x + c.y) & 1) * 0.5 + 0.25; + } + + #define NOISE_TEXTURE_SIZE 64 + + float jitter_texture(int2 c) { + c = int2(c.x & (NOISE_TEXTURE_SIZE - 1), c.y & (NOISE_TEXTURE_SIZE - 1)); + float2 uv = float2(c) / NOISE_TEXTURE_SIZE; + return TEX2DLOD(noise, uv, 0).r; + } + + float4 calc_velocity_radius_depth(Sampler2D velocity_texture, Sampler2D radius_depth_texture, float2 uv) { + float2 velocity = decode_velocity(TEX2D(velocity_texture, uv).VELOCITY_COMPONENTS); + float2 radius_depth = TEX2D(radius_depth_texture, uv).xy; + + velocity = adjust_velocity(velocity * back_buffer_size, radius_depth.x); + + return float4(velocity, radius_depth); + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + const float2 center_uv = input.uv; + + const float3 neighbor_max_velocity_radius = TEX2DLOD(input_texture3, center_uv, 0).xyz; + const float4 center_color = TEX2D(input_texture0, center_uv); + + if(length(neighbor_max_velocity_radius.z) <= 0.5) + return center_color; + + float4 center_velocity_radius_depth = calc_velocity_radius_depth(input_texture1, input_texture2, center_uv); + + const float center_override_threshold = 5; + + // neighbor_w is a unit vector in screen space represent the velocity direction. + // If the center is moving very fast, sample along the center direction to avoid tile polution + float2 neighbor_w = normalize((center_velocity_radius_depth.z >= center_override_threshold) ? center_velocity_radius_depth.xy : neighbor_max_velocity_radius.xy); + + // Choose the direction at this pixel to be the same as neighbor_w if this pixel is not itself moving. + float2 center_w = (center_velocity_radius_depth.z < VARIANCE_THRESHOLD) ? neighbor_w : normalize(center_velocity_radius_depth.xy); + + const float jit = jitter_texture(int2(input.position.xy)) - 0.5f; + const float center_radius_inv = 1.0 / center_velocity_radius_depth.z; + + // Accumulated color; start with the center sample. + // Higher initial weight increases the ability of the background + // to overcome the out-blurred part of moving objects + float total_weight = ((float)NUM_SAMPLES_ODD / 40.0) * center_radius_inv; + float4 result = center_color * total_weight; + + for(int i = 0; i < NUM_SAMPLES_ODD; i++) + { + // Signed step distance from center to sample point. + float t = clamp(2.4 * (float(i) + 1.0 + jit) / (NUM_SAMPLES_ODD + 1.0) - 1.2, -1, 1); + float dist = t * neighbor_max_velocity_radius.z; + + float2 sampling_direction = (((i & 1) == 1) ? center_w : neighbor_w); + float2 offset = dist * sampling_direction; + + float2 sample_uv = center_uv + offset * inv_input_texture0_size; + float2 sample_radius_depth = TEX2DLOD(input_texture2, sample_uv, 0).xy; + float4 sample_color = TEX2DLOD(input_texture0, sample_uv, 0); + + float front= soft_depth_compare(center_velocity_radius_depth.w, sample_radius_depth.y); + float back = soft_depth_compare(sample_radius_depth.y, center_velocity_radius_depth.w); + + // Blurry me, estimate background + float weight = back * fast_cone(dist , center_radius_inv); + + // Blurry other over any me + weight += front * cone(dist, sample_radius_depth.x); + + // Mutually blurry me and other, optimized implementation + weight += cylinder(dist, min(center_velocity_radius_depth.z, sample_radius_depth.x)) * 2.0; + + total_weight += weight; + result += sample_color * weight; + } + + result /= total_weight; + return result; + } + """ + } + + ssao_ao_pass = { + includes = [ "common", "gbuffer_access", "post_processing_common"] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_point" } + } + + code=""" + // The height in pixels of a 1m object if viewed from 1m away. + // You can compute it from your projection matrix. The actual value is just + // a scale factor on radius; you can simply hardcode this to a constant (~500) + // and make your radius value unitless (...but resolution dependent.) */ + #define PROJECTION_SCALE 500.0 + + // If using depth mip levels, the log of the maximum pixel offset before we need to switch to a lower + // miplevel to maintain reasonable spatial locality in the cache + // If this number is too small (< 3), too many taps will land in the same pixel, and we'll get bad variance that manifests as flashing. + // If it is too high (> 5), we'll get bad performance because we're not using the MIP levels effectively + #define LOG_MAX_OFFSET 4 + + // This must be less than or equal to the MAX_MIP_LEVEL + #define MAX_MIP_LEVEL 5 + + #define AO_BIAS 0.02 + + // The number of jittered samples to use for temporal ao + #define AO_NUM_SAMPLE_OFFSETS 8 + + #define AO_DYNAMIC_RADIUS_START 0.0 + #define AO_DYNAMIC_RADIUS_END 5.0 + #define AO_DYNAMIC_RADIUS_SCALAR 0.01 + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture0_size; + float2 input_texture1_size; + float ao_radius; + float ao_intensity; + CBUFFER_END + + // NUM_SPIRAL_TURNS is the number of turns around the circle that the spiral pattern makes. This should be prime to prevent + // taps from lining up. For better result, this should be the closest prime number to the number of samples used. + #if defined(AO_LOW_QUALITY) + #define NUM_SPIRAL_TURNS 7 + #define NUM_SPIRAL_TURNS_X_TWO_PI NUM_SPIRAL_TURNS * TWOPI + + #define ao_number_of_samples 6 + #elif defined(AO_MID_QUALITY) + #define NUM_SPIRAL_TURNS 11 + #define NUM_SPIRAL_TURNS_X_TWO_PI NUM_SPIRAL_TURNS * TWOPI + + #define ao_number_of_samples 12 + #else + #define NUM_SPIRAL_TURNS 23 + #define NUM_SPIRAL_TURNS_X_TWO_PI NUM_SPIRAL_TURNS * TWOPI + + #define ao_number_of_samples 24 + #endif + + static const float rcp_ao_number_of_samples = (1.0 / ao_number_of_samples); // rcp(ao_number_of_samples) + + Texture2D input_texture0; + Texture2D input_texture1; + + static const float4 projection_info = { + -2.f / (input_texture0_size.x * camera_projection._m00), + -2.f / (input_texture0_size.y * camera_projection._m21), + (1.f - camera_projection._m02) / camera_projection._m00, + (1.f + camera_projection._m22) / camera_projection._m21 + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + // Used for packing z into the gb channels + void pack_key(float key, out float2 p) { + // Round to the nearest 1/256.0 + float temp = floor(key * 256.0); + + // Integer part + p.x = temp * (1.0 / 256.0); + + // Fractional part + p.y = key * 256.0 - temp; + } + + // Used for expressing z as gb channels + float cs_z_to_key(float z) { + return clamp(z * (1.0 / AO_MAX_DISTANCE), 0.0, 1.0); + } + + // Reconstruct camera-space P.xyz from screen-space S = (x, y) in + // pixels and camera-space z > 0. Assumes that the upper-left pixel center + // is at (0.5, 0.5) + float3 reconstruct_cs_pos(float2 ss_pos, float z) { + return float3((ss_pos * projection_info.xy + projection_info.zw) * z,z); + } + + // Read the camera-space position of the point at screen-space pixel ss_pos + float3 get_cs_position(int2 ss_pos) { + float3 cs_pos; + + cs_pos.z = input_texture0.Load(int3(ss_pos, 0)).r; + + // Offset to pixel center + cs_pos = reconstruct_cs_pos(float2(ss_pos) + float2(0.5, 0.5), cs_pos.z); + return cs_pos; + } + + // Reconstructs screen-space unit normal from the normals stored in the gbuffer + float3 reconstruct_cs_face_normal(float2 uv) { + float3 n = decode_signed_normal(input_texture1.Load(int3(uv * input_texture1_size, 0)).rgb); + n = mul(n, (float3x3)camera_view).rgb; + // Swithcing to the coordinate system of the sao algorithm + return float3(-n.r, n.b, n.g); + } + + // Returns a unit vector and a screen-space radius for the tap on a unit disk + // (the caller should scale by the actual disk radius) + float2 ao_tap_location(int sample_number, float spin_angle, out float ss_radius){ + // Radius relative to ss_radius + ss_radius = float(sample_number + 0.5) * rcp_ao_number_of_samples; + float angle = ss_radius * NUM_SPIRAL_TURNS_X_TWO_PI + spin_angle; + return float2(cos(angle), sin(angle)); + } + + // Read the camera-space position of the point at screen-space + // pixel ss_pos + unit_offset * ss_radius. Assumes length(unit_offset) == 1 + float3 get_cs_offset_position(int2 ss_pos, float2 unit_offset, float ss_radius, out bool sample_is_moving) { + // Derivation: + // mip_level = floor(log(ss_radius / MAX_OFFSET)); + int mip_level = clamp((int)floor(log2(ss_radius)) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL); + + int2 ss_offsetted_pos = int2(ss_radius * unit_offset) + ss_pos; + + float3 cs_offsetted_pos; + // Divide coordinate by 2^mip_level + + uint encoded_depth = asint(input_texture0.Load(int3(ss_offsetted_pos >> mip_level, mip_level)).r); + + cs_offsetted_pos.z = decode_bit_from_float(encoded_depth, sample_is_moving); + + // Offset to pixel center + cs_offsetted_pos = reconstruct_cs_pos(float2(ss_offsetted_pos) + float2(0.5, 0.5), cs_offsetted_pos.z); + + return cs_offsetted_pos; + } + + float is_valid(float3 sample_pos, float3 source_pos) + { + return sample_pos.z < source_pos.z; + } + + // Compute the occlusion due to sample with index 'i' about the pixel at 'ss_pos' that corresponds + // to camera-space point 'cs_pos' with unit normal 'cs_normal', using maximum screen-space sampling radius 'ssDiskRadius' + float sample_ao(in int2 ss_pos, in float3 cs_pos, in float3 cs_normal, in float ss_disk_radius, in int tap_index, in float random_pattern_rotation_angle, in float radius2, out bool sample_is_moving) { + // Offset on the unit disk, spun for this pixel + float ss_radius; + float2 unit_offset = ao_tap_location(tap_index, random_pattern_rotation_angle, ss_radius); + ss_radius *= ss_disk_radius; + + // The occluding point in camera space + float3 cs_occluding_pos = get_cs_offset_position(ss_pos, unit_offset, ss_radius, sample_is_moving); + + float3 v = cs_occluding_pos - cs_pos; + float vv = dot(v, v); + float vn = dot(v, cs_normal); + + // A: From the HPG12 paper + // Note large epsilon to avoid overdarkening within cracks + // return float(vv < radius2) * max((vn - AO_BIAS) / (epsilon + vv), 0.0) * radius2 * 0.6 * sample_is_valid; + + float sample_is_valid = is_valid(cs_occluding_pos, cs_pos); + + // B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended] + float f = max(radius2 - vv, 0.0); + return f * f * f * max((vn - AO_BIAS) / (0.01 + vv), 0.0) * sample_is_valid; + + // C: Medium contrast (which looks better at high radii), no division. Note that the + // contribution still falls off with radius^2, but we've adjusted the rate in a way that is + // more computationally efficient and happens to be aesthetically pleasing. + // return 4.0 * max(1.0 - vv * 1.0/radius2, 0.0) * max(vn - AO_BIAS, 0.0) * sample_is_valid; + + // D: Low contrast, no division operation + // return 2.0 * float(vv < radius2) * max(vn - AO_BIAS, 0.0) * sample_is_valid; + } + + #if defined(OPTIMIZED) + float sample_ao_iterative(in int2 ss_pos, in float3 cs_pos, in float3 cs_normal, in float ss_radius, in float2 unit_offset, in float radius2, out bool sample_is_moving) { + + // The occluding point in camera space + float3 cs_occluding_pos = get_cs_offset_position(ss_pos, unit_offset, ss_radius, sample_is_moving); + + float3 v = cs_occluding_pos - cs_pos; + float vv = dot(v, v); + float vn = dot(v, cs_normal); + + + float sample_is_valid = is_valid(cs_occluding_pos, cs_pos); + + // B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended] + float f = max(radius2 - vv, 0.0); + return f * f * f * max((vn - AO_BIAS) / (0.01 + vv), 0) * sample_is_valid; + } + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + + // Pack the AO term in .r and the z linear depth in .gb + float4 output = float4(0,0,0,0); + + // Pixel coordinate + int2 ss_pos = int2(input.uv * input_texture0_size); + + // Camera space point being shaded + float3 cs_pos = get_cs_position(ss_pos); + + // Pack 'z' into the gb 8bit + pack_key(cs_z_to_key(cs_pos.z), output.gb); + + // Reconstruct normals + float3 cs_normal = reconstruct_cs_face_normal(input.uv); + + // Choose the screen-space sample radius + // proportional to the projected area of the sphere + float clamped_depth = clamp(cs_pos.z, AO_DYNAMIC_RADIUS_START, AO_DYNAMIC_RADIUS_END); + float nld = (clamped_depth - AO_DYNAMIC_RADIUS_START)/(AO_DYNAMIC_RADIUS_END - AO_DYNAMIC_RADIUS_START); + nld = (nld + AO_DYNAMIC_RADIUS_SCALAR)/(1.0 + AO_DYNAMIC_RADIUS_SCALAR); + + float radius1 = ao_radius * nld; + float radius2 = radius1 * radius1; + float radius6 = radius2 * radius2 * radius2; + float ss_disk_radius = PROJECTION_SCALE * radius1 / cs_pos.z; + + // Hash function used in the HPG12 AlchemyAO paper + float random_pattern_rotation_angle = (3 * ss_pos.x ^ ss_pos.y + ss_pos.x * ss_pos.y) * 1.2; + float temporal_angle_offset = halton_sequence_1d[frame_number % AO_NUM_SAMPLE_OFFSETS] * TWOPI; + + // Sample ambient occlusion + float sum = 0.0; + #if !defined(OPTIMIZED) + uint num_moving_samples = 0.0; + for (int i = 0; i < ao_number_of_samples; ++i) { + bool sample_is_moving = 0; + sum += sample_ao(ss_pos, cs_pos, cs_normal, ss_disk_radius, i, random_pattern_rotation_angle + temporal_angle_offset, radius2, sample_is_moving); + num_moving_samples += (uint)sample_is_moving; + } + #else + + // Optimized loop, removes sin/cos + + // equation to solve iteraively + //float angle = (float(sample_number + 0.5) * rcp_ao_number_of_samples) * NUM_SPIRAL_TURNS_X_TWO_PI + (random_pattern_angle + temporal_angle_offset); + + const float f2 = rcp_ao_number_of_samples; + const float a = cos(f2 * NUM_SPIRAL_TURNS_X_TWO_PI); + const float b = sin(f2 * NUM_SPIRAL_TURNS_X_TWO_PI); + const float f = 0.5 * f2 * NUM_SPIRAL_TURNS_X_TWO_PI + random_pattern_rotation_angle + temporal_angle_offset; + float s = sin(f); + float c = cos(f); + + //float ss_radius = 0.5 * rcp_ao_number_of_samples; + uint num_moving_samples = 0.0; + for (int i = 0; i < ao_number_of_samples; ++i) { + float2 unit_offset = float2(c, s); + bool sample_is_moving = 0; + float ss_radius = float(i + 0.5) * rcp_ao_number_of_samples * ss_disk_radius; + sum += sample_ao_iterative(ss_pos, cs_pos, cs_normal, ss_radius, unit_offset, radius2, sample_is_moving); + num_moving_samples += (uint)sample_is_moving; + const float ns = b*c + a*s; + const float nc = a*c - b*s; + c = nc; + s = ns; + //ss_radius += f2; + } + #endif + // Calculate the final ambient occlusion term + float ao = max(0.0, 1.0 - sum * rcp(radius6) * ao_intensity * (5.0 * rcp_ao_number_of_samples)); + + // Pack 'ao' into the .r 8bit + output.r = ao; + + output.a = 1.0 - (num_moving_samples * rcp_ao_number_of_samples); + + return output; + } + """ + } + + ssao_blur_pass = { + includes = [ "common", "gbuffer_access", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_point" } + input_texture2 = { sampler_states = "clamp_linear" } + + } + + code=""" + #define SCALE 1 + #define NUM_GAUSSIAN_WEIGHTS 4 + #define AO_EDGE_SHARPNESS 6 + #define DEPTH_DIFFERENCE_THRESHOLD 0.08 + #define VELOCITY_SCALAR 80.0 + #define LOW_VELOCITY_SIMILARITY 0.9 + #define HIGH_VELOCITY_SIMILARITY 0.8 + #define MOVING_SAMPLES_SCALAR 2 + #define MOVING_SAMPLES_MIN_SIMILARITY 0.3 + #define SIMILARITY_HISTORY_DAMPNING 0.8 + + // Gaussian coefficients + static float gaussian_weights[] = + // { 0.398943, 0.241971, 0.053991, 0.004432, 0.000134 }; // stddev = 1.0 + { 0.153170, 0.144893, 0.122649, 0.092902, 0.062970 }; // stddev = 2.0 + // { 0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108 }; // stddev = 3.0 + + // Bilateral key range + static float epsilon = 0.0001; + static float ao_max_distance_x_ao_edge_sharpness = AO_MAX_DISTANCE * AO_EDGE_SHARPNESS; + // float2 axis + #if defined(SEPARABLE_SSAO_BLUR_9TAP_X) + static int2 axis = int2(1,0); + #else + static int2 axis = int2(0,1); + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture0_size; + CBUFFER_END + + Texture2D input_texture0; + DECLARE_SAMPLER_2D(input_texture1); + DECLARE_SAMPLER_2D(input_texture2); + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + // Returns a number on (0, 1) + float unpack_key(float2 p) { + return p.x * (256.0 / 257.0) + p.y * (1.0 / 257.0); + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + int2 ss_pos = int2(input.uv * input_texture0_size); + float4 packed_ao_data = input_texture0.Load(int3(ss_pos, 0)); + float num_moving_samples = packed_ao_data.a; + // Forword the key to the gb channel (we don't blur the key) + float4 output = float4(0, packed_ao_data.gb, 0); + + float key = unpack_key(output.gb); + float sum = packed_ao_data.r; + + // Base weight for depth falloff. Decrease this for more blurriness, + // increase it for better edge discrimination + float total_weight = gaussian_weights[0] * 0.5; + sum *= total_weight; + + ss_pos -= axis * NUM_GAUSSIAN_WEIGHTS * SCALE; + + [unroll] + for (int r = NUM_GAUSSIAN_WEIGHTS; r > 0 ; --r) { + packed_ao_data = input_texture0.Load(int3(ss_pos, 0)); + num_moving_samples += packed_ao_data.a; + + // Sample data + float tap_key = unpack_key(packed_ao_data.gb); + float sample_value = packed_ao_data.r; + float weight = gaussian_weights[r]; + + // Range domain (the "bilateral" weight). As depth difference increases, decrease weight. + weight *= max(0.0, 1.0 - ao_max_distance_x_ao_edge_sharpness * abs(tap_key - key)); + + sum += sample_value * weight; + total_weight += weight; + + ss_pos += axis; + } + + // The case where r == 0 is handled above the for loop, just change the sample pos here and go into the next loop. + ss_pos += axis; + + [unroll] + for (int r = 1; r <= NUM_GAUSSIAN_WEIGHTS; ++r) { + packed_ao_data = input_texture0.Load(int3(ss_pos, 0)); + num_moving_samples += packed_ao_data.a; + // Sample data + float tap_key = unpack_key(packed_ao_data.gb); + float sample_value = packed_ao_data.r; + float weight = gaussian_weights[r]; + + // Range domain (the "bilateral" weight). As depth difference increases, decrease weight. + weight *= max(0.0, 1.0 - ao_max_distance_x_ao_edge_sharpness * abs(tap_key - key)); + + sum += sample_value * weight; + total_weight += weight; + + ss_pos += axis; + } + + num_moving_samples = saturate( num_moving_samples / (NUM_GAUSSIAN_WEIGHTS * 2.0 + 1.0)); + + // Store the ao term in the .r channel + output.r = sum / (total_weight + epsilon); + output.a = num_moving_samples; + + #ifdef SEPARABLE_SSAO_BLUR_9TAP_Y_PLUS_MERGE_AO_REPROJECTION + float2 pixel_position = input.uv; + float2 motion_vector = decode_velocity(TEX2D(input_texture1, pixel_position.rg).VELOCITY_COMPONENTS); + float2 prev_pixel_position = pixel_position - motion_vector; + + float current_depth = key; + + // Reprojected values + float4 reprojected_data = TEX2D(input_texture2, prev_pixel_position); + float reprojected_ao = reprojected_data.r; + float reprojected_depth = unpack_key(reprojected_data.gb); + float reprojected_samples_similarity = reprojected_data.a; + + float velocity = sqrt(motion_vector.x * motion_vector.x + motion_vector.y * motion_vector.y); + + // We use the relative depth between the current and reprojected depth to detect disocclusions + // as described in "Temporal Coherence Methods in Real-Time Rendering". We also reduce the blend + // amount for fast moving pixels (so ghosting pixels introduced due to fast motion disapears quickly) + float depth_similarity = saturate(pow(saturate(reprojected_depth/current_depth), 4) + 0.5); + + float velocity_similarity = saturate(velocity * VELOCITY_SCALAR); + + float pixel_similarity = depth_similarity * LOW_VELOCITY_SIMILARITY - velocity_similarity * (LOW_VELOCITY_SIMILARITY - HIGH_VELOCITY_SIMILARITY); + + float samples_similarity = num_moving_samples > 0 ? 1.0 - saturate(num_moving_samples * MOVING_SAMPLES_SCALAR) : 0.0; + samples_similarity *= (LOW_VELOCITY_SIMILARITY - MOVING_SAMPLES_MIN_SIMILARITY); + float current_samples_similarity = samples_similarity; + samples_similarity = lerp(samples_similarity, reprojected_samples_similarity, 0.9); + samples_similarity = max(samples_similarity, current_samples_similarity); + + float similarity = (pixel_similarity > (LOW_VELOCITY_SIMILARITY - VELOCITY_EPSILON)) ? saturate(pixel_similarity - samples_similarity) : pixel_similarity; + + // We don't blend values if the projection is outside the screen + similarity = (prev_pixel_position.x < 1 && prev_pixel_position.x > 0) ? similarity : 0; + similarity = (prev_pixel_position.y < 1 && prev_pixel_position.y > 0) ? similarity : 0; + + // We need to add a small term to avoid the reprojection to 'stall' when lerping bit values. This ensures + // values close to '1' will alawys get to converge to 1. + output.r = lerp(output.r, reprojected_ao + 1.0/255.0, similarity); + + output.a = samples_similarity; + + #endif + + return output; + } + """ + } + + ssao_mip_pass = { + includes = [ "common", "gbuffer_access", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "ssao_sample_mip_index" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture0_size; + float input_mip_level; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + #ifdef RENDERER_GNM + #pragma PSSL_target_output_format (target 0 FMT_32_R) + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + int2 ssp = int2(input.uv * input_texture0_size); + int2 uv_i = int2(ssp * 2 + int2((ssp.y & 1) ^ 1, (ssp.x & 1) ^ 1)); + float2 uv = 0.5 * (float2)uv_i / input_texture0_size; + return TEX2DLOD(input_texture0, uv, input_mip_level).r; + } + """ + } + + ssao_depth_copy_pass = { + includes = [ "common", "gbuffer_access", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture0_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + #ifdef RENDERER_GNM + #pragma PSSL_target_output_format (target 0 FMT_32_R) + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float ps_main(PS_INPUT input) : SV_TARGET0 { + float d = TEX2D(input_texture0, input.uv).r; + float2 v = decode_velocity(TEX2D(input_texture1, input.uv).VELOCITY_COMPONENTS); + return encode_bit_in_float(d, length(v) > VELOCITY_EPSILON); + } + """ + } + + calculate_coc = { + includes = [ "common", "gbuffer_access", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float dof_focal_distance; + float dof_focal_region; + float dof_focal_region_start; + float dof_focal_region_end; + float dof_focal_near_scale; + float dof_focal_far_scale; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float ps_main(PS_INPUT input) : SV_TARGET0 { + + float depth = TEX2D(input_texture0, input.uv).r; + float coc = (depth - dof_focal_distance); + + if(coc > 0) + coc = saturate((coc - dof_focal_region) / dof_focal_region_end) * dof_focal_far_scale; + else + coc = max(coc / dof_focal_region_start, -1.0) * dof_focal_near_scale; + + return encode_coc(coc); + } + """ + } + + depth_of_field = { + includes = [ "common", "gbuffer_access", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_point" } + input_texture2 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + DECLARE_SAMPLER_2D(input_texture2); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture1_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + // The bleeding bias and multiplier are used to weight sample values + // in a way that prevents bleeding artifacts (as proposed by "Efficiently + // Simulating the Bokeh of Polygonal Apertures in a Post-Process DoF Shader"). + // Currently using the values proposed by the paper (bias=0.02, multiplier=30). + const static float dof_num_of_samples = 5; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + const float signed_coc = decode_coc(TEX2D(input_texture1, input.uv).r); + const float coc = saturate(abs(signed_coc)); + + float3 total_color = float3(0,0,0); + float total_weight = 0.0; + + #if defined(ASCENDING_DIAGONAL_PASS) || defined(DESCENDING_DIAGONAL_PASS) + const float inv_aspect_ratio = input_texture1_size.x / input_texture1_size.y; + #endif + + #ifdef RENDERER_GNM + float dynamic_num_samples = dof_num_of_samples; + // Uncomment this line to enable dynamic number of samples depending on the coc + //dynamic_num_samples = max(coc * dof_num_of_samples, 1); + #else + float dynamic_num_samples = dof_num_of_samples; + #endif + + const float offset = 1.0 / float(dynamic_num_samples) * (coc * MAX_COC); + + #if defined(HORIZONTAL_PASS) + // Distribution of first pass " --- " + const float2 step_size = float2(offset, 0.0f); + #elif defined(ASCENDING_DIAGONAL_PASS) + // Distribution of second pass " / " + const float2 step_size = float2(offset * 0.5, offset * inv_aspect_ratio); + #elif defined(DESCENDING_DIAGONAL_PASS) + // Distribution of third pass " \ " + const float2 step_size = float2(offset * 0.5, -offset * inv_aspect_ratio); + #endif + + [unroll] + for (int n = -dynamic_num_samples/2; n < dynamic_num_samples/2; ++n) + { + float2 sample_uv = input.uv + n * step_size; + const float sample_signed_coc = decode_coc(TEX2D(input_texture1, sample_uv).r); + + #if defined(HORIZONTAL_PASS) + float3 sample_color = TEX2D(input_texture0, sample_uv).rgb; + #else + float3 sample_color = rgbm_decode(TEX2D(input_texture0, sample_uv)); + #endif + + // Weight samples using Scheuermann's method (Adcanced Depth of Field 2004) + float weight = sample_signed_coc >= signed_coc ? 1.0 : saturate(abs(sample_signed_coc/MAX_COC)); + + total_color += sample_color * weight; + total_weight += weight; + } + total_color /= total_weight; + + #if defined(DESCENDING_DIAGONAL_PASS) + // The descending diagonal pass is the last pass. We combine the boolean + // operation with this pass to save the overhead of outputing it into another + // pass just to perform the union after. + total_color = min(total_color, rgbm_decode(TEX2D(input_texture2, input.uv))); + return float4(total_color, coc); + #endif + + return rgbm_encode(total_color); + } + """ + } + + merge_depth_of_field = { + includes = [ "common", "gbuffer_access", "sampling_common", "bicubic_sampling" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture0_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + // From Tiago Sousa's Graphics Gems From Cryengine 3 (Advances In Real Time Rendering 2013). + // Using a non linear transition helps reduce the frequency artifacts that arrises when going + // from half to fullres. Using smoothstep for now but there might be something better. + float coc = smoothstep(0.0, 1.0, TEX2D(input_texture0, input.uv).a); + float3 dof_values = bicubic_sample_2d(input_texture0, input.uv, input_texture0_size); + float3 hdr_values = TEX2D(input_texture1, input.uv).rgb; + float3 merged_values = lerp(hdr_values, dof_values, coc); + return float4(merged_values, 0); + } + """ + } + + depth_fog = { + includes = [ "common", "gbuffer_access" ] + samplers = { + linear_depth = { sampler_states = "clamp_point" } + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + CBUFFER_START(c0) + UNIFORM mat4 world_view_proj; + CBUFFER_END + + out vec2 v_uv0; + out vec4 v_w; + + void main() { + float4 p = mul(in_pos, world_view_proj); + p.z = 1.0; + v_uv0 = in_uv0; + v_w = encode_world_pos(p); + + gl_Position = p; + } + """ + + fp_code = """ + uniform highp usampler2D linear_depth; + + in vec2 v_uv0; + in highp vec4 v_w; + + CBUFFER_START(c1) + uniform vec2 fog_depth_range; + uniform vec3 fog_color; + uniform vec3 fog_sun_blend; + uniform vec3 sun_direction; + uniform vec3 sun_color; + CBUFFER_END + + layout(location = 0) out vec4 out_base; + + void main() { + float d = uintBitsToFloat(texture(linear_depth, v_uv0).r); + float start = fog_depth_range.x; + float end = fog_depth_range.y; + + float3 wp = decode_world_pos(v_w, d); + float3 view_dir = normalize(float3(camera_world[0].w, camera_world[1].w, camera_world[2].w) - wp); + + float b = 1.f / (end-start); + float dist = d - start; + //float a = 1-saturate(exp(-dist * b)); + float a = saturate(exp(-camera_world[2].w * b) * (1.0-exp(-dist * -view_dir.z * b)) / -view_dir.z); + half sa = fog_sun_blend.x * pow(saturate(dot(view_dir, sun_direction)), fog_sun_blend.y); + float3 c = lerp(fog_color, fog_sun_blend.z * sun_color, sa); + + out_base = vec4(c, d > start ? a : 0.0); + } + """ + + code=""" + DECLARE_SAMPLER_2D(linear_depth); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float4 w : TEXCOORD3; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 fog_depth_range; + float2 height_fog_offset_falloff; // TODO: merge this param with fog_depth_range? + float fog_type; + float3 fog_color; + float3 fog_sun_blend; + float3 sun_direction; + float3 sun_color; + #if defined(CUSTOM_FOG_BLEND) + float3 custom_fog_blend_direction; + float3 custom_fog_blend_color; + float3 custom_fog_blend; + #endif + #if defined(SECONDARY_SUN_BLEND) + float3 secondary_sun_direction; + float3 secondary_sun_color; + float3 secondary_sun_blend; + #endif + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + p.z = 1; + o.position = p; + o.uv = input.uv; + o.w = encode_world_pos(o.position); + + return o; + } + + #define DEFAULT_EXP_HEIGHT 0 + #define EXP_HEIGHT 1 + #define EXP 2 + #define LINEAR 3 + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + float d = gbuffer_decode_depth(TEX2D(linear_depth, input.uv)); + float start = fog_depth_range.x; + float end = fog_depth_range.y; + + float3 wp = decode_world_pos(input.w, d); + float3 view_dir = normalize(camera_world._m30_m31_m32 - wp); + + float b = 1.f / (end-start); + float dist = d - start; + + half sa = 0.0; + float3 c = fog_color; + + #if defined(SECONDARY_SUN_BLEND) + sa = secondary_sun_blend.x * pow(saturate(dot(view_dir, secondary_sun_direction)), secondary_sun_blend.y); + c = lerp(c, secondary_sun_blend.z * secondary_sun_color, sa); + #endif + + #if defined(CUSTOM_FOG_BLEND) + sa = custom_fog_blend.x * pow(saturate(dot(view_dir, custom_fog_blend_direction)), custom_fog_blend.y); + c = lerp(c, custom_fog_blend.z * custom_fog_blend_color, sa); + #endif + + // TODO: branch in render config. fog in paricle effects and standard base etc. stills need branching in shader. + float a = 0.0; + [branch] + if (fog_type == DEFAULT_EXP_HEIGHT) { + a = saturate(exp(-camera_world._m32 * b) * (1.0 - exp(-dist * -view_dir.z * b)) / -view_dir.z); + } else if (fog_type == EXP_HEIGHT) { + a = saturate(exp((height_fog_offset_falloff.x - wp.z)/height_fog_offset_falloff.y) * (1.0 - exp(-dist * b))); + } else if (fog_type == EXP) { + a = 1.0 - saturate(exp(-dist * b)); + } else { + a = d > start ? saturate((d-start) * b) : 0; + } + + sa = fog_sun_blend.x * pow(saturate(dot(view_dir, sun_direction)), fog_sun_blend.y); + c = lerp(c, fog_sun_blend.z * sun_color, sa); + + return half4(c, d > start ? a : 0); + } + + #undef DEFAULT_EXP_HEIGHT + #undef EXP_HEIGHT + #undef EXP + #undef LINEAR + """ + } + + bright_pass = { + includes = [ "common", "gbuffer_access", "color_management" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + defined_EYE_ADAPTATION = { + luminance_adaptation = { sampler_states="clamp_linear" } + } + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + CBUFFER_START(c0) + uniform mat4 world_view_proj; + CBUFFER_END + + out vec2 v_uv0; + + void main() { + v_uv0 = in_uv0; + gl_Position = in_pos * world_view_proj; + } + """ + + fp_code = """ + DECLARE_SAMPLER_2D(input_texture0); + + CBUFFER_START(c1) + uniform half exposure; + uniform vec3 bloom_threshold_offset_falloff; + CBUFFER_END + + in vec2 v_uv0; + layout(location = 0) out vec4 out_color; + + void main() { + vec4 c = TEX2D(input_texture0, v_uv0); + c.rgb *= exposure; + + c = vec4(max(c.rgb - bloom_threshold_offset_falloff.x, 0.0), c.a); + c.rgb = safe_range_tone_map_offset(c.rgb, bloom_threshold_offset_falloff.y); + + out_color = c; + } + """ + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + #if defined(EYE_ADAPTATION) + DECLARE_SAMPLER_2D(luminance_adaptation); + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float3 bloom_threshold_offset_falloff; + float exposure; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + float4 color = TEX2D(input_texture0, input.uv); + #if !defined(D3D11) + const bool capture_cubemap = false; + #endif + if (!capture_cubemap) { + #ifdef EYE_ADAPTATION + float2 eye_adaption_uv = viewport.zw + viewport.xy * 0.5; + color.rgb *= exposure / TEX2D(luminance_adaptation, eye_adaption_uv).r; + #else + color.rgb *= exposure; + #endif + } + + float4 c = float4(max(color.rgb - bloom_threshold_offset_falloff.x, 0.0), c.a); + c.rgb = safe_range_tone_map_offset(c.rgb, bloom_threshold_offset_falloff.y); + #ifdef EYE_ADAPTATION + c.a = log(max(dot(color.rgb, luminance_vector), min_positive_f16)); + #endif + + return c; + } + """ + } + + filter = { + includes = [ "common", "gbuffer_access" ] + samplers = { + + ndefined_DOWNSAMPLE_MIP = { + input_texture0 = { sampler_states = "clamp_linear" } + } + + defined_DOWNSAMPLE_MIP = { + input_texture0 = { sampler_states = "downsample_mip_index" } + } + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + CBUFFER_START(c0) + uniform mat4 world_view_proj; + CBUFFER_END + + out vec2 v_uv0; + + void main() { + v_uv0 = in_uv0; + gl_Position = in_pos * world_view_proj; + } + """ + + fp_code = """ + DECLARE_SAMPLER_2D(input_texture0); + + CBUFFER_START(c1) + uniform vec2 inv_input_texture0_size; + uniform float input_mip_level; + uniform float output_mip_level; + CBUFFER_END + + in vec2 v_uv0; + layout(location = 0) out vec4 out_color; + + const vec3 luminance_vector = vec3(0.2127, 0.7152, 0.0721); + // If gaussian_taps is declared const the output from the for loop further down is black. + vec2 gaussian_taps[5] = vec2[5]( + vec2(-4.30908, 0.055028), + vec2(-2.37532, 0.244038), + vec2(-0.50000, 0.401870), + vec2(1.37532, 0.244038), + vec2(3.30908, 0.055028)); + void main() { + #if defined(DOWNSAMPLE_4x4) + float d = inv_input_texture0_size.x; + vec4 c = + TEX2D(input_texture0, v_uv0 + vec2(-d, -d)) + + TEX2D(input_texture0, v_uv0 + vec2( d, -d)) + + TEX2D(input_texture0, v_uv0 + vec2(-d, d)) + + TEX2D(input_texture0, v_uv0 + vec2( d, d)); + c *= 0.25; + #elif defined(DOWNSAMPLE_2X2) + vec4 c = TEX2D(input_texture0, v_uv0); + #elif defined(DOWNSAMPLE_MIP) + vec4 c = TEX2DLOD(input_texture0, v_uv0, input_mip_level); + #elif defined(SEPARABLE_BILINEAR_GAUSSIAN_5TAP_X) || defined(SEPARABLE_BILINEAR_GAUSSIAN_5TAP_Y) + vec4 c = vec4(0.0,0.0,0.0,0.0); + for (int i = 0; i < 5; ++i) { + vec2 gaussian_tap = gaussian_taps[i]; + #if defined(SEPARABLE_BILINEAR_GAUSSIAN_5TAP_X) + c += TEX2DLOD(input_texture0, v_uv0 + vec2(gaussian_tap.x, 0.5) * inv_input_texture0_size, output_mip_level) * gaussian_tap.y; + #else + c += TEX2DLOD(input_texture0, v_uv0 + vec2(0.5, gaussian_tap.x) * inv_input_texture0_size, output_mip_level) * gaussian_tap.y; + #endif + } + #else + vec4 c = TEX2D(input_texture0, v_uv0); + #endif + + out_color = c; + } + """ + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 inv_input_texture0_size; + float input_mip_level; + float output_mip_level; + CBUFFER_END + + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + const static float2 gaussian_taps[5] = { + float2(-4.30908, 0.055028), + float2(-2.37532, 0.244038), + float2(-0.50000, 0.401870), + float2( 1.37532, 0.244038), + float2( 3.30908, 0.055028), + }; + + static const float3 luminance_vector = float3(0.2127, 0.7152, 0.0721); + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + #if defined(DOWNSAMPLE_4x4) + float d = inv_input_texture0_size.x; + float4 c = + TEX2D(input_texture0, input.uv + float2(-d, -d)) + + TEX2D(input_texture0, input.uv + float2( d, -d)) + + TEX2D(input_texture0, input.uv + float2(-d, d)) + + TEX2D(input_texture0, input.uv + float2( d, d)); + c *= 0.25; + #elif defined(DOWNSAMPLE_2X2) + float4 c = TEX2D(input_texture0, input.uv); + #elif defined(DOWNSAMPLE_MIP) + float4 c = TEX2DLOD(input_texture0, input.uv, input_mip_level); + #elif defined(SEPARABLE_BILINEAR_GAUSSIAN_5TAP_X) || defined(SEPARABLE_BILINEAR_GAUSSIAN_5TAP_Y) + float4 c = float4(0,0,0,0); + for (int i = 0; i < 5; ++i) { + #if defined(SEPARABLE_BILINEAR_GAUSSIAN_5TAP_X) + c += TEX2DLOD(input_texture0, input.uv + float2(gaussian_taps[i].x, 0.5) * inv_input_texture0_size, output_mip_level) * gaussian_taps[i].y; + #else + c += TEX2DLOD(input_texture0, input.uv + float2(0.5, gaussian_taps[i].x) * inv_input_texture0_size, output_mip_level) * gaussian_taps[i].y; + #endif + } + #elif defined(FIREFLIES_REDUCTION) + float4 c = TEX2D(input_texture0, input.uv); + float lum = dot(c.rgb, luminance_vector); + c.rgb *= 1.0 / (1.0 + lum); + #elif defined(INV_FIREFLIES_REDUCTION) + float4 c = TEX2D(input_texture0, input.uv); + float lum = dot(c.rgb, luminance_vector); + c.rgb *= 1.0 / (1.0 - lum); + #else + float4 c = TEX2D(input_texture0, input.uv); + #endif + + return c; + } + """ + } + + blend_bloom = { + includes = [ "common", "gbuffer_access", "color_management", "sampling_common", "bicubic_sampling" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_linear" } + input_texture2 = { sampler_states = "clamp_linear" } + input_texture3 = { sampler_states = "clamp_linear" } + input_texture4 = { sampler_states = "clamp_linear" } + global_lens_dirt_map = { sampler_states = "clamp_linear" } + } + + vp_code = """ + layout(location = POSITION0) in vec4 in_pos; + layout(location = TEXCOORD0) in vec2 in_uv0; + + CBUFFER_START(c0) + uniform mat4 world_view_proj; + CBUFFER_END + + out vec2 v_uv0; + + void main() { + v_uv0 = in_uv0; + gl_Position = in_pos * world_view_proj; + } + """ + + fp_code = """ + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + DECLARE_SAMPLER_2D(input_texture2); + DECLARE_SAMPLER_2D(input_texture4); + DECLARE_SAMPLER_2D(input_texture3); + DECLARE_SAMPLER_2D(global_lens_dirt_map); + + CBUFFER_START(c1) + uniform vec3 bloom_threshold_offset_falloff; + uniform vec3 bloom_tint; + uniform float bloom_lens_dirt_amount; + CBUFFER_END + + in vec2 v_uv0; + layout(location = 0) out vec4 out_color; + + void main() { + float3 level0 = inv_safe_range_tone_map_offset(TEX2D(input_texture0, v_uv0).rgb, bloom_threshold_offset_falloff.y); + float3 level1 = inv_safe_range_tone_map_offset(TEX2D(input_texture1, v_uv0).rgb, bloom_threshold_offset_falloff.y); + float3 level2 = inv_safe_range_tone_map_offset(TEX2D(input_texture2, v_uv0).rgb, bloom_threshold_offset_falloff.y); + float3 level3 = inv_safe_range_tone_map_offset(TEX2D(input_texture3, v_uv0).rgb, bloom_threshold_offset_falloff.y); + float3 level4 = inv_safe_range_tone_map_offset(TEX2D(input_texture4, v_uv0).rgb, bloom_threshold_offset_falloff.y); + + half level_blending_factor = bloom_threshold_offset_falloff.z * 4.0; + + half level1_weight = 1.0 - saturate((1.0 - level_blending_factor)/1.0); + half level2_weight = 1.0 - saturate((2.0 - level_blending_factor)/2.0); + half level3_weight = 1.0 - saturate((3.0 - level_blending_factor)/3.0); + half level4_weight = 1.0 - saturate((4.0 - level_blending_factor)/4.0); + + level1_weight *= level1_weight; + level2_weight *= level2_weight; + level3_weight *= level3_weight; + level4_weight *= level4_weight; + + float3 lens_dirt = TEX2D(global_lens_dirt_map, v_uv0).rgb; + float3 first_levels = level0 + (level1 * level1_weight); + float3 last_levels = (level2 * level2_weight) + (level3 * level3_weight) + (level4 * level4_weight); + + float lum_of_last_levels = saturate(dot(last_levels.rgb, luminance_vector.rgb)); + + // Only adding dirt to the lens if the last levels of the blooms aren't empty (change this if the dirt isn't present enough in the effect) + vec3 o = safe_range_tone_map_offset((first_levels + last_levels + (lum_of_last_levels * lens_dirt * bloom_lens_dirt_amount)) * bloom_tint, bloom_threshold_offset_falloff.y); + out_color = vec4(o, 1.0); + } + """ + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + DECLARE_SAMPLER_2D(input_texture2); + DECLARE_SAMPLER_2D(input_texture4); + DECLARE_SAMPLER_2D(input_texture3); + DECLARE_SAMPLER_2D(global_lens_dirt_map); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float3 bloom_threshold_offset_falloff; + float3 bloom_tint; + float bloom_lens_dirt_amount; + float2 input_texture0_size; + float2 input_texture1_size; + float2 input_texture2_size; + float2 input_texture3_size; + float2 input_texture4_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + float3 level0 = inv_safe_range_tone_map_offset(bicubic_sample_2d(input_texture0, input.uv, input_texture0_size), bloom_threshold_offset_falloff.y); + float3 level1 = inv_safe_range_tone_map_offset(bicubic_sample_2d(input_texture1, input.uv, input_texture1_size), bloom_threshold_offset_falloff.y); + float3 level2 = inv_safe_range_tone_map_offset(bicubic_sample_2d(input_texture2, input.uv, input_texture2_size), bloom_threshold_offset_falloff.y); + float3 level3 = inv_safe_range_tone_map_offset(bicubic_sample_2d(input_texture3, input.uv, input_texture3_size), bloom_threshold_offset_falloff.y); + float3 level4 = inv_safe_range_tone_map_offset(bicubic_sample_2d(input_texture4, input.uv, input_texture4_size), bloom_threshold_offset_falloff.y); + + /* + // temporary reference code for comparing between linear and bicubic upsampling + float t = sin(time*2) > 0 ? 1 : 0; + level0 = lerp(inv_safe_range_tone_map_offset(TEX2D(input_texture0, input.uv).rgb, bloom_threshold_offset_falloff.y), level0, t); + level1 = lerp(inv_safe_range_tone_map_offset(TEX2D(input_texture1, input.uv).rgb, bloom_threshold_offset_falloff.y), level1, t); + level2 = lerp(inv_safe_range_tone_map_offset(TEX2D(input_texture2, input.uv).rgb, bloom_threshold_offset_falloff.y), level2, t); + level3 = lerp(inv_safe_range_tone_map_offset(TEX2D(input_texture3, input.uv).rgb, bloom_threshold_offset_falloff.y), level3, t); + level4 = lerp(inv_safe_range_tone_map_offset(TEX2D(input_texture4, input.uv).rgb, bloom_threshold_offset_falloff.y), level4, t); + */ + + half level_blending_factor = bloom_threshold_offset_falloff.z * 4.0; + + half level1_weight = 1 - saturate((1 - level_blending_factor)/1); + half level2_weight = 1 - saturate((2 - level_blending_factor)/2); + half level3_weight = 1 - saturate((3 - level_blending_factor)/3); + half level4_weight = 1 - saturate((4 - level_blending_factor)/4); + + level1_weight *= level1_weight; + level2_weight *= level2_weight; + level3_weight *= level3_weight; + level4_weight *= level4_weight; + + float3 lens_dirt = TEX2D(global_lens_dirt_map, input.uv).rgb; + float3 first_levels = level0 + (level1 * level1_weight); + float3 last_levels = (level2 * level2_weight) + (level3 * level3_weight) + (level4 * level4_weight); + + float lum_of_last_levels = saturate(dot(last_levels.rgb, luminance_vector.rgb)); + + float3 o = safe_range_tone_map_offset((first_levels + last_levels + (lum_of_last_levels * lens_dirt * bloom_lens_dirt_amount)) * bloom_tint, bloom_threshold_offset_falloff.y); + // Only adding dirt to the lens if the last levels of the blooms aren't empty (change this if the dirt isn't present enough in the effect) + + return float4(o, 1); + } + """ + } + + temporal_aa = { + includes = [ "common", "gbuffer_access", "taa_offsets", "color_management", "post_processing_common", "sampling_common", "lagrange_cubic_sampling" ] + samplers = { + defined_LINEAR_SAMPLING = { + input_texture0 = { sampler_states = "clamp_linear" } + } + ndefined_LINEAR_SAMPLING = { + input_texture0 = { sampler_states = "clamp_point" } + } + input_texture1 = { sampler_states = "clamp_linear" } + input_texture2 = { sampler_states = "clamp_point" } + input_texture3 = { sampler_states = "clamp_point" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + DECLARE_SAMPLER_2D(input_texture2); + DECLARE_SAMPLER_2D(input_texture3); + + #if defined(SIMPLE) + #undef TAA_ENABLE_HIGH_CONTRAST_AA + #define TAA_SIMPLE_BLEND_FACTOR 0.05 + #else + #define TAA_ENABLE_HIGH_CONTRAST_AA + #endif + + #define EPSILON 0.0000001 + #define TAA_DILATION_WIDTH 2 // (High Quality Temporal Supersampling, Karis 2014) + #define TAA_MAX_HISTORY_DIFFERENCE 0.5 // Maximum blend factor allowed + #define TAA_ANTIBLURINESS_MIN 0.125 + + #if defined(CUBIC_INTERPOLATION) + #define TAA_ANTIBLURINESS_MAX 0.05 + #define TAA_ANTIBLURINESS_VELOCITY_SCALAR 0.01 + #else + #define TAA_ANTIBLURINESS_MAX 0.375 + #define TAA_ANTIBLURINESS_VELOCITY_SCALAR 0.1 + #endif + + #if defined(TAA_ENABLE_HIGH_CONTRAST_AA) + float4 safe_range_tone_map(float4 color) + { + return float4(safe_range_tone_map(color.rgb), color.a); + } + + float4 inv_safe_range_tone_map(float4 color) + { + return float4(inv_safe_range_tone_map(color.rgb), color.a); + } + + #define APPLY_TONE_MAP safe_range_tone_map + #define APPLY_INV_TONE_MAP inv_safe_range_tone_map + #else + #define APPLY_TONE_MAP + #define APPLY_INV_TONE_MAP + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture1_size; + float2 input_texture3_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + float intersect_aabb(float3 dir, float3 start, float3 aabb) + { + float3 inv_dir = rcp(dir + 0.000001); + float3 diff_min = ( aabb - start) * inv_dir; + float3 diff_max = (-aabb - start) * inv_dir; + + float min_x = min(diff_min.x, diff_max.x); + float min_y = min(diff_min.y, diff_max.y); + float min_z = min(diff_min.z, diff_max.z); + + return max(max(min_x, min_y), min_z); + } + + float distance_to_ycocg_box(float3 prev_sample, float3 curr_sample, float3 neighborhood_min, float3 neighborhood_max) + { + float3 min_value = min(curr_sample, min(neighborhood_min, neighborhood_max)); + float3 max_value = max(curr_sample, max(neighborhood_min, neighborhood_max)); + float3 average_value = (max_value + min_value) * 0.5; + + float3 dir = curr_sample - prev_sample; + float3 start = prev_sample - average_value; + float3 aabb = max_value - average_value; + + return saturate(intersect_aabb(dir, start, aabb)); + } + + float3 find_closest_neighbor(float3 ss_pos) + { + float3 ss_front_most_neighbor = ss_pos; + + const int2 offset_x = int2(-TAA_DILATION_WIDTH, -TAA_DILATION_WIDTH); + const int2 offset_y = int2( TAA_DILATION_WIDTH, -TAA_DILATION_WIDTH); + const int2 offset_z = int2(-TAA_DILATION_WIDTH, TAA_DILATION_WIDTH); + const int2 offset_w = int2( TAA_DILATION_WIDTH, TAA_DILATION_WIDTH); + + float4 depths; + depths.x = Sample(input_texture3, ss_pos.xy, offset_x).r; + depths.y = Sample(input_texture3, ss_pos.xy, offset_y).r; + depths.z = Sample(input_texture3, ss_pos.xy, offset_z).r; + depths.w = Sample(input_texture3, ss_pos.xy, offset_w).r; + + float min_depth = min(min(min(depths.x, depths.y), depths.z), depths.w); + int2 offset_to_front_most_neighbor = + (min_depth == depths.x) ? offset_x : + (min_depth == depths.y) ? offset_y : + (min_depth == depths.z) ? offset_z : offset_w; + + if (min_depth < ss_pos.z) { + ss_front_most_neighbor.xy += float2(offset_to_front_most_neighbor) / input_texture3_size; + ss_front_most_neighbor.z = min_depth; + } + + return ss_front_most_neighbor; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + // Current fragment generic info + float4 result = 0; + + float2 uv = input.uv; + #if defined(SIMPLE) + float3 ss_pos = float3(uv, 0); + float3 ss_front_most_neighbor = ss_pos; + #else + float3 ss_pos = float3(uv, TEX2D(input_texture3, uv).r); + float3 ss_front_most_neighbor = find_closest_neighbor(ss_pos); + #endif + + // Reprojection info + float2 motion_vector = decode_velocity(TEX2D(input_texture2, ss_front_most_neighbor.xy).VELOCITY_COMPONENTS); + float2 ss_prev_pos = ss_pos.xy - motion_vector; + + #if defined(CUBIC_INTERPOLATION) + SAMPLE_TYPE prev_sample = catmull_rom_sample_2d(input_texture1, ss_prev_pos, input_texture1_size).SAMPLE_CHANNELS; + #else + SAMPLE_TYPE prev_sample = TEX2D(input_texture1, ss_prev_pos).SAMPLE_CHANNELS; + #endif + + prev_sample = APPLY_TONE_MAP(prev_sample); + + //#define DEBUG_REPROJECTION + #if defined(DEBUG_REPROJECTION) + SAMPLE_TYPE prev_sample_copy = prev_sample; + #endif + + bool reprojection_is_offscreen = !all(ss_prev_pos >= viewport.zw && ss_prev_pos <= (viewport.zw + viewport.xy)); + + // 3x3 neighbors info + SAMPLE_TYPE sample0 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[0]).SAMPLE_CHANNELS); + SAMPLE_TYPE sample1 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[1]).SAMPLE_CHANNELS); + SAMPLE_TYPE sample2 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[2]).SAMPLE_CHANNELS); + SAMPLE_TYPE sample3 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[3]).SAMPLE_CHANNELS); + SAMPLE_TYPE sample4 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[4]).SAMPLE_CHANNELS); + SAMPLE_TYPE sample5 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[5]).SAMPLE_CHANNELS); + SAMPLE_TYPE sample6 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[6]).SAMPLE_CHANNELS); + SAMPLE_TYPE sample7 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[7]).SAMPLE_CHANNELS); + SAMPLE_TYPE sample8 = APPLY_TONE_MAP(Sample(input_texture0, ss_pos.xy, neighbor_offsets_i[8]).SAMPLE_CHANNELS); + + #if defined(SIMPLE) + SAMPLE_TYPE reconstructed_sample = sample4; + #else + // Calculate velocity information + float velocity = sqrt(dot(motion_vector * back_buffer_size, motion_vector * back_buffer_size)); + + // Store the luminance of the reprojected pixel before performing 'back and fort error compensation' + float prev_luminance = luminance(prev_sample); + + // don't reconstruct the signal with blackman-harris at all (use the center of the pixel as the input) + #if 1 + SAMPLE_TYPE reconstructed_sample = sample4; + // blackman-harris weights pre calculated + #elif 0 + // Reconstruct the signal using the Blackman-harris 3.3 filter (Karis 2014) + int halton_harris_offset_id = frame_number % NUM_HALTON_OFFSETS; + + SAMPLE_TYPE reconstructed_sample = + sample0 * blackman_harris_weights[halton_harris_offset_id][0] + + sample1 * blackman_harris_weights[halton_harris_offset_id][1] + + sample2 * blackman_harris_weights[halton_harris_offset_id][2] + + sample3 * blackman_harris_weights[halton_harris_offset_id][3] + + sample4 * blackman_harris_weights[halton_harris_offset_id][4] + + sample5 * blackman_harris_weights[halton_harris_offset_id][5] + + sample6 * blackman_harris_weights[halton_harris_offset_id][6] + + sample7 * blackman_harris_weights[halton_harris_offset_id][7] + + sample8 * blackman_harris_weights[halton_harris_offset_id][8]; + // blackman-harris weights re-calculated per frame + #else + // Reconstruct the signal using the Blackman-harris 3.3 filter (Karis 2014) + int halton_harris_offset_id = frame_number % NUM_HALTON_OFFSETS; + + float2 taa_offset = halton_offsets[halton_harris_offset_id]; + float sample_weights[9]; + float total_weight = 0; + for(int i = 0; i != 9; ++i) { + sample_weights[i] = gaussian_blackman_harris(neighbor_offsets[i] + taa_offset); + total_weight += sample_weights[i]; + } + + for(int i = 0; i != 9; ++i) { + sample_weights[i] /= total_weight; + } + + SAMPLE_TYPE reconstructed_sample = + sample0 * sample_weights[0] + + sample1 * sample_weights[1] + + sample2 * sample_weights[2] + + sample3 * sample_weights[3] + + sample4 * sample_weights[4] + + sample5 * sample_weights[5] + + sample6 * sample_weights[6] + + sample7 * sample_weights[7] + + sample8 * sample_weights[8]; + #endif + #endif + + // Shaped Neighorhood clamp info + // We want the clamped of the min/max values to appear filtered. + // We split the samples into two "neighborhoods" and average + // them together (Karis 2014) + // ________________ ________________ + // Neighborhood '1' Neighborhood '2' + // 0 1 2 - 1 - + // 3 4 5 3 4 5 + // 6 7 8 - 7 - + SAMPLE_TYPE neighborhood_1_min = min(min(sample0, sample2), min(sample6, sample8)); + SAMPLE_TYPE neighborhood_1_max = max(max(sample0, sample2), max(sample6, sample8)); + SAMPLE_TYPE neighborhood_2_min = min(min(min(sample1, sample3), min(sample4, sample5)), sample7); + SAMPLE_TYPE neighborhood_2_max = max(max(max(sample1, sample3), max(sample4, sample5)), sample7); + neighborhood_1_min = min(neighborhood_1_min, neighborhood_2_min); + neighborhood_1_max = max(neighborhood_1_max, neighborhood_2_max); + + #if defined(SIMPLE) + prev_sample = clamp(prev_sample, neighborhood_1_min, neighborhood_1_max); + result.SAMPLE_CHANNELS = reprojection_is_offscreen ? reconstructed_sample : lerp(prev_sample, reconstructed_sample, TAA_SIMPLE_BLEND_FACTOR); + #else + SAMPLE_TYPE neighborhood_min = lerp(neighborhood_1_min, neighborhood_2_min, 0.5); + SAMPLE_TYPE neighborhood_max = lerp(neighborhood_1_max, neighborhood_2_max, 0.5); + float neighborhood_luminance_range = luminance(neighborhood_max) - luminance(neighborhood_min); + + // Clip history to a YCoCg box (Karis 2014) + float history_clip_amount = distance_to_ycocg_box(prev_sample, reconstructed_sample, neighborhood_min, neighborhood_max); + prev_sample = lerp(prev_sample, reconstructed_sample, saturate(history_clip_amount)); + + // The blend factor is calculated into two parts which attempt to minimize the two main + // problems of taa (flickering and blurriness of slowly moving objects) + + // 1) To reduce flickering, we calculate a term which represents the distance of the history + // value to the current clamping range (the local luminance contrast). The term is calculated + // as the ratio of the previous luminance vs the local luminance contrast + float antiflickering_term = prev_luminance/(prev_luminance + neighborhood_luminance_range + EPSILON); + + // 2) To reduce numerical duffusion (over blurriness), we calculate a term which re-introduces + // some of the aliasing of the current frame into the history buffer for moving pixels. term is + // calculated as the current pixel velocity remaped into a predefined min and max range + float antiblurriness_term = TAA_ANTIBLURINESS_MIN + saturate(velocity * TAA_ANTIBLURINESS_VELOCITY_SCALAR) * TAA_ANTIBLURINESS_MAX; + + float history_difference = antiflickering_term * antiblurriness_term; + + // Finally, if the pixel is moving very differently than the history we will keep more of the pixel + history_difference = min(TAA_MAX_HISTORY_DIFFERENCE, history_difference); + + // Handle offscreen case + if(reprojection_is_offscreen) { + prev_sample = reconstructed_sample; + } + + + #if defined(DEBUG_REPROJECTION) + if (frame_number%100 == 0) + result.SAMPLE_CHANNELS = sample4; + else { + if(reprojection_is_offscreen) { + prev_sample = 0; + } else { + result.SAMPLE_CHANNELS = prev_sample_copy; + } + } + #else + // Blend the reprojected sample with the current one + result.SAMPLE_CHANNELS = lerp(prev_sample, sample4, history_difference); + #endif + result.SAMPLE_CHANNELS = APPLY_INV_TONE_MAP(result.SAMPLE_CHANNELS); + result.SAMPLE_CHANNELS = -min(-result.SAMPLE_CHANNELS, 0.0); + #endif + + return result; + } + """ + } + + ssr_hiz_pass = { + includes = [ "common", "gbuffer_access" ] + samplers = { + input_texture0 = { sampler_states = "hiz_sample_mip_index" } + } + + code=""" + #ifdef RENDERER_GNM + #pragma PSSL_target_output_format (target 0 FMT_32_R) + #endif + + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture0_base_size; + float input_mip_level; + float output_mip_level; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float ps_main(PS_INPUT input) : SV_TARGET0 { + #if defined(LEVEL_0) + return TEX2DLOD(input_texture0, input.uv, 0).r; + #else + // map the current pixel to it's corresponding pixel in the previous mip + float2 prev_cell_count = (int2)input_texture0_base_size >> (int)(input_mip_level); + float2 cell_count = (int2)input_texture0_base_size >> (int)output_mip_level; + + int2 cell_id = (int2)(input.uv * cell_count) * 2; + + // To be safe we sample at a half pixel offset (so if we sample cell_id [0.0] we sample at [0.5, 0.5]/prev_cell_count) + float v1 = TEX2DLOD(input_texture0, (cell_id + int2(0,0) + float2(0.5, 0.5)) / prev_cell_count, input_mip_level).r; + float v2 = TEX2DLOD(input_texture0, (cell_id + int2(1,0) + float2(0.5, 0.5)) / prev_cell_count, input_mip_level).r; + float v3 = TEX2DLOD(input_texture0, (cell_id + int2(0,1) + float2(0.5, 0.5)) / prev_cell_count, input_mip_level).r; + float v4 = TEX2DLOD(input_texture0, (cell_id + int2(1,1) + float2(0.5, 0.5)) / prev_cell_count, input_mip_level).r; + + return min(min(min(v1, v2), v3), v4); + #endif + } + """ + } + + ssr_ray_march_pass = { + includes = [ "common", "gbuffer_access", "color_management", "space_conversion", "taa_offsets", "post_processing_common" ] + samplers = { + input_texture0 = { sampler_states = "clamp_point" } + input_texture1 = { sampler_states = "clamp_linear" } + input_texture2 = { sampler_states = "clamp_point" } + input_texture3 = { sampler_states = "clamp_point" } + input_texture4 = { sampler_states = "clamp_linear" } + input_texture5 = { sampler_states = "clamp_point" } + } + + code=""" + #define HIZ_START_LEVEL 1.0 + #define HIZ_STOP_LEVEL 0.0 + #define HIZ_MAX_LEVEL 9.0 + #define CROSS_EPSILON 0.0000001 + #define SSR_MIPMAP_ROUGNESS_SCALE 14 // Maps a rougness value to a mip level + #define SSR_REPROJECTION_MIN 0.05 + #define SSR_REPROJECTION_SCALAR 1000.0 + + // #define SSR_ENABLE_CONTACT_HARDERING + #define SSR_ROUNGESS_OFFSET_START_VALUE 0.1 // Rougness value at which a mip level starts being added + #define SSR_ROUNGESS_OFFSET_END_VALUE 0.05 // Rougness value at which a mip level finishes being added (0.15 - SSR_ROUNGESS_OFFSET_START_VALUE) + #define SSR_RAYLENGTH_SCALE 3 // Controls the amount of contact hardening + #define SSR_CONTACT_HARDENING_CURVE 1.5 // Controls the blend curve between (should be linear in theory, but a linear blend can cause ugly discontinuities for roughness values > 0.3) + + #if defined(SSR_LOW_QUALITY) + #define MAX_ITERATIONS 64 + #else + #define MAX_ITERATIONS 64 + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_OUTPUT { + float4 buffer0; + float buffer1; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float4x4 camera_inv_proj; + float2 output_target_base_size; + float2 input_texture2_size; + float2 ssr_surface_thickness_threshold; + float ssr_screen_edge_threshold; + float ssr_ray_bending_enabled; + CBUFFER_END + + DECLARE_SAMPLER_2D(input_texture0); + DECLARE_SAMPLER_2D(input_texture1); + Texture2D input_texture2; // It's easier to track the corresponding hiz cell of a ray with the Load operator + DECLARE_SAMPLER_2D(input_texture3); + DECLARE_SAMPLER_2D(input_texture4); + DECLARE_SAMPLER_2D(input_texture5); + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float4 p = mul(input.position, world_view_proj); + o.position = p; + o.uv = input.uv; + + return o; + } + + float3 decode_world_normal(float2 uv) { + return decode_signed_normal(TEX2D(input_texture0, uv).rgb); + } + + float3 move_point(float3 o, float3 d, float t) { + return o + d * t; + } + + float2 cell(float2 ray, float2 cell_count) { + return floor(ray * cell_count); + } + + float2 cell_count(float level) { + float2 div = level == 0.0f ? 1.0f : exp2(level); + return input_texture2_size / div; + } + + float3 intersect_cell_boundary(float3 pos, float3 dir, float2 cell_id, float2 cell_count, float2 cross_step, float2 cross_offset) { + float2 cell_size = 1.0 / cell_count; + float2 planes = cell_id/(cell_count) + cell_size * cross_step + cross_offset; + float2 solutions = (planes - pos.xy)/dir.xy; + return move_point(pos, dir, min(solutions.x, solutions.y)); + } + + bool crossed_cell_boundary(float2 cell_id_one, float2 cell_id_two) { + return (int)cell_id_one.x != (int)cell_id_two.x || (int)cell_id_one.y != (int)cell_id_two.y; + } + + float minimum_depth_plane(float2 ray, float level, float2 cell_count) { + return input_texture2.Load(int3(ray.xy * cell_count, level)).r; + } + + float3 hi_z_trace(float3 p, float3 v, float dithering_term) { + + float level = HIZ_START_LEVEL; + float2 hi_z_size = cell_count(level); + float3 ray = p; + + float2 cross_step = float2(v.x >= 0.0f ? 1.0f : -1.0f, v.y >= 0.0f ? 1.0f : -1.0f); + float2 cross_offset = float2(cross_step.xy) * CROSS_EPSILON; + cross_step.xy = saturate(cross_step.xy); + + float2 ray_cell = cell(ray.xy, hi_z_size.xy); + ray = intersect_cell_boundary(ray, v, ray_cell, hi_z_size.xy, cross_step.xy, cross_offset.xy); + + float3 v_z = v/v.z; + int iterations = 0; + while((level >= HIZ_STOP_LEVEL) && (iterations < MAX_ITERATIONS)) { + // get the cell number of the current ray + float2 current_cell_count = cell_count(level); + float2 old_cell_id = cell(ray.xy, current_cell_count); + + // get the minimum depth plane in which the current ray resides + float min_z = minimum_depth_plane(ray.xy, level, current_cell_count); + + // intersect only if ray depth is below the minimum depth plane + float3 tmp_ray = ray; + if(v.z > 0) { + float min_minus_ray = min_z - ray.z; + if(min_minus_ray > 0) { + tmp_ray = move_point(ray, v_z, min_minus_ray); + } + float2 new_cell_id = cell(tmp_ray.xy, current_cell_count); + if(crossed_cell_boundary(old_cell_id, new_cell_id)) { + tmp_ray = intersect_cell_boundary(ray, v, old_cell_id, current_cell_count.xy, cross_step.xy, cross_offset.xy); + level = min(HIZ_MAX_LEVEL, level + 2.0f); + } + } else { + if(ray.z < min_z) { + tmp_ray = intersect_cell_boundary(ray, v, old_cell_id, current_cell_count.xy, cross_step.xy, cross_offset.xy); + level = min(HIZ_MAX_LEVEL, level + 2.0f); + } + } + + ray.xyz = tmp_ray.xyz; + --level; + ++iterations; + } + + #if defined(SSR_LOW_QUALITY) + return ray; + #else + // Dither the ray to prevent banding artifacts + float2 dithered_pos = ray.xy - normalize(v.xy) * (dithering_term + 1) * 0.015/back_buffer_size; + float dithered_z = input_texture2.Load(int3(dithered_pos * back_buffer_size, 0)).r; + float dithered_to_blend = abs(linearize_depth(ray.z) - linearize_depth(dithered_z) * 20); + + ray.xy = lerp(ray.xy, dithered_pos, dithered_to_blend); + return ray; + #endif + + + } + + float generate_reflection_mask(float3 ss_ray, float3 ss_reflection, float3 view_pos_reflection) { + + // Reject reflections that ended up 'behind' a surface + float2 ss_ray_dir = normalize(ss_reflection.xy) * ssr_surface_thickness_threshold.x; + float2 ss_ray1 = ss_ray.xy + ss_ray_dir; + float2 ss_ray2 = ss_ray.xy; + + // Unfortunate 2 taps, but so far this is the most stable way to identify rays that have traveled behing a surface. + // We basically find the ray intersection and evalute the depth discontinuity a closer and further along the ray + float delta1 = linearize_depth(input_texture2.Load(int3(ss_ray1 * input_texture2_size, 0)).r); + float delta2 = linearize_depth(input_texture2.Load(int3(ss_ray2 * input_texture2_size, 0)).r); + float ray_z = linearize_depth(ss_ray.z); + float reflection_visibility_mask = 1 - saturate(abs(delta1 - ray_z) * ssr_surface_thickness_threshold.y + abs(delta2 - ray_z) * ssr_surface_thickness_threshold.y); + + // Reject points that are too close to the screen's edge + float distance_to_horizontal_edge = min(saturate(ss_ray.y), abs(ss_ray.y - 1)); + float distance_to_vertical_edge = min(saturate(ss_ray.x), abs(ss_ray.x - 1)); + float distance_to_edge = min(distance_to_horizontal_edge, distance_to_vertical_edge); + float edge_mask = saturate(distance_to_edge / ssr_screen_edge_threshold); + edge_mask = max(edge_mask, ssr_ray_bending_enabled); + + float skydome_mask = (ss_ray.z != 1.0); + + // We shouldn't need this. Needs to be investigated further. + float dangerous_reflection_mask = (view_pos_reflection.y >= 0); + + return reflection_visibility_mask * edge_mask * skydome_mask * dangerous_reflection_mask; + } + + float3 proj_point_in_plane(float3 p, float3 v0, float3 n, out float d) { + d = dot(n, p - v0); + return p - (n * d); + } + + float3 find_reflection_incident_point(float3 p0, float3 p1, float3 v0, float3 n) { + float d0 = 0; + float d1 = 0; + float3 proj_p0 = proj_point_in_plane(p0, v0, n, d0); + float3 proj_p1 = proj_point_in_plane(p1, v0, n, d1); + + if(d1 < d0) { + return (proj_p0 - proj_p1) * d1/(d0+d1) + proj_p1; + }else{ + return (proj_p1 - proj_p0) * d0/(d0+d1) + proj_p0; + } + } + + float2 find_previous_reflection_position(float3 ss_pos, float3 ss_ray, float2 surface_motion_vector, float2 reflection_motion_vector, float3 world_normal) { + float3 ss_p0 = 0; + ss_p0.xy = ss_pos.xy - surface_motion_vector; + ss_p0.z = TEX2D(input_texture5, ss_p0.xy).r; + + float3 ss_p1 = 0; + ss_p1.xy = ss_ray.xy - reflection_motion_vector; + ss_p1.z = TEX2D(input_texture5, ss_p1.xy).r; + + float3 view_n = normalize(world_to_prev_view(world_normal, 0)); + float3 view_p0 = float3(0,0,0); + float3 view_v0 = ss_to_view(ss_p0, 1); + float3 view_p1 = ss_to_view(ss_p1, 1); + + float3 view_intersection = find_reflection_incident_point(view_p0, view_p1, view_v0, view_n); + float3 ss_intersection = view_to_ss(view_intersection, 1); + + return ss_intersection.xy; + } + + float map_reflection_to_mip_level(float ray_length, float roughness, float mask) { + float ssr_glossy_term = roughness * SSR_MIPMAP_ROUGNESS_SCALE; + + #if defined(SSR_ENABLE_CONTACT_HARDERING) + float ssr_blur_offset = (saturate(roughness - SSR_ROUNGESS_OFFSET_START_VALUE)/SSR_ROUNGESS_OFFSET_END_VALUE); + float ssr_blur_scale = ray_length * roughness * SSR_RAYLENGTH_SCALE; + float ssr_blur_term = saturate(ssr_blur_scale + ssr_blur_offset); + float ssr_contact_hardening_term = (pow(ssr_blur_term, SSR_CONTACT_HARDENING_CURVE)); + ssr_glossy_term *= ssr_contact_hardening_term; + #endif + + return min(ssr_glossy_term , SSR_MIPMAP_LEVELS); + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) : SV_TARGET0 { + float4 result = 0.0; + float mip_level = 0.0; + + float2 uv = input.uv; + #if defined(SSR_LOW_QUALITY) + // When running ssr at lower resolution, start the raytrace in the center of the corresponding hiz cell + int2 pixel_pos = input.uv * back_buffer_size; + uv = float2(pixel_pos)/back_buffer_size + 0.5/back_buffer_size; + #endif + + float non_linear_depth = input_texture2.Load(int3(uv * input_texture2_size, 0)).r; + + // Do not execute for the skydome + if(non_linear_depth < 1) { + + float3 ss_pos = float3(uv, non_linear_depth); + + float3 view_pos = ss_to_view(ss_pos, 1); + float3 view_ray = normalize(view_pos); + float3 world_normal = normalize(decode_world_normal(input.uv)); + float3 view_normal = normalize(world_to_view(world_normal, 0)); + + float3 view_reflection = normalize(reflect(view_ray, view_normal)); + float3 view_pos_reflection = view_pos + view_reflection; + + float3 ss_pos_reflection = view_to_ss(view_pos_reflection, 1); + float3 ss_reflection = normalize(ss_pos_reflection - ss_pos); + + float roughness = TEX2D(input_texture0, input.uv).a; + + #if defined(SSR_LOW_QUALITY) + float dithering_term = 0; + #else + // We adjust the number of halton offsets we use to dither the ssr. We do this to + // prevent jitters to be propagated into the blurred ssr mip chain. Anything above + // roughness of 0.1 will not jitter the dither pattern. The idea behind this is that + // we can jitter the dither for mirror reflections and still have stable glossy reflections + uint num_offsets = 8 - saturate(roughness/0.1) * 8; + int2 pixel_pos = input.uv * back_buffer_size + 8 * halton_offsets[int(frame_number) % num_offsets]; + float dithering_term = dither_pattern[pixel_pos.x % 4][pixel_pos.y % 4]; + #endif + + // Bend the ray as it appraoch the screen's edge + float left_edge_term = 1.0 - ss_pos.x/ssr_screen_edge_threshold; + float right_edge_term = (ss_pos.x - (1.0 - ssr_screen_edge_threshold))/ssr_screen_edge_threshold; + float bending_amount = saturate(max(left_edge_term, right_edge_term)); + bending_amount = smoothstep(0, 1, bending_amount); + float eps = left_edge_term < 0 ? -0.02 : 0.02; + + ss_reflection.xy = lerp(ss_reflection.xy, float2(eps, -1), bending_amount * ssr_ray_bending_enabled); + + // Trace reflection + float3 ss_ray = hi_z_trace(ss_pos, ss_reflection, dithering_term); + float ray_length = length(ss_to_view(ss_ray, 1) - view_pos); + + // Generate reflection mask + float mask = generate_reflection_mask(ss_ray, ss_reflection, view_pos_reflection); + + if(mask > 0) { + // Fetch surface and reflected motion vectors + float2 reflection_motion_vector = decode_velocity(TEX2D(input_texture3, ss_ray.xy).VELOCITY_COMPONENTS); + + // Note: Remember that the ssr pass gets executed before the lighting pass so the hdr data we + // use is one frame old (that's why we need to fetch the data with a motion vector offset) + float4 color = TEX2D(input_texture1, ss_ray.xy - reflection_motion_vector); + color.rgb = safe_range_tone_map(color.rgb); + color.a = mask; + result = color; + } else { + // Needed to avoid getting a black hallow around the reflections + result.rgb = safe_range_tone_map(TEX2D(input_texture1, input.uv).rgb); + } + + float2 motion_vector = decode_velocity(TEX2D(input_texture3, input.uv).VELOCITY_COMPONENTS); + float4 prev_val = TEX2D(input_texture4, input.uv); + float blend_factor = length(motion_vector) * SSR_REPROJECTION_SCALAR + SSR_REPROJECTION_MIN; + float4 reprojection = lerp(result, prev_val, (1.0 - saturate(blend_factor)) * taa_enabled); + + // Since we cannot track the trace depth of an ssr reflection, clamp the reprojected + // reflection into a defined range to prevent introducing excessive amounts of light + // (Think of reflections between two pefect perpendicular mirrors) + result = clamp(reprojection, 0, 1); + + mip_level = map_reflection_to_mip_level(ray_length, roughness, mask); + } + + PS_OUTPUT to_return; + to_return.buffer0 = result; + to_return.buffer1 = mip_level/SSR_MIPMAP_LEVELS; + + return to_return; + } + """ + } + + lens_effects = { + includes = [ "common", "gbuffer_access" ] + samplers = { + input_texture0 = { sampler_states = "clamp_linear" } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float2 input_texture0_size; + float4x4 world_view_proj; + float3 lens_quality_properties; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) + { + PS_INPUT o; + o.position = mul( input.position, world_view_proj ); + o.uv = input.uv; + return o; + } + + static const float distorsion_uv_scale = 0.660; + static const float fringe_uv_scale = 0.91; + static const float3 fringe_red_cyan = float3( 0.092, 0.047, 0.045 ); + static const float3 fringe_blue_yellow = float3( 0.090, 0.092, 0.045 ); + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main( PS_INPUT input ) : SV_TARGET0 + { + float lens_distortion = lens_quality_properties.x; + float lens_fringe_intensity = lens_quality_properties.y; + float lens_fringe_color = lens_quality_properties.z; + + float2 uv = input.uv - 0.5; + float radius_2 = dot(uv, uv); + // Compute lense pincushion/barrel distortion. + // + // NOTE: Mustache (or complex) distortions can also be easily supported + // by adding another lens_cubic_distortion factor to the equation: + // + // distortion = 1.0 + r^2 * lens_distortion * ( 1.0 + lens_cubic_distortion * r ) + // + // Since it is a rare effect and we want to minimize overall user driven uniforms + // a design choice was made to cut it out. + // + float distortion = 1.0 + radius_2 * lens_distortion; + + float3 fringe_color = lerp(fringe_red_cyan, fringe_blue_yellow, lens_fringe_color); + float3 uv_weights = distortion * (1.0 + lens_fringe_intensity * fringe_color); + + // Scale UV weights to remove texture repeats/clamping on screen edges. + uv_weights *= + lerp(1.0, distorsion_uv_scale, pow(saturate(lens_distortion), 0.75)) * + lerp(1.0, fringe_uv_scale, lens_fringe_intensity); + + // Compute color considering lateral chromatic aberration + float3 color; + for (int i = 0; i < 3; i++) { + color[i] = TEX2D(input_texture0, uv_weights[i] * uv + 0.5)[i]; + } + + return float4(color.rgb, 1.0); + } + """ + } + + repopulate_hiz = { + includes = [ "common" ] + + code=""" + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.position.z = o.position.w; + return o; + } + + struct PS_OUTPUT { + float4 base : SV_TARGET0; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_OUTPUT ps_main(PS_INPUT input) { + PS_OUTPUT o; + o.base = 1.0f; + + return o; + } + """ + } + + copy_filter = { + includes = [ "common", "sampling_common", "bicubic_sampling", "lagrange_cubic_sampling", "hermite_cubic_sampling"] + + samplers = { + defined_LINEAR_FILTER = { + input_texture0 = { sampler_states = "clamp_linear" } + } + ndefined_LINEAR_FILTER = { + input_texture0 = { sampler_states = "clamp_point" } + } + } + + code=""" + DECLARE_SAMPLER_2D(input_texture0); + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 input_texture0_size; + CBUFFER_END + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + o.uv = input.uv; + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + half4 ps_main(PS_INPUT input) : SV_TARGET0 { + float4 result = float4(0, 0, 0, 1); + + #if defined(POINT_FILTER) || defined(LINEAR_FILTER) + result.SAMPLE_CHANNELS = TEX2D(input_texture0, input.uv).SAMPLE_CHANNELS; + #elif defined(CUBIC_FILTER) + result.SAMPLE_CHANNELS = bicubic_sample_2d(input_texture0, input.uv, input_texture0_size); + #elif defined(LAGRANGE_CUBIC_FILTER) + result.SAMPLE_CHANNELS = lagrange_cubic_sample_2d(input_texture0, input.uv, input_texture0_size); + #elif defined(HERMITE_CUBIC_FILTER) + result.SAMPLE_CHANNELS = hermite_cubic_sample_2d(input_texture0, input.uv, input_texture0_size); + #endif + + return result; + } + """ + } +} + +shaders = { + scene_combine = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [{ + defined="PREMULTIPLIED" + pass = [ + { hlsl_shader="scene_combine" render_states="filter_premultiplied" } + ] + fail = [ + { hlsl_shader="scene_combine" render_states="filter" } + ] + }] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + bilateral_upsample = { + editor_advanced_mode = true + + contexts={ + default = { + passes_sort_mode="immediate" + passes=[ + {hlsl_shader="bilateral_upsample" render_states="filter"} + ] + } + } + + compile= { + default = [ + {defines=[""]} + ] + } + } + + merge_skydome_motion_vectors = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="merge_skydome_motion_vectors" render_states="filter_farplane" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + + mb_tile_max = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="mb_tile_max" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + mb_neighbour_max = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="mb_neighbour_max" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + mb_bake_velocity_depth = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="mb_bake_velocity_depth" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + mb_bake_radius_depth = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="mb_bake_radius_depth" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + mb_reconstruct_filter_blur = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="mb_reconstruct_filter_blur" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + ssao_ao_pass = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="ssao_ao_pass" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + ssao_blur_pass = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="ssao_blur_pass" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + ssao_mip_pass = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="ssao_mip_pass" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + + ssao_depth_copy_pass = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="ssao_depth_copy_pass" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + depth_of_field = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="depth_of_field" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + merge_depth_of_field = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="merge_depth_of_field" defines=["SAMPLE_RGB"] render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + calculate_coc = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="calculate_coc" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + depth_fog = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="depth_fog" render_states="filter_fog" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + bright_pass = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="bright_pass" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + filter = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="filter" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + blend_bloom = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="blend_bloom" defines=["SAMPLE_RGB"] render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + temporal_aa = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [{ + defined="SAMPLE_RGBA" + pass = [ + { hlsl_shader="temporal_aa" render_states="filter" } + ] + fail = [ + { hlsl_shader="temporal_aa" defines=["SAMPLE_RGB"] render_states="filter" } + ] + }] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + ssr_hiz_pass = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="ssr_hiz_pass" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + ssr_ray_march_pass = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="ssr_ray_march_pass" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + lens_effects = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="lens_effects" render_states="filter" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + repopulate_hiz = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="repopulate_hiz" render_states="repopulate_hiz" } + ] + } + } + + compile = { + default = [ + { defines=[""] } + ] + } + } + + copy_filter = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [{ + defined="PREMULTIPLIED" + pass = [ + { hlsl_shader="copy_filter" defines=["SAMPLE_RGBA"] render_states="filter_premultiplied" } + ] + fail = [ + { hlsl_shader="copy_filter" defines=["SAMPLE_RGB"] render_states="filter" } + ] + }] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } +} + +static_compile= [ + { shader="scene_combine" } + { shader="scene_combine" defines=["APPLY_BLOOM"] } + { shader="scene_combine" defines=["APPLY_BLOOM" "COLOR_GRADING"] } + { shader="scene_combine" defines=["COLOR_GRADING"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="bilateral_upsample" } + { shader="blend_bloom" defines=[] } + { shader="filter" defines=["DOWNSAMPLE_4x4"] } + { shader="filter" defines=["DOWNSAMPLE_2x2"] } + { shader="filter" defines=["DOWNSAMPLE_MIP"] } + { shader="filter" defines=["SEPARABLE_BILINEAR_GAUSSIAN_5TAP_X"] } + { shader="filter" defines=["SEPARABLE_BILINEAR_GAUSSIAN_5TAP_Y"] } + { shader="bright_pass" } + { shader="depth_fog" } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="merge_skydome_motion_vectors" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="mb_tile_max" defines=["HORIZONTAL_PASS"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="mb_tile_max" defines=["VERTICAL_PASS"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="mb_neighbour_max" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="mb_bake_velocity_depth" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="mb_bake_radius_depth" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="mb_reconstruct_filter_blur" } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_ao_pass" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_ao_pass" defines=["AO_LOW_QUALITY"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_ao_pass" defines=["AO_MID_QUALITY"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_ao_pass" defines=["AO_HIGH_QUALITY"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_ao_pass" defines=["AO_LOW_QUALITY" "OPTIMIZED"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_ao_pass" defines=["AO_MID_QUALITY" "OPTIMIZED"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_ao_pass" defines=["AO_HIGH_QUALITY" "OPTIMIZED"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_blur_pass" defines=["SEPARABLE_SSAO_BLUR_9TAP_X"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_blur_pass" defines=["SEPARABLE_SSAO_BLUR_9TAP_Y_PLUS_MERGE_AO_REPROJECTION"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_reprojection_pass" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_mip_pass" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssao_depth_copy_pass" } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="calculate_coc" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="depth_of_field" defines=["HORIZONTAL_PASS"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="depth_of_field" defines=["ASCENDING_DIAGONAL_PASS"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="depth_of_field" defines=["DESCENDING_DIAGONAL_PASS"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="merge_depth_of_field" } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="temporal_aa" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="temporal_aa" defines=["SIMPLE"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="temporal_aa" defines=["CUBIC_INTERPOLATION"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssr_hiz_pass" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssr_hiz_pass" defines=["LEVEL_0"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssr_ray_march_pass" } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="ssr_ray_march_pass" defines=["SSR_LOW_QUALITY"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="lens_effects" } + + { if: "on_platform(XB1)" shader="repopulate_hiz" } + + // Fatshark + { shader="scene_combine" defines=["COLOR_GRADING" "ADJUST_GAMMA"] } + { shader="scene_combine" defines=["EYE_ADAPTATION" "COLOR_GRADING" "ADJUST_GAMMA"] } + { shader="scene_combine" defines=["APPLY_BLOOM" "COLOR_GRADING" "ADJUST_GAMMA"] } + { shader="scene_combine" defines=["APPLY_LIGHT_SHAFTS" "COLOR_GRADING" "ADJUST_GAMMA"] } + { shader="scene_combine" defines=["APPLY_BLOOM" "APPLY_LIGHT_SHAFTS" "COLOR_GRADING" "ADJUST_GAMMA"] } + { shader="scene_combine" defines=["EYE_ADAPTATION" "APPLY_BLOOM" "COLOR_GRADING" "ADJUST_GAMMA"] } + { shader="scene_combine" defines=["EYE_ADAPTATION" "APPLY_LIGHT_SHAFTS" "COLOR_GRADING" "ADJUST_GAMMA"] } + { shader="scene_combine" defines=["EYE_ADAPTATION" "APPLY_BLOOM" "APPLY_LIGHT_SHAFTS" "COLOR_GRADING" "ADJUST_GAMMA"] } + + { shader="scene_combine" defines=["COLOR_GRADING" "ADJUST_GAMMA" "NO_TONE_MAPPING"] } + { shader="scene_combine" defines=["EYE_ADAPTATION" "COLOR_GRADING" "ADJUST_GAMMA" "NO_TONE_MAPPING"] } + { shader="scene_combine" defines=["APPLY_BLOOM" "COLOR_GRADING" "ADJUST_GAMMA" "NO_TONE_MAPPING"] } + { shader="scene_combine" defines=["APPLY_LIGHT_SHAFTS" "COLOR_GRADING" "ADJUST_GAMMA" "NO_TONE_MAPPING"] } + { shader="scene_combine" defines=["APPLY_BLOOM" "APPLY_LIGHT_SHAFTS" "COLOR_GRADING" "ADJUST_GAMMA" "NO_TONE_MAPPING"] } + { shader="scene_combine" defines=["EYE_ADAPTATION" "APPLY_BLOOM" "COLOR_GRADING" "ADJUST_GAMMA" "NO_TONE_MAPPING"] } + { shader="scene_combine" defines=["EYE_ADAPTATION" "APPLY_LIGHT_SHAFTS" "COLOR_GRADING" "ADJUST_GAMMA" "NO_TONE_MAPPING"] } + { shader="scene_combine" defines=["EYE_ADAPTATION" "APPLY_BLOOM" "APPLY_LIGHT_SHAFTS" "COLOR_GRADING" "ADJUST_GAMMA" "NO_TONE_MAPPING"] } + + { shader="bright_pass" defines=["EYE_ADAPTATION"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="depth_fog" defines=["SECONDARY_SUN_BLEND"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="depth_fog" defines=["CUSTOM_FOG_BLEND"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="depth_fog" defines=["SECONDARY_SUN_BLEND" "CUSTOM_FOG_BLEND"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="temporal_aa" defines=["SIMPLE" "LINEAR_SAMPLING"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="copy_filter" defines=["CUBIC_FILTER"] } + { if: "on_renderer(D3D11, D3D12, GNM)" shader="copy_filter" defines=["CUBIC_FILTER" "PREMULTIPLIED"] } + + { if: "on_renderer(D3D11, D3D12, GNM)" shader="temporal_aa" defines=["CUBIC_INTERPOLATION", "SAMPLE_RGBA"] } + + { shader="scene_combine" defines=["COLOR_GRADING" "ADJUST_GAMMA" "PREMULTIPLIED"] } +] \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/post_processing_common.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/post_processing_common.shader_source new file mode 100644 index 0000000..6292252 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/post_processing_common.shader_source @@ -0,0 +1,55 @@ +hlsl_shaders = { + post_processing_common = { + code=""" + #if !defined(RENDERER_GL) + #define AO_MAX_DISTANCE 300.0 + #define SSR_MIPMAP_LEVELS 5.0 + + #define TWOPI PI * 2 + + #define VELOCITY_COMPONENTS xy + + #if defined(PLATFORM_XB1) + #define VELOCITY_EPSILON 0.00005 + #else + #define VELOCITY_EPSILON 0.0001 + #endif + + // This is ~15px for a 1920x1080 back_buffer + #define MAX_COC 0.00925925925 + + // The first 8 terms of a base 3 halton sequence + static const float halton_sequence_1d[8] = { 1.0/3.0, 2.0/3.0, 1.0/9.0, 4.0/9.0, 7.0/9.0, 2.0/9.0, 5.0/9.0, 8.0/9.0}; + + // Beyer dithering pattern + static const float dither_pattern[4][4] = { + {( 0.0/15.0 - 0.5) * 2.0, ( 8.0/15.0 - 0.5) * 2.0, ( 2.0/15.0 - 0.5) * 2.0, (10.0/15.0 - 0.5) * 2.0}, + {(12.0/15.0 - 0.5) * 2.0, ( 4.0/15.0 - 0.5) * 2.0, (14.0/15.0 - 0.5) * 2.0, ( 6.0/15.0 - 0.5) * 2.0}, + {( 3.0/15.0 - 0.5) * 2.0, (11.0/15.0 - 0.5) * 2.0, ( 1.0/15.0 - 0.5) * 2.0, ( 9.0/15.0 - 0.5) * 2.0}, + {(15.0/15.0 - 0.5) * 2.0, ( 7.0/15.0 - 0.5) * 2.0, (13.0/15.0 - 0.5) * 2.0, ( 5.0/15.0 - 0.5) * 2.0} + }; + + float encode_bit_in_float(in float value, in bool bit) { + uint encoded = asint(value.x); + encoded = encoded & ~1; + encoded += bit; + return asfloat(encoded); + } + + float decode_bit_from_float(in uint encoded, out bool bit) { + bit = ((encoded << 31) == 0); + return asfloat(encoded & ~1); + } + + float encode_coc(float c) { + return saturate((c + 1.0) * 0.5); + } + + float decode_coc(float c) { + return c * 2.0 - 1.0; + } + + #endif + """ + } +} \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/sampling_common.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/sampling_common.shader_source new file mode 100644 index 0000000..47946b0 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/sampling_common.shader_source @@ -0,0 +1,84 @@ +hlsl_shaders = { + random = { + code=""" + // from http://www.reedbeta.com/blog/2013/01/12/quick-and-easy-gpu-random-numbers-in-d3d11/ + // (which in turn reference http://www.burtleburtle.net/bob/hash/integer.html) + uint wang_hash(uint seed) { + seed = (seed ^ 61) ^ (seed >> 16); + seed *= 9; + seed = seed ^ (seed >> 4); + seed *= 0x27d4eb2d; + seed = seed ^ (seed >> 15); + return seed; + } + + float noise2d(float2 uv) { + return frac(sin(dot(uv, float2(12.9898,78.233)*2.0)) * 43758.5453); + } + + static const uint shuffle_frames16[16] = {13,7,5,10,12,4,11,3,14,9,2,0,8,6,1,15 }; + """ + } + sampling = { + code=""" + float2 hammersley_sequence_2d(uint i, uint n) { + return float2((float)i / n, reversebits(i) * 2.3283064365386963e-10); + } + + float3 orient_frame(float3 n, float3 p) { + float3 up = abs(n.z) < 0.999 ? float3(0.0,0.0,1.0) : float3(1.0,0.0,0.0); + float3 uu = normalize(cross(up, n)); + float3 vv = cross(n, uu); + return float3(p.x*uu + p.y*vv + p.z*n); + } + + float3 importance_sample_ggx(float2 rnd, float roughness, float3 normal) { + float a = roughness*roughness; + float a2 = a*a; + + float phi = 2 * PI * rnd.x; + float cos_phi = cos(phi); + float sin_phi = sin(phi); + float cos_theta = sqrt((1 - rnd.y) / ((a2 - 1) * rnd.y + 1)); + float sin_theta = sqrt(1 - cos_theta * cos_theta); + float3 h = float3(sin_theta * cos_phi, sin_theta * sin_phi, cos_theta); + + return orient_frame(normal, h); + } + + float3 random_point_on_oriented_disc(float3 normal, float radius, float2 rnd) { + float r = rnd.x; + float t = rnd.y * 2.0 * PI; + float rr = sqrt(r) * radius; + float x = rr * cos(t); + float y = rr * sin(t); + return orient_frame(normal, float3(x, y, 0)); + } + + float3 cos_weighted_random_hemisphere_direction_hammersley(float3 n, uint sample_index, uint max_samples, float scramble_rotation) { + float2 rnd = hammersley_sequence_2d(sample_index, max_samples); + float phi = 2 * PI * rnd.x; + float cos_theta = sqrt(1.f - rnd.y); + float sin_theta = sqrt(1.f - cos_theta * cos_theta); + + float cos_phi = cos(phi + scramble_rotation); + float sin_phi = sin(phi + scramble_rotation); + float3 rr = float3(cos_phi * sin_theta, sin_phi * sin_theta, cos_theta); + return orient_frame(n, rr); + } + + float3 cos_weighted_random_hemisphere_direction_fibonacci(float3 n, uint sample_index, uint max_samples, float scramble_rotation) { + // spherical fibonacci + float z = 1.0 - (sample_index + 0.5) / max_samples; + float phi = sample_index * (float)(PI * (3.0-sqrt(5.0))); + float cos_theta = sqrt(z); + float sin_theta = sqrt(1-z); + + float cos_phi = cos(phi + scramble_rotation); + float sin_phi = sin(phi + scramble_rotation); + float3 rr = float3(cos_phi * sin_theta, sin_phi * sin_theta, cos_theta); + return orient_frame(n, rr); + } + """ + } +} diff --git a/vmf_source/core/stingray_renderer/shader_libraries/shadow_map_common.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/shadow_map_common.shader_source new file mode 100644 index 0000000..2191a36 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/shadow_map_common.shader_source @@ -0,0 +1,131 @@ +sampler_states = { + shadow_map = { + states = { + srgb = "false" + + defined_RENDERER_D3D11 = { + comparison_func = "less" + filter = "comparison_min_mag_linear_mip_point" + address_u = "address_clamp" + address_v = "address_clamp" + address_w = "address_clamp" + } + defined_RENDERER_D3D12 = { + comparison_func = "less" + filter = "comparison_min_mag_linear_mip_point" + address_u = "address_clamp" + address_v = "address_clamp" + address_w = "address_clamp" + } + defined_RENDERER_GNM = { + comparison_func = "less" + filter = "min_mag_mip_linear" + address_u = "address_clamp" + address_v = "address_clamp" + address_w = "address_clamp" + } + defined_RENDERER_GL = { + comparison_func = "less" + filter = "min_mag_linear" + address_u = "address_clamp_to_edge" + address_v = "address_clamp_to_edge" + address_w = "address_clamp_to_edge" + } + } + } +} + +hlsl_shaders = { + shadow_bias = { + fp_code = { ref = "code" } + vp_code = { ref = "code" } + code = """ + CBUFFER_START(shadow_data) + float3 local_shadow_map_bias; + float3 sun_shadow_map_bias; + float3 ssm_shadow_map_bias; + CBUFFER_END + + float apply_shadow_depth_comparison_bias(float linear_depth, float shadow_comparison_depth, float3 shadow_map_bias) { + // Do not need a sun shadow map bias on d3d12 as we're using 32bit shadow maps, and they handle the shitz! + #if defined(RENDERER_D3D12) + return shadow_comparison_depth; + #else + const float min_bias = shadow_map_bias.x; + const float max_bias = shadow_map_bias.y; + const float blend_distance = shadow_map_bias.z; + return shadow_comparison_depth - lerp(min_bias, max_bias, saturate(linear_depth / blend_distance)); + #endif + } + """ + } + + shadow_map_filtering = { + vp_code = { ref = "code" } + fp_code = { ref = "code" } + code = """ + #if defined(RENDERER_D3D11) || defined(RENDERER_D3D12) || defined(RENDERER_GNM) + float shadow_intensity_2d(ComparisonSampler2D shadow_map, float2 shadow_map_size, float2 uv, float comparison_depth) { + float2 tscale = 1.f / shadow_map_size; + + #if defined(PCF_5X5) + uv *= shadow_map_size; + + float2 base_uv = floor(uv + 0.5); + float s = (uv.x + 0.5 - base_uv.x); + float t = (uv.y + 0.5 - base_uv.y); + base_uv = (base_uv - float2(0.5, 0.5)) * tscale; + + float a = 0; + + float2 w0 = float2(4 - 3 * s, 4 - 3 * t); + float2 w1 = float2(7, 7); + float2 w2 = float2(1 + 3 * s, 1 + 3 * t); + + float2 uv0 = float2((3 - 2 * s) / w0.x - 2, (3 - 2 * t) / w0.y - 2) * tscale; + float2 uv1 = float2((3 + s) / w1.x, (3 + t) / w1.y) * tscale; + float2 uv2 = float2(s / w2.x + 2, t / w2.y + 2) * tscale; + + a += w0.x * w0.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv0.x, uv0.y), comparison_depth); + a += w1.x * w0.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv1.x, uv0.y), comparison_depth); + a += w2.x * w0.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv2.x, uv0.y), comparison_depth); + + a += w0.x * w1.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv0.x, uv1.y), comparison_depth); + a += w1.x * w1.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv1.x, uv1.y), comparison_depth); + a += w2.x * w1.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv2.x, uv1.y), comparison_depth); + + a += w0.x * w2.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv0.x, uv2.y), comparison_depth); + a += w1.x * w2.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv1.x, uv2.y), comparison_depth); + a += w2.x * w2.y * TEX2DCMPLOD0(shadow_map, base_uv + float2(uv2.x, uv2.y), comparison_depth); + + return a / 144.f; + #else + float2 half_pixel = tscale * 0.5f; + + float a = + TEX2DCMPLOD0(shadow_map, uv + float2(-half_pixel.x, half_pixel.y), comparison_depth) + + TEX2DCMPLOD0(shadow_map, uv + float2( half_pixel.x, half_pixel.y), comparison_depth) + + TEX2DCMPLOD0(shadow_map, uv + float2(-half_pixel.x, -half_pixel.y), comparison_depth) + + TEX2DCMPLOD0(shadow_map, uv + float2( half_pixel.x, -half_pixel.y), comparison_depth); + + a *= 0.25; + + return a; + #endif + } + #else + lowp float shadow_intensity_2d(ComparisonSampler2D shadow_map, mediump vec2 shadow_map_size, highp vec2 uv, highp float comparison_depth) { + highp vec2 half_pixel = vec2(0.5, 0.5) / shadow_map_size; + + lowp float shadow = + TEX2DCMP(shadow_map, vec3(uv + vec2(-half_pixel.x, half_pixel.y), comparison_depth)) + + TEX2DCMP(shadow_map, vec3(uv + vec2( half_pixel.x, half_pixel.y), comparison_depth)) + + TEX2DCMP(shadow_map, vec3(uv + vec2( half_pixel.x, -half_pixel.y), comparison_depth)) + + TEX2DCMP(shadow_map, vec3(uv + vec2(-half_pixel.x, -half_pixel.y), comparison_depth)); + + return shadow * 0.25; + } + #endif + """ + } +} diff --git a/vmf_source/core/stingray_renderer/shader_libraries/terrain.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/terrain.shader_source new file mode 100644 index 0000000..621b4ff --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/terrain.shader_source @@ -0,0 +1,872 @@ +// in this context include refers to another shader file +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source" ] + + +render_states = { + wireframe = { + inherits = "opacity" + states = { + fill_mode = "fill_wireframe" + defined_D3D11 = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + defined_D3D12 = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + } + + terrain = { + inherits = "gbuffer_material" + states = { + fill_mode = "fill_solid" + cull_mode = "cull_ccw" + } + } + + filter = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "false" + } + } + + brush = { + inherits = "filter" + states = { + ndefined_SAMPLE_BASED = { + ndefined_SAMPLE_HEIGHT = { + ndefined_FLATTEN = { + blend_enable = "true" + defined_SUB = { + blend_op = "blend_op_rev_sub" + } + ndefined_SUB = { + blend_op = "blend_op_add" + } + dest_blend = "blend_one" + src_blend = "blend_one" + } + } + } + } + } + + marker = { + inherits = "opacity" + states = { + cull_mode = "cull_cw" + z_func = "greater_equal" + //blend_op = "blend_op_add" + //dest_blend = "blend_one" + //src_blend = "blend_one" + } + } + + depth_only = { + inherits = "default" + states = { + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + } + } + + shadow_caster = { + inherits = "depth_only" + states = { + defined_D3D11 = { + depth_bias = "0xff" + slope_scale_depth_bias = "1.0" + } + defined_D3D12 = { + depth_bias = "0xff" + slope_scale_depth_bias = "1.0" + } + defined_GL2 = { + offset_units = "1.0" + offset_factor = "2.0" + depth_bias_enable = "true" + } + } + } +} + +sampler_states = { + terrain_anisotropic = { + inherits = "wrap_anisotropic" + states = { + ndefined_GL2 = { + max_anisotropy = "0x4" + } + defined_GL2 = { + max_anisotropy = "16.0" + } + } + } + terrain_anisotropic_srgb = { + inherits = "wrap_anisotropic_srgb" + states = { + ndefined_GL2 = { + max_anisotropy = "0x4" + } + defined_GL2 = { + max_anisotropy = "16.0" + } + } + } +} + +hlsl_shaders = { + terrain_shared = { + code=""" + float2 morph(float2 uv, float2 wp, float t, float gsize, float psize) { + float3 grid_size = { gsize, gsize*0.5, 2.f/gsize }; + float2 frac_part = (frac(uv*grid_size.yy) * grid_size.zz) * psize.xx; + return wp - frac_part * t; + } + float3 normal_from_hmap(Sampler2D height_map, float2 uv, float2 texel_size, float texel_aspect) { + #if defined(D3D11) || defined(D3D12) || defined(GNM) + float4 h = { + TEX2D(height_map, uv + texel_size * float2(-1, 0)).r, + TEX2D(height_map, uv + texel_size * float2(1, 0)).r, + TEX2D(height_map, uv + texel_size * float2(0, -1)).r, + TEX2D(height_map, uv + texel_size * float2(0, 1)).r + }; + #elif defined(GL2) + float4 h = { + TEX2D(height_map, uv + texel_size * float2(-1, 0)).r, + TEX2D(height_map, uv + texel_size * float2(1, 0)).r, + TEX2D(height_map, uv + texel_size * float2(0, 1)).r, + TEX2D(height_map, uv + texel_size * float2(0, -1)).r + }; + #endif + h *= texel_aspect; + + float3 n = { + h[0] - h[1], + h[3] - h[2], + 2 + }; + + return normalize(n); + } + """ + } + + + terrain_depth = { + includes = [ "common", "terrain_shared" ] + + samplers = { + defined_D3D11 = { + clamp_linear = { sampler_states = "clamp_linear" } + } + defined_D3D12 = { + clamp_linear = { sampler_states = "clamp_linear" } + } + ndefined_D3D11 = { + ndefined_D3D12 = { + defined_GNM = { + clamp_linear = { sampler_states = "clamp_linear" } + } + ndefined_GNM = { + hmap = { sampler_states ="clamp_linear" } + } + } + } + } + + code=""" + struct VS_INPUT { + float2 position : POSITION; + #if defined(GL2) + float4 pos_scale : TEXCOORD0; + float4 tile_info : TEXCOORD1; + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float4x4 world; + float3 terrain_size; + float3 lod_camera_pos; + CBUFFER_END + + #if defined(D3D11) || defined(D3D12) || defined(GNM) + SamplerState clamp_linear; + Texture2D hmap; + Buffer idata; + #elif defined(GL2) + sampler2D hmap; + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input + #if defined(D3D11) || defined(D3D12) || defined(GNM) + , uint instance_id : SV_InstanceId + #endif + ) + { + PS_INPUT o; + + #if defined(GL2) + float4 pos_scale = input.pos_scale; + float4 tile_info = input.tile_info; + #elif defined(D3D11) || defined(D3D12) || defined(GNM) + float4 pos_scale = idata.Load(instance_id * 2 + 0); + float4 tile_info = idata.Load(instance_id * 2 + 1); + #endif + + float2 half_size = terrain_size.xy * 0.5; + float2 mip = input.position.xy; + float2 pos = (mip.xy - 0.5) * pos_scale.zw + pos_scale.xy; + + float3 p = mul(float4(pos, 0, 1), world); + float t = 1-saturate((distance(p, lod_camera_pos) - (tile_info.x - pos_scale.z*0.5)) / tile_info.y); + pos = morph(input.position.xy, pos, t, 32, pos_scale.z); + + float2 huv = ((pos.xy / half_size) * 0.5 + 0.5); + #if defined(D3D11) || defined(D3D12) || defined(GNM) + huv.y = 1-huv.y; + #endif + + #if defined(D3D11) || defined(D3D12) + float h = hmap.SampleLevel(clamp_linear, huv, 0).r * terrain_size.z; + #elif defined(GNM) + float h = hmap.SampleLOD(clamp_linear, huv, 0).r * terrain_size.z; + #else + float h = TEX2Dlod(hmap, float4(huv,0,0)).r * terrain_size.z; + #endif + + o.position = mul(float4(pos.xy, h, 1), world_view_proj); + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return float4(1,1,1,1); + } + """ + } + + terrain = { + includes = [ "common", "gbuffer_access", "terrain_shared" ] + + samplers = { + hmap = { sampler_states = "clamp_linear" } + blend_mask = { sampler_states = "clamp_linear" } + // global_diffuse_map = { sampler_states = "clamp_linear" } + + defined_DIFFUSE_MAP = { + diffuse_map = { sampler_states = "terrain_anisotropic_srgb" } + } + defined_MATERIAL_MAP = { + // TODO: this is inactivated because we currently have to many samplers + // material_map = { sampler_states = "terrain_anisotropic_srgb" } + } + defined_NORMAL_MAP = { + // TODO: this is inactivated because we currently have to many samplers + // normal_map = { sampler_states = "wrap_anisotropic" } + } + + defined_RED_DIFFUSE_MAP = { + first_blend_diffuse_map = { sampler_states = "terrain_anisotropic_srgb" } + } + defined_RED_NORMAL_MAP = { + first_blend_normal_map = { sampler_states = "terrain_anisotropic" } + } + defined_RED_MATERIAL_MAP = { + first_blend_material_map = { sampler_states = "terrain_anisotropic_srgb" } + } + + defined_GREEN_DIFFUSE_MAP = { + second_blend_diffuse_map = { sampler_states = "terrain_anisotropic_srgb" } + } + defined_GREEN_NORMAL_MAP = { + second_blend_normal_map = { sampler_states = "terrain_anisotropic" } + } + defined_GREEN_MATERIAL_MAP = { + second_blend_material_map = { sampler_states = "terrain_anisotropic_srgb" } + } + + defined_BLUE_DIFFUSE_MAP = { + third_blend_diffuse_map = { sampler_states = "terrain_anisotropic_srgb" } + } + defined_BLUE_NORMAL_MAP = { + third_blend_normal_map = { sampler_states = "terrain_anisotropic" } + } + defined_BLUE_MATERIAL_MAP = { + third_blend_material_map = { sampler_states = "terrain_anisotropic_srgb" } + } + + defined_ALPHA_DIFFUSE_MAP = { + fourth_blend_diffuse_map = { sampler_states = "terrain_anisotropic_srgb" } + } + defined_ALPHA_NORMAL_MAP = { + fourth_blend_normal_map = { sampler_states = "terrain_anisotropic" } + } + defined_ALPHA_MATERIAL_MAP = { + fourth_blend_material_map = { sampler_states = "terrain_anisotropic_srgb" } + } + } + + code=""" + struct VS_INPUT { + float2 position : POSITION; + #if defined(GL2) + float4 pos_scale : TEXCOORD0; + float4 tile_info : TEXCOORD1; + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + float2 depth : TEXCOORD1; + float4 color : COLOR; + }; + + #ifdef DIFFUSE_MAP + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Diffuse Map" type="resource" sort_tag="0_DIFFUSE_MAP"} + #endif + + #ifdef MATERIAL_MAP + // TODO: this is inactivated because we currently have to many samplers + //DECLARE_SAMPLER_2D(material_map); // exports={ name="Material Map" type="resource" sort_tag="0_MATERIAL_MAP" } + #endif + #ifdef NORMAL_MAP + // TODO: this is inactivated because we currently have to many samplers + //DECLARE_SAMPLER_2D(normal_map); // exports = { name = "Normal Map" type = "resource" sort_tag = "0_NORMAL_MAP" } + #endif + + #ifdef RED_DIFFUSE_MAP + DECLARE_SAMPLER_2D(first_blend_diffuse_map); // exports={ name="Red Blend - Diffuse Map" type="resource" sort_tag="1_DIFFUSE_MAP"} + #endif + #ifdef RED_NORMAL_MAP + DECLARE_SAMPLER_2D(first_blend_normal_map); // exports={ name="Red Blend - Normal Map" type="resource" sort_tag="1_NORMAL_MAP"} + #endif + #ifdef RED_MATERIAL_MAP + DECLARE_SAMPLER_2D(first_blend_material_map); // exports={ name="Red Blend - Material Map" type="resource" sort_tag="1_MATERIAL_MAP"} + #endif + + #ifdef GREEN_DIFFUSE_MAP + DECLARE_SAMPLER_2D(second_blend_diffuse_map); // exports={ name="Green Blend - Diffuse Map" type="resource" sort_tag="2_DIFFUSE_MAP"} + #endif + #ifdef GREEN_NORMAL_MAP + DECLARE_SAMPLER_2D(second_blend_normal_map); // exports={ name="Green Blend - Normal Map" type="resource" sort_tag="2_NORMAL_MAP"} + #endif + #ifdef GREEN_MATERIAL_MAP + DECLARE_SAMPLER_2D(second_blend_material_map); // exports={ name="Green Blend - Material Map" type="resource" sort_tag="2_MATERIAL_MAP"} + #endif + + #ifdef BLUE_DIFFUSE_MAP + DECLARE_SAMPLER_2D(third_blend_diffuse_map); // exports={ name="Blue Blend - Diffuse Map" type="resource" sort_tag="3_DIFFUSE_MAP"} + #endif + #ifdef BLUE_NORMAL_MAP + DECLARE_SAMPLER_2D(third_blend_normal_map); // exports={ name="Blue Blend - Normal Map" type="resource" sort_tag="3_NORMAL_MAP"} + #endif + #ifdef BLUE_MATERIAL_MAP + DECLARE_SAMPLER_2D(third_blend_material_map); // exports={ name="Blue Blend - Material Map" type="resource" sort_tag="3_MATERIAL_MAP"} + #endif + + #ifdef ALPHA_DIFFUSE_MAP + DECLARE_SAMPLER_2D(fourth_blend_diffuse_map); // exports={ name="Alpha Blend - Diffuse Map" type="resource" sort_tag="4_DIFFUSE_MAP"} + #endif + #ifdef ALPHA_NORMAL_MAP + DECLARE_SAMPLER_2D(fourth_blend_normal_map); // exports={ name="Alpha Blend - Normal Map" type="resource" sort_tag="4_NORMAL_MAP"} + #endif + #ifdef ALPHA_MATERIAL_MAP + DECLARE_SAMPLER_2D(fourth_blend_material_map); // exports={ name="Alpha Blend - Material Map" type="resource" sort_tag="4_MATERIAL_MAP"} + #endif + + #if defined(DIFFUSE_MAP)// || defined(MATERIAL_MAP) || defined(NORMAL_MAP) // TODO: this is inactivated because we currently have to many samplers + #define BASE_MAP + #endif + + #if defined(RED_DIFFUSE_MAP) || defined(RED_NORMAL_MAP) || defined(RED_MATERIAL_MAP) + #define RED_BLEND + #endif + #if defined(GREEN_DIFFUSE_MAP) || defined(GREEN_NORMAL_MAP) || defined(GREEN_MATERIAL_MAP) + #define GREEN_BLEND + #endif + #if defined(BLUE_DIFFUSE_MAP) || defined(BLUE_NORMAL_MAP) || defined(BLUE_MATERIAL_MAP) + #define BLUE_BLEND + #endif + #if defined(ALPHA_DIFFUSE_MAP) || defined(ALPHA_NORMAL_MAP) || defined(ALPHA_MATERIAL_MAP) + #define ALPHA_BLEND + #endif + + CBUFFER_START(c0) + float4x4 world_view_proj; + float4x4 world; + float3 terrain_size; + float3 lod_camera_pos; + #ifdef BASE_MAP + float2 base_map_scale; // exports={ name="Base Map - UV Scale" type="vector2" value=[1 1] min=[0 0] max=[1024 1024] step=[1 1] } + #endif + #ifndef MATERIAL_MAP + float glossiness; // exports={ name="Glossiness Amount" type="scalar" value=0.5 min=0.0 max=1.0 step=0.001 sort_tag="0_MATERIAL_MAP_0" } + float specular; // exports={ name="Specular Mask" type="scalar" value=0.0 min=0.0 max=1.0 step=0.001 sort_tag="0_MATERIAL_MAP_1" } + #endif + + #ifdef RED_BLEND + float2 red_maps_scale; // exports={ name="Red Blend - UV Scale" type="vector2" value=[1 1] min=[0 0] max=[1024 1024] step=[1 1] sort_tag="1_BLEND_SMOOTHING" } + float red_masked_blend_smoothing; // exports={ name="Red Blend - Smoothing" type="scalar" value=0.2 min=0.001 max=1.0 step=0.001 sort_tag="1_BLEND_SMOOTHING" } + #ifndef RED_MATERIAL_MAP + float red_glossiness; // exports={ name="Red Blend - Glossiness Amount" type="scalar" value=0.5 min=0.0 max=1.0 step=0.001 sort_tag="1_MATERIAL_MAP_0" } + float red_specular; // exports={ name="Red Blend - Specular Mask" type="scalar" value=0.0 min=0.0 max=1.0 step=0.001 sort_tag="1_MATERIAL_MAP_1" } + #endif + #endif + #ifdef GREEN_BLEND + float2 green_maps_scale; // exports={ name="Green Blend - UV Scale" type="vector2" value=[1 1] min=[0 0] max=[1024 1024] step=[1 1] sort_tag="2_BLEND_SMOOTHING"} + float green_masked_blend_smoothing; // exports={ name="Green Blend - Smoothing" type="scalar" value=0.2 min=0.001 max=1.0 step=0.001 sort_tag="2_BLEND_SMOOTHING" } + #ifndef GREEN_MATERIAL_MAP + float green_glossiness; // exports={ name="Green Blend - Glossiness Amount" type="scalar" value=0.5 min=0.0 max=1.0 step=0.001 sort_tag="2_MATERIAL_MAP_0" } + float green_specular; // exports={ name="Green Blend - Specular Mask" type="scalar" value=0.0 min=0.0 max=1.0 step=0.001 sort_tag="2_MATERIAL_MAP_1" } + #endif + #endif + #ifdef BLUE_BLEND + float2 blue_maps_scale; // exports={ name="Blue Blend - UV Scale" type="vector2" value=[1 1] min=[0 0] max=[1024 1024] step=[1 1] sort_tag="3_BLEND_SMOOTHING" } + float blue_masked_blend_smoothing; // exports={ name="Blue Blend - Smoothing" type="scalar" value=0.2 min=0.001 max=1.0 step=0.001 sort_tag="3_BLEND_SMOOTHING" } + #ifndef BLUE_MATERIAL_MAP + float blue_glossiness; // exports={ name="Blue Blend - Glossiness Amount" type="scalar" value=0.5 min=0.0 max=1.0 step=0.001 sort_tag="3_MATERIAL_MAP_0" } + float blue_specular; // exports={ name="Blue Blend - Specular Mask" type="scalar" value=0.0 min=0.0 max=1.0 step=0.001 sort_tag="3_MATERIAL_MAP_1" } + #endif + #endif + #ifdef ALPHA_BLEND + float2 alpha_maps_scale; // exports={ name="Alpha Blend - UV Scale" type="vector2" value=[1 1] min=[0 0] max=[1024 1024] step=[1 1] sort_tag="4_BLEND_SMOOTHING" } + float alpha_masked_blend_smoothing; // exports={ name="Alpha Blend - Smoothing" type="scalar" value=0.2 min=0.001 max=1.0 step=0.001 sort_tag="4_BLEND_SMOOTHING" } + #ifndef ALPHA_MATERIAL_MAP + float alpha_glossiness; // exports={ name="Alpha Blend - Glossiness Amount" type="scalar" value=0.5 min=0.0 max=1.0 step=0.001 sort_tag="4_MATERIAL_MAP_0" } + float alpha_specular; // exports={ name="Alpha Blend - Specular Mask" type="scalar" value=0.0 min=0.0 max=1.0 step=0.001 sort_tag="4_MATERIAL_MAP_1" } + #endif + #endif + + #ifdef DETAIL_FADE + float2 fade_out_detail_settings; // exports={ name="Fade out details [start, fade]" type="vector2" value=[30 20] min=[5 1] max=[500 250] step=[0.5 0.5] sort_tag="DETAIL_FADE" } + #endif + + #if defined(GL2) + float2 inv_hmap_size; + #endif + + float4 dev_wireframe_color; + CBUFFER_END + + #if defined(D3D11) || defined(D3D12) || defined(GNM) + Buffer idata; + #endif + DECLARE_SAMPLER_2D(hmap); + DECLARE_SAMPLER_2D(blend_mask); + // DECLARE_SAMPLER_CUBE(global_diffuse_map); + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input, uint instance_id : SV_InstanceId) { + PS_INPUT o; + + #if defined(GL2) + float4 pos_scale = input.pos_scale; + float4 tile_info = input.tile_info; + //float4 pos_scale = float4(0,0,32, 32); + //float4 tile_info = float4(0, 32, 32, 0); + #elif defined(D3D11) || defined(D3D12) || defined(GNM) + float4 pos_scale = idata.Load(instance_id * 2 + 0); + float4 tile_info = idata.Load(instance_id * 2 + 1); + #endif + + float2 half_size = terrain_size.xy * 0.5; + float2 mip = input.position.xy; + float2 pos = (mip.xy - 0.5) * pos_scale.zw + pos_scale.xy; + + float3 wp = mul(float4(pos, 0, 1), world); + float t = 1-saturate((distance(wp, camera_pos) - (tile_info.x - pos_scale.z*0.5)) / tile_info.y); + + float2 huv = ((pos.xy / half_size) * 0.5 + 0.5); + #if defined(D3D11) || defined(D3D12) || defined(GNM) + huv.y = 1-huv.y; + #endif + + float h = TEX2DLOD(hmap, huv, 0).r * terrain_size.z; + + float4 object_pos = float4(pos.xy, h, 1); + float4 p = mul(object_pos, world_view_proj); + o.position = p; + o.uv = huv; + + const float3 lod_cols[8] = { + float3(1,0,0), + float3(0,1,0), + float3(0,0,1), + float3(1,1,0), + float3(0,1,1), + float3(1,1,0.5), + float3(1,0,0.5), + float3(0,1,0.5) + }; + + o.color = float4(lod_cols[(uint)tile_info.w], t); + o.depth = float2(p.z + camera_near_far.x, 0); + + return o; + } + + void terrain_layer(inout half4 tnormal, inout float3 diffuse_color, inout float3 gsm, half opacity, half smoothing, bool threshold_blending, half4 layer_normal, float3 layer_diffuse_color, float3 layer_gsm, half fadeout) { + //opacity = threshold_blending ? smoothstep(saturate(gsm.b - smoothing), gsm.b, opacity) : opacity; + opacity = threshold_blending ? 1-saturate((gsm.b - opacity) / smoothing) : opacity; + #if defined(DETAIL_FADE) + opacity *= fadeout; + #endif + diffuse_color = lerp(diffuse_color, layer_diffuse_color, opacity); + tnormal = lerp(tnormal, layer_normal, opacity); + gsm = lerp(gsm, layer_gsm, opacity); + } + + #if defined(DRAW_WIREFRAME) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return dev_wireframe_color; + } + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + GBUFFER_OUT ps_main(PS_INPUT input) { + GBUFFER_OUT o; + + #if defined(D3D11) || defined(D3D12) || defined(GNM) + float2 res; + TEXTURE_NAME(hmap).GetDimensions(res.x, res.y); + float2 inv_hmap_size = 1.0/res; + #endif + float3 n = normal_from_hmap(hmap, input.uv, inv_hmap_size, terrain_size.z); + + #ifdef DIFFUSE_MAP + float3 base_color = TEX2D(diffuse_map, input.uv * base_map_scale); + #else + float3 base_color = float3(0.5,0.5,0.5); + #endif + + #ifdef MATERIAL_MAP + // TODO: this is inactivated because we currently have to many samplers + //float3 gsm = TEX2D(material_map, input.uv * base_map_scale).rgb; + float3 gsm = float3(0.5f, 0.0f, 0.f); + #else + float3 gsm = float3(glossiness, specular, 0.f); + #endif + + #if defined(DETAIL_FADE) + float4 mask = float4(0,0,0,0); + half fadeout = 1-saturate((input.depth - fade_out_detail_settings.x) / fade_out_detail_settings.y); + if (fadeout > 0) + mask = TEX2D(blend_mask, input.uv); + #else + float4 mask = TEX2D(blend_mask, input.uv); + half fadeout = 1; + #endif + + #ifdef NORMAL_MAP + // TODO: this is inactivated because we currently have to many samplers + //half4 tnormal = TEX2D(normal_map, input.uv * base_map_scale); + half4 tnormal = float4(0.5, 0.5, 0.5, 0.5); + + //tnormal = normalize(tnormal); + #else + half4 tnormal = float4(0.5, 0.5, 0.5, 0.5); + #endif + + float3 layer_gsm; + half4 layer_base_color; + half4 layer_normal; + half opacity; + #if defined(RED_BLEND) + opacity = mask.r; + red_maps_scale *= opacity > 0 ? 1 : 0; + if (opacity > 0.0) { + #if defined(RED_DIFFUSE_MAP) + layer_base_color = TEX2D(first_blend_diffuse_map, input.uv * red_maps_scale); + #else + layer_base_color.rgb = base_color; + layer_base_color.a = 0; + #endif + + #if defined(RED_MATERIAL_MAP) + layer_gsm = TEX2D(first_blend_material_map, input.uv * red_maps_scale).rgb; + #if !defined(RED_WRITE_HEIGHT) + layer_gsm.b = gsm.b; + #endif + #else + layer_gsm = float3(red_glossiness, red_specular, gsm.b); + #if defined(RED_WRITE_HEIGHT) + gsm.b = layer_base_color.a; + #endif + #endif + + #if defined(RED_NORMAL_MAP) + layer_normal = TEX2D(first_blend_normal_map, input.uv * red_maps_scale); + #else + layer_normal = float4(0.5, 0.5, 0.5, 0.5); + #endif + + #if defined(RED_THRESHOLD_BLENDING) + const bool red_threshold_blending = true; + #else + const bool red_threshold_blending = false; + #endif + terrain_layer(tnormal, base_color, gsm, opacity, red_masked_blend_smoothing, red_threshold_blending, layer_normal, layer_base_color.rgb, layer_gsm, fadeout); + } + #endif + + #if defined(GREEN_BLEND) + opacity = mask.g; + green_maps_scale *= opacity > 0 ? 1 : 0; + if (opacity > 0.0) { + #if defined(GREEN_DIFFUSE_MAP) + layer_base_color = TEX2D(second_blend_diffuse_map, input.uv * green_maps_scale); + #else + layer_base_color.rgb = base_color; + layer_base_color.a = 0; + #endif + + #if defined(GREEN_MATERIAL_MAP) + layer_gsm = TEX2D(second_blend_material_map, input.uv * green_maps_scale).rgb; + #if !defined(GREEN_WRITE_HEIGHT) + layer_gsm.b = gsm.b; + #endif + #else + layer_gsm = float3(green_glossiness, green_specular, gsm.b); + #if defined(RED_WRITE_HEIGHT) + layer_gsm.b = layer_base_color.a; + #endif + #endif + + #if defined(GREEN_NORMAL_MAP) + layer_normal = TEX2D(second_blend_normal_map, input.uv * green_maps_scale); + #else + layer_normal = float4(0.5, 0.5, 0.5, 0.5); + #endif + + #if defined(GREEN_THRESHOLD_BLENDING) + const bool green_threshold_blending = true; + #else + const bool green_threshold_blending = false; + #endif + terrain_layer(tnormal, base_color, gsm, opacity, green_masked_blend_smoothing, green_threshold_blending, layer_normal, layer_base_color.rgb, layer_gsm, fadeout); + } + #endif + + #if defined(BLUE_BLEND) + opacity = mask.b; + blue_maps_scale *= opacity > 0 ? 1 : 0; + if (opacity > 0.0) { + #if defined(BLUE_DIFFUSE_MAP) + layer_base_color = TEX2D(third_blend_diffuse_map, input.uv * blue_maps_scale); + #else + layer_base_color.rgb = base_color; + layer_base_color.a = 0; + #endif + + #if defined(BLUE_MATERIAL_MAP) + layer_gsm = TEX2D(third_blend_material_map, input.uv * blue_maps_scale).rgb; + #if !defined(BLUE_WRITE_HEIGHT) + layer_gsm.b = gsm.b; + #endif + #else + layer_gsm = float3(blue_glossiness, blue_specular, gsm.b); + #if defined(RED_WRITE_HEIGHT) + layer_gsm.b = layer_base_color.a; + #endif + #endif + + #if defined(BLUE_NORMAL_MAP) + layer_normal = TEX2D(third_blend_normal_map, input.uv * blue_maps_scale); + #else + layer_normal = float4(0.5, 0.5, 0.5, 0.5); + #endif + + #if defined(BLUE_THRESHOLD_BLENDING) + const bool blue_threshold_blending = true; + #else + const bool blue_threshold_blending = false; + #endif + terrain_layer(tnormal, base_color, gsm, opacity, blue_masked_blend_smoothing, blue_threshold_blending, layer_normal, layer_base_color.rgb, layer_gsm, fadeout); + } + #endif + + #if defined(ALPHA_BLEND) + opacity = mask.a; + alpha_maps_scale *= opacity > 0 ? 1 : 0; + if (opacity > 0.0) { + #if defined(ALPHA_DIFFUSE_MAP) + layer_base_color = TEX2D(fourth_blend_diffuse_map, input.uv * alpha_maps_scale); + #else + layer_base_color.rgb = base_color; + layer_base_color.a = 0; + #endif + + #if defined(ALPHA_MATERIAL_MAP) + layer_gsm = TEX2D(fourth_blend_material_map, input.uv * alpha_maps_scale).rgb; + #if !defined(ALPHA_WRITE_HEIGHT) + layer_gsm.b = gsm.b; + #endif + #else + layer_gsm = float3(alpha_glossiness, alpha_specular, gsm.b); + #if defined(RED_WRITE_HEIGHT) + layer_gsm.b = layer_base_color.a; + #endif + #endif + + #if defined(ALPHA_NORMAL_MAP) + layer_normal = TEX2D(fourth_blend_normal_map, input.uv * alpha_maps_scale); + #else + layer_normal = float4(0.5, 0.5, 0.5, 0.5); + #endif + + #if defined(ALPHA_THRESHOLD_BLENDING) + const bool alpha_threshold_blending = true; + #else + const bool alpha_threshold_blending = false; + #endif + terrain_layer(tnormal, base_color, gsm, opacity, alpha_masked_blend_smoothing, alpha_threshold_blending, layer_normal, layer_base_color.rgb, layer_gsm, fadeout); + } + #endif + + tnormal.xyz = normalize(decode_normal_map(tnormal)); + float3 world_normal = normalize(float3(n.xy + tnormal.xy, n.z * tnormal.z)); + + BASE_COLOR(o) = gbuffer_encode_base_color(base_color); + NORMAL(o) = gbuffer_encode_normal(world_normal); + METALLIC(o) = gbuffer_encode_metallic_mask(gsm.g); + ROUGHNESS(o) = gbuffer_encode_roughness(1.0 - gsm.r); // Note: roughness ~ 1 - glossiness + + // Velocity vector -- always static, motion vectors calculated as post processing step + VELOCITY(o) = encode_velocity(0.0); + + DENSITY(o) = 1.0; + // AMBIENT_DIFFUSE_LIGHT(o) = gbuffer_encode_ambient_diffuse_light(rgbm_decode(TEXCUBELOD(global_diffuse_map, world_normal, 0))); + AMBIENT_OCCLUSION(o) = gbuffer_encode_ambient_occlusion(1.f); + + return o; + } + #endif + """ + } +} + +shaders = { + terrain = { + editor_advanced_mode = false + + editor_options = [ + { + name="Base Texture Layers" + options = [ + { name="Diffuse Map" define="DIFFUSE_MAP" } + // TODO: these are inactivated because we currently have to many samplers + // { name="Material Map" define="MATERIAL_MAP" } + // { name="Normal Map" define="NORMAL_MAP" } + ] + } + + { + name="Deferred Decals" + options = [ + { name="Decal Group 1" define="DEFERRED_DECALS_GROUP_1" } + { name="Decal Group 2" define="DEFERRED_DECALS_GROUP_2" } + { name="Decal Group 3" define="DEFERRED_DECALS_GROUP_3" } + ] + } + + + { + name="Settings" + options = [ + { name="Fade-out Mask Textures" define="DETAIL_FADE" } + ] + } + + { + name="Red Mask Blend - Texture Layers" + options = [ + { name="Threshold Blending" define="RED_THRESHOLD_BLENDING" } + { name="Writes Height for Threshold Blending" define="RED_WRITE_HEIGHT" } + { name="Diffuse Map" define="RED_DIFFUSE_MAP" } + { name="Material Map" define="RED_MATERIAL_MAP" } + { name="Normal Map" define="RED_NORMAL_MAP" } + ] + } + + { + name="Green Mask Blend - Texture Layers" + options = [ + { name="Threshold Blending" define="GREEN_THRESHOLD_BLENDING" } + { name="Writes Height for Threshold Blending" define="GREEN_WRITE_HEIGHT" } + { name="Diffuse Map" define="GREEN_DIFFUSE_MAP" } + { name="Material Map" define="GREEN_MATERIAL_MAP" } + { name="Normal Map" define="GREEN_NORMAL_MAP" } + ] + } + + { + name="Blue Mask Blend - Texture Layers" + options = [ + { name="Threshold Blending" define="BLUE_THRESHOLD_BLENDING" } + { name="Writes Height for Threshold Blending" define="BLUE_WRITE_HEIGHT" } + { name="Diffuse Map" define="BLUE_DIFFUSE_MAP" } + { name="Material Map" define="BLUE_MATERIAL_MAP" } + { name="Normal Map" define="BLUE_NORMAL_MAP" } + ] + } + + { + name="Alpha Mask Blend - Texture Layers" + options = [ + { name="Threshold Blending" define="ALPHA_THRESHOLD_BLENDING" } + { name="Writes Height for Threshold Blending" define="ALPHA_WRITE_HEIGHT" } + { name="Diffuse Map" define="ALPHA_DIFFUSE_MAP" } + { name="Material Map" define="ALPHA_MATERIAL_MAP" } + { name="Normal Map" define="ALPHA_NORMAL_MAP" } + ] + } + ] + + contexts = { + shadow_caster = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="terrain_depth" render_states="shadow_caster" } + ] + } + + default = { + passes = [ + { layer="gbuffer" hlsl_shader="terrain" render_states="terrain" } + { layer="wireframe" hlsl_shader="terrain" defines="DRAW_WIREFRAME" render_states="wireframe" branch_key="dev_wireframe" } + ] + } + } + + compile = { + shadow_caster = [ + { defines="" platforms="D3D11 D3D12 GL2 GNM" } + ] + + default = [ + { defines="" platforms="D3D11 D3D12 GL2 GNM" } + ] + } + } +} + +static_compile = [ + { shader="terrain" } +] \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/terrain_utilities.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/terrain_utilities.shader_source new file mode 100644 index 0000000..90d8c09 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/terrain_utilities.shader_source @@ -0,0 +1,522 @@ +// in this context include refers to another shader file +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source" ] + +render_states = { + filter = { + inherits = "default" + states = { + z_write_enable = "false" + z_enable = "false" + } + } + + brush = { + inherits = "filter" + states = { + ndefined_SAMPLE_BASED = { + ndefined_SAMPLE_HEIGHT = { + ndefined_FLATTEN = { + blend_enable = "true" + defined_SUB = { + blend_op = "blend_op_rev_sub" + } + ndefined_SUB = { + blend_op = "blend_op_add" + } + dest_blend = "blend_one" + src_blend = "blend_one" + } + } + } + } + } + + marker = { + inherits = "opacity" + states = { + cull_mode = "cull_cw" + z_func = "greater_equal" + } + } + + depth_only = { + inherits = "default" + states = { + write_mask0 = "0x0" + write_mask1 = "0x0" + write_mask2 = "0x0" + write_mask3 = "0x0" + } + } + +} + +hlsl_shaders = { + terrain_shared = { + code=""" + float2 morph(float2 uv, float2 wp, float t, float gsize, float psize) { + float3 grid_size = { gsize, gsize*0.5, 2.f/gsize }; + float2 frac_part = (frac(uv*grid_size.yy) * grid_size.zz) * psize.xx; + return wp - frac_part * t; + } + + #if defined(D3D11) || defined(D3D12) || defined(GNM) + + float3 normal_from_hmap(Texture2D height_map, SamplerState s, float2 uv, float2 texel_size, float texel_aspect) { + float4 h = { + height_map.Sample(s, uv + texel_size * float2(-1, 0)).r, + height_map.Sample(s, uv + texel_size * float2(1, 0)).r, + height_map.Sample(s, uv + texel_size * float2(0, -1)).r, + height_map.Sample(s, uv + texel_size * float2(0, 1)).r + }; + + #elif defined(GL2) + + float3 normal_from_hmap(sampler2D height_map, float2 uv, float2 texel_size, float texel_aspect) { + float4 h = { + tex2D(height_map, uv + texel_size * float2(-1, 0)).r, + tex2D(height_map, uv + texel_size * float2(1, 0)).r, + tex2D(height_map, uv + texel_size * float2(0, 1)).r, + tex2D(height_map, uv + texel_size * float2(0, -1)).r + }; + + #endif + h *= texel_aspect; + + float3 n = { + h[0] - h[1], + h[3] - h[2], + 2 + }; + + return normalize(n); + } + """ + } + terrain_editor_brush = { + includes = [ "common", "gbuffer_access" ] + + samplers = { + defined_SAMPLE_BASED = { + input_texture0 = { sampler_states = "clamp_linear" } + } + } + + code=""" + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if defined(SAMPLE_HEIGHT) + float world_height : TEXCOORD0; + float layer_value : TEXCOORD1; + #else + float2 uv : TEXCOORD0; + #endif + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float radius; + float strength; + float falloff; + float mask_idx; + float replay_layer_value; + float3 replay_brush_pos; + float3 mouse_pos; + float4x4 inverse_terrain_wtm; + float2 terrain_size; + float3 camera_ray_origin; + float3 camera_ray_direction; + #ifdef SAMPLE_BASED + float2 inv_input_texture0_size; + #endif + float part_of_replay_stroke; + CBUFFER_END + + Texture2D linear_depth; + #if defined(SAMPLE_HEIGHT) + Texture2D output; + #elif defined(SAMPLE_BASED) + sampler2D input_texture0; + #endif + + #ifndef SAMPLE_HEIGHT + Texture2D height_sample; + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + #if defined(SAMPLE_HEIGHT) + // Reconstruct world height from pixel behind back buffer + float3 smp = float3(mouse_pos.x/back_buffer_size.x, mouse_pos.z/back_buffer_size.y, 0) * 2 - 1; + float4 w = encode_world_pos(float4(smp,1)); + float d = gbuffer_decode_depth(linear_depth.Load(int3(mouse_pos.x, back_buffer_size.y-mouse_pos.z, 0))); + //float d = gbuffer_decode_depth(depth.Load(int3(mouse_pos.x, mouse_pos.z, 0))); + float3 wp = decode_world_pos(w, d); + + o.world_height = wp.z; + + // Sample active layer value + float3 terrain_pos = mul(float4(wp, 1), inverse_terrain_wtm); + float2 half_size = terrain_size * 0.5; + float2 huv = ((terrain_pos.xy / half_size) * 0.5 + 0.5); + huv.y = 1-huv.y; + + float2 layer_res; + output.GetDimensions(layer_res.x, layer_res.y); + + o.layer_value = output.Load(int3(layer_res * huv.xy,0))[(int)mask_idx]; + #else + // Find camera ray/terrain intersection, terrain is represented as a plane with normal [0,0,1] and d=last sampled world height + float3 wp; + if(part_of_replay_stroke == 0) + { + float world_height = height_sample.Load(int3(0,0,0)).r; + float t = (world_height - camera_ray_origin.z)/camera_ray_direction.z; + wp = camera_ray_origin + camera_ray_direction * t; + } + else + { + wp = replay_brush_pos; + } + + o.uv = input.position.xy; + + float3 terrain_pos = mul(float4(wp, 1), inverse_terrain_wtm); + terrain_pos.xy = clamp(2*(terrain_pos.xy / terrain_size), -1, 1); + + float2 scale = (2*radius.xx) / terrain_size; + #if defined(SAMPLE) + // Increase brush size with filter kernel size + scale += inv_input_texture0_size*2; + #endif + input.position.xy *= scale; + input.position.xy += terrain_pos.xy; + #endif + + o.position = mul(input.position, world_view_proj); + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + const float4 mask_lookup[4] = { + float4(1,0,0,0), + float4(0,1,0,0), + float4(0,0,1,0), + float4(0,0,0,1) + }; + + #ifdef SAMPLE_HEIGHT + return float4(input.world_height, input.layer_value, 0, 0); + #else + float phase = saturate(length(input.uv)) * (3.1415926*0.5); + float a = cos(phase); + + #if defined(FLATTEN) + float2 layer_uv = input.position.xy * inv_input_texture0_size; + float4 center = tex2D(input_texture0, layer_uv); + float4 v = center; + + float height; + if(part_of_replay_stroke == 0) + { + height = height_sample.Load(int3(0,0,0)).g; + } + else + { + height = replay_layer_value; + } + + v = (1-mask_lookup[(int)mask_idx]) * v + mask_lookup[(int)mask_idx] * lerp(center[(int)mask_idx], height, a * strength); + return v; + #elif defined(SAMPLE) + float2 layer_uv = input.position.xy * inv_input_texture0_size; + float4 c = tex2D(input_texture0, layer_uv); + return c; + #else + a = pow(a, falloff); + + #if defined(BOX_FILTER) + float d = 0.5; + + float2 layer_uv = input.position.xy * inv_input_texture0_size; + float4 center = tex2D(input_texture0, layer_uv); + float4 c = + tex2D(input_texture0, layer_uv + half2(-d, -d) * inv_input_texture0_size) + + tex2D(input_texture0, layer_uv + half2( d, -d) * inv_input_texture0_size) + + tex2D(input_texture0, layer_uv + half2(-d, d) * inv_input_texture0_size) + + tex2D(input_texture0, layer_uv + half2( d, d) * inv_input_texture0_size); + + c *= 0.25; + float4 v = center; + float4 mask = mask_lookup[(int)mask_idx]; + v = (1-mask) * v + mask * lerp(center[(int)mask_idx], c[(int)mask_idx], saturate(a * strength * 10.0)); + return v; + #else + #if defined(INVERT) + const float4 invert_mask_lookup[4] = { + float4(0,1,1,1), + float4(0,0,1,1), + float4(0,0,0,1), + float4(0,0,0,0) + }; + float4 v = invert_mask_lookup[(int)mask_idx] * strength * a; + #else + float4 v = mask_lookup[(int)mask_idx] * strength * a; + #endif + return v; + #endif + #endif + #endif + } + """ + } + + terrain_editor_brush_marker = { + includes = [ "common", "gbuffer_access" ] + + code=""" + struct VS_INPUT { + float4 position : POSITION; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float4 w : TEXCOORD0; + float3 center : TEXCOORD1; + }; + + Texture2D linear_depth; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float4x4 world; + float radius; + float falloff; + float strength; + float3 brush_color; + float brush_type; + float3 mouse_pos; + float3 camera_ray_origin; + float3 camera_ray_direction; + CBUFFER_END + + Texture2D height_sample; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + + // Find camera ray/terrain intersection, terrain is represented as a plane with normal [0,0,1] and d=last sampled world height + float world_height = height_sample.Load(int3(0,0,0)).r; + float t = (world_height - camera_ray_origin.z)/camera_ray_direction.z; + float3 wp = camera_ray_origin + camera_ray_direction * t; + + input.position.xy *= radius; + input.position.z *= 1000; + input.position.xyz += wp; + o.position = mul(input.position, world_view_proj); + o.center = wp; + o.w = encode_world_pos(o.position); + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + float d = gbuffer_decode_depth(linear_depth.Load(int3(input.position.xy,0))); + float3 wp = decode_world_pos(input.w, d); + wp.z = 0; + float l = length(float3(wp.xy,0)-float3(input.center.xy, 0)); + + float stroke_width = 0.02 * radius; + float falloff_marker = (1-(0.5 * falloff)) * radius; + float4 c; + + if (brush_type == 0) { + float3 mark = l < stroke_width ? float3(1,1,1) : ((l > falloff_marker-stroke_width && l < falloff_marker) || (l > radius-stroke_width && l < radius)) ? brush_color : float3(0,0,0); + c = float4(mark, dot(mark, float3(1,1,1)) == 0 ? 0 : lerp(0.2, 0.6, strength)); + } else { + float fade = (l > (falloff_marker-stroke_width) && l < radius) ? (1-saturate((l - falloff_marker) / (radius - falloff_marker))) : 1; + float3 mark = l < stroke_width ? float3(1,1,1) : (l < radius) ? brush_color : float3(0,0,0); + c = float4(mark, dot(mark, float3(1,1,1)) == 0 ? 0 : fade * lerp(0.2, 0.6, strength)); + } + return c; + + } + """ + } + + terrain_decoration = { + includes = [ "common", "gbuffer_access", "terrain_shared" ] + samplers = { + defined_D3D11 = { + clamp_linear = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_linear" } + } + defined_D3D12 = { + clamp_linear = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_linear" } + } + ndefined_D3D11 = { + ndefined_D3D12 = { + input_texture0 = { sampler_states = "clamp_linear" } + input_texture1 = { sampler_states = "clamp_linear" } + } + } + } + + code=""" + #if defined(D3D11) || defined(D3D12) + SamplerState clamp_linear; + Texture2D input_texture0; + sampler2D input_texture1; + #elif defined(GL2) + sampler2D input_texture0; + sampler2D input_texture1; + #endif + + struct VS_INPUT { + float4 position : POSITION; + float2 uv : TEXCOORD0; + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + float2 uv : TEXCOORD0; + }; + + CBUFFER_START(c0) + float4x4 world_view_proj; + float2 scale; + float2 offset; + float height; + #if defined(GL2) + float2 inv_input_texture0_size; + #endif + CBUFFER_END + + struct DECORATION_OUT { + half4 buffer0 : COLOR0; + half4 buffer1 : COLOR1; + }; + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + o.position = mul(input.position, world_view_proj); + float2 uv = input.uv; + o.uv = uv * scale + offset; + #if defined(D3D11) || defined(D3D12) + o.uv.y = 1-o.uv.y; + #endif + + return o; + } + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + DECORATION_OUT ps_main(PS_INPUT input) { + DECORATION_OUT o; + #if defined(D3D11) || defined(D3D12) + float2 res; + input_texture0.GetDimensions(res.x, res.y); + float3 hmap_normal = normal_from_hmap(input_texture0, clamp_linear, input.uv, 1.0/res, height); + #elif defined(GL2) + float3 hmap_normal = normal_from_hmap(input_texture0, input.uv, inv_input_texture0_size, height); + #endif + + float3 n = encode_signed_normal(hmap_normal); + + #if defined(D3D11) || defined(D3D12) + float h = input_texture0.Sample(clamp_linear, input.uv).r; + #elif defined(GL2) + float h = tex2D(input_texture0, input.uv).r; + #endif + o.buffer0 = float4(encode_float_rg(h), n.xy /*encode_float_rg(h)*/); + float4 mat = tex2D(input_texture1, input.uv); + o.buffer1 = mat; + return o; + } + """ + } +} + +shaders = { + terrain_editor_brush = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [ + { hlsl_shader="terrain_editor_brush" render_states="brush" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + terrain_editor_brush_marker = { + editor_advanced_mode = true + + contexts = { + default = { + passes = [ + { layer="transparent" hlsl_shader="terrain_editor_brush_marker" render_states="marker" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } + + terrain_decoration = { + editor_advanced_mode = true + + contexts = { + default = { + passes_sort_mode="immediate" + passes = [{ + defined="OES2" + pass = [ + ] + fail = [ + { hlsl_shader="terrain_decoration" render_states="filter" } + ] + }] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } +} + +static_compile = [ + { if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" } + { if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SUB"] } + { if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SUB" "INVERT"] } + { if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SAMPLE_HEIGHT"] } + { if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SAMPLE_BASED" "FLATTEN"] } + { if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SAMPLE_BASED" "SAMPLE"] } + { if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush" defines=["SAMPLE_BASED" "BOX_FILTER"] } + { if: "on_renderer(D3D11, D3D12)" shader="terrain_editor_brush_marker" } + { if: "on_renderer(D3D11, D3D12)" shader="terrain_decoration" } +] \ No newline at end of file diff --git a/vmf_source/core/stingray_renderer/shader_libraries/water.shader_source b/vmf_source/core/stingray_renderer/shader_libraries/water.shader_source new file mode 100644 index 0000000..1899ec8 --- /dev/null +++ b/vmf_source/core/stingray_renderer/shader_libraries/water.shader_source @@ -0,0 +1,734 @@ +// in this context include refers to another shader file +includes = [ "core/stingray_renderer/shader_libraries/common.shader_source" "core/stingray_renderer/shader_libraries/lighting_common.shader_source" ] + +// TODO: disable sun if it's disabled in environment settings. + +// TODO: velocity vectors important for SSR + +// TODO: we are using clip depth as linear depth, which is incorrect + +render_states = { + filter_max = { + inherits = "default" + states = { + z_write_enable = "false" + + blend_enable = "true" + blend_op = "blend_op_max" + dest_blend = "blend_one" + src_blend = "blend_one" + + write_mask0 = "alpha" + } + } + + water_mask = { + inherits = "default" + states = { + z_write_enable="false" + write_mask0 = "red" + cull_mode = "cull_none" + } + } + + water_depth_only = { + inherits = "default" + states = { + defined_ONE_SIDED = { + cull_mode = "cull_cw" + } + ndefined_ONE_SIDED = { + cull_mode = "cull_none" + } + } + } + + water_opacity = { + inherits = "opacity" + states = { + defined_ONE_SIDED = { + cull_mode = "cull_cw" + } + } + } + + wireframe = { + inherits = "opacity" + states = { + fill_mode = "fill_wireframe" + defined_D3D11 = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + defined_D3D12 = { + depth_bias = "-1" + depth_bias_clamp = "-0.00015" + slope_scale_depth_bias = "-2.0" + } + } + } +} + +hlsl_shaders = { + water_displaced = { + includes = [ "common", "gbuffer_access", "fog", "brdf" "taa_offsets" ] + + samplers = { + ndefined_MASK = { + defined_COLOR_EXTINCTION = { + linear_depth = { sampler_states = "clamp_linear" } + } + ndefined_COLOR_EXTINCTION = { + defined_FOAM = { + linear_depth = { sampler_states = "clamp_linear" } + } + ndefined_FOAM = { + defined_GEOMETRY_FADE_OUT = { + linear_depth = { sampler_states = "clamp_linear" } + } + } + } + + normal_map = { sampler_states = "wrap_anisotropic" } + blend_normal_map = { sampler_states = "wrap_anisotropic" } + + hdr0 = { sampler_states = "clamp_linear" } + ldr0 = { sampler_states = "clamp_point" } + defined_CUSTOM_DIFFUSE_CUBEMAP = { + diffuse_cubemap = { sampler_states = "clamp_linear"} + } + ndefined_CUSTOM_DIFFUSE_CUBEMAP = { + global_diffuse_map = { sampler_states = "clamp_linear"} + } + + defined_FOAM = { + diffuse_map = { sampler_states = "wrap_anisotropic_srgb" } + defined_FOAM_NORMAL_ROUGHNESS_MAP = { + foam_normal_roughness_map = { sampler_states = "wrap_anisotropic" } + } + } + ndefined_FOAM = { + defined_CREST_FOAM = { + diffuse_map = { sampler_states = "wrap_anisotropic_srgb" } + defined_FOAM_NORMAL_ROUGHNESS_MAP = { + foam_normal_roughness_map = { sampler_states = "wrap_anisotropic" } + } + } + } + + defined_FORWARD_LIGHTING = { + brdf_lut = { sampler_states = "clamp_linear"} + defined_CUSTOM_SPECULAR_CUBEMAP = { + specular_cubemap = { sampler_states = "clamp_linear"} + } + ndefined_CUSTOM_SPECULAR_CUBEMAP = { + global_specular_map = { sampler_states = "clamp_linear"} + } + } + + water_mask = { sampler_states = "clamp_point" } + } + } + + code=""" + #ifdef VERTEX_DISPLACEMENT + // Gerstner waves + // http://http.developer.nvidia.com/GPUGems/gpugems_ch01.html + // http://graphics.ucsd.edu/courses/rendering/2005/jdewall/tessendorf.pdf + float3 gerstner_wave_displacement( float2 wave_direction, float wave_length, float wave_amplitude, float wave_steepness, float3 wp ) + { + const float GRAVITY = 9.82; + + float w = 2 * PI / wave_length; // angular frequency + float s = sqrt( GRAVITY * w ); // phase animation multiplier + float q = wave_steepness/(w*wave_amplitude); // steepness 0 -> 1 + + float2 xy = q * wave_amplitude * wave_direction * cos( w * dot( wave_direction, wp.xy ) + s * time ); + float z = wave_amplitude * sin( w * dot( wave_direction, wp.xy ) + s * time ); + + return float3(xy, z); + } + + float2 wave_direction_to_vector(float dir) + { + float x = dir < 0.5 ? dir*4-1 : (1-dir)*4-1; + float y = dir < 0.5 ? abs(x)-1 : 1-abs(x); + + return float2(x,y); + } + #endif + + #if !defined(MASK) + DECLARE_SAMPLER_2D(water_mask); + + #if defined(COLOR_EXTINCTION) || defined(FOAM) || defined(GEOMETRY_FADE_OUT) + DECLARE_SAMPLER_2D(linear_depth); + #endif + + DECLARE_SAMPLER_2D(normal_map); // exports={ name="Normal Map" type="resource" } + DECLARE_SAMPLER_2D(blend_normal_map); // exports={ name="Perlin Noise Map" type="resource" } + + DECLARE_SAMPLER_2D(hdr0); + DECLARE_SAMPLER_2D(ldr0); + #ifdef CUSTOM_DIFFUSE_CUBEMAP + DECLARE_SAMPLER_CUBE(diffuse_cubemap); // exports={ name="Diffuse Cubemap" type="resource" } + #else + DECLARE_SAMPLER_CUBE(global_diffuse_map); + #define diffuse_cubemap global_diffuse_map + #endif + + #if defined(FOAM) || defined(CREST_FOAM) + DECLARE_SAMPLER_2D(diffuse_map); // exports={ name="Foam Map" type="resource" } + #if defined(FOAM_NORMAL_ROUGHNESS_MAP) + DECLARE_SAMPLER_2D(foam_normal_roughness_map); // exports={ name="Foam Normal Roughness Map" type="resource" } + #endif + #endif + + #ifdef FORWARD_LIGHTING + DECLARE_SAMPLER_2D(brdf_lut); + #ifdef CUSTOM_SPECULAR_CUBEMAP + DECLARE_SAMPLER_CUBE(specular_cubemap); // exports={ name="Specular Cubemap" type="resource" } + #else + DECLARE_SAMPLER_CUBE(global_specular_map); + #define specular_cubemap global_specular_map + #endif + #endif + #endif + + struct VS_INPUT { + float4 position : POSITION; + #if !defined(MASK) + float3 normal : NORMAL0; + #ifndef WORLD_XY_AS_UV + float3 tangent : TANGENT; + float3 binormal : BINORMAL; + float2 uv : TEXCOORD0; + #endif + #endif + }; + + struct PS_INPUT { + float4 position : SV_POSITION; + #if !defined(MASK) + float4 wp : TEXCOORD0; + #if defined(COLOR_EXTINCTION) || defined(FOAM) + float4 w : TEXCOORD1; + #endif + float3 tsm0 : TEXCOORD3; + float3 tsm1 : TEXCOORD4; + float3 tsm2 : TEXCOORD5; + float4 uv : TEXCOORD2; + #if defined(BLEND_TO_PERLIN_NOISE) + float2 perlin_uv : TEXCOORD6; + #endif + #if defined(FOAM) || defined(CREST_FOAM) + float2 foam_uv : TEXCOORD7; + #endif + #if defined(CREST_FOAM) + float wave_height : TEXCOORD8; + #endif + #endif + }; + + CBUFFER_START(c0) + float4x4 world; + float4x4 view_proj; + #if defined(VERTEX_DISPLACEMENT) + float wave_direction; // exports = { name="Wave #1 Direction" type="scalar" min=0 max=1 value=0 step=0.001 } + float wave_length; // exports = { name="Wave #1 Length" type="scalar" min=0.01 max=200 value=100.0 step=0.01 } + float wave_amplitude; // exports = { name="Wave #1 Amplitude" type="scalar" min=0.01 max=3.0 value=0.3 step=0.01 } + float wave_steepness; // exports = { name="Wave #1 Wave Steepness" type="scalar" min=0.0 max=1.0 value=0.1 step=0.01 } + #if defined(VERTEX_DISPLACEMENT_2) + float wave_direction_2; // exports = { name="Wave #2 Direction" type="scalar" min=0 max=1 value=0 step=0.001 } + float wave_length_2; // exports = { name="Wave #2 Length" type="scalar" min=0.01 max=200 value=100.0 step=0.01 } + float wave_amplitude_2; // exports = { name="Wave #2 Amplitude" type="scalar" min=0.01 max=3.0 value=0.3 step=0.01 } + float wave_steepness_2; // exports = { name="Wave #2 Steepness" type="scalar" min=0.0 max=1.0 value=0.1 step=0.01 } + #endif + #endif + + float4 dev_wireframe_color; + CBUFFER_END + + #if !defined(MASK) && !defined(DRAW_WIREFRAME) + CBUFFER_START(c_water) + #ifdef WORLD_XY_AS_UV + float2 layer0_normal_tile_size; // exports={ name="Layer 0 Normal Map Tile" type="vector2" value=[2.0 2.0] min=[0 0] max=[30 30] step=[0.01 0.01]} + float2 layer1_normal_tile_size; // exports={ name="Layer 1 Normal Map Tile" type="vector2" value=[1.0 1.0] min=[0 0] max=[30 30] step=[0.01 0.01]} + #ifdef BLEND_TO_PERLIN_NOISE + float2 perlin_noise_tile_size; // exports={ name="Perlin Normal Map Tile" type="vector2" value=[1.0 1.0] min=[0 0] max=[30 30] step=[0.01 0.01]} + #endif + #if defined(FOAM) || defined(CREST_FOAM) + float2 foam_tile_size; // exports={ name="Foam Map Tile" type="vector2" value=[2.0 2.0] min=[0 0] max=[30 30] step=[0.01 0.01]} + #endif + #else + float2 layer0_normal_tile_scale; // exports={ name="Layer 0 Normal Map Scale" type="vector2" value=[2.0 2.0] min=[0 0] max=[30 30] step=[0.01 0.01]} + float2 layer1_normal_tile_scale; // exports={ name="Layer 1 Normal Map Scale" type="vector2" value=[1.0 1.0] min=[0 0] max=[30 30] step=[0.01 0.01]} + #ifdef BLEND_TO_PERLIN_NOISE + float2 perlin_noise_tile_size; // exports={ name="Perlin Normal Map Scale" type="vector2" value=[1.0 1.0] min=[0 0] max=[30 30] step=[0.01 0.01]} + #endif + #if defined(FOAM) || defined(CREST_FOAM) + float2 foam_tile_scale; // exports={ name="Foam Map Scale" type="vector2" value=[2.0 2.0] min=[0 0] max=[30 30] step=[0.01 0.01]} + #endif + #endif + float2 layer0_normal_tile_scroll_speed; // exports={ name="Layer 0 Normal Map Scroll Speed" type="vector2" value=[0.1 0.1] min=[-2 -2] max=[2 2] step=[0.005 0.005]} + float2 layer1_normal_tile_scroll_speed; // exports={ name="Layer 1 Normal Map Scroll Speed" type="vector2" value=[0.1 0.1] min=[-2 -2] max=[2 2] step=[0.005 0.005]} + #ifdef BLEND_TO_PERLIN_NOISE + float2 perlin_noise_tile_scroll_speed; // exports={ name="Perlin Noise Scroll Speed" type="vector2" value=[0.1 0.1] min=[-2 -2] max=[2 2] step=[0.005 0.005]} + float3 perlin_noise_octaves; // exports={ name="Perlin Noise Octaves" type="vector3" value=[1.12 0.59 0.23] min=[0 0 0] max=[2 2 2] step=[0.005 0.005 0.005]} + float3 perlin_noise_gradients; // exports={ name="Perlin Noise Gradients" type="vector3" value=[1.4 1.6 2.2] min=[0 0 0] max=[3 3 3] step=[0.005 0.005 0.005]} + float2 perlin_noise_blend; // exports = { name="Perlin Noise Blend Start/Distance" type="vector2" value=[80 150] min=[10 10] max=[1000 1000] step=[0.5 0.5]} + #endif + #if defined(FOAM) || defined(CREST_FOAM) + float2 foam_tile_scroll_speed; // exports={ name="Foam Map Scroll Speed" type="vector2" value=[0.1 0.1] min=[-2 -2] max=[2 2] step=[0.005 0.005]} + #if defined(FOAM) + float foam_fade_in; // exports={ name="Foam Fade in Depth" type="scalar" value=0.2 min=0.01 max=2.0 step=0.005 } + #endif + #endif + float2 fresnel_settings; // exports={ name="Fresnel Settings [bias, exp]" type="vector2" value=[0.1 3] min=[0 0] max=[1 10] step=[0.001 0.005]} + float refraction_amount; // exports={ name="Refraction Amount" type="scalar" value=0.01 min=0.0 max=1.0 step=0.001 } + float water_metallic; // exports={ name="Metallic" type="scalar" value=1.0 min=0.0 max=1.0 step=0.001 } + float water_roughness; // exports={ name="Roughness" type="scalar" value=0.0 min=0.0 max=1.0 step=0.001 } + #if defined(FOAM) || defined(CREST_FOAM) + float foam_metallic; // exports={ name="Foam Metallic" type="scalar" value=0.0 min=0.0 max=1.0 step=0.001 } + #if !defined(defined_FOAM_NORMAL_ROUGHNESS_MAP) + float foam_roughness; // exports={ name="Foam Roughness" type="scalar" value=0.3 min=0.0 max=1.0 step=0.001 } + #endif + #endif + + float normal_contrast; // exports={ name="Normal Contrast" type="scalar" value=1 min=0.0 max=1.0 step=0.001 } + float3 base_color; // exports={ name="Surface Color" type="vector3" value=[0.02 0.02 0.02] min=[0 0 0] max=[1 1 1] step=[0.001 0.001 0.001]} + #ifdef COLOR_EXTINCTION + float3 color_extinction; // exports={ name="RGB color extinction depth" type="vector3" value=[4.5 75 300] min=[0 0 0] max=[300 300 300] step=[0.2 0.2 0.2] } + float3 depth_color; // exports={ name="Deep Water Color" type="vector3" value=[0.1 0.1 0.4] min=[0 0 0] max=[4 4 4] step=[0.005 0.005 0.005] } + #endif + #ifdef GEOMETRY_FADE_OUT + float geometry_fade_out_distance; // exports={ name="Geometry Fade out Distance" type="scalar" value=0.2 min=0.01 max=0.8 step=0.005 } + #endif + + #if defined(CREST_FOAM) + half crest_foam_min_height; // exports = { name="Crest Foam Min Height" type="scalar" min=-3.0 max=3.0 value=0.0 step=0.01 } + half crest_foam_max_height; // exports = { name="Crest Foam Max Height" type="scalar" min=-3.0 max=3.0 value=0.5 step=0.01 } + half crest_foam_strength; // exports = { name="Crest Foam Strength" type="scalar" min=0.0 max=1.0 value=0.5 step=0.01 } + #endif + + float3 ambient_tint; + float sun_enabled; + float ambient_diffuse_fade_type; + float3 ambient_diffuse_fade; // min multiplier, height offset, falloff + CBUFFER_END + #endif + + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + PS_INPUT vs_main(VS_INPUT input) { + PS_INPUT o; + float3 wp = mul(input.position, world); + + #if defined(VERTEX_DISPLACEMENT) + float3 displacement = gerstner_wave_displacement( wave_direction_to_vector(wave_direction), wave_length, wave_amplitude, wave_steepness, wp ); + #ifdef VERTEX_DISPLACEMENT_2 + displacement += gerstner_wave_displacement( wave_direction_to_vector(wave_direction_2), wave_length_2, wave_amplitude_2, wave_steepness_2, wp ); + #endif + wp += displacement; + + // Frequency adjusted to water depth + //float D = 50; + //float k = 2 * PI / l; + //float w = sqrt( GRAVITY * k * tanh(k*D) ); + #endif + float4 p = mul(float4(wp, 1), view_proj); + + float4 view_space = p / p.w; + view_space.xy += get_vs_halton_offset(frame_number); + o.position = view_space * p.w; + + #if defined(MASK) || defined(DRAW_WIREFRAME) + return o; + #else + #if defined(CREST_FOAM) + o.wave_height = displacement.z; + #endif + + #if defined(COLOR_EXTINCTION) || defined(FOAM) + o.w = encode_world_pos(o.position); + #endif + o.wp.xyz = wp; + o.wp.w = linearize_depth(o.position.z / o.position.w); + + #define layer0_scroll layer0_normal_tile_scroll_speed + #define layer1_scroll layer1_normal_tile_scroll_speed + #define foam_scroll foam_tile_scroll_speed + + float3 normal = input.normal; + #ifdef WORLD_XY_AS_UV + float3 tangent = float3(-1,0,0); + float3 binormal = float3(0,-1,0); + #else + float3 tangent = input.tangent; + float3 binormal = input.binormal; + #endif + tspace_transform_transpose(o.tsm0, o.tsm1, o.tsm2, tangent, binormal, normal, (float3x3)world); + + #ifdef WORLD_XY_AS_UV + o.uv = float4( (wp.xy / layer0_normal_tile_size) + time * layer0_scroll, + (wp.xy / layer1_normal_tile_size) + time * layer1_scroll); + #ifdef BLEND_TO_PERLIN_NOISE + o.perlin_uv.xy = (wp.xy / perlin_noise_tile_size); + #endif + + #if defined(FOAM) || defined(CREST_FOAM) + o.foam_uv = float2((wp.xy / foam_tile_size) + time * foam_scroll); + #endif + #else + o.uv = float4( (input.uv * layer0_normal_tile_scale) + time * layer0_scroll, + (input.uv * layer1_normal_tile_scale) + time * layer1_scroll); + #ifdef BLEND_TO_PERLIN_NOISE + o.perlin_uv.xy = (wp.xy * perlin_noise_tile_size); + #endif + + #if defined(FOAM) || defined(CREST_FOAM) + o.foam_uv = float2((input.uv * foam_tile_scale) + time * foam_scroll); + #endif + #endif + + return o; + #endif + } + + half fresnel(half n_dot_e, half bias, half power) { + return saturate(bias + (1-bias) * pow(1-n_dot_e, power)); + } + + #if defined(MASK) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return float4(1,1,1,1); + } + #elif defined(DRAW_WIREFRAME) + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input) : SV_TARGET0 { + return dev_wireframe_color; + } + #else + DEFAULT_ROOT_SIGNATURE_ATTRIBUTE + float4 ps_main(PS_INPUT input + #if !defined(ONE_SIDED) + #if defined(GNM) + , bool vface : S_FRONT_FACE + #else + , float vface : VFACE + #endif + #endif + ) : SV_TARGET0 + { + #if !defined(AVOID_CAMERA_CULLING) + if (camera_pos.z < input.wp.z) { + // Camera is under water surface + discard; + } + #endif + + #if defined(GL2) + half2 screen_uv = wpos.xy / back_buffer_size; + #else + half2 screen_uv = input.position.xy / back_buffer_size; + #endif + + float3 view_vector = camera_pos - input.wp; + float3 view_dir = normalize(view_vector); + + #ifdef BLEND_TO_PERLIN_NOISE + half blend = saturate((length(view_vector.xy) - perlin_noise_blend.x) / perlin_noise_blend.y); + normal_contrast = lerp(1, normal_contrast, blend); + + half2 perlin0_uv = blend > 0 ? input.perlin_uv.xy * perlin_noise_octaves.x + perlin_noise_tile_scroll_speed * time : 0; + half2 perlin1_uv = blend > 0 ? input.perlin_uv.xy * perlin_noise_octaves.y + perlin_noise_tile_scroll_speed * time : 0; + half2 perlin2_uv = blend > 0 ? input.perlin_uv.xy * perlin_noise_octaves.z + perlin_noise_tile_scroll_speed * time : 0; + half2 perlin0 = TEX2D(blend_normal_map, perlin0_uv).xy; + half2 perlin1 = TEX2D(blend_normal_map, perlin1_uv).xy; + half2 perlin2 = TEX2D(blend_normal_map, perlin2_uv).xy; + + half2 perlin = perlin0 * perlin_noise_gradients.x + perlin1 * perlin_noise_gradients.y + perlin2 * perlin_noise_gradients.z; + + input.uv *= blend > 1 ? 0 : 1; + #endif + + half2 tnormal_grad = decode_normal_grad(TEX2D(normal_map, input.uv.xy)) + decode_normal_grad(TEX2D(normal_map, input.uv.zw)); + + //float3 tnormal = normalize(float3(normal_contrast,normal_contrast,1) * (decode_normal_map(TEX2D(normal_map, input.uv.xy)) + decode_normal_map(TEX2D(normal_map, input.uv.zw)))); + #ifdef BLEND_TO_PERLIN_NOISE + float3 tnormal = normalize(float3(normal_contrast * lerp(tnormal_grad, perlin, blend), 1)); + #else + float3 tnormal = normalize(float3(normal_contrast * tnormal_grad, 1)); + #endif + + #if !defined(ONE_SIDED) + if (!front_facing(vface)) { + input.tsm0.z = -input.tsm0.z; + input.tsm1.z = -input.tsm1.z; + input.tsm2.z = -input.tsm2.z; + } + #endif + float3 wn = rotate_vector3(tnormal, input.tsm0, input.tsm1, input.tsm2); + + // calc under water color + half2 refracted_uv = screen_uv + tnormal.xy * refraction_amount; + half refraction_mask = TEX2D(water_mask, refracted_uv).r; + refracted_uv = lerp(screen_uv, refracted_uv, refraction_mask); + half3 refracted_texel = TEX2D(hdr0, refracted_uv); + float3 under_water_color = refracted_texel; + + // calc fresnel term + half cosNV = saturate(dot(wn, view_dir)); + half f = fresnel(cosNV, fresnel_settings.x, fresnel_settings.y); + + #if defined(COLOR_EXTINCTION) || defined(FOAM) || defined(GEOMETRY_FADE_OUT) + float d = gbuffer_decode_depth(TEX2D(linear_depth, screen_uv)); + #endif + + #if defined(COLOR_EXTINCTION) || defined(FOAM) + float3 wp = decode_world_pos(input.w, d); + float water_depth = abs(wp.z - input.wp.z); + #endif + + #if defined(COLOR_EXTINCTION) + under_water_color = lerp(under_water_color, depth_color, saturate(water_depth / color_extinction)); + #endif + + #ifdef GEOMETRY_FADE_OUT + half op = saturate(abs(d - input.wp.w) / geometry_fade_out_distance); + #else + half op = 1; + #endif + + #ifdef MASK_WITH_DEFERRED_SHADOW + // TODO: The correct way, would be to sample the depth values from the shadows slices and then compare to the surface depth, however this is probably to expensive. + half4 shadow = TEX2D(ldr0, screen_uv).r; + #else + half shadow = 1; + #endif + + half roughness = water_roughness; + half metallic = water_metallic; + + #if defined(FOAM) || defined(CREST_FOAM) + #if defined(FOAM_GEOMETRY_NORMALS) + float3 foam_wn = normalize(float3(input.tsm0.z, input.tsm1.z, input.tsm2.z)); + #elif defined(FOAM_NORMAL_ROUGHNESS_MAP) + float4 foam_normal_roughness = TEX2D(foam_normal_roughness_map, input.foam_uv); + float3 foam_wn = normalize(2.0*foam_normal_roughness.rgb - 1.0); + float foam_roughness = foam_normal_roughness.a; + #else + float3 foam_wn = wn; + #endif + + half4 foam_texel = TEX2D(diffuse_map, input.foam_uv); + #if defined(FOAM) + #if defined(ALPHA_WEIGHT_FOAM) + half foam_intensity = foam_texel.a - saturate(water_depth / foam_fade_in) * foam_texel.a; + #else + half foam_intensity = 1.0 - saturate(water_depth / foam_fade_in); + #endif + #if !defined(SEPERABLE_LIGHTING) + #if defined(FOAM_GEOMETRY_NORMALS) || defined(FOAM_NORMAL_ROUGHNESS_MAP) + wn = lerp(wn, foam_wn, foam_intensity); + #endif + metallic = lerp(metallic, foam_metallic, foam_intensity); + roughness = lerp(roughness, foam_roughness, foam_intensity); + base_color = lerp(base_color, foam_texel.rgb, foam_intensity); + f = lerp(f, 1.0, foam_intensity); + #endif + #endif + + #if defined(CREST_FOAM) + #if defined(ALPHA_WEIGHT_CREST_FOAM) + half crest_foam_intensity = saturate((input.wave_height - crest_foam_min_height) / (crest_foam_max_height - crest_foam_min_height)) * foam_texel.a * crest_foam_strength; + #else + half crest_foam_intensity = saturate((input.wave_height - crest_foam_min_height) / (crest_foam_max_height - crest_foam_min_height)) * crest_foam_strength; + #endif + #if !defined(SEPERABLE_LIGHTING) + #if defined(FOAM_GEOMETRY_NORMALS) || defined(FOAM_NORMAL_ROUGHNESS_MAP) + wn = lerp(wn, foam_wn, crest_foam_intensity); + #endif + metallic = lerp(metallic, foam_metallic, crest_foam_intensity); + roughness = lerp(roughness, foam_roughness, crest_foam_intensity); + base_color = lerp(base_color, foam_texel.rgb, crest_foam_intensity); + f = lerp(f, 1.0, crest_foam_intensity); + #endif + #endif + #endif + + #if defined(D3D11) + ambient_tint = (capture_cubemap == 1) ? 1.0 : ambient_tint; + #endif + + // TODO: use clustered shading instead! + + float3 indirect_light_tint = ambient_tint; + float fade_type = ambient_diffuse_fade_type; + float3 fade_params = ambient_diffuse_fade; + [branch] + if(fade_type != 0.0) { + if(fade_type == 1.0) { + indirect_light_tint *= max(fade_params.x, 1.0 - exp((input.wp.z - fade_params.y)/fade_params.z)); + } else if(fade_type == 2.0) { + indirect_light_tint *= max(fade_params.x, 1.0 - exp((fade_params.y - input.wp.z)/fade_params.z)); + } else { + indirect_light_tint *= max(fade_params.x, 1.0 - exp(-abs(fade_params.y - input.wp.z)/fade_params.z)); + } + } + + // Same lighting code as in global_lighting + // 0.04 is taken as a common value for dielectrics. Source: Real-time rendering 3rd edition. + float3 specular_color = lerp(float3(0.04,0.04,0.04), base_color, metallic); + float3 diffuse_color = lerp(base_color, float3(0,0,0), metallic); + + float3 L = normalize(-sun_direction); // TODO: unnecessary normalization? + float2 scale_bias = TEX2D(brdf_lut, float2(cosNV, roughness).xy).xy; + + float mipmap_index = roughness * 7; + #ifdef FLIP_DIFFUSE_CUBEMAP + float3 ambient = rgbm_decode(TEXCUBELOD(diffuse_cubemap, -wn, 0)) * indirect_light_tint * diffuse_color; + #else + float3 ambient = rgbm_decode(TEXCUBELOD(diffuse_cubemap, wn, 0)) * indirect_light_tint * diffuse_color; + #endif + #ifdef FLIP_SPECULAR_CUBEMAP + ambient += rgbm_decode(TEXCUBELOD(specular_cubemap, -reflect(-view_dir, wn), mipmap_index)) * (specular_color * scale_bias.x + scale_bias.y) * indirect_light_tint; + #else + ambient += rgbm_decode(TEXCUBELOD(specular_cubemap, reflect(-view_dir, wn), mipmap_index)) * (specular_color * scale_bias.x + scale_bias.y) * indirect_light_tint; + #endif + + //#if defined(SUN_ENABLED) + float3 acc_diff = 0; + float3 acc_spec = 0; + if (sun_enabled) + bsdf(L, view_dir, wn, sun_color, diffuse_color, specular_color, roughness, shadow, acc_diff, acc_spec); + + float3 surface_color = ambient + acc_diff + acc_spec; + //#else + // float3 surface_color = ambient; + //#endif + + // lerp between them + float3 c = lerp(under_water_color, surface_color, f); + + // Do lighting again for foam + // Reusing ambient, even if this is incorrect. + #if defined(SEPERABLE_LIGHTING) + float foam_alpha = 0.0; + #if defined(FOAM) + foam_alpha += foam_intensity; + #endif + #if defined(CREST_FOAM) + foam_alpha += crest_foam_intensity; + #endif + foam_alpha = min(foam_alpha, 1.0); + float3 foam_specular_color = lerp(float3(0.04,0.04,0.04), foam_texel.rgb, foam_metallic); + float3 foam_diffuse_color = lerp(foam_texel.rgb, float3(0,0,0), foam_roughness); + float2 foam_scale_bias = TEX2D(brdf_lut, float2(dot(foam_wn, view_dir), foam_roughness).xy).xy; + float foam_mipmap_index = foam_roughness * 7; + #ifdef FLIP_DIFFUSE_CUBEMAP + float3 foam_ambient = rgbm_decode(TEXCUBELOD(diffuse_cubemap, -foam_wn, 0)) * diffuse_tint * foam_diffuse_color; + #else + float3 foam_ambient = rgbm_decode(TEXCUBELOD(diffuse_cubemap, foam_wn, 0)) * diffuse_tint * foam_diffuse_color; + #endif + #ifdef FLIP_SPECULAR_CUBEMAP + foam_ambient += rgbm_decode(TEXCUBELOD(specular_cubemap, -reflect(-view_dir, foam_wn), foam_mipmap_index)) * (foam_specular_color * foam_scale_bias.x + foam_scale_bias.y) * reflections_tint; + #else + foam_ambient += rgbm_decode(TEXCUBELOD(specular_cubemap, reflect(-view_dir, foam_wn), foam_mipmap_index)) * (foam_specular_color * foam_scale_bias.x + foam_scale_bias.y) * reflections_tint; + #endif + float3 foam_acc_diff = 0; + float3 foam_acc_spec = 0; + if (sun_enabled) + bsdf(L, view_dir, foam_wn, sun_color, foam_diffuse_color, foam_specular_color, foam_roughness, shadow, foam_acc_diff, foam_acc_spec); + float3 foam_color = foam_ambient + foam_acc_diff + foam_acc_spec; + c = lerp(c, foam_color, foam_alpha); + #endif + + return apply_fog(float4(c, op), input.wp.xyz, input.wp.w); + } + #endif + """ + } +} + +shaders = { + water = { + editor_options = [ + { + name="Water Shading" + options = [ + { name="Seperable Foam Lighting" define="SEPERABLE_LIGHTING" condition="FOAM || CREST_FOAM"} + ] + } + { + name="UV Generation" + options = [ + { name="Base UV coordinates on World XY" define="WORLD_XY_AS_UV" } + ] + } + { + name="Water Surface Features" + options = [ + { name="Depth color extinction" define="COLOR_EXTINCTION" } + { name="Geometry Fade out" define="GEOMETRY_FADE_OUT" } + { name="Depth Foam" define="FOAM" } + { name="Crest Foam" define="CREST_FOAM" condition="VERTEX_DISPLACEMENT" } + { name="Weight Depth Foam with Alpha" define="ALPHA_WEIGHT_FOAM" condition="FOAM" } + { name="Weight Crest Foam with Alpha" define="ALPHA_WEIGHT_CREST_FOAM" condition="CREST_FOAM" } + { name="Foam Uses Geometry Normals" define="FOAM_GEOMETRY_NORMALS" condition="!FOAM_NORMAL_ROUGHNESS_MAP && (FOAM || CREST_FOAM)" } + { name="Foam Normal-Roughness Map" define="FOAM_NORMAL_ROUGHNESS_MAP" condition="!FOAM_GEOMETRY_NORMALS && (FOAM || CREST_FOAM)" } + { name="Blend to perlin noise to hide tiling" define="BLEND_TO_PERLIN_NOISE" } + { name="Mask sun with deferred shadows" define="MASK_WITH_DEFERRED_SHADOW" } + + ] + } + { + name="Vertex Displacement" + options = [ + { name="Vertex Displaced Wave" define="VERTEX_DISPLACEMENT" } + { name=" - Wave #2" define="VERTEX_DISPLACEMENT_2" } + ] + } + { + name="Vertex Displacement" + options = [ + { name="Vertex Displaced Wave" define="VERTEX_DISPLACEMENT" } + { name=" - Wave #2" define="VERTEX_DISPLACEMENT_2" } + ] + } + { + name="Pixel Modifiers" + options = [ + { name="Avoid Culling Water with Camera Z" define="AVOID_CAMERA_CULLING" } + { name="Use Custom Specular Cubemap" define="CUSTOM_SPECULAR_CUBEMAP" } + { name="Use Custom Diffuse Cubemap" define="CUSTOM_DIFFUSE_CUBEMAP" } + { name="Flip Specular Cubemap" define="FLIP_SPECULAR_CUBEMAP" } + { name="Flip Diffuse Cubemap" define="FLIP_DIFFUSE_CUBEMAP" } + ] + } + { + name="Misc" + options = [ + { name="One Sided" define="ONE_SIDED" } + ] + } + ] + + contexts = { + default = { + passes = [ + { layer="water_mask" hlsl_shader="water_displaced" defines="MASK" render_states="water_mask" } + { layer="water" hlsl_shader="water_displaced" defines="FORWARD_LIGHTING" render_states="water_opacity" } + { layer="wireframe" hlsl_shader="water_displaced" defines="DRAW_WIREFRAME" render_states="wireframe" branch_key="dev_wireframe" } + ] + } + } + + compile = { + default = [ + { defines=[] } + ] + } + } +} diff --git a/vmf_source/gui/header_background.dds b/vmf_source/gui/header_background.dds new file mode 100644 index 0000000..ea334f1 Binary files /dev/null and b/vmf_source/gui/header_background.dds differ diff --git a/vmf_source/gui/header_background.texture b/vmf_source/gui/header_background.texture new file mode 100644 index 0000000..4ba414e --- /dev/null +++ b/vmf_source/gui/header_background.texture @@ -0,0 +1,18 @@ +common = { + input = { + filename = "gui/header_background" + } + output = { + apply_processing = true + correct_gamma = true + cut_alpha_threshold = 0.5 + enable_cut_alpha_threshold = false + format = "A8R8G8B8" + mipmap_filter = "kaiser" + mipmap_filter_wrap_mode = "mirror" + mipmap_keep_original = false + mipmap_num_largest_steps_to_discard = 0 + mipmap_num_smallest_steps_to_discard = 0 + srgb = true + } +} \ No newline at end of file diff --git a/vmf_source/gui/header_background_lit.png b/vmf_source/gui/header_background_lit.png new file mode 100644 index 0000000..ba1d59b Binary files /dev/null and b/vmf_source/gui/header_background_lit.png differ diff --git a/vmf_source/gui/header_background_lit.texture b/vmf_source/gui/header_background_lit.texture new file mode 100644 index 0000000..2c9d5ba --- /dev/null +++ b/vmf_source/gui/header_background_lit.texture @@ -0,0 +1,17 @@ +common = { + input = { + filename = "gui/header_background_lit" + } + output = { + apply_processing = true + correct_gamma = true + cut_alpha_threshold = 0.5 + enable_cut_alpha_threshold = false + format = "A8R8G8B8" + mipmap_filter = "kaiser" + mipmap_filter_wrap_mode = "mirror" + mipmap_keep_original = false + mipmap_num_largest_steps_to_discard = 0 + mipmap_num_smallest_steps_to_discard = 0 + } +} \ No newline at end of file diff --git a/vmf_source/materials/header_background.material b/vmf_source/materials/header_background.material new file mode 100644 index 0000000..c3a142b --- /dev/null +++ b/vmf_source/materials/header_background.material @@ -0,0 +1,14 @@ +header_background = { + material_contexts = { + surface_material = "" + } + + shader = "gui_gradient:DIFFUSE_MAP:MASKED" + + textures = { + diffuse_map = "gui/header_background" + } + + variables = { + } +} diff --git a/vmf_source/materials/header_background_lit.material b/vmf_source/materials/header_background_lit.material new file mode 100644 index 0000000..4526cec --- /dev/null +++ b/vmf_source/materials/header_background_lit.material @@ -0,0 +1,14 @@ +header_background_lit = { + material_contexts = { + surface_material = "" + } + + shader = "gui:DIFFUSE_MAP" + + textures = { + diffuse_map = "gui/header_background_lit" + } + + variables = { + } +} diff --git a/vmf_source/resource_packages/vmf.package b/vmf_source/resource_packages/vmf.package new file mode 100644 index 0000000..ff5fe83 --- /dev/null +++ b/vmf_source/resource_packages/vmf.package @@ -0,0 +1,23 @@ +mod = [ + "vmf" +] + +package = [ + "resource_packages/vmf" +] + +material = [ + "materials/header_background" + "materials/header_background_lit" +] + +lua = [ + "scripts/mods/vmf/vmf_loader" + "scripts/mods/vmf/modules/old_hook_and_console" + "scripts/mods/vmf/modules/mods" + "scripts/mods/vmf/modules/hooks" + "scripts/mods/vmf/modules/settings" + "scripts/mods/vmf/modules/gui" + "scripts/mods/vmf/modules/vmf_options_view" + "scripts/mods/vmf/modules/testing_stuff_here" +] \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/gui.lua b/vmf_source/scripts/mods/vmf/modules/gui.lua new file mode 100644 index 0000000..d10dfde --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/gui.lua @@ -0,0 +1,294 @@ +local vmf = get_mod("VMF") + +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +local injected_materials = {} +local none_atlas_textures = {} + +-- @TODO: can several materials be specified via 1 material file? Figure it out. +function inject_material(material_path, material_name, ...) + + --print("[FUCK]: Mods.gui.inject_material " .. material_path .. material_name) + + local material_users = {...} + if #material_users == 0 then + material_users = {"all"} + end + + for _, material_user in ipairs(material_users) do + if not injected_materials[material_user] then + injected_materials[material_user] = {} + end + + table.insert(injected_materials[material_user], material_path) + end + + none_atlas_textures[material_name] = true +end + + +--table.dump(injected_materials, "injected_materials", 2) + +vmf:hook("UIRenderer.create", function(func, world, ...) + + local ui_renderer_creator = nil + + -- extract the part with actual callstack + local callstack = Script.callstack():match('Callstack>(.-)<') + if callstack then + + -- get the name of lua script which called 'UIRenderer.create' + -- it's always the 4th string of callstack ([ ] [0] [1] >[2]<) + -- print(callstack) -- @DEBUG + local i = 0 + for s in callstack:gmatch("(.-)\n") do + i = i + 1 + if i == 4 then + ui_renderer_creator = s:match("([^%/]+)%.lua") + break --@TODO: uncomment after debugging or ... (?) + end + --EchoConsole(s) -- @DELETEME + end + end + + if ui_renderer_creator then + print("UI_RENDERER CREATED BY: " .. ui_renderer_creator) -- @DEBUG + else + --EchoConsole("You're never supposed to see this.") + --assert(true, "That's not right. That's not right at all!") + --EchoConsole(callstack) + return func(world, ...) + end + + local ui_renderer_materials = {...} + + if injected_materials[ui_renderer_creator] then + for _, material in ipairs(injected_materials[ui_renderer_creator]) do + table.insert(ui_renderer_materials, "material") + table.insert(ui_renderer_materials, material) + end + end + + if injected_materials["all"] then + for _, material in ipairs(injected_materials["all"]) do + table.insert(ui_renderer_materials, "material") + table.insert(ui_renderer_materials, material) + end + end + + table.dump(ui_renderer_materials, "UI_RENDERER MATERIALS", 2) -- @DEBUG + + local wtf = func(world, unpack(ui_renderer_materials)) + print (tostring(wtf)) + return wtf +end) + + +vmf:hook("UIAtlasHelper.get_atlas_settings_by_texture_name", function(func, texture_name) + + if none_atlas_textures[texture_name] then + return + end + + return func(texture_name) +end) + +--inject_material("materials/yoba_face", "yoba_face", "ui_passes", "ingame_ui") + +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ + +-- @TODO: close and unload menu on_reload +-- something for hotkeys +--IngameUI.init to make hotkeys? no + +local views_settings = {} +local transitions = {} + +VMFMod.register_new_view = function (self, new_view_data) + views_settings[new_view_data.view_name] = new_view_data.view_settings + + for transition_name, transition_function in pairs(new_view_data.view_transitions) do + transitions[transition_name] = transition_function + end + + -- @TODO: maybe there's better way to do this? + -- can be called only on reloading vmf + local ingame_ui_exists, ingame_ui = pcall(function () return Managers.player.network_manager.matchmaking_manager.matchmaking_ui.ingame_ui end) + + if ingame_ui_exists then + + -- set 'ingame_ui.views' + local new_view_name = new_view_data.view_name + local new_view_init_function = new_view_data.view_settings.init_view_function + + if not ingame_ui.views[new_view_name] then --@TODO: since I do this check, close and unload custom menus while reloading + ingame_ui.views[new_view_name] = new_view_init_function(ingame_ui.ingame_ui_context) + end + + -- set 'ingame_ui.blocked_transitions' + local blocked_transitions = new_view_data.view_settings.blocked_transitions + local current_blocked_transitions = ingame_ui.is_in_inn and blocked_transitions.inn or blocked_transitions.ingame + + for blocked_transition_name, _ in pairs(current_blocked_transitions) do + ingame_ui.blocked_transitions[blocked_transition_name] = true + end + + vmf:echo("INGAME_UI EXISTS") + else + vmf:echo("INGAME_UI DOESN'T EXIST") + end +end + + + +--@TODO: hotkey_mapping +vmf:hook("IngameUI.setup_views", function(func, self, ingame_ui_context) + func(self, ingame_ui_context) + + for view_name, view_settings in pairs(views_settings) do + + if self.is_in_inn then + if view_settings.active.inn then + self.views[view_name] = view_settings.init_view_function(ingame_ui_context) + --self.hotkey_mapping = view_settings.hotkey_mapping + end + + for blocked_transition_name, _ in pairs(view_settings.blocked_transitions.inn) do + self.blocked_transitions[blocked_transition_name] = true + end + else + if view_settings.active.ingame then + self.views[view_name] = view_settings.init_view_function(ingame_ui_context) + --self.hotkey_mapping = view_settings.hotkey_mapping + end + + for blocked_transition_name, _ in pairs(view_settings.blocked_transitions.ingame) do + self.blocked_transitions[blocked_transition_name] = true + end + end + + end +end) + + +vmf:hook("IngameUI.handle_transition", function(func, self, new_transition, ...) + + local successful_execution = pcall(func, self, new_transition, ...) + if successful_execution then + return + else + if not transitions[new_transition] then -- @TODO: is it right? + vmf:echo("Some mod is trying to use non existing view transition: " .. new_transition) + return + end + + -- this block is pure copypasta from 'IngameUI.handle_transition' + local blocked_transitions = self.blocked_transitions + + if blocked_transitions and blocked_transitions[new_transition] then + return + end + + local previous_transition = self._previous_transition + + if not self.is_transition_allowed(self, new_transition) or (previous_transition and previous_transition == new_transition) then + return + end + + local transition_params = { + ... + } + + if self.new_transition_old_view then + return + end + + local old_view = self.current_view + + transitions[new_transition](self, unpack(transition_params)) + + local new_view = self.current_view + + if old_view ~= new_view then + if self.views[old_view] and self.views[old_view].on_exit then + printf("[IngameUI] menu view on_exit %s", old_view) + self.views[old_view]:on_exit(unpack(transition_params)) + end + + if new_view and self.views[new_view] and self.views[new_view].on_enter then + printf("[IngameUI] menu view on_enter %s", new_view) + self.views[new_view]:on_enter(unpack(transition_params)) + Managers.state.event:trigger("ingame_ui_view_on_enter", new_view) + end + end + + self.new_transition = new_transition + self.new_transition_old_view = old_view + self.transition_params = transition_params + self._previous_transition = new_transition + + + end +end) + +--[[ +Mods.hook.set("whatever", "IngameUI.update", function(func, self, dt, t, disable_ingame_ui, end_of_level_ui) + func(self, dt, t, disable_ingame_ui, end_of_level_ui) + + local end_screen_active = self.end_screen_active(self) + local gdc_build = Development.parameter("gdc") + local input_service = self.input_manager:get_service("ingame_menu") + + if not self.pending_transition(self) and not end_screen_active and not self.menu_active and not self.leave_game and not self.return_to_title_screen and not gdc_build and not self.popup_join_lobby_handler.visible and input_service.get(input_service, "cancel_matchmaking", true) then + self.handle_transition(self, "vmf_options_view_force") + + --MOOD_BLACKBOARD.menu = true + end + +end) +]] + +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ +------------------------------------------------------------------------------------ + +local mod = new_mod("DisableThings") + +mod:hook("StateSplashScreen.on_enter", function(func, self) + self._skip_splash = true + func(self) +end) + +mod:hook("SplashView.init", function(self, input_manager, world) + return +end) +mod:hook("SplashView.set_index", function(self, index) + return +end) + + +mod:hook("StateSplashScreen.setup_splash_screen_view", function(func, self) + func(self) + self.splash_view = nil +end) \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/hooks.lua b/vmf_source/scripts/mods/vmf/modules/hooks.lua new file mode 100644 index 0000000..37ddde8 --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/hooks.lua @@ -0,0 +1,260 @@ +--@TODO: maybe update_function_hook_chain() pass entry instead of name +local vmf = get_mod("VMF") + +HOOKED_FUNCTIONS = HOOKED_FUNCTIONS or {} -- global, because 'loadstring' doesn't see local variables @TODO: or just HOOKED_FUNCTIONS = {} + +-- #################################################################################################################### +-- ##### Private functions ############################################################################################ +-- #################################################################################################################### + +local function get_function_by_name(function_name) + + local _, value = vmf:pcall(loadstring("return " .. function_name)) + -- no need to check status of 'pcall' - if there will be error, it's gonna be string instead of function + -- also, it can be anything else instead of function, even if 'loadstring' run will be successful, so check it + if type(value) == "function" then + return value + else + return nil + end +end + + +local function create_hooked_function_entry(hooked_function_name) + + local hooked_function = get_function_by_name(hooked_function_name) + if not hooked_function then + return nil + end + + local hooked_function_entry = {} + + hooked_function_entry.name = hooked_function_name + hooked_function_entry.original_function = hooked_function + hooked_function_entry.exec_function = hooked_function + hooked_function_entry.hooks = {} + + table.insert(HOOKED_FUNCTIONS, hooked_function_entry) + + return hooked_function_entry +end + + +local function create_hook_entry(mod, hooked_function_entry, hook_function) + + local hook_entry = {} + + hook_entry.mod = mod + hook_entry.hook_function = hook_function + hook_entry.exec_function = nil + hook_entry.is_enabled = true + + table.insert(hooked_function_entry.hooks, hook_entry) + + --return hook_entry -- @TODO: do I need this return? +end + + +-- Pick already existing function entry if it's already being hooked by some mod +local function get_hooked_function_entry(hooked_function_name) + + for i, hooked_function_entry in ipairs(HOOKED_FUNCTIONS) do + if hooked_function_entry.name == hooked_function_name then + return hooked_function_entry, i + end + end + + return nil +end + + +-- Pick already existing hook entry if there is one +local function get_hook_entry(mod, hooked_function_entry) + + for i, hook_entry in ipairs(hooked_function_entry.hooks) do + if hook_entry.mod == mod then + return hook_entry, i + end + end + + return nil +end + + +local function update_function_hook_chain(hooked_function_name) + + local hooked_function_entry, hooked_function_entry_index = get_hooked_function_entry(hooked_function_name) + + for i, hook_entry in ipairs(hooked_function_entry.hooks) do + if i == 1 then + if hook_entry.is_enabled then + hook_entry.exec_function = function(...) + return hook_entry.hook_function(hooked_function_entry.original_function, ...) + end + else + hook_entry.exec_function = function(...) + return hooked_function_entry.original_function + end + end + else + if hook_entry.is_enabled then + hook_entry.exec_function = function(...) + return hook_entry.hook_function(hooked_function_entry.hooks[i - 1].exec_function, ...) + end + else + hook_entry.exec_function = function(...) + return hooked_function_entry.hooks[i - 1].exec_function + end + end + end + end + + if #hooked_function_entry.hooks > 0 then + hooked_function_entry.exec_function = hooked_function_entry.hooks[#hooked_function_entry.hooks].exec_function + else + hooked_function_entry.exec_function = hooked_function_entry.original_function + end + + assert(loadstring(hooked_function_name .. " = HOOKED_FUNCTIONS[" .. hooked_function_entry_index .. "].exec_function"))() + --table.dump(HOOKED_FUNCTIONS, "HOOKED_FUNCTIONS", 3) +end + + +local function modify_hook(mod, hooked_function_name, action) + + if not get_function_by_name(hooked_function_name) then + mod:echo("ERROR: 'hook_".. action .. "' - function [" .. hooked_function_name .. "] doesn't exist", true) + return + end + + local hooked_function_entry, hooked_function_entry_index = get_hooked_function_entry(hooked_function_name) + if not hooked_function_entry then + return + end + + local hook_entry, hook_entry_index = get_hook_entry(mod, hooked_function_entry) + + if hook_entry then + if action == "remove" then + table.remove(hooked_function_entry.hooks, hook_entry_index) + elseif action == "enable" then + hook_entry.is_enabled = true + elseif action == "disable" then + hook_entry.is_enabled = false + end + + update_function_hook_chain(hooked_function_name) + end + + if #hooked_function_entry.hooks == 0 then + table.remove(HOOKED_FUNCTIONS, hooked_function_entry_index) + end + +end + +local function modify_all_hooks(mod, action) + + local no_hooks_functions_indexes = {} + + for i, hooked_function_entry in ipairs(HOOKED_FUNCTIONS) do + for j, hook_entry in ipairs(hooked_function_entry.hooks) do + if hook_entry.mod == mod then + + if action == "remove" then + table.remove(hooked_function_entry.hooks, j) + elseif action == "enable" then + hook_entry.is_enabled = true + elseif action == "disable" then + hook_entry.is_enabled = false + end + + update_function_hook_chain(hooked_function_entry.name) + break + + end + end + + -- can't delete functions entries right away + -- because next function entry will be skiped by 'for' + -- so it have to be done later + if #hooked_function_entry.hooks == 0 then + table.insert(no_hooks_functions_indexes, 1, i) + end + + end + + for _, no_hooks_function_index in ipairs(no_hooks_functions_indexes) do + table.remove(HOOKED_FUNCTIONS, no_hooks_function_index) + end + +end + +-- #################################################################################################################### +-- ##### VMFMod ####################################################################################################### +-- #################################################################################################################### + +VMFMod.hook = function (self, hooked_function_name, hook_function) + + local hooked_function_entry = get_hooked_function_entry(hooked_function_name) or create_hooked_function_entry(hooked_function_name) + if not hooked_function_entry then + self:echo("ERROR: 'hook' - function [" .. hooked_function_name .. "] doesn't exist", true) + return + end + + local hook_entry = get_hook_entry(self, hooked_function_entry) + + -- overwrite existing hook + if hook_entry then + hook_entry.hook_function = hook_function + hook_entry.is_enabled = true + -- create the new one + else + create_hook_entry(self, hooked_function_entry, hook_function) + end + + update_function_hook_chain(hooked_function_name) +end + + +VMFMod.hook_remove = function (self, hooked_function_name) + modify_hook(self, hooked_function_name, "remove") +end + + +VMFMod.hook_disable = function (self, hooked_function_name) + modify_hook(self, hooked_function_name, "disable") +end + + +VMFMod.hook_enable = function (self, hooked_function_name) + modify_hook(self, hooked_function_name, "enable") +end + + +VMFMod.remove_all_hooks = function (self) + modify_all_hooks(self, "remove") +end + + +VMFMod.disable_all_hooks = function (self) + modify_all_hooks(self, "disable") +end + + +VMFMod.enable_all_hooks = function (self) + modify_all_hooks(self, "enable") +end + +-- #################################################################################################################### +-- ##### Event functions ############################################################################################## +-- #################################################################################################################### + +-- removes all hooks when VMF is about to be reloaded +vmf.hooks_unload = function() + for _, hooked_function_entry in ipairs(HOOKED_FUNCTIONS) do + hooked_function_entry.hooks = {} + update_function_hook_chain(hooked_function_entry.name) + end + + HOOKED_FUNCTIONS = {} +end \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/mods.lua b/vmf_source/scripts/mods/vmf/modules/mods.lua new file mode 100644 index 0000000..2cc308f --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/mods.lua @@ -0,0 +1,87 @@ +-- @TODO: on_game_state_changed, show messages in the chat even if they were sent when the chat wasn't initialized +local vmf = nil + +local MODS = {} +local MODS_UNLOADING_ORDER = {} + +-- #################################################################################################################### +-- ##### Public functions ############################################################################################# +-- #################################################################################################################### + +function new_mod(mod_name) + if MODS[mod_name] then + vmf:echo("ERROR: you can't create mod \"" .. mod_name .. "\" because it already exists") + return nil + end + + table.insert(MODS_UNLOADING_ORDER, 1, mod_name) + + local mod = VMFMod:new(mod_name) + MODS[mod_name] = mod + + return mod +end + +function get_mod(mod_name) + return MODS[mod_name] +end + +-- #################################################################################################################### +-- ##### VMFMod ####################################################################################################### +-- #################################################################################################################### + +VMFMod = class(VMFMod) + + +VMFMod.init = function (self, mod_name) + self._name = mod_name +end + + +VMFMod.echo = function (self, message, show_mod_name) + + print("[ECHO][" .. self._name .. "] " .. message) + + if Managers.chat and Managers.chat:has_channel(1) then + if show_mod_name then + message = "[" .. self._name .. "] " .. message + end + Managers.chat:add_local_system_message(1, message, true) + end +end + + +VMFMod.pcall = function (self, ...) + local status, value = pcall(...) + + if not status then + self:echo("ERROR(pcall): " .. tostring(value), true) + end + + return status, value +end + + +-- #################################################################################################################### +-- ##### Event functions ############################################################################################## +-- #################################################################################################################### + +vmf = new_mod("VMF") + +-- call 'unload' for every mod which definded it +vmf.mods_unload = function() + for _, mod_name in pairs(MODS_UNLOADING_ORDER) do --@TODO: maybe ipairs? + if MODS[mod_name].unload then + MODS[mod_name].unload() + end + end +end + +-- call 'update' for every mod which definded it +vmf.mods_update = function(dt) + for _, mod in pairs(MODS) do --@TODO: maybe ipairs? + if mod.update then + mod.update(dt) + end + end +end \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/old_hook_and_console.lua b/vmf_source/scripts/mods/vmf/modules/old_hook_and_console.lua new file mode 100644 index 0000000..3ea531b --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/old_hook_and_console.lua @@ -0,0 +1,256 @@ +Mods={} +--[[ + Mods Hook v2: + New version with better control +--]] + +-- Hook structure +if not MODS_HOOKS then + MODS_HOOKS = {} +end + +local item_template = { + name = "", + func = EMPTY_FUNC, + hooks = {}, +} + +local item_hook_template = { + name = "", + func = EMPTY_FUNC, + enable = false, + exec = EMPTY_FUNC, +} + +Mods.hook = { + -- + -- Set hook + -- + set = function(mod_name, func_name, hook_func) + local item = Mods.hook._get_item(func_name) + local item_hook = Mods.hook._get_item_hook(item, mod_name) + + item_hook.enable = true + item_hook.func = hook_func + + Mods.hook._patch() + end, + + -- + -- Enable/Disable hook + -- + enable = function(value, mod_name, func_name) + for _, item in ipairs(MODS_HOOKS) do + if item.name == func_name or func_name == nil then + for _, hook in ipairs(item.hooks) do + if hook.name == mod_name then + hook.enable = value + Mods.hook._patch() + end + end + end + end + + return + end, + + ["remove"] = function(func_name, mod_name) + for i, item in ipairs(MODS_HOOKS) do + if item.name == func_name then + if mod_name ~= nil then + for j, hook in ipairs(item.hooks) do + if hook.name == mod_name then + table.remove(item.hooks, j) + + Mods.hook._patch() + end + end + else + local item_name = "MODS_HOOKS[" .. tostring(i) .. "]" + + -- Restore orginal function + assert(loadstring(item.name .. " = " .. item_name .. ".func"))() + + -- Remove hook function + table.remove(MODS_HOOKS, i) + + return + end + end + end + + return + end, + + front = function(mod_name, func_name) + for _, item in ipairs(MODS_HOOKS) do + if item.name == func_name or func_name == nil then + for i, hook in ipairs(item.hooks) do + if hook.name == mod_name then + local saved_hook = table.clone(hook) + table.remove(item.hooks, i) + table.insert(item.hooks, saved_hook) + + Mods.hook._patch() + end + end + end + end + + return + end, + + back = function(mod_name, func_name) + for _, item in ipairs(MODS_HOOKS) do + if item.name == func_name or func_name == nil then + local saved_hook = nil + local saved_index = nil + for i, hook in ipairs(item.hooks) do + if hook.name == mod_name then + saved_hook = table.clone(hook) + saved_index = i + break + end + end + if saved_hook then + table.remove(item.hooks, saved_index) + table.insert(item.hooks, 1, saved_hook) + Mods.hook._patch() + end + end + end + + return + end, + + -- + -- Get function by function name + -- + _get_func = function(func_name) + return assert(loadstring("return " .. func_name))() + end, + + -- + -- Get item by function name + -- + _get_item = function(func_name) + -- Find existing item + for _, item in ipairs(MODS_HOOKS) do + if item.name == func_name then + return item + end + end + + -- Create new item + local item = table.clone(item_template) + item.name = func_name + item.func = Mods.hook._get_func(func_name) + + -- Save + table.insert(MODS_HOOKS, item) + + return item + end, + + -- + -- Get item hook by mod name + -- + _get_item_hook = function(item, mod_name) + -- Find existing item + for _, hook in ipairs(item.hooks) do + if hook.name == mod_name then + return hook + end + end + + -- Create new item + local item_hook = table.clone(item_hook_template) + item_hook.name = mod_name + + -- Save + table.insert(item.hooks, 1, item_hook) -- @MINE: why he's inserting it at the beginning? + + return item_hook + end, + + -- + -- If settings are changed the hook itself needs to be updated + -- + _patch = function(mods_hook_item) + for i, item in ipairs(MODS_HOOKS) do + local item_name = "MODS_HOOKS[" .. i .. "]" + + local last_j = 1 + for j, hook in ipairs(item.hooks) do + local hook_name = item_name .. ".hooks[" .. j .. "]" + local before_hook_name = item_name .. ".hooks[" .. (j - 1) .. "]" + + if j == 1 then + if hook.enable then + assert( + loadstring( + hook_name .. ".exec = function(...)" .. + " return " .. hook_name .. ".func(" .. item_name .. ".func, ...)" .. + "end" + ) + )() + else + assert( + loadstring( + hook_name .. ".exec = function(...)" .. + " return " .. item_name .. ".func(...)" .. + "end" + ) + )() + end + else + if hook.enable then + assert( + loadstring( + hook_name .. ".exec = function(...)" .. + " return " .. hook_name .. ".func(" .. before_hook_name .. ".exec, ...)" .. + "end" + ) + )() + else + assert( + loadstring( + hook_name .. ".exec = function(...)" .. + " return " .. before_hook_name .. ".exec(...)" .. + "end" + ) + )() + end + end + + last_j = j + end + + -- Patch orginal function call + assert(loadstring(item.name .. " = " .. item_name .. ".hooks[" .. last_j .. "].exec"))() + end + end, +} + +-- #################################################################################################################### +-- ##### CONSOLE ###################################################################################################### +-- #################################################################################################################### + +if not CONSOLE_ENABLED then + CONSOLE_ENABLED = false +end + +local console_is_active = true +Mods.hook.set("DevConsole", "print", function(func, ...) + if console_is_active then + CommandWindow.print(...) + func(...) + else + func(...) + end +end) + +if CONSOLE_ENABLED == false then + CommandWindow.open("Development command window") + CONSOLE_ENABLED = true +end \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/settings.lua b/vmf_source/scripts/mods/vmf/modules/settings.lua new file mode 100644 index 0000000..5ccec0c --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/settings.lua @@ -0,0 +1,84 @@ +--[[ + This is the settings manager. + * It operates settings within the mod namespace (you can define settings with the same name for different mods) + * Settings location: "%AppData%\Roaming\Fatshark\Warhammer End Times Vermintide\user_settings.config" + * All settings are being saved to the settings-file only when map changes +--]] + +local vmf = get_mod("VMF") + +local MODS_SETTINGS = {} +local THERE_ARE_UNSAVED_CHANGES = false + +-- #################################################################################################################### +-- ##### Private functions ############################################################################################ +-- #################################################################################################################### + +local function load_settings(mod_name) + local mod_settings = Application.user_setting(mod_name) + + mod_settings = mod_settings or {} + + MODS_SETTINGS[mod_name] = mod_settings +end + +local function save_all_settings() + + if THERE_ARE_UNSAVED_CHANGES then + Application.save_user_settings() + + THERE_ARE_UNSAVED_CHANGES = false + end +end + +-- #################################################################################################################### +-- ##### VMFMod ####################################################################################################### +-- #################################################################################################################### + +--[[ + * setting_name [string] : setting name, can contain any characters lua-string can @TODO: check this + * setting_value [anything]: setting value, will be serialized to SJSON format, so you can save whole tables + * call_setting_changed_event [bool] : if 'true', when some setting will be changed, 'setting_changed' event will be called (if mod defined one) +--]] +VMFMod.set = function (self, setting_name, setting_value, call_setting_changed_event) + + local mod_name = self._name + + if not MODS_SETTINGS[mod_name] then + load_settings(mod_name) + end + + local mod_settings = MODS_SETTINGS[mod_name] + mod_settings[setting_name] = setting_value + + Application.set_user_setting(mod_name, mod_settings) + + THERE_ARE_UNSAVED_CHANGES = true + + if call_setting_changed_event and self.setting_changed then + self.setting_changed(setting_name) + end +end + +--[[ + * setting_name [string]: setting name, can contain any characters lua-string can @TODO: check this +--]] +VMFMod.get = function (self, setting_name) + local mod_name = self._name + + if not MODS_SETTINGS[mod_name] then + load_settings(mod_name) + end + + local mod_settings = MODS_SETTINGS[mod_name] + + return mod_settings[setting_name] +end + +-- #################################################################################################################### +-- ##### Event functions ############################################################################################## +-- #################################################################################################################### + +vmf.save_unsaved_settings_to_file = function() + save_all_settings() +end \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/testing_stuff_here.lua b/vmf_source/scripts/mods/vmf/modules/testing_stuff_here.lua new file mode 100644 index 0000000..9c93abf --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/testing_stuff_here.lua @@ -0,0 +1,97 @@ +local mod = new_mod("test_mod") +--[[ + mod:hook("GenericAmmoUserExtension.update", function(func, self, unit, input, dt, context, t) + func(self, unit, input, dt, context, t) + print("333") + end) + --mod:hook_disable("GenericAmmoUserExtension.update") + --mod:hook_enable("GenericAmmoUserExtension.update") + --mod:hook_disable("GenericAmmoUserExtension.update") + --mod:hook_remove("GenericAmmoUserExtension.update") + mod:hook("MatchmakingManager.update", function(func, ...) + func(...) + print("555") + end) +--]] + --mod:disable_all_hooks() + --mod:enable_all_hooks() + --mod:remove_all_hooks() + + --mod:hook_remove("GenericAmmoUserExtension.update") + --mod:hook_remove("MatchmakingManager.update") + --table.dump(HOOKED_FUNCTIONS, "HOOKED_FUNCTIONS", 3) + + + --mod.unload = function() + -- print("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA") + --end + + --mod:pcall(function() + -- return assert(loadstring("return bla.bla"))() + -- end) + + +local options_widgets = { + { + ["setting_name"] = "game_mode", + ["widget_type"] = "stepper", + ["text"] = "Game mode", + ["tooltip"] = "Pick the goddamn game mode" .. "\n" .. + "you litle bitch", + ["options"] = { + {--[[1]] text = "Vanilla", value = "vanilla"}, + {--[[2]] text = "Onslaught", value = "onslaught"}, + {--[[3]] text = "Hide'and'Seek", value = "hide_n_seek"}, + {--[[4]] text = "Death Wish", value = "deathwish"}, + {--[[5]] text = "Legendary", value = "legendary"}, + }, + ["default_value"] = "hide_n_seek", + ["sub_widgets"] = { + { + ["show_widget_condition"] = {3, 4, 5}, + + ["setting_name"] = "enable_god_mode", + ["widget_type"] = "checkbox", + ["text"] = "Enable God Mode", + ["tooltip"] = "Can't do it without cheats," .. "\n" .. + "you poor guy?", + ["default_value"] = false + }, + { + ["show_widget_condition"] = {2, 3, 4, 5}, + + ["setting_name"] = "warn_others", + ["widget_type"] = "checkbox", + ["text"] = "Warn joining players about game mode", + ["tooltip"] = "You don't want others to ruin your game," .. "\n" .. + "do you?", + ["default_value"] = true -- Default first option is enabled. In this case Below + } + } + }, + { + ["setting_name"] = "git_gut", + ["widget_type"] = "checkbox", + ["text"] = "Git Gut", + ["tooltip"] = "Get better at this game," .. "\n" .. + "mkay?", + ["default_value"] = true -- Default first option is enabled. In this case Below + } + } + + mod:create_options(options_widgets, true, "Salvage on the Loottable", "Mod description") + + local mod = new_mod("test_mod2") + mod:create_options(options_widgets, true, "Bots Improvements", "Mod description") + + local mod = new_mod("test_mod3") + mod:create_options(options_widgets, true, "Show Healhbars", "Mod description") + + local mod = new_mod("test_mod4") + mod:create_options(options_widgets, true, "Ammo Meter", "Mod description") + + local mod = new_mod("test_mod5") + mod:create_options(options_widgets, true, "Show Damage", "Mod description") + + local mod = new_mod("test_mod6") + mod:create_options(options_widgets, true, "Kick & Ban", "Mod description") \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/vmf_options_view.lua b/vmf_source/scripts/mods/vmf/modules/vmf_options_view.lua new file mode 100644 index 0000000..ff9b695 --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/vmf_options_view.lua @@ -0,0 +1,1815 @@ +--[[ + Don't set settings to the values which aren't defined in options + + + + + VMFOptionsView.update_picked_option_for_settings_list_widgets +]] +local vmf = get_mod("VMF") -- @TODO: replace it with VMF later + +inject_material("materials/header_background", "header_background", "ingame_ui") +inject_material("materials/header_background_lit", "header_background_lit", "ingame_ui") + +--███████╗ ██████╗███████╗███╗ ██╗███████╗ ██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗███████╗ +--██╔════╝██╔════╝██╔════╝████╗ ██║██╔════╝██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║ ██║██╔════╝ +--███████╗██║ █████╗ ██╔██╗ ██║█████╗ ██║ ███╗██████╔╝███████║██████╔╝███████║███████╗ +--╚════██║██║ ██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██╔══██║██╔═══╝ ██╔══██║╚════██║ +--███████║╚██████╗███████╗██║ ╚████║███████╗╚██████╔╝██║ ██║██║ ██║██║ ██║ ██║███████║ +--╚══════╝ ╚═════╝╚══════╝╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝ + +local scenegraph_definition = { + + sg_root = { + size = {1920, 1080}, + position = {0, 0, UILayer.default + 10}, + is_root = true + }, + + sg_background_border = { + size = {1206, 1056}, + position = {357, 12, 0}, + + parent = "sg_root" + }, + + sg_background_settings_list = { + size = {1200, 1000}, + position = {360, 65, 1}, + + parent = "sg_root" + }, + + sg_mousewheel_scroll_area = { + size = {1200, 1000}, + position = {0, 0, 0}, + + parent = "sg_background_settings_list" + }, + + sg_settings_list_mask = { + size = {1200, 1000}, + position = {0, 0, 2}, + + parent = "sg_background_settings_list" + }, + + sg_settings_list_mask_edge_fade_top = { + size = {1200, 15}, + position = {0, 985, 3}, + + parent = "sg_background_settings_list" + }, + + sg_settings_list_mask_edge_fade_bottom = { + size = {1200, 15}, + position = {0, 0, 3}, + + parent = "sg_background_settings_list" + }, + + sg_background_search_bar = { + size = {1200, 47}, + position = {360, 15, 1}, + + parent = "sg_root" + }, + + sg_scrollbar = { + size = {360, 1050}, + position = {1560, 40, 0}, + + parent = "sg_root" + }, + + sg_dead_space_filler = { + size = {1920, 1080}, + position = {0, 0, 0}, + scale = "fit" -- WHY? + } +} + + +--███╗ ███╗███████╗███╗ ██╗██╗ ██╗ ██╗ ██╗██╗██████╗ ██████╗ ███████╗████████╗███████╗ +--████╗ ████║██╔════╝████╗ ██║██║ ██║ ██║ ██║██║██╔══██╗██╔════╝ ██╔════╝╚══██╔══╝██╔════╝ +--██╔████╔██║█████╗ ██╔██╗ ██║██║ ██║ ██║ █╗ ██║██║██║ ██║██║ ███╗█████╗ ██║ ███████╗ +--██║╚██╔╝██║██╔══╝ ██║╚██╗██║██║ ██║ ██║███╗██║██║██║ ██║██║ ██║██╔══╝ ██║ ╚════██║ +--██║ ╚═╝ ██║███████╗██║ ╚████║╚██████╔╝ ╚███╔███╔╝██║██████╔╝╚██████╔╝███████╗ ██║ ███████║ +--╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚══╝╚══╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚══════╝ + +local menu_widgets_definition = { + + + static_menu_elements = { + scenegraph_id = "sg_root", + element = { + passes = { + { + pass_type = "rect", + + style_id = "background_border" + }, + { + pass_type = "rect", + + style_id = "background_search_bar" + }, + { + pass_type = "rect", + + style_id = "background_settings_list" + }, + { + pass_type = "texture", + + style_id = "settings_list_mask", + texture_id = "settings_list_mask_texture_id" + }, + { + pass_type = "texture_uv", + + style_id = "settings_list_mask_edge_fade_top", + content_id = "settings_list_mask_edge_fade_top" + }, + { + pass_type = "texture_uv", + + style_id = "settings_list_mask_edge_fade_bottom", + content_id = "settings_list_mask_edge_fade_bottom" + }, + { + pass_type = "rect", + + style_id = "dead_space_filler" + } + } + }, + content = { + settings_list_mask_texture_id = "mask_rect", + + settings_list_mask_edge_fade_top = { + texture_id = "mask_rect_edge_fade", + uvs = {{0, 0}, {1, 1}} + }, + + settings_list_mask_edge_fade_bottom = { + texture_id = "mask_rect_edge_fade", + uvs = {{0, 1}, {1, 0}} + } + }, + style = { + + background_border = { + scenegraph_id = "sg_background_border", + color = {255, 140, 100, 50} + }, + + background_search_bar = { + scenegraph_id = "sg_background_search_bar", + color = {255, 0, 0, 0} + }, + + background_settings_list = { + scenegraph_id = "sg_background_settings_list", + color = {255, 0, 0, 0} + }, + + settings_list_mask = { + scenegraph_id = "sg_settings_list_mask", + color = {255, 255, 255, 255} + }, + + settings_list_mask_edge_fade_top = { + scenegraph_id = "sg_settings_list_mask_edge_fade_top", + color = {255, 255, 255, 255} + }, + + settings_list_mask_edge_fade_bottom = { + scenegraph_id = "sg_settings_list_mask_edge_fade_bottom", + color = {255, 255, 255, 255} + }, + + dead_space_filler = { + scenegraph_id = "sg_dead_space_filler", + color = {150, 0, 0, 0} + } + } + }, + + mousewheel_scroll_area = { + scenegraph_id = "sg_mousewheel_scroll_area", + element = { + passes = { + { + pass_type = "scroll", + -- the function is being called only during scrolls + scroll_function = function (ui_scenegraph, ui_style, ui_content, input_service, scroll_axis) + local scroll_step = ui_content.scroll_step + local current_scroll_value = ui_content.internal_scroll_value + + --current_scroll_value = current_scroll_value + scroll_step*-scroll_axis.y + --ui_content.internal_scroll_value = math.clamp(current_scroll_value, 0, 1) + + ui_content.internal_scroll_value = ui_content.internal_scroll_value - scroll_axis.y + end + } + } + }, + content = { + internal_scroll_value = 0, + scroll_step = 0.01 + }, + style = { + } + }, + + scrollbar = UIWidgets.create_scrollbar(scenegraph_definition.sg_scrollbar.size[2], "sg_scrollbar") +} + + +-- @TODO: make scrollbar full windowed o_O + +menu_widgets_definition.scrollbar.content.scroll_bar_info.bar_height_percentage = 0.5 +menu_widgets_definition.scrollbar.content.scroll_bar_info.old_value = 0 +menu_widgets_definition.scrollbar.content.disable_frame = true +menu_widgets_definition.scrollbar.style.scroll_bar_box.size[1] = 360 -- don't change visual scrollbox size + +menu_widgets_definition.scrollbar.content.button_up_hotspot.disable_button = true +menu_widgets_definition.scrollbar.content.button_down_hotspot.disable_button = true + +-- removing up and down buttons +table.remove(menu_widgets_definition.scrollbar.element.passes, 7) +table.remove(menu_widgets_definition.scrollbar.element.passes, 7) +table.remove(menu_widgets_definition.scrollbar.element.passes, 8) +table.remove(menu_widgets_definition.scrollbar.element.passes, 8) +--table.remove(menu_widgets_definition.scrollbar.element.passes, 7) + + + + + + + + +script_data.ui_debug_hover = false + +local DEBUG_WIDGETS = false + +local SETTINGS_LIST_BIG_WIDGET_SIZE = {1194, 80} +local SETTINGS_LIST_REGULAR_WIDGET_SIZE = {1194, 50} + + +--██╗ ██╗███████╗ █████╗ ██████╗ ███████╗██████╗ +--██║ ██║██╔════╝██╔══██╗██╔══██╗██╔════╝██╔══██╗ +--███████║█████╗ ███████║██║ ██║█████╗ ██████╔╝ +--██╔══██║██╔══╝ ██╔══██║██║ ██║██╔══╝ ██╔══██╗ +--██║ ██║███████╗██║ ██║██████╔╝███████╗██║ ██║ +--╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚═╝ ╚═╝ + + +local function create_header_widget(widget_definition, scenegraph_id, offset_y) + + local widget_size = SETTINGS_LIST_BIG_WIDGET_SIZE + offset_y = offset_y - widget_size[2] + + local definition = { + element = { + passes = { + -- VISUALS + { + pass_type = "texture", + + style_id = "background", + texture_id = "background_texture" + }, + { + pass_type = "texture", + + style_id = "highlight_texture", + texture_id = "highlight_texture", + + content_check_function = function (content) + return content.highlight_hotspot.is_hover + end + }, + { + pass_type = "text", + + style_id = "text", + text_id = "text" + }, + { + pass_type = "texture", + + style_id = "checkbox", + texture_id = "checkbox_texture", + + content_check_function = function (content) + return content.is_checkbox_visible + end + }, + -- HOTSPOTS + { + pass_type = "hotspot", + + style_id = "checkbox_hotspot", + content_id = "checkbox_hotspot", + + content_check_function = function (content) + return content.parent.is_checkbox_visible + end + }, + { + pass_type = "hotspot", + + content_id = "highlight_hotspot" + }, + -- PROCESSING + { + pass_type = "local_offset", + + offset_function = function (ui_scenegraph, ui_style, ui_content, ui_renderer) + + if ui_content.checkbox_hotspot.on_release then + + local mod_name = ui_content.mod_name + local is_mod_suspended = ui_content.is_checkbox_checked + + ui_content.is_checkbox_checked = not ui_content.is_checkbox_checked + + ui_content.callback_mod_suspend_state_changed(mod_name, is_mod_suspended) + end + + ui_content.checkbox_texture = ui_content.is_checkbox_checked and "checkbox_checked" or "checkbox_unchecked" + end + }, + -- DEBUG + { + pass_type = "border", + + content_check_function = function (content, style) + if DEBUG_WIDGETS then + style.thickness = 1 + end + + return DEBUG_WIDGETS + end + }, + { + pass_type = "rect", + + style_id = "debug_middle_line", + content_check_function = function (content) + return DEBUG_WIDGETS + end + } + } + }, + content = { + is_checkbox_checked = true, + is_checkbox_visible = false, + is_widget_visible = true, -- for header it will always be 'true', but I need this variable anyways + + checkbox_texture = "checkbox_unchecked", + highlight_texture = "playerlist_hover", + background_texture = "header_background", + + checkbox_hotspot = {}, + highlight_hotspot = {}, + + text = widget_definition.readable_mod_name, + + mod_name = widget_definition.mod_name, + setting_name = widget_definition.setting_name, + widget_type = widget_definition.widget_type, + }, + style = { + + -- VISUALS + + background = { + size = {widget_size[1], widget_size[2] - 3}, + offset = {0, offset_y + 1, 1}, + masked = true + }, + + highlight_texture = { + size = {widget_size[1], widget_size[2] - 3}, + offset = {0, offset_y + 1, 1}, + color = {255, 255, 255, 255}, + masked = true + }, + + text = { + offset = {60, offset_y + 18, 2}, + font_size = 28, + font_type = "hell_shark_masked", + dynamic_font = true, + text_color = Colors.get_color_table_with_alpha("white", 255) + }, + + checkbox = { + size = {30, 30}, + offset = {widget_size[1] - 180, offset_y + 25, 2}, + masked = true + }, + + -- HOTSPOTS + + checkbox_hotspot = { + size = {80, 80}, + offset = {widget_size[1] - 205, offset_y, 0} + }, + + -- DEBUG + + debug_middle_line = { + size = {widget_size[1], 1}, + offset = {0, (offset_y + widget_size[2]/2) - 1, 3}, + color = {200, 0, 255, 0} + }, + + offset = {0, offset_y, 0}, + size = {widget_size[1], widget_size[2]}, + color = {50, 255, 255, 255} + }, + scenegraph_id = scenegraph_id, + offset = {0, 0, 0} + } + + return UIWidget.init(definition) +end + +-- ██████╗██╗ ██╗███████╗ ██████╗██╗ ██╗██████╗ ██████╗ ██╗ ██╗ +-- ██╔════╝██║ ██║██╔════╝██╔════╝██║ ██╔╝██╔══██╗██╔═══██╗╚██╗██╔╝ +-- ██║ ███████║█████╗ ██║ █████╔╝ ██████╔╝██║ ██║ ╚███╔╝ +-- ██║ ██╔══██║██╔══╝ ██║ ██╔═██╗ ██╔══██╗██║ ██║ ██╔██╗ +-- ╚██████╗██║ ██║███████╗╚██████╗██║ ██╗██████╔╝╚██████╔╝██╔╝ ██╗ +-- ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═════╝╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ + +local function create_checkbox_widget(widget_definition, scenegraph_id, offset_y) + + local widget_size = SETTINGS_LIST_REGULAR_WIDGET_SIZE + offset_y = offset_y - widget_size[2] + + local show_widget_condition = nil + if widget_definition.show_widget_condition then + show_widget_condition = {} + for _, i in ipairs(widget_definition.show_widget_condition) do + show_widget_condition[i] = true + end + end + + local definition = { + element = { + passes = { + -- VISUALS + { + pass_type = "texture", + + style_id = "highlight_texture", + texture_id = "highlight_texture", + content_check_function = function (content) + return content.highlight_hotspot.is_hover + end + }, + { + pass_type = "text", + + style_id = "text", + text_id = "text" + }, + { + pass_type = "texture", + + style_id = "checkbox", + texture_id = "checkbox_texture" + }, + -- HOTSPOTS + { + pass_type = "hotspot", + + style_id = "checkbox_hotspot", + content_id = "checkbox_hotspot" + }, + { + pass_type = "hotspot", + + content_id = "highlight_hotspot" + }, + -- PROCESSING + { + pass_type = "local_offset", + + offset_function = function (ui_scenegraph, ui_style, ui_content, ui_renderer) + + if ui_content.checkbox_hotspot.on_release then + local mod_name = ui_content.mod_name + local setting_name = ui_content.setting_name + local old_value = ui_content.is_checkbox_checked + local new_value = not old_value + + ui_content.is_checkbox_checked = new_value + + ui_content.callback_setting_changed(mod_name, setting_name, old_value, new_value) + end + + ui_content.checkbox_texture = ui_content.is_checkbox_checked and "checkbox_checked" or "checkbox_unchecked" + end + }, + -- DEBUG + { + pass_type = "rect", + + content_check_function = function (content) + return DEBUG_WIDGETS + end + }, + { + pass_type = "border", + + content_check_function = function (content, style) + if DEBUG_WIDGETS then + style.thickness = 1 + end + + return DEBUG_WIDGETS + end + }, + { + pass_type = "rect", + + style_id = "debug_middle_line", + content_check_function = function (content) + return DEBUG_WIDGETS + end + } + } + }, + content = { + is_checkbox_checked = false, + is_widget_visible = true, + + checkbox_texture = "checkbox_unchecked", -- texture name + highlight_texture = "playerlist_hover", -- texture name + + checkbox_hotspot = {}, + highlight_hotspot = {}, + + text = widget_definition.text, + + mod_name = widget_definition.mod_name, + setting_name = widget_definition.setting_name, + widget_type = widget_definition.widget_type, + default_value = widget_definition.default_value, + parent_widget_number = widget_definition.parent_widget_number, + show_widget_condition = show_widget_condition, + }, + style = { + + -- VISUALS + + highlight_texture = { + size = {widget_size[1], widget_size[2] - 3}, + offset = {0, offset_y + 1, 0}, + masked = true + }, + + text = { + offset = {60 + widget_definition.widget_level * 40, offset_y + 5, 0}, + font_size = 28, + font_type = "hell_shark_masked", + dynamic_font = true, + text_color = Colors.get_color_table_with_alpha("white", 255) + }, + + checkbox = { + size = {30, 30}, + offset = {widget_size[1] - 180, offset_y + 10, 2}, + masked = true + }, + + -- HOTSPOTS + + checkbox_hotspot = { + size = {30, 30}, + offset = {widget_size[1] - 180, offset_y + 10, 0}, + }, + + -- DEBUG + + debug_middle_line = { + size = {widget_size[1], 2}, + offset = {0, (offset_y + widget_size[2]/2) - 1, 10}, + color = {200, 0, 255, 0} + }, + + offset = {0, offset_y, 0}, + size = {widget_size[1], widget_size[2]}, + color = {50, 255, 255, 255} + }, + scenegraph_id = scenegraph_id, + offset = {0, 0, 0} + } + + return UIWidget.init(definition) +end + + +--███████╗████████╗███████╗██████╗ ██████╗ ███████╗██████╗ +--██╔════╝╚══██╔══╝██╔════╝██╔══██╗██╔══██╗██╔════╝██╔══██╗ +--███████╗ ██║ █████╗ ██████╔╝██████╔╝█████╗ ██████╔╝ +--╚════██║ ██║ ██╔══╝ ██╔═══╝ ██╔═══╝ ██╔══╝ ██╔══██╗ +--███████║ ██║ ███████╗██║ ██║ ███████╗██║ ██║ +--╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ + +--[[ + new_widget_definition.widget_index = new_widget_index + new_widget_definition.widget_level = level + new_widget_definition.mod_name = self._name + new_widget_definition.setting_name = current_widget.setting_name + new_widget_definition.text = current_widget.text + new_widget_definition.tooltip = current_widget.tooltip + new_widget_definition.options = current_widget.options + new_widget_definition.default = current_widget.default + new_widget_definition.show_widget_condition = current_widget.show_widget_condition + new_widget_definition.parent_widget_number = parent_number +]] + +local function create_stepper_widget(widget_definition, scenegraph_id, offset_y, options) + + local widget_size = SETTINGS_LIST_REGULAR_WIDGET_SIZE + offset_y = offset_y - widget_size[2] + + local show_widget_condition = nil + if widget_definition.show_widget_condition then + show_widget_condition = {} + for _, i in ipairs(widget_definition.show_widget_condition) do + show_widget_condition[i] = true + end + end + + local options_texts = {} + local options_values = {} + + for _, option in ipairs(widget_definition.options) do + table.insert(options_texts, option.text) + table.insert(options_values, option.value) + end + + local definition = { + element = { + passes = { + -- VISUALS + { + pass_type = "texture", + + style_id = "highlight_texture", + texture_id = "highlight_texture", + content_check_function = function (content) + return content.highlight_hotspot.is_hover + end + }, + { + pass_type = "text", + + style_id = "text", + text_id = "text" + }, + { + pass_type = "texture", + + style_id = "left_arrow", + texture_id = "left_arrow_texture" + }, + { + pass_type = "text", + + style_id = "current_option_text", + text_id = "current_option_text" + }, + { + pass_type = "rotated_texture", + + style_id = "right_arrow", + texture_id = "right_arrow_texture" + }, + -- HOTSPOTS + { + pass_type = "hotspot", + + content_id = "highlight_hotspot" + }, + { + pass_type = "hotspot", + + style_id = "left_arrow_hotspot", + content_id = "left_arrow_hotspot" + }, + { + pass_type = "hotspot", + + style_id = "right_arrow_hotspot", + content_id = "right_arrow_hotspot" + }, + -- PROCESSING + { + pass_type = "local_offset", + + offset_function = function (ui_scenegraph, ui_style, ui_content, ui_renderer) + ui_content.left_arrow_texture = ui_content.left_arrow_hotspot.is_hover and "settings_arrow_clicked" or "settings_arrow_normal" + ui_content.right_arrow_texture = ui_content.right_arrow_hotspot.is_hover and "settings_arrow_clicked" or "settings_arrow_normal" + + if ui_content.left_arrow_hotspot.on_release or ui_content.right_arrow_hotspot.on_release then + local mod_name = ui_content.mod_name + local setting_name = ui_content.setting_name + local old_value = ui_content.options_values[ui_content.current_option_number] + local new_option_number = nil + + if ui_content.left_arrow_hotspot.on_release then + new_option_number = ((ui_content.current_option_number - 1) == 0) and ui_content.total_options_number or (ui_content.current_option_number - 1) + else + new_option_number = ((ui_content.current_option_number + 1) == (ui_content.total_options_number + 1)) and 1 or (ui_content.current_option_number + 1) + end + + ui_content.current_option_number = new_option_number + ui_content.current_option_text = ui_content.options_texts[new_option_number] + + local new_value = ui_content.options_values[new_option_number] + ui_content.callback_setting_changed(mod_name, setting_name, old_value, new_value) + end + end + }, + -- DEBUG + { + pass_type = "rect", + + content_check_function = function (content) + return DEBUG_WIDGETS + end + }, + { + pass_type = "border", + + content_check_function = function (content, style) + if DEBUG_WIDGETS then + style.thickness = 1 + end + + return DEBUG_WIDGETS + end + }, + { + pass_type = "rect", + + style_id = "debug_middle_line", + content_check_function = function (content) + return DEBUG_WIDGETS + end + } + } + }, + content = { + is_widget_visible = true, + + highlight_texture = "playerlist_hover", -- texture name + left_arrow_texture = "settings_arrow_normal", + right_arrow_texture = "settings_arrow_normal", + + highlight_hotspot = {}, + left_arrow_hotspot = {}, + right_arrow_hotspot = {}, + + text = widget_definition.text, + + mod_name = widget_definition.mod_name, + setting_name = widget_definition.setting_name, + widget_type = widget_definition.widget_type, + + options_texts = options_texts, + options_values = options_values, + total_options_number = #options_texts, + current_option_number = 1, + current_option_text = options_texts[1], + default_value = widget_definition.default_value, + parent_widget_number = widget_definition.parent_widget_number, + show_widget_condition = show_widget_condition, + }, + style = { + + -- VISUALS + + highlight_texture = { + size = {widget_size[1], widget_size[2] - 3}, + offset = {0, offset_y + 1, 0}, + masked = true + }, + + text = { + offset = {60 + widget_definition.widget_level * 40, offset_y + 5, 2}, + font_size = 28, + font_type = "hell_shark_masked", + dynamic_font = true, + text_color = Colors.get_color_table_with_alpha("white", 255) + }, + + left_arrow = { + size = {28, 34}, + offset = {widget_size[1] - 300, offset_y + 8, 2}, + --color = {255, 255, 255, 255}, + masked = true + }, + + right_arrow = { + size = {28, 34}, + offset = {widget_size[1] - 60, offset_y + 8, 2}, + masked = true, + --color = {255, 255, 255, 255}, + angle = math.pi, + pivot = {14, 17} + }, + + current_option_text = { + offset = {widget_size[1] - 165, offset_y + 4, 3}, + horizontal_alignment = "center", + font_size = 28, + font_type = "hell_shark_masked", + dynamic_font = true, + text_color = Colors.get_color_table_with_alpha("cheeseburger", 255) + }, + -- HOTSPOTS + + left_arrow_hotspot = { + size = {28, 34}, + offset = {widget_size[1] - 300, offset_y + 7, 0} + }, + + right_arrow_hotspot = { + size = {28, 34}, + offset = {widget_size[1] - 60, offset_y + 7, 0} + }, + + -- DEBUG + + debug_middle_line = { + size = {widget_size[1], 2}, + offset = {0, (offset_y + widget_size[2]/2) - 1, 10}, + color = {200, 0, 255, 0} + }, + + offset = {0, offset_y, 0}, + size = {widget_size[1], widget_size[2]}, + color = {50, 255, 255, 255} + }, + scenegraph_id = scenegraph_id, + offset = {0, 0, 0} + } + + return UIWidget.init(definition) +end + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +-- ██████╗██╗ █████╗ ███████╗███████╗ +--██╔════╝██║ ██╔══██╗██╔════╝██╔════╝ +--██║ ██║ ███████║███████╗███████╗ +--██║ ██║ ██╔══██║╚════██║╚════██║ +--╚██████╗███████╗██║ ██║███████║███████║ +-- ╚═════╝╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝ + +local SETTINGS_LIST_WIDGETS_DEFINITIONS = {} -- numerical sorting [ipairs] + + +VMFOptionsView = class(VMFOptionsView) +VMFOptionsView.init = function (self, ingame_ui_context) + + self.current_setting_list_offset_y = 0 -- [int] + self.max_setting_list_offset_y = nil -- [int] + self.setting_list_mask_size_y = nil -- [int] + self.scroll_step = 40 -- [int] + + self.is_setting_changes_applied_immidiately = true + + self.menu_widgets = nil -- [table] + self.settings_list_widgets = nil -- [table] + + self.definitions = {} + self.definitions.scenegraph = scenegraph_definition + self.definitions.menu_widgets = menu_widgets_definition + self.definitions.settings_list_widgets = SETTINGS_LIST_WIDGETS_DEFINITIONS + + -- get necessary things for the rendering + self.ui_renderer = ingame_ui_context.ui_renderer + self.render_settings = {snap_pixel_positions = true} + self.ingame_ui = ingame_ui_context.ingame_ui + + -- create the input service + local input_manager = ingame_ui_context.input_manager + input_manager:create_input_service("vmf_options_menu", "IngameMenuKeymaps", "IngameMenuFilters") + input_manager:map_device_to_service("vmf_options_menu", "keyboard") + input_manager:map_device_to_service("vmf_options_menu", "mouse") + input_manager:map_device_to_service("vmf_options_menu", "gamepad") + self.input_manager = input_manager + + -- wwise_world is used for making sounds (for opening menu, closing menu, etc.) + local world = ingame_ui_context.world_manager:world("music_world") + self.wwise_world = Managers.world:wwise_world(world) + + self:create_ui_elements() +end + + +VMFOptionsView.create_ui_elements = function (self) + + self.menu_widgets = {} + + for name, definition in pairs(self.definitions.menu_widgets) do + self.menu_widgets[name] = UIWidget.init(definition) + end + + self.settings_list_widgets = self:build_settings_list() + + self.ui_scenegraph = UISceneGraph.init_scenegraph(self.definitions.scenegraph) + + self.setting_list_mask_size_y = self.ui_scenegraph.sg_settings_list_mask.size[2] + + self:calculate_scrollbar_size() +end + + +VMFOptionsView.build_settings_list = function (self) + + local scenegraph_id = "sg_settings_list" + local scenegraph_id_start = "sg_settings_list_start" + local list_size_y = 0 + + local all_widgets = {} + local mod_widgets = nil + + for _, mod_settings_list_definition in ipairs(self.definitions.settings_list_widgets) do + + mod_widgets = {} + + for _, definition in ipairs(mod_settings_list_definition) do + + local offset_y = -list_size_y + local widget = nil + local size_y = 0 + local widget_type = definition.widget_type + + if widget_type == "checkbox" then + widget = self:build_checkbox_widget(definition, scenegraph_id_start, offset_y) + elseif widget_type == "stepper" then + widget = self:build_stepper_widget(definition, scenegraph_id_start, offset_y) + elseif widget_type == "header" then + widget = self:build_header_widget(definition, scenegraph_id_start, offset_y) + end + + if widget then + list_size_y = list_size_y + widget.style.size[2] + + table.insert(mod_widgets, widget) + end + end + + table.insert(all_widgets, mod_widgets) + end + + local mask_size = self.definitions.scenegraph.sg_settings_list_mask.size + local mask_size_x = mask_size[1] + local mask_size_y = mask_size[2] + + self.definitions.scenegraph[scenegraph_id] = { + size = {mask_size_x, list_size_y}, + position = {0, 0, -1}, -- changed -1 to 100. nothing changed. seems like it doesn't matter + offset = {0, 0, 0}, + + vertical_alignment = "top", + horizontal_alignment = "center", + + parent = "sg_settings_list_mask" + } + + self.definitions.scenegraph[scenegraph_id_start] = { + size = {1, 1}, + position = {3, 0, 10}, + + vertical_alignment = "top", + horizontal_alignment = "left", + + parent = scenegraph_id + } + + local is_scrolling_enabled = false + local max_offset_y = 0 + + if mask_size_y < list_size_y then + is_scrolling_enabled = true + max_offset_y = list_size_y - mask_size_y + end + + self.menu_widgets["scrollbar"].content.visible = is_scrolling_enabled + self.menu_widgets["mousewheel_scroll_area"].content.visible = is_scrolling_enabled + + self.max_setting_list_offset_y = max_offset_y + self.settings_list_size_y = list_size_y + self.original_settings_list_size_y = list_size_y + self.settings_list_scenegraph_id = scenegraph_id + self.settings_list_scenegraph_id_start = scenegraph_id_start + self.is_scrolling_enabled = is_scrolling_enabled + + return all_widgets +end + +VMFOptionsView.build_header_widget = function (self, definition, scenegraph_id, offset_y) + + local widget = create_header_widget(definition, scenegraph_id, offset_y) + local content = widget.content + content.is_checkbox_checked = true + content.is_checkbox_visible = true + + content.callback_mod_suspend_state_changed = callback(self, "callback_mod_suspend_state_changed") + + return widget +end + +VMFOptionsView.build_stepper_widget = function (self, definition, scenegraph_id, offset_y) + + local widget = create_stepper_widget(definition, scenegraph_id, offset_y) + local content = widget.content + + content.callback_setting_changed = callback(self, "callback_setting_changed") + + return widget +end + +VMFOptionsView.build_checkbox_widget = function (self, definition, scenegraph_id, offset_y) + + local widget = create_checkbox_widget(definition, scenegraph_id, offset_y) + local content = widget.content + + content.callback_setting_changed = callback(self, "callback_setting_changed") + + return widget +end + +VMFOptionsView.rearrange_settings_list = function (self) + + local offset_y = 0 + + for _, mod_widgets in ipairs(self.settings_list_widgets) do + for _, widget in ipairs(mod_widgets) do + if widget.content.is_widget_visible then + widget.offset[2] = offset_y + else + offset_y = offset_y + ((widget.content.widget_type == "header") and SETTINGS_LIST_BIG_WIDGET_SIZE[2] or SETTINGS_LIST_REGULAR_WIDGET_SIZE[2]) + end + end + end + + local list_size_y = self.original_settings_list_size_y - offset_y + local mask_size_y = self.setting_list_mask_size_y + local is_scrolling_enabled = false + local max_offset_y = 0 + + if mask_size_y < list_size_y then + is_scrolling_enabled = true + max_offset_y = list_size_y - mask_size_y + end + + self.ui_scenegraph[self.settings_list_scenegraph_id].size[2] = list_size_y + + self.max_setting_list_offset_y = max_offset_y + self.settings_list_size_y = list_size_y + self.current_setting_list_offset_y = math.clamp(self.current_setting_list_offset_y, 0, max_offset_y) + + + self.menu_widgets["scrollbar"].content.visible = is_scrolling_enabled + self.menu_widgets["mousewheel_scroll_area"].content.visible = is_scrolling_enabled + + if is_scrolling_enabled then + self:calculate_scrollbar_size() + end +end + + + +VMFOptionsView.callback_setting_changed = function (self, mod_name, setting_name, old_value, new_value) + vmf:echo("CHANGED: " .. mod_name .. " " .. setting_name .. " " .. tostring(old_value) .. " " .. tostring(new_value)) + + if self.is_setting_changes_applied_immidiately then + get_mod(mod_name):set(setting_name, new_value, true) + end + + self:update_settings_list_widgets_visibility(mod_name) + self:rearrange_settings_list() +end + +VMFOptionsView.callback_mod_suspend_state_changed = function (self, mod_name, is_suspended) + vmf:echo("SUSPENDED: " .. mod_name .. " " .. tostring(is_suspended)) + + local mod_suspend_state_list = vmf:get("mod_suspend_state_list") + + mod_suspend_state_list[mod_name] = is_suspended + + vmf:set("mod_suspend_state_list", mod_suspend_state_list) + + local mod = get_mod(mod_name) + + if is_suspended then + if mod.suspended then + mod.suspended() + else + mod:echo("ERROR: suspending from options menu is specified, but function 'mod.suspended()' is not defined", true) + end + else + if mod.unsuspended then + mod.unsuspended() + else + mod:echo("ERROR: suspending from options menu is specified, but function 'mod.unsuspended()' is not defined", true) + end + end + + self:update_settings_list_widgets_visibility(mod_name) + self:rearrange_settings_list() +end + + +VMFOptionsView.update_picked_option_for_settings_list_widgets = function (self) + + local widget_content = nil + local widget_type = nil + local loaded_setting_value = nil + + for _, mod_widgets in ipairs(self.settings_list_widgets) do + for _, widget in ipairs(mod_widgets) do + + widget_content = widget.content + widget_type = widget_content.widget_type + + if widget_type == "checkbox" then + + loaded_setting_value = get_mod(widget_content.mod_name):get(widget_content.setting_name) + + if type(loaded_setting_value) == "boolean" then + widget_content.is_checkbox_checked = loaded_setting_value + else + -- @TODO: echo error? + widget_content.is_checkbox_checked = widget_content.default_value + get_mod(widget_content.mod_name):set(widget_content.setting_name, widget_content.default_value) + end + + elseif widget_type == "stepper" then + + loaded_setting_value = get_mod(widget_content.mod_name):get(widget_content.setting_name) + + local success = false + + for i, option_value in ipairs(widget_content.options_values) do + + if loaded_setting_value == option_value then + widget_content.current_option_number = i + widget_content.current_option_text = widget_content.options_texts[i] + success = true + end + end + + if not success then + for i, option_value in ipairs(widget_content.options_values) do + + if widget_content.default_value == option_value then + widget_content.current_option_number = i + widget_content.current_option_text = widget_content.options_texts[i] + get_mod(widget_content.mod_name):set(widget_content.setting_name, widget_content.default_value) + end + end + end + + elseif widget_type == "header" then + + loaded_setting_value = vmf:get("mod_suspend_state_list") + + local is_mod_suspended = loaded_setting_value[widget_content.mod_name] + if type(is_mod_suspended) == "boolean" then + widget_content.is_checkbox_checked = not is_mod_suspended + else + -- @TODO: echo error? + widget_content.is_checkbox_checked = true + loaded_setting_value[widget_content.mod_name] = true + vmf:set("mod_suspend_state_list", loaded_setting_value) + end + end + end + end +end + +VMFOptionsView.update_settings_list_widgets_visibility = function (self, mod_name) + + --table.dump(self.settings_list_widgets, "WIDGETSSSSSSSS", 3) + + for _, mod_widgets in ipairs(self.settings_list_widgets) do + + if not mod_name or mod_widgets[1].content.mod_name == mod_name then + + for _, widget in ipairs(mod_widgets) do + + if widget.content.parent_widget_number then + local parent_widget = mod_widgets[widget.content.parent_widget_number] + + -- if 'header' or 'checkbox' + if parent_widget.style.checkbox then + + widget.content.is_widget_visible = parent_widget.content.is_checkbox_checked and parent_widget.content.is_widget_visible + + -- if 'stepper' + else + if widget.content.show_widget_condition then + widget.content.is_widget_visible = widget.content.show_widget_condition[parent_widget.content.current_option_number] and parent_widget.content.is_widget_visible + else + get_mod(widget.content.mod_name):echo("ERROR: the stepper widget in the options menu has sub_widgets, but some of its sub_widgets doesn't have 'show_widget_condition'", true) + end + end + end + + end + end + + end +end + + + + + + + + +VMFOptionsView.update = function (self, dt) + if self.suspended then + return + end + + if self.is_scrolling_enabled then + self:update_scrollbar() + + self:update_mouse_scroll_input(false) + end + + self.draw_widgets(self, dt) + + + + local input_manager = self.input_manager + local input_service = input_manager:get_service("vmf_options_menu") + if input_service.get(input_service, "toggle_menu") then + --self.ingame_ui:transition_with_fade("ingame_menu") + self.ingame_ui:handle_transition("exit_menu") + end + + return +end + + +VMFOptionsView.draw_widgets = function (self, dt) + local ui_renderer = self.ui_renderer + local ui_scenegraph = self.ui_scenegraph + local input_manager = self.input_manager + local input_service = input_manager.get_service(input_manager, "vmf_options_menu") + + UIRenderer.begin_pass(ui_renderer, ui_scenegraph, input_service, dt, nil, self.render_settings) + + local menu_widgets = self.menu_widgets + + for _, widget in pairs(menu_widgets) do + UIRenderer.draw_widget(ui_renderer, widget) + end + + self:update_settings_list(self.settings_list_widgets, ui_renderer, ui_scenegraph, input_service, dt) + + + UIRenderer.end_pass(ui_renderer) +end + + + +VMFOptionsView.update_settings_list = function (self, settings_list_widgets, ui_renderer, ui_scenegraph, input_service, dt) + + --self.update_scrollbar(self, settings_list, ui_scenegraph) + + + -- instead of self.update_scrollbar: + local scenegraph = ui_scenegraph[self.settings_list_scenegraph_id] + scenegraph.offset[2] = self.current_setting_list_offset_y + ------------------------------------ + local temp_pos_table = {x = 0, y = 0} + + + + local scenegraph_id_start = self.settings_list_scenegraph_id_start + local list_position = UISceneGraph.get_world_position(ui_scenegraph, scenegraph_id_start) + local mask_pos = Vector3.deprecated_copy(UISceneGraph.get_world_position(ui_scenegraph, "sg_settings_list_mask")) + local mask_size = UISceneGraph.get_size(ui_scenegraph, "sg_settings_list_mask") + + for _, mod_widgets in ipairs(settings_list_widgets) do + for _, widget in ipairs(mod_widgets) do + if widget.content.is_widget_visible then + local style = widget.style + local widget_name = widget.name + local size = style.size + local offset = style.offset + --offset[2] = offset[2] + widget.offset[2] + temp_pos_table.x = list_position[1] + offset[1] + temp_pos_table.y = list_position[2] + offset[2] + widget.offset[2] + local lower_visible = math.point_is_inside_2d_box(temp_pos_table, mask_pos, mask_size) + temp_pos_table.y = temp_pos_table.y + size[2]/2 + local middle_visible = math.point_is_inside_2d_box(temp_pos_table, mask_pos, mask_size) + temp_pos_table.y = temp_pos_table.y + size[2]/2 + local top_visible = math.point_is_inside_2d_box(temp_pos_table, mask_pos, mask_size) + local visible = lower_visible or top_visible + --widget.content.visible = visible + if visible then + UIRenderer.draw_widget(ui_renderer, widget) + end + end + end + end +end + +--@TODO: refactor +-- 'ignore_mousewheel_scroll' - 'true' if user is changing the scrollbar in the meantime +VMFOptionsView.update_mouse_scroll_input = function (self, ignore_mousewheel_scroll) + local widget_content = self.menu_widgets["mousewheel_scroll_area"].content + + local mouse_scroll_value = widget_content.internal_scroll_value + + if mouse_scroll_value ~= 0 then + + local new_offset = self.current_setting_list_offset_y + mouse_scroll_value * self.scroll_step + + self.current_setting_list_offset_y = math.clamp(new_offset, 0, self.max_setting_list_offset_y) + + widget_content.internal_scroll_value = 0 + + self:set_scrollbar_value() + end +end + +-- @TODO: refactor +VMFOptionsView.set_scrollbar_value = function (self) + + local widget_content = self.menu_widgets["scrollbar"].content + + local percentage = self.current_setting_list_offset_y / self.max_setting_list_offset_y + + widget_content.scroll_bar_info.value = percentage + widget_content.scroll_bar_info.old_value = percentage +end + +-- @TODO: refactor + +VMFOptionsView.calculate_scrollbar_size = function (self) + + local widget_content = self.menu_widgets["scrollbar"].content + + local percentage = self.setting_list_mask_size_y / self.settings_list_size_y + + widget_content.scroll_bar_info.bar_height_percentage = percentage +end + +-- if scrollbar was moved, change offset_y +VMFOptionsView.update_scrollbar = function (self) + local scrollbar_info = self.menu_widgets["scrollbar"].content.scroll_bar_info + local value = scrollbar_info.value + local old_value = scrollbar_info.old_value + + if value ~= old_value then + self.current_setting_list_offset_y = self.max_setting_list_offset_y * value + scrollbar_info.old_value = value + end + + return +end + + + + +VMFOptionsView.input_service = function (self) + return self.input_manager:get_service("vmf_options_menu") +end + +VMFOptionsView.on_enter = function (self) + + local input_manager = self.input_manager + input_manager.block_device_except_service(input_manager, "vmf_options_menu", "keyboard", 1) + input_manager.block_device_except_service(input_manager, "vmf_options_menu", "mouse", 1) + input_manager.block_device_except_service(input_manager, "vmf_options_menu", "gamepad", 1) + + WwiseWorld.trigger_event(self.wwise_world, "Play_hud_map_open") + + self.menu_active = true + + self:update_picked_option_for_settings_list_widgets() + self:update_settings_list_widgets_visibility() + self:rearrange_settings_list() +end + +VMFOptionsView.on_exit = function (self) + + WwiseWorld.trigger_event(self.wwise_world, "Play_hud_map_close") + + self.exiting = nil + self.menu_active = nil + + return +end + +VMFOptionsView.exit = function (self, return_to_game) + + local exit_transition = (return_to_game and "exit_menu") or "ingame_menu" + + self.ingame_ui:transition_with_fade(exit_transition) + + self.exiting = true + + return +end + + +-- i'm not really sure if suspend and unsuspend are needed: +-- +-- StateInGameRunning.gm_event_end_conditions_met -> +-- IngameUI.suspend_active_view -> +-- XXXXXXX.suspend +VMFOptionsView.suspend = function (self) + self.suspended = true + + self.input_manager:device_unblock_all_services("keyboard", 1) + self.input_manager:device_unblock_all_services("mouse", 1) + self.input_manager:device_unblock_all_services("gamepad", 1) + + return +end +VMFOptionsView.unsuspend = function (self) + self.suspended = nil + + self.input_manager:block_device_except_service("vmf_options_menu", "keyboard", 1) + self.input_manager:block_device_except_service("vmf_options_menu", "mouse", 1) + self.input_manager:block_device_except_service("vmf_options_menu", "gamepad", 1) + + return +end + + + + + + +-- #################################################################################################################### +-- ##### VMFMod ####################################################################################################### +-- #################################################################################################################### + +local function check_widget_definition(mod, widget) + +end + +--[[ + +--]] +VMFMod.create_options = function (self, widgets_definition, is_mod_toggable, readable_mod_name, mod_description) + -- Yeah, it's kinda complicated, but it's working, mkay? + table.dump(widgets_definition, "options_widgets", 3) -- @TODO: remove it later + + local mod_settings_list_widgets_definitions = {} + + local new_widget_definition = nil + local new_widget_index = nil + + -- defining header widget + + new_widget_index = 1 + + new_widget_definition = {} + + new_widget_definition.widget_type = "header" + new_widget_definition.widget_index = new_widget_index + new_widget_definition.mod_name = self._name + new_widget_definition.readable_mod_name = readable_mod_name or self._name + new_widget_definition.tooltip = mod_description + new_widget_definition.default = true + + local mod_suspend_state_list = vmf:get("mod_suspend_state_list") + mod_suspend_state_list = (type(mod_suspend_state_list) == "table") and mod_suspend_state_list or {} + if type(mod_suspend_state_list[self._name]) == "nil" then + mod_suspend_state_list[self._name] = false + vmf:set("mod_suspend_state_list", mod_suspend_state_list) + end + + table.insert(mod_settings_list_widgets_definitions, new_widget_definition) + + -- defining its subwidgets + + local level = 1 + local parent_number = new_widget_index + local parent_widget = {["widget_type"] = "header", ["sub_widgets"] = widgets_definition} + local current_widget = widgets_definition[1] + local current_widget_index = 1 + + local parent_number_stack = {} + local parent_widget_stack = {} + local current_widget_index_stack = {} + + while new_widget_index <= 256 do + + -- if 'nil', we reached the end of the current level widgets list and need to go up + if current_widget then + + new_widget_index = new_widget_index + 1 + + new_widget_definition = {} + + new_widget_definition.widget_type = current_widget.widget_type + new_widget_definition.widget_index = new_widget_index + new_widget_definition.widget_level = level + new_widget_definition.mod_name = self._name + new_widget_definition.setting_name = current_widget.setting_name + new_widget_definition.text = current_widget.text + new_widget_definition.tooltip = current_widget.tooltip + new_widget_definition.options = current_widget.options + new_widget_definition.default_value = current_widget.default_value + new_widget_definition.show_widget_condition = current_widget.show_widget_condition + new_widget_definition.parent_widget_number = parent_number + + check_widget_definition(self, new_widget_definition) + + if type(self:get(current_widget.setting_name)) == "nil" then + self:set(current_widget.setting_name, current_widget.default_value) + end + + table.insert(mod_settings_list_widgets_definitions, new_widget_definition) + end + + if current_widget and (current_widget.widget_type == "header" or current_widget.widget_type == "checkbox" + or current_widget.widget_type == "stepper") and current_widget.sub_widgets then + + -- going down to the first subwidget + + level = level + 1 + + table.insert(parent_number_stack, parent_number) + parent_number = new_widget_index + + table.insert(parent_widget_stack, parent_widget) + parent_widget = current_widget + + table.insert(current_widget_index_stack, current_widget_index) + current_widget_index = 1 + current_widget = current_widget.sub_widgets[1] + + else + current_widget_index = current_widget_index + 1 + + if parent_widget.sub_widgets[current_widget_index] then + -- going to the next widget + current_widget = parent_widget.sub_widgets[current_widget_index] + else + + -- going up to the widget next to the parent one + + level = level - 1 + + parent_number = table.remove(parent_number_stack) + + parent_widget = table.remove(parent_widget_stack) + + current_widget_index = table.remove(current_widget_index_stack) + + if not current_widget_index then + break + end + + current_widget_index = current_widget_index + 1 + + -- widget next to parent one, or 'nil', if there are no more widgets on this level + current_widget = parent_widget.sub_widgets[current_widget_index] + end + end + end + + if new_widget_index == 257 then -- @TODO: remove it later + vmf:echo("The limit of options was reached. Something's wrong") + end + + table.dump(mod_settings_list_widgets_definitions, "mod_settings_list_widgets_definitions", 3) -- @TODO: remove it later + + table.insert(SETTINGS_LIST_WIDGETS_DEFINITIONS, mod_settings_list_widgets_definitions) +end + + +-- table.insert(t, new, table.remove(t,old)) + +-- mod:create_options(options_widgets, true, "Readable Mod Name", "Mod description") + + + + + + + + --[[ + local t = {10,20,30,40,50,60} + print(table.concat(t, ',')) -- 10,20,30,40,50,60 + table.move(t, 2, 5) + print(table.concat(t, ',')) -- 10,30,40,50,20,60 + + table.move = function(t, old_index, new_index) + return table.insert(t, new_index, table.remove(t, old_index)) + end +]] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +vmf:hook("IngameUI.update", function(func, self, dt, t, disable_ingame_ui, end_of_level_ui) + func(self, dt, t, disable_ingame_ui, end_of_level_ui) + + local end_screen_active = self.end_screen_active(self) + local gdc_build = Development.parameter("gdc") + local input_service = self.input_manager:get_service("ingame_menu") + + if not self.pending_transition(self) and not end_screen_active and not self.menu_active and not self.leave_game and not self.return_to_title_screen and not gdc_build and not self.popup_join_lobby_handler.visible and input_service.get(input_service, "open_vmf_options", true) then + self.handle_transition(self, "vmf_options_view_force") + --vmf:echo("F10") + --MOOD_BLACKBOARD.menu = true + end +--vmf:echo("F10") +end) +--vmf:echo("F10") + +IngameMenuKeymaps.win32.open_vmf_options = { + "keyboard", + "f4", + "pressed" + } + + +local view_data = { + view_name = "vmf_options_view", + view_settings = { + init_view_function = function (ingame_ui_context) + return VMFOptionsView:new(ingame_ui_context) + end, + active = { + inn = true, + ingame = false + }, + blocked_transitions = { + inn = {}, + ingame = { + vmf_options_view = true, + vmf_options_view_force = true + } + }, + hotkey_mapping = { --@TODO: find out what the hell is this -> 'IngameUI.handle_menu_hotkeys' (only in inn -> useless) + --view = "inventory_view", + --error_message = "matchmaking_ready_interaction_message_inventory", + --in_transition = "inventory_view_force", --opening from hotkey + --in_transition_menu = "inventory_view" -- opening from esc menu + }, + hotkey_name = "open_vmf_options", + hotkey = { + "keyboard", + "f9", + "pressed" + }, + transition_fade = false + }, + view_transitions = { + + vmf_options_view = function (self) + self.current_view = "vmf_options_view" + + return + end, + + vmf_options_view_force = function (self) + ShowCursorStack.push() + + self.current_view = "vmf_options_view" + + self.views[self.current_view].exit_to_game = true -- why? + return + end + } +} + +vmf:register_new_view(view_data) + + + +local ingame_ui_exists, ingame_ui = pcall(function () return Managers.player.network_manager.matchmaking_manager.matchmaking_ui.ingame_ui end) +if ingame_ui_exists then + ingame_ui.handle_transition(ingame_ui, "leave_group") +end + + +--vmf:set("what if", {255,10,10,10}) + +--[[ +vmf:hook("OptionsView.update", function(func, self, dt) + func(self, dt) + --self.scroll_field_widget.style.edge_fade_bottom_id.color = {0,0,0,0} + self.scroll_field_widget.style.edge_fade_bottom_id.color = {0,0,0,0} + self.ui_scenegraph["list_mask"].size[2] = 850 +end) + + +vmf:hook("InputManager.change_keybinding", function(func, self, keybinding_table_name, keybinding_table_key, keymap_name, new_button_index, new_device_type, new_state_type) + vmf:echo("keybinding_table_name: " .. keybinding_table_name) + vmf:echo("keybinding_table_key: " .. keybinding_table_key) + vmf:echo("keymap_name: " .. keymap_name) + vmf:echo("new_button_index: " .. new_button_index) + vmf:echo("new_device_type: " .. new_device_type) + vmf:echo("new_state_type: " .. new_state_type) + + local keymaps_data = self.keymaps_data(self, keybinding_table_name) + + --table.dump(keymaps_data, "keymaps_data", 2) + + func(self, keybinding_table_name, keybinding_table_key, keymap_name, new_button_index, new_device_type, new_state_type) +end)]] \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/modules/vmf_options_view_temp.lua b/vmf_source/scripts/mods/vmf/modules/vmf_options_view_temp.lua new file mode 100644 index 0000000..061318b --- /dev/null +++ b/vmf_source/scripts/mods/vmf/modules/vmf_options_view_temp.lua @@ -0,0 +1,1454 @@ +local mod = new_mod("vmf_options_view") -- @TODO: replace it with VMF later + + +--███████╗ ██████╗███████╗███╗ ██╗███████╗ ██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗███████╗ +--██╔════╝██╔════╝██╔════╝████╗ ██║██╔════╝██╔════╝ ██╔══██╗██╔══██╗██╔══██╗██║ ██║██╔════╝ +--███████╗██║ █████╗ ██╔██╗ ██║█████╗ ██║ ███╗██████╔╝███████║██████╔╝███████║███████╗ +--╚════██║██║ ██╔══╝ ██║╚██╗██║██╔══╝ ██║ ██║██╔══██╗██╔══██║██╔═══╝ ██╔══██║╚════██║ +--███████║╚██████╗███████╗██║ ╚████║███████╗╚██████╔╝██║ ██║██║ ██║██║ ██║ ██║███████║ +--╚══════╝ ╚═════╝╚══════╝╚═╝ ╚═══╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚══════╝ + +local scenegraph_definition = { + + sg_root = { + size = {1920, 1080}, + position = {0, 0, UILayer.default + 10}, + is_root = true + }, + + sg_background_border = { + size = {1206, 1056}, + position = {357, 12, 0}, + + parent = "sg_root" + }, + + sg_background_settings_list = { + size = {1200, 1000}, + position = {360, 65, 1}, + + parent = "sg_root" + }, + + sg_mousewheel_scroll_area = { + size = {1200, 1000}, + position = {0, 0, 0}, + + parent = "sg_background_settings_list" + }, + + sg_settings_list_mask = { + size = {1200, 1000}, + position = {0, 0, 2}, + + parent = "sg_background_settings_list" + }, + + sg_settings_list_mask_edge_fade_top = { + size = {1200, 15}, + position = {0, 985, 3}, + + parent = "sg_background_settings_list" + }, + + sg_settings_list_mask_edge_fade_bottom = { + size = {1200, 15}, + position = {0, 0, 3}, + + parent = "sg_background_settings_list" + }, + + sg_background_search_bar = { + size = {1200, 47}, + position = {360, 15, 1}, + + parent = "sg_root" + }, + + sg_scrollbar = { + size = {360, 1050}, + position = {1560, 40, 0}, + + parent = "sg_root" + }, + + sg_dead_space_filler = { + size = {1920, 1080}, + position = {0, 0, 0}, + scale = "fit" -- WHY? + } +} + + +--███╗ ███╗███████╗███╗ ██╗██╗ ██╗ ██╗ ██╗██╗██████╗ ██████╗ ███████╗████████╗███████╗ +--████╗ ████║██╔════╝████╗ ██║██║ ██║ ██║ ██║██║██╔══██╗██╔════╝ ██╔════╝╚══██╔══╝██╔════╝ +--██╔████╔██║█████╗ ██╔██╗ ██║██║ ██║ ██║ █╗ ██║██║██║ ██║██║ ███╗█████╗ ██║ ███████╗ +--██║╚██╔╝██║██╔══╝ ██║╚██╗██║██║ ██║ ██║███╗██║██║██║ ██║██║ ██║██╔══╝ ██║ ╚════██║ +--██║ ╚═╝ ██║███████╗██║ ╚████║╚██████╔╝ ╚███╔███╔╝██║██████╔╝╚██████╔╝███████╗ ██║ ███████║ +--╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚══╝╚══╝ ╚═╝╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚══════╝ + +local menu_widgets_definition = { + + + static_menu_elements = { + scenegraph_id = "sg_root", + element = { + passes = { + { + pass_type = "rect", + + style_id = "background_border" + }, + { + pass_type = "rect", + + style_id = "background_search_bar" + }, + { + pass_type = "rect", + + style_id = "background_settings_list" + }, + { + pass_type = "texture", + + style_id = "settings_list_mask", + texture_id = "settings_list_mask_texture_id" + }, + { + pass_type = "texture_uv", + + style_id = "settings_list_mask_edge_fade_top", + content_id = "settings_list_mask_edge_fade_top" + }, + { + pass_type = "texture_uv", + + style_id = "settings_list_mask_edge_fade_bottom", + content_id = "settings_list_mask_edge_fade_bottom" + }, + { + pass_type = "rect", + + style_id = "dead_space_filler" + } + } + }, + content = { + settings_list_mask_texture_id = "mask_rect", + + settings_list_mask_edge_fade_top = { + texture_id = "mask_rect_edge_fade", + uvs = {{0, 0}, {1, 1}} + }, + + settings_list_mask_edge_fade_bottom = { + texture_id = "mask_rect_edge_fade", + uvs = {{0, 1}, {1, 0}} + } + }, + style = { + + background_border = { + scenegraph_id = "sg_background_border", + color = {255, 140, 100, 50} + }, + + background_search_bar = { + scenegraph_id = "sg_background_search_bar", + color = {255, 0, 0, 0} + }, + + background_settings_list = { + scenegraph_id = "sg_background_settings_list", + color = {255, 0, 0, 0} + }, + + settings_list_mask = { + scenegraph_id = "sg_settings_list_mask", + color = {255, 255, 255, 255} + }, + + settings_list_mask_edge_fade_top = { + scenegraph_id = "sg_settings_list_mask_edge_fade_top", + color = {255, 255, 255, 255} + }, + + settings_list_mask_edge_fade_bottom = { + scenegraph_id = "sg_settings_list_mask_edge_fade_bottom", + color = {255, 255, 255, 255} + }, + + dead_space_filler = { + scenegraph_id = "sg_dead_space_filler", + color = {150, 0, 0, 0} + } + } + }, + + mousewheel_scroll_area = { + scenegraph_id = "sg_mousewheel_scroll_area", + element = { + passes = { + { + pass_type = "scroll", + -- the function is being called only during scrolls + scroll_function = function (ui_scenegraph, ui_style, ui_content, input_service, scroll_axis) + local scroll_step = ui_content.scroll_step + local current_scroll_value = ui_content.internal_scroll_value + + --current_scroll_value = current_scroll_value + scroll_step*-scroll_axis.y + --ui_content.internal_scroll_value = math.clamp(current_scroll_value, 0, 1) + + ui_content.internal_scroll_value = ui_content.internal_scroll_value - scroll_axis.y + end + } + } + }, + content = { + internal_scroll_value = 0, + scroll_step = 0.01 + }, + style = { + } + }, + + scrollbar = UIWidgets.create_scrollbar(scenegraph_definition.sg_scrollbar.size[2], "sg_scrollbar") +} + + +-- @TODO: make scrollbar full windowed o_O + +menu_widgets_definition.scrollbar.content.scroll_bar_info.bar_height_percentage = 0.5 +menu_widgets_definition.scrollbar.content.scroll_bar_info.old_value = 0 +menu_widgets_definition.scrollbar.content.disable_frame = true +menu_widgets_definition.scrollbar.style.scroll_bar_box.size[1] = 360 -- don't change visual scrollbox size + +menu_widgets_definition.scrollbar.content.button_up_hotspot.disable_button = true +menu_widgets_definition.scrollbar.content.button_down_hotspot.disable_button = true + +-- removing up and down buttons +table.remove(menu_widgets_definition.scrollbar.element.passes, 7) +table.remove(menu_widgets_definition.scrollbar.element.passes, 7) +table.remove(menu_widgets_definition.scrollbar.element.passes, 8) +table.remove(menu_widgets_definition.scrollbar.element.passes, 8) +--table.remove(menu_widgets_definition.scrollbar.element.passes, 7) + + + + + + +--████████╗███████╗███╗ ███╗██████╗ +--╚══██╔══╝██╔════╝████╗ ████║██╔══██╗ +-- ██║ █████╗ ██╔████╔██║██████╔╝ +-- ██║ ██╔══╝ ██║╚██╔╝██║██╔═══╝ +-- ██║ ███████╗██║ ╚═╝ ██║██║ +-- ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ + + +script_data.ui_debug_hover = false + +local DEBUG_WIDGETS = false + +local SETTINGS_LIST_BIG_WIDGET_SIZE = {1194, 70} +local SETTINGS_LIST_REGULAR_WIDGET_SIZE = {1194, 50} + + +--[[alright, this is it]] + +local function create_header_widget(text, scenegraph_id, offset_y) + + base_offset[2] = base_offset[2] - SETTINGS_LIST_BIG_WIDGET_SIZE[2] + + local definition = { + element = { + passes = { + { + style_id = "checkbox", + pass_type = "hotspot", + content_id = "hotspot" + }, + { + pass_type = "hotspot", + content_id = "highlight_hotspot" + }, + { + pass_type = "texture", + style_id = "highlight_texture", + texture_id = "highlight_texture", + content_check_function = function (content) + return content.is_highlighted + end + }, + { + pass_type = "local_offset", + offset_function = function (ui_scenegraph, ui_style, ui_content, ui_renderer) + if ui_content.hotspot.on_release then + ui_content.flag = not ui_content.flag + end + + local flag = ui_content.flag + + if flag then + ui_content.checkbox = "checkbox_checked" + else + ui_content.checkbox = "checkbox_unchecked" + end + + return + end + }, + { + pass_type = "texture", + style_id = "checkbox", + texture_id = "checkbox" + }, + { + style_id = "text", + pass_type = "text", + text_id = "text" + }, + { + pass_type = "rect", + content_check_function = function (content) + return DEBUG_WIDGETS + end + }, + { + pass_type = "border", + content_check_function = function (content, style) + if DEBUG_WIDGETS then + style.thickness = 1 + end + + return DEBUG_WIDGETS + end + }, + { + style_id = "debug_middle_line", + pass_type = "rect", + content_check_function = function (content) + return DEBUG_WIDGETS + end + } + } + }, + content = { + flag = false, + checkbox = "checkbox_unchecked", --just a texture name + highlight_texture = "playerlist_hover", + hotspot = {}, + highlight_hotspot = {}, + text = text, + hotspot_content_ids = { + "hotspot" + } + }, + style = { + highlight_texture = { + masked = true, + offset = { + base_offset[1], + base_offset[2], + base_offset[3] + }, + color = Colors.get_table("white"), + size = { + SETTINGS_LIST_BIG_WIDGET_SIZE[1], + SETTINGS_LIST_BIG_WIDGET_SIZE[2] + } + }, + checkbox = { + masked = true, + offset = { + base_offset[1] + 642, + base_offset[2] + 17, + base_offset[3] + }, + size = { + 16, + 16 + } + }, + text = { + font_type = "hell_shark_masked", + dynamic_font = true, + localize = true, + font_size = 28, + offset = { + base_offset[1] + 2, + base_offset[2] + 5, + base_offset[3] + }, + text_color = Colors.get_color_table_with_alpha("white", 255) + }, + offset = { + base_offset[1], + base_offset[2], + base_offset[3] + }, + size = table.clone(SETTINGS_LIST_BIG_WIDGET_SIZE), + color = { + 50, + 255, + 255, + 255 + }, + debug_middle_line = { + offset = { + base_offset[1], + (base_offset[2] + SETTINGS_LIST_BIG_WIDGET_SIZE[2]/2) - 1, + base_offset[3] + 10 + }, + size = { + SETTINGS_LIST_BIG_WIDGET_SIZE[1], + 2 + }, + color = { + 200, + 0, + 255, + 0 + } + } + }, + scenegraph_id = scenegraph_id + } + + return UIWidget.init(definition) +end + + +local function build_header_widget (element, scenegraph_id, base_offset) + --local callback_name = element.callback + --local callback_func = self.make_callback(self, callback_name) + --local saved_value_cb_name = element.saved_value + --local saved_value_cb = callback(self, saved_value_cb_name) + --local setup_name = element.setup + --local flag, text, default_value = self[setup_name](self) + + --fassert(type(flag) == "boolean", "Flag type is wrong, need boolean, got %q", type(flag)) + + local text = "Whatever" + + local widget = create_header_widget(text, scenegraph_id, base_offset) + local content = widget.content + content.flag = true + --content.callback = callback_func + --content.saved_value_cb = saved_value_cb + --content.default_value = default_value + content.callback = function () + return + end + content.saved_value_cb = function () + return + end + + return widget +end + + + + + + + + + + + +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ +------------------------------------------------------------ + +local CHECKBOX_WIDGET_SIZE = { + 795, + 50 +} + +local function create_checkbox_widget(text, scenegraph_id, base_offset) + base_offset[2] = base_offset[2] - CHECKBOX_WIDGET_SIZE[2] + local definition = { + element = { + passes = { + { + style_id = "checkbox", + pass_type = "hotspot", + content_id = "hotspot" + }, + { + pass_type = "hotspot", + content_id = "highlight_hotspot" + }, + { + pass_type = "texture", + style_id = "highlight_texture", + texture_id = "highlight_texture", + content_check_function = function (content) + return content.is_highlighted + end + }, + { + pass_type = "local_offset", + offset_function = function (ui_scenegraph, ui_style, ui_content, ui_renderer) + if ui_content.hotspot.on_release then + ui_content.flag = not ui_content.flag + end + + local flag = ui_content.flag + + if flag then + ui_content.checkbox = "checkbox_checked" + else + ui_content.checkbox = "checkbox_unchecked" + end + + return + end + }, + { + pass_type = "texture", + style_id = "checkbox", + texture_id = "checkbox" + }, + { + style_id = "text", + pass_type = "text", + text_id = "text" + }, + { + pass_type = "rect", + content_check_function = function (content) + return DEBUG_WIDGETS + end + }, + { + pass_type = "border", + content_check_function = function (content, style) + if DEBUG_WIDGETS then + style.thickness = 1 + end + + return DEBUG_WIDGETS + end + }, + { + style_id = "debug_middle_line", + pass_type = "rect", + content_check_function = function (content) + return DEBUG_WIDGETS + end + } + } + }, + content = { + flag = false, + checkbox = "checkbox_unchecked", --just a texture name + highlight_texture = "playerlist_hover", + hotspot = {}, + highlight_hotspot = {}, + text = text, + hotspot_content_ids = { + "hotspot" + } + }, + style = { + highlight_texture = { + masked = true, + offset = { + base_offset[1], + base_offset[2], + base_offset[3] + }, + color = Colors.get_table("white"), + size = { + CHECKBOX_WIDGET_SIZE[1], + CHECKBOX_WIDGET_SIZE[2] + } + }, + checkbox = { + masked = true, + offset = { + base_offset[1] + 642, + base_offset[2] + 17, + base_offset[3] + }, + size = { + 16, + 16 + } + }, + text = { + font_type = "hell_shark_masked", + dynamic_font = true, + localize = true, + font_size = 28, + offset = { + base_offset[1] + 2, + base_offset[2] + 5, + base_offset[3] + }, + text_color = Colors.get_color_table_with_alpha("white", 255) + }, + offset = { + base_offset[1], + base_offset[2], + base_offset[3] + }, + size = table.clone(CHECKBOX_WIDGET_SIZE), + color = { + 50, + 255, + 255, + 255 + }, + debug_middle_line = { + offset = { + base_offset[1], + (base_offset[2] + CHECKBOX_WIDGET_SIZE[2]/2) - 1, + base_offset[3] + 10 + }, + size = { + CHECKBOX_WIDGET_SIZE[1], + 2 + }, + color = { + 200, + 0, + 255, + 0 + } + } + }, + scenegraph_id = scenegraph_id + } + + return UIWidget.init(definition) +end + + +local function build_checkbox_widget (element, scenegraph_id, base_offset) + --local callback_name = element.callback + --local callback_func = self.make_callback(self, callback_name) + --local saved_value_cb_name = element.saved_value + --local saved_value_cb = callback(self, saved_value_cb_name) + --local setup_name = element.setup + --local flag, text, default_value = self[setup_name](self) + + --fassert(type(flag) == "boolean", "Flag type is wrong, need boolean, got %q", type(flag)) + + local text = "Whatever" + + local widget = create_checkbox_widget(text, scenegraph_id, base_offset) + local content = widget.content + content.flag = true + --content.callback = callback_func + --content.saved_value_cb = saved_value_cb + --content.default_value = default_value + content.callback = function () + return + end + content.saved_value_cb = function () + return + end + + return widget +end + + +local function create_simple_texture_widget(texture, texture_size, scenegraph_id, base_offset) + base_offset[2] = base_offset[2] - texture_size[2] + local definition = { + element = { + passes = { + { + texture_id = "texture_id", + style_id = "texture_id", + pass_type = "texture" + } + } + }, + content = { + texture_id = texture + }, + style = { + size = { + texture_size[1], + texture_size[2] + }, + offset = { + base_offset[1], + base_offset[2], + base_offset[3] + }, + texture_id = { + masked = true, -- GOD DAMN IT THIS IS THE FUCKING REASON! + color = { + 255, + 255, + 255, + 255 + }, + offset = { + base_offset[1], + base_offset[2], + base_offset[3] + }, + size = { + texture_size[1], + texture_size[2] + } + } + }, + scenegraph_id = scenegraph_id + } + + return UIWidget.init(definition) +end + +local function build_image(element, scenegraph_id, base_offset) + local widget = create_simple_texture_widget(element.image, element.image_size, scenegraph_id, base_offset) + local content = widget.content + content.callback = function () + return + end + content.saved_value_cb = function () + return + end + content.disabled = true + + return widget +end + +local settings_list_definition = { + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "checkbox" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "header" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "header" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + }, + { + image = "stance_bar_blue", + image_size = {1194, 60}, + callback = "cb_mouse_look_invert_y", + widget_type = "image" + } +} + + + +-- ██████╗██╗ █████╗ ███████╗███████╗ +--██╔════╝██║ ██╔══██╗██╔════╝██╔════╝ +--██║ ██║ ███████║███████╗███████╗ +--██║ ██║ ██╔══██║╚════██║╚════██║ +--╚██████╗███████╗██║ ██║███████║███████║ +-- ╚═════╝╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝ + + + + +VMFOptionsView = class(VMFOptionsView) +VMFOptionsView.init = function (self, ingame_ui_context) + + self.current_setting_list_offset_y = 0 -- [int] + self.max_setting_list_offset_y = nil -- [int] + self.setting_list_mask_size_y = nil -- [int] + self.scroll_step = 10 -- [int] + + self.menu_widgets = nil -- [table] + self.settings_list_widgets = nil -- [table] + + -- get necessary things for rendering + self.ui_renderer = ingame_ui_context.ui_renderer + self.render_settings = {snap_pixel_positions = true} + self.ingame_ui = ingame_ui_context.ingame_ui + + -- create input service + local input_manager = ingame_ui_context.input_manager + input_manager:create_input_service("vmf_options_menu", "IngameMenuKeymaps", "IngameMenuFilters") + input_manager:map_device_to_service("vmf_options_menu", "keyboard") + input_manager:map_device_to_service("vmf_options_menu", "mouse") + input_manager:map_device_to_service("vmf_options_menu", "gamepad") + self.input_manager = input_manager + + -- wwise_world is used for making sounds (for opening menu, closing menu etc) + local world = ingame_ui_context.world_manager:world("music_world") + self.wwise_world = Managers.world:wwise_world(world) + + self:create_ui_elements() +end + + +VMFOptionsView.create_ui_elements = function (self) + + self.menu_widgets = {} + + for name, definition in pairs(menu_widgets_definition) do + self.menu_widgets[name] = UIWidget.init(definition) + end + + self.settings_list_widgets = self:build_settings_list(settings_list_definition) + + self.ui_scenegraph = UISceneGraph.init_scenegraph(scenegraph_definition) + + self.setting_list_mask_size_y = self.ui_scenegraph.sg_settings_list_mask.size[2] + + self:calculate_scrollbar_size() +end + + +VMFOptionsView.build_settings_list = function (self, setting_list_widgets_definition) + + --local scenegraph_definition = definitions.scenegraph_definition + local scenegraph_id = "sg_settings_list" + local scenegraph_id_start = "sg_settings_list_start" + local list_size_y = 0 + local widgets = {} + + for i, definition in ipairs(setting_list_widgets_definition) do + + local element = definition + local base_offset = {0, -list_size_y, 0} + local widget = nil + local size_y = 0 + local widget_type = element.widget_type + + --if widget_type == "drop_down" then + -- widget = self.build_drop_down_widget(self, element, scenegraph_id_start, base_offset) + --elseif widget_type == "slider" then + -- widget = self.build_slider_widget(self, element, scenegraph_id_start, base_offset) + if widget_type == "checkbox" then + widget = build_checkbox_widget(element, scenegraph_id_start, base_offset) + --elseif widget_type == "stepper" then + -- widget = self.build_stepper_widget(self, element, scenegraph_id_start, base_offset) + --elseif widget_type == "keybind" then + -- widget = self.build_keybind_widget(self, element, scenegraph_id_start, base_offset) + elseif widget_type == "image" then + widget = build_image(element, scenegraph_id_start, base_offset) + elseif widget_type == "header" then + widget = build_header_widget(element, scenegraph_id_start, base_offset) + --elseif widget_type == "gamepad_layout" then + -- widget = self.build_gamepad_layout(self, element, scenegraph_id_start, base_offset) + -- self.gamepad_layout_widget = widget + -- local using_left_handed_option = assigned(self.changed_user_settings.gamepad_left_handed, Application.user_setting("gamepad_left_handed")) + + -- self.update_gamepad_layout_widget(self, DefaultGamepadLayoutKeymaps, using_left_handed_option) + --elseif widget_type == "empty" then + -- size_y = element.size_y + --else + -- error("[OptionsView] Unsupported widget type") + end + + if widget then + local name = element.callback + size_y = widget.style.size[2] + widget.type = widget_type + widget.name = name + end + + list_size_y = list_size_y + size_y + + if widget then + if element.name then + widget.name = element.name + end + + table.insert(widgets, widget) + end + end + + local mask_size = scenegraph_definition.sg_settings_list_mask.size + local size_x = mask_size[1] + + scenegraph_definition[scenegraph_id] = { + size = {size_x, list_size_y}, + position = {0, 0, -1}, -- changed -1 to 100. nothing changed. seems like it doesn't matter + offset = {0, 0, 0}, + + vertical_alignment = "top", + horizontal_alignment = "center", + + parent = "sg_settings_list_mask" + } + + scenegraph_definition[scenegraph_id_start] = { + size = {1, 1}, + position = {3, 0, 10}, + + vertical_alignment = "top", + horizontal_alignment = "left", + + parent = scenegraph_id + } + + local scrollbar = false + local max_offset_y = 0 + + if mask_size[2] < list_size_y then + scrollbar = true + max_offset_y = list_size_y - mask_size[2] + end + + self.max_setting_list_offset_y = max_offset_y + self.settings_list_size_y = list_size_y + + local widget_list = { + scenegraph_id = scenegraph_id, + scenegraph_id_start = scenegraph_id_start, + scrollbar = scrollbar, + max_offset_y = max_offset_y, + widgets = widgets + } + + return widget_list +end + + +local temp_pos_table = { + x = 0, + y = 0 +} +VMFOptionsView.update_settings_list = function (self, settings_list, ui_renderer, ui_scenegraph, input_service, dt) + + --self.update_scrollbar(self, settings_list, ui_scenegraph) + + + -- instead of self.update_scrollbar: + local scenegraph = ui_scenegraph[settings_list.scenegraph_id] + scenegraph.offset[2] = self.current_setting_list_offset_y + ------------------------------------ + + local scenegraph_id_start = settings_list.scenegraph_id_start + local list_position = UISceneGraph.get_world_position(ui_scenegraph, scenegraph_id_start) + local mask_pos = Vector3.deprecated_copy(UISceneGraph.get_world_position(ui_scenegraph, "sg_settings_list_mask")) + local mask_size = UISceneGraph.get_size(ui_scenegraph, "sg_settings_list_mask") + --local selected_widget = self.selected_widget + + for i, widget in ipairs(settings_list.widgets) do + + local style = widget.style + local widget_name = widget.name + local size = style.size + local offset = style.offset + temp_pos_table.x = list_position[1] + offset[1] + temp_pos_table.y = list_position[2] + offset[2] + local lower_visible = math.point_is_inside_2d_box(temp_pos_table, mask_pos, mask_size) + temp_pos_table.y = temp_pos_table.y + size[2]/2 + local middle_visible = math.point_is_inside_2d_box(temp_pos_table, mask_pos, mask_size) + temp_pos_table.y = temp_pos_table.y + size[2]/2 + local top_visible = math.point_is_inside_2d_box(temp_pos_table, mask_pos, mask_size) + local visible = lower_visible or top_visible + widget.content.visible = visible + + UIRenderer.draw_widget(ui_renderer, widget) + end +end + +--@TODO: refactor +-- 'ignore_mousewheel_scroll' - 'true' if user is changing the scrollbar in the meantime +VMFOptionsView.update_mouse_scroll_input = function (self, ignore_mousewheel_scroll) + local widget_content = self.menu_widgets["mousewheel_scroll_area"].content + + local mouse_scroll_value = widget_content.internal_scroll_value + + if mouse_scroll_value ~= 0 then + + local new_offset = self.current_setting_list_offset_y + mouse_scroll_value * self.scroll_step + + self.current_setting_list_offset_y = math.clamp(new_offset, 0, self.max_setting_list_offset_y) + + widget_content.internal_scroll_value = 0 + + self:set_scrollbar_value() + end +end + +-- @TODO: refactor +VMFOptionsView.set_scrollbar_value = function (self) + + local widget_content = self.menu_widgets["scrollbar"].content + + local percentage = self.current_setting_list_offset_y / self.max_setting_list_offset_y + + widget_content.scroll_bar_info.value = percentage + widget_content.scroll_bar_info.old_value = percentage +end + +-- @TODO: refactor + +VMFOptionsView.calculate_scrollbar_size = function (self) + + local widget_content = self.menu_widgets["scrollbar"].content + + local percentage = self.setting_list_mask_size_y / self.settings_list_size_y + + widget_content.scroll_bar_info.bar_height_percentage = percentage +end + +-- if scrollbar was moved, change offset_y +VMFOptionsView.update_scrollbar = function (self) + local scrollbar_info = self.menu_widgets["scrollbar"].content.scroll_bar_info + local value = scrollbar_info.value + local old_value = scrollbar_info.old_value + + if value ~= old_value then + self.current_setting_list_offset_y = self.max_setting_list_offset_y * value + scrollbar_info.old_value = value + end + + return +end + + +VMFOptionsView.update = function (self, dt) + if self.suspended then + return + end + + self:update_scrollbar() + + self:update_mouse_scroll_input(false) + + self.draw_widgets(self, dt) + + + + local input_manager = self.input_manager + local input_service = input_manager:get_service("vmf_options_menu") + if input_service.get(input_service, "toggle_menu") then + --self.ingame_ui:transition_with_fade("ingame_menu") + self.ingame_ui:handle_transition("exit_menu") + end + + return +end + +VMFOptionsView.draw_widgets = function (self, dt) + local ui_renderer = self.ui_renderer + local ui_scenegraph = self.ui_scenegraph + local input_manager = self.input_manager + local input_service = input_manager.get_service(input_manager, "vmf_options_menu") + + UIRenderer.begin_pass(ui_renderer, ui_scenegraph, input_service, dt, nil, self.render_settings) + + local menu_widgets = self.menu_widgets + + for _, widget in pairs(menu_widgets) do + UIRenderer.draw_widget(ui_renderer, widget) + end + + self:update_settings_list(self.settings_list_widgets, ui_renderer, ui_scenegraph, input_service, dt) + + + + UIRenderer.end_pass(ui_renderer) + + return +end + +VMFOptionsView.input_service = function (self) + return self.input_manager:get_service("vmf_options_menu") +end + +VMFOptionsView.on_enter = function (self) + + local input_manager = self.input_manager + + input_manager.block_device_except_service(input_manager, "vmf_options_menu", "keyboard", 1) + input_manager.block_device_except_service(input_manager, "vmf_options_menu", "mouse", 1) + input_manager.block_device_except_service(input_manager, "vmf_options_menu", "gamepad", 1) + + + + WwiseWorld.trigger_event(self.wwise_world, "Play_hud_map_open") + + self.menu_active = true + return +end + +VMFOptionsView.on_exit = function (self) + + WwiseWorld.trigger_event(self.wwise_world, "Play_hud_map_close") + + self.exiting = nil + self.menu_active = nil + + return +end + +VMFOptionsView.exit = function (self, return_to_game) + + local exit_transition = (return_to_game and "exit_menu") or "ingame_menu" + + self.ingame_ui:transition_with_fade(exit_transition) + + self.exiting = true + + return +end + + +-- i'm not really sure if suspend and unsuspend are needed: +-- +-- StateInGameRunning.gm_event_end_conditions_met -> +-- IngameUI.suspend_active_view -> +-- XXXXXXX.suspend +VMFOptionsView.suspend = function (self) + self.suspended = true + + self.input_manager:device_unblock_all_services("keyboard", 1) + self.input_manager:device_unblock_all_services("mouse", 1) + self.input_manager:device_unblock_all_services("gamepad", 1) + + return +end +VMFOptionsView.unsuspend = function (self) + self.suspended = nil + + self.input_manager:block_device_except_service("vmf_options_menu", "keyboard", 1) + self.input_manager:block_device_except_service("vmf_options_menu", "mouse", 1) + self.input_manager:block_device_except_service("vmf_options_menu", "gamepad", 1) + + return +end + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +mod:hook("IngameUI.update", function(func, self, dt, t, disable_ingame_ui, end_of_level_ui) + func(self, dt, t, disable_ingame_ui, end_of_level_ui) + + local end_screen_active = self.end_screen_active(self) + local gdc_build = Development.parameter("gdc") + local input_service = self.input_manager:get_service("ingame_menu") + + if not self.pending_transition(self) and not end_screen_active and not self.menu_active and not self.leave_game and not self.return_to_title_screen and not gdc_build and not self.popup_join_lobby_handler.visible and input_service.get(input_service, "open_vmf_options", true) then + self.handle_transition(self, "vmf_options_view_force") + --mod:echo("F10") + --MOOD_BLACKBOARD.menu = true + end +--mod:echo("F10") +end) +--mod:echo("F10") + +IngameMenuKeymaps.win32.open_vmf_options = { + "keyboard", + "f4", + "pressed" + } + + +local view_data = { + view_name = "vmf_options_view", + view_settings = { + init_view_function = function (ingame_ui_context) + return VMFOptionsView:new(ingame_ui_context) + end, + active = { + inn = true, + ingame = false + }, + blocked_transitions = { + inn = {}, + ingame = { + vmf_options_view = true, + vmf_options_view_force = true + } + }, + hotkey_mapping = { --@TODO: find out what the hell is this -> 'IngameUI.handle_menu_hotkeys' (only in inn -> useless) + --view = "inventory_view", + --error_message = "matchmaking_ready_interaction_message_inventory", + --in_transition = "inventory_view_force", --opening from hotkey + --in_transition_menu = "inventory_view" -- opening from esc menu + }, + hotkey_name = "open_vmf_options", + hotkey = { + "keyboard", + "f9", + "pressed" + }, + transition_fade = false + }, + view_transitions = { + + vmf_options_view = function (self) + self.current_view = "vmf_options_view" + + return + end, + + vmf_options_view_force = function (self) + ShowCursorStack.push() + + self.current_view = "vmf_options_view" + + self.views[self.current_view].exit_to_game = true -- why? + return + end + } +} + +mod:register_new_view(view_data) + + +local ingame_ui_exists, ingame_ui = pcall(function () return Managers.player.network_manager.matchmaking_manager.matchmaking_ui.ingame_ui end) +if ingame_ui_exists then + ingame_ui.handle_transition(ingame_ui, "leave_group") +end + + +--[[ +mod:hook("OptionsView.update", function(func, self, dt) + func(self, dt) + --self.scroll_field_widget.style.edge_fade_bottom_id.color = {0,0,0,0} + self.scroll_field_widget.style.edge_fade_bottom_id.color = {0,0,0,0} + self.ui_scenegraph["list_mask"].size[2] = 850 +end) + + +mod:hook("InputManager.change_keybinding", function(func, self, keybinding_table_name, keybinding_table_key, keymap_name, new_button_index, new_device_type, new_state_type) + mod:echo("keybinding_table_name: " .. keybinding_table_name) + mod:echo("keybinding_table_key: " .. keybinding_table_key) + mod:echo("keymap_name: " .. keymap_name) + mod:echo("new_button_index: " .. new_button_index) + mod:echo("new_device_type: " .. new_device_type) + mod:echo("new_state_type: " .. new_state_type) + + local keymaps_data = self.keymaps_data(self, keybinding_table_name) + + --table.dump(keymaps_data, "keymaps_data", 2) + + func(self, keybinding_table_name, keybinding_table_key, keymap_name, new_button_index, new_device_type, new_state_type) +end)]] \ No newline at end of file diff --git a/vmf_source/scripts/mods/vmf/vmf_loader.lua b/vmf_source/scripts/mods/vmf/vmf_loader.lua new file mode 100644 index 0000000..adacc1e --- /dev/null +++ b/vmf_source/scripts/mods/vmf/vmf_loader.lua @@ -0,0 +1,41 @@ +return{ + init = function(object) + + dofile("scripts/mods/vmf/modules/old_hook_and_console") + dofile("scripts/mods/vmf/modules/mods") + dofile("scripts/mods/vmf/modules/hooks") + dofile("scripts/mods/vmf/modules/gui") + dofile("scripts/mods/vmf/modules/settings") + dofile("scripts/mods/vmf/modules/vmf_options_view") + + --Application.set_user_setting("mod_developer_mode", true) + --Application.save_user_settings() + + object.vmf = get_mod("VMF") + + -- temporary solution: + dofile("scripts/mods/vmf/modules/testing_stuff_here") + end, + + update = function(object, dt) + --print("UPDATE: " .. tostring(dt)) + object.vmf.mods_update(dt) + end, + + on_unload = function(object) + print("VMF:ON_UNLOAD()") + object.vmf = nil + end, + + on_reload = function(object) + print("VMF:ON_RELOAD()") + object.vmf.mods_unload() + object.vmf.hooks_unload() + object.vmf.save_unsaved_settings_to_file() + end, + + on_game_state_changed = function(object, status, state) + print("VMF:ON_GAME_STATE_CHANGED(), status: " .. tostring(status) .. ", state: " .. tostring(state)) + object.vmf.save_unsaved_settings_to_file() + end +} diff --git a/vmf_source/settings.ini b/vmf_source/settings.ini new file mode 100644 index 0000000..2353650 --- /dev/null +++ b/vmf_source/settings.ini @@ -0,0 +1 @@ +boot_package = "resource_packages/vmf" \ No newline at end of file diff --git a/vmf_source/vmf.mod b/vmf_source/vmf.mod new file mode 100644 index 0000000..7aca429 --- /dev/null +++ b/vmf_source/vmf.mod @@ -0,0 +1,11 @@ +print("Vermintide Mod Framework loading") + +local ret = { + run = function() + return dofile("scripts/mods/vmf/vmf_loader") + end, + packages = { + "resource_packages/vmf" + }, +} +return ret \ No newline at end of file