Render Pass初始化

接下来会介绍各个子Pass的初始化。稍微看了一下,Piccolo的shader有些是OpenGL的,有些是OpenGL ES的,这种混合有点让人迷惑。

(主要内容待补充,还在学习渲染相关)

1.1. Point Light Shadow Passs初始化

void PointLightShadowPass::initialize(const RenderPassInitInfo* init_info)
{
    RenderPass::initialize(nullptr);

    setupAttachments();
    setupRenderPass();
    setupFramebuffer();
    setupDescriptorSetLayout();
}

由于几乎所有的Render Pass都是相同的初始化流程,因此先对该Pass的初始化进行简单分析。
TODO

Point Light Shadow Passs用到了三个Shader:

mesh_point_light_shadow.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
#include "structures.h"

layout(set = 0, binding = 1) readonly buffer _unused_name_per_drawcall
{
    VulkanMeshInstance mesh_instances[m_mesh_per_drawcall_max_instance_count];
};

layout(set = 0, binding = 2) readonly buffer _unused_name_per_drawcall_vertex_blending
{
    mat4 joint_matrices[m_mesh_vertex_blending_max_joint_count * m_mesh_per_drawcall_max_instance_count];
};

layout(set = 1, binding = 0) readonly buffer _unused_name_per_mesh_joint_binding
{
    VulkanMeshVertexJointBinding indices_and_weights[];
};

layout(location = 0) in highp vec3 in_position;

layout(location = 0) out highp vec3 out_position_world_space;

void main()
{
    highp mat4 model_matrix = mesh_instances[gl_InstanceIndex].model_matrix;
    highp float enable_vertex_blending = mesh_instances[gl_InstanceIndex].enable_vertex_blending;

    highp vec3 model_position;
    if (enable_vertex_blending > 0.0)
    {
        highp ivec4 in_indices = indices_and_weights[gl_VertexIndex].indices;
        highp vec4 in_weights = indices_and_weights[gl_VertexIndex].weights;

        highp mat4 vertex_blending_matrix = mat4x4(
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0));

        if (in_weights.x > 0.0 && in_indices.x > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.x] * in_weights.x;
        }

        if (in_weights.y > 0.0 && in_indices.y > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.y] * in_weights.y;
        }

        if (in_weights.z > 0.0 && in_indices.z > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.z] * in_weights.z;
        }

        if (in_weights.w > 0.0 && in_indices.w > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.w] * in_weights.w;
        }

        model_position = (vertex_blending_matrix * vec4(in_position, 1.0)).xyz;
    }
    else
    {
        model_position = in_position;
    }

    out_position_world_space = (model_matrix * vec4(model_position, 1.0)).xyz;
}

mesh_point_light_shadow.geom

#version 310 es

#extension GL_GOOGLE_include_directive : enable

// TODO: geometry shader is inefficient for Mali GPU
#extension GL_EXT_geometry_shader : enable

// Imagination Technologies Limited. "Dual Paraboloid Environment Mapping." Power SDK Whitepaper 2017.
// https://github.com/powervr-graphics/Native_SDK/blob/R17.1-v4.3/Documentation/Whitepapers/Dual%20Paraboloid%20Environment%20Mapping.Whitepaper.pdf

#include "constants.h"

layout(set = 0, binding = 0) readonly buffer _unused_name_global_set_per_frame_binding_buffer
{
    uint point_light_count;
    uint _padding_point_light_count_0;
    uint _padding_point_light_count_1;
    uint _padding_point_light_count_2;
    highp vec4 point_lights_position_and_radius[m_max_point_light_count];
};

layout(triangles) in;
layout(triangle_strip, max_vertices = m_max_point_light_geom_vertices) out;

layout(location = 0) in highp vec3 in_positions_world_space[];

layout(location = 0) out highp float out_inv_length;
layout(location = 1) out highp vec3 out_inv_length_position_view_space;

void main()
{
    for (highp int point_light_index = 0; point_light_index < int(point_light_count) && point_light_index < m_max_point_light_count; ++point_light_index)
    {
        vec3 point_light_position = point_lights_position_and_radius[point_light_index].xyz;
        float point_light_radius = point_lights_position_and_radius[point_light_index].w;

        // TODO: find more effificient ways
        // we draw twice, since the gl_Layer of three vetices may not be the same
        for (highp int layer_index = 0; layer_index < 2; ++layer_index)
        {
            for (highp int vertex_index = 0; vertex_index < 3; ++vertex_index)
            {
                highp vec3 position_world_space = in_positions_world_space[vertex_index];

                // world space to light view space
                // identity rotation
                // Z - Up
                // Y - Forward
                // X - Right
                highp vec3 position_view_space = position_world_space - point_light_position;

                highp vec3 position_spherical_function_domain = normalize(position_view_space);

                // z > 0
                // (x_2d, y_2d, 0) + (0, 0, 1) = λ ((x_sph, y_sph, z_sph) + (0, 0, 1))
                // (x_2d, y_2d) = (x_sph, y_sph) / (z_sph + 1)
                // z < 0
                // (x_2d, y_2d, 0) + (0, 0, -1) = λ ((x_sph, y_sph, z_sph) + (0, 0, -1))
                // (x_2d, y_2d) = (x_sph, y_sph) / (-z_sph + 1)
                highp float layer_position_spherical_function_domain_z[2];
                layer_position_spherical_function_domain_z[0] = -position_spherical_function_domain.z;
                layer_position_spherical_function_domain_z[1] = position_spherical_function_domain.z;
                highp vec4 position_clip;
                position_clip.xy = position_spherical_function_domain.xy;
                position_clip.w = layer_position_spherical_function_domain_z[layer_index] + 1.0;
                position_clip.z = 0.5 * position_clip.w; //length(position_view_space) * position_clip.w / point_light_radius;
                gl_Position = position_clip;

                out_inv_length = 1.0f / length(position_view_space);
                out_inv_length_position_view_space = out_inv_length * position_view_space;

                gl_Layer = layer_index + 2 * point_light_index;
                EmitVertex();
            }
            EndPrimitive();
        }
    }
}

mesh_point_light_shadow.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

// TODO: geometry shader is inefficient for Mali GPU
#extension GL_EXT_geometry_shader : enable

#include "constants.h"

layout(set = 0, binding = 0) readonly buffer _unused_name_global_set_per_frame_binding_buffer
{
    uint point_light_count;
    uint _padding_point_light_count_0;
    uint _padding_point_light_count_1;
    uint _padding_point_light_count_2;
    highp vec4 point_lights_position_and_radius[m_max_point_light_count];
};

layout(location = 0) in highp float in_inv_length;
// NOTE: we can't interpolate the length of "position_view_space" directly, otherwise the result is incorrect
layout(location = 1) in highp vec3 in_inv_length_position_view_space;

layout(location = 0) out highp float out_depth;

void main()
{
    // perspective correct interpolation_
    highp vec3 position_view_space = in_inv_length_position_view_space / in_inv_length;

    highp float point_light_radius = point_lights_position_and_radius[gl_Layer / 2].w;

    highp float ratio = length(position_view_space) / point_light_radius;

    // Trick: we don't write to depth, and thus we can use early depth test
    gl_FragDepth = ratio;
    out_depth = ratio;
}

里面用到了两个头文件:

structures.h

struct VulkanMeshInstance
{
    highp float enable_vertex_blending;
    highp float _padding_enable_vertex_blending_1;
    highp float _padding_enable_vertex_blending_2;
    highp float _padding_enable_vertex_blending_3;
    highp mat4  model_matrix;
};

struct VulkanMeshVertexJointBinding
{
    highp ivec4 indices;
    highp vec4  weights;
};

constants.h

#define m_max_point_light_count 15
#define m_max_point_light_geom_vertices 90 // 90 = 2 * 3 * m_max_point_light_count
#define m_mesh_per_drawcall_max_instance_count 64
#define m_mesh_vertex_blending_max_joint_count 1024
#define CHAOS_LAYOUT_MAJOR row_major
layout(CHAOS_LAYOUT_MAJOR) buffer;
layout(CHAOS_LAYOUT_MAJOR) uniform;

1.2. Directional Light Pass初始化

void DirectionalLightShadowPass::initialize(const RenderPassInitInfo* init_info)
{
    RenderPass::initialize(nullptr);

    setupAttachments();
    setupRenderPass();
    setupFramebuffer();
    setupDescriptorSetLayout();
}

Directional Light Pass用到了以下两个Shader,用于向Directional Light方向正交投影,输出图像记录每个点的投影深度。

mesh_directional_light_shadow.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
#include "structures.h"

layout(set = 0, binding = 0) readonly buffer _unused_name_global_set_per_frame_binding_buffer
{
    mat4 light_proj_view;
};

layout(set = 0, binding = 1) readonly buffer _unused_name_per_drawcall
{
    VulkanMeshInstance mesh_instances[m_mesh_per_drawcall_max_instance_count];
};

layout(set = 0, binding = 2) readonly buffer _unused_name_per_drawcall_vertex_blending
{
    mat4 joint_matrices[m_mesh_vertex_blending_max_joint_count * m_mesh_per_drawcall_max_instance_count];
};

layout(set = 1, binding = 0) readonly buffer _unused_name_per_mesh_joint_binding
{
    VulkanMeshVertexJointBinding indices_and_weights[];
};

layout(location = 0) in highp vec3 in_position;

