From 1a4a44b5aff0f387b3afde7ffe5b9cca851d717d Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Sat, 6 Oct 2018 16:25:35 +0200 Subject: [PATCH] Start adding support for multi-layer ocean refraction. --- assets/shaders/ocean/ocean.frag | 61 ++++++++++++++++++----- assets/shaders/sprite.frag | 4 ++ renderer/ocean.cpp | 88 +++++++++++++++++++++------------ renderer/ocean.hpp | 10 ++-- renderer/render_graph.cpp | 8 +++ ui/sprite.cpp | 3 ++ ui/sprite.hpp | 1 + vulkan/command_buffer.cpp | 5 +- 8 files changed, 131 insertions(+), 49 deletions(-) diff --git a/assets/shaders/ocean/ocean.frag b/assets/shaders/ocean/ocean.frag index 271efff16..2bc2541a4 100644 --- a/assets/shaders/ocean/ocean.frag +++ b/assets/shaders/ocean/ocean.frag @@ -21,15 +21,28 @@ layout(set = 2, binding = 3) uniform mediump sampler2D uNormal; #if VARIANT_BIT_1 #include "../inc/render_parameters.h" #include "../inc/bandlimited_pixel_filter.h" -layout(set = 2, binding = 4) uniform mediump sampler2D uDirectRefraction; -layout(set = 2, binding = 5, std140) uniform Refraction +// sampler2DArray makes way more sense here. TODO: Add support for layers in render graph. +layout(set = 2, binding = 4) uniform mediump sampler2D uDirectRefraction0; +layout(set = 2, binding = 5) uniform mediump sampler2D uDirectRefraction1; +layout(set = 2, binding = 6) uniform mediump sampler2D uDirectRefraction2; +layout(set = 2, binding = 7) uniform mediump sampler2D uDirectRefraction3; + +layout(set = 2, binding = 8, std140) uniform Refraction { vec4 refraction_size; + vec4 refraction_depths; float refraction_uv_scale; - float refraction_depth; float refraction_emissive_mod; }; + +mediump vec3 merge_emissive_layers(mediump vec4 layer0, mediump vec4 layer1, mediump vec4 layer2, mediump vec4 layer3) +{ + mediump vec3 merged = mix(layer2.rgb, layer3.rgb, layer2.a); + merged = mix(layer1.rgb, merged, layer1.a); + merged = mix(layer0.rgb, merged, layer0.a); + return merged; +} #endif void main() @@ -46,7 +59,10 @@ void main() #if VARIANT_BIT_1 mediump vec3 to_ocean = normalize(vPos - global.camera_position); - vec2 uv = vPos.xz * refraction_uv_scale; + vec2 uv0 = vPos.xz * refraction_uv_scale; + vec2 uv1 = uv0; + vec2 uv2 = uv0; + vec2 uv3 = uv0; mediump vec3 refracted_dir = refract(to_ocean, normal, 1.0 / 1.33); mediump float dir_to_bottom = -refracted_dir.y; @@ -59,17 +75,38 @@ void main() if (dir_to_bottom > 0.0) { - mediump vec2 refracted_xz_offset = refracted_dir.xz * ((refraction_depth + vPos.y) / dir_to_bottom); - mediump vec3 refracted_pos_offset = vec3(refracted_xz_offset.x, vPos.y + refraction_depth, refracted_xz_offset.y); - mediump float fade_length = length(refracted_pos_offset); - uv = refraction_uv_scale * (refracted_xz_offset + vPos.xz); + mediump vec2 refracted_xz_offset; + mediump vec4 depths = (refraction_depths + vPos.y) / dir_to_bottom; + refracted_xz_offset = refracted_dir.xz * depths.x; + uv0 = refraction_uv_scale * (refracted_xz_offset + vPos.xz); + refracted_xz_offset = refracted_dir.xz * depths.y; + uv1 = refraction_uv_scale * (refracted_xz_offset + vPos.xz); + refracted_xz_offset = refracted_dir.xz * depths.z; + uv2 = refraction_uv_scale * (refracted_xz_offset + vPos.xz); + refracted_xz_offset = refracted_dir.xz * depths.w; + uv3 = refraction_uv_scale * (refracted_xz_offset + vPos.xz); } + #if VARIANT_BIT_2 - BandlimitedPixelInfo info = - compute_pixel_weights(uv, refraction_size.xy, refraction_size.zw, exp2(3.0 * turbulence)); - mediump vec3 emissive = refraction_emissive_mod * sample_bandlimited_pixel(uDirectRefraction, uv, info, 3.0 * turbulence).rgb; + mediump float turbulence_extent_mod = exp2(2.0 * turbulence); + BandlimitedPixelInfo info; + + info = compute_pixel_weights(uv0, refraction_size.xy, refraction_size.zw, turbulence_extent_mod); + mediump vec4 emissive0 = sample_bandlimited_pixel(uDirectRefraction0, uv0, info, 2.0 * turbulence); + info = compute_pixel_weights(uv1, refraction_size.xy, refraction_size.zw, turbulence_extent_mod); + mediump vec4 emissive1 = sample_bandlimited_pixel(uDirectRefraction1, uv1, info, 2.0 * turbulence); + info = compute_pixel_weights(uv2, refraction_size.xy, refraction_size.zw, turbulence_extent_mod); + mediump vec4 emissive2 = sample_bandlimited_pixel(uDirectRefraction2, uv2, info, 2.0 * turbulence); + info = compute_pixel_weights(uv3, refraction_size.xy, refraction_size.zw, turbulence_extent_mod); + mediump vec4 emissive3 = sample_bandlimited_pixel(uDirectRefraction3, uv3, info, 2.0 * turbulence); + + mediump vec3 emissive = refraction_emissive_mod * merge_emissive_layers(emissive0, emissive1, emissive2, emissive3); #else - mediump vec3 emissive = refraction_emissive_mod * texture(uDirectRefraction, uv, 3.0 * turbulence).rgb; + mediump vec4 emissive0 = texture(uDirectRefraction0, uv0, 2.0 * turbulence); + mediump vec4 emissive1 = texture(uDirectRefraction1, uv1, 2.0 * turbulence); + mediump vec4 emissive2 = texture(uDirectRefraction2, uv2, 2.0 * turbulence); + mediump vec4 emissive3 = texture(uDirectRefraction3, uv3, 2.0 * turbulence); + mediump vec3 emissive = refraction_emissive_mod * merge_emissive_layers(emissive0, emissive1, emissive2, emissive3); #endif #else const mediump vec3 emissive = vec3(0.0); diff --git a/assets/shaders/sprite.frag b/assets/shaders/sprite.frag index c6456b517..7a7e4615a 100644 --- a/assets/shaders/sprite.frag +++ b/assets/shaders/sprite.frag @@ -81,4 +81,8 @@ void main() #endif Color = color; #endif + +#if defined(VARIANT_BIT_3) + Color.a = 0.0; +#endif } diff --git a/renderer/ocean.cpp b/renderer/ocean.cpp index 37e49db47..1081b8bdc 100644 --- a/renderer/ocean.cpp +++ b/renderer/ocean.cpp @@ -185,10 +185,13 @@ void Ocean::setup_render_pass_dependencies(RenderGraph &, RenderPass &target) target.add_texture_input("ocean-gradient-jacobian-output", VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); target.add_texture_input("ocean-normal-fft-output", VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - if (!config.refraction.input.empty() && config.refraction.input_is_render_graph) - refraction_resource = &target.add_texture_input(config.refraction.input); - else - refraction_resource = nullptr; + for (unsigned i = 0; i < OceanLayers; i++) + { + if (!config.refraction.inputs[i].empty() && config.refraction.input_is_render_graph) + refraction_resources[i] = &target.add_texture_input(config.refraction.inputs[i]); + else + refraction_resources[i] = nullptr; + } } void Ocean::setup_render_pass_resources(RenderGraph &graph) @@ -255,18 +258,21 @@ void Ocean::setup_render_pass_resources(RenderGraph &graph) displacement_fft->process(&deferred_cmd, &displacement_output, &displacement_input); } - refraction = nullptr; - if (!config.refraction.input.empty()) + for (unsigned i = 0; i < OceanLayers; i++) { - if (config.refraction.input_is_render_graph) - { - refraction = &graph.get_physical_texture_resource(*refraction_resource); - } - else + refractions[i] = nullptr; + if (!config.refraction.inputs[i].empty()) { - auto *texture = graph.get_device().get_texture_manager().request_texture(config.refraction.input); - if (texture) - refraction = &texture->get_image()->get_view(); + if (config.refraction.input_is_render_graph) + { + refractions[i] = &graph.get_physical_texture_resource(*refraction_resources[i]); + } + else + { + auto *texture = graph.get_device().get_texture_manager().request_texture(config.refraction.inputs[i]); + if (texture) + refractions[i] = &texture->get_image()->get_view(); + } } } } @@ -704,8 +710,8 @@ struct OceanData struct RefractionData { vec4 texture_size; + vec4 depths; float uv_scale; - float depth; float emissive_mod; }; @@ -732,7 +738,7 @@ struct OceanInfo unsigned lod_stride; OceanData data; - const Vulkan::ImageView *refraction; + const Vulkan::ImageView *refractions[OceanLayers]; RefractionData refraction_data; }; @@ -757,12 +763,19 @@ static void ocean_render(Vulkan::CommandBuffer &cmd, const RenderQueueData *info cmd.set_texture(2, 2, *ocean_info.grad_jacobian, Vulkan::StockSampler::TrilinearWrap); cmd.set_texture(2, 3, *ocean_info.normal, Vulkan::StockSampler::TrilinearWrap); - if (ocean_info.refraction) + bool has_refraction = false; + for (unsigned i = 0; i < 4; i++) { - cmd.set_texture(2, 4, *ocean_info.refraction, Vulkan::StockSampler::TrilinearWrap); - *cmd.allocate_typed_constant_data(2, 5, 1) = ocean_info.refraction_data; + if (ocean_info.refractions[i]) + { + cmd.set_texture(2, 4 + i, *ocean_info.refractions[i], Vulkan::StockSampler::TrilinearWrap); + has_refraction = true; + } } + if (has_refraction) + *cmd.allocate_typed_constant_data(2, 8, 1) = ocean_info.refraction_data; + for (unsigned lod = 0; lod < ocean_info.lods; lod++) { cmd.set_uniform_buffer(3, 0, *ocean_info.ubo, @@ -803,14 +816,22 @@ void Ocean::get_render_info(const RenderContext &, hasher.u64(grad_jacobian.get_cookie()); hasher.u64(ubo.get_cookie()); hasher.u64(indirect.get_cookie()); - if (refraction) - hasher.u64(refraction->get_cookie()); - else - hasher.u32(0); + + bool has_refraction = false; + for (unsigned i = 0; i < 4; i++) + { + if (refractions[i]) + { + hasher.u64(refractions[i]->get_cookie()); + has_refraction = true; + } + else + hasher.u32(0); + } auto instance_key = hasher.get(); - auto *patch_data = queue.push(refraction ? + auto *patch_data = queue.push(has_refraction ? Queue::OpaqueEmissive : Queue::Opaque, instance_key, 1, RenderFunctions::ocean_render, @@ -818,7 +839,7 @@ void Ocean::get_render_info(const RenderContext &, if (patch_data) { - uint32_t refraction_flag = refraction ? 2 : 0; + uint32_t refraction_flag = has_refraction ? 2 : 0; if (config.refraction.bandlimited_pixel) refraction_flag |= 4; @@ -855,17 +876,20 @@ void Ocean::get_render_info(const RenderContext &, patch_data->index_type = index_type; patch_data->border_count = border_count; - patch_data->refraction = refraction; - if (refraction) + memcpy(patch_data->refractions, refractions, sizeof(refractions)); + if (has_refraction) { patch_data->refraction_data.texture_size = vec4( - float(refraction->get_image().get_width()), - float(refraction->get_image().get_height()), - 1.0f / float(refraction->get_image().get_width()), - 1.0f / float(refraction->get_image().get_height())); + float(refractions[0]->get_image().get_width()), + float(refractions[0]->get_image().get_height()), + 1.0f / float(refractions[0]->get_image().get_width()), + 1.0f / float(refractions[0]->get_image().get_height())); patch_data->refraction_data.uv_scale = config.refraction.uv_scale; - patch_data->refraction_data.depth = config.refraction.depth; + + for (unsigned i = 0; i < OceanLayers; i++) + patch_data->refraction_data.depths[i] = config.refraction.depth[i]; + patch_data->refraction_data.emissive_mod = config.refraction.emissive_mod; } diff --git a/renderer/ocean.hpp b/renderer/ocean.hpp index 4957be7ab..7c8479d2a 100644 --- a/renderer/ocean.hpp +++ b/renderer/ocean.hpp @@ -35,6 +35,8 @@ namespace Granite class RenderTextureResource; class RenderBufferResource; +static constexpr unsigned OceanLayers = 4; + struct OceanConfig { unsigned fft_resolution = 512; @@ -49,9 +51,9 @@ struct OceanConfig struct { - std::string input; + std::string inputs[OceanLayers]; float uv_scale = 0.01f; - float depth = 3.0f; + float depth[OceanLayers] = { 2.0f, 4.0f, 6.0f, 8.0f }; float emissive_mod = 1.0f; bool bandlimited_pixel = false; bool input_is_render_graph = false; @@ -187,7 +189,7 @@ class Ocean : public AbstractRenderable, vec2 heightmap_world_size() const; vec2 normalmap_world_size() const; - Vulkan::ImageView *refraction = nullptr; - RenderTextureResource *refraction_resource = nullptr; + Vulkan::ImageView *refractions[OceanLayers]; + RenderTextureResource *refraction_resources[OceanLayers] = {}; }; } \ No newline at end of file diff --git a/renderer/render_graph.cpp b/renderer/render_graph.cpp index 7ca115664..84f07a50b 100644 --- a/renderer/render_graph.cpp +++ b/renderer/render_graph.cpp @@ -149,6 +149,14 @@ RenderTextureResource &RenderPass::add_texture_input(const std::string &name, Vk res.read_in_pass(index); res.add_image_usage(VK_IMAGE_USAGE_SAMPLED_BIT); + // Support duplicate add_texture_inputs. + auto itr = find_if(begin(generic_texture), end(generic_texture), [&](const AccessedTextureResource &acc) { + return acc.texture == &res; + }); + + if (itr != end(generic_texture)) + return *itr->texture; + AccessedTextureResource acc; acc.texture = &res; acc.layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; diff --git a/ui/sprite.cpp b/ui/sprite.cpp index 3217c6ca3..f0179f200 100644 --- a/ui/sprite.cpp +++ b/ui/sprite.cpp @@ -174,6 +174,7 @@ void Sprite::get_sprite_render_info(const SpriteTransformInfo &transform, Render hasher.s32(transparent); hasher.s32(bandlimited_pixel); hasher.s32(luma_to_alpha); + hasher.s32(clear_alpha_to_zero); hasher.s32(texture_alt ? 1 : 0); auto pipe_hash = hasher.get(); @@ -203,6 +204,8 @@ void Sprite::get_sprite_render_info(const SpriteTransformInfo &transform, Render flags |= 1 << 1; if (luma_to_alpha) flags |= 1 << 2; + if (clear_alpha_to_zero) + flags |= 1 << 3; sprite.program = suite.get_program(pipeline, MESH_ATTRIBUTE_POSITION_BIT | diff --git a/ui/sprite.hpp b/ui/sprite.hpp index 8434c70c5..099a25dff 100644 --- a/ui/sprite.hpp +++ b/ui/sprite.hpp @@ -71,6 +71,7 @@ struct Sprite : AbstractRenderable float texture_blending_factor = 0.0f; bool bandlimited_pixel = false; bool luma_to_alpha = false; + bool clear_alpha_to_zero = false; void get_sprite_render_info(const SpriteTransformInfo &transform, RenderQueue &queue) const override; void get_render_info(const RenderContext &, const CachedSpatialTransformComponent *, RenderQueue &) const override diff --git a/vulkan/command_buffer.cpp b/vulkan/command_buffer.cpp index 3502ddce6..408d91415 100644 --- a/vulkan/command_buffer.cpp +++ b/vulkan/command_buffer.cpp @@ -1703,7 +1703,10 @@ void CommandBuffer::set_transparent_sprite_state() state.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP; state.write_mask = ~0u; - set_blend_factors(VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA); + // The alpha layer should start at 1 (fully transparent). + // As layers are blended in, the transparency is multiplied with other transparencies (1 - alpha). + set_blend_factors(VK_BLEND_FACTOR_SRC_ALPHA, VK_BLEND_FACTOR_ZERO, + VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA); set_blend_op(VK_BLEND_OP_ADD); set_dirty(COMMAND_BUFFER_DIRTY_STATIC_STATE_BIT);