game_kernel 0.1.0

A 3D game engine written entirely in rust
Documentation
#version 450

layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;

#include<light_structs.h>
#include<poisson.h>

layout(set = 0, binding = 0) uniform sampler2D in_depth; 
layout(set = 0, binding = 1) uniform sampler2D in_norm_rough;
layout(set = 0, binding = 2) writeonly uniform image2D out_diffuse;
layout(set = 0, binding = 3) writeonly uniform image2D out_specular;

layout(std430, set = 0, binding = 4) buffer DirectionalLightData 
{
    int n;
    DirectionalLight directional_lights[10];
}directional_light_data;

layout(std430, set = 0, binding = 5) buffer PointLightData 
{
    PointLight point_lights[10];
}point_light_data;

layout(std430, set = 0, binding = 6) buffer SpotLightData 
{
    SpotLight spot_lights[10];
}spot_light_data;

layout(std430, set = 0, binding = 7) buffer AreaLightData
{
    AreaLight area_lights[10];
}area_light_data;

layout(std430, set = 0, binding = 8) buffer ReflectionProbeData 
{
    ReflectionProbe probes[10];
}reflection_probe_data;

/*layout(constant_id = 0) const int tiles_count_x = 64;
layout(constant_id = 1) const int tiles_count_y = 64;*/
const int ntiles_x = 120,  ntiles_y = 68;
layout(set = 0, binding = 9) buffer TileData 
{
    Tile tiles[ntiles_x][ntiles_y];
    Item items[81600];
}tile_data;

layout(set = 0, binding = 10) buffer Res
{
    uvec2 res;
}res;

layout(set = 0, binding = 11) uniform sampler2DShadow shadow_atlas;
layout(set = 0, binding = 12) uniform sampler2D shadow_atlas_raw;
layout(set = 0, binding = 13) buffer ShadowData 
{
    mat4 shadow_transforms[16];
    vec4 shadow_maps[16];
} shadow_data;

layout(push_constant) uniform ProjectionData
{
    mat4 mv;
    mat4 p;
    //vec3 view_pos;
} projection_data;

#include<common.h>

#include<pbr.h>

void pbr_light(out vec3 specular, out vec3 diffuse, vec3 point_to_light, vec3 color, float attenuation, vec3 norm, vec3 point, float roughness)
{
    //diffuse = vec3(sin(3.1415*point.yyy));return;
    vec3 vdir = normalize(get_view_pos() - point);
    vec3 halfway = normalize(point_to_light + vdir); 
    float cos_theta = max(dot(norm, point_to_light), 0.0);

    vec3 radiance = color.xyz * attenuation;
    float F0 = 0.04;
    float fresnel = fresnel_schlick(max(dot(halfway, vdir), 0.0), F0);
    
    float ndf = normal_distribution_ggx(norm, halfway, roughness);
    float g = geometry_smith(norm, vdir, point_to_light, k_direct(roughness));
    //cook-torrance
    float specular_factor = ndf * g * fresnel / max(4.0 * max(dot(norm, vdir) * dot(norm, point_to_light), 0.0), 0.001);

    float ks = fresnel,
          kd = 1.0-ks;

    diffuse = (kd / PI) * radiance * cos_theta;
    specular = specular_factor * radiance * cos_theta;
}

/*****************************************light processing functions***************************************************/

#define CUTOFF_MARGIN 5

float cutoff_function(float dist, float influence_radius)
{
    return square(clamp((CUTOFF_MARGIN-CUTOFF_MARGIN*dist/influence_radius), 0.0, 1.0));
}

void process_directional(out vec3 specular, out vec3 diffuse, DirectionalLight light, vec3 norm, vec3 point, float roughness)
{
    vec3 point_to_light = normalize(-light.direction.xyz);
    vec3 vdir = normalize(get_view_pos() - point);
    vec3 halfway = normalize(vdir+norm); 
    point_to_light = normalize(point_to_light);
    pbr_light(specular, diffuse, point_to_light, light.color.xyz, 1.0, norm, point, roughness);
}

void process_point(out vec3 specular, out vec3 diffuse, PointLight light, vec3 norm, vec3 point, float roughness)
{
    vec3 point_to_light = light.position.xyz-point;
    float dist = length(point_to_light);
    vec3 vdir = normalize(get_view_pos() - point);
    vec3 halfway = normalize(vdir+norm); 
    point_to_light = normalize(point_to_light);
    float attenuation = cutoff_function(dist, light.influence_radius) / square(dist);
    pbr_light(specular, diffuse, point_to_light, light.color.xyz, attenuation, norm, point, roughness);
}