void main()
{
    highp mat4 model_matrix = mesh_instances[gl_InstanceIndex].model_matrix;
    highp float enable_vertex_blending = mesh_instances[gl_InstanceIndex].enable_vertex_blending;

    highp vec3 model_position;
    if (enable_vertex_blending > 0.0)
    {
        highp ivec4 in_indices = indices_and_weights[gl_VertexIndex].indices;
        highp vec4 in_weights = indices_and_weights[gl_VertexIndex].weights;

        highp mat4 vertex_blending_matrix = mat4x4(
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0));

        if (in_weights.x > 0.0 && in_indices.x > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.x] * in_weights.x;
        }

        if (in_weights.y > 0.0 && in_indices.y > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.y] * in_weights.y;
        }

        if (in_weights.z > 0.0 && in_indices.z > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.z] * in_weights.z;
        }

        if (in_weights.w > 0.0 && in_indices.w > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.w] * in_weights.w;
        }

        model_position = (vertex_blending_matrix * vec4(in_position, 1.0)).xyz;
    }
    else
    {
        model_position = in_position;
    }

    highp vec3 position_world_space = (model_matrix * vec4(model_position, 1.0)).xyz;

    gl_Position = light_proj_view * vec4(position_world_space, 1.0f);
}

mesh_directional_light_shadow.frag

#version 310 es

layout(early_fragment_tests) in;

layout(location = 0) out highp float out_depth;

void main()
{
    out_depth = gl_FragCoord.z;
}

1.3. Main Camera Pass初始化

axis.vert

#version 460 core

#extension GL_GOOGLE_include_directive :enable

#include "constants.h"

struct PointLight
{
	vec3 position;
	float radius;
	vec3 intensity;
	float _padding_intensity;
};

layout(set = 0, binding = 0) readonly buffer _unused_name_perframe
{
    mat4 proj_view_matrix;
    vec3 camera_position;
    float _padding_camera_position;
    vec3 ambient_light;
    float _padding_ambient_light;
    uint point_light_num;
    uint _padding_point_light_num_1;
	uint _padding_point_light_num_2;
	uint _padding_point_light_num_3;
    PointLight scene_point_lights[m_max_point_light_count];
};

layout(set = 0, binding = 1) readonly buffer _unused_name_axis
{
    mat4 model_matrix;
    uint selected_axis;
};

layout(location = 0) in vec3 in_position; // for some types as dvec3 takes 2 locations
layout(location = 1) in vec3 in_normal;
layout(location = 2) in vec3 in_tangent;
layout(location = 3) in vec2 in_texcoord;

layout(location = 0) out vec3 out_color; // output in location 0 for fragment shader

void main()
{
    vec3 world_position = (model_matrix * vec4(in_position, 1.0)).xyz;
    vec4 clip_position = proj_view_matrix * vec4(world_position, 1.0f);

    // depth set to 0.0001 (closest)
    clip_position.z = clip_position.z * 0.0001;
    gl_Position = clip_position;

    if(in_texcoord.x < 0.01f)
    {
        if(selected_axis == 0)
        {
            out_color = vec3(1.0, 1.0, 0.0);
        }
        else
        {
            out_color = vec3(1.0, 0.0, 0.0);
        }
    }
    else if(in_texcoord.x < 1.01f)
    {
        if(selected_axis == 1)
        {
            out_color = vec3(1.0, 1.0, 0.0);
        }
        else
        {
            out_color = vec3(0.0, 1.0, 0.0);
        }
    }
    else if(in_texcoord.x < 2.01f)
    {
        if(selected_axis == 2)
        {
            out_color = vec3(1.0, 1.0, 0.0);
        }
        else
        {
            out_color = vec3(0.0, 0.0, 1.0);
        }
    }
    else
    {
        out_color = vec3(1.0, 1.0, 1.0);
    }
}

axis.frag

#version 460

layout (location = 0) in vec3 in_color;

layout (location = 0) out vec4 outColor;

void main() 
{
	outColor = vec4(in_color, 1.0);
}

deferred_lighting.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

layout(location = 0) out vec2 out_texcoord;

void main()
{
    vec3 fullscreen_triangle_positions[3] = vec3[3](vec3(3.0, 1.0, 0.5), vec3(-1.0, 1.0, 0.5), vec3(-1.0, -3.0, 0.5));

    vec2 fullscreen_triangle_uvs[3] = vec2[3](vec2(2.0, 1.0), vec2(0.0, 1.0), vec2(0.0, -1.0));

    gl_Position  = vec4(fullscreen_triangle_positions[gl_VertexIndex], 1.0);
    out_texcoord = fullscreen_triangle_uvs[gl_VertexIndex];
}

gbuffer.h

struct PGBufferData
{
    highp vec3  worldNormal;
    highp vec3  baseColor;
    highp float metallic;
    highp float specular;
    highp float roughness;
    highp uint  shadingModelID;
};

#define SHADINGMODELID_UNLIT 0U
#define SHADINGMODELID_DEFAULT_LIT 1U

highp vec3 EncodeNormal(highp vec3 N) { return N * 0.5 + 0.5; }

highp vec3 DecodeNormal(highp vec3 N) { return N * 2.0 - 1.0; }

highp vec3 EncodeBaseColor(highp vec3 baseColor)
{
    // we use sRGB on the render target to give more precision to the darks
    return baseColor;
}

highp vec3 DecodeBaseColor(highp vec3 baseColor)
{
    // we use sRGB on the render target to give more precision to the darks
    return baseColor;
}

highp float EncodeShadingModelId(highp uint ShadingModelId)
{
    highp uint Value = ShadingModelId;
    return (float(Value) / float(255));
}

highp uint DecodeShadingModelId(highp float InPackedChannel) { return uint(round(InPackedChannel * float(255))); }

void EncodeGBufferData(PGBufferData   InGBuffer,
                       out highp vec4 OutGBufferA,
                       out highp vec4 OutGBufferB,
                       out highp vec4 OutGBufferC)
{
    OutGBufferA.rgb = EncodeNormal(InGBuffer.worldNormal);

    OutGBufferB.r = InGBuffer.metallic;
    OutGBufferB.g = InGBuffer.specular;
    OutGBufferB.b = InGBuffer.roughness;
    OutGBufferB.a = EncodeShadingModelId(InGBuffer.shadingModelID);

    OutGBufferC.rgb = EncodeBaseColor(InGBuffer.baseColor);
}

void DecodeGBufferData(out PGBufferData OutGBuffer, highp vec4 InGBufferA, highp vec4 InGBufferB, highp vec4 InGBufferC)
{
    OutGBuffer.worldNormal = DecodeNormal(InGBufferA.xyz);

    OutGBuffer.metallic       = InGBufferB.r;
    OutGBuffer.specular       = InGBufferB.g;
    OutGBuffer.roughness      = InGBufferB.b;
    OutGBuffer.shadingModelID = DecodeShadingModelId(InGBufferB.a);

    OutGBuffer.baseColor = DecodeBaseColor(InGBufferC.rgb);
}

mesh_lighting.h

#define PI 3.1416

// todo: param/const
#define MAX_REFLECTION_LOD 8.0

// Normal Distribution function --------------------------------------
highp float D_GGX(highp float dotNH, highp float roughness)
{
    highp float alpha  = roughness * roughness;
    highp float alpha2 = alpha * alpha;
    highp float denom  = dotNH * dotNH * (alpha2 - 1.0) + 1.0;
    return (alpha2) / (PI * denom * denom);
}

// Geometric Shadowing function --------------------------------------
highp float G_SchlicksmithGGX(highp float dotNL, highp float dotNV, highp float roughness)
{
    highp float r  = (roughness + 1.0);
    highp float k  = (r * r) / 8.0;
    highp float GL = dotNL / (dotNL * (1.0 - k) + k);
    highp float GV = dotNV / (dotNV * (1.0 - k) + k);
    return GL * GV;
}

// Fresnel function ----------------------------------------------------
highp float Pow5(highp float x)
{
    return (x * x * x * x * x);
}

highp vec3 F_Schlick(highp float cosTheta, highp vec3 F0) 
{ 
    return F0 + (1.0 - F0) * Pow5(1.0 - cosTheta); 
    }

highp vec3 F_SchlickR(highp float cosTheta, highp vec3 F0, highp float roughness)
{
    return F0 + (max(vec3(1.0 - roughness, 1.0 - roughness, 1.0 - roughness), F0) - F0) * Pow5(1.0 - cosTheta);
}

// Specular and diffuse BRDF composition --------------------------------------------
highp vec3 BRDF(highp vec3  L,
                highp vec3  V,
                highp vec3  N,
                highp vec3  F0,
                highp vec3  basecolor,
                highp float metallic,
                highp float roughness)
{
    // Precalculate vectors and dot products
    highp vec3  H     = normalize(V + L);
    highp float dotNV = clamp(dot(N, V), 0.0, 1.0);
    highp float dotNL = clamp(dot(N, L), 0.0, 1.0);
    highp float dotLH = clamp(dot(L, H), 0.0, 1.0);
    highp float dotNH = clamp(dot(N, H), 0.0, 1.0);

    // Light color fixed
    // vec3 lightColor = vec3(1.0);

    highp vec3 color = vec3(0.0);

    highp float rroughness = max(0.05, roughness);
    // D = Normal distribution (Distribution of the microfacets)
    highp float D = D_GGX(dotNH, rroughness);
    // G = Geometric shadowing term (Microfacets shadowing)
    highp float G = G_SchlicksmithGGX(dotNL, dotNV, rroughness);
    // F = Fresnel factor (Reflectance depending on angle of incidence)
    highp vec3 F = F_Schlick(dotNV, F0);

    highp vec3 spec = D * F * G / (4.0 * dotNL * dotNV + 0.001);
    highp vec3 kD   = (vec3(1.0) - F) * (1.0 - metallic);

    color += (kD * basecolor / PI + (1.0 - kD) * spec);
    // color += (kD * basecolor / PI + spec) * dotNL;
    // color += (kD * basecolor / PI + spec) * dotNL * lightColor;

    return color;
}

