diff --git a/CMakeLists.txt b/CMakeLists.txt index ea413441..7323541a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10) project(vsgExamples - VERSION 1.1.11 + VERSION 1.1.14 DESCRIPTION "Set of example programs that test and illustrate how to use the VulkanSceneGraph" LANGUAGES CXX C ) @@ -21,13 +21,13 @@ if (VULKAN_SDK) set(ENV{VULKAN_SDK} ${VULKAN_SDK}) endif() -find_package(vsg 1.1.14) +find_package(vsg 1.1.13) vsg_setup_dir_vars() vsg_setup_build_vars() # find the optional vsgXchange that can be used for reading a range of image and 3d model formats and shader compilation -find_package(vsgXchange 1.1.9 QUIET) +find_package(vsgXchange 1.1.12 QUIET) # find the optional vsgImGui that can be used for GUI elements added into graphics windows. find_package(vsgImGui QUIET) diff --git a/data/shaders/barycentric.frag b/data/shaders/barycentric.frag index c0e513f7..596df065 100644 --- a/data/shaders/barycentric.frag +++ b/data/shaders/barycentric.frag @@ -4,5 +4,19 @@ layout(location = 0) out vec4 color; void main() { + float margin = 0.02; + if (min(min(gl_BaryCoordEXT.x, gl_BaryCoordEXT.y), gl_BaryCoordEXT.z) < margin) + { + color = vec4(0.0, 0.0, 0.0, 1.0); + return; + } + + float radius = 0.1; + if (length(gl_BaryCoordEXT-vec3(0.333, 0.333, 0.333)) < radius) + { + color = vec4(1.0, 1.0, 1.0, 1.0); + return; + } + color = vec4(gl_BaryCoordEXT, 1.0); } 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/shaders/highlight.frag b/data/shaders/highlight.frag new file mode 100644 index 00000000..dc43cb72 --- /dev/null +++ b/data/shaders/highlight.frag @@ -0,0 +1,152 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) in vec3 normalDir; +layout(location = 1) in vec3 eyePos; +layout(location = 2) in vec3 viewDir; +layout(set = 0, binding = 0) uniform SlcColors +{ + vec4 values[3]; +} slcColors; +layout(set = 1, binding = 0) uniform LightData +{ + vec4 values[256]; // 256??? +} lightData; +layout(set = 2, binding = 0) uniform MaterialData +{ + vec4 ambientColor; + vec4 diffuseColor; + vec4 specularColor; + vec4 emissiveColor; + float shininess; + float alphaMask; + float alphaMaskCutoff; +} material; +layout(set = 3, binding = 0) uniform SlcIndex +{ + uint value; +} slcIndex; + +layout(location = 0) out vec4 outColor; + +void main() +{ + float intensityMinimum = 0.001; + vec3 nd = normalize(normalDir); + vec3 vd = normalize(viewDir); + + vec3 color = vec3(0.0, 0.0, 0.0); + vec3 diffuse = material.diffuseColor.rgb; + if (slcIndex.value == 1 || slcIndex.value == 2) diffuse = slcColors.values[slcIndex.value].rgb; + + 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; i < numAmbientLights; ++i) + { + vec4 lightColor = lightData.values[lightDataIndex++]; + color += (material.ambientColor.rgb * lightColor.rgb) * (lightColor.a); + } + } + + if (numDirectionalLights > 0) + { + for(int i = 0; i < numDirectionalLights; ++i) + { + vec4 lightColor = lightData.values[lightDataIndex++]; + vec3 direction = -lightData.values[lightDataIndex++].xyz; + + float intensity = lightColor.a; + + float unclamped_LdotN = dot(direction, nd); + float diff = max(unclamped_LdotN, 0.0); + intensity *= diff; + + color.rgb += (diffuse.rgb * lightColor.rgb) * (intensity); + + if (material.shininess > 0.0 && diff > 0.0) + { + vec3 halfDir = normalize(direction + vd); + color.rgb += material.specularColor.rgb * (pow(max(dot(halfDir, nd), 0.0), material.shininess) * intensity); + } + lightDataIndex++; + } + } + + if (numPointLights>0) + { + // point light + for(int i = 0; i 0.0 && diff > 0.0) + { + vec3 halfDir = normalize(direction + vd); + color.rgb += material.specularColor.rgb * (pow(max(dot(halfDir, nd), 0.0), material.shininess) * scale); + } + } + } + + if (numSpotLights>0) + { + for(int i = 0; i < numSpotLights; ++i) + { + vec4 lightColor = lightData.values[lightDataIndex++]; + vec4 position_cosInnerAngle = lightData.values[lightDataIndex++]; + vec4 lightDirection_cosOuterAngle = lightData.values[lightDataIndex++]; + + float intensity = lightColor.a; + + vec3 delta = position_cosInnerAngle.xyz - eyePos; + float distance2 = delta.x * delta.x + delta.y * delta.y + delta.z * delta.z; + float dist = sqrt(distance2); + + vec3 direction = delta / dist; + + float dot_lightdirection = dot(lightDirection_cosOuterAngle.xyz, -direction); + + // 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 += (diffuse.rgb * lightColor.rgb) * diff; + if (material.shininess > 0.0 && diff > 0.0) + { + vec3 halfDir = normalize(direction + vd); + color.rgb += material.specularColor.rgb * (pow(max(dot(halfDir, nd), 0.0), material.shininess) * scale); + } + lightDataIndex++; + } + } + + outColor = vec4(color, material.diffuseColor.a); +} diff --git a/data/shaders/highlight.vert b/data/shaders/highlight.vert new file mode 100644 index 00000000..28de611e --- /dev/null +++ b/data/shaders/highlight.vert @@ -0,0 +1,30 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(push_constant) uniform PushConstants +{ + mat4 projection; + mat4 modelview; +} pc; + +layout(location = 0) in vec3 vsg_Vertex; +layout(location = 1) in vec3 vsg_Normal; + +layout(location = 0) out vec3 normalDir; +layout(location = 1) out vec3 eyePos; +layout(location = 2) out vec3 viewDir; + +out gl_PerVertex +{ + vec4 gl_Position; +}; + +void main() +{ + vec4 tVertex = vec4(vsg_Vertex, 1.0); + vec4 tNormal = vec4(vsg_Normal, 0.0); + gl_Position = (pc.projection * pc.modelview) * tVertex; + eyePos = (pc.modelview * tVertex).xyz; + viewDir = - (pc.modelview * tVertex).xyz; + normalDir = (pc.modelview * tNormal).xyz; +} diff --git a/data/shaders/wireframe_pbr.frag b/data/shaders/wireframe_pbr.frag new file mode 100644 index 00000000..f2fa2198 --- /dev/null +++ b/data/shaders/wireframe_pbr.frag @@ -0,0 +1,574 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable +#extension GL_EXT_fragment_shader_barycentric : 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_METALLROUGHNESS_MAP, VSG_SPECULAR_MAP, VSG_TWO_SIDED_LIGHTING, VSG_WORKFLOW_SPECGLOSS, VSG_SHADOWS_PCSS, VSG_SHADOWS_SOFT, VSG_SHADOWS_HARD, SHADOWMAP_DEBUG, VSG_ALPHA_TEST) + +// define by default for backwards compatibility +#define VSG_SHADOWS_HARD + +#define VIEW_DESCRIPTOR_SET 0 +#define MATERIAL_DESCRIPTOR_SET 1 + +#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 + +const float PI = 3.14159265359; +const float RECIPROCAL_PI = 0.31830988618; +const float RECIPROCAL_PI2 = 0.15915494; +const float EPSILON = 1e-6; +const float c_MinRoughness = 0.04; + +#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 + +#ifdef VSG_METALLROUGHNESS_MAP +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 6) uniform sampler2D mrMap; +#endif + +layout(set = MATERIAL_DESCRIPTOR_SET, binding = 10) uniform PbrMaterial +{ + vec4 baseColorFactor; + vec4 emissiveFactor; + vec4 diffuseFactor; + vec4 specularFactor; + float metallicFactor; + float roughnessFactor; + float alphaMask; + float alphaMaskCutoff; +} pbr; + +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; + +// ViewDependentState +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]; + +layout(location = 0) out vec4 outColor; + + +// Encapsulate the various inputs used by the various functions in the shading equation +// We store values in this struct to simplify the integration of alternative implementations +// of the shading terms, outlined in the Readme.MD Appendix. +struct PBRInfo +{ + float NdotL; // cos angle between normal and light direction + float NdotV; // cos angle between normal and view direction + float NdotH; // cos angle between normal and half vector + float LdotH; // cos angle between light direction and half vector + float VdotH; // cos angle between view direction and half vector + float VdotL; // cos angle between view direction and light direction + float perceptualRoughness; // roughness value, as authored by the model creator (input to shader) + float metalness; // metallic value at the surface + vec3 reflectance0; // full reflectance color (normal incidence angle) + vec3 reflectance90; // reflectance color at grazing angle + float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2]) + vec3 diffuseColor; // color contribution from diffuse lighting + vec3 specularColor; // color contribution from specular lighting +}; + +float rcp(const in float value) +{ + return 1.0 / value; +} + +float pow5(const in float value) +{ + return value * value * value * value * value; +} + +// 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; +} + +// Basic Lambertian diffuse +// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog +// See also [1], Equation 1 +vec3 BRDF_Diffuse_Lambert(PBRInfo pbrInputs) +{ + return pbrInputs.diffuseColor * RECIPROCAL_PI; +} + +vec3 BRDF_Diffuse_Custom_Lambert(PBRInfo pbrInputs) +{ + return pbrInputs.diffuseColor * RECIPROCAL_PI * pow(pbrInputs.NdotV, 0.5 + 0.3 * pbrInputs.perceptualRoughness); +} + +// [Gotanda 2012, "Beyond a Simple Physically Based Blinn-Phong Model in Real-Time"] +vec3 BRDF_Diffuse_OrenNayar(PBRInfo pbrInputs) +{ + float a = pbrInputs.alphaRoughness; + float s = a;// / ( 1.29 + 0.5 * a ); + float s2 = s * s; + float VoL = 2 * pbrInputs.VdotH * pbrInputs.VdotH - 1; // double angle identity + float Cosri = pbrInputs.VdotL - pbrInputs.NdotV * pbrInputs.NdotL; + float C1 = 1 - 0.5 * s2 / (s2 + 0.33); + float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * ( Cosri >= 0 ? 1.0 / max(pbrInputs.NdotL, pbrInputs.NdotV) : 1 ); + return pbrInputs.diffuseColor / PI * ( C1 + C2 ) * ( 1 + pbrInputs.perceptualRoughness * 0.5 ); +} + +// [Gotanda 2014, "Designing Reflectance Models for New Consoles"] +vec3 BRDF_Diffuse_Gotanda(PBRInfo pbrInputs) +{ + float a = pbrInputs.alphaRoughness; + float a2 = a * a; + float F0 = 0.04; + float VoL = 2 * pbrInputs.VdotH * pbrInputs.VdotH - 1; // double angle identity + float Cosri = VoL - pbrInputs.NdotV * pbrInputs.NdotL; + float a2_13 = a2 + 1.36053; + float Fr = ( 1 - ( 0.542026*a2 + 0.303573*a ) / a2_13 ) * ( 1 - pow( 1 - pbrInputs.NdotV, 5 - 4*a2 ) / a2_13 ) * ( ( -0.733996*a2*a + 1.50912*a2 - 1.16402*a ) * pow( 1 - pbrInputs.NdotV, 1 + rcp(39*a2*a2+1) ) + 1 ); + //float Fr = ( 1 - 0.36 * a ) * ( 1 - pow( 1 - NoV, 5 - 4*a2 ) / a2_13 ) * ( -2.5 * Roughness * ( 1 - NoV ) + 1 ); + float Lm = ( max( 1 - 2*a, 0 ) * ( 1 - pow5( 1 - pbrInputs.NdotL ) ) + min( 2*a, 1 ) ) * ( 1 - 0.5*a * (pbrInputs.NdotL - 1) ) * pbrInputs.NdotL; + float Vd = ( a2 / ( (a2 + 0.09) * (1.31072 + 0.995584 * pbrInputs.NdotV) ) ) * ( 1 - pow( 1 - pbrInputs.NdotL, ( 1 - 0.3726732 * pbrInputs.NdotV * pbrInputs.NdotV ) / ( 0.188566 + 0.38841 * pbrInputs.NdotV ) ) ); + float Bp = Cosri < 0 ? 1.4 * pbrInputs.NdotV * pbrInputs.NdotL * Cosri : Cosri; + float Lr = (21.0 / 20.0) * (1 - F0) * ( Fr * Lm + Vd + Bp ); + return pbrInputs.diffuseColor * RECIPROCAL_PI * Lr; +} + +vec3 BRDF_Diffuse_Burley(PBRInfo pbrInputs) +{ + float energyBias = mix(pbrInputs.perceptualRoughness, 0.0, 0.5); + float energyFactor = mix(pbrInputs.perceptualRoughness, 1.0, 1.0 / 1.51); + float fd90 = energyBias + 2.0 * pbrInputs.VdotH * pbrInputs.VdotH * pbrInputs.perceptualRoughness; + float f0 = 1.0; + float lightScatter = f0 + (fd90 - f0) * pow(1.0 - pbrInputs.NdotL, 5.0); + float viewScatter = f0 + (fd90 - f0) * pow(1.0 - pbrInputs.NdotV, 5.0); + + return pbrInputs.diffuseColor * lightScatter * viewScatter * energyFactor; +} + +vec3 BRDF_Diffuse_Disney(PBRInfo pbrInputs) +{ + float Fd90 = 0.5 + 2.0 * pbrInputs.perceptualRoughness * pbrInputs.VdotH * pbrInputs.VdotH; + vec3 f0 = vec3(0.1); + vec3 invF0 = vec3(1.0, 1.0, 1.0) - f0; + float dim = min(invF0.r, min(invF0.g, invF0.b)); + float result = ((1.0 + (Fd90 - 1.0) * pow(1.0 - pbrInputs.NdotL, 5.0 )) * (1.0 + (Fd90 - 1.0) * pow(1.0 - pbrInputs.NdotV, 5.0 ))) * dim; + return pbrInputs.diffuseColor * result; +} + +// The following equation models the Fresnel reflectance term of the spec equation (aka F()) +// Implementation of fresnel from [4], Equation 15 +vec3 specularReflection(PBRInfo pbrInputs) +{ + //return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0); + return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance90*pbrInputs.reflectance0) * exp2((-5.55473 * pbrInputs.VdotH - 6.98316) * pbrInputs.VdotH); +} + +// This calculates the specular geometric attenuation (aka G()), +// where rougher material will reflect less light back to the viewer. +// This implementation is based on [1] Equation 4, and we adopt their modifications to +// alphaRoughness as input as originally proposed in [2]. +float geometricOcclusion(PBRInfo pbrInputs) +{ + float NdotL = pbrInputs.NdotL; + float NdotV = pbrInputs.NdotV; + float r = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness; + + float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r + (1.0 - r) * (NdotL * NdotL))); + float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r + (1.0 - r) * (NdotV * NdotV))); + return attenuationL * attenuationV; +} + +// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D()) +// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz +// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3. +float microfacetDistribution(PBRInfo pbrInputs) +{ + float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness; + float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0; + return roughnessSq / (PI * f * f); +} + +vec3 BRDF(vec3 u_LightColor, vec3 v, vec3 n, vec3 l, vec3 h, float perceptualRoughness, float metallic, vec3 specularEnvironmentR0, vec3 specularEnvironmentR90, float alphaRoughness, vec3 diffuseColor, vec3 specularColor, float ao) +{ + float unclmapped_NdotL = dot(n, l); + + vec3 reflection = -normalize(reflect(v, n)); + reflection.y *= -1.0f; + + float NdotL = clamp(unclmapped_NdotL, 0.001, 1.0); + float NdotV = clamp(abs(dot(n, v)), 0.001, 1.0); + float NdotH = clamp(dot(n, h), 0.0, 1.0); + float LdotH = clamp(dot(l, h), 0.0, 1.0); + float VdotH = clamp(dot(v, h), 0.0, 1.0); + float VdotL = clamp(dot(v, l), 0.0, 1.0); + + PBRInfo pbrInputs = PBRInfo(NdotL, + NdotV, + NdotH, + LdotH, + VdotH, + VdotL, + perceptualRoughness, + metallic, + specularEnvironmentR0, + specularEnvironmentR90, + alphaRoughness, + diffuseColor, + specularColor); + + // Calculate the shading terms for the microfacet specular shading model + vec3 F = specularReflection(pbrInputs); + float G = geometricOcclusion(pbrInputs); + float D = microfacetDistribution(pbrInputs); + + // Calculation of analytical lighting contribution + vec3 diffuseContrib = (1.0 - F) * BRDF_Diffuse_Disney(pbrInputs); + vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV); + // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law) + vec3 color = NdotL * u_LightColor * (diffuseContrib + specContrib); + + color *= ao; + + return color; +} + +float convertMetallic(vec3 diffuse, vec3 specular, float maxSpecular) +{ + float perceivedDiffuse = sqrt(0.299 * diffuse.r * diffuse.r + 0.587 * diffuse.g * diffuse.g + 0.114 * diffuse.b * diffuse.b); + float perceivedSpecular = sqrt(0.299 * specular.r * specular.r + 0.587 * specular.g * specular.g + 0.114 * specular.b * specular.b); + + if (perceivedSpecular < c_MinRoughness) + { + return 0.0; + } + + float a = c_MinRoughness; + float b = perceivedDiffuse * (1.0 - maxSpecular) / (1.0 - c_MinRoughness) + perceivedSpecular - 2.0 * c_MinRoughness; + float c = c_MinRoughness - perceivedSpecular; + float D = max(b * b - 4.0 * a * c, 0.0); + return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0); +} + +void main() +{ + float margin = 0.02; + if (min(min(gl_BaryCoordEXT.x, gl_BaryCoordEXT.y), gl_BaryCoordEXT.z) < margin) + { + outColor = vec4(0.0, 0.0, 0.0, 1.0); + return; + } + + float intensityMinimum = 0.001; + +#ifdef VSG_POINT_SPRITE + const vec2 texCoordDiffuse = gl_PointCoord.xy; +#else + const vec2 texCoordDiffuse = texCoord[texCoordIndices.diffuseMap].st; +#endif + + float perceptualRoughness = 0.0; + float metallic; + vec3 diffuseColor; + vec4 baseColor; + + float ambientOcclusion = 1.0; + + vec3 f0 = vec3(0.04); + +#ifdef VSG_DIFFUSE_MAP + #ifdef VSG_GREYSCALE_DIFFUSE_MAP + float v = texture(diffuseMap, texCoordDiffuse).s * pbr.baseColorFactor; + baseColor = vertexColor * vec4(v, v, v, 1.0); + #else + baseColor = vertexColor * texture(diffuseMap, texCoordDiffuse) * pbr.baseColorFactor; + #endif +#else + baseColor = vertexColor * pbr.baseColorFactor; +#endif + + +#ifdef VSG_DETAIL_MAP + vec4 detailColor = texture(detailMap, texCoord[texCoordIndices.detailMap].st); + baseColor.rgb = mix(baseColor.rgb, detailColor.rgb, detailColor.a); +#endif + + +#ifdef VSG_ALPHA_TEST + if (pbr.alphaMask == 1.0f && baseColor.a < pbr.alphaMaskCutoff) discard; +#endif + +#ifdef VSG_WORKFLOW_SPECGLOSS + #ifdef VSG_DIFFUSE_MAP + vec4 diffuse = texture(diffuseMap, texCoord[texCoordIndices.diffuseMap]); + #else + vec4 diffuse = vec4(1.0); + #endif + + #ifdef VSG_SPECULAR_MAP + vec4 specular_texel = texture(specularMap, texCoord[texCoordIndices.specularMap]); + vec3 specular = specular_texel.rgb; + perceptualRoughness = 1.0 - specular_texel.a; + #else + vec3 specular = vec3(0.0); + perceptualRoughness = 0.0; + #endif + + float maxSpecular = max(max(specular.r, specular.g), specular.b); + + // Convert metallic value from specular glossiness inputs + metallic = convertMetallic(diffuse.rgb, specular, maxSpecular); + + const float epsilon = 1e-6; + vec3 baseColorDiffusePart = diffuse.rgb * ((1.0 - maxSpecular) / (1 - c_MinRoughness) / max(1 - metallic, epsilon)) * pbr.diffuseFactor.rgb; + vec3 baseColorSpecularPart = specular - (vec3(c_MinRoughness) * (1 - metallic) * (1 / max(metallic, epsilon))) * pbr.specularFactor.rgb; + baseColor = vec4(mix(baseColorDiffusePart, baseColorSpecularPart, metallic * metallic), diffuse.a); +#else + perceptualRoughness = pbr.roughnessFactor; + metallic = pbr.metallicFactor; + + #ifdef VSG_METALLROUGHNESS_MAP + vec4 mrSample = texture(mrMap, texCoord[texCoordIndices.mrMap]); + perceptualRoughness = mrSample.g * perceptualRoughness; + metallic = mrSample.b * metallic; + #endif +#endif + +#ifdef VSG_LIGHTMAP_MAP + ambientOcclusion = texture(aoMap, texCoord[texCoordIndices.aoMap]).r; +#endif + + diffuseColor = baseColor.rgb * (vec3(1.0) - f0); + diffuseColor *= 1.0 - metallic; + + float alphaRoughness = perceptualRoughness * perceptualRoughness; + + vec3 specularColor = mix(f0, baseColor.rgb, metallic); + + // Compute reflectance. + float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); + + // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. + // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. + float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); + vec3 specularEnvironmentR0 = specularColor.rgb; + vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90; + + vec3 n = getNormal(); + vec3 v = normalize(viewDir); // Vector from surface point to camera + + float shininess = 100.0f; + + 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; + + vec3 l = direction; // Vector from surface point to light + vec3 h = normalize(l+v); // Half vector between both l and v + float scale = intensity; + + color.rgb += BRDF(lightColor.rgb * scale, v, n, l, h, perceptualRoughness, metallic, specularEnvironmentR0, specularEnvironmentR90, alphaRoughness, diffuseColor, specularColor, ambientOcclusion); + } + } + + if (numPointLights>0) + { + // point light + 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)); + + // 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; + + vec3 l = direction; // Vector from surface point to light + vec3 h = normalize(l+v); // Half vector between both l and v + float scale = (intensity * smoothstep(lightDirection_cosOuterAngle.w, position_cosInnerAngle.w, dot_lightdirection)) / distance2; + + color.rgb += BRDF(lightColor.rgb * scale, v, n, l, h, perceptualRoughness, metallic, specularEnvironmentR0, specularEnvironmentR90, alphaRoughness, diffuseColor, specularColor, ambientOcclusion); + } + } + +#ifdef VSG_EMISSIVE_MAP + vec3 emissive = texture(emissiveMap, texCoord[texCoordIndices.emissiveMap]).rgb * pbr.emissiveFactor.rgb * pbr.emissiveFactor.a; +#else + vec3 emissive = pbr.emissiveFactor.rgb * pbr.emissiveFactor.a; +#endif + color += emissive; + + outColor = vec4(color, baseColor.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/app/CMakeLists.txt b/examples/app/CMakeLists.txt index efea043a..5e314046 100644 --- a/examples/app/CMakeLists.txt +++ b/examples/app/CMakeLists.txt @@ -23,4 +23,5 @@ add_subdirectory(vsgrenderoutline) if (vsgXchange_FOUND) add_subdirectory(vsghelloworld) + add_subdirectory(vsgdepthpeeling) endif() diff --git a/examples/app/vsgdepthpeeling/CMakeLists.txt b/examples/app/vsgdepthpeeling/CMakeLists.txt new file mode 100644 index 00000000..a13b35c0 --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/Bindings.cpp b/examples/app/vsgdepthpeeling/depthpeeling/Bindings.cpp new file mode 100644 index 00000000..e8612433 --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/Bindings.h b/examples/app/vsgdepthpeeling/depthpeeling/Bindings.h new file mode 100644 index 00000000..f0be918a --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/Builder.cpp b/examples/app/vsgdepthpeeling/depthpeeling/Builder.cpp new file mode 100644 index 00000000..ca97023f --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/Builder.h b/examples/app/vsgdepthpeeling/depthpeeling/Builder.h new file mode 100644 index 00000000..bfb8f6f5 --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/PipelineConfigurator.cpp b/examples/app/vsgdepthpeeling/depthpeeling/PipelineConfigurator.cpp new file mode 100644 index 00000000..d8fe8559 --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/PipelineConfigurator.h b/examples/app/vsgdepthpeeling/depthpeeling/PipelineConfigurator.h new file mode 100644 index 00000000..6e89655b --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/RenderGraph.cpp b/examples/app/vsgdepthpeeling/depthpeeling/RenderGraph.cpp new file mode 100644 index 00000000..171f7a38 --- /dev/null +++ b/examples/app/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 in_window, ref_ptr in_view) + : Inherit(in_window, in_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/app/vsgdepthpeeling/depthpeeling/RenderGraph.h b/examples/app/vsgdepthpeeling/depthpeeling/RenderGraph.h new file mode 100644 index 00000000..d3778462 --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/Resources.cpp b/examples/app/vsgdepthpeeling/depthpeeling/Resources.cpp new file mode 100644 index 00000000..b401a192 --- /dev/null +++ b/examples/app/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; + 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; + 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; + 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/app/vsgdepthpeeling/depthpeeling/Resources.h b/examples/app/vsgdepthpeeling/depthpeeling/Resources.h new file mode 100644 index 00000000..3c6a7ed3 --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/ShaderSet.cpp b/examples/app/vsgdepthpeeling/depthpeeling/ShaderSet.cpp new file mode 100644 index 00000000..cc904a84 --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/depthpeeling/ShaderSet.h b/examples/app/vsgdepthpeeling/depthpeeling/ShaderSet.h new file mode 100644 index 00000000..b3a13487 --- /dev/null +++ b/examples/app/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/app/vsgdepthpeeling/vsgdepthpeeling.cpp b/examples/app/vsgdepthpeeling/vsgdepthpeeling.cpp new file mode 100644 index 00000000..a437624e --- /dev/null +++ b/examples/app/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(static_cast(x) * 1.5f, static_cast(y) * 1.5f, static_cast(z) * 1.5f))); + } + } + } + } + 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; +} diff --git a/examples/io/CMakeLists.txt b/examples/io/CMakeLists.txt index 02352b0b..3c90443d 100644 --- a/examples/io/CMakeLists.txt +++ b/examples/io/CMakeLists.txt @@ -3,3 +3,7 @@ add_subdirectory(vsgio) add_subdirectory(vsglog) add_subdirectory(vsglog_mt) add_subdirectory(vsgpath) + +if (vsgXchange_FOUND) + add_subdirectory(vsgcustombuilders) +endif() diff --git a/examples/io/vsgcustombuilders/CMakeLists.txt b/examples/io/vsgcustombuilders/CMakeLists.txt new file mode 100644 index 00000000..997faee5 --- /dev/null +++ b/examples/io/vsgcustombuilders/CMakeLists.txt @@ -0,0 +1,8 @@ +set(SOURCES + vsgcustombuilders.cpp +) + +add_executable(vsgcustombuilders ${SOURCES}) +target_link_libraries(vsgcustombuilders vsgXchange::vsgXchange) + +install(TARGETS vsgcustombuilders RUNTIME DESTINATION bin) diff --git a/examples/io/vsgcustombuilders/vsgcustombuilders.cpp b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp new file mode 100644 index 00000000..4f893a9f --- /dev/null +++ b/examples/io/vsgcustombuilders/vsgcustombuilders.cpp @@ -0,0 +1,497 @@ +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#ifdef vsgXchange_gltf +namespace CustomBuilders +{ + class MyGltfBuilder : public vsg::Inherit + { + public: + MyGltfBuilder() + { + } + + MyGltfBuilder(const MyGltfBuilder&, const vsg::CopyOp& = {}) : + Inherit() + { + } + + vsg::ref_ptr clone(const vsg::CopyOp& copyop = {}) const override + { + return MyGltfBuilder::create(*this, copyop); + } + + + vsg::ref_ptr createSceneGraph(vsg::ref_ptr in_model, vsg::ref_ptr in_options) override + { + vsg::info("MyGltfBuilder::createSceneGraph(", in_model, ", ", in_options,") ", this); + return Inherit::createSceneGraph(in_model, in_options); + } + + }; +} +EVSG_type_name(CustomBuilders::MyGltfBuilder) +#endif + +#ifdef vsgXchange_3DTiles +namespace CustomBuilders +{ + class MyTiles3DBuilder : public vsg::Inherit + { + public: + MyTiles3DBuilder() + { + } + + MyTiles3DBuilder(const MyTiles3DBuilder&, const vsg::CopyOp& = {}) : + Inherit() + { + } + + vsg::ref_ptr clone(const vsg::CopyOp& copyop = {}) const override + { + return MyTiles3DBuilder::create(*this, copyop); + } + + vsg::ref_ptr createSceneGraph(vsg::ref_ptr tileset, vsg::ref_ptr in_options) override + { + vsg::info("MyTiles3DBuilder::createSceneGraph(", tileset, ", ", in_options,") ", this); + return Inherit::createSceneGraph(tileset, in_options); + } + }; +} +EVSG_type_name(CustomBuilders::MyTiles3DBuilder) +#endif + +#ifdef vsgXchange_assimp +namespace CustomBuilders +{ + class MyAssimpBuilder : public vsg::Inherit + { + public: + MyAssimpBuilder() + { + } + + MyAssimpBuilder(const MyAssimpBuilder&, const vsg::CopyOp& = {}) : + Inherit() + { + } + + vsg::ref_ptr clone(const vsg::CopyOp& copyop = {}) const override + { + return MyAssimpBuilder::create(*this, copyop); + } + + vsg::ref_ptr createSceneGraph(const aiScene* in_scene, vsg::ref_ptr in_options, const vsg::Path& ext) override + { + vsg::info("MyAssimpBuilder::createSceneGraph(", in_scene, ", ", in_options, ", ", ext, ") ", this); + return Inherit::createSceneGraph(in_scene, in_options, ext); + } + }; + +} +EVSG_type_name(CustomBuilders::MyAssimpBuilder) +#endif + +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); + + // 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_gltf + options->setObject(vsgXchange::gltf::prototype_builder, CustomBuilders::MyGltfBuilder::create()); +#endif + +#ifdef vsgXchange_3DTiles + options->setObject(vsgXchange::Tiles3D::prototype_builder, CustomBuilders::MyTiles3DBuilder::create()); +#endif + +#ifdef vsgXchange_assimp + options->setObject(vsgXchange::assimp::prototype_builder, CustomBuilders::MyAssimpBuilder::create()); +#endif + + // add vsgXchange's support for reading and writing 3rd party file formats + options->add(vsgXchange::all::create()); + + options->readOptions(arguments); + + if (uint32_t numOperationThreads = 0; arguments.read("--ot", numOperationThreads)) options->operationThreads = vsg::OperationThreads::create(numOperationThreads); + + 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; + } + + 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"); + auto pathFilename = arguments.value("", "-p"); + auto loadLevels = arguments.value(0, "--load-levels"); + auto maxPagedLOD = arguments.value(0, "--maxPagedLOD"); + auto LODScale = arguments.value(1.0, "--LODScale"); + + auto horizonMountainHeight = arguments.value(0.0, "--hmh"); + auto nearFarRatio = arguments.value(0.001, "--nfr"); + if (arguments.read("--rgb")) options->mapRGBtoRGBAHint = false; + + bool depthClamp = arguments.read({"--dc", "--depthClamp"}); + if (depthClamp) + { + std::cout << "Enabled depth clamp." << std::endl; + auto deviceFeatures = windowTraits->deviceFeatures = vsg::DeviceFeatures::create(); + deviceFeatures->get().samplerAnisotropy = VK_TRUE; + deviceFeatures->get().depthClamp = VK_TRUE; + } + + vsg::ref_ptr resourceHints; + if (auto resourceHintsFilename = arguments.value("", "--rh")) + { + resourceHints = vsg::read_cast(resourceHintsFilename, options); + } + + + 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"); + + 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); + } + + // should animations be automatically played + auto autoPlay = !arguments.read({"--no-auto-play", "--nop"}); + + if (arguments.errors()) return arguments.writeErrorMessages(std::cerr); + + if (argc <= 1) + { + std::cout << "Please specify a 3d model or image file on the command line." << std::endl; + return 1; + } + + auto group = vsg::Group::create(); + + vsg::Path path; + + // read any vsg files + for (int i = 1; i < argc; ++i) + { + vsg::Path filename = arguments[i]; + path = vsg::filePath(filename); + + auto object = vsg::read(filename, options); + if (auto node = object.cast()) + { + group->addChild(node); + } + else if (object) + { + std::cout << "Unable to view object of type " << object->className() << std::endl; + } + else + { + std::cout << "Unable to load file " << filename << std::endl; + } + } + + if (group->children.empty()) + { + return 1; + } + + vsg::ref_ptr vsg_scene; + if (group->children.size() == 1) + vsg_scene = group->children[0]; + else + vsg_scene = group; + + // 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); + + auto ellipsoidModel = vsg_scene->getRefObject("EllipsoidModel"); + + vsg::ref_ptr lookAt; + vsg::ref_ptr perspective; + if (ellipsoidModel) + { + // compute the bounds of the scene graph to help position camera + vsg::ComputeBounds computeBounds; + vsg_scene->accept(computeBounds); + + double initialRadius = vsg::length(computeBounds.bounds.max - computeBounds.bounds.min) * 0.5; + double modelToEarthRatio = (initialRadius / ellipsoidModel->radiusEquator()); + + // if the model is small compared to the radius of the earth position camera in local coordinate frame of the model rather than ECEF. + if (modelToEarthRatio < 1.0) + { + vsg::dvec3 lla = ellipsoidModel->convertECEFToLatLongAltitude((computeBounds.bounds.min + computeBounds.bounds.max) * 0.5); + + auto worldToLocal = ellipsoidModel->computeWorldToLocalTransform(lla); + auto localToWorld = ellipsoidModel->computeLocalToWorldTransform(lla); + + // recompute the bounds of the model in the local coordinate frame of the model, rather than ECEF + // to give a tigher bound around the dataset. + computeBounds.matrixStack.clear(); + computeBounds.matrixStack.push_back(worldToLocal); + computeBounds.bounds.reset(); + vsg_scene->accept(computeBounds); + + auto bounds = computeBounds.bounds; + vsg::dvec3 centre = (bounds.min + bounds.max) * 0.5; + double radius = vsg::length(bounds.max - bounds.min) * 0.5; + + lookAt = vsg::LookAt::create(localToWorld * (centre + vsg::dvec3(0.0, 0.0, radius)), localToWorld * centre, vsg::dvec3(0.0, 1.0, 0.0) * worldToLocal); + } + else + { + lookAt = vsg::LookAt::create(vsg::dvec3(initialRadius * 2.0, 0.0, 0.0), vsg::dvec3(0.0, 0.0, 0.0), vsg::dvec3(0.0, 0.0, 1.0)); + } + + perspective = vsg::EllipsoidPerspective::create(lookAt, ellipsoidModel, 30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio, horizonMountainHeight); + } + else + { + // compute the bounds of the scene graph to help position camera + vsg::ComputeBounds computeBounds; + vsg_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())); + + // add close handler to respond to the close window button and pressing escape + viewer->addEventHandler(vsg::CloseHandler::create(viewer)); + + auto cameraAnimation = vsg::CameraAnimationHandler::create(camera, pathFilename, options); + viewer->addEventHandler(cameraAnimation); + if (autoPlay && cameraAnimation->animation) + { + cameraAnimation->play(); + + if (reportAverageFrameRate && maxTime == std::numeric_limits::max()) + { + maxTime = cameraAnimation->animation->maxTime(); + } + } + + viewer->addEventHandler(vsg::Trackball::create(camera, ellipsoidModel)); + + // if required preload specific number of PagedLOD levels. + if (loadLevels > 0) + { + vsg::LoadPagedLOD loadPagedLOD(camera, loadLevels); + + auto startTime = vsg::clock::now(); + + vsg_scene->accept(loadPagedLOD); + + auto time = std::chrono::duration(vsg::clock::now() - startTime).count(); + std::cout << "No. of tiles loaded " << loadPagedLOD.numTiles << " in " << time << "ms." << std::endl; + } + + auto view = vsg::View::create(camera); + view->LODScale = LODScale; + view->addChild(vsg::createHeadlight()); + view->addChild(vsg_scene); + + auto renderGraph = vsg::RenderGraph::create(window, view); + auto commandGraph = vsg::CommandGraph::create(window, renderGraph); + 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(resourceHints); + + if (maxPagedLOD > 0) + { + // set targetMaxNumPagedLODWithHighResSubgraphs after Viewer::compile() as it will assign any DatabasePager if required. + for (auto& task : viewer->recordAndSubmitTasks) + { + if (task->databasePager) task->databasePager->targetMaxNumPagedLODWithHighResSubgraphs = maxPagedLOD; + } + } + + if (autoPlay) + { + // find any animation groups in the loaded scene graph and play the first animation in each of the animation groups. + auto animationGroups = vsg::visit(vsg_scene).animationGroups; + for (auto ag : animationGroups) + { + if (!ag->animations.empty()) viewer->animationManager->play(ag->animations.front()); + } + } + + 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; +} diff --git a/examples/meshshaders/vsgmeshshader/vsgmeshshader.cpp b/examples/meshshaders/vsgmeshshader/vsgmeshshader.cpp index 976da711..c111b268 100644 --- a/examples/meshshaders/vsgmeshshader/vsgmeshshader.cpp +++ b/examples/meshshaders/vsgmeshshader/vsgmeshshader.cpp @@ -43,13 +43,13 @@ int main(int argc, char** argv) auto features = windowTraits->deviceFeatures = vsg::DeviceFeatures::create(); auto& meshFeatures = features->get(); - meshFeatures.meshShader = 1; - meshFeatures.taskShader = 1; + meshFeatures.meshShader = VK_TRUE; + meshFeatures.taskShader = VK_TRUE; if (barycentric) { auto& barycentricFeatures = features->get(); - barycentricFeatures.fragmentShaderBarycentric = 1; + barycentricFeatures.fragmentShaderBarycentric = VK_TRUE; } auto window = vsg::Window::create(windowTraits); diff --git a/examples/nodes/vsggroups/SharedPtrNode.cpp b/examples/nodes/vsggroups/SharedPtrNode.cpp index 4bc65737..2a69dbe5 100644 --- a/examples/nodes/vsggroups/SharedPtrNode.cpp +++ b/examples/nodes/vsggroups/SharedPtrNode.cpp @@ -11,6 +11,15 @@ namespace experimental { } + void SharedPtrGroup::accept(SharedPtrVisitor& spv) + { + spv.apply(*this); + } + void SharedPtrGroup::traverse(SharedPtrVisitor& spv) + { + for (const auto& child : _children) child->accept(spv); + } + void SharedPtrQuadGroup::accept(SharedPtrVisitor& spv) { spv.apply(*this); diff --git a/examples/nodes/vsggroups/SharedPtrNode.h b/examples/nodes/vsggroups/SharedPtrNode.h index 73385cd8..7127e378 100644 --- a/examples/nodes/vsggroups/SharedPtrNode.h +++ b/examples/nodes/vsggroups/SharedPtrNode.h @@ -2,6 +2,9 @@ #include #include +#include + +#include namespace experimental { @@ -31,15 +34,40 @@ namespace experimental std::shared_ptr _auxiliary; }; + class SharedPtrGroup; class SharedPtrQuadGroup; class SharedPtrVisitor { public: virtual void apply(SharedPtrNode&) {} + virtual void apply(SharedPtrGroup&) {} virtual void apply(SharedPtrQuadGroup&) {} }; + class SharedPtrGroup : public SharedPtrNode + { + public: + SharedPtrGroup() {} + SharedPtrGroup(size_t size) : + _children(size, nullptr) + {} + + virtual void accept(SharedPtrVisitor& spv); + virtual void traverse(SharedPtrVisitor& spv); + + using Children = std::vector, vsg::allocator_affinity_nodes>>; + + void setChild(std::size_t i, std::shared_ptr node) { _children[i] = node; } + SharedPtrNode* getChild(std::size_t i) { return _children[i].get(); } + const SharedPtrNode* getChild(std::size_t i) const { return _children[i].get(); } + + virtual ~SharedPtrGroup() {} + + protected: + Children _children; + }; + class SharedPtrQuadGroup : public SharedPtrNode { public: diff --git a/examples/nodes/vsggroups/vsggroups.cpp b/examples/nodes/vsggroups/vsggroups.cpp index df38046a..4368bce6 100644 --- a/examples/nodes/vsggroups/vsggroups.cpp +++ b/examples/nodes/vsggroups/vsggroups.cpp @@ -97,6 +97,13 @@ class ExperimentVisitor : public experimental::SharedPtrVisitor object.traverse(*this); } + void apply(experimental::SharedPtrGroup& group) final + { + //std::cout<<"ExperimentVisitor::apply(vsg::SharedPtrGroup&)"< createSharedPtrQuadTree(uint64_t nu numNodes += 1; numBytes += sizeof(experimental::SharedPtrNode); - return std::make_shared(); + return std::allocate_shared(vsg::allocator_affinity_nodes()); } - std::shared_ptr t = std::make_shared(); + std::shared_ptr t = std::allocate_shared(vsg::allocator_affinity_nodes(), 4); --numLevels; numNodes += 1; - numBytes += sizeof(experimental::SharedPtrQuadGroup); + numBytes += sizeof(experimental::SharedPtrGroup); t->setChild(0, createSharedPtrQuadTree(numLevels, numNodes, numBytes)); t->setChild(1, createSharedPtrQuadTree(numLevels, numNodes, numBytes)); @@ -180,6 +187,31 @@ std::shared_ptr createSharedPtrQuadTree(uint64_t nu return t; } +std::shared_ptr createSharedPtrFixedQuadTree(uint64_t numLevels, uint64_t& numNodes, uint64_t& numBytes) +{ + if (numLevels == 0) + { + numNodes += 1; + numBytes += sizeof(experimental::SharedPtrNode); + + return std::allocate_shared(vsg::allocator_affinity_nodes()); + } + + std::shared_ptr t = std::allocate_shared(vsg::allocator_affinity_nodes()); + + --numLevels; + + numNodes += 1; + numBytes += sizeof(experimental::SharedPtrQuadGroup); + + t->setChild(0, createSharedPtrFixedQuadTree(numLevels, numNodes, numBytes)); + t->setChild(1, createSharedPtrFixedQuadTree(numLevels, numNodes, numBytes)); + t->setChild(2, createSharedPtrFixedQuadTree(numLevels, numNodes, numBytes)); + t->setChild(3, createSharedPtrFixedQuadTree(numLevels, numNodes, numBytes)); + + return t; +} + // consider tcmalloc? https://goog-perftools.sourceforge.net/doc/tcmalloc.html // consider Alloc https://www.codeproject.com/Articles/1084801/Replace-malloc-free-with-a-Fast-Fixed-Block-Memory class StdAllocator : public vsg::Allocator @@ -289,6 +321,7 @@ int main(int argc, char** argv) if (type == "vsg::Group") vsg_root = createVsgQuadTree(numLevels, numNodes, numBytes); if (type == "vsg::QuadGroup") vsg_root = createFixedQuadTree(numLevels, numNodes, numBytes); if (type == "SharedPtrGroup") shared_root = createSharedPtrQuadTree(numLevels, numNodes, numBytes)->shared_from_this(); + if (type == "SharedPtrQuadGroup") shared_root = createSharedPtrFixedQuadTree(numLevels, numNodes, numBytes)->shared_from_this(); } if (!vsg_root && !shared_root) diff --git a/examples/state/CMakeLists.txt b/examples/state/CMakeLists.txt index cb84ceee..898af476 100644 --- a/examples/state/CMakeLists.txt +++ b/examples/state/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(vsgdynamicvertex) add_subdirectory(vsgclip) add_subdirectory(vsgextendstate) add_subdirectory(vsgsampler) +add_subdirectory(vsgwireframe) if (vsgImGui_FOUND) add_subdirectory(vsgdynamicstate) diff --git a/examples/state/vsgsampler/vsgsampler.cpp b/examples/state/vsgsampler/vsgsampler.cpp index b28d9374..4bf60c73 100644 --- a/examples/state/vsgsampler/vsgsampler.cpp +++ b/examples/state/vsgsampler/vsgsampler.cpp @@ -224,7 +224,7 @@ int main(int argc, char** argv) { auto mipmapLayout = image->getMipmapLayout(); auto [width, height, depth, numLayers] = image->pixelExtents(); - vsg::vec2 extents(width, height); + vsg::vec2 extents(static_cast(width), static_cast(height)); // default mipmap settings { diff --git a/examples/state/vsgwireframe/CMakeLists.txt b/examples/state/vsgwireframe/CMakeLists.txt new file mode 100644 index 00000000..badd3005 --- /dev/null +++ b/examples/state/vsgwireframe/CMakeLists.txt @@ -0,0 +1,14 @@ +set(SOURCES + vsgwireframe.cpp +) + +add_executable(vsgwireframe ${SOURCES}) + +target_link_libraries(vsgwireframe vsg::vsg) + +if (vsgXchange_FOUND) + target_compile_definitions(vsgwireframe PRIVATE vsgXchange_FOUND) + target_link_libraries(vsgwireframe vsgXchange::vsgXchange) +endif() + +install(TARGETS vsgwireframe RUNTIME DESTINATION bin) diff --git a/examples/state/vsgwireframe/vsgwireframe.cpp b/examples/state/vsgwireframe/vsgwireframe.cpp new file mode 100644 index 00000000..6bdd25d3 --- /dev/null +++ b/examples/state/vsgwireframe/vsgwireframe.cpp @@ -0,0 +1,167 @@ +#include +#include + +#ifdef vsgXchange_FOUND +# include +#endif + +namespace custom +{ + vsg::ref_ptr pbr_ShaderSet(vsg::ref_ptr options) + { + vsg::info("Local pbr_ShaderSet(", options, ")"); + + auto vertexShader = vsg::read_cast("shaders/standard.vert", options); + auto fragmentShader = vsg::read_cast("shaders/wireframe_pbr.frag", options); + + if (!vertexShader || !fragmentShader) + { + vsg::error("pbr_ShaderSet(...) could not find shaders."); + return {}; + } + + // use the built PBR ShaderSet as a starting place + auto shaderSet = vsg::createPhysicsBasedRenderingShaderSet(options); + + // replace the shader stages with our custom wireframe_pbr fragment shader + shaderSet->stages = vsg::ShaderStages{vertexShader, fragmentShader}; + + // clear precompiled shader variants to force use of the new shader stages. + shaderSet->variants.clear(); + + return shaderSet; + } +} + +int main(int argc, char** argv) +{ + // set up defaults and read command line arguments to override them + vsg::CommandLine arguments(&argc, argv); + + auto windowTraits = vsg::WindowTraits::create(arguments); + + // use the vsg::Options object to pass the ReaderWriter_all to use when reading files. + auto options = vsg::Options::create(); +#ifdef vsgXchange_FOUND + options->add(vsgXchange::all::create()); +#endif + options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); + options->sharedObjects = vsg::SharedObjects::create(); + + // read any command line options that the ReaderWriters support + options->readOptions(arguments); + + auto numFrames = arguments.value(-1, "-f"); + auto outputFilename = arguments.value("", "-o"); + auto outputShaderSetFilename = arguments.value("", "--os"); + auto horizonMountainHeight = arguments.value(0.0, "--hmh"); + auto nearFarRatio = arguments.value(0.001, "--nfr"); + bool reportAverageFrameRate = arguments.read("--fps"); + + vsg::ref_ptr shaderSet = custom::pbr_ShaderSet(options); + options->shaderSets["pbr"] = shaderSet; + if (!shaderSet) + { + std::cout << "No vsg::ShaderSet to process." << std::endl; + return 1; + } + + if (outputShaderSetFilename) + { + vsg::write(shaderSet, outputShaderSetFilename, options); + } + + auto vsg_scene = vsg::Group::create(); + + vsg::Path path; + + // read any vsg files + for (int i = 1; i < argc; ++i) + { + vsg::Path filename = arguments[i]; + if (auto node = vsg::read_cast(filename, options)) + { + vsg_scene->addChild(node); + } + else + { + std::cout << "Unable to load file " << filename << std::endl; + } + } + + if (vsg_scene->children.empty()) + { + return 1; + } + + if (outputFilename) + { + vsg::write(vsg_scene, outputFilename, options); + } + + // 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); + + // compute the bounds of the scene graph to help position camera + auto bounds = vsg::visit(vsg_scene).bounds; + vsg::dvec3 centre = (bounds.min + bounds.max) * 0.5; + double radius = vsg::length(bounds.max - bounds.min) * 0.6; + + vsg::info("scene bounds ", bounds); + + // set up the camera + auto lookAt = vsg::LookAt::create(centre + vsg::dvec3(0.0, -radius * 3.5, 0.0), centre, vsg::dvec3(0.0, 0.0, 1.0)); + + vsg::ref_ptr perspective; + auto ellipsoidModel = vsg_scene->getRefObject("EllipsoidModel"); + if (ellipsoidModel) + { + perspective = vsg::EllipsoidPerspective::create(lookAt, ellipsoidModel, 30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio, horizonMountainHeight); + } + else + { + perspective = vsg::Perspective::create(30.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), nearFarRatio * radius, radius * 4.5); + } + + auto camera = vsg::Camera::create(perspective, lookAt, vsg::ViewportState::create(window->extent2D())); + + // add close handler to respond to the close window button and pressing escape + viewer->addEventHandler(vsg::CloseHandler::create(viewer)); + viewer->addEventHandler(vsg::Trackball::create(camera, ellipsoidModel)); + + auto commandGraph = vsg::createCommandGraphForView(window, camera, vsg_scene); + viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); + + viewer->compile(); + + viewer->start_point() = vsg::clock::now(); + + // rendering main loop + while (viewer->advanceToNextFrame() && (numFrames < 0 || (numFrames--) > 0)) + { + // 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; + } + return 0; +} diff --git a/examples/threading/vsgdynamicload/vsgdynamicload.cpp b/examples/threading/vsgdynamicload/vsgdynamicload.cpp index 36434f79..bf5bb609 100644 --- a/examples/threading/vsgdynamicload/vsgdynamicload.cpp +++ b/examples/threading/vsgdynamicload/vsgdynamicload.cpp @@ -111,6 +111,7 @@ struct LoadOperation : public vsg::Inherit if (result) ref_viewer->addUpdateOperation(Merge::create(filename, viewer, attachmentPoint, node, result)); + else vsg::info("Loaded ", filename, " but compile failed { ", result.result, ", ", result.message, " }"); } } }; diff --git a/examples/utils/CMakeLists.txt b/examples/utils/CMakeLists.txt index 1e080732..cd8fa24a 100644 --- a/examples/utils/CMakeLists.txt +++ b/examples/utils/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(vsgintersection) add_subdirectory(vsgstoragebuffer) add_subdirectory(vsgcustomshaderset) add_subdirectory(vsginstrumentation) +add_subdirectory(vsghighlight) if (Tracy_FOUND) add_subdirectory(vsgtracyinstrumentation) diff --git a/examples/utils/vsghighlight/CMakeLists.txt b/examples/utils/vsghighlight/CMakeLists.txt new file mode 100644 index 00000000..117efce3 --- /dev/null +++ b/examples/utils/vsghighlight/CMakeLists.txt @@ -0,0 +1,9 @@ +set(SOURCES + vsghighlight.cpp +) + +add_executable(vsghighlight ${SOURCES}) + +target_link_libraries(vsghighlight vsg::vsg) + +install(TARGETS vsghighlight RUNTIME DESTINATION bin) diff --git a/examples/utils/vsghighlight/vsghighlight.cpp b/examples/utils/vsghighlight/vsghighlight.cpp new file mode 100644 index 00000000..3fbf0906 --- /dev/null +++ b/examples/utils/vsghighlight/vsghighlight.cpp @@ -0,0 +1,311 @@ +#include + +#include + +namespace +{ + vsg::ref_ptr buildPipelineConfig(vsg::ref_ptr options) + { + auto vertexShader = vsg::read_cast("shaders/highlight.vert", options); + auto fragmentShader = vsg::read_cast("shaders/highlight.frag", options); + if (!vertexShader || !fragmentShader) + { + vsg::error("No highlight shaders found"); + return {}; + } + + //setup highlight/selection colors + auto slcColors = vsg::vec4Array::create(3); + slcColors->at(0) = vsg::vec4(0.0, 0.0, 0.0, 1.0); //dummy to sync index to use material. + slcColors->at(1) = vsg::vec4(1.0, 1.0, 0.0, 1.0); //pre highlight + slcColors->at(2) = vsg::vec4(1.0, 1.0, 1.0, 1.0); //highlight + + // set up shaders + auto shaderSet = vsg::ShaderSet::create(vsg::ShaderStages{vertexShader, fragmentShader}); + shaderSet->addPushConstantRange("pc", "", VK_SHADER_STAGE_VERTEX_BIT, 0, 128); + shaderSet->addAttributeBinding("vsg_Vertex", "", 0, VK_FORMAT_R32G32B32_SFLOAT, vsg::vec3Array::create(1)); + shaderSet->addAttributeBinding("vsg_Normal", "", 1, VK_FORMAT_R32G32B32_SFLOAT, vsg::vec3Array::create(1)); + shaderSet->addDescriptorBinding("slcColors", "", 0, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, slcColors, vsg::CoordinateSpace::LINEAR); + shaderSet->addDescriptorBinding("lightData", "", 1, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, vsg::vec4Array::create(64)); + shaderSet->addDescriptorBinding("material", "", 2, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, vsg::PhongMaterialValue::create(), vsg::CoordinateSpace::LINEAR); + shaderSet->addDescriptorBinding("slcIndex", "", 3, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, vsg::uintValue::create(0)); + shaderSet->customDescriptorSetBindings.push_back(vsg::ViewDependentStateBinding::create(1)); + + // set up pipeline + auto pipeConfig = vsg::GraphicsPipelineConfigurator::create(shaderSet); + pipeConfig->enableArray("vsg_Vertex", VK_VERTEX_INPUT_RATE_VERTEX, 12); + pipeConfig->enableArray("vsg_Normal", VK_VERTEX_INPUT_RATE_VERTEX, 12); + pipeConfig->enableDescriptor("slcColors"); + pipeConfig->enableDescriptor("lightData"); + pipeConfig->enableDescriptor("material"); + pipeConfig->enableDescriptor("slcIndex"); + pipeConfig->init(); + return pipeConfig; + } + + struct FaceBuilder + { + const vsg::GraphicsPipelineConfigurator& plConfig; + + FaceBuilder() = delete; + FaceBuilder(const vsg::GraphicsPipelineConfigurator& plConfigIn) : + plConfig(plConfigIn) {} + vsg::ref_ptr build(const std::array& pointsIn, const vsg::dvec3& normalIn, const vsg::vec4& colorIn, const std::string& nameIn) + { + auto out = vsg::StateGroup::create(); + + auto material = vsg::PhongMaterialValue::create(); + material->value().diffuse = colorIn; + auto materialDescriptor = vsg::DescriptorBuffer::create(material); + auto materialSet = vsg::DescriptorSet::create(plConfig.layout->setLayouts.at(2), vsg::Descriptors{materialDescriptor}); + auto materialBind = vsg::BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, plConfig.layout, 2, materialSet); + out->addChild(materialBind); + + auto slcIndex = vsg::uintValue::create(0); + auto slcDescriptor = vsg::DescriptorBuffer::create(slcIndex); + auto slcSet = vsg::DescriptorSet::create(plConfig.layout->setLayouts.at(3), vsg::Descriptors{slcDescriptor}); + auto slcBind = vsg::BindDescriptorSet::create(VK_PIPELINE_BIND_POINT_GRAPHICS, plConfig.layout, 3, slcSet); + out->addChild(slcBind); + + auto draw = vsg::VertexIndexDraw::create(); + auto points = vsg::vec3Array::create(4); + for (int index = 0; index < 4; ++index) points->at(index) = pointsIn.at(index); + auto normals = vsg::vec3Array::create(4, vsg::vec3(normalIn)); + auto indexes = vsg::ushortArray::create({0, 1, 2, 2, 3, 0}); + draw->assignArrays(vsg::DataList{points, normals}); + draw->assignIndices(indexes); + draw->indexCount = static_cast(indexes->size()); + draw->instanceCount = 1; + out->addChild(draw); + + out->setObject("slcIndexBuffer", slcDescriptor); + out->setValue("name", nameIn); + + return out; + } + }; + + vsg::ref_ptr buildBox(const vsg::GraphicsPipelineConfigurator& plConfigIn) + { + auto out = vsg::Group::create(); + FaceBuilder builder(plConfigIn); + + double length = 10.0, width = 10.0, height = 10.0; + std::array ps = + {{{0.0, 0.0, 0.0}, {length, 0.0, 0.0}, {length, width, 0.0}, {0.0, width, 0.0}, {0.0, 0.0, height}, {length, 0.0, height}, {length, width, height}, {0.0, width, height}}}; + out->addChild(builder.build(std::array{{ps.at(0), ps.at(3), ps.at(2), ps.at(1)}}, vsg::dvec3(0.0, 0.0, -1.0), vsg::vec4(0.0, 0.0, 1.0, 1.0), "bottom")); + out->addChild(builder.build(std::array{{ps.at(0), ps.at(1), ps.at(5), ps.at(4)}}, vsg::dvec3(0.0, -1.0, 0.0), vsg::vec4(0.0, 1.0, 0.0, 1.0), "front")); + out->addChild(builder.build(std::array{{ps.at(1), ps.at(2), ps.at(6), ps.at(5)}}, vsg::dvec3(1.0, 0.0, 0.0), vsg::vec4(1.0, 0.0, 0.0, 1.0), "right")); + out->addChild(builder.build(std::array{{ps.at(2), ps.at(3), ps.at(7), ps.at(6)}}, vsg::dvec3(0.0, 1.0, 0.0), vsg::vec4(0.0, 1.0, 0.0, 1.0), "back")); + out->addChild(builder.build(std::array{{ps.at(3), ps.at(0), ps.at(4), ps.at(7)}}, vsg::dvec3(-1.0, 0.0, 0.0), vsg::vec4(1.0, 0.0, 0.0, 1.0), "left")); + out->addChild(builder.build(std::array{{ps.at(4), ps.at(5), ps.at(6), ps.at(7)}}, vsg::dvec3(0.0, 0.0, 1.0), vsg::vec4(0.0, 0.0, 1.0, 1.0), "top")); + + return out; + } + + class IntersectionHandler : public vsg::Inherit + { + public: + vsg::ref_ptr camera; + vsg::ref_ptr scenegraph; + vsg::ref_ptr viewer; + vsg::ref_ptr preSelection; + std::vector> selections; + + IntersectionHandler(vsg::ref_ptr camIn, vsg::ref_ptr sceneIn, vsg::ref_ptr viewerIn) : + camera(camIn), + scenegraph(sceneIn), + viewer(viewerIn) + { + } + + void setSelectionState(vsg::StateGroup& stateGroup, uint32_t freshIndex) + { + auto* object = stateGroup.getObject("slcIndexBuffer"); + auto* indexBuffer = object->cast(); + auto* indexBufferInfo = indexBuffer->bufferInfoList[0]->cast(); + auto* localIndex = indexBufferInfo->data->cast(); + localIndex->set(freshIndex); + localIndex->dirty(); + + //manual transfer operation + for (auto& task : viewer->recordAndSubmitTasks) + { + task->transferTask->assign(indexBuffer->bufferInfoList); + } + } + + void clearPreSelection() + { + if (!preSelection) return; + setSelectionState(*preSelection, 0); + preSelection.reset(); + } + + void setPreSelection(const vsg::Node* nodeIn) + { + auto* temp = nodeIn->cast(); + preSelection = const_cast(temp); + setSelectionState(*preSelection, 1); + } + + bool select() + { + if (!preSelection) return false; + setSelectionState(*preSelection, 2); + selections.push_back(preSelection); + + std::string name = "no name"; + preSelection->getValue("name", name); + std::cout << name << " selected" << std::endl; + + preSelection.reset(); + return true; + } + + bool clearSelection() + { + bool out = !selections.empty(); + for (const auto& ptr : selections) setSelectionState(*ptr, 0); + selections.clear(); + std::cout << "selection cleared" << std::endl; + return out; + } + + void apply(vsg::MoveEvent& moveEvent) override + { + if (moveEvent.handled) + { + clearPreSelection(); + return; + } + auto intersector = vsg::LineSegmentIntersector::create(*camera, moveEvent.x, moveEvent.y); + scenegraph->accept(*intersector); + auto& intersections = intersector->intersections; + if (intersections.empty()) + { + clearPreSelection(); + return; + } + std::sort(intersections.begin(), intersections.end(), [](auto& lhs, auto& rhs) { return lhs->ratio < rhs->ratio; }); + + auto isSelected = [&](const vsg::Node* nodeIn) -> bool //return true if node is in selection vector + { + for (const auto& s : selections) + if (s == nodeIn) return true; + return false; + }; + for (const auto& i : intersections) + { + const auto& np = i->nodePath; + const auto* node = np.at(np.size() - 2); + if (node == preSelection) return; + if (isSelected(node)) continue; + clearPreSelection(); + setPreSelection(node); + moveEvent.handled = true; + + break; + } + } + + void apply(vsg::ButtonReleaseEvent& event) override + { + if (event.handled) return; + if (event.button == 1) event.handled = select(); + if (event.button == 2) event.handled = clearSelection(); + } + }; +} // namespace + +int main(int argc, char** argv) +{ + vsg::CommandLine arguments(&argc, argv); + if (arguments.errors()) return arguments.writeErrorMessages(std::cerr); + + if (arguments.read("--help") || arguments.read("-h")) + { + std::cout << std::endl + << "Description: Altering face color on mouse move and button click." << std::endl + << " mouse moves sets pre-highlight color." << std::endl + << " mouse button 1 click, changes current pre-highlight to highlight and stores selection." << std::endl + << " mouse button 2 click, clears selection." << std::endl + << std::endl + << "-h, --help " << "Display this message" << std::endl + << "-d, --debug " << "Enable the vulkan validation layer" << std::endl + << std::endl; + return 0; + } + + auto options = vsg::Options::create(); + options->paths = vsg::getEnvPaths("VSG_FILE_PATH"); + + // build scene + auto facePipelineConfig = buildPipelineConfig(options); + if (!facePipelineConfig) return 1; + auto scene = vsg::StateGroup::create(); + facePipelineConfig->copyTo(scene); + scene->addChild(buildBox(*facePipelineConfig)); + + // scene calc + vsg::ComputeBounds computeBounds; + scene->accept(computeBounds); + vsg::dvec3 center = (computeBounds.bounds.min + computeBounds.bounds.max) * 0.5; + double radius = vsg::length(computeBounds.bounds.max - computeBounds.bounds.min) * 0.6; + double camDistance = radius * 3.5; + vsg::dvec3 camProjection(0.0, -camDistance, 0.0); + + //light + auto absoluteTransform = vsg::AbsoluteTransform::create(); + scene->addChild(absoluteTransform); + { + auto ambientLight = vsg::AmbientLight::create(); + ambientLight->name = "ambient"; + ambientLight->color.set(1.0f, 1.0f, 1.0f); + ambientLight->intensity = 0.0044f; + absoluteTransform->addChild(ambientLight); + } + { + vsg::dquat xRot(vsg::radians(10.0), vsg::dvec3(-1.0, 0.0, 0.0)); + vsg::dquat yRot(vsg::radians(30.0), vsg::dvec3(0.0, 1.0, 0.0)); + auto dLight = vsg::DirectionalLight::create(); + dLight->name = "directional"; + dLight->color = {1.0f, 1.0f, 1.0f}; + dLight->intensity = 0.8f; + dLight->direction = xRot * yRot * vsg::dvec3(0.0, 0.0, -1.0); + absoluteTransform->addChild(dLight); + } + + //build window + auto windowTraits = vsg::WindowTraits::create(arguments); + if (arguments.read("--debug") || arguments.read("-d")) windowTraits->debugLayer = true; + auto window = vsg::Window::create(windowTraits); + if (!window) + { + std::cout << "Could not create window." << std::endl; + return 1; + } + auto viewer = vsg::Viewer::create(); + viewer->addWindow(window); + + auto viewport = vsg::ViewportState::create(0, 0, window->extent2D().width, window->extent2D().height); + auto perspective = vsg::Perspective::create(45.0, static_cast(window->extent2D().width) / static_cast(window->extent2D().height), 0.1, radius * 10.0); + auto lookAt = vsg::LookAt::create(center + camProjection, center, vsg::dvec3(0.0, 0.0, 1.0)); + auto camera = vsg::Camera::create(perspective, lookAt, viewport); + auto commandGraph = vsg::createCommandGraphForView(window, camera, scene); + viewer->assignRecordAndSubmitTaskAndPresentation({commandGraph}); + viewer->compile(); + viewer->addEventHandlers({vsg::CloseHandler::create(viewer)}); + viewer->addEventHandler(vsg::Trackball::create(camera)); + viewer->addEventHandler(IntersectionHandler::create(camera, scene, viewer)); + + while (viewer->advanceToNextFrame()) + { + viewer->handleEvents(); + viewer->update(); + viewer->recordAndSubmit(); + viewer->present(); + } + + return 0; +}