void process_spot(out vec3 specular, out vec3 diffuse, SpotLight light, vec3 norm, vec3 point, float roughness)
{
    vec3 point_to_light = light.position.xyz-point;
    float dist = length(point_to_light);
    float attenuation = light.size*light.size/square(tan(light.cone_angle)*dist);
    vec3 vdir = normalize(get_view_pos() - point);
    vec3 halfway = normalize(vdir+norm); 
    point_to_light = normalize(point_to_light);
    attenuation *= clamp((dot(-light.direction, normalize(point_to_light))-cos(light.cone_angle))/cos(light.cone_angle*0.1), 0.0, 1.0);
    pbr_light(specular, diffuse, point_to_light, light.color.xyz, attenuation, norm, point, roughness);
}

void process_area(out vec3 specular, out vec3 diffuse, AreaLight light, vec3 norm, vec3 point, float roughness)
{
    specular = vec3(1.0);
    diffuse = vec3(1.0);
}
void process_probe(out vec3 specular, out vec3 diffuse, ReflectionProbe light, vec3 norm, vec3 point, float roughness)
{
    specular = vec3(1.0);
    diffuse = vec3(1.0);
}
/*****************************************light processing functions***************************************************/

/**********************************************shadow mapping**********************************************************/
#define CASCADE_MARGIN 0.01
#define PCF_SAMPLES 32

vec3 get_shadow_coords(vec4 wp_coord, mat4 mat, vec4 shadow_map, vec3 norm, vec3 ldir)
{
    //vec3 bias = ldir * max(0.005 * (1 - dot(norm, ldir)), shadow_map.w);
    //vec4 r = mat * (wp_coord + vec4(bias, 0.0));
    vec4 r = mat * wp_coord;
    r.xyz /= r.w;
    r.xy = (r.xy+1)/2.0;
    r.z -=  max(0.005 * (1 - dot(norm, ldir)), shadow_map.w);
    r.xy *= shadow_map.z;
    r.xy += shadow_map.xy;
    return clamp(r.xyz, vec3(0.0), vec3(1.0));
}

vec3 get_shadow_uv(vec4 wp_coord, uint shadow_index) 
{
    mat4 mat = shadow_data.shadow_transforms[shadow_index];
    vec4 shadow_map = shadow_data.shadow_maps[shadow_index];
    vec4 r = mat * wp_coord;
    r.xyz /= r.w;
    r.xy = (r.xy + 1) / 2.0;
    r.z -=  shadow_map.w;
    return r.xyz;
}

vec2 get_atlas_coord(vec2 uv, uint shadow_index)
{
    vec4 shadow_map = shadow_data.shadow_maps[shadow_index];
    uv *= shadow_map.z;
    uv += shadow_map.xy;
    return uv;
}

bool check_shadow_range(vec4 wp_coord, mat4 mat) 
{
    vec4 r = mat * wp_coord;
    r.xyz /= r.w;

    return r.x < (1.0-CASCADE_MARGIN) && r.x > -(1.0-CASCADE_MARGIN) &&
           r.y < (1.0-CASCADE_MARGIN) && r.y > -(1.0-CASCADE_MARGIN) &&
           r.z < (1.0-CASCADE_MARGIN) && r.z > -(1.0-CASCADE_MARGIN);
}

uint find_shadow_cascade(vec3 point, uint shadow_index, uint n_cascade) 
{
    for (int cascade = 1; 

        cascade <= n_cascade && 
                !check_shadow_range(vec4(point, 1.0), shadow_data.shadow_transforms[shadow_index]); 

        cascade++, shadow_index++
    );

    return shadow_index;
}

void find_blocker(vec3 uv, float light_angle, out float avg_depth, out uint n_blockers, uint shadow_index)
{
    float search_width = light_angle * 0.2;

    float blocker_sum = 0.0;
    n_blockers = 0;

    for(int i = 0; i < 16; i++) 
    {
        vec2 coords = uv.xy + poisson_disk_border[i] * search_width;
        vec2 atlas_coords = get_atlas_coord(coords, shadow_index);
        float shadow_map_depth = texture(shadow_atlas_raw, atlas_coords).r;

        if(shadow_map_depth < uv.z)
        {
            blocker_sum += shadow_map_depth;
            n_blockers++;
        }
    }
    avg_depth = blocker_sum / n_blockers;
}

float pcf(vec3 uv, float radius, uint shadow_index) 
{
    float shadow_fact = 0.0;

    for(int i = 0;i < PCF_SAMPLES; i++)
    {
        vec3 atlas_coords = vec3(get_atlas_coord(uv.xy + radius * poisson_disk[i], shadow_index), uv.z);
        shadow_fact += texture(shadow_atlas, 
                               atlas_coords);
    }
    return shadow_fact / PCF_SAMPLES;
}