highp vec2 ndcxy_to_uv(highp vec2 ndcxy) { return ndcxy * vec2(0.5, 0.5) + vec2(0.5, 0.5); }

highp vec2 uv_to_ndcxy(highp vec2 uv) { return uv * vec2(2.0, 2.0) + vec2(-1.0, -1.0); }

mesh_lighting.inl

highp vec3 V = normalize(camera_position - in_world_position);
highp vec3 R = reflect(-V, N);

highp vec3 origin_samplecube_N = vec3(N.x, N.z, N.y);
highp vec3 origin_samplecube_R = vec3(R.x, R.z, R.y);

highp vec3 F0 = mix(vec3(dielectric_specular, dielectric_specular, dielectric_specular), basecolor, metallic);

// direct light specular and diffuse BRDF contribution
highp vec3 Lo = vec3(0.0, 0.0, 0.0);
for (highp int light_index = 0; light_index < int(point_light_num) && light_index < m_max_point_light_count;
     ++light_index)
{
    highp vec3  point_light_position = scene_point_lights[light_index].position;
    highp float point_light_radius   = scene_point_lights[light_index].radius;

    highp vec3  L   = normalize(point_light_position - in_world_position);
    highp float NoL = min(dot(N, L), 1.0);

    // point light
    highp float distance             = length(point_light_position - in_world_position);
    highp float distance_attenuation = 1.0 / (distance * distance + 1.0);
    highp float radius_attenuation   = 1.0 - ((distance * distance) / (point_light_radius * point_light_radius));

    highp float light_attenuation = radius_attenuation * distance_attenuation * NoL;
    if (light_attenuation > 0.0)
    {
        highp float shadow;
        {
            // world space to light view space
            // identity rotation
            // Z - Up
            // Y - Forward
            // X - Right
            highp vec3 position_view_space = in_world_position - point_light_position;

            highp vec3 position_spherical_function_domain = normalize(position_view_space);

            // use abs to avoid divergence
            // z > 0
            // (x_2d, y_2d, 0) + (0, 0, 1) = λ ((x_sph, y_sph, z_sph) + (0, 0, 1))
            // (x_2d, y_2d) = (x_sph, y_sph) / (z_sph + 1)
            // z < 0
            // (x_2d, y_2d, 0) + (0, 0, -1) = λ ((x_sph, y_sph, z_sph) + (0, 0, -1))
            // (x_2d, y_2d) = (x_sph, y_sph) / (-z_sph + 1)
            highp vec2 position_ndcxy =
                position_spherical_function_domain.xy / (abs(position_spherical_function_domain.z) + 1.0);

            // use sign to avoid divergence
            // -1.0 to 0
            // 1.0 to 1
            highp vec2  uv = ndcxy_to_uv(position_ndcxy);
            highp float layer_index =
                (0.5 + 0.5 * sign(position_spherical_function_domain.z)) + 2.0 * float(light_index);

            highp float depth          = texture(point_lights_shadow, vec3(uv, layer_index)).r + 0.000075;
            highp float closest_length = (depth)*point_light_radius;

            highp float current_length = length(position_view_space);

            shadow = (closest_length >= current_length) ? 1.0f : -1.0f;
        }

        if (shadow > 0.0f)
        {
            highp vec3 En = scene_point_lights[light_index].intensity * light_attenuation;
            Lo += BRDF(L, V, N, F0, basecolor, metallic, roughness) * En;
        }
    }
};

// direct ambient contribution
highp vec3 La = vec3(0.0f, 0.0f, 0.0f);
La            = basecolor * ambient_light;

// indirect environment
highp vec3 irradiance = texture(irradiance_sampler, origin_samplecube_N).rgb;
highp vec3 diffuse    = irradiance * basecolor;

highp vec3 F       = F_SchlickR(clamp(dot(N, V), 0.0, 1.0), F0, roughness);
highp vec2 brdfLUT = texture(brdfLUT_sampler, vec2(clamp(dot(N, V), 0.0, 1.0), roughness)).rg;

highp float lod        = roughness * MAX_REFLECTION_LOD;
highp vec3  reflection = textureLod(specular_sampler, origin_samplecube_R, lod).rgb;
highp vec3  specular   = reflection * (F * brdfLUT.x + brdfLUT.y);

highp vec3 kD = 1.0 - F;
kD *= 1.0 - metallic;
highp vec3 Libl = (kD * diffuse + specular);

// directional light
{
    highp vec3  L   = normalize(scene_directional_light.direction);
    highp float NoL = min(dot(N, L), 1.0);

    if (NoL > 0.0)
    {
        highp float shadow;
        {
            highp vec4 position_clip = directional_light_proj_view * vec4(in_world_position, 1.0);
            highp vec3 position_ndc  = position_clip.xyz / position_clip.w;

            highp vec2 uv = ndcxy_to_uv(position_ndc.xy);

            highp float closest_depth = texture(directional_light_shadow, uv).r + 0.000075;
            highp float current_depth = position_ndc.z;

            shadow = (closest_depth >= current_depth) ? 1.0f : -1.0f;
        }

        if (shadow > 0.0f)
        {
            highp vec3 En = scene_directional_light.color * NoL;
            Lo += BRDF(L, V, N, F0, basecolor, metallic, roughness) * En;
        }
    }
}

// result
result_color = Lo + La + Libl;

deferred_lighting.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
#include "gbuffer.h"

struct DirectionalLight
{
    highp vec3 direction;
    lowp float _padding_direction;
    highp vec3 color;
    lowp float _padding_color;
};

struct PointLight
{
    highp vec3  position;
    highp float radius;
    highp vec3  intensity;
    lowp float  _padding_intensity;
};

layout(set = 0, binding = 0) readonly buffer _mesh_per_frame
{
    highp mat4       proj_view_matrix;
    highp vec3       camera_position;
    lowp float       _padding_camera_position;
    highp vec3       ambient_light;
    lowp float       _padding_ambient_light;
    highp uint       point_light_num;
    uint             _padding_point_light_num_1;
    uint             _padding_point_light_num_2;
    uint             _padding_point_light_num_3;
    PointLight       scene_point_lights[m_max_point_light_count];
    DirectionalLight scene_directional_light;
    highp mat4       directional_light_proj_view;
};

layout(set = 0, binding = 3) uniform sampler2D brdfLUT_sampler;
layout(set = 0, binding = 4) uniform samplerCube irradiance_sampler;
layout(set = 0, binding = 5) uniform samplerCube specular_sampler;
layout(set = 0, binding = 6) uniform highp sampler2DArray point_lights_shadow;
layout(set = 0, binding = 7) uniform highp sampler2D directional_light_shadow;

layout(input_attachment_index = 0, set = 1, binding = 0) uniform highp subpassInput in_gbuffer_a;
layout(input_attachment_index = 1, set = 1, binding = 1) uniform highp subpassInput in_gbuffer_b;
layout(input_attachment_index = 2, set = 1, binding = 2) uniform highp subpassInput in_gbuffer_c;
layout(input_attachment_index = 3, set = 1, binding = 3) uniform highp subpassInput in_scene_depth;

layout(set = 2, binding = 1) uniform samplerCube skybox_sampler;

layout(location = 0) in highp vec2 in_texcoord;
layout(location = 0) out highp vec4 out_color;

#include "mesh_lighting.h"

void main()
{
    PGBufferData gbuffer;
    highp vec4   gbuffer_a = subpassLoad(in_gbuffer_a).rgba;
    highp vec4   gbuffer_b = subpassLoad(in_gbuffer_b).rgba;
    highp vec4   gbuffer_c = subpassLoad(in_gbuffer_c).rgba;
    DecodeGBufferData(gbuffer, gbuffer_a, gbuffer_b, gbuffer_c);

    highp vec3  N                   = gbuffer.worldNormal;
    highp vec3  basecolor           = gbuffer.baseColor;
    highp float metallic            = gbuffer.metallic;
    highp float dielectric_specular = 0.08 * gbuffer.specular;
    highp float roughness           = gbuffer.roughness;

    highp vec3 in_world_position;
    {
        highp float scene_depth              = subpassLoad(in_scene_depth).r;
        highp vec4  ndc                      = vec4(uv_to_ndcxy(in_texcoord), scene_depth, 1.0);
        highp mat4  inverse_proj_view_matrix = inverse(proj_view_matrix);
        highp vec4  in_world_position_with_w = inverse_proj_view_matrix * ndc;
        in_world_position                    = in_world_position_with_w.xyz / in_world_position_with_w.www;
    }

    highp vec3 result_color = vec3(0.0, 0.0, 0.0);

    if (SHADINGMODELID_UNLIT == gbuffer.shadingModelID)
    {
        // skybox
        highp vec3 in_UVW            = normalize(in_world_position - camera_position);
        highp vec3 origin_sample_UVW = vec3(in_UVW.x, in_UVW.z, in_UVW.y);

        result_color = textureLod(skybox_sampler, origin_sample_UVW, 0.0).rgb;
    }
    else if (SHADINGMODELID_DEFAULT_LIT == gbuffer.shadingModelID)
    {
#include "mesh_lighting.inl"
    }

    out_color = vec4(result_color, 1.0f);
}

mesh.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
#include "structures.h"

