diff --git a/CMakeLists.txt b/CMakeLists.txt index ea413441..4ddaf357 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,6 +89,7 @@ add_subdirectory(examples/ui) add_subdirectory(examples/utils) add_subdirectory(examples/vk) add_subdirectory(examples/volume) +add_subdirectory(examples/oit) # VSG tests add_subdirectory(tests) diff --git a/data/shaders/dp_combine.frag b/data/shaders/dp_combine.frag new file mode 100644 index 00000000..6ce66cd7 --- /dev/null +++ b/data/shaders/dp_combine.frag @@ -0,0 +1,25 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_KHR_vulkan_glsl : enable + +#pragma import_defines (DEPTHPEELING_PASS) + +#define PEEL_DESCRIPTOR_SET 2 + +layout(input_attachment_index = 0, set = PEEL_DESCRIPTOR_SET, binding = 2) uniform subpassInput peelOutput; + +layout(location = 0) out vec4 outColor; + +void main() +{ + // (!) in case of final compose pass input is already premultiplied + // in this case over-blending is done in fixed function pipeline by using blending operation + outColor = subpassLoad(peelOutput); + +#ifdef DEPTHPEELING_PASS + // (!) premultiplied alpha because of under-blending-usage + // under-blending is done in fixed function pipeline by using blending operation + outColor.rgb *= outColor.a; +#endif +} diff --git a/data/shaders/dp_fullscreen.vert b/data/shaders/dp_fullscreen.vert new file mode 100644 index 00000000..c06fbc83 --- /dev/null +++ b/data/shaders/dp_fullscreen.vert @@ -0,0 +1,33 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_KHR_vulkan_glsl : enable + +layout(push_constant) uniform PushConstants { + mat4 projection; + mat4 modelView; +} pc; + +void main() +{ + // 0---^-----------2 + // | S | S | / + // <---.---|---/---> x+ + // | S | S | / + // |-------/ + // | | / + // | / + // | / | + // 1 V + // y+ + + // gl_VertexIndex: + // 0 = 0.0, 0.0, 0.0, 1.0 + // 1 = 0.0, 4.0, 0.0, 1.0 + // 2 = 4.0, 0.0, 0.0, 1.0 + vec4 pos = vec4((float((gl_VertexIndex >> 1U) & 1U)) * 4.0, (float(gl_VertexIndex & 1U)) * 4.0, 0, 1.0); + // screen space for x/y from -1 to 1 -> so offset coordinates by -1 + pos.xy -= vec2(1.0, 1.0); + + gl_Position = pos; +} diff --git a/data/shaders/dp_pass_flat.frag b/data/shaders/dp_pass_flat.frag new file mode 100644 index 00000000..fe685cf4 --- /dev/null +++ b/data/shaders/dp_pass_flat.frag @@ -0,0 +1,106 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_KHR_vulkan_glsl : enable + +#pragma import_defines (VSG_TEXTURECOORD_0, VSG_TEXTURECOORD_1, VSG_TEXTURECOORD_2, VSG_TEXTURECOORD_3, VSG_POINT_SPRITE, VSG_DIFFUSE_MAP, VSG_GREYSCALE_DIFFUSE_MAP, VSG_DETAIL_MAP, DEPTHPEELING_PASS, DEPTHPEELING_FIRSTPASS) + +#define VIEW_DESCRIPTOR_SET 0 +#define MATERIAL_DESCRIPTOR_SET 1 +#define PEEL_DESCRIPTOR_SET 2 + +#if defined(VSG_TEXTURECOORD_3) + #define VSG_TEXCOORD_COUNT 4 +#elif defined(VSG_TEXTURECOORD_2) + #define VSG_TEXCOORD_COUNT 3 +#elif defined(VSG_TEXTURECOORD_1) + #define VSG_TEXCOORD_COUNT 2 +#else + #define VSG_TEXCOORD_COUNT 1 +#endif + +#ifdef VSG_DIFFUSE_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 0) uniform sampler2D diffuseMap; +#endif + +#ifdef VSG_DETAIL_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 1) uniform sampler2D detailMap; +#endif + +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 10) uniform MaterialData +{ + vec4 ambientColor; + vec4 diffuseColor; + vec4 specularColor; + vec4 emissiveColor; + float shininess; + float alphaMask; + float alphaMaskCutoff; +} material; + +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 11) uniform TexCoordIndices +{ + // indices into texCoord[] array for each texture type + int diffuseMap; + int detailMap; + int normalMap; + int aoMap; + int emissiveMap; + int specularMap; + int mrMap; +} texCoordIndices; + +layout(location = 2) in vec4 vertexColor; +layout(location = 4) in vec2 texCoord[VSG_TEXCOORD_COUNT]; + +#ifdef DEPTHPEELING_PASS +layout(input_attachment_index = 0, set = PEEL_DESCRIPTOR_SET, binding = 0) uniform subpassInput opaqueDepth; +layout(input_attachment_index = 1, set = PEEL_DESCRIPTOR_SET, binding = 1) uniform subpassInput prevPassDepth; +#endif + +layout(location = 0) out vec4 outColor; + +void main() +{ +#ifdef DEPTHPEELING_PASS + if (gl_FragCoord.z <= subpassLoad(opaqueDepth).r) + discard; + +#ifndef DEPTHPEELING_FIRSTPASS + if (gl_FragCoord.z >= subpassLoad(prevPassDepth).r) + discard; +#endif +#endif + +#ifdef VSG_POINT_SPRITE + const vec2 texCoordDiffuse = gl_PointCoord.xy; +#else + const vec2 texCoordDiffuse = texCoord[texCoordIndices.diffuseMap].st; +#endif + + vec4 diffuseColor = vertexColor * material.diffuseColor; + +#ifdef VSG_DIFFUSE_MAP + #ifdef VSG_GREYSCALE_DIFFUSE_MAP + float v = texture(diffuseMap, texCoordDiffuse); + diffuseColor *= vec4(v, v, v, 1); + #else + diffuseColor *= texture(diffuseMap, texCoordDiffuse); + #endif +#endif + +#ifdef VSG_DETAIL_MAP + vec4 detailColor = texture(detailMap, texCoord[texCoordIndices.detailMap].st); + diffuseColor.rgb = mix(diffuseColor.rgb, detailColor.rgb, detailColor.a); +#endif + + +#ifdef DEPTHPEELING_PASS + if (diffuseColor.a < (1.0 - material.alphaMaskCutoff) || diffuseColor.a >= material.alphaMaskCutoff) discard; +#else + if (diffuseColor.a < material.alphaMaskCutoff) discard; + diffuseColor.a = 1.0; +#endif + + outColor = vec4(diffuseColor.rgb, diffuseColor.a); +} diff --git a/data/shaders/dp_pass_phong.frag b/data/shaders/dp_pass_phong.frag new file mode 100644 index 00000000..2bd96857 --- /dev/null +++ b/data/shaders/dp_pass_phong.frag @@ -0,0 +1,373 @@ +#version 450 + +#extension GL_ARB_separate_shader_objects : enable +#extension GL_KHR_vulkan_glsl : enable + +#pragma import_defines (VSG_TEXTURECOORD_0, VSG_TEXTURECOORD_1, VSG_TEXTURECOORD_2, VSG_TEXTURECOORD_3, VSG_POINT_SPRITE, VSG_DIFFUSE_MAP, VSG_GREYSCALE_DIFFUSE_MAP, VSG_DETAIL_MAP, VSG_EMISSIVE_MAP, VSG_LIGHTMAP_MAP, VSG_NORMAL_MAP, VSG_SPECULAR_MAP, VSG_TWO_SIDED_LIGHTING, VSG_SHADOWS_PCSS, VSG_SHADOWS_SOFT, VSG_SHADOWS_HARD, SHADOWMAP_DEBUG, DEPTHPEELING_PASS, DEPTHPEELING_FIRSTPASS) + +// define by default for backwards compatibility +#define VSG_SHADOWS_HARD + +#if defined(VSG_TEXTURECOORD_3) + #define VSG_TEXCOORD_COUNT 4 +#elif defined(VSG_TEXTURECOORD_2) + #define VSG_TEXCOORD_COUNT 3 +#elif defined(VSG_TEXTURECOORD_1) + #define VSG_TEXCOORD_COUNT 2 +#else + #define VSG_TEXCOORD_COUNT 1 +#endif + +#define VIEW_DESCRIPTOR_SET 0 +#define MATERIAL_DESCRIPTOR_SET 1 +#define PEEL_DESCRIPTOR_SET 2 + +const float PI = radians(180); + +#ifdef VSG_DIFFUSE_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 0) uniform sampler2D diffuseMap; +#endif + +#ifdef VSG_DETAIL_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 1) uniform sampler2D detailMap; +#endif + +#ifdef VSG_NORMAL_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 2) uniform sampler2D normalMap; +#endif + +#ifdef VSG_LIGHTMAP_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 3) uniform sampler2D aoMap; +#endif + +#ifdef VSG_EMISSIVE_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 4) uniform sampler2D emissiveMap; +#endif + +#ifdef VSG_SPECULAR_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 5) uniform sampler2D specularMap; +#endif + +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 10) uniform MaterialData +{ + vec4 ambientColor; + vec4 diffuseColor; + vec4 specularColor; + vec4 emissiveColor; + float shininess; + float alphaMask; + float alphaMaskCutoff; +} material; + +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 11) uniform TexCoordIndices +{ + // indices into texCoord[] array for each texture type + int diffuseMap; + int detailMap; + int normalMap; + int aoMap; + int emissiveMap; + int specularMap; + int mrMap; +} texCoordIndices; + +layout(constant_id = 3) const int lightDataSize = 256; +layout(set = VIEW_DESCRIPTOR_SET, binding = 0) uniform LightData +{ + vec4 values[lightDataSize]; +} lightData; + +layout(location = 0) in vec3 eyePos; +layout(location = 1) in vec3 normalDir; +layout(location = 2) in vec4 vertexColor; +layout(location = 3) in vec3 viewDir; +layout(location = 4) in vec2 texCoord[VSG_TEXCOORD_COUNT]; + +#ifdef DEPTHPEELING_PASS +layout(input_attachment_index = 0, set = PEEL_DESCRIPTOR_SET, binding = 0) uniform subpassInput opaqueDepth; +layout(input_attachment_index = 1, set = PEEL_DESCRIPTOR_SET, binding = 1) uniform subpassInput prevPassDepth; +#endif + +layout(location = 0) out vec4 outColor; + +// include the calculateShadowCoverageForDirectionalLight(..) implementation +#include "shadows.glsl" + +// Find the normal for this fragment, pulling either from a predefined normal map +// or from the interpolated mesh normal and tangent attributes. +vec3 getNormal() +{ + vec3 result; +#ifdef VSG_NORMAL_MAP + // Perturb normal, see http://www.thetenthplanet.de/archives/1180 + vec3 tangentNormal = texture(normalMap, texCoord[texCoordIndices.normalMap]).xyz * 2.0 - 1.0; + + //tangentNormal *= vec3(2,2,1); + + vec3 q1 = dFdx(eyePos); + vec3 q2 = dFdy(eyePos); + vec2 st1 = dFdx(texCoord[texCoordIndices.normalMap]); + vec2 st2 = dFdy(texCoord[texCoordIndices.normalMap]); + + vec3 N = normalize(normalDir); + vec3 T = normalize(q1 * st2.t - q2 * st1.t); + vec3 B = -normalize(cross(N, T)); + mat3 TBN = mat3(T, B, N); + + result = normalize(TBN * tangentNormal); +#else + result = normalize(normalDir); +#endif +#ifdef VSG_TWO_SIDED_LIGHTING + if (!gl_FrontFacing) + result = -result; +#endif + return result; +} + +vec3 computeLighting(vec3 ambientColor, vec3 diffuseColor, vec3 specularColor, vec3 emissiveColor, float shininess, float ambientOcclusion, vec3 ld, vec3 nd, vec3 vd) +{ + vec3 color = vec3(0.0); + color.rgb += ambientColor; + + float diff = max(dot(ld, nd), 0.0); + color.rgb += diffuseColor * diff; + + if (diff > 0.0) + { + vec3 halfDir = normalize(ld + vd); + color.rgb += specularColor * pow(max(dot(halfDir, nd), 0.0), shininess); + } + + vec3 result = color + emissiveColor; + result *= ambientOcclusion; + + return result; +} + + +void main() +{ +#ifdef DEPTHPEELING_PASS + if (gl_FragCoord.z <= subpassLoad(opaqueDepth).r) + discard; + +#ifndef DEPTHPEELING_FIRSTPASS + if (gl_FragCoord.z >= subpassLoad(prevPassDepth).r) + discard; +#endif +#endif + + float intensityMinimum = 0.001; + +#ifdef VSG_POINT_SPRITE + const vec2 texCoordDiffuse = gl_PointCoord.xy; +#else + const vec2 texCoordDiffuse = texCoord[texCoordIndices.diffuseMap].st; +#endif + + vec4 diffuseColor = vertexColor * material.diffuseColor; +#ifdef VSG_DIFFUSE_MAP + #ifdef VSG_GREYSCALE_DIFFUSE_MAP + float v = texture(diffuseMap, texCoordDiffuse).s; + diffuseColor *= vec4(v, v, v, 1.0); + #else + diffuseColor *= texture(diffuseMap, texCoordDiffuse); + #endif +#endif + +#ifdef VSG_DETAIL_MAP + vec4 detailColor = texture(detailMap, texCoord[texCoordIndices.detailMap].st); + diffuseColor.rgb = mix(diffuseColor.rgb, detailColor.rgb, detailColor.a); +#endif + + vec4 ambientColor = diffuseColor * material.ambientColor * material.ambientColor.a; + vec4 specularColor = material.specularColor; + vec4 emissiveColor = material.emissiveColor; + float shininess = material.shininess; + float ambientOcclusion = 1.0; + +#ifdef DEPTHPEELING_PASS + if (diffuseColor.a < (1.0 - material.alphaMaskCutoff) || diffuseColor.a >= material.alphaMaskCutoff) discard; +#else + if (diffuseColor.a < material.alphaMaskCutoff) discard; + diffuseColor.a = 1.0; +#endif + +#ifdef VSG_EMISSIVE_MAP + emissiveColor *= texture(emissiveMap, texCoord[texCoordIndices.emissiveMap].st); +#endif + +#ifdef VSG_LIGHTMAP_MAP + ambientOcclusion *= texture(aoMap, texCoord[texCoordIndices.aoMap].st).r; +#endif + +#ifdef VSG_SPECULAR_MAP + specularColor *= texture(specularMap, texCoord[texCoordIndices.specularMap].st); +#endif + + vec3 nd = getNormal(); + vec3 vd = normalize(viewDir); + + vec3 color = vec3(0.0, 0.0, 0.0); + + vec4 lightNums = lightData.values[0]; + int numAmbientLights = int(lightNums[0]); + int numDirectionalLights = int(lightNums[1]); + int numPointLights = int(lightNums[2]); + int numSpotLights = int(lightNums[3]); + int lightDataIndex = 1; + + if (numAmbientLights>0) + { + // ambient lights + for(int i = 0; i0) + { + vec3 q1 = dFdx(eyePos); + vec3 q2 = dFdy(eyePos); + vec2 st1 = dFdx(texCoord[0]); + vec2 st2 = dFdy(texCoord[0]); + + vec3 N = normalize(normalDir); + vec3 T = normalize(q1 * st2.t - q2 * st1.t); + vec3 B = -normalize(cross(N, T)); + + // directional lights + for(int i = 0; i 0) + { + if (intensity > intensityMinimum) + intensity *= (1.0-calculateShadowCoverageForDirectionalLight(lightDataIndex, shadowMapIndex, T, B, color)); + + lightDataIndex += 1 + 8 * shadowMapCount; + shadowMapIndex += shadowMapCount; + } + else + lightDataIndex++; + + // if light is too shadowed to effect the rendering skip it + if (intensity < intensityMinimum ) continue; + + color.rgb += (diffuseColor.rgb * lightColor.rgb) * (intensity); + + if (shininess > 0.0 && diff > 0.0) + { + vec3 halfDir = normalize(direction + vd); + color.rgb += specularColor.rgb * (pow(max(dot(halfDir, nd), 0.0), shininess) * intensity); + } + } + } + + if (numPointLights>0) + { + // point light + for(int i = 0; i 0.0 && diff > 0.0) + { + vec3 halfDir = normalize(direction + vd); + color.rgb += specularColor.rgb * (pow(max(dot(halfDir, nd), 0.0), shininess) * scale); + } + } + } + + if (numSpotLights>0) + { + vec3 q1 = dFdx(eyePos); + vec3 q2 = dFdy(eyePos); + vec2 st1 = dFdx(texCoord[0]); + vec2 st2 = dFdy(texCoord[0]); + + vec3 N = normalize(normalDir); + vec3 T = normalize(q1 * st2.t - q2 * st1.t); + vec3 B = -normalize(cross(N, T)); + + // spot light + for(int i = 0; i 0) + { + if (lightDirection_cosOuterAngle.w < dot_lightdirection) + intensity *= (1.0-calculateShadowCoverageForSpotLight(lightDataIndex, shadowMapIndex, T, B, dist, color)); + + lightDataIndex += 1 + 8 * shadowMapCount; + shadowMapIndex += shadowMapCount; + } + else + lightDataIndex++; + + // if light is too shadowed to effect the rendering skip it + if (intensity < intensityMinimum ) continue; + + float scale = (intensity * smoothstep(lightDirection_cosOuterAngle.w, position_cosInnerAngle.w, dot_lightdirection)) / distance2; + + float unclamped_LdotN = dot(direction, nd); + + float diff = scale * max(unclamped_LdotN, 0.0); + color.rgb += (diffuseColor.rgb * lightColor.rgb) * diff; + if (shininess > 0.0 && diff > 0.0) + { + vec3 halfDir = normalize(direction + vd); + color.rgb += specularColor.rgb * (pow(max(dot(halfDir, nd), 0.0), shininess) * scale); + } + } + } + + outColor.rgb = (color * ambientOcclusion) + emissiveColor.rgb; + outColor.a = diffuseColor.a; +} diff --git a/data/textures/wood.png b/data/textures/wood.png new file mode 100644 index 00000000..c6f38e25 Binary files /dev/null and b/data/textures/wood.png differ diff --git a/examples/oit/CMakeLists.txt b/examples/oit/CMakeLists.txt new file mode 100644 index 00000000..39b39f4a --- /dev/null +++ b/examples/oit/CMakeLists.txt @@ -0,0 +1,3 @@ +if (vsgXchange_FOUND) + add_subdirectory(vsgdepthpeeling) +endif() diff --git a/examples/oit/vsgdepthpeeling/CMakeLists.txt b/examples/oit/vsgdepthpeeling/CMakeLists.txt new file mode 100644 index 00000000..a13b35c0 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/CMakeLists.txt @@ -0,0 +1,26 @@ +set(SOURCES + depthpeeling/Bindings.cpp + depthpeeling/Bindings.h + depthpeeling/Builder.cpp + depthpeeling/Builder.h + depthpeeling/PipelineConfigurator.cpp + depthpeeling/PipelineConfigurator.h + depthpeeling/RenderGraph.cpp + depthpeeling/RenderGraph.h + depthpeeling/Resources.cpp + depthpeeling/Resources.h + depthpeeling/ShaderSet.cpp + depthpeeling/ShaderSet.h + vsgdepthpeeling.cpp +) + +add_executable(vsgdepthpeeling ${SOURCES}) + +target_link_libraries(vsgdepthpeeling vsg::vsg) + +if (vsgXchange_FOUND) + target_compile_definitions(vsgdepthpeeling PRIVATE vsgXchange_FOUND) + target_link_libraries(vsgdepthpeeling vsgXchange::vsgXchange) +endif() + +install(TARGETS vsgdepthpeeling RUNTIME DESTINATION bin) diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/Bindings.cpp b/examples/oit/vsgdepthpeeling/depthpeeling/Bindings.cpp new file mode 100644 index 00000000..e8612433 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/Bindings.cpp @@ -0,0 +1,127 @@ +#include "Bindings.h" +#include "Resources.h" + +using namespace vsg::oit::depthpeeling; +using namespace vsg; + +DescriptorSetBinding::DescriptorSetBinding(Resources& r, ref_ptr s, ref_ptr l) + : Inherit(3) + , _resources(&r) + , _shaderSet(s) + , _layout(l) +{ +} + +void DescriptorSetBinding::compile(Context& context) +{ + _context = &context; + + if (_layout.valid()) + { + _layout->compile(context); + } + + ensureBindingUpToDate(); +} + +void DescriptorSetBinding::record(CommandBuffer& commandBuffer) const +{ + if (ensureBindingUpToDate()) + { + _binding->record(commandBuffer); + } +} + +BindPeelDescriptorSet::BindPeelDescriptorSet( + Resources& r, ref_ptr s, ref_ptr l, int32_t i) + : Inherit(r, s, l) + , _peelIndex(i) +{ +} + +ref_ptr BindPeelDescriptorSet::createBinding() const +{ + auto resources = _resources.valid() ? _resources.ref_ptr() : nullptr; + if (!resources.valid()) + { + return {}; + } + + const auto peelIdx = (_peelIndex + 1) % 2; + + auto opaqueDepthImageInfo = ImageInfo::create(nullptr, + resources->attachments().opaqueDepth.view, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + auto prevPassDepthImageInfo = ImageInfo::create(nullptr, + resources->attachments().depth[peelIdx].view, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + + auto& baseBinding = _shaderSet->getDescriptorBinding("opaqueDepth"); + + DescriptorConfigurator descriptorConfigurator{ _shaderSet }; + descriptorConfigurator.assignTexture("opaqueDepth", { opaqueDepthImageInfo }, 0); + descriptorConfigurator.assignTexture("prevPassDepth", { prevPassDepthImageInfo }, 0); + + if (_peelIndex == 0) + { + descriptorConfigurator.defines.insert("DEPTHPEELING_FIRSTPASS"); + } + + return BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, _layout, + baseBinding.set, descriptorConfigurator.descriptorSets.at(baseBinding.set)); +} + +CombineDescriptorSetBinding::CombineDescriptorSetBinding( + Resources& r, ref_ptr s, ref_ptr l) + : Inherit(r, s, l) +{ +} + +ref_ptr CombineDescriptorSetBinding::createBinding() const +{ + auto outputImageInfo = ImageInfo::create(nullptr, + getInputImageView(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + auto& baseBinding = _shaderSet->getDescriptorBinding("peelOutput"); + + DescriptorConfigurator descriptorConfigurator{ _shaderSet }; + descriptorConfigurator.assignTexture("peelOutput", { outputImageInfo }, 0); + + if (auto define = inputImageViewDefine(); !define.empty()) + { + descriptorConfigurator.defines.insert(define); + } + + return BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, _layout, + baseBinding.set, descriptorConfigurator.descriptorSets.at(baseBinding.set)); +} + +BindPeelCombineDescriptorSet::BindPeelCombineDescriptorSet( + Resources& r, ref_ptr s, ref_ptr l) + : Inherit(r, s, l) +{ +} + +ref_ptr BindPeelCombineDescriptorSet::getInputImageView() const +{ + return _resources.valid() ? _resources.ref_ptr()->attachments().color.view : nullptr; +} + +std::string BindPeelCombineDescriptorSet::inputImageViewDefine() const +{ + return "DEPTHPEELING_PASS"; +} + +BindFinalCombineDescriptorSet::BindFinalCombineDescriptorSet( + Resources& r, ref_ptr s, ref_ptr l) + : Inherit(r, s, l) +{ +} + +ref_ptr BindFinalCombineDescriptorSet::getInputImageView() const +{ + return _resources.valid() ? _resources.ref_ptr()->attachments().accum.view : nullptr; +} + +std::string BindFinalCombineDescriptorSet::inputImageViewDefine() const +{ + return ""; +} diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/Bindings.h b/examples/oit/vsgdepthpeeling/depthpeeling/Bindings.h new file mode 100644 index 00000000..f0be918a --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/Bindings.h @@ -0,0 +1,140 @@ +#pragma once + +#include + +#include +#include + +#include +#include +#include + +namespace vsg::oit::depthpeeling { + + class Resources; + + class DescriptorSetBinding : public Inherit + { + protected: + vsg::observer_ptr _resources; + + vsg::ref_ptr _context; + mutable vsg::Context* _compiledWithContext = nullptr; + + vsg::ref_ptr _shaderSet; + vsg::ref_ptr _layout; + + mutable vsg::ref_ptr _binding; + + public: + DescriptorSetBinding(Resources& r, + vsg::ref_ptr s, vsg::ref_ptr l); + + template + static void t_traverse(N& dsb, V& visitor) + { + if (dsb._layout.valid()) + { + dsb._layout->accept(visitor); + } + if (dsb._binding.valid()) + { + dsb._binding->accept(visitor); + } + } + + void traverse(Visitor& visitor) override + { + t_traverse(*this, visitor); + } + void traverse(ConstVisitor& visitor) const override + { + t_traverse(*this, visitor); + } + + void dirty() + { + _compiledWithContext = nullptr; + } + + void compile(Context& context) override; + void record(CommandBuffer& commandBuffer) const override; + + protected: + bool ensureBindingUpToDate() const + { + if (_compiledWithContext != nullptr || !_resources.valid() + || !_context.valid() || !_layout.valid() || !_shaderSet.valid()) + { + return _binding.valid(); + } + + _binding = createBinding(); + if (!_binding.valid()) + { + return false; + } + + _binding->compile(*_context); + const_cast(slot) = _binding->slot; + + _compiledWithContext = _context.get(); + return true; + } + virtual vsg::ref_ptr createBinding() const = 0; + }; + + class BindPeelDescriptorSet final : public Inherit + { + int32_t _peelIndex; + + public: + BindPeelDescriptorSet(Resources& r, + vsg::ref_ptr s, vsg::ref_ptr l, int32_t i); + + protected: + ref_ptr createBinding() const override; + }; + + class CombineDescriptorSetBinding : public Inherit + { + public: + CombineDescriptorSetBinding(Resources& r, + vsg::ref_ptr s, vsg::ref_ptr l); + + protected: + virtual ref_ptr getInputImageView() const = 0; + virtual std::string inputImageViewDefine() const = 0; + + ref_ptr createBinding() const override; + }; + + class BindPeelCombineDescriptorSet final : public Inherit + { + public: + BindPeelCombineDescriptorSet(Resources& r, + vsg::ref_ptr s, vsg::ref_ptr l); + + protected: + ref_ptr getInputImageView() const override; + std::string inputImageViewDefine() const override; + }; + + class BindFinalCombineDescriptorSet : public Inherit + { + public: + BindFinalCombineDescriptorSet(Resources& r, + vsg::ref_ptr s, vsg::ref_ptr l); + + protected: + ref_ptr getInputImageView() const override; + std::string inputImageViewDefine() const override; + }; + +} + +EVSG_type_name(vsg::oit::depthpeeling::DescriptorSetBinding); +EVSG_type_name(vsg::oit::depthpeeling::BindPeelDescriptorSet); +EVSG_type_name(vsg::oit::depthpeeling::CombineDescriptorSetBinding); +EVSG_type_name(vsg::oit::depthpeeling::BindPeelCombineDescriptorSet); +EVSG_type_name(vsg::oit::depthpeeling::BindFinalCombineDescriptorSet); diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/Builder.cpp b/examples/oit/vsgdepthpeeling/depthpeeling/Builder.cpp new file mode 100644 index 00000000..be9c177e --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/Builder.cpp @@ -0,0 +1,225 @@ +#include "Builder.h" + +#include "PipelineConfigurator.h" +#include "Bindings.h" + +#include +#include +#include + +#include +#include +#include + +#include + +using namespace vsg::oit::depthpeeling; +using namespace vsg; + +Builder::Builder(ref_ptr settings) + : _settings(settings) +{ +} + +Builder::RenderGraphs Builder::createRenderGraphs() +{ + ref_ptr headlight = _settings->headlight ? createHeadlight() : nullptr; + return { + createOpaqueRenderGraph(headlight), + createTransparencyRenderGraph(headlight) + }; +} + +void Builder::setCamera(ref_ptr camera) +{ + _camera = camera; +} + +ref_ptr Builder::getCamera() const +{ + return _camera; +} + +void Builder::setScene(Pass pass, ref_ptr scene) +{ + switch (pass) + { + case Pass::Opaque: + _opaqueScene = scene; + break; + case Pass::Transparency: + _transparencyScene = scene; + break; + } +} + +ref_ptr Builder::getScene(Pass pass) const +{ + switch (pass) + { + case Pass::Opaque: + return _opaqueScene; + case Pass::Transparency: + return _transparencyScene; + default: + return nullptr; + } +} + +ref_ptr Builder::getOrCreateMaterialBinding(MaterialConfigurator configurator, bool share) const +{ + if (!configurator || !_settings.valid()) + { + return {}; + } + + auto graphicsPipelineConfigurator = OpaquePipelineConfigurator::getOrCreate(_settings->shadingModel, _settings->options); + DescriptorConfigurator descriptorConfigurator{ graphicsPipelineConfigurator->shaderSet }; + + configurator(descriptorConfigurator, _settings->options); + + auto& materialDescriptorBinding = graphicsPipelineConfigurator->shaderSet->getDescriptorBinding("material"); + auto binding = vsg::BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, + graphicsPipelineConfigurator->layout, materialDescriptorBinding.set, + descriptorConfigurator.descriptorSets.at(materialDescriptorBinding.set)); + + if (share && _settings->options.valid() && _settings->options->sharedObjects.valid()) + { + _settings->options->sharedObjects->share(binding); + } + + return binding; +} + +ref_ptr Builder::createView(const ref_ptr& headlight) const +{ + auto proj = Perspective::create(*_camera->projectionMatrix.cast()); + auto cam = Camera::create(proj, _camera->viewMatrix, _camera->viewportState); + + auto view = View::create(cam); + if (headlight.valid()) + { + view->addChild(headlight); + } + + return view; +} + +ref_ptr Builder::createClearAttachments() const +{ + ClearAttachments::Attachments attachments; + attachments.push_back({ VK_IMAGE_ASPECT_COLOR_BIT, 0, {0.0f, 0.0f, 0.0f, 0.0f} }); + attachments.push_back({ VK_IMAGE_ASPECT_DEPTH_BIT, 0, {0.0f, 0} }); + + VkClearRect clearRect; + clearRect.rect.offset = { 0, 0 }; + clearRect.rect.extent = _settings->window->extent2D(); + clearRect.baseArrayLayer = 0; + clearRect.layerCount = 1; + + ClearAttachments::Rects clearRects; + clearRects.emplace_back(clearRect); + + return ClearAttachments::create(attachments, clearRects); +} + +ref_ptr Builder::createPeelScene(Resources& resources, int32_t peelIndex) const +{ + auto scene = StateGroup::create(); + + auto configurator = PeelPipelineConfigurator::getOrCreate(_settings->shadingModel, peelIndex, _settings->options); + configurator->copyTo(scene); + + auto bindDescriptorSet = BindPeelDescriptorSet::create( + resources, configurator->shaderSet, configurator->layout, peelIndex); + scene->add(bindDescriptorSet); + resources.registerDescriptorSetBinding(*bindDescriptorSet); + + scene->addChild(createClearAttachments()); + scene->addChild(_transparencyScene); + + return scene; +} + +ref_ptr Builder::createPeelCombineScene(Resources& resources, int32_t peelIndex) const +{ + auto scene = StateGroup::create(); + + auto configurator = PeelCombinePipelineConfigurator::getOrCreate(peelIndex, _settings->options); + configurator->copyTo(scene); + + auto bindDescriptorSet = BindPeelCombineDescriptorSet::create( + resources, configurator->shaderSet, configurator->layout); + scene->add(bindDescriptorSet); + resources.registerDescriptorSetBinding(*bindDescriptorSet); + + scene->addChild(Draw::create(3, 1, 0, 0)); + + return scene; +} + +ref_ptr Builder::createFinalCombineScene(Resources& resources) const +{ + auto scene = StateGroup::create(); + + auto configurator = FinalCombinePipelineConfigurator::getOrCreate(_settings->numPeelLayers, _settings->options); + configurator->copyTo(scene); + + auto bindDescriptorSet = BindFinalCombineDescriptorSet::create( + resources, configurator->shaderSet, configurator->layout); + scene->add(bindDescriptorSet); + resources.registerDescriptorSetBinding(*bindDescriptorSet); + + scene->addChild(Draw::create(3, 1, 0, 0)); + + return scene; +} + +ref_ptr Builder::createOpaqueRenderGraph(const ref_ptr& headlight) const +{ + if (_settings == nullptr || _opaqueScene == nullptr) + { + return {}; + } + + auto configurator = OpaquePipelineConfigurator::getOrCreate(_settings->shadingModel, _settings->options); + + auto scene = StateGroup::create(); + configurator->copyTo(scene); + scene->addChild(_opaqueScene); + + auto view = createView(headlight); + view->addChild(scene); + + auto renderGraph = vsg::RenderGraph::create(_settings->window, view); + renderGraph->contents = VK_SUBPASS_CONTENTS_INLINE; + + return renderGraph; +} + +ref_ptr Builder::createTransparencyRenderGraph(const ref_ptr& headlight) const +{ + if (_settings == nullptr || _transparencyScene == nullptr || _settings->numPeelLayers == 0) + { + return {}; + } + + auto view = createView(headlight); + auto renderGraph = RenderGraph::create(_settings->window, view); + renderGraph->contents = VK_SUBPASS_CONTENTS_INLINE; + renderGraph->numPeelLayers = _settings->numPeelLayers; + + auto scene = Group::create(); + for (auto peelIndex = 0; peelIndex < _settings->numPeelLayers; ++peelIndex) + { + scene->addChild(createPeelScene(*renderGraph->resources(), peelIndex)); + scene->addChild(NextSubPass::create()); + scene->addChild(createPeelCombineScene(*renderGraph->resources(), peelIndex)); + scene->addChild(NextSubPass::create()); + } + scene->addChild(createFinalCombineScene(*renderGraph->resources())); + + view->addChild(scene); + + return renderGraph; +} diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/Builder.h b/examples/oit/vsgdepthpeeling/depthpeeling/Builder.h new file mode 100644 index 00000000..bfb8f6f5 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/Builder.h @@ -0,0 +1,84 @@ +#pragma once + +#include "RenderGraph.h" +#include "ShaderSet.h" + +#include +#include + +namespace vsg::oit::depthpeeling { + + class Resources; + + class Builder final : public Inherit + { + public: + struct Settings : Inherit + { + Settings() = default; + explicit Settings(ref_ptr w, ref_ptr o, + ShadingModel m = ShadingModel::Phong, bool h = true, uint32_t n = 8) + : window(w) + , options(o) + , shadingModel(m) + , headlight(h) + , numPeelLayers(n) + { + } + + ref_ptr window; + ref_ptr options; + + ShadingModel shadingModel = ShadingModel::Phong; + bool headlight = true; + + int32_t numPeelLayers = 8; + }; + + enum class Pass + { + Opaque, + Transparency + }; + + Builder(ref_ptr settings); + + struct RenderGraphs + { + ref_ptr opaque; + ref_ptr transparency; + }; + + RenderGraphs createRenderGraphs(); + + void setCamera(ref_ptr camera); + ref_ptr getCamera() const; + + void setScene(Pass pass, ref_ptr scene); + ref_ptr getScene(Pass pass) const; + + using MaterialConfigurator = std::function&)>; + ref_ptr getOrCreateMaterialBinding(MaterialConfigurator configurator, bool share = true) const; + + private: + ref_ptr createView(const ref_ptr& headlight) const; + + ref_ptr createClearAttachments() const; + ref_ptr createPeelScene(Resources& resources, int32_t peelIndex) const; + ref_ptr createPeelCombineScene(Resources& resources, int32_t peelIndex) const; + ref_ptr createFinalCombineScene(Resources& resources) const; + + ref_ptr createOpaqueRenderGraph(const ref_ptr& headlight) const; + ref_ptr createTransparencyRenderGraph(const ref_ptr& headlight) const; + + ref_ptr _settings; + + ref_ptr _camera; + ref_ptr _opaqueScene; + ref_ptr _transparencyScene; + }; + +} + +EVSG_type_name(vsg::oit::depthpeeling::Builder) +EVSG_type_name(vsg::oit::depthpeeling::Builder::Settings) diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/PipelineConfigurator.cpp b/examples/oit/vsgdepthpeeling/depthpeeling/PipelineConfigurator.cpp new file mode 100644 index 00000000..d8fe8559 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/PipelineConfigurator.cpp @@ -0,0 +1,175 @@ +#include "PipelineConfigurator.h" + +using namespace vsg::oit::depthpeeling; +using namespace vsg; + +struct SetPeelCombinePipelineStates : public Visitor +{ + bool underblending = true; + + SetPeelCombinePipelineStates(bool underblend) + : underblending{ underblend } + { + } + + void apply(Object& object) override + { + object.traverse(*this); + } + + void apply(ColorBlendState& cbs) override + { + for (auto& blendAttachment : cbs.attachments) + { + blendAttachment.blendEnable = VK_TRUE; + + if (underblending) + { + // ------------------------------- + // Under-Blending + // ------------------------------- + + // Color: dst.rgb + src.rgb * (1 - dst.a) + blendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + blendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + + // Alpha: dst.a + src.a * (1 - dst.a) + blendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA; + blendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + } + else + { + // ------------------------------- + // Over-Blending + // ------------------------------- + + // Color: src.rgb + dst.rgb * (1 - src.a) + blendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blendAttachment.colorBlendOp = VK_BLEND_OP_ADD; + + // Alpha: src.a + dst.a * (1 - src.a) + blendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + blendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + blendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; + } + + blendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + } + } + + void apply(RasterizationState& rs) override + { + // disable culling mode completely for blending passes + rs.cullMode = VK_CULL_MODE_NONE; + } + + void apply(DepthStencilState& dss) override + { + // disable depth buffer completely for blending passes + dss.depthTestEnable = VK_FALSE; + dss.depthWriteEnable = VK_FALSE; + } +}; + +void configureSharedBindings(GraphicsPipelineConfigurator& configurator, ref_ptr& options) +{ + if (options.valid()) + { + configurator.assignInheritedState(options->inheritedState); + } + + configurator.enableArray("vsg_Vertex", VK_VERTEX_INPUT_RATE_VERTEX, 12); + configurator.enableArray("vsg_Normal", VK_VERTEX_INPUT_RATE_VERTEX, 12); + configurator.enableArray("vsg_TexCoord0", VK_VERTEX_INPUT_RATE_VERTEX, 8); + configurator.enableArray("vsg_Color", VK_VERTEX_INPUT_RATE_INSTANCE, 4); + + configurator.enableDescriptor("material"); + configurator.enableDescriptor("texCoordIndices"); + + configurator.enableTexture("diffuseMap"); + + if (auto* descriptorConfigurator = configurator.descriptorConfigurator.get(); descriptorConfigurator != nullptr) + { + descriptorConfigurator->assignDefaults(configurator.inheritedSets); + configurator.shaderHints->defines.insert( + descriptorConfigurator->defines.begin(), descriptorConfigurator->defines.end()); + } +} + +void initializePipelineConfigurator(vsg::ref_ptr& configurator, ref_ptr& options) +{ + if (options.valid() && options->sharedObjects.valid()) + { + options->sharedObjects->share(configurator, [](auto gpc) { gpc->init(); }); + } + else + { + configurator->init(); + } +} + +ref_ptr OpaquePipelineConfigurator::getOrCreate(ShadingModel model, ref_ptr options) +{ + auto configurator = GraphicsPipelineConfigurator::create(getOrCreateShadingShaderSet(model, options)); + configureSharedBindings(*configurator, options); + + initializePipelineConfigurator(configurator, options); + return configurator; +} + +vsg::ref_ptr PeelPipelineConfigurator::getOrCreate( + ShadingModel model, uint32_t peelIndex, vsg::ref_ptr options) +{ + auto configurator = GraphicsPipelineConfigurator::create(getOrCreateShadingShaderSet(model, options)); + configurator->subpass = peelIndex * 2; + configureSharedBindings(*configurator, options); + + configurator->shaderHints->defines.insert("DEPTHPEELING_PASS"); + if (peelIndex == 0) + { + configurator->shaderHints->defines.insert("DEPTHPEELING_FIRSTPASS"); + } + + initializePipelineConfigurator(configurator, options); + return configurator; +} + +vsg::ref_ptr PeelCombinePipelineConfigurator::getOrCreate( + uint32_t peelIndex, vsg::ref_ptr options) +{ + auto configurator = GraphicsPipelineConfigurator::create(getOrCreateCombineShaderSet(options)); + configurator->subpass = peelIndex * 2 + 1; + if (options.valid()) + { + configurator->assignInheritedState(options->inheritedState); + } + + configurator->shaderHints->defines.insert("DEPTHPEELING_PASS"); + + SetPeelCombinePipelineStates sps{ true }; + configurator->accept(sps); + + initializePipelineConfigurator(configurator, options); + return configurator; +} + +vsg::ref_ptr FinalCombinePipelineConfigurator::getOrCreate( + uint32_t numPeelLayers, vsg::ref_ptr options) +{ + auto configurator = GraphicsPipelineConfigurator::create(getOrCreateCombineShaderSet(options)); + configurator->subpass = numPeelLayers * 2; + if (options.valid()) + { + configurator->assignInheritedState(options->inheritedState); + } + + SetPeelCombinePipelineStates sps{ false }; + configurator->accept(sps); + + initializePipelineConfigurator(configurator, options); + return configurator; +} diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/PipelineConfigurator.h b/examples/oit/vsgdepthpeeling/depthpeeling/PipelineConfigurator.h new file mode 100644 index 00000000..6e89655b --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/PipelineConfigurator.h @@ -0,0 +1,45 @@ +#pragma once + +#include "ShaderSet.h" + +#include + +namespace vsg::oit::depthpeeling { + + class OpaquePipelineConfigurator + { + public: + OpaquePipelineConfigurator() = delete; + + static vsg::ref_ptr getOrCreate( + ShadingModel model, vsg::ref_ptr options); + }; + + class PeelPipelineConfigurator + { + public: + PeelPipelineConfigurator() = delete; + + static vsg::ref_ptr getOrCreate( + ShadingModel model, uint32_t peelIndex, vsg::ref_ptr options); + }; + + class PeelCombinePipelineConfigurator + { + public: + PeelCombinePipelineConfigurator() = delete; + + static vsg::ref_ptr getOrCreate( + uint32_t peelIndex, vsg::ref_ptr options); + }; + + class FinalCombinePipelineConfigurator + { + public: + FinalCombinePipelineConfigurator() = delete; + + static vsg::ref_ptr getOrCreate( + uint32_t numPeelLayers, vsg::ref_ptr options); + }; + +} diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/RenderGraph.cpp b/examples/oit/vsgdepthpeeling/depthpeeling/RenderGraph.cpp new file mode 100644 index 00000000..90558cfc --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/RenderGraph.cpp @@ -0,0 +1,97 @@ +#include "RenderGraph.h" + +#include "Bindings.h" + +#include + +using namespace vsg::oit::depthpeeling; + +namespace vsg::oit::depthpeeling +{ + + template + class ScopedOverride { + public: + ScopedOverride(T& a, T& b) + : _a(a) + , _b(b) + { + std::swap(_a, _b); + } + + ~ScopedOverride() + { + std::swap(_a, _b); + } + + private: + T& _a; + T& _b; + }; + + template + ScopedOverride make_scoped_override(T& x, T& y) + { + return ScopedOverride(x, y); + } +} + +RenderGraph::RenderGraph(ref_ptr window, ref_ptr view) + : Inherit(window, view) + , _resources(Resources::create()) +{ +} + +template +void RenderGraph::t_accept(V& visitor) +{ + if (numPeelLayers == 0) + { + Inherit::accept(visitor); + return; + } + + // replace with compatible renderpass used in this RenderGraph, + // then restore the original renderpass after traversal + auto select_renderpass = _resources->getOrCreateRenderPass({ window, numPeelLayers }); + auto renderPass_override = make_scoped_override(renderPass, select_renderpass); + + // update the attachments used in the RenderPass + _resources->ensureAttachmentsUpToDate({ window, getExtent() }); + + // traverse the RenderGraph as usual with the base visitor + Inherit::accept(visitor); +} + +void RenderGraph::accept(Visitor& visitor) +{ + t_accept(visitor); +} + +void RenderGraph::accept(ConstVisitor& visitor) const +{ + const_cast(this)->t_accept(visitor); +} + +void RenderGraph::accept(vsg::RecordTraversal& recordTraversal) const +{ + if (numPeelLayers == 0) + { + return; + } + + // VSG exposes accept(RecordTraversal) as const, but for traversal we need to + // temporarily replace the framebuffer and restore it afterwards. + auto& active_framebuffer = const_cast&>(framebuffer); + auto select_framebuffer = _resources->getOrCreateFramebuffer({ window, getExtent() }); + auto framebuffer_override = make_scoped_override(active_framebuffer, select_framebuffer); + + // VSG exposes accept(RecordTraversal) as const, but for traversal we need to + // temporarily replace the clear values and restore it afterwards. + auto& active_clearValues = const_cast(clearValues); + auto select_clearValues = Resources::clearValues(); + auto clearValues_override = make_scoped_override(active_clearValues, select_clearValues); + + // traverse the RenderGraph as usual with the RecordTraversal visitor + vsg::RenderGraph::accept(recordTraversal); +} diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/RenderGraph.h b/examples/oit/vsgdepthpeeling/depthpeeling/RenderGraph.h new file mode 100644 index 00000000..d3778462 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/RenderGraph.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Resources.h" + +#include + +#include +#include +#include +#include + +namespace vsg::oit::depthpeeling { + + class RenderGraph final : public Inherit + { + mutable ref_ptr _resources; + + template + void t_accept(V& visitor); + + public: + RenderGraph() = default; + explicit RenderGraph(ref_ptr window, ref_ptr view); + + uint32_t numPeelLayers = 0; + + const ref_ptr& resources() const + { + return _resources; + } + + void accept(Visitor& visitor) override; + void accept(ConstVisitor& visitor) const override; + void accept(RecordTraversal& recordTraversal) const override; + }; + +} +EVSG_type_name(vsg::oit::depthpeeling::RenderGraph); diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/Resources.cpp b/examples/oit/vsgdepthpeeling/depthpeeling/Resources.cpp new file mode 100644 index 00000000..9e95c095 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/Resources.cpp @@ -0,0 +1,389 @@ +#include "Resources.h" + +#include "Bindings.h" + +using namespace vsg::oit::depthpeeling; +using namespace vsg; + +ref_ptr Resources::createTransparencyPass(const CreateRenderPassInfo& createInfo) +{ + if (!createInfo.window.valid()) + { + return {}; + } + + // ======================================================================== + // Attachment descriptions + // ======================================================================== + + auto opaqueColorDescription = defaultColorAttachment(createInfo.window->surfaceFormat().format); + opaqueColorDescription.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + opaqueColorDescription.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + + auto opaqueDepthDescription = defaultDepthAttachment(createInfo.window->depthFormat()); + opaqueDepthDescription.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + opaqueDepthDescription.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + opaqueDepthDescription.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + + auto accumDescription = defaultColorAttachment(ColorAccumImageType); + accumDescription.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + accumDescription.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + accumDescription.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + + auto peelColorDescription = defaultColorAttachment(ColorPeelImageType); + peelColorDescription.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + peelColorDescription.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + peelColorDescription.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + + auto depthDescription = defaultDepthAttachment(DepthPeelImageType); + depthDescription.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depthDescription.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + depthDescription.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + + // ======================================================================== + // Attachments + // ======================================================================== + + RenderPass::Attachments attachments{ + opaqueColorDescription, accumDescription, peelColorDescription, + opaqueDepthDescription, depthDescription, depthDescription }; + + // Attachment-References + + std::vector attachmentRefs{ + {ColorOpaque, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT}, + {ColorAccum, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT}, + {ColorPeel, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_COLOR_BIT}, + {DepthOpaque, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT}, + {DepthPeel0, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT}, + {DepthPeel1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT}}; + + RenderPass::Subpasses subpasses; + RenderPass::Dependencies dependencies; + + // ======================================================================== + // Subpasses + // ======================================================================== + + // Subpass 0 => Peels - 1 => Gather and compute transparencies in correct order + + for (auto pass = 0u; pass < createInfo.numPeelLayers; ++pass) + { + SubpassDescription renderSubpass{0, VK_PIPELINE_BIND_POINT_GRAPHICS}; + renderSubpass.colorAttachments.emplace_back(attachmentRefs.at(ColorPeel)); + renderSubpass.depthStencilAttachments.emplace_back( + attachmentRefs.at(DepthPeel0 + (pass % 2))); + + renderSubpass.inputAttachments.emplace_back(attachmentRefs.at(DepthOpaque)); + renderSubpass.inputAttachments.back().layout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + renderSubpass.inputAttachments.emplace_back( + attachmentRefs.at(DepthPeel0 + ((pass + 1) % 2))); + renderSubpass.inputAttachments.back().layout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + + renderSubpass.preserveAttachments.push_back(ColorOpaque); + renderSubpass.preserveAttachments.push_back(ColorAccum); + + subpasses.emplace_back(renderSubpass); + + SubpassDescription combineSubpass{0, VK_PIPELINE_BIND_POINT_GRAPHICS}; + combineSubpass.colorAttachments.emplace_back(attachmentRefs.at(ColorAccum)); + + combineSubpass.inputAttachments.emplace_back(attachmentRefs.at(ColorPeel)); + combineSubpass.inputAttachments.back().layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + combineSubpass.preserveAttachments.push_back(ColorOpaque); + combineSubpass.preserveAttachments.push_back(DepthOpaque); + combineSubpass.preserveAttachments.push_back(DepthPeel0); + combineSubpass.preserveAttachments.push_back(DepthPeel1); + + subpasses.emplace_back(combineSubpass); + } + + // Combine Subpass => Combine opaque and transparent fragments + + SubpassDescription combineSubpass{0, VK_PIPELINE_BIND_POINT_GRAPHICS}; + combineSubpass.colorAttachments.emplace_back(attachmentRefs.at(ColorOpaque)); + + combineSubpass.inputAttachments.emplace_back(attachmentRefs.at(ColorAccum)); + combineSubpass.inputAttachments.back().layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + subpasses.emplace_back(combineSubpass); + + // ======================================================================== + // Dependencies between subpasses + // ======================================================================== + + constexpr auto ColorDepthAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT + | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + constexpr auto ColorAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT + | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + + // EXTERNAL => First peeling subpass + + SubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT; + dependency.srcAccessMask = ColorDepthAccessMask; + dependency.dstAccessMask = ColorDepthAccessMask; + dependency.dependencyFlags = 0; + + // Subpass 0 => (Peels * 2) - 1 => Gather transparencies and combine 1 - Peels * 2 + + for (auto pass = 0u; pass < createInfo.numPeelLayers; ++pass) + { + dependency.srcAccessMask = ColorDepthAccessMask; + dependencies.emplace_back(dependency); + dependency.srcSubpass = dependency.dstSubpass++; + + dependency.srcAccessMask = ColorAccessMask; + dependencies.emplace_back(dependency); + dependency.srcSubpass = dependency.dstSubpass++; + } + + // Last gather transparencies subpass => Combine subpass + + dependency.srcAccessMask = ColorAccessMask; + dependencies.emplace_back(dependency); + + // Combine subpass => EXTERNAL + + dependency.srcSubpass = dependency.dstSubpass; + dependency.dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies.emplace_back(dependency); + + // ======================================================================== + // Create renderpass + // ======================================================================== + + return RenderPass::create( + createInfo.window->getOrCreateDevice(), attachments, subpasses, dependencies); +} + +void Resources::ensureRenderPassUpToDate(const CreateRenderPassInfo& createInfo) +{ + if (_renderPass.valid() + && _state.renderPass.window == createInfo.window + && _state.renderPass.numPeelLayers == createInfo.numPeelLayers) + { + return; + } + + if (!createInfo.window.valid() || createInfo.numPeelLayers == 0) + { + _renderPass.reset(); + } + else + { + _renderPass = createTransparencyPass(createInfo); + } + + _state.renderPass = createInfo; +} + +ref_ptr Resources::getOrCreateRenderPass(const CreateRenderPassInfo& createInfo) +{ + ensureRenderPassUpToDate(createInfo); + return _renderPass; +} + +void Resources::ensureAttachmentsUpToDate(const CreateAttachmentInfo& createInfo) +{ + if (_attachments.valid() + && _state.attachment.window == createInfo.window + && _state.attachment.extent.width == createInfo.extent.width + && _state.attachment.extent.height == createInfo.extent.height) + { + return; + } + + createImages(createInfo); + + // update all registered descriptor set bindings as attachments have changed and + // they might be used as input attachments in the shader, so need to be re-bound + for (auto& dsb : _descriptorSetBindings) + { + dsb->dirty(); + } +} + +const Resources::Attachments& Resources::getOrCreateAttachments(const CreateAttachmentInfo& createInfo) +{ + ensureAttachmentsUpToDate(createInfo); + return _attachments; +} + +ref_ptr Resources::getOrCreateFramebuffer(const CreateFramebufferInfo& createInfo) +{ + auto valid = createInfo.window.valid() + && _state.framebuffer.window == createInfo.window + && _state.framebuffer.extent.width == createInfo.extent.width + && _state.framebuffer.extent.height == createInfo.extent.height; + if (valid) + { + for (auto& framebuffer : _framebuffers) + { + if (!framebuffer.valid() || framebuffer->extent2D() != createInfo.extent) + { + valid = false; + break; + } + } + } + + if (valid) + { + return framebuffer(); + } + + createFramebuffer(createInfo); + return framebuffer(); +} + +ref_ptr Resources::framebuffer() const +{ + const auto idx = _state.framebuffer.window.valid() ? _state.framebuffer.window->imageIndex() : 0; + if (!_state.framebuffer.window.valid() || idx >= _framebuffers.size()) + { + return {}; + } + + return _framebuffers.at(idx); +} + +const vsg::RenderGraph::ClearValues& Resources::clearValues() +{ + static const vsg::RenderGraph::ClearValues clearValues{ + { 0.0f, 0.0f, 0.0f, 0.0f }, // ColorOpaque + { 0.0f, 0.0f, 0.0f, 0.0f }, // ColorAccum + { 0.0f, 0.0f, 0.0f, 0.0f }, // ColorPeel + { 0.0f, 0 }, // DepthOpaque + { 0.0f, 0 }, // DepthPeel0 + { 0.0f, 0 } // DepthPeel1 + }; + + return clearValues; +} + +void Resources::registerDescriptorSetBinding(DescriptorSetBinding& dsb) +{ + _descriptorSetBindings.emplace_back(&dsb); +} + +void Resources::createImages(const CreateAttachmentInfo& createInfo) +{ + if (!createInfo.window.valid()) + { + _attachments = {}; + _state.attachment = createInfo; + + return; + } + + auto device = createInfo.window->getOrCreateDevice(); + + // determine depth attachment from opaque pass of window + _attachments.opaqueDepth = { + createInfo.window->getOrCreateDepthImage(), + createInfo.window->getOrCreateDepthImageView() + }; + + // create accum color image + _attachments.accum.image = Image::create(); + _attachments.accum.image->imageType = VK_IMAGE_TYPE_2D; + _attachments.accum.image->extent = {createInfo.extent.width, createInfo.extent.height, 1}; + _attachments.accum.image->mipLevels = 1; + _attachments.accum.image->arrayLayers = 1; + _attachments.accum.image->format = ColorAccumImageType; + _attachments.accum.image->tiling = VK_IMAGE_TILING_OPTIMAL; + _attachments.accum.image->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + _attachments.accum.image->samples = VK_SAMPLE_COUNT_1_BIT; + _attachments.accum.image->sharingMode = VK_SHARING_MODE_EXCLUSIVE; + _attachments.accum.image->usage = + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + + _attachments.accum.image->compile(device); + _attachments.accum.image->allocateAndBindMemory(device); + _attachments.accum.view = ImageView::create(_attachments.accum.image, VK_IMAGE_ASPECT_COLOR_BIT); + _attachments.accum.view->compile(device); + // create peel target color image + _attachments.color.image = Image::create(); + _attachments.color.image->imageType = VK_IMAGE_TYPE_2D; + _attachments.color.image->extent = _attachments.accum.image->extent; + _attachments.color.image->mipLevels = 1; + _attachments.color.image->arrayLayers = 1; + _attachments.color.image->format = ColorPeelImageType; + _attachments.color.image->tiling = VK_IMAGE_TILING_OPTIMAL; + _attachments.color.image->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + _attachments.color.image->samples = VK_SAMPLE_COUNT_1_BIT; + _attachments.color.image->sharingMode = VK_SHARING_MODE_EXCLUSIVE; + _attachments.color.image->usage = + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + + _attachments.color.image->compile(device); + _attachments.color.image->allocateAndBindMemory(device); + _attachments.color.view = ImageView::create(_attachments.color.image, VK_IMAGE_ASPECT_COLOR_BIT); + _attachments.color.view->compile(device); + // Gather passes images + for (auto& depth : _attachments.depth) + { + // create depth image + depth.image = Image::create(); + depth.image->imageType = VK_IMAGE_TYPE_2D; + depth.image->extent = _attachments.accum.image->extent; + depth.image->mipLevels = 1; + depth.image->arrayLayers = 1; + depth.image->format = DepthPeelImageType; + depth.image->tiling = VK_IMAGE_TILING_OPTIMAL; + depth.image->initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + depth.image->samples = VK_SAMPLE_COUNT_1_BIT; + depth.image->sharingMode = VK_SHARING_MODE_EXCLUSIVE; + depth.image->usage = + VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + + depth.image->compile(device); + depth.image->allocateAndBindMemory(device); + + depth.view = ImageView::create(depth.image, VK_IMAGE_ASPECT_DEPTH_BIT); + depth.view->compile(device); + } + + _state.attachment = createInfo; +} + +void Resources::createFramebuffer(const CreateFramebufferInfo& createInfo) +{ + if (!createInfo.window.valid()) + { + _framebuffers.clear(); + _state.framebuffer = createInfo; + + return; + } + + auto& attachments = getOrCreateAttachments({ createInfo.window, createInfo.extent }); + + _framebuffers.clear(); + for (auto index = 0u; index < createInfo.window->numFrames(); ++index) + { + auto colorImageView = createInfo.window->imageView(index); + + ImageViews imageViews{ + colorImageView, + attachments.accum.view, + attachments.color.view, + attachments.opaqueDepth.view, + attachments.depth[0].view, + attachments.depth[1].view }; + + _framebuffers.push_back(Framebuffer::create( + getOrCreateRenderPass(_state.renderPass), imageViews, + createInfo.extent.width, createInfo.extent.height, 1)); + } + + _state.framebuffer = createInfo; +} diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/Resources.h b/examples/oit/vsgdepthpeeling/depthpeeling/Resources.h new file mode 100644 index 00000000..3c6a7ed3 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/Resources.h @@ -0,0 +1,111 @@ +#pragma once + +#include "ShaderSet.h" + +#include +#include + +#include +#include + +#include + +namespace vsg::oit::depthpeeling { + + class DescriptorSetBinding; + + class Resources final : public Inherit + { + public: + struct CreateRenderPassInfo + { + ref_ptr window; + uint32_t numPeelLayers = 8; + }; + + struct CreateAttachmentInfo + { + ref_ptr window; + VkExtent2D extent{ vsg::RenderGraph::invalid_dimension, vsg::RenderGraph::invalid_dimension }; + }; + using CreateFramebufferInfo = CreateAttachmentInfo; + + struct Attachments + { + struct Attachment + { + ref_ptr image; + ref_ptr view; + + bool valid() const { return image.valid() && view.valid(); } + }; + + // resolved opaque depth attachment from opaque pass + Attachment opaqueDepth; + + // accumulated transparency attachment over all peel layers + Attachment accum; + // current peel layer attachment + Attachment color; + // two depth attachments used in ping-pong fashion + std::array depth; + + bool valid() const + { + return opaqueDepth.valid() && accum.valid() + && color.valid() && depth[0].valid() && depth[1].valid(); + } + }; + + void ensureRenderPassUpToDate(const CreateRenderPassInfo& createInfo); + ref_ptr getOrCreateRenderPass(const CreateRenderPassInfo& createInfo); + ref_ptr renderPass() const { return _renderPass; } + + void ensureAttachmentsUpToDate(const CreateAttachmentInfo& createInfo); + const Attachments& getOrCreateAttachments(const CreateAttachmentInfo& createInfo); + const Attachments& attachments() const { return _attachments; } + + ref_ptr getOrCreateFramebuffer(const CreateFramebufferInfo& createInfo); + ref_ptr framebuffer() const; + + static const vsg::RenderGraph::ClearValues& clearValues(); + + void registerDescriptorSetBinding(DescriptorSetBinding& dsb); + + protected: + enum + { + ColorOpaque = 0, // resolved (!) color from opaque pass + ColorAccum = 1, + ColorPeel = 2, + DepthOpaque = 3, // resolved (!) depth from opaque pass + DepthPeel0 = 4, + DepthPeel1 = 5, + }; + + struct State + { + CreateRenderPassInfo renderPass; + CreateAttachmentInfo attachment; + CreateFramebufferInfo framebuffer; + }; + + constexpr static auto ColorAccumImageType = VK_FORMAT_R32G32B32A32_SFLOAT; + constexpr static auto ColorPeelImageType = ColorAccumImageType; + constexpr static auto DepthPeelImageType = VK_FORMAT_D32_SFLOAT; + + ref_ptr createTransparencyPass(const CreateRenderPassInfo& createInfo); + + void createImages(const CreateAttachmentInfo& createInfo); + void createFramebuffer(const CreateFramebufferInfo& createInfo); + + State _state; + + Attachments _attachments; + ref_ptr _renderPass; + std::vector> _framebuffers; + std::vector> _descriptorSetBindings; + }; + +} +EVSG_type_name(vsg::oit::depthpeeling::Resources) \ No newline at end of file diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/ShaderSet.cpp b/examples/oit/vsgdepthpeeling/depthpeeling/ShaderSet.cpp new file mode 100644 index 00000000..cc904a84 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/ShaderSet.cpp @@ -0,0 +1,252 @@ +#include "ShaderSet.h" + +#include +#include + +using namespace vsg::oit::depthpeeling; +using namespace vsg; + +ref_ptr createShadingShaderSet(ShadingModel model, ref_ptr options) +{ + constexpr auto ViewDescriptorSet = 0; + constexpr auto MaterialDescriptorSet = 1; + constexpr auto PeelDescriptorSet = 2; + + auto vertexShader = read_cast("shaders/standard.vert", options); + auto fragmentShader = read_cast(model == ShadingModel::Phong + ? "shaders/dp_pass_phong.frag" + : "shaders/dp_pass_flat.frag", + options); + + auto shaderSet = ShaderSet::create(ShaderStages{ vertexShader, fragmentShader }); + + shaderSet->addAttributeBinding("vsg_Vertex", "", 0, VK_FORMAT_R32G32B32_SFLOAT, + vec3Array::create(1)); + shaderSet->addAttributeBinding("vsg_Normal", "", 1, VK_FORMAT_R32G32B32_SFLOAT, + vec3Array::create(1)); + shaderSet->addAttributeBinding("vsg_TexCoord0", "VSG_TEXTURECOORD_0", 2, + VK_FORMAT_R32G32_SFLOAT, vec2Array::create(1)); + shaderSet->addAttributeBinding("vsg_TexCoord1", "VSG_TEXTURECOORD_1", 3, + VK_FORMAT_R32G32_SFLOAT, vec2Array::create(1)); + shaderSet->addAttributeBinding("vsg_TexCoord2", "VSG_TEXTURECOORD_2", 4, + VK_FORMAT_R32G32_SFLOAT, vec2Array::create(1)); + shaderSet->addAttributeBinding("vsg_TexCoord3", "VSG_TEXTURECOORD_3", 5, + VK_FORMAT_R32G32_SFLOAT, vec2Array::create(1)); + shaderSet->addAttributeBinding("vsg_Color", "", 6, VK_FORMAT_R32G32B32A32_SFLOAT, + vec4Array::create(1), CoordinateSpace::LINEAR); + + shaderSet->addAttributeBinding("vsg_Translation_scaleDistance", "VSG_BILLBOARD", 7, + VK_FORMAT_R32G32B32A32_SFLOAT, vec4Array::create(1)); + + shaderSet->addAttributeBinding("vsg_Translation", "VSG_INSTANCE_TRANSLATION", 7, + VK_FORMAT_R32G32B32_SFLOAT, vec3Array::create(1)); + shaderSet->addAttributeBinding("vsg_Rotation", "VSG_INSTANCE_ROTATION", 8, + VK_FORMAT_R32G32B32A32_SFLOAT, quatArray::create(1)); + shaderSet->addAttributeBinding("vsg_Scale", "VSG_INSTANCE_SCALE", 9, + VK_FORMAT_R32G32B32_SFLOAT, vec3Array::create(1)); + + shaderSet->addAttributeBinding("vsg_JointIndices", "VSG_SKINNING", 10, + VK_FORMAT_R32G32B32A32_SINT, ivec4Array::create(1)); + shaderSet->addAttributeBinding("vsg_JointWeights", "VSG_SKINNING", 11, + VK_FORMAT_R32G32B32A32_SFLOAT, vec4Array::create(1)); + + shaderSet->addDescriptorBinding( + "diffuseMap", "VSG_DIFFUSE_MAP", MaterialDescriptorSet, 0, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, + ubvec4Array2D::create(1, 1, Data::Properties{ VK_FORMAT_R8G8B8A8_UNORM })); + shaderSet->addDescriptorBinding( + "detailMap", "VSG_DETAIL_MAP", MaterialDescriptorSet, 1, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, + ubvec4Array2D::create(1, 1, Data::Properties{ VK_FORMAT_R8G8B8A8_UNORM })); + + if (model == ShadingModel::Phong) + { + shaderSet->addDescriptorBinding( + "normalMap", "VSG_NORMAL_MAP", MaterialDescriptorSet, 2, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, + vec3Array2D::create(1, 1, Data::Properties{ VK_FORMAT_R32G32B32_SFLOAT }), + CoordinateSpace::LINEAR); + shaderSet->addDescriptorBinding( + "aoMap", "VSG_LIGHTMAP_MAP", MaterialDescriptorSet, 3, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, + vec4Array2D::create(1, 1, Data::Properties{ VK_FORMAT_R32_SFLOAT })); + shaderSet->addDescriptorBinding( + "emissiveMap", "VSG_EMISSIVE_MAP", MaterialDescriptorSet, 4, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, + vec4Array2D::create(1, 1, Data::Properties{ VK_FORMAT_R8G8B8A8_UNORM })); + } + + shaderSet->addDescriptorBinding( + "displacementMap", "VSG_DISPLACEMENT_MAP", MaterialDescriptorSet, 7, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_VERTEX_BIT, + floatArray2D::create(1, 1, Data::Properties{ VK_FORMAT_R32_SFLOAT }), + CoordinateSpace::LINEAR); + shaderSet->addDescriptorBinding("displacementMapScale", "VSG_DISPLACEMENT_MAP", + MaterialDescriptorSet, 8, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + 1, VK_SHADER_STAGE_VERTEX_BIT, + vec3Value::create(1.0f, 1.0f, 1.0f)); + + shaderSet->addDescriptorBinding( + "material", "", MaterialDescriptorSet, 10, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, + VK_SHADER_STAGE_FRAGMENT_BIT, PhongMaterialValue::create(), CoordinateSpace::LINEAR); + shaderSet->addDescriptorBinding( + "texCoordIndices", "", MaterialDescriptorSet, 11, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, + VK_SHADER_STAGE_FRAGMENT_BIT, TexCoordIndicesValue::create(), CoordinateSpace::LINEAR); + + shaderSet->addDescriptorBinding("jointMatrices", "VSG_SKINNING", MaterialDescriptorSet, 12, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, + VK_SHADER_STAGE_VERTEX_BIT, mat4Value::create()); + + shaderSet->addDescriptorBinding( + "lightData", "", ViewDescriptorSet, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, vec4Array::create(64)); + shaderSet->addDescriptorBinding("viewportData", "", ViewDescriptorSet, 1, + VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, + VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + vec4Value::create(.0f, .0f, 1280.0f, 1024.0f)); + shaderSet->addDescriptorBinding( + "shadowMaps", "", ViewDescriptorSet, 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, + VK_SHADER_STAGE_FRAGMENT_BIT, + floatArray3D::create(1, 1, 1, Data::Properties{ VK_FORMAT_R32_SFLOAT })); + + if (model == ShadingModel::Phong) + { + shaderSet->addDescriptorBinding("shadowMapDirectSampler", "VSG_SHADOWS_PCSS", + ViewDescriptorSet, 3, VK_DESCRIPTOR_TYPE_SAMPLER, 1, + VK_SHADER_STAGE_FRAGMENT_BIT, nullptr); + shaderSet->addDescriptorBinding("shadowMapShadowSampler", "", ViewDescriptorSet, 4, + VK_DESCRIPTOR_TYPE_SAMPLER, 1, + VK_SHADER_STAGE_FRAGMENT_BIT, nullptr); + } + + shaderSet->addDescriptorBinding("opaqueDepth", "DEPTHPEELING_PASS", PeelDescriptorSet, 0, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, + VK_SHADER_STAGE_FRAGMENT_BIT, {}); + shaderSet->addDescriptorBinding("prevPassDepth", "DEPTHPEELING_PASS", PeelDescriptorSet, 1, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, + VK_SHADER_STAGE_FRAGMENT_BIT, {}); + + shaderSet->addPushConstantRange("pc", "", VK_SHADER_STAGE_ALL, 0, 128); + + if (model == ShadingModel::Phong) + { + shaderSet->optionalDefines = { "VSG_GREYSCALE_DIFFUSE_MAP", + "VSG_TWO_SIDED_LIGHTING", + "VSG_POINT_SPRITE", + "VSG_SHADOWS_PCSS", + "VSG_SHADOWS_SOFT", + "VSG_SHADOWS_HARD", + "SHADOWMAP_DEBUG" }; + } + else + { + shaderSet->optionalDefines = { "VSG_POINT_SPRITE", "VSG_GREYSCALE_DIFFUSE_MAP" }; + } + shaderSet->optionalDefines.insert("DEPTHPEELING_PASS"); + shaderSet->optionalDefines.insert("DEPTHPEELING_FIRSTPASS"); + + shaderSet->definesArrayStates.push_back( + DefinesArrayState{ {"VSG_INSTANCE_TRANSLATION"}, TranslationArrayState::create() }); + shaderSet->definesArrayStates.push_back(DefinesArrayState{ + {"VSG_INSTANCE_TRANSLATION", "VSG_INSTANCE_ROTATION", "VSG_INSTANCE_SCALE"}, + TranslationRotationScaleArrayState::create() }); + shaderSet->definesArrayStates.push_back( + DefinesArrayState{ {"VSG_INSTANCE_TRANSLATION", "VSG_DISPLACEMENT_MAP"}, + TranslationAndDisplacementMapArrayState::create() }); + shaderSet->definesArrayStates.push_back( + DefinesArrayState{ {"VSG_DISPLACEMENT_MAP"}, DisplacementMapArrayState::create() }); + shaderSet->definesArrayStates.push_back( + DefinesArrayState{ {"VSG_BILLBOARD"}, BillboardArrayState::create() }); + + shaderSet->customDescriptorSetBindings.push_back( + ViewDependentStateBinding::create(ViewDescriptorSet)); + + shaderSet->defaultShaderHints = ShaderCompileSettings::create(); + if (model == ShadingModel::Phong) + { + shaderSet->defaultShaderHints->defines.insert("VSG_TWO_SIDED_LIGHTING"); + } + for (auto& stage : shaderSet->stages) + { + stage->module->hints = shaderSet->defaultShaderHints; + } + + return shaderSet; +} + +ref_ptr vsg::oit::depthpeeling::getOrCreateShadingShaderSet(ShadingModel model, ref_ptr options) +{ + std::string shaderSetName{ model == ShadingModel::Flat ? FlatShadingModelShaderSetName : PhongShadingModelShaderSetName }; + + // check if a ShaderSet has already been assigned to the options object, if so return it + if (options) + { + if (auto itr = options->shaderSets.find(shaderSetName); itr != options->shaderSets.end()) + { + return itr->second; + } + } + + // if not found then create a new ShaderSet, assign it to the options object and return it + auto shaderSet = createShadingShaderSet(model, options); + + if (options) + { + options->shaderSets[shaderSetName] = shaderSet; + } + + return shaderSet; +} + +ref_ptr createCombineShaderSet(ref_ptr options) +{ + constexpr auto PeelDescriptorSet = 2; + + auto vertexShader = read_cast("shaders/dp_fullscreen.vert", options); + auto fragmentShader = read_cast("shaders/dp_combine.frag", options); + + auto shaderSet = ShaderSet::create(ShaderStages{ vertexShader, fragmentShader }); + + shaderSet->addAttributeBinding("vsg_Vertex", "", 0, VK_FORMAT_R32G32B32_SFLOAT, + vec3Array::create(1)); + shaderSet->addAttributeBinding("vsg_Normal", "", 1, VK_FORMAT_R32G32B32_SFLOAT, + vec3Array::create(1)); + + shaderSet->addDescriptorBinding("peelOutput", "", PeelDescriptorSet, 2, + VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1, + VK_SHADER_STAGE_FRAGMENT_BIT, {}); + + shaderSet->addPushConstantRange("pc", "", VK_SHADER_STAGE_ALL, 0, 128); + + shaderSet->optionalDefines = { "DEPTHPEELING_PASS" }; + + shaderSet->defaultShaderHints = ShaderCompileSettings::create(); + for (auto& stage : shaderSet->stages) + { + stage->module->hints = shaderSet->defaultShaderHints; + } + + return shaderSet; +} + +ref_ptr vsg::oit::depthpeeling::getOrCreateCombineShaderSet(ref_ptr options) +{ + // check if a ShaderSet has already been assigned to the options object, if so return it + if (options) + { + if (auto itr = options->shaderSets.find(CombineShaderSetName); itr != options->shaderSets.end()) + { + return itr->second; + } + } + + // if not found then create a new ShaderSet, assign it to the options object and return it + auto shaderSet = createCombineShaderSet(options); + + if (options) + { + options->shaderSets[CombineShaderSetName] = shaderSet; + } + + return shaderSet; +} diff --git a/examples/oit/vsgdepthpeeling/depthpeeling/ShaderSet.h b/examples/oit/vsgdepthpeeling/depthpeeling/ShaderSet.h new file mode 100644 index 00000000..b3a13487 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/depthpeeling/ShaderSet.h @@ -0,0 +1,23 @@ +#pragma once + +#include "ShaderSet.h" + +#include +#include + +namespace vsg::oit::depthpeeling { + + enum class ShadingModel + { + Flat, + Phong + }; + + constexpr const char* FlatShadingModelShaderSetName = "depthPeeling_flat"; + constexpr const char* PhongShadingModelShaderSetName = "depthPeeling_phong"; + constexpr const char* CombineShaderSetName = "depthPeeling_combine"; + + extern ref_ptr getOrCreateShadingShaderSet(ShadingModel model, ref_ptr options = {}); + extern ref_ptr getOrCreateCombineShaderSet(ref_ptr options = {}); + +} diff --git a/examples/oit/vsgdepthpeeling/vsgdepthpeeling.cpp b/examples/oit/vsgdepthpeeling/vsgdepthpeeling.cpp new file mode 100644 index 00000000..2a31b4f9 --- /dev/null +++ b/examples/oit/vsgdepthpeeling/vsgdepthpeeling.cpp @@ -0,0 +1,446 @@ +#include "depthpeeling/Builder.h" + +#include + +#ifdef vsgXchange_FOUND +# include +#endif + +#include +#include +#include +#include + +vsg::ref_ptr createBox(vsg::oit::depthpeeling::Builder& builder, + vsg::ref_ptr texture, vsg::vec4 color, vsg::vec3 offset) +{ + using namespace vsg; + + vsg::ref_ptr positions; + + auto colors = vsg::vec4Array::create(1, color); + + const vsg::vec3 dx{1.0f, 0.0f, 0.0f}; + const vsg::vec3 dy{0.0f, 1.0f, 0.0f}; + const vsg::vec3 dz{0.0f, 0.0f, 1.0f}; + const auto origin = offset - dx * 0.5f - dy * 0.5f - dz * 0.5f; + + auto [t_origin, t_scale, t_top] = vsg::vec3{0.0f, 1.0f, 1.0f}.value; + + vsg::vec3 v000(origin); + vsg::vec3 v100(origin + dx); + vsg::vec3 v110(origin + dx + dy); + vsg::vec3 v010(origin + dy); + vsg::vec3 v001(origin + dz); + vsg::vec3 v101(origin + dx + dz); + vsg::vec3 v111(origin + dx + dy + dz); + vsg::vec3 v011(origin + dy + dz); + + vsg::vec2 t00(0.0f, t_top); + vsg::vec2 t01(0.0f, t_origin); + vsg::vec2 t10(1.0f, t_top); + vsg::vec2 t11(1.0f, t_origin); + + vsg::ref_ptr vertices; + vsg::ref_ptr normals; + vsg::ref_ptr texcoords; + vsg::ref_ptr indices; + + vsg::vec3 n0 = vsg::normalize(vsg::cross(dx, dz)); + vsg::vec3 n1 = vsg::normalize(vsg::cross(dy, dz)); + vsg::vec3 n2 = -n0; + vsg::vec3 n3 = -n1; + vsg::vec3 n4 = vsg::normalize(vsg::cross(dy, dx)); + vsg::vec3 n5 = -n4; + + // set up vertex and index arrays + vertices = vsg::vec3Array::create( + {v000, v100, v101, v001, // front + v100, v110, v111, v101, // right + v110, v010, v011, v111, // far + v010, v000, v001, v011, // left + v010, v110, v100, v000, // bottom + v001, v101, v111, v011}); // top + + normals = vsg::vec3Array::create( + {n0, n0, n0, n0, + n1, n1, n1, n1, + n2, n2, n2, n2, + n3, n3, n3, n3, + n4, n4, n4, n4, + n5, n5, n5, n5}); + + texcoords = vsg::vec2Array::create( + {t00, t10, t11, t01, + t00, t10, t11, t01, + t00, t10, t11, t01, + t00, t10, t11, t01, + t00, t10, t11, t01, + t00, t10, t11, t01}); + + indices = vsg::ushortArray::create( + {0, 1, 2, 0, 2, 3, + 4, 5, 6, 4, 6, 7, + 8, 9, 10, 8, 10, 11, + 12, 13, 14, 12, 14, 15, + 16, 17, 18, 16, 18, 19, + 20, 21, 22, 20, 22, 23}); + + auto vid = vsg::VertexIndexDraw::create(); + + vsg::DataList arrays; + arrays.push_back(vertices); + if (normals) arrays.push_back(normals); + if (texcoords) arrays.push_back(texcoords); + if (colors) arrays.push_back(colors); + if (positions) arrays.push_back(positions); + vid->assignArrays(arrays); + + vid->assignIndices(indices); + vid->indexCount = static_cast(indices->size()); + vid->instanceCount = 1; + + auto box = vsg::StateGroup::create(); + box->add(builder.getOrCreateMaterialBinding([&texture](auto& descriptorConfigurator, auto& options) { + auto material = vsg::PhongMaterialValue::create(); + material->value().alphaMaskCutoff = 0.95f; + + descriptorConfigurator.assignDescriptor("material", material); + descriptorConfigurator.assignDescriptor("texCoordIndices", vsg::TexCoordIndicesValue::create()); + + auto sampler = vsg::Sampler::create(); + sampler->addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + sampler->addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + if (options.valid() && options->sharedObjects.valid()) + { + options->sharedObjects->share(sampler); + } + + descriptorConfigurator.assignTexture("diffuseMap", texture, sampler); + })); + + box->addChild(vid); + return box; +} + +vsg::ref_ptr createScene(vsg::oit::depthpeeling::Builder& builder, vsg::ref_ptr texture, bool largeScene) +{ + auto scene = vsg::Group::create(); + + static const std::array colors = { + vsg::vec4(1.0, 1.0, 0.0, 1.0), + vsg::vec4(0.0, 1.0, 1.0, 1.0), + vsg::vec4(1.0, 0.0, 0.0, 0.5), + vsg::vec4(0.0, 1.0, 0.0, 0.5), + vsg::vec4(0.0, 0.0, 1.0, 0.5)}; + + if (largeScene) + { + auto object = 0u; + for (auto z = -3; z <= 3; ++z) + { + for (auto x = -3; x <= 3; ++x) + { + for (auto y = -3; y <= 3; ++y, ++object) + { + scene->addChild(createBox( + builder, texture, colors[object % colors.size()], vsg::vec3(x * 1.5, y * 1.5, z * 1.5))); + } + } + } + } + else + { + scene->addChild(createBox(builder, texture, colors[0], vsg::vec3(1.25, 0.0, 0.0))); + scene->addChild(createBox(builder, texture, colors[1], vsg::vec3(0.0, -1.25, 0.0))); + scene->addChild(createBox(builder, texture, colors[2], vsg::vec3(-1.25, 0.0, 0.0))); + scene->addChild(createBox(builder, texture, colors[3], vsg::vec3(0.0, 0.0, 0.0))); + scene->addChild(createBox(builder, texture, colors[4], vsg::vec3(0.0, 1.25, 0.0))); + } + + return scene; +} + +int main(int argc, char** argv) +{ + try + { + // set up defaults and read command line arguments to override them + vsg::CommandLine arguments(&argc, argv); + + // create windowTraits using the any command line arugments to configure settings + auto windowTraits = vsg::WindowTraits::create(arguments); + + // NOTE: + // depth peeling requires the use of input attachments to read back the depth and color information from previous peels, + // so we need to ensure that the swapchain image usage and depth image usage include VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT + // and we also need to ensure that the depth image usage includes VK_IMAGE_USAGE_TRANSFER_SRC_BIT so that we can use + // the depth image in transparency passes to reject fragments that are behind the nearest opaque fragments. + windowTraits->swapchainPreferences.imageUsage |= + (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT); + windowTraits->depthImageUsage |= + (VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); + + // if we want to redirect std::cout and std::cerr to the vsg::Logger call vsg::Logger::redirect_stdout() + if (arguments.read({"--redirect-std", "-r"})) vsg::Logger::instance()->redirect_std(); + + // set up vsg::Options to pass in filepaths, ReaderWriters and other IO related options to use when reading and writing files. + auto options = vsg::Options::create(); + options->sharedObjects = vsg::SharedObjects::create(); + options->fileCache = vsg::getEnv("VSG_FILE_CACHE"); + options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); + +#ifdef vsgXchange_all + // add vsgXchange's support for reading and writing 3rd party file formats + options->add(vsgXchange::all::create()); +#endif + + options->readOptions(arguments); + + if (uint32_t numOperationThreads = 0; arguments.read("--ot", numOperationThreads)) options->operationThreads = vsg::OperationThreads::create(numOperationThreads); + + if (arguments.read({ "-s", "--sampled" })) + { + windowTraits->samples = VK_SAMPLE_COUNT_8_BIT; + } + + bool reportAverageFrameRate = arguments.read("--fps"); + bool reportMemoryStats = arguments.read("--rms"); + if (arguments.read({"-t", "--test"})) + { + windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + windowTraits->fullscreen = true; + reportAverageFrameRate = true; + } + if (arguments.read({"--st", "--small-test"})) + { + windowTraits->swapchainPreferences.presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; + windowTraits->width = 192, windowTraits->height = 108; + windowTraits->decoration = false; + reportAverageFrameRate = true; + } + + auto largeScene = arguments.read({"--ls", "--large-scene"}); + + bool multiThreading = arguments.read("--mt"); + auto maxTime = arguments.value(std::numeric_limits::max(), "--max-time"); + + if (arguments.read("--ThreadLogger")) vsg::Logger::instance() = vsg::ThreadLogger::create(); + if (int log_level = 0; arguments.read("--log-level", log_level)) vsg::Logger::instance()->level = vsg::Logger::Level(log_level); + auto numFrames = arguments.value(-1, "-f"); + + if (int log_level = 0; arguments.read("--log-level", log_level)) vsg::Logger::instance()->level = vsg::Logger::Level(log_level); + auto logFilename = arguments.value("", "--log"); + + auto nearFarRatio = arguments.value(0.001, "--nfr"); + + vsg::ref_ptr instrumentation; + if (arguments.read({"--gpu-annotation", "--ga"}) && vsg::isExtensionSupported(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) + { + windowTraits->debugUtils = true; + + auto gpu_instrumentation = vsg::GpuAnnotation::create(); + if (arguments.read("--name")) + gpu_instrumentation->labelType = vsg::GpuAnnotation::SourceLocation_name; + else if (arguments.read("--className")) + gpu_instrumentation->labelType = vsg::GpuAnnotation::Object_className; + else if (arguments.read("--func")) + gpu_instrumentation->labelType = vsg::GpuAnnotation::SourceLocation_function; + + instrumentation = gpu_instrumentation; + } + else if (arguments.read({"--profiler", "--pr"})) + { + // set Profiler options + auto settings = vsg::Profiler::Settings::create(); + arguments.read("--cpu", settings->cpu_instrumentation_level); + arguments.read("--gpu", settings->gpu_instrumentation_level); + arguments.read("--log-size", settings->log_size); + + // create the profiler + instrumentation = vsg::Profiler::create(settings); + } + + vsg::Affinity affinity; + uint32_t cpu = 0; + while (arguments.read("-c", cpu)) + { + affinity.cpus.insert(cpu); + } + + if (arguments.errors()) return arguments.writeErrorMessages(std::cerr); + + // read first texture file specified on command line + vsg::ref_ptr texture; + for (int i = 1; i < argc; ++i) + { + auto object = vsg::read(arguments[i], options); + if (texture = object.cast(); texture && texture->available()) + { + break; + } + + texture.reset(); + } + + if (!texture) + { + texture = vsg::read("textures/wood.png", options).cast(); + } + + if (!texture || texture->empty()) + { + std::cout << "Please specify a texture file on the command line or ensure the default texture 'textures/wood.png' is available." << std::endl; + return 1; + } + + // create the viewer and assign window(s) to it + auto viewer = vsg::Viewer::create(); + auto window = vsg::Window::create(windowTraits); + if (!window) + { + std::cout << "Could not create window." << std::endl; + return 1; + } + + viewer->addWindow(window); + + // create depth peeling builder + auto builder = vsg::oit::depthpeeling::Builder::create( + vsg::oit::depthpeeling::Builder::Settings::create(window, options)); + + auto scene = createScene(*builder, texture, largeScene); + + vsg::ref_ptr lookAt; + vsg::ref_ptr perspective; + + { + // compute the bounds of the scene graph to help position camera + vsg::ComputeBounds computeBounds; + scene->accept(computeBounds); + + vsg::dvec3 centre = (computeBounds.bounds.min + computeBounds.bounds.max) * 0.5; + double radius = vsg::length(computeBounds.bounds.max - computeBounds.bounds.min) * 0.6; + + // set up the camera + lookAt = vsg::LookAt::create(centre + vsg::dvec3(0.0, -radius * 3.5, 0.0), centre, vsg::dvec3(0.0, 0.0, 1.0)); + perspective = vsg::Perspective::create(30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio * radius, radius * 10.5); + } + + auto camera = vsg::Camera::create(perspective, lookAt, vsg::ViewportState::create(window->extent2D())); + + // assign the camera and scenes to the builder - NOTE: the same scene can be used for both the opaque and transparency + builder->setCamera(camera); + builder->setScene(vsg::oit::depthpeeling::Builder::Pass::Opaque, scene); + builder->setScene(vsg::oit::depthpeeling::Builder::Pass::Transparency, scene); + + // add close handler to respond to the close window button and pressing escape + viewer->addEventHandler(vsg::CloseHandler::create(viewer)); + // add trackball handler to respond to mouse events for interactive camera control + viewer->addEventHandler(vsg::Trackball::create(camera)); + + // create the needed render graphs used for depth peeling + auto renderGraphs = builder->createRenderGraphs(); + + // create command graph and assign the render graphs to it + auto commandGraph = vsg::CommandGraph::create(window); + commandGraph->addChild(renderGraphs.opaque); + commandGraph->addChild(renderGraphs.transparency); + + viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); + + if (instrumentation) viewer->assignInstrumentation(instrumentation); + + if (multiThreading) + { + viewer->setupThreading(); + + if (affinity) + { + auto cpu_itr = affinity.cpus.begin(); + + // set affinity of main thread + if (cpu_itr != affinity.cpus.end()) + { + std::cout << "vsg::setAffinity() " << *cpu_itr << std::endl; + vsg::setAffinity(vsg::Affinity(*cpu_itr++)); + } + + for (auto& thread : viewer->threads) + { + if (thread.joinable() && cpu_itr != affinity.cpus.end()) + { + std::cout << "vsg::setAffinity(" << thread.get_id() << ") " << *cpu_itr << std::endl; + vsg::setAffinity(thread, vsg::Affinity(*cpu_itr++)); + } + } + } + } + else if (affinity) + { + std::cout << "vsg::setAffinity("; + for (auto cpu_num : affinity.cpus) + { + std::cout << " " << cpu_num; + } + std::cout << " )" << std::endl; + + vsg::setAffinity(affinity); + } + + viewer->compile(); + viewer->start_point() = vsg::clock::now(); + + // rendering main loop + while (viewer->advanceToNextFrame() && (numFrames < 0 || (numFrames--) > 0) && (viewer->getFrameStamp()->simulationTime < maxTime)) + { + // pass any events into EventHandlers assigned to the Viewer + viewer->handleEvents(); + + viewer->update(); + + viewer->recordAndSubmit(); + + viewer->present(); + } + + if (reportAverageFrameRate) + { + auto fs = viewer->getFrameStamp(); + double fps = static_cast(fs->frameCount) / std::chrono::duration(vsg::clock::now() - viewer->start_point()).count(); + std::cout << "Average frame rate = " << fps << " fps" << std::endl; + } + + if (reportMemoryStats) + { + if (options->sharedObjects) + { + vsg::LogOutput output; + options->sharedObjects->report(output); + } + } + + if (auto profiler = instrumentation.cast()) + { + instrumentation->finish(); + if (logFilename) + { + std::ofstream fout(logFilename); + profiler->log->report(fout); + } + else + { + profiler->log->report(std::cout); + } + } + } + catch (const vsg::Exception& ve) + { + for (int i = 0; i < argc; ++i) std::cerr << argv[i] << " "; + std::cerr << "\n[Exception] - " << ve.message << " result = " << ve.result << std::endl; + return 1; + } + + // clean up done automatically thanks to ref_ptr<> + return 0; +}