float directional_shadows(vec3 point, uint light_index) 
{
    float light_angle = directional_light_data.directional_lights[light_index].angle;
    uint shadow_index = directional_light_data.directional_lights[light_index].shadows;
    if(shadow_index == 0xffffffff) {
        return 1.0;
    }
    uint n_cascades = directional_light_data.directional_lights[light_index].n_cascades;
    shadow_index = find_shadow_cascade(point, shadow_index, n_cascades);
    vec3 uv = get_shadow_uv(vec4(point, 1.0), shadow_index);

    float avg_depth = 0;
    uint n_blockers = 0;

    find_blocker(uv, light_angle, avg_depth, n_blockers, shadow_index);

    if(n_blockers < 1)
        return 1.0;

    float radius = (uv.z - avg_depth) * light_angle;
    return pcf(uv, radius, shadow_index);
}
/**********************************************shadow mapping**********************************************************/

void main()
{
    if(gl_GlobalInvocationID.x > res.res.x || gl_GlobalInvocationID.y > res.res.y)
        return;

    vec2 uv = vec2(gl_GlobalInvocationID.xy) / vec2(res.res);

    ivec2 ntiles = ivec2(ceil(res.res.x/16), ceil(res.res.y/16));
    Tile tile = tile_data.tiles[int(uv.x*ntiles.x)][int(uv.y*ntiles.y)];
    int point_count, spot_count, area_count, probe_count;
    point_count = tile.point_spot_area_probe_count & 0xff;
    spot_count = tile.point_spot_area_probe_count >> 8 & 0xff;
    area_count = tile.point_spot_area_probe_count >> 16 & 0xff;
    probe_count = tile.point_spot_area_probe_count >> 24 & 0xff;

    vec3 norm = texelFetch(in_norm_rough, ivec2(gl_GlobalInvocationID.xy), 0).xyz*2.0-1.0;
    float roughness = texture(in_norm_rough, uv).w/4.0; 
    float depth = texelFetch(in_depth, ivec2(gl_GlobalInvocationID.xy), 0).x;
    vec3 point = world_space_location(uv, depth);

    if(depth >= 1.0) return;

    /****************light processing****************/
    vec3 specular = vec3(0.0), diffuse = vec3(0.0);

    //processes directional lights
    for(int i = 0; i < directional_light_data.n; i++)
    {
        float shadow_fact = directional_shadows(point, i);
        vec3 tspec, tdiff;
        process_directional(tspec, tdiff, directional_light_data.directional_lights[i], norm, point, roughness);
        specular += tspec * shadow_fact;
        diffuse += tdiff * shadow_fact;
    }
    
    //processes point lights
    for(int i = 0;i < point_count;i++)
    {
        int idx = tile_data.items[i+tile.offset].point_spot_offset >> 16 & 0xffff;
        vec3 tspec, tdiff;
        process_point(tspec, tdiff, point_light_data.point_lights[idx], norm, point, roughness);
        specular += tspec;
        diffuse += tdiff;
    }

    //processes spot lights
    for(int i = 0;i < spot_count;i++)
    {
        int idx = tile_data.items[i+tile.offset].point_spot_offset & 0xffff;
        float shadow_fact = 0.0;
        uint shadows_index = spot_light_data.spot_lights[idx].shadows;
        if(shadows_index != 0xffffffff)
        {
            for(int i = 0;i < 32; i++)
            {
                shadow_fact += texture(shadow_atlas, 
                                       get_shadow_coords(vec4(point, 1.0), 
                                                         shadow_data.shadow_transforms[shadows_index], 
                                                         shadow_data.shadow_maps[shadows_index] ,
                                                         norm, 
                                                         normalize(spot_light_data.spot_lights[idx].position-point))
                                       + vec3(poisson_disk[i]/3000.0, 0.0) 
                                       );
            }
            shadow_fact /= 32;
        }
        else
        {
            shadow_fact = 1.0;
        }
        vec3 tspec, tdiff;
        process_spot(tspec, tdiff, spot_light_data.spot_lights[idx], norm, point, roughness);
        specular += tspec * shadow_fact;
        diffuse += tdiff * shadow_fact;
    }

    //processes area lights
    for(int i = 0;i < area_count;i++)
    {
        int idx = i+tile.offset;
        vec3 tspec, tdiff;
        process_area(tspec, tdiff, area_light_data.area_lights[idx], norm, point, roughness);
        specular += tspec;
        diffuse += tdiff;
    }

    //processes probes
    for(int i = 0;i < probe_count;i++)
    {
        int idx = i+tile.offset;
        vec3 tspec, tdiff;
        //process_probe(tspec, tdiff, reflection_probe_data.probes[idx], norm, point, roughness);
        specular += tspec;
        diffuse += tdiff;
    }

    imageStore(out_diffuse,  ivec2(gl_GlobalInvocationID.xy), diffuse.xyzz);
    imageStore(out_specular, ivec2(gl_GlobalInvocationID.xy), specular.xyzz);
}