struct DirectionalLight
{
    vec3  direction;
    float _padding_direction;
    vec3  color;
    float _padding_color;
};

struct PointLight
{
    vec3  position;
    float radius;
    vec3  intensity;
    float _padding_intensity;
};

layout(set = 0, binding = 0) readonly buffer _unused_name_perframe
{
    mat4             proj_view_matrix;
    vec3             camera_position;
    float            _padding_camera_position;
    vec3             ambient_light;
    float            _padding_ambient_light;
    uint             point_light_num;
    uint             _padding_point_light_num_1;
    uint             _padding_point_light_num_2;
    uint             _padding_point_light_num_3;
    PointLight       scene_point_lights[m_max_point_light_count];
    DirectionalLight scene_directional_light;
    highp mat4       directional_light_proj_view;
};

layout(set = 0, binding = 1) readonly buffer _unused_name_per_drawcall
{
    VulkanMeshInstance mesh_instances[m_mesh_per_drawcall_max_instance_count];
};

layout(set = 0, binding = 2) readonly buffer _unused_name_per_drawcall_vertex_blending
{
    highp mat4 joint_matrices[m_mesh_vertex_blending_max_joint_count * m_mesh_per_drawcall_max_instance_count];
};
layout(set = 1, binding = 0) readonly buffer _unused_name_per_mesh_joint_binding
{
    VulkanMeshVertexJointBinding indices_and_weights[];
};

layout(location = 0) in vec3 in_position; // for some types as dvec3 takes 2 locations
layout(location = 1) in vec3 in_normal;
layout(location = 2) in vec3 in_tangent;
layout(location = 3) in vec2 in_texcoord;

layout(location = 0) out vec3 out_world_position; // output in framebuffer 0 for fragment shader
layout(location = 1) out vec3 out_normal;
layout(location = 2) out vec3 out_tangent;
layout(location = 3) out vec2 out_texcoord;

void main()
{
    highp mat4  model_matrix           = mesh_instances[gl_InstanceIndex].model_matrix;
    highp float enable_vertex_blending = mesh_instances[gl_InstanceIndex].enable_vertex_blending;

    highp vec3 model_position;
    highp vec3 model_normal;
    highp vec3 model_tangent;
    if (enable_vertex_blending > 0.0)
    {
        highp ivec4 in_indices = indices_and_weights[gl_VertexIndex].indices;
        highp vec4  in_weights = indices_and_weights[gl_VertexIndex].weights;

        highp mat4 vertex_blending_matrix = mat4x4(
            vec4(0.0, 0.0, 0.0, 0.0), vec4(0.0, 0.0, 0.0, 0.0), vec4(0.0, 0.0, 0.0, 0.0), vec4(0.0, 0.0, 0.0, 0.0));

        if (in_weights.x > 0.0 && in_indices.x > 0)
        {
            vertex_blending_matrix +=
                joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.x] * in_weights.x;
        }

        if (in_weights.y > 0.0 && in_indices.y > 0)
        {
            vertex_blending_matrix +=
                joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.y] * in_weights.y;
        }

        if (in_weights.z > 0.0 && in_indices.z > 0)
        {
            vertex_blending_matrix +=
                joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.z] * in_weights.z;
        }

        if (in_weights.w > 0.0 && in_indices.w > 0)
        {
            vertex_blending_matrix +=
                joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.w] * in_weights.w;
        }

        model_position = (vertex_blending_matrix * vec4(in_position, 1.0)).xyz;

        highp mat3x3 vertex_blending_tangent_matrix =
            mat3x3(vertex_blending_matrix[0].xyz, vertex_blending_matrix[1].xyz, vertex_blending_matrix[2].xyz);

        model_normal  = normalize(vertex_blending_tangent_matrix * in_normal);
        model_tangent = normalize(vertex_blending_tangent_matrix * in_tangent);
    }
    else
    {
        model_position = in_position;
        model_normal   = in_normal;
        model_tangent  = in_tangent;
    }

    out_world_position = (model_matrix * vec4(model_position, 1.0)).xyz;

    gl_Position = proj_view_matrix * vec4(out_world_position, 1.0f);

    // TODO: normal matrix
    mat3x3 tangent_matrix = mat3x3(model_matrix[0].xyz, model_matrix[1].xyz, model_matrix[2].xyz);
    out_normal            = normalize(tangent_matrix * model_normal);
    out_tangent           = normalize(tangent_matrix * model_tangent);

    out_texcoord = in_texcoord;
}

mesh_gbuffer.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
#include "gbuffer.h"

layout(set = 2, binding = 0) uniform _unused_name_permaterial
{
    highp vec4  baseColorFactor;
    highp float metallicFactor;
    highp float roughnessFactor;
    highp float normalScale;
    highp float occlusionStrength;
    highp vec3  emissiveFactor;
    uint        is_blend;
    uint        is_double_sided;
};

layout(set = 2, binding = 1) uniform sampler2D base_color_texture_sampler;
layout(set = 2, binding = 2) uniform sampler2D metallic_roughness_texture_sampler;
layout(set = 2, binding = 3) uniform sampler2D normal_texture_sampler;
layout(set = 2, binding = 4) uniform sampler2D occlusion_texture_sampler;
layout(set = 2, binding = 5) uniform sampler2D emissive_color_texture_sampler;

// read in fragnormal (from vertex shader)
layout(location = 0) in highp vec3 in_world_position;
layout(location = 1) in highp vec3 in_normal;
layout(location = 2) in highp vec3 in_tangent;
layout(location = 3) in highp vec2 in_texcoord;

// output screen color to location 0
layout(location = 0) out highp vec4 out_gbuffer_a;
layout(location = 1) out highp vec4 out_gbuffer_b;
layout(location = 2) out highp vec4 out_gbuffer_c;
// layout(location = 3) out highp vec4 out_scene_color;

highp vec3 getBasecolor()
{
    highp vec3 basecolor = texture(base_color_texture_sampler, in_texcoord).xyz * baseColorFactor.xyz;
    return basecolor;
}

highp vec3 calculateNormal()
{
    highp vec3 tangent_normal = texture(normal_texture_sampler, in_texcoord).xyz * 2.0 - 1.0;

    highp vec3 N = normalize(in_normal);
    highp vec3 T = normalize(in_tangent.xyz);
    highp vec3 B = normalize(cross(N, T));

    highp mat3 TBN = mat3(T, B, N);
    return normalize(TBN * tangent_normal);
}

void main()
{
    PGBufferData gbuffer;
    gbuffer.worldNormal    = calculateNormal();
    gbuffer.baseColor      = getBasecolor();
    gbuffer.metallic       = texture(metallic_roughness_texture_sampler, in_texcoord).z * metallicFactor;
    gbuffer.specular       = 0.5;
    gbuffer.roughness      = texture(metallic_roughness_texture_sampler, in_texcoord).y * roughnessFactor;
    gbuffer.shadingModelID = SHADINGMODELID_DEFAULT_LIT;

    highp vec3 Le = texture(emissive_color_texture_sampler, in_texcoord).xyz * emissiveFactor;

    EncodeGBufferData(gbuffer, out_gbuffer_a, out_gbuffer_b, out_gbuffer_c);

    // out_scene_color.rgba = vec4(Le, 1.0);
}

mesh.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

struct DirectionalLight
{
    highp vec3 direction;
    lowp float _padding_direction;
    highp vec3 color;
    lowp float _padding_color;
};

struct PointLight
{
    highp vec3  position;
    highp float radius;
    highp vec3  intensity;
    lowp float  _padding_intensity;
};

layout(set = 0, binding = 0) readonly buffer _unused_name_perframe
{
    highp mat4       proj_view_matrix;
    highp vec3       camera_position;
    lowp float       _padding_camera_position;
    highp vec3       ambient_light;
    lowp float       _padding_ambient_light;
    highp uint       point_light_num;
    uint             _padding_point_light_num_1;
    uint             _padding_point_light_num_2;
    uint             _padding_point_light_num_3;
    PointLight       scene_point_lights[m_max_point_light_count];
    DirectionalLight scene_directional_light;
    highp mat4       directional_light_proj_view;
};

layout(set = 0, binding = 3) uniform sampler2D brdfLUT_sampler;
layout(set = 0, binding = 4) uniform samplerCube irradiance_sampler;
layout(set = 0, binding = 5) uniform samplerCube specular_sampler;
layout(set = 0, binding = 6) uniform highp sampler2DArray point_lights_shadow;
layout(set = 0, binding = 7) uniform highp sampler2D directional_light_shadow;

layout(set = 2, binding = 0) uniform _unused_name_permaterial
{
    highp vec4  baseColorFactor;
    highp float metallicFactor;
    highp float roughnessFactor;
    highp float normalScale;
    highp float occlusionStrength;
    highp vec3  emissiveFactor;
    uint        is_blend;
    uint        is_double_sided;
};

layout(set = 2, binding = 1) uniform sampler2D base_color_texture_sampler;
layout(set = 2, binding = 2) uniform sampler2D metallic_roughness_texture_sampler;
layout(set = 2, binding = 3) uniform sampler2D normal_texture_sampler;
layout(set = 2, binding = 4) uniform sampler2D occlusion_texture_sampler;
layout(set = 2, binding = 5) uniform sampler2D emissive_color_texture_sampler;

// read in fragnormal (from vertex shader)
layout(location = 0) in highp vec3 in_world_position;
layout(location = 1) in highp vec3 in_normal;
layout(location = 2) in highp vec3 in_tangent;
layout(location = 3) in highp vec2 in_texcoord;

layout(location = 0) out highp vec4 out_scene_color;

highp vec3 getBasecolor()
{
    highp vec3 basecolor = texture(base_color_texture_sampler, in_texcoord).xyz * baseColorFactor.xyz;
    return basecolor;
}

highp vec3 calculateNormal()
{
    highp vec3 tangent_normal = texture(normal_texture_sampler, in_texcoord).xyz * 2.0 - 1.0;

    highp vec3 N = normalize(in_normal);
    highp vec3 T = normalize(in_tangent.xyz);
    highp vec3 B = normalize(cross(N, T));

    highp mat3 TBN = mat3(T, B, N);
    return normalize(TBN * tangent_normal);
}

#include "mesh_lighting.h"

void main()
{
    highp vec3  N                   = calculateNormal();
    highp vec3  basecolor           = getBasecolor();
    highp float metallic            = texture(metallic_roughness_texture_sampler, in_texcoord).z * metallicFactor;
    highp float dielectric_specular = 0.04;
    highp float roughness           = texture(metallic_roughness_texture_sampler, in_texcoord).y * roughnessFactor;

    highp vec3 result_color;

#include "mesh_lighting.inl"

    out_scene_color = vec4(result_color, 1.0);
}

skybox.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

struct DirectionalLight
{
    highp vec3 direction;
    lowp float _padding_direction;
    highp vec3 color;
    lowp float _padding_color;
};

struct PointLight
{
    highp vec3  position;
    highp float radius;
    highp vec3  intensity;
    lowp float  _padding_intensity;
};

layout(set = 0, binding = 0) readonly buffer _skybox_per_frame
{
    highp mat4       proj_view_matrix;
    highp vec3       camera_position;
    lowp float       _padding_camera_position;
    highp vec3       ambient_light;
    lowp float       _padding_ambient_light;
    highp uint       point_light_num;
    uint             _padding_point_light_num_1;
    uint             _padding_point_light_num_2;
    uint             _padding_point_light_num_3;
    PointLight       scene_point_lights[m_max_point_light_count];
    DirectionalLight scene_directional_light;
    highp mat4       directional_light_proj_view;
};

layout(location = 0) out vec3 out_UVW;

void main()
{
    const vec3 cube_corner_vertex_offsets[8] = vec3[8](vec3(1.0, 1.0, 1.0),
                                                       vec3(1.0, 1.0, -1.0),
                                                       vec3(1.0, -1.0, -1.0),
                                                       vec3(1.0, -1.0, 1.0),
                                                       vec3(-1.0, 1.0, 1.0),
                                                       vec3(-1.0, 1.0, -1.0),
                                                       vec3(-1.0, -1.0, -1.0),
                                                       vec3(-1.0, -1.0, 1.0));

    const int cube_triangle_index[36] = int[36](
            0,1,2,
            2,3,0,
            4,5,1,
            1,0,4,
            7,6,5,
            5,4,7,
            3,2,6,
            6,7,3,
            4,0,3,
            3,7,4,
            1,5,6,
            6,2,1); // x+, y+, x-, y-, z+, z-

    // vec3 world_position = camera_position + (camera_z_far_plane / 1.733) *
    // cube_corner_vertex_offsets[cube_triangle_index[gl_VertexIndex]];
    vec3 world_position = camera_position + cube_corner_vertex_offsets[cube_triangle_index[gl_VertexIndex]];

    // world to NDC
    vec4 clip_position = proj_view_matrix * vec4(world_position, 1.0);

    // depth set to 0.99999?
    clip_position.z = clip_position.w * 0.99999;
    gl_Position     = clip_position;

    out_UVW = normalize(world_position - camera_position);
}

skybox.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
#include "gbuffer.h"

layout(set = 0, binding = 1) uniform samplerCube specular_sampler;

layout(location = 0) in highp vec3 in_UVW;

layout(location = 0) out highp vec4 out_scene_color;

void main()
{
    highp vec3 origin_sample_UVW = vec3(in_UVW.x, in_UVW.z, in_UVW.y);
    highp vec3 color             = textureLod(specular_sampler, origin_sample_UVW, 0.0).rgb;

    out_scene_color = vec4(color, 1.0);
}

1.4. Tone Mapping Pass初始化

post_process.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

void main()
{
    const vec3 fullscreen_triangle_positions[3] =
        vec3[3](vec3(3.0, 1.0, 0.5), vec3(-1.0, 1.0, 0.5), vec3(-1.0, -3.0, 0.5));
    gl_Position = vec4(fullscreen_triangle_positions[gl_VertexIndex], 1.0);
}

tone_mapping.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

layout(input_attachment_index = 0, set = 0, binding = 0) uniform highp subpassInput in_color;

layout(location = 0) out highp vec4 out_color;

highp vec3 Uncharted2Tonemap(highp vec3 x);

void main()
{
    highp vec3 color = subpassLoad(in_color).rgb;

    // tone mapping
    color = Uncharted2Tonemap(color * 4.5f);
    color = color * (1.0f / Uncharted2Tonemap(vec3(11.2f)));

    // Gamma correct
    // TODO: select the VK_FORMAT_B8G8R8A8_SRGB surface format,
    // there is no need to do gamma correction in the fragment shader
    color = vec3(pow(color.x, 1.0 / 2.2), pow(color.y, 1.0 / 2.2), pow(color.z, 1.0 / 2.2));

    out_color = vec4(color, 1.0f);
}

highp vec3 Uncharted2Tonemap(highp vec3 x)
{
    highp float A = 0.15;
    highp float B = 0.50;
    highp float C = 0.10;
    highp float D = 0.20;
    highp float E = 0.02;
    highp float F = 0.30;
    return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
}

1.5. Color Grading Pass初始化

color_grading.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

layout(input_attachment_index = 0, set = 0, binding = 0) uniform highp subpassInput in_color;

layout(set = 0, binding = 1) uniform sampler2D color_grading_lut_texture_sampler;

layout(location = 0) out highp vec4 out_color;

void main()
{
    highp ivec2 lut_tex_size = textureSize(color_grading_lut_texture_sampler, 0);
    highp float _COLORS      = float(lut_tex_size.y);

    highp vec4 color       = subpassLoad(in_color).rgba;
    
    // texture(color_grading_lut_texture_sampler, uv)

    out_color = color;
}

1.6. UI Pass初始化

1.7. Combine UI Pass初始化

combine_ui.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

layout(input_attachment_index = 0, set = 0, binding = 0) uniform highp subpassInput in_scene_color;

layout(input_attachment_index = 1, set = 0, binding = 1) uniform highp subpassInput in_ui_color;

layout(location = 0) out highp vec4 out_color;

void main()
{
    highp vec4 scene_color = subpassLoad(in_scene_color).rgba;
    
    highp vec4 ui_color = subpassLoad(in_ui_color).rgba;
    
    // Gamma correct
    // TODO: select the VK_FORMAT_B8G8R8A8_SRGB surface format,
    // there is no need to do gamma correction in the fragment shader
    if(ui_color.r<1e-6&&ui_color.g<1e-6&&ui_color.a<1e-6)
    {
        ui_color = vec4(pow(ui_color.r, 1.0 / 2.2), pow(ui_color.g, 1.0 / 2.2), pow(ui_color.b, 1.0 / 2.2), pow(ui_color.a, 1.0 / 2.2));
        out_color = scene_color;
    }
    else
    {
        ui_color = vec4(pow(ui_color.r, 1.0 / 2.2), pow(ui_color.g, 1.0 / 2.2), pow(ui_color.b, 1.0 / 2.2), pow(ui_color.a, 1.0 / 2.2));
        out_color = ui_color;
    }
}

1.8. Pick Pass初始化

mesh_inefficient_pick.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
#include "structures.h"

layout(set = 0, binding = 0) readonly buffer _unused_name_perframe
{
    mat4 proj_view_matrix;
    uint rt_width;
    uint rt_height;
};

layout(set = 0, binding = 1) readonly buffer _unused_name_perdrawcall
{
    mat4 model_matrices[m_mesh_per_drawcall_max_instance_count];
    uint node_ids[m_mesh_per_drawcall_max_instance_count];
    float enable_vertex_blendings[m_mesh_per_drawcall_max_instance_count];
};

layout(set = 0, binding = 2) readonly buffer _unused_name_per_drawcall_vertex_blending
{
    mat4 joint_matrices[m_mesh_vertex_blending_max_joint_count * m_mesh_per_drawcall_max_instance_count];
};

layout(set = 1, binding = 0) readonly buffer _unused_name_per_mesh_joint_binding
{
    VulkanMeshVertexJointBinding indices_and_weights[];
};

layout(location = 0) in vec3 in_position;

layout(location = 0) flat out highp uint out_nodeid;

void main()
{
    highp mat4 model_matrix = model_matrices[gl_InstanceIndex];
    highp float enable_vertex_blending = enable_vertex_blendings[gl_InstanceIndex];

    highp vec3 model_position;
    if (enable_vertex_blending > 0.0)
    {
        highp ivec4 in_indices = indices_and_weights[gl_VertexIndex].indices;
        highp vec4 in_weights = indices_and_weights[gl_VertexIndex].weights;

        highp mat4 vertex_blending_matrix = mat4x4(
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0),
            vec4(0.0, 0.0, 0.0, 0.0));

        if (in_weights.x > 0.0 && in_indices.x > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.x] * in_weights.x;
        }

        if (in_weights.y > 0.0 && in_indices.y > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.y] * in_weights.y;
        }

        if (in_weights.z > 0.0 && in_indices.z > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.z] * in_weights.z;
        }

        if (in_weights.w > 0.0 && in_indices.w > 0)
        {
            vertex_blending_matrix += joint_matrices[m_mesh_vertex_blending_max_joint_count * gl_InstanceIndex + in_indices.w] * in_weights.w;
        }

        model_position = (vertex_blending_matrix * vec4(in_position, 1.0)).xyz;
    }
    else
    {
        model_position = in_position;
    }

    gl_Position = proj_view_matrix * model_matrix * vec4(in_position, 1.0);

    out_nodeid = node_ids[gl_InstanceIndex];
}

mesh_inefficient_pick.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

layout(location = 0) flat in highp uint in_nodeid;

layout(early_fragment_tests) in;

layout (location = 0) out highp uint out_node_id;

void main()
{
    out_node_id = in_nodeid;
}

1.9. FXAA Pass初始化

fxaa.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

layout(location = 0) out vec2 out_uv;

void main()
{
    const vec3 fullscreen_triangle_positions[3] =
        vec3[3](vec3(3.0, 1.0, 0.5), vec3(-1.0, 1.0, 0.5), vec3(-1.0, -3.0, 0.5));

    out_uv = 0.5 * (fullscreen_triangle_positions[gl_VertexIndex].xy + vec2(1.0, 1.0));
    gl_Position = vec4(fullscreen_triangle_positions[gl_VertexIndex], 1.0);
}

fxaa.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
precision highp float;
precision highp int;

layout(set = 0, binding = 0) uniform sampler2D in_color;

layout(location = 0) in vec2 in_uv;

layout(location = 0) out vec4 out_color;


/* pixel index in 3*3 kernel
    +---+---+---+
    | 0 | 1 | 2 |
    +---+---+---+
    | 3 | 4 | 5 |
    +---+---+---+
    | 6 | 7 | 8 |
    +---+---+---+
*/
#define UP_LEFT      0
#define UP           1
#define UP_RIGHT     2
#define LEFT         3
#define CENTER       4
#define RIGHT        5
#define DOWN_LEFT    6
#define DOWN         7
#define DOWN_RIGHT   8
vec2 KERNEL_STEP_MAT[] = vec2[9](
    vec2(-1.0, 1.0), vec2(0.0, 1.0), vec2(1.0, 1.0),
    vec2(-1.0, 0.0), vec2(0.0, 0.0), vec2(1.0, 0.0),
    vec2(-1.0, -1.0), vec2(0.0, -1.0), vec2(1.0, -1.0)
);



/* in order to accelerate exploring along tangent bidirectional, step by an increasing amount of pixels QUALITY(i) 
   the max step count is 12
    +-----------------+---+---+---+---+---+---+---+---+---+---+---+---+
    |step index       | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |
    +-----------------+---+---+---+---+---+---+---+---+---+---+---+---+
    |step pixels count|1.0|1.0|1.0|1.0|1.0|1.5|2.0|2.0|2.0|2.0|4.0|8.0|
    +-----------------+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define STEP_COUNT_MAX   12
float QUALITY(int i) {
    if (i < 5) return 1.0;
    if (i == 5) return 1.5;
    if (i < 10) return 2.0;
    if (i == 10) return 4.0;
    if (i == 11) return 8.0;
}


// L = 0.299 * R + 0.587 * G + 0.114 * B
float RGB2LUMA(vec3 color) {
    return dot(vec3(0.299, 0.578, 0.114), color);
}


#define EDGE_THRESHOLD_MIN  0.0312
#define EDGE_THRESHOLD_MAX  0.125
#define SUBPIXEL_QUALITY    0.75
#define GRADIENT_SCALE      0.25

void main()
{
    highp ivec2 screen_size = textureSize(in_color, 0);
    highp vec2 uv_step = vec2(1.0 / float(screen_size.x), 1.0 / float(screen_size.y));

    float luma_mat[9];
    for (int i = 0; i < 9; i++) {
        luma_mat[i] = RGB2LUMA(texture(in_color, in_uv + uv_step * KERNEL_STEP_MAT[i]).xyz);
    }

    // detecting where to apply FXAA, return the pixel color if not
    float luma_min = min(luma_mat[CENTER], min(min(luma_mat[UP], luma_mat[DOWN]), min(luma_mat[LEFT], luma_mat[RIGHT])));
    float luma_max = max(luma_mat[CENTER], max(max(luma_mat[UP], luma_mat[DOWN]), max(luma_mat[LEFT], luma_mat[RIGHT])));
    float luma_range = luma_max - luma_min;
    if(luma_range < max(EDGE_THRESHOLD_MIN, luma_max * EDGE_THRESHOLD_MAX)) {
        out_color = texture(in_color, in_uv);
        return;
    }

    // choosing edge tangent
    // horizontal: |(upleft-left)-(left-downleft)|+2*|(up-center)-(center-down)|+|(upright-right)-(right-downright)|
    // vertical: |(upright-up)-(up-upleft)|+2*|(right-center)-(center-left)|+|(downright-down)-(down-downleft)|
    float luma_horizontal = 
        abs(luma_mat[UP_LEFT] + luma_mat[DOWN_LEFT] - 2.0 * luma_mat[LEFT])
        + 2.0 * abs(luma_mat[UP] + luma_mat[DOWN] - 2.0 * luma_mat[CENTER])
        + abs(luma_mat[UP_RIGHT] + luma_mat[DOWN_RIGHT] - 2.0 * luma_mat[RIGHT]);
    float luma_vertical = 
        abs(luma_mat[UP_LEFT] + luma_mat[UP_RIGHT] - 2.0 * luma_mat[UP])
        + 2.0 * abs(luma_mat[LEFT] + luma_mat[RIGHT] - 2.0 * luma_mat[CENTER])
        + abs(luma_mat[DOWN_LEFT] + luma_mat[DOWN_RIGHT] - 2.0 * luma_mat[DOWN]);
    bool is_horizontal = luma_horizontal > luma_vertical;

    // choosing edge normal 
    float gradient_down_left = (is_horizontal ? luma_mat[DOWN] : luma_mat[LEFT]) - luma_mat[CENTER];
    float gradient_up_right = (is_horizontal ? luma_mat[UP] : luma_mat[RIGHT]) - luma_mat[CENTER];
    bool is_down_left = abs(gradient_down_left) > abs(gradient_up_right);

    // get the tangent uv step vector and the normal uv step vector
    vec2 step_tangent = (is_horizontal ? vec2(1.0, 0.0) : vec2(0.0, 1.0)) * uv_step;
    vec2 step_normal =  (is_down_left ? -1.0 : 1.0) * (is_horizontal ? vec2(0.0, 1.0) : vec2(1.0, 0.0)) * uv_step;

    // get the change rate of gradient in normal per pixel
    float gradient = is_down_left ? gradient_down_left : gradient_up_right;

    // start at middle point of tangent edge
    vec2 uv_start = in_uv + 0.5 * step_normal;
    float luma_average_start = luma_mat[CENTER] + 0.5 * gradient;    
    //return vec4(luma_average_start, luma_average_start,luma_average_start, 1.0);

    // explore along tangent bidirectional until reach the edge both
    vec2 uv_pos = uv_start + step_tangent;
    vec2 uv_neg = uv_start - step_tangent;

    float delta_luma_pos = RGB2LUMA(texture(in_color, uv_pos).rgb) - luma_average_start;
    float delta_luma_neg = RGB2LUMA(texture(in_color, uv_neg).rgb) - luma_average_start;

    bool reached_pos = abs(delta_luma_pos) > GRADIENT_SCALE * abs(gradient);
    bool reached_neg = abs(delta_luma_neg) > GRADIENT_SCALE * abs(gradient);
    bool reached_both = reached_pos && reached_neg;

    if (!reached_pos) uv_pos += step_tangent;
    if (!reached_neg) uv_neg -= step_tangent;

    if (!reached_both) {
        for(int i = 2; i < STEP_COUNT_MAX; i++){
            if(!reached_pos) delta_luma_pos = RGB2LUMA(texture(in_color, uv_pos).rgb) - luma_average_start;
            if(!reached_neg) delta_luma_neg = RGB2LUMA(texture(in_color, uv_neg).rgb) - luma_average_start;

            bool reached_pos = abs(delta_luma_pos) > GRADIENT_SCALE * abs(gradient);
            bool reached_neg = abs(delta_luma_neg) > GRADIENT_SCALE * abs(gradient);
            bool reached_both = reached_pos && reached_neg;

            if (!reached_pos) uv_pos += (QUALITY(i) * step_tangent);
            if (!reached_neg) uv_neg -= (QUALITY(i) * step_tangent);

            if (reached_both) break;
        }
    }

    // estimating offset
    float length_pos = max(abs(uv_pos - uv_start).x, abs(uv_pos - uv_start).y);
    float length_neg = max(abs(uv_neg - uv_start).x, abs(uv_neg - uv_start).y);
    bool is_pos_near = length_pos < length_neg;

    float pixel_offset = -1.0 * (is_pos_near ? length_pos : length_neg) / (length_pos + length_neg) + 0.5;

    // no offset if the bidirectional point is too far
    if(((is_pos_near ? delta_luma_pos : delta_luma_neg) < 0.0) == (luma_mat[CENTER] < luma_average_start)) pixel_offset = 0.0;

    // subpixel antialiasing
    float luma_average_center = 0.0;
    float average_weight_mat[] = float[9](
        1.0, 2.0, 1.0,
        2.0, 0.0, 2.0,
        1.0, 2.0, 1.0
    );
    for (int i = 0; i < 9; i++) luma_average_center += average_weight_mat[i] * luma_mat[i];
    luma_average_center /= 12.0;

    float subpixel_luma_range = clamp(abs(luma_average_center - luma_mat[CENTER]) / luma_range, 0.0, 1.0);
    float subpixel_offset = (-2.0 * subpixel_luma_range + 3.0) * subpixel_luma_range * subpixel_luma_range;
    subpixel_offset = subpixel_offset * subpixel_offset * SUBPIXEL_QUALITY;

    // use the max offset between subpixel offset with before
    pixel_offset = max(pixel_offset, subpixel_offset);


    out_color = texture(in_color, in_uv + pixel_offset * step_normal);
}

1.10. Particle Pass初始化

particle_emit.comp

#version 450

#define POINT_TYPE_EMITTER 0
#define MESH_TYPE_EMITTER  1

struct Particle
{
    vec3  pos;
    float life;
    vec3  vel;
    float size_x;
    vec3  acc;
    float size_y;
    vec4  color;
};

struct CountBuffer
{
    int dead_count;
    int alive_count;
    int alive_count_after_sim;
    int emit_count;
};

struct Argument
{
    uvec4 emit_count;
    uvec4 simulateCount;
    int   alive_flap_bit;
};

struct EmitterInfo
{
    vec4 pos;    // position base, variance
    mat4 rotation; // rotation
    vec4 vel;    // velocity base, variance
    vec4 acc;    // acceleration base, variance
    vec3 size;   // size base
    int  emitter_type;
    vec4 life;  // life base, variance, accumulate time
    vec4 color; // color rgba
    
};

layout(binding = 0) uniform UBO
{
    float emit_delta;
    int   xemit_count;
    float max_life;
    float fixed_time_step;
    float random0;
    float random1;
    float random2;
    uint  frameindex;
    vec4  gravity;
    uvec4 viewport;
    vec4  extent;
}
ubo;

layout(set = 0, binding = 1) buffer Pos { Particle Particles[]; };

layout(set = 0, binding = 2) buffer Counter { CountBuffer counter; };

layout(set = 0, binding = 3) buffer indirectArgumentBuffer { Argument argument; };

layout(set = 0, binding = 4) buffer AliveBuffer { ivec4 alivelist[]; };

layout(set = 0, binding = 5) buffer DeadBuffer { ivec4 deadbuffer[]; };

layout(set = 0, binding = 6) buffer AliveBufferNext { ivec4 alivelistnext[]; };

layout(set = 0, binding = 7) buffer EmitterInfoBuffer { EmitterInfo emitterinfo; };

layout(set = 0, binding = 10) uniform sampler2D piccolotexture;

// Gold Noise ©2015 dcerisano@standard3d.com
// - based on the Golden Ratio
// - uniform normalized distribution
// - fastest static noise generator function (also runs at low precision)
// - use with indicated fractional seeding method
const float PHI = 1.61803398874989484820459; // Φ = Golden Ratio

float gold_noise(in vec2 xy, in float seed) { return fract(tan(distance(xy * PHI, xy) * seed) * xy.x); }

const float PI = 3.1415926535897932384626433832795;

layout(local_size_x = 256) in;
void main()
{
    emitterinfo.life.z += 1;
    uint threadId = gl_GlobalInvocationID.x;
    if (emitterinfo.life.z > ubo.emit_delta)
    {
        emitterinfo.life.z = 1;
        if (threadId < counter.emit_count)
        {
            bool     fix = false;
            Particle particle;
            float    rnd0 = gold_noise(vec2(threadId * ubo.random0, threadId * ubo.random1), ubo.random2);
            float    rnd1 = gold_noise(vec2(threadId * ubo.random0, threadId * ubo.random1), ubo.random2 + 0.2);
            float    rnd2 = gold_noise(vec2(threadId * ubo.random0, threadId * ubo.random1), ubo.random2 + 0.4);
            if (emitterinfo.emitter_type == POINT_TYPE_EMITTER)
            {
                float theta = 0.15 * PI;
                float phi   = (2 * rnd0 - 1) * PI;
                float r     = 1 + rnd1;
                float x     = r * sin(theta) * cos(phi);
                float y     = r * sin(theta) * sin(phi);
                float z     = r * cos(theta);

                particle.pos.x = 0.1 * (2 * rnd0 - 1) * emitterinfo.pos.w + emitterinfo.pos.x;
                particle.pos.y = 0.1 * (2 * rnd1 - 1) * emitterinfo.pos.w + emitterinfo.pos.y;
                particle.pos.z = 0.1 * (2 * rnd2 - 1) * emitterinfo.pos.w + emitterinfo.pos.z;

                particle.vel.x = x * emitterinfo.vel.w + emitterinfo.vel.x;
                particle.vel.y = y * emitterinfo.vel.w + emitterinfo.vel.y;
                particle.vel.z = z * emitterinfo.vel.w + emitterinfo.vel.z;

                particle.color = emitterinfo.color;
            }
            else if (emitterinfo.emitter_type == MESH_TYPE_EMITTER)
            {
                vec4 rotated_pos = emitterinfo.rotation * vec4(0, rnd0, rnd1, 0);
                particle.pos.x   = emitterinfo.pos.x + rotated_pos.x;
                particle.pos.y   = emitterinfo.pos.y + rotated_pos.y;
                particle.pos.z   = emitterinfo.pos.z + rotated_pos.z;

                vec4 color = texture(piccolotexture, vec2(1.0 - rnd0, 1.0 - rnd1));
                if (color.w > 0.9)
                {
                    particle.color = color;
                    fix            = true;
                }
                else
                {
                    particle.color.x = 1.0 - rnd0;
                    particle.color.y = 1.0 - rnd1;
                    particle.color.z = 1.0 - rnd2;
                    particle.color.w = 0.0f;

                    vec4 rotated_vel =
                        emitterinfo.rotation * vec4((rnd0 * 2 - 1) * emitterinfo.vel.w + emitterinfo.vel.x,
                                                     (rnd1 * 2 - 1) * emitterinfo.vel.w + emitterinfo.vel.y,
                                                     (rnd2 * 2 - 1) * emitterinfo.vel.w + emitterinfo.vel.z,
                                                     1);
                    particle.vel.x = rotated_vel.x;
                    particle.vel.y = rotated_vel.y;
                    particle.vel.z = rotated_vel.z;
                }
            }
            else
            {
                // wrong emitter type, should not happen
            }

            if (!fix)
            {
                particle.acc.x = emitterinfo.acc.x + ubo.gravity.x;
                particle.acc.y = emitterinfo.acc.y + ubo.gravity.y;
                particle.acc.z = emitterinfo.acc.z + ubo.gravity.z;
            }
            else
            {
                particle.acc = vec3(0, 0, 0);
                particle.vel = vec3(0, 0, 0);
            }

            particle.life = rnd0 * emitterinfo.life.y + emitterinfo.life.x;

            particle.size_x = emitterinfo.size.x;
            particle.size_y = emitterinfo.size.y;

            // retrieve particle from dead pool
            int deadCount = atomicAdd(counter.dead_count, -1);
            int index     = deadbuffer[deadCount - 1].x;

            // append to particle buffer
            Particles[index] = particle;

            // add index to alive list
            if (argument.alive_flap_bit == 0)
            {
                int aliveIndex          = atomicAdd(counter.alive_count, 1);
                alivelist[aliveIndex].x = index;
            }
            else
            {
                int aliveIndex              = atomicAdd(counter.alive_count, 1);
                alivelistnext[aliveIndex].x = index;
            }
        }
    }
}

particle_kickoff.comp

#version 450

struct Particle
{
    vec3  pos;
    float life;
    vec3  vel;
    float size_x;
    vec3  acc;
    float size_y;
    vec4  color;
};

struct CountBuffer
{
    int dead_count;
    int alive_count;
    int alive_count_after_sim;
    int emit_count;
};

layout(binding = 0) uniform UBO
{
    float emit_delta;
    int   xemit_count;
    float max_life;
    float fixed_time_step;
    float random0;
    float random1;
    float random2;
    uint  frame_index;
    vec4  gravity;
    uvec4 viewport;
    vec4  extent;
}
ubo;

layout(std140, binding = 2) buffer Counter { CountBuffer counter; };

struct Argument
{
    uvec4 emit_count;
    uvec4 simulate_count;
    int   alive_flap_bit;
};

layout(std140, binding = 3) buffer ArgumentBuffer { Argument argument; };

layout(local_size_x = 1) in;
void main()
{
    int emitCount = min(counter.dead_count, ubo.xemit_count);

    // indirect argument for emit
    argument.emit_count.xyz = uvec3(ceil(float(emitCount) / float(256)), 1, 1);

    // indirect argument for simulate
    argument.simulate_count.xyz = uvec3(ceil(float(emitCount + counter.alive_count_after_sim) / float(256)), 1, 1);

    // set new alive cnt
    counter.alive_count = counter.alive_count_after_sim;

    // reset particle cnt
    counter.alive_count_after_sim = 0;

    counter.emit_count = emitCount;

    argument.alive_flap_bit = 1 - argument.alive_flap_bit;
}

particle_simulate.comp

#version 450

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

struct Particle
{
    vec3  pos;
    float life;
    vec3  vel;
    float size_x;
    vec3  acc;
    float size_y;
    vec4  color;
};

struct CountBuffer
{
    int dead_count;
    int alive_count;
    int alive_count_after_sim;
    int emit_count;
};

struct Argument
{
    uvec4 emit_count;
    uvec4 simulateCount;
    int   alive_flap_bit;
};

layout(binding = 0) uniform UBO
{
    float emit_delta;
    int   xemit_count;
    float max_life;
    float fixed_time_step;
    float random0;
    float random1;
    float random2;
    uint  frameindex;
    vec4  gravity;
    uvec4 viewport;
    vec4  extent;
}
ubo;

layout(std140, binding = 1) buffer Pos { Particle Particles[]; };

layout(std140, binding = 2) buffer Counter { CountBuffer counter; };

layout(std140, binding = 3) buffer indirectArgumentBuffer { Argument argument; };

layout(std140, binding = 4) buffer AliveBuffer { ivec4 alivelist[]; };

layout(std140, binding = 5) buffer DeadBuffer { ivec4 deadbuffer[]; };

layout(std140, binding = 6) buffer AliveBufferNext { ivec4 alivelistnext[]; };

layout(std140, binding = 8) uniform _unused_name_perframe
{
    mat4 view_matrix;
    mat4 proj_view_matrix;
    mat4 proj_inv_matrix;
};

layout(std140, binding = 9) buffer _unsed_name_render_particle { Particle renderParticles[]; };

layout(set = 1, binding = 0, rgba8) uniform readonly image2D in_normal;
layout(set = 1, binding = 1) uniform sampler2D in_scene_depth;

layout(local_size_x = 256) in;
void main()
{
    uint threadId = gl_GlobalInvocationID.x;
    if (threadId < counter.alive_count)
    {
        float    dt         = ubo.fixed_time_step;
        int      particleId = argument.alive_flap_bit == 0 ? alivelist[threadId].x : alivelistnext[threadId].x;
        Particle particle   = Particles[particleId];

        if (particle.life > 0)
        {
            particle.vel += particle.acc * dt;
            particle.pos += particle.vel * dt;

            // transfrom position to view space
            vec3 viewSpaceParticlePosition = (view_matrix * vec4(particle.pos, 1)).xyz;

            vec4 screenSpaceParticlePosition = proj_view_matrix * vec4(particle.pos, 1);
            screenSpaceParticlePosition.xyz /= screenSpaceParticlePosition.w;

            // on screen collision
            if (screenSpaceParticlePosition.x > -1 && screenSpaceParticlePosition.x < 1 &&
                screenSpaceParticlePosition.y > -1 && screenSpaceParticlePosition.y < 1)
            {
                vec2  uv = screenSpaceParticlePosition.xy * vec2(0.5f, 0.5f) + 0.5f;
                float px = (ubo.viewport.x + uv.x * ubo.viewport.z);
                float py = (ubo.viewport.y + uv.y * ubo.viewport.w);

                // Fetch the depth value at this point
                float depth = texture(in_scene_depth, vec2(px / ubo.extent.x, py / ubo.extent.y)).r;

                vec4 viewSpacePosOfDepthBuffer;
                viewSpacePosOfDepthBuffer.xy = screenSpaceParticlePosition.xy;
                viewSpacePosOfDepthBuffer.z  = depth;
                viewSpacePosOfDepthBuffer.w  = 1;

                // Transform into view space using the inverse projection matrix
                viewSpacePosOfDepthBuffer = proj_inv_matrix * viewSpacePosOfDepthBuffer;
                viewSpacePosOfDepthBuffer.xyz /= viewSpacePosOfDepthBuffer.w;
                float colliderthickness = 0.5f;

                if ((viewSpaceParticlePosition.z < viewSpacePosOfDepthBuffer.z) &&
                    viewSpaceParticlePosition.z + colliderthickness > viewSpacePosOfDepthBuffer.z)
                {
                    vec3 worldnormal = (imageLoad(in_normal, ivec2(px, py)).rgb) * 2 - 1;
                    if (dot(particle.vel, worldnormal) < 0)
                    {
                        vec3  prevd = normalize(particle.vel);
                        float distance =
                            abs(dot(prevd, (viewSpacePosOfDepthBuffer.xyz - viewSpaceParticlePosition.xyz)));

                        vec3 newv = 0.4f * reflect(particle.vel, worldnormal);

                        // stabilize
                        if (length(newv) < 0.3f)
                        {
                            particle.vel = vec3(0, 0, 0);
                        }
                        else
                        {
                            particle.vel = newv;
                        }

                        // push to surface, avoid multiple collision
                        particle.pos += -distance * prevd + particle.vel * dt;
                    }
                }
            }
        }

        if (particle.life < 0)
        {
            uint deadIndex          = atomicAdd(counter.dead_count, 1);
            deadbuffer[deadIndex].x = particleId;
            particle.pos            = vec3(0, 0, 0);
            particle.life           = 0;
            particle.vel            = vec3(0, 0, 0);
            particle.size_x         = 0;
            particle.acc            = vec3(0, 0, 0);
            particle.size_y         = 0;
            particle.color          = vec4(0, 0, 0, 0);
        }
        else
        {
            int nextAliveIndex = atomicAdd(counter.alive_count_after_sim, 1);
            if (argument.alive_flap_bit == 0)
                alivelistnext[nextAliveIndex].x = particleId;
            else
                alivelist[nextAliveIndex].x = particleId;

            particle.life -= dt;

            renderParticles[nextAliveIndex] = particle;
        }
        Particles[particleId] = particle;
    }
}

particlebillboard.vert

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"

layout(set = 0, binding = 0) uniform _unused_name_perframe
{
    mat4 proj_view_matrix;
    vec3 right_diection;
    vec3 up_direction;
    vec3 forward_diection;
};

struct Particle
{
    vec3  pos;
    float life;
    vec3  vel;
    float size_x;
    vec3  acc;
    float size_y;
    vec4  color;
};

layout(set = 0, binding = 1) readonly buffer _unused_name_perdrawcall { Particle particles[]; };

layout(location = 0) out vec4 out_color;
layout(location = 1) out vec2 out_uv;

void main()
{
    const vec2 vertex_buffer[4] = vec2[4](vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(-0.5, -0.5), vec2(0.5, -0.5));
    const vec2 uv_buffer[4]     = vec2[4](vec2(0, 1), vec2(1, 1), vec2(0, 0), vec2(1, 0));
    vec2       model_position   = vertex_buffer[gl_VertexIndex];

    // Real-Time Rendering Fourth Edition
    // 13.6 Billboarding
    // 13.6.2 World-Oriented Billboard
    Particle particle        = particles[gl_InstanceIndex];
    vec3     anchor_location = particle.pos;

    // viewport-oriented
    vec3  vel_dir      = particle.vel;
    float projectvel_x = dot(vel_dir, right_diection);
    float projectvel_y = dot(vel_dir, up_direction);
    float size_x       = particle.size_x;
    float size_y       = particle.size_y;

    vec3 world_position;
    if (abs(projectvel_x) < size_x || abs(projectvel_y) < size_y)
    {
        world_position =
            size_x * right_diection * model_position.x + size_y * up_direction * model_position.y + anchor_location;
    }
    else
    {
        vec3 project_dir = normalize(projectvel_x * right_diection + projectvel_y * up_direction);
        vec3 side_dir    = normalize(cross(forward_diection, project_dir));
        world_position =
            size_x * side_dir * model_position.x + size_y * project_dir * model_position.y + anchor_location;
    }

    // world to NDC
    gl_Position = proj_view_matrix * vec4(world_position, 1.0);

    out_color = particle.color;
    out_uv    = uv_buffer[gl_VertexIndex];
}

particlebillboard.frag

#version 310 es

#extension GL_GOOGLE_include_directive : enable

#include "constants.h"
#include "gbuffer.h"

layout(location = 0) in highp vec4 in_color;
layout(location = 1) in highp vec2 in_uv;

layout(set = 0, binding = 2) uniform sampler2D sparktexture;

layout(location = 0) out highp vec4 out_scene_color;

void main() { 
    out_scene_color.xyz = 4.0f * texture(sparktexture, in_uv).r * in_color.xyz;
    out_scene_color.w = texture(sparktexture, in_uv).r;
}

2. Debug Draw Manager初始化

debugdraw.vert

#version 450

#extension GL_GOOGLE_include_directive :enable
#include "constants.h"

layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec4 inColor;
layout(location = 2) in vec2 texcoord;

layout(set = 0, binding = 0) uniform UniformBufferObject {
    mat4 proj_view_matrix;
} ubo;

layout(set = 0, binding = 1) uniform UniformDynamicBufferObject {
    mat4 model;
    vec4 color;
} dynamic_ubo;

layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec2 fragTexCoord;

void main() {
    if(texcoord.x<0)
    {
        gl_Position = ubo.proj_view_matrix * dynamic_ubo.model * vec4(inPosition,1.0);
    }
    else
    {
        gl_Position = vec4(inPosition,1.0);
    }
    
    gl_PointSize = 2;

    if(dynamic_ubo.color.a>0.000001)
    {
        fragColor = dynamic_ubo.color;
    }
    else 
    {
        fragColor = inColor;
    }
    fragTexCoord = texcoord;
}

debugdraw.frag

#version 450

#extension GL_GOOGLE_include_directive :enable
layout(location = 0) in vec4 fragColor;
layout(location = 1) in vec2 fragTexCoord;

layout(location = 0) out vec4 outColor;

layout(set = 0, binding = 2) uniform sampler2D texSampler;


void main(){
    outColor = fragColor;
    if(fragTexCoord.x >= 0.0f && fragTexCoord.y >= 0.0f)
    {
        vec4 tex = texture(texSampler, fragTexCoord);
        float xi = tex.r;
        outColor = vec4(fragColor.r*xi,fragColor.g*xi,fragColor.b*xi,fragColor.a*xi);
    }
}

3. Editor UI初始化