<|start_filename|>Resource/Shaders/scene_constants_main.glsl<|end_filename|>
#include "scene_constants.glsl"
#include "quad.glsl"


#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

void main()
{
    fs_output = vec4(1.0, 0.0, 0.0, 1.0);
}
#endif

<|start_filename|>Resource/Shaders/debug_bone.glsl<|end_filename|>
#define SKELETAL 0

#include "scene_constants.glsl"

uniform mat4 matrix1;
uniform mat4 matrix2;

struct VERTEX_OUTPUT
{
    vec3 world_position;
#if SKELETAL
    vec4 bone_indicies;
    vec4 bone_weights;
#endif
};

//----------- VERTEX_SHADER ---------------//

#ifdef VERTEX_SHADER
    layout (location = 0) in vec3 vs_in_position;
    layout (location = 1) in vec4 vs_in_color;
    layout (location = 2) in vec3 vs_in_normal;
    layout (location = 3) in vec3 vs_in_tangent;
    layout (location = 4) in vec2 vs_in_tex_coord;
#if SKELETAL
    layout (location = 5) in vec4 vs_in_bone_indicies;
    layout (location = 6) in vec4 vs_in_bone_weights;
#endif

layout (location = 0) out VERTEX_OUTPUT vs_output;

void main() {
    vec3 local_pos = vs_in_position.xyz * vec3(0.1, 0.1, 0.1);
    if(vs_in_position.y > 0.0)
    {
        vs_output.world_position = (matrix2 * vec4(local_pos, 1.0)).xyz;
    }
    else
    {
        vs_output.world_position = (matrix1 * vec4(local_pos, 1.0)).xyz;
    }

#if SKELETAL
    vs_output.bone_indicies = vs_in_bone_indicies;
    vs_output.bone_weights = vs_in_bone_weights;
#endif

    gl_Position = PROJECTION * VIEW * vec4(vs_output.world_position, 1.0);
}
#endif


// FRAGMENT SHADER

#ifdef FRAGMENT_SHADER
    layout (location = 0) in vec3 vs_out_world_position;
#if SKELETAL
    layout (location = 1) in vec4 vs_out_bone_indicies;
    layout (location = 2) in vec4 vs_out_bone_weights;
#endif
layout (location = 0) out vec4 fs_output;

void main() {
    fs_output = vec4(1.0, 1.0, 1.0, 1.0);
}
#endif

<|start_filename|>Resource/Shaders/ssao.glsl<|end_filename|>
#include "utility.glsl"
#include "PCFKernels.glsl"
#include "scene_constants.glsl"
#include "quad.glsl"

const int kernel_size = 32;
uniform vec3 kernel[kernel_size];

uniform float texture_lod;
uniform vec2 texture_size;
uniform vec2 radius_min_max;

uniform sampler2D texture_noise;
uniform sampler2D texture_normal;
uniform sampler2D texture_linear_depth;

#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

void main() {
    vec2 tex_coord = vs_output.tex_coord.xy;
    vec2 texel_size = 1.0 / texture_size;

    float linear_depth = texture2DLod(texture_linear_depth, tex_coord, texture_lod).x;

    /*if(linear_depth >= NEAR_FAR.y)
    {
        fs_output = vec4(1.0);
        return;
    }*/

    vec4 relative_pos = linear_depth_to_relative_world(tex_coord, linear_depth);
    vec3 normal = texture2D(texture_normal, tex_coord).xyz * 2.0 - 1.0;
    vec2 noise_size = textureSize(texture_noise, 0);

    vec3 randomVec = texture2D(texture_noise, tex_coord * texture_size / noise_size).xyz;

    vec3 tangent   = normalize(randomVec - normal * dot(randomVec, normal));
    vec3 bitangent = cross(normal, tangent);
    mat3 tbn = mat3(tangent, normal, bitangent);

    float occlusion = 0.0;
    int sample_count = min(16, kernel_size);
    for (int i = 0; i < sample_count; ++i)
    {
        vec3 pos = (tbn * kernel[i]) * radius_min_max.y + relative_pos.xyz;

        // project sample position:
        vec4 offset = vec4(pos, 1.0);
        offset = PROJECTION * VIEW_ORIGIN * offset;
        offset.xy /= offset.w;
        offset.xy = offset.xy * 0.5 + 0.5;

        if(offset.x < 0.0 || offset.x > 1.0 || offset.y < 0.0 || offset.y > 1.0)
        {
            continue;
        }

        float sampleDepth = texture2DLod(texture_linear_depth, offset.xy, texture_lod).x;

        if(offset.w < sampleDepth) continue;

        sampleDepth = linear_depth - sampleDepth;
        if(radius_min_max.x <= sampleDepth && sampleDepth <= radius_min_max.y)
        {
            float weight = clamp(1.0 - (sampleDepth - radius_min_max.x) / (radius_min_max.y - radius_min_max.x), 0.0, 1.0);
            occlusion += pow(weight, 3.0);
        }


    }

    occlusion = clamp(1.0 - occlusion / float(sample_count), 0.0, 1.0);
    occlusion *= occlusion;
    fs_output.xyz = vec3(occlusion);
    fs_output.w = 1.0;
}
#endif // FRAGMENT_SHADER

<|start_filename|>Resource/Shaders/precomputed_atmosphere/atmosphere.glsl<|end_filename|>
#include "blending.glsl"
#include "utility.glsl"
#include "precomputed_atmosphere/atmosphere_predefine.glsl"
#include "precomputed_atmosphere/atmosphere_vs.glsl"

uniform sampler2D texture_shadow;
uniform sampler2D texture_linear_depth;
uniform sampler2D texture_normal;

uniform sampler3D texture_cloud;
uniform sampler3D texture_noise;

uniform float cloud_exposure;
uniform float cloud_altitude;
uniform float cloud_height;
uniform float cloud_speed;
uniform float cloud_absorption;

uniform float cloud_tiling;
uniform float cloud_contrast;
uniform float cloud_coverage;

uniform float noise_tiling;
uniform float noise_contrast;
uniform float noise_coverage;

#ifdef FRAGMENT_SHADER
in vec3 eye_ray;
in vec2 uv;
layout(location = 0) out vec4 out_color;
layout(location = 1) out vec4 out_inscatter;


float get_cloud_density(vec3 cloud_scale, vec3 noise_scale, vec3 uvw, vec3 speed, float weight)
{
    uvw.xy += CAMERA_POSITION.xz;

    float cloud = texture3D(texture_cloud, uvw * cloud_scale + speed * cloud_tiling / noise_tiling).x;
    cloud = saturate(Contrast((cloud - 1.0 + cloud_coverage), cloud_contrast));

    float noise = texture3D(texture_noise, uvw * noise_scale + speed * 0.3).x;
    noise = saturate(Contrast((noise - 1.0 + noise_coverage) * weight, noise_contrast));

    // Remap is very important!!
    return saturate(Remap(noise, 1.0 - cloud, 1.0, 0.0, 1.0));
}

void main()
{
    out_color = vec4(0.0, 0.0, 0.0, 1.0);

    const float min_dist = 1000.0;
    const float far_dist = NEAR_FAR.y * 4.0;

    vec3 camera = vec3(0.0, max(10.0, CAMERA_POSITION.y), 0.0) * atmosphere_ratio;

    float world_pos_y = max(0.0, CAMERA_POSITION.y);

    vec3 sun_direction = LIGHT_DIRECTION.xyz;
    vec3 eye_direction = normalize(eye_ray);
    vec3 screen_center_ray = -vec3(VIEW_ORIGIN[0].z, VIEW_ORIGIN[1].z, VIEW_ORIGIN[2].z);
    float VdotL = dot(eye_direction, sun_direction);

    float scene_linear_depth = texture2DLod(texture_linear_depth, uv, 0.0).x;
    float scene_dist = clamp(scene_linear_depth / dot(screen_center_ray, eye_direction), 0.0, NEAR_FAR.y);
    float scene_shadow_length = GetSceneShadowLength(scene_dist, eye_direction, texture_shadow);

    // Sky
    vec3 transmittance;
    vec3 radiance = GetSkyRadiance(ATMOSPHERE, camera - earth_center, eye_direction, scene_shadow_length, sun_direction, transmittance);

    // Sun
    vec3 sun_disc = vec3(0.0);
    vec3 solar_radiance = GetSolarRadiance(ATMOSPHERE);
    const float sun_absorption = 0.9;
    const float sun_intensity = 1.0;
    if (!render_light_probe_mode && sun_size.y < VdotL)
    {
        sun_disc = transmittance * solar_radiance * pow(clamp((VdotL - sun_size.y) / (1.0 - sun_size.y), 0.0, 1.0), 2.0);
        sun_disc *= LIGHT_COLOR.xyz * sun_intensity;
        radiance += sun_disc * sun_absorption;
    }

    // Cloud
    vec4 cloud = vec4(0.0);
    vec3 earth_center_pos = earth_center / atmosphere_ratio;

    // distance from earch center
    const float cloud_bottom_dist = cloud_altitude - earth_center_pos.y;
    const float cloud_top_dist = cloud_bottom_dist + cloud_height;
    float altitude_diff = cloud_altitude - world_pos_y;
    const bool in_the_cloud = -cloud_height < altitude_diff && altitude_diff < 0.0;
    bool above_the_cloud = false;
    bool render_cloud = true;

    // relative ray march start pos from the camera
    vec3 ray_start_pos;
    float hit_dist;

    if(in_the_cloud)
    {
        // be in clouds
        ray_start_pos = vec3(0.0, 0.0, 0.0);
        hit_dist = 0.0;
    }
    else
    {
        // https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
        vec3 to_origin = vec3(0.0, world_pos_y, 0.0) - earth_center_pos;
        float c = pow(dot(eye_direction, to_origin), 2.0) - dot(to_origin, to_origin);

        if(cloud_altitude < world_pos_y)
        {
            // above the sky
            if(eye_direction.y < 0.0)
            {
                // look down
                c = -sqrt(c + cloud_top_dist * cloud_top_dist);
                above_the_cloud = true;
            }
            else
            {
                // look up, discard
                render_cloud = false;
            }
        }
        else
        {
            // under the sky
            float r = cloud_altitude - earth_center_pos.y;
            c = sqrt(c + cloud_bottom_dist * cloud_bottom_dist);
        }

        hit_dist = -dot(eye_direction, to_origin) + c;
        ray_start_pos = eye_direction * hit_dist;
    }

    // apply altitude of camera
    ray_start_pos.y += world_pos_y;

    // vec3 smooth_N = normalize(ray_start_pos.xyz - earth_center);
    vec3 N = normalize(ray_start_pos.xyz);

    float altitude_ratio = saturate(world_pos_y / (cloud_altitude + cloud_height));
    float atmosphere_lighting = max(0.2, pow(saturate(dot(N, sun_direction) * 0.5 + 0.5), 1.0));

    // Cloud
    if(render_cloud)
    {
        vec3 cloud_inscatter = vec3(0.0);
        vec3 cloud_sun_irradiance = vec3(0.0);
        vec3 cloud_sky_irradiance = vec3(0.0);

        // NOTE : 0.1 is more colorful scattering cloud.
        float dist_to_point = hit_dist * (above_the_cloud ? 1.0 : 0.01);

        GetCloudRadiance(ATMOSPHERE, dist_to_point, eye_direction, scene_shadow_length,
            cloud_sun_irradiance, cloud_sky_irradiance, cloud_inscatter);

        if(in_the_cloud || above_the_cloud)
        {
            cloud_inscatter = vec3(0.0);
        }

        vec3 light_color = cloud_sun_irradiance + cloud_sky_irradiance;
        light_color *= cloud_exposure * LIGHT_COLOR.xyz * atmosphere_lighting;

        if(0.0 <= hit_dist && hit_dist < far_dist)
        {
            const vec3 speed = vec3(cloud_speed, cloud_speed, 0.0) * TIME;

            vec3 cloud_scale = textureSize(texture_cloud, 0);
            cloud_scale = max(cloud_scale.x, max(cloud_scale.y, cloud_scale.z)) / cloud_scale;
            cloud_scale *= cloud_tiling;

            vec3 noise_scale = textureSize(texture_noise, 0);
            noise_scale = max(noise_scale.x, max(noise_scale.y, noise_scale.z)) / noise_scale;
            noise_scale *= noise_tiling;

            // float view_angle = in_the_cloud ? 0.0 : pow(abs(eye_direction.y), 0.2);
            float march_count = 128.0;
            float light_march_count = 32.0;
            float march_step = cloud_height / march_count;
            float cloud_march_step = march_step;
            float increase_march_step = march_step * 0.03;

            for(int i=0; i<int(march_count); ++i)
            {
                vec3 ray_pos;
                ray_pos = ray_start_pos.xyz + eye_direction.xyz * float(i) * cloud_march_step;

                // fade top and bottom
                float relative_altitude = length(ray_pos - earth_center_pos.xyz) - cloud_bottom_dist;

                if(cloud_height < relative_altitude || relative_altitude < 0.0)
                {
                    continue;
                }

                float fade = saturate(relative_altitude / cloud_height);
                fade = 1.0 - pow(abs(fade * 2.0 - 1.0), 3.0);

                float cloud_density = get_cloud_density(cloud_scale, noise_scale, ray_pos.xzy, speed, fade);

                if(cloud_density <= 0.01)
                {
                    // increase march step
                    cloud_march_step += increase_march_step;
                    continue;
                }
                else
                {
                    // NOTE : decrease is more detail, but not natural.
                    // cloud_march_step = max(march_step, cloud_march_step - increase_march_step * 0.5);
                }

                float light_intensity = 1.0;

                for(int j=0; j<light_march_count; ++j)
                {
                    vec3 light_pos = ray_pos + sun_direction * float(light_march_count - j) * march_step;
                    relative_altitude = length(light_pos.xyz - earth_center_pos.xyz) - cloud_bottom_dist;

                    if(cloud_height < relative_altitude || relative_altitude < 0.0)
                    {
                        continue;
                    }

                    fade = 1.0 - pow(abs(saturate(relative_altitude / cloud_height) * 2.0 - 1.0), 3.0);

                    float light_density = get_cloud_density(cloud_scale, noise_scale, light_pos.xzy, speed, fade);

                    if(light_density <= 0.01)
                    {
                        continue;
                    }

                    light_intensity *= (1.0 - light_density * cloud_absorption);

                    if(light_intensity <= 0.01)
                    {
                        light_intensity = 0.0;
                        break;
                    }
                }

                cloud.xyz += cloud_density * light_color * light_intensity;

                cloud.w = clamp(cloud.w + cloud_density * cloud_absorption, 0.0, 1.0);

                if(1.0 <= cloud.w)
                {
                    break;
                }
            }

            float horizontal_line = pow(saturate(((N.y * 0.5 + 0.5) - 0.49) * 30.0), 0.1);
            cloud.w *= horizontal_line;
        }

        out_color.xyz += max(vec3(0.0), mix(radiance, cloud.xyz, cloud.w));
        out_color.xyz += sun_disc * saturate(1.0 - cloud.w);
        out_color.w = clamp(cloud.w, 0.0, 1.0);
    }


    vec3 far_point = camera + eye_direction.xyz * max(NEAR_FAR.x, scene_dist) * atmosphere_ratio;
    vec3 scene_transmittance;
    vec3 scene_inscatter = GetSkyRadianceToPoint(
        ATMOSPHERE, camera - earth_center, far_point.xyz - earth_center, scene_shadow_length, LIGHT_DIRECTION.xyz, scene_transmittance);
    scene_inscatter = max(vec3(0.0), scene_inscatter);

    if(render_light_probe_mode)
    {
        out_color.xyz += scene_inscatter;
    }

    if(render_cloud)
    {
        out_inscatter.xyz += scene_inscatter;
    }
}
#endif


<|start_filename|>Resource/Shaders/motion_blur.glsl<|end_filename|>


#include "quad.glsl"

uniform float motion_blur_scale;
uniform sampler2D texture_diffuse;
uniform sampler2D texture_velocity;

const vec2 gaussFilter[7] =
{
	vec2(-3.0,	0.015625),
	vec2(-2.0,	0.09375),
	vec2(-1.0,	0.234375),
	vec2(0.0,	0.3125),
	vec2(1.0,	0.234375),
	vec2(2.0,	0.09375),
	vec2(3.0,	0.015625)
};

#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

void main() {
    vec2 tex_coord = vs_output.tex_coord.xy;
    vec2 velocity = texture2D(texture_velocity, tex_coord).xy * motion_blur_scale;

    float weights = 0.0;

    for( int i = 0; i < 7; i++ )
	{
	    vec2 uv = vec2(tex_coord.x + gaussFilter[i].x * velocity.x, tex_coord.y + gaussFilter[i].x * velocity.y);
		fs_output += texture2D(texture_diffuse, uv) * gaussFilter[i].yyyy;
		weights += gaussFilter[i].y;
	}

	fs_output /= weights;
	fs_output.w = 1.0;
}
#endif // FRAGMENT_SHADER

<|start_filename|>Resource/Shaders/selected_object_composite.glsl<|end_filename|>
#include "utility.glsl"
#include "quad.glsl"

uniform sampler2D texture_mask;

#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

void main() {
    fs_output = vec4(0.0, 0.0, 0.0, 1.0);
    vec2 tex_coord = vs_output.tex_coord.xy;
    vec2 inv_texture_size = 1.0 / textureSize(texture_mask, 0);
    float center_mask = texture2D(texture_mask, tex_coord).x;
    float mask = 0.0;

    for(int y=-2; y<=2; ++y)
    {
        for(int x=-2; x<=2; ++x)
        {
            if(x == 0 && y == 0)
            {
                continue;
            }

            mask = max(mask, texture2D(texture_mask, tex_coord + vec2(x, y) * inv_texture_size).x);
        }
    }

    mask = saturate(mask - center_mask) + center_mask * 0.5;
    fs_output = vec4(0.5, 0.5, 1.0, 1.0) * mask;
}
#endif // FRAGMENT_SHADER

<|start_filename|>Resource/Shaders/render_texture.glsl<|end_filename|>
#include "utility.glsl"
#include "scene_constants.glsl"
#include "quad.glsl"

const int GL_TEXTURE_2D = 3553;
const int GL_TEXTURE_2D_ARRAY = 35866;
const int GL_TEXTURE_3D = 32879;
const int GL_TEXTURE_CUBE_MAP = 34067;

uniform sampler2D texture_source_2d;
uniform sampler2DArray texture_source_2d_array;
uniform sampler3D texture_source_3d;
uniform samplerCube texture_source_cube;

uniform bool debug_absolute;
uniform float debug_mipmap;
uniform float debug_intensity_min;
uniform float debug_intensity_max;
uniform int debug_target;

#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

vec4 get_texture_2d_array(sampler2DArray texture_source)
{
    vec3 texture_size = textureSize(texture_source, 0);
    float width = ceil(sqrt(texture_size.z));
    float height = ceil(texture_size.z / width);
    float depth = floor(vs_output.tex_coord.x * width) + floor((1.0 - vs_output.tex_coord.y) * height) * width;
    if(texture_size.z <= depth)
    {
        return vec4(0.0, 0.0, 0.0, 0.0);
    }
    vec3 texcoord = vec3(fract(vs_output.tex_coord.x * width), fract(vs_output.tex_coord.y * height), depth);
    return texture2DArray(texture_source, texcoord, debug_mipmap);
}

vec4 get_texture_3d(sampler3D texture_source)
{
    vec3 texture_size = textureSize(texture_source, 0);
    float width = ceil(sqrt(texture_size.z));
    float height = ceil(texture_size.z / width);
    float depth = floor(vs_output.tex_coord.x * width) + floor((1.0 - vs_output.tex_coord.y) * height) * width;
    if(texture_size.z <= depth)
    {
        return vec4(0.0, 0.0, 0.0, 0.0);
    }
    depth /= texture_size.z;
    vec3 texcoord = vec3(fract(vs_output.tex_coord.x * width), fract(vs_output.tex_coord.y * height), depth);
    return texture3D(texture_source, texcoord, debug_mipmap);
}

void main()
{
    if(GL_TEXTURE_2D == debug_target)
    {
        vec2 texcoord = vs_output.tex_coord.xy;
        fs_output = texture2D(texture_source_2d, texcoord, debug_mipmap);
    }
    else if(GL_TEXTURE_2D_ARRAY == debug_target)
    {
        fs_output = get_texture_2d_array(texture_source_2d_array);
    }
    else if(GL_TEXTURE_3D == debug_target)
    {
        fs_output = get_texture_3d(texture_source_3d);
    }
    else if(GL_TEXTURE_CUBE_MAP == debug_target)
    {
        vec4 position = vec4(vs_output.tex_coord.xy * 2.0 - 1.0 + JITTER_OFFSET, -1.0, 1.0);
        position = INV_VIEW_ORIGIN * INV_PROJECTION * position;
        position.xyz /= position.w;
        position.y = -position.y;
        fs_output = textureCube(texture_source_cube, normalize(position.xyz), debug_mipmap);
    }

    if(debug_absolute)
    {
        fs_output.xyz = abs(fs_output.xyz);
    }

    fs_output.xyz = clamp((fs_output.xyz - debug_intensity_min) / (debug_intensity_max - debug_intensity_min), 0.0, 1.0);
    fs_output.w = 1.0;
}
#endif // FRAGMENT_SHADER

<|start_filename|>Resource/Shaders/shadowmap.glsl<|end_filename|>


#include "utility.glsl"
#include "scene_constants.glsl"
#include "default_vs.glsl"

//-------------- MATERIAL_COMPONENTS ---------------//

#include "default_material.glsl"

//----------- FRAGMENT_SHADER ---------------//


#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

void main() {
    vec4 base_color = get_base_color(vs_output.tex_coord.xy);

    if(base_color.a < 0.333f)
    {
        discard;
    }

    fs_output = vec4(vec3(gl_FragCoord.z, 0.0, 0.0), 1.0);
}
#endif

<|start_filename|>Resource/Shaders/compute_focus_distance.glsl<|end_filename|>
uniform float focus_sensitivity;

#ifdef COMPUTE_SHADER
layout(local_size_x = 1, local_size_y = 1) in;

layout(r32f, binding=0) uniform image2D img_input;
layout(r32f, binding=1) uniform image2D img_output;

void main()
{
    ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
    ivec2 center_pos = imageSize(img_input) / 2;
    vec4 curr_depth = imageLoad(img_input, center_pos);
    vec4 prev_depth = imageLoad(img_output, pixel_coords);
    curr_depth = mix(prev_depth, curr_depth, focus_sensitivity);
    imageStore(img_output, pixel_coords, curr_depth);
}
#endif

<|start_filename|>Resource/Shaders/default_vs.glsl<|end_filename|>
#define SKELETAL 0

#include "scene_constants.glsl"
#include "default_material.glsl"

uniform bool is_instancing;
uniform mat4 model;

#if 1 == SKELETAL
uniform mat4 bone_matrices[MAX_BONES];
uniform mat4 prev_bone_matrices[MAX_BONES];
#endif

struct VERTEX_OUTPUT
{
    vec3 world_position;
    vec3 vertex_normal;
    vec4 vertex_color;
    vec2 tex_coord;
    mat4 tangent_to_world;
    vec4 projection_pos;
    vec4 prev_projection_pos;
};

//----------- VERTEX_SHADER ---------------//

#ifdef VERTEX_SHADER
layout (location = 0) in vec3 vs_in_position;
layout (location = 1) in vec4 vs_in_color;
layout (location = 2) in vec3 vs_in_normal;
layout (location = 3) in vec3 vs_in_tangent;
layout (location = 4) in vec2 vs_in_tex_coord;
#if 1 == SKELETAL
layout (location = 5) in vec4 vs_in_bone_indicies;
layout (location = 6) in vec4 vs_in_bone_weights;
#endif
layout (location = 7) in mat4 vs_in_isntance_matrix;

layout (location = 0) out VERTEX_OUTPUT vs_output;

void main() {
    vec4 position = vec4(0.0, 0.0, 0.0, 0.0);
    vec4 prev_position = vec4(0.0, 0.0, 0.0, 0.0);
    vec3 vertex_normal = vec3(0.0, 0.0, 0.0);
    vec3 vertex_tangent = vec3(0.0, 0.0, 0.0);

#if 1 == SKELETAL
    for(int i=0; i<MAX_BONES_PER_VERTEX; ++i)
    {
        prev_position += (prev_bone_matrices[int(vs_in_bone_indicies[i])] * vec4(vs_in_position, 1.0)) * vs_in_bone_weights[i];
        position += (bone_matrices[int(vs_in_bone_indicies[i])] * vec4(vs_in_position, 1.0)) * vs_in_bone_weights[i];
        vertex_normal += (bone_matrices[int(vs_in_bone_indicies[i])] * vec4(vs_in_normal, 0.0)).xyz * vs_in_bone_weights[i];
        vertex_tangent += (bone_matrices[int(vs_in_bone_indicies[i])] * vec4(vs_in_tangent, 0.0)).xyz * vs_in_bone_weights[i];
    }
    position /= position.w;
    prev_position /= prev_position.w;
#else
    position = vec4(vs_in_position, 1.0);
    vertex_normal = vs_in_normal;
    vertex_tangent = vs_in_tangent;
    prev_position = position;
#endif

    vertex_normal = normalize(vertex_normal);
    vertex_tangent = normalize(vertex_tangent);

    mat4 local_matrix = is_instancing ? model * vs_in_isntance_matrix : model;

    vs_output.world_position = (local_matrix * position).xyz;
    vs_output.vertex_normal = vertex_normal;
    vs_output.vertex_color = vs_in_color;
    vs_output.tex_coord = vs_in_tex_coord * uv_tiling;

    vec3 bitangent = cross(vertex_tangent, vertex_normal);

    // Note : Normalization is very important because tangent_to_world may have been scaled..
    vs_output.tangent_to_world = local_matrix *
        mat4(vec4(vertex_tangent, 0.0), vec4(vertex_normal, 0.0), vec4(bitangent, 0.0), vec4(0.0, 0.0, 0.0, 1.0));

    position = VIEW_PROJECTION * local_matrix * position;
    prev_position = PREV_VIEW_PROJECTION * local_matrix * prev_position;

    vs_output.projection_pos = position;
    vs_output.prev_projection_pos = prev_position;

    gl_Position = position;
}
#endif

<|start_filename|>Resource/Shaders/shading.glsl<|end_filename|>
#include "scene_constants.glsl"
#include "PCFKernels.glsl"
#include "utility.glsl"
#include "precomputed_atmosphere/atmosphere_predefine.glsl"

float get_shadow_factor(vec3 world_position, float NdotL, sampler2D texture_shadow, const bool isSimple = false)
{
    const vec2 shadow_size = textureSize(texture_shadow, 0);
    const vec2 shadow_texel_size = 1.0 / shadow_size;
    const int samnple_count = isSimple ? 1 : SHADOW_SAMPLES;
    const vec2 shadow_noise_radius = shadow_texel_size * max(1.0, log2(samnple_count));
    const vec2 shadow_uv_min = shadow_texel_size * 0.5;
    const vec2 shadow_uv_max = vec2(1.0) - shadow_texel_size * 0.5;
    vec4 shadow_proj = SHADOW_MATRIX * vec4(world_position, 1.0);
    shadow_proj.xyz /= shadow_proj.w;
    shadow_proj.xyz = shadow_proj.xyz * 0.5 + 0.5;

    if(1.0 < shadow_proj.x || shadow_proj.x < 0.0 || 1.0 < shadow_proj.y || shadow_proj.y < 0.0 || 1.0 < shadow_proj.z || shadow_proj.z < 0.0)
    {
        return 1.0;
    }

    const vec2 uv_offsets[4] = {
        vec2(0.0, 0.0),
        vec2(shadow_texel_size.x, 0.0),
        vec2(0.0, shadow_texel_size.y),
        vec2(shadow_texel_size.x, shadow_texel_size.y),
    };

    const float shadow_bias = SHADOW_BIAS * tan(acos(saturate(NdotL)));

    float total_shadow_factor = 0.0;
    for(int sample_index = 0; sample_index < samnple_count; ++sample_index)
    {
        vec2 uv = shadow_proj.xy + PoissonSamples[sample_index % PoissonSampleCount] * shadow_noise_radius;

        vec4 shadow_factors = vec4(1.0);
        for(int component_index = 0; component_index < 4; ++component_index)
        {
            const vec2 shadow_uv = clamp(uv + uv_offsets[component_index], shadow_uv_min, shadow_uv_max);
            const float shadow_depth = texture2DLod(texture_shadow, shadow_uv, 0.0).x + shadow_bias;
            if(shadow_depth <= shadow_proj.z)
            {
                shadow_factors[component_index] = saturate(exp(-SHADOW_EXP * (shadow_proj.z - shadow_depth)));
            }
        }

        const vec2 pixel_ratio = fract(uv * shadow_size);
        total_shadow_factor += mix(
            mix(shadow_factors[0], shadow_factors[1], pixel_ratio.x),
            mix(shadow_factors[2], shadow_factors[3], pixel_ratio.x), pixel_ratio.y);
    }
    return clamp(total_shadow_factor / float(samnple_count), 0.0, 1.0);
}


// https://en.wikipedia.org/wiki/Oren%E2%80%93Nayar_reflectance_model
vec3 oren_nayar(float roughness2, float NdotL, float NdotV, vec3 N, vec3 V, vec3 L)
{
    float incidentTheta = acos(NdotL);
    float outTheta = acos(NdotV);

    float A = 1.0 - 0.5 * (roughness2 / (roughness2 + 0.33));
    float B = (0.45 * roughness2) / (roughness2 + 0.09);
    float alpha = max(incidentTheta, outTheta);
    float beta  = min(incidentTheta, outTheta);

    vec3 u = normalize(V - N * NdotV);
    vec3 v = normalize(L - N * NdotL);
    float phiDiff = max(0.0, dot(u, v));

    // Exactly correct fomular.
    // return (A + (B * phiDiff * sin(alpha) * tan(beta))) * NdotL / PI;

    return vec3((A + (B * phiDiff * sin(alpha) * tan(beta))) * NdotL);
}

vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}

vec3 fresnelSchlickRoughness(float cosTheta, vec3 F0, float roughness)
{
    return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}

float D_blinn(float roughness, float NdH)
{
    float m = roughness * roughness;
    float m2 = m * m;
    float n = 2.0 / m2 - 2.0;
    return (n + 2.0) / (2.0 * PI) * pow(NdH, n);
}

float D_beckmann(float roughness, float NdH)
{
    float m = roughness * roughness;
    float m2 = m * m;
    float NdH2 = NdH * NdH;
    return exp((NdH2 - 1.0) / (m2 * NdH2)) / (PI * m2 * NdH2 * NdH2);
}

float DistributionGGX(float NdH, float roughness)
{
    float a      = roughness * roughness;
    float a2     = a * a;
    float NdH2 = NdH * NdH;

    float num   = a2;
    float denom = (NdH2 * (a2 - 1.0) + 1.0);
    denom = PI * denom * denom;

    return num / denom;
}

float GeometrySchlickGGX(float NdV, float roughness)
{
    float r = (roughness + 1.0);
    float k = (r * r) / 8.0;
    float num   = NdV;
    float denom = NdV * (1.0 - k) + k;
    return num / denom;
}

float GeometrySmith(float NdV, float NdL, float roughness)
{
    float ggx2  = GeometrySchlickGGX(NdV, roughness);
    float ggx1  = GeometrySchlickGGX(NdL, roughness);
    return ggx1 * ggx2;
}

// simple phong specular calculation with normalization
vec3 phong_specular(in float LdR, in vec3 specular, in float roughness)
{
    float spec = max(0.0, LdR);
    float k = 1.999 / (roughness * roughness);
    return min(1.0, 3.0 * 0.0398 * k) * pow(spec, min(10000.0, k)) * specular;
}

// simple blinn specular calculation with normalization
vec3 blinn_specular(in float NdH, in vec3 specular, in float roughness)
{
    float k = 1.999 / (roughness * roughness);
    return min(1.0, 3.0 * 0.0398 * k) * pow(NdH, min(10000.0, k)) * specular;
}


// cook-torrance specular calculation
vec3 cooktorrance_specular(vec3 F, float NdL, float NdV, float NdH, float roughness)
{
    //float D = D_GGX(roughness, NdH);
    //float G = G_schlick(roughness, NdV, NdL);
    //float rim = mix(1.0 - roughness * 0.9, 1.0, NdV);
    //return (1.0 / rim) * F * G * D;

    // cook-torrance brdf
    float NDF = DistributionGGX(NdH, roughness);
    float G   = GeometrySmith(NdV, NdL, roughness);
    vec3 numerator    = NDF * G * F;
    float denominator = 4.0 * NdV * NdL;
    return numerator / max(denominator, 0.001);
}

vec2 env_BRDF_pproximate(float NdV, float roughness)
{
    // see https://www.unrealengine.com/blog/physically-based-shading-on-mobile
    const vec4 c0 = vec4(-1.0, -0.0275, -0.572,  0.022);
    const vec4 c1 = vec4( 1.0,  0.0425,  1.040, -0.040);
    vec4 r = roughness * c0 + c1;
    float a004 = min(r.x * r.x, exp2(-9.28 * NdV)) * r.x + r.y;
    return vec2(-1.04, 1.04) * a004 + r.zw;
}


/* PBR reference
    - http://www.curious-creature.com/pbr_sandbox/shaders/pbr.fs
    - https://gist.github.com/galek/53557375251e1a942dfa */
vec4 surface_shading(vec4 base_color,
                    vec3 emissive_color,
                    float metallic,
                    float roughness,
                    float reflectance,
                    float ssao_factor,
                    vec4 scene_reflect_color,
                    samplerCube texture_probe,
                    sampler2D texture_shadow,
                    vec2 screen_tex_coord,
                    vec3 world_position,
                    vec3 light_color,
                    vec3 N,
                    vec3 V,
                    vec3 L,
                    float depth)
{
    // Atmosphere
    vec3 scene_in_scatter;
    vec3 scene_sun_irradiance;
    vec3 scene_sky_irradiance;
    float scene_shadow_length;
    float scene_linear_depth = depth_to_linear_depth(depth);
    GetSceneRadiance(ATMOSPHERE, scene_linear_depth, -V, N, scene_sun_irradiance, scene_sky_irradiance, scene_in_scatter);

    light_color = light_color * scene_sun_irradiance;

    // safe roughness
    roughness = clamp(roughness, 0.05, 1.0);

    // compute material reflectance
    vec3 R = reflect(-V, N);
    vec3 H = normalize(V + L);

    float NdL = max(0.0, dot(N, L));
    float NdV = max(0.001, dot(N, V));
    float NdH = max(0.001, dot(N, H));
    float HdV = max(0.001, dot(H, V));
    float LdV = max(0.001, dot(L, V));

    vec3 result = vec3(0.0, 0.0, 0.0);
    float opacity = base_color.w;

    vec3 F0 = vec3(0.04);
    F0 = mix(max(F0, reflectance), base_color.xyz, metallic);
    vec3 fresnel = fresnelSchlick(NdV, F0);

    vec3 ambient_light = vec3(0.0, 0.0, 0.0);
    vec3 diffuse_light = vec3(0.0, 0.0, 0.0);
    vec3 specular_light = vec3(0.0, 0.0, 0.0);
    vec3 shadow_factor = vec3(get_shadow_factor(world_position, dot(N, L), texture_shadow));

    // Image based lighting
    {
        const vec2 env_size = textureSize(texture_probe, 0);
        const float max_env_mipmap = 8.0; // log2(max(env_size.x, env_size.y));
        vec2 envBRDF = clamp(env_BRDF_pproximate(NdV, roughness), 0.0, 1.0);
        vec3 shValue = fresnel * envBRDF.x + envBRDF.y;

        vec3 ibl_diffuse_light = textureCubeLod(texture_probe, invert_y(N), max_env_mipmap).xyz;
        vec3 ibl_specular_light = textureCubeLod(texture_probe, invert_y(R), max_env_mipmap * roughness).xyz;

        ambient_light = normalize(mix(ibl_diffuse_light, scene_sky_irradiance, 0.5)) * length(scene_sky_irradiance);
        shadow_factor = max(shadow_factor, ambient_light);

        diffuse_light += ibl_diffuse_light * shadow_factor;
        specular_light += ibl_specular_light * shValue * shadow_factor;

         // mix scene reflection
        if(RENDER_SSR)
        {
            // NoShadow
            specular_light.xyz = mix(specular_light.xyz, scene_reflect_color.xyz * shValue * shadow_factor, scene_reflect_color.w);
        }
    }

#if TRANSPARENT_MATERIAL == 1
    float reflectivity = max(max(fresnel.r, fresnel.g), fresnel.b);
    opacity = clamp(base_color.w + base_color.w * reflectivity, 0.0, 1.0);
#endif

    // Dynamic Light
    {
        vec3 light_fresnel = fresnelSchlick(HdV, F0);

        // Directional Light
        diffuse_light += oren_nayar(roughness, NdL, NdV, N, V, L) / PI * light_color * shadow_factor;
        specular_light += cooktorrance_specular(light_fresnel, NdL, NdV, NdH, roughness) * NdL * light_color * shadow_factor;

        // Point Lights
        for(int i=0; i<MAX_POINT_LIGHTS; ++i)
        {
            if(1.0 != POINT_LIGHTS[i].render)
            {
                break;
            }

            float point_light_radius = POINT_LIGHTS[i].radius;
            vec3 point_light_dir = POINT_LIGHTS[i].pos.xyz - world_position;
            float point_light_dist = length(point_light_dir);

            if(point_light_radius < point_light_dist)
            {
                continue;
            }

            point_light_dir /= point_light_dist;

            vec3 point_light_half = normalize(V + point_light_dir);
            float point_light_attenuation = clamp(1.0 - point_light_dist / point_light_radius, 0.0, 1.0);
            point_light_attenuation *= point_light_attenuation;
            vec3 point_light_color = POINT_LIGHTS[i].color.xyz * point_light_attenuation;

            float point_light_NdL = max(0.01, dot(N, point_light_dir));
            float point_light_NdH = max(0.01, dot(N, point_light_half));

            diffuse_light += oren_nayar(roughness, point_light_NdL, NdV, N, V, point_light_dir) / PI * point_light_color;
            specular_light += cooktorrance_specular(light_fresnel, point_light_NdL, NdV, point_light_NdH, roughness) * point_light_NdL * point_light_color;
        }
    }

/*
#ifdef FRAGMENT_SHADER
    // Compute curvature
    vec3 ddx = dFdx(N);
    vec3 ddy = dFdy(N);
    vec3 xneg = N - ddx;
    vec3 xpos = N + ddx;
    vec3 yneg = N - ddy;
    vec3 ypos = N + ddy;
    float curvature = (cross(xneg, xpos).y - cross(yneg, ypos).x) / scene_linear_depth;

    float corrosion = clamp(-curvature * 3.0, 0.0, 1.0);
    float shine = clamp(curvature * 5.0, 0.0, 1.0);

    curvature = pow(saturate((curvature * 0.5 + 0.5) * 1.0), 4.0);

    return vec4(curvature, curvature, curvature, 1.0);
#endif
*/

    // final result
    diffuse_light *= base_color.xyz * clamp((vec3(1.0) - fresnel) * (1.0 - metallic), 0.0, 1.0);
    specular_light = mix(specular_light, specular_light * base_color.xyz, vec3(metallic));

    result = diffuse_light + specular_light;

    // SSAO
    if(RENDER_SSAO)
    {
        result *= ssao_factor;
    }

    // Emissive
    result += emissive_color;

    return vec4(max(vec3(0.0), result), opacity);
}

<|start_filename|>Resource/Shaders/default.glsl<|end_filename|>
#include "scene_constants.glsl"
#include "utility.glsl"
#include "shading.glsl"
#include "default_material.glsl"
#include "default_vs.glsl"

uniform bool is_render_gbuffer;

uniform sampler2D texture_depth;
uniform sampler2D texture_shadow;
uniform sampler2D texture_ssao;
uniform sampler2D texture_scene_reflect;
uniform samplerCube texture_probe;

#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;

layout (location = 0) out vec4 fs_diffuse;
layout (location = 1) out vec4 fs_material;
layout (location = 2) out vec4 fs_normal;
#if 1 == SKELETAL
layout (location = 3) out vec2 fs_velocity;
#endif

void main()
{
    vec2 screen_tex_coord = vs_output.projection_pos.xy / vs_output.projection_pos.w * 0.5 + 0.5;
    float depth = texture2D(texture_depth, screen_tex_coord).x;
    vec4 base_color = get_base_color(vs_output.tex_coord.xy);

#if TRANSPARENT_MATERIAL == 1
    base_color.a *= opacity;
#else
    if(base_color.a < 0.333)
    {
        discard;
    }
#endif

    vec4 emissive_color = get_emissive_color();
    vec4 material_factors = texture2D(texture_material, vs_output.tex_coord.xy);
    float ao_factor = material_factors.x;
    float roughness_factor = material_factors.y * get_roughness();
    float metallic_factor = material_factors.z * get_metalicness();

    base_color.xyz *= ao_factor;

    vec3 N = get_normal(vs_output.tex_coord.xy);

    // Note : Normalization is very important because tangent_to_world may have been scaled..
    N = normalize((vs_output.tangent_to_world * vec4(N, 0.0)).xyz);
    vec3 V = normalize(CAMERA_POSITION.xyz - vs_output.world_position);
    vec3 L = normalize(LIGHT_DIRECTION.xyz);

    // Render GBuffer
    if(is_render_gbuffer)
    {
        fs_diffuse.xyz = base_color.xyz + base_color.xyz * emissive_color.xyz * emissive_color.w * 0.5;
        // emissive
        fs_diffuse.w = saturate(get_luminance(emissive_color.xyz * 0.5) * emissive_color.w * 0.1);
        fs_material = vec4(roughness_factor, metalicness, reflectance, 0.0);
        fs_normal = vec4(N * 0.5 + 0.5, 0.0);
#if 1 == SKELETAL
        fs_velocity = (vs_output.projection_pos.xy / vs_output.projection_pos.w) - (vs_output.prev_projection_pos.xy / vs_output.prev_projection_pos.w);
        // NDC coord -> Screen Coord
        fs_velocity *= 0.5;
        fs_velocity.xy -= JITTER_DELTA;
#endif
    }
    else
    {
        // Render Forward
        fs_diffuse = surface_shading(
                        base_color,
                        base_color.xyz * emissive_color.xyz * emissive_color.w,
                        metalicness,
                        metallic_factor,
                        reflectance,
                        texture2D(texture_ssao, screen_tex_coord).x,
                        texture2D(texture_scene_reflect, screen_tex_coord),
                        texture_probe,
                        texture_shadow,
                        screen_tex_coord,
                        vs_output.world_position,
                        LIGHT_COLOR.xyz,
                        N,
                        V,
                        L,
                        depth);
    }
}
#endif

<|start_filename|>Resource/Shaders/tonemapping.glsl<|end_filename|>
#include "blending.glsl"
#include "quad.glsl"

uniform bool is_render_tonemapping;
uniform float exposure;
uniform float contrast;
uniform sampler2D texture_diffuse;

uniform bool is_render_bloom;
uniform float bloom_intensity;
uniform sampler2D texture_bloom0;
uniform sampler2D texture_bloom1;
uniform sampler2D texture_bloom2;
uniform sampler2D texture_bloom3;
uniform sampler2D texture_bloom4;

uniform bool is_render_light_shaft;
uniform sampler2D texture_light_shaft;

#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

const float A = 0.15;
const float B = 0.50;
const float C = 0.10;
const float D = 0.20;
const float E = 0.02;
const float F = 0.30;
const float W = 11.2;
const float ExposureBias = 2.0f;


vec3 Uncharted2TonemapFunction(vec3 x)
{
     return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;
}

vec3 Uncharted2Tonemap(vec3 hdrColor)
{
    hdrColor *= exposure;
    hdrColor = Uncharted2TonemapFunction(hdrColor * ExposureBias);
    vec3 whiteScale = 1.0f / Uncharted2TonemapFunction(vec3(W));
    return pow(hdrColor * whiteScale, vec3(1.0 / 2.2));
}

vec3 ReinhardTonemap(vec3 hdrColor)
{
    vec3 mapped = hdrColor / (hdrColor + vec3(1.0));
    return pow(mapped, vec3(1.0 / 2.2));
}

vec3 SimpleTonemap(vec3 hdrColor)
{
    // Exposure tone mapping
    vec3 mapped = vec3(1.0) - exp(-hdrColor * exposure);
    return pow(mapped, vec3(1.0 / 2.2));
}

float vignetting(vec2 uv, float inner_value, float outter_value)
{
    float f = smoothstep(0.0, 1.0, length(uv - vec2(0.5)));
    return mix(inner_value, outter_value, f * f);
}

void main() {
    vec2 tex_coord = vs_output.tex_coord.xy;
    vec3 texColor = texture2D(texture_diffuse, tex_coord).xyz;

    vec3 bloom = vec3(0.0);
    if(is_render_bloom)
    {
        bloom += texture2D(texture_bloom0, tex_coord).xyz;
        bloom += texture2D(texture_bloom1, tex_coord).xyz;
        bloom += texture2D(texture_bloom2, tex_coord).xyz;
        bloom += texture2D(texture_bloom3, tex_coord).xyz;
        bloom += texture2D(texture_bloom4, tex_coord).xyz;
        bloom *= bloom_intensity;
    }
    texColor += bloom;

    vec3 light_shaft = vec3(0.0);
    if(is_render_light_shaft)
    {
        light_shaft = texture2D(texture_light_shaft, tex_coord).xyz;
    }
    texColor += light_shaft;

    if(is_render_tonemapping)
    {
        texColor = Uncharted2Tonemap(texColor);
        texColor *= vignetting(tex_coord, 1.0, 0.0);
    }
    else
    {
        texColor = pow(texColor, vec3(1.0 / 2.2));
    }

    fs_output.xyz = Contrast(texColor, contrast);
    fs_output.a = 1.0;
}
#endif // FRAGMENT_SHADER


<|start_filename|>Resource/Shaders/linear_depth.glsl<|end_filename|>


#include "utility.glsl"
#include "quad.glsl"

uniform sampler2D texture_depth;

#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

void main() {
    float depth = texture2D(texture_depth, vs_output.tex_coord.xy).x;
    fs_output = vec4(depth_to_linear_depth(depth));
}
#endif // FRAGMENT_SHADER

<|start_filename|>Resource/Shaders/deferred_shading.glsl<|end_filename|>
#include "scene_constants.glsl"
#include "utility.glsl"
#include "shading.glsl"
#include "quad.glsl"

uniform sampler2D texture_diffuse;
uniform sampler2D texture_material;
uniform sampler2D texture_normal;

uniform sampler2D texture_depth;
uniform sampler2D texture_shadow;
uniform sampler2D texture_ssao;
uniform sampler2D texture_scene_reflect;
uniform samplerCube texture_probe;


#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;

void main() {
    vec2 screen_tex_coord = vs_output.tex_coord.xy;

    float depth = texture2D(texture_depth, screen_tex_coord).x;

    if(depth == 1.0)
    {
        fs_output = vec4(0.0);
        return;
    }

    vec4 base_color = texture2D(texture_diffuse, screen_tex_coord);
    // decoding
    base_color.w *= 10.0;

    vec4 material = texture2D(texture_material, screen_tex_coord);
    vec3 N = normalize(texture2D(texture_normal, screen_tex_coord).xyz * 2.0 - 1.0);

    vec4 world_position = vec4(screen_tex_coord * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
    world_position = INV_VIEW * INV_PROJECTION * world_position;
    world_position /= world_position.w;

    float roughness = material.x;
    float metalicness = material.y;
    float reflectance = material.z;

    vec3 V = normalize(CAMERA_POSITION.xyz - world_position.xyz);
    vec3 L = normalize(LIGHT_DIRECTION.xyz);

    fs_output = surface_shading(
                    base_color,
                    base_color.xyz * base_color.w,
                    metalicness,
                    roughness,
                    reflectance,
                    texture2D(texture_ssao, screen_tex_coord).x,
                    texture2D(texture_scene_reflect, screen_tex_coord),
                    texture_probe,
                    texture_shadow,
                    screen_tex_coord,
                    world_position.xyz,
                    LIGHT_COLOR.xyz,
                    N,
                    V,
                    L,
                    depth);

    fs_output.w = 1.0;
}
#endif

<|start_filename|>Resource/Shaders/quad.glsl<|end_filename|>
struct VERTEX_OUTPUT
{
    vec2 tex_coord;
    vec3 position;
};

#ifdef VERTEX_SHADER
layout (location = 0) in vec4 vs_in_position;
layout (location = 0) out VERTEX_OUTPUT vs_output;

void main() {
    vs_output.position = vs_in_position.xyz;
    vs_output.tex_coord = vs_in_position.xy * 0.5 + 0.5;
    gl_Position = vs_in_position;
    gl_Position.z = -1.0;
    gl_Position.w = 1.0;
}
#endif // VERTEX_SHADER

<|start_filename|>Resource/Shaders/default_material.glsl<|end_filename|>
//----------- MATERIAL_COMPONENTS ------------//

#define TRANSPARENT_MATERIAL 0

#ifdef MATERIAL_COMPONENTS
    uniform float brightness;
    uniform float reflectance;
    uniform float roughness;
    uniform float metalicness;
    uniform float normal_intensity;
    uniform vec2 uv_tiling;
    uniform vec4 emissive_color;
    uniform vec4 diffuse_color;
    uniform sampler2D texture_diffuse;
    uniform sampler2D texture_material;
    uniform sampler2D texture_normal;
#if TRANSPARENT_MATERIAL == 1
    uniform float opacity;
#endif
#endif

vec4 get_emissive_color()
{
    return emissive_color;
}

vec4 get_base_color(vec2 tex_coord)
{
    vec4 color = texture2D(texture_diffuse, tex_coord);
    // gamma correction
    color.xyz = pow(color.xyz, vec3(2.2));
    color.xyz = color.xyz * brightness * diffuse_color.xyz;
    return color;
}

float get_reflection()
{
    return 1.0;
}

vec3 get_edge_tint_color()
{
    return vec3(1.0, 1.0, 1.0);
}

vec3 get_sss_color()
{
    return vec3(1.0, 1.0, 1.0);
}

float get_sss_scale()
{
    return 1.0;
}

vec3 get_normal(vec2 tex_coord)
{
    // Y-Up
    vec3 normal = texture2D(texture_normal, tex_coord).xzy * 2.0 - 1.0;
    normal.xz *= normal_intensity;
    return normalize(normal);
}

float get_ior()
{
    return 1.0;
}

float get_roughness()
{
    return clamp(roughness, 0.05, 1.0);
}

float get_metalicness()
{
    return metalicness;
}

vec3 get_world_offset()
{
    return vec3(1.0, 1.0, 1.0);
}


<|start_filename|>Resource/Shaders/fft_ocean/render.glsl<|end_filename|>
#include "scene_constants.glsl"
#include "utility.glsl"
#include "shading.glsl"

uniform float height;
uniform float simulation_wind;
uniform float simulation_amplitude;
uniform vec4 simulation_size;
uniform vec2 cell_size;
uniform float t;

uniform sampler2DArray fftWavesSampler;
uniform sampler3D slopeVarianceSampler;
uniform sampler2D texture_scene;
uniform sampler2D texture_linear_depth;
uniform sampler2D texture_shadow;
uniform samplerCube texture_probe;

uniform sampler2D texture_noise;
uniform sampler2D texture_caustic;
uniform sampler2D texture_foam;


struct VERTEX_OUTPUT
{
    vec4 uvs;
    vec3 wave_offset;
    float shadow_factor;
    float vertex_noise;
    float screen_fade;
    vec3 vertex_normal;
    vec3 relative_pos;
    vec4 proj_pos;
    vec3 sun_irradiance;
    vec3 sky_irradiance;
};

#ifdef VERTEX_SHADER
layout (location = 0) in vec3 vs_in_position;
layout (location = 1) in vec4 vs_in_color;
layout (location = 2) in vec3 vs_in_normal;
layout (location = 3) in vec3 vs_in_tangent;
layout (location = 4) in vec2 vs_in_tex_coord;
layout (location = 5) in vec2 offset;   // instance buffer

layout (location = 0) out VERTEX_OUTPUT vs_output;


vec3 oceanWorldPos(vec4 vertex)
{
    float height_diff = height - CAMERA_POSITION.y;
    vec3 cameraDir = normalize((INV_PROJECTION * (vertex + vec4(JITTER_OFFSET, 0.0, 0.0))).xyz);
    vec3 worldDir = (INV_VIEW_ORIGIN * vec4(cameraDir, 0.0)).xyz;
    const float far_dist = NEAR_FAR.y * 2.0;

    float dist = 0.0;

    if(0.0 < height_diff)
    {
        dist = (0.0 < worldDir.y) ? (height_diff / worldDir.y) : far_dist;
    }
    else
    {
        dist = (worldDir.y < 0.0) ? (height_diff / worldDir.y) : far_dist;
    }

    dist = min(far_dist, dist);

    vec3 world_pos = vec3(0.0, height, 0.0);
    world_pos.xz += CAMERA_POSITION.xz + dist * worldDir.xz;
    return world_pos;
}

void main()
{
    vec3 vertex_scale = vec3(1.5, 1.5, 1.0);
    vec4 vertex_pos = vec4(vs_in_position * vertex_scale, 1.0);
    vec3 world_pos = oceanWorldPos(vertex_pos);
    vec3 relative_pos = world_pos - CAMERA_POSITION.xyz;
    float dist_xz = length(relative_pos.xz);
    float dist = length(relative_pos);

    float screen_fade = 1.0f - saturate(ceil(max(abs(vs_in_position.x), abs(vs_in_position.y)) - 0.999f));

    vec2 u = world_pos.xz;
    vec2 ux = oceanWorldPos(vertex_pos + vec4(cell_size.x, 0.0, 0.0, 0.0)).xz;
    vec2 uy = oceanWorldPos(vertex_pos + vec4(0.0, cell_size.y, 0.0, 0.0)).xz;
    vec2 dux = abs(ux - u) * 2.0;
    vec2 duy = abs(uy - u) * 2.0;

    vec3 dP = vec3(0.0);
    dP.y += textureGrad(fftWavesSampler, vec3(u / simulation_size.x, 0.0), dux / simulation_size.x, duy / simulation_size.x).x;
    dP.y += textureGrad(fftWavesSampler, vec3(u / simulation_size.y, 0.0), dux / simulation_size.y, duy / simulation_size.y).y;
    dP.y += textureGrad(fftWavesSampler, vec3(u / simulation_size.z, 0.0), dux / simulation_size.z, duy / simulation_size.z).z;
    dP.y += textureGrad(fftWavesSampler, vec3(u / simulation_size.w, 0.0), dux / simulation_size.w, duy / simulation_size.w).w;

    dP.xz += textureGrad(fftWavesSampler, vec3(u / simulation_size.x, 3.0), dux / simulation_size.x, duy / simulation_size.x).xy;
    dP.xz += textureGrad(fftWavesSampler, vec3(u / simulation_size.y, 3.0), dux / simulation_size.y, duy / simulation_size.y).zw;
    dP.xz += textureGrad(fftWavesSampler, vec3(u / simulation_size.z, 4.0), dux / simulation_size.z, duy / simulation_size.z).xy;
    dP.xz += textureGrad(fftWavesSampler, vec3(u / simulation_size.w, 4.0), dux / simulation_size.w, duy / simulation_size.w).zw;

    vec3 vertex_normal = vec3(-dP.x, dP.y * 0.5 + 0.5, -dP.z);
    vertex_normal = safe_normalize(mix(vec3(0.0, 1.0, 0.0), vertex_normal, saturate(simulation_amplitude)));

    world_pos += dP * simulation_amplitude;
    relative_pos = world_pos - CAMERA_POSITION.xyz;

    vec3 eye_direction = normalize(relative_pos);
    vec4 proj_pos = VIEW_PROJECTION * vec4(world_pos.xyz, 1.0);

    float fade = 1.0f;
    if(dist_xz < NEAR_FAR.y && vs_in_position.y < 0.0)
    {
        proj_pos.xy = mix(vs_in_position.xy * proj_pos.w, proj_pos.xy, screen_fade);
    }

    vec2 screen_coord = (proj_pos.xy / proj_pos.w) * 0.5 + 0.5;

    float vertex_noise = texture2DLod(texture_noise, world_pos.xz * 0.005, 0.0).x;
    float shadow_factor = get_shadow_factor(world_pos, dot(LIGHT_DIRECTION.xyz, vertex_normal.xyz), texture_shadow);

    vec3 in_scatter;
    vec3 sun_irradiance;
    vec3 sky_irradiance;
    GetSceneRadiance(ATMOSPHERE, dist * 0.1, eye_direction, vertex_normal, sun_irradiance, sky_irradiance, in_scatter);

    vs_output.sun_irradiance = sun_irradiance;
    vs_output.sky_irradiance = sky_irradiance;

    vs_output.uvs.xy = u;
    vs_output.uvs.zw = world_pos.xz;
    vs_output.shadow_factor = shadow_factor;
    vs_output.vertex_noise = vertex_noise;
    vs_output.screen_fade = screen_fade;
    vs_output.wave_offset = dP;
    vs_output.vertex_normal = vertex_normal;
    vs_output.relative_pos = relative_pos;
    vs_output.proj_pos = proj_pos;
    gl_Position = proj_pos;
}
#endif


#ifdef FRAGMENT_SHADER
layout (location = 0) in VERTEX_OUTPUT vs_output;
layout (location = 0) out vec4 fs_output;


void main()
{
    vec2 uv = vs_output.uvs.xy;
    vec2 fft_uv = vs_output.uvs.zw;
    vec2 screen_tex_coord = (vs_output.proj_pos.xy / vs_output.proj_pos.w) * 0.5 + 0.5;
    vec3 view_center_ray = vec3(VIEW_ORIGIN[0].z, VIEW_ORIGIN[1].z, VIEW_ORIGIN[2].z);
    float screen_fade = pow(vs_output.screen_fade, 100.0f);

    vec3 relative_pos = vs_output.relative_pos;
    vec3 world_pos = relative_pos + CAMERA_POSITION.xyz;
    float dist = length(relative_pos);
    vec3 V = -relative_pos / dist;
    float view_ray_angle = dot(view_center_ray, V);

    float scene_dist = texture2D(texture_linear_depth, screen_tex_coord).x / view_ray_angle;
    float vertex_noise = vs_output.vertex_noise;

    // fix scene_depth
    vec3 tempPos = -V * scene_dist;
    tempPos.xz = mix(relative_pos.xz, tempPos.xz, abs(V.y));
    scene_dist = length(tempPos.xyz);

    vec3 sun_irradiance = vs_output.sun_irradiance;
    vec3 sky_irradiance = vs_output.sky_irradiance;
    vec3 shadow_factor = max(sky_irradiance, vec3(vs_output.shadow_factor));

    vec2 slopes = texture2DArrayLod(fftWavesSampler, vec3(uv / simulation_size.x, 1.0), 0.0).xy;
    slopes += texture2DArrayLod(fftWavesSampler, vec3(uv / simulation_size.y, 1.0), 0.0).zw;
    slopes += texture2DArrayLod(fftWavesSampler, vec3(uv / simulation_size.z, 2.0), 0.0).xy;
    slopes += texture2DArrayLod(fftWavesSampler, vec3(uv / simulation_size.w, 2.0), 0.0).zw;

    vec3 vertex_normal = normalize(vs_output.vertex_normal);
    vec3 N = normalize(vec3(-slopes.x, 1.0, -slopes.y) + vertex_normal * 0.2);
    vec3 smooth_normal = normalize(vec3(-slopes.x, 1.0, -slopes.y) + vertex_normal * 0.5);

    vec3 L = LIGHT_DIRECTION.xyz;
    vec3 H = normalize(V + L);

    float NdL = max(0.0, dot(N, L));
    float NdV = max(0.0, dot(N, V));
    float NdH = max(0.0, dot(N, H));
    float HdV = max(0.0, dot(H, V));
    float LdV = max(0.0, dot(L, V));

    vec3 F0 = vec3(0.04);
    float fresnel = fresnelSchlick(max(0.2, dot(smooth_normal, V)), F0).x;

    // refract
    vec2 reflected_screen_uv = screen_tex_coord + N.xz * 0.05f;
    float refracted_scene_dist = texture2D(texture_linear_depth, reflected_screen_uv).x / view_ray_angle;
    float refracted_scene_dist_origin = refracted_scene_dist;

    // fix refractedSceneDepth
    tempPos = -V * refracted_scene_dist;
    tempPos.xz = mix(relative_pos.xz, tempPos.xz, abs(V.y));
    refracted_scene_dist = length(tempPos.xyz);

    float dist_diff = max(0.0f, max(scene_dist, refracted_scene_dist) - dist);

    // groud pos
    vec3 groundPos = world_pos - V * dist_diff + vec3(N.x, 0.0f, N.z) * 0.5f;

    bool isUnderWater = CAMERA_POSITION.y < height;
    float opacity = pow(saturate(dist_diff * 0.3), 1.0) * screen_fade;
    float inv_opacity = 1.0f - opacity;

    vec3 light_color = LIGHT_COLOR.xyz * sun_irradiance;

    // Water Base Color
    vec3 sea_color_near = vec3(198.0, 230.0, 213.0) / 255.0;
    vec3 sea_color_mid = vec3(121.0, 176.0, 188.0) / 255.0;
    vec3 sea_color_far = vec3(58.0, 47.0, 99.0) / 255.0;
    vec3 water_color;
    {
        water_color = mix(sea_color_near, sea_color_mid, sqrt(saturate(dist * 0.5)));
        water_color = mix(water_color, sea_color_far, sqrt(saturate(dist * 0.05)));
        water_color = pow(water_color, vec3(2.2));
    }

    // Reflection
    vec3 R = reflect(-V, smooth_normal);
    vec3 scene_reflect_color = textureCubeLod(texture_probe, invert_y(R), 0.0).xyz;

    // Under Water
    vec3 under_water_color = texture2DLod(texture_scene, (refracted_scene_dist <= dist) ? screen_tex_coord : reflected_screen_uv, 0.0).xyz;
    {
        // Under Water Caustic
        if(false == isUnderWater)
        {
            const bool isSimpleShadow = true;
            vec3 under_water_shadow = vec3(get_shadow_factor(world_pos, dot(L, vertex_normal.xyz), texture_shadow, isSimpleShadow));
            under_water_shadow = max(sky_irradiance, under_water_shadow);

            const float chromaSeperation = sin(t * 3.5f) * 0.005;
            vec2 caustic_uv = (groundPos + L * dist_diff).xz * 0.3 + vertex_normal.xz * 0.5;

            vec3 caustic_color;
            caustic_color.r = texture2D(texture_caustic, caustic_uv + vec2(0.0f, chromaSeperation)).r;
            caustic_color.g = texture2D(texture_caustic, caustic_uv + vec2(chromaSeperation, 0.0f)).g;
            caustic_color.b = texture2D(texture_caustic, caustic_uv - vec2(chromaSeperation, chromaSeperation)).b;
            caustic_color *= under_water_shadow * sun_irradiance * screen_fade * saturate(dist_diff) * 2.0;

            // apply caustic
            under_water_color += caustic_color;
        }

        float fog_ratio = saturate(abs(refracted_scene_dist_origin) * 0.05f);
        vec3 fog_color = mix(under_water_color, sea_color_far, fog_ratio * fog_ratio);

        under_water_color = mix(under_water_color, water_color * under_water_color, opacity);
        under_water_color = mix(fog_color, under_water_color, screen_fade) * inv_opacity;
    }

    // White cap
    float wave_peak = pow(saturate((vs_output.wave_offset.y * 0.5 + 0.5) * 1.7 + saturate(1.0f - N.y) * 2.0), 12.0f);
    float white_cap = saturate(wave_peak * simulation_wind);

    // Transmission
    float transmission = wave_peak * 2.0;

    // Foam
    vec3 foam = vec3(0.0);
    {
        foam = pow(texture2D(texture_foam, uv * 0.3 + vertex_noise * 0.2).xyz, vec3(2.2));
        foam += pow(texture2D(texture_foam, uv * -0.05 + vertex_noise * 0.3).xyz, vec3(2.2));
        foam *= 0.5;

        float foam_amount = saturate(inv_opacity * saturate(dist_diff) + white_cap * 1.0);
        float sharpen = mix(0.6, 0.1, foam_amount);
        foam = mix(vec3(0.0), foam, saturate((foam.x - sharpen) / (1.0f - sharpen) * 3.0f)) * foam_amount;
    }

    // Specular
    vec3 light_fresnel = fresnelSchlick(HdV, F0);
    float roughness = 0.1;
    float specular_light = cooktorrance_specular(light_fresnel, NdL, NdV, NdH, roughness).x * 2.0;

    // scattering
    vec3 scattering_normal = normalize(vec3(vertex_normal.x, 5.0, vertex_normal.z));
    float scattering = pow(abs(dot(H, scattering_normal)), 20.0) * (0.5 - dot(scattering_normal, V) * 0.5);
    specular_light += scattering;
    transmission += scattering * 10.0;

    float foam_lum = dot(vec3(0.3f, 0.59f, 0.11f), foam);
    specular_light *= saturate(1.0f - foam_lum * 2.0f);
    fresnel *= saturate(1.0f - foam_lum * 2.0f);

    vec3 diffuse_lighting = max(sky_irradiance, vec3(dot(N, L) * 0.5 + 0.5));

    fs_output.xyz = (mix(water_color, scene_reflect_color, fresnel) + foam) * diffuse_lighting + specular_light;
    fs_output.xyz += transmission * water_color;
    fs_output.xyz *= light_color * shadow_factor;

    // final output
    opacity = saturate(opacity + (foam_lum + specular_light + fresnel + transmission * 0.01f)) * saturate(dist_diff);
    fs_output.xyz = mix(under_water_color.xyz, fs_output.xyz, opacity * screen_fade);
    fs_output.w = 1.0;
}
#endif


<|start_filename|>Resource/Shaders/empty.glsl<|end_filename|>
#ifdef VERTEX_SHADER
void main() {
    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
}
#endif


#ifdef FRAGMENT_SHADER
layout(location = 0) out vec4 fs_output;
void main() {
    fs_output = vec4(0.0, 0.0, 0.0, 1.0);
}
#endif

<|start_filename|>Resource/Shaders/scene_constants.glsl<|end_filename|>
layout(std140, binding=0) uniform scene_constants
{
    float TIME;
    float JITTER_FRAME;
    bool RENDER_SSR;
    bool RENDER_SSAO;
    vec2 SCREEN_SIZE;
    vec2 BACKBUFFER_SIZE;
    vec2 MOUSE_POS;
    float DELTA_TIME;
    int SCENE_DUMMY_0;
};

layout(std140, binding=1) uniform view_constants
{
    mat4 VIEW;
    mat4 INV_VIEW;
    mat4 VIEW_ORIGIN;
    mat4 INV_VIEW_ORIGIN;
    mat4 PROJECTION;
    mat4 INV_PROJECTION;
    vec3 CAMERA_POSITION;
    float VIEW_DUMMY_0;
    vec2 NEAR_FAR;
    vec2 JITTER_DELTA;
    vec2 JITTER_OFFSET;
    vec2 VIEWCONSTANTS_DUMMY0;
};

layout(std140, binding=2) uniform view_projection
{
    mat4 VIEW_PROJECTION;
    mat4 PREV_VIEW_PROJECTION;
};

layout(std140, binding=3) uniform light_constants
{
    mat4 SHADOW_MATRIX;
    vec3 LIGHT_POSITION;
    float SHADOW_EXP;
    vec3 LIGHT_DIRECTION;
    float SHADOW_BIAS;
    vec3 LIGHT_COLOR;
    int SHADOW_SAMPLES;
};

const int MAX_POINT_LIGHTS = 10;

struct POINT_LIGHT
{
    vec3 color;
    float radius;
    vec3 pos;
    float render;
};

layout(std140, binding=4) uniform point_light_constants
{
    POINT_LIGHT POINT_LIGHTS[MAX_POINT_LIGHTS];
};


layout(std140, binding=5) uniform particle_common
{
    vec3 PARTICLE_COLOR;
    int PARTICLE_ALIGN_MODE;
    ivec2 PARTICLE_CELL_COUNT;
    int PARTICLE_BLEND_MODE;
    int PARTICLE_COMMON_DUMMY_0;
};

layout(std140, binding=6) uniform particle_infos
{
    mat4 PARTICLE_PARENT_MATRIX;
    vec2 PARTICLE_DELAY;
    vec2 PARTICLE_LIFE_TIME;
    vec3 PARTICLE_TRANSFORM_ROTATION_MIN;
    float PARTICLE_FADE_IN;
    vec3 PARTICLE_TRANSFORM_ROTATION_MAX;
    float PARTICLE_FADE_OUT;
    vec3 PARTICLE_TRANSFORM_SCALE_MIN;
    float PARTICLE_OPACITY;
    vec3 PARTICLE_TRANSFORM_SCALE_MAX;
    bool PARTICLE_ENABLE_VECTOR_FIELD;
    vec3 PARTICLE_VELOCITY_POSITION_MIN;
    float PARTICLE_VECTOR_FIELD_STRENGTH;
    vec3 PARTICLE_VELOCITY_POSITION_MAX;
    float PARTICLE_VECTOR_FIELD_TIGHTNESS;
    vec3 PARTICLE_VELOCITY_ROTATION_MIN;
    uint PARTICLE_MAX_COUNT;
    vec3 PARTICLE_VELOCITY_ROTATION_MAX;
    uint PARTICLE_SPAWN_COUNT;
    vec3 PARTICLE_VELOCITY_SCALE_MIN;
    float PARTICLE_VELOCITY_STRETCH;
    vec3 PARTICLE_VELOCITY_SCALE_MAX;
    float PARTICLE_VELOCITY_ACCELERATION;
    mat4 PARTICLE_VECTOR_FIELD_MATRIX;
    mat4 PARTICLE_VECTOR_FIELD_INV_MATRIX;
    vec3 PARTICLE_SPAWN_VOLUME_INFO;
    uint PARTICLE_SPAWN_VOLUME_TYPE;
    mat4 PARTICLE_SPAWN_VOLUME_MATRIX;
    vec2 PARTICLE_VELOCITY_LIMIT;
    float PARTICLE_FORCE_GRAVITY;
    float PARTICLE_PLAY_SPEED;
    uint PARTICLE_VELOCITY_TYPE;
    float PARTICLE_FORCE_ELASTICITY;
    float PARTICLE_FORCE_FRICTION;
    uint PARTICLE_DUMMY_0;
};


// referene : RenderOptions.py
const int BLEND = 0;
const int ADDITIVE = 1;
const int MULTIPLY = 2;
const int SUBTRACT = 3;

#define WORK_GROUP_SIZE 64

const int MAX_BONES_PER_VERTEX = 4;
const int MAX_BONES = 100;

const float PI = 3.14159265358979323846;
const float HALF_PI = PI * 0.5;
const float TWO_PI = PI * 2.0;
const float deg = PI / 180.0;

const vec3 kSphereCenter = vec3(1.0, 1.0, -2.0);
const float kSphereRadius = 1.0;
const vec3 kSphereAlbedo = vec3(0.8);
const vec3 kGroundAlbedo = vec3(0.0, 0.0, 0.04);


<|start_filename|>PyEngine3D/Utilities/SaveTexture.cpp<|end_filename|>
// save to file
SaveTextureImage(GL_TEXTURE_2D, GL_RGBA32F, transmittance_texture_, "transmittance.texture");
SaveTextureImage(GL_TEXTURE_3D, GL_RGBA32F, scattering_texture_, "scattering.texture");
SaveTextureImage(GL_TEXTURE_2D, GL_RGBA32F, irradiance_texture_, "irradiance.texture");


GLuint SaveTextureImage(GLuint target, GLuint internal_format, GLuint texture, const char* filename)
{
	std::cout << "SaveTextureImage : " << filename << std::endl;
	glBindTexture(target, texture);

	GLint textureWidth, textureHeight, textureDepth;
	int count;
	glGetTexLevelParameteriv(target, 0, GL_TEXTURE_WIDTH, &textureWidth);
	glGetTexLevelParameteriv(target, 0, GL_TEXTURE_HEIGHT, &textureHeight);
	glGetTexLevelParameteriv(target, 0, GL_TEXTURE_DEPTH, &textureDepth);
	count = textureWidth * textureHeight * 4;

	const char* texture_type = (target == GL_TEXTURE_3D) ? "Texture3D" : "Texture2D";
	const char* szInternalFormat = (internal_format == GL_RGBA32F) ? "GL_RGBA32F" : "GL_RGBA16F";

	std::ofstream myfile;
	myfile.open(filename);

	myfile << "{" << std::endl;
	myfile << "\t'width': " << textureWidth << ", " << std::endl;
	myfile << "\t'height': " << textureHeight << ", " << std::endl;
	myfile << "\t'depth': " << textureDepth << ", " << std::endl;
	myfile << "\t'image_mode': " << "'RGBA', " << std::endl;
	myfile << "\t'internal_format': " << szInternalFormat << ", " << std::endl;
	myfile << "\t'texture_format': " << "GL_RGBA, " << std::endl;
	myfile << "\t'mag_filter': " << "GL_LINEAR, " << std::endl;
	myfile << "\t'min_filter': " << "GL_LINEAR, " << std::endl;
	myfile << "\t'texture_type': " << texture_type << ", " << std::endl;
	myfile << "\t'wrap': " << "GL_CLAMP_TO_EDGE, " << std::endl;
	myfile << "\t'data_type': " << "GL_FLOAT, " << std::endl;

	////////////////////////////////////////////////
	// write data
	float *pixels = new float[count];

	myfile << "\t'data': [";

	if(GL_TEXTURE_2D == target)
	{
		glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, pixels);
		int nWidthCount = 0;
		for(int i=0; i<count; ++i)
		{
			++nWidthCount;
			myfile << (std::isnan(pixels[i]) ? 0.0f : pixels[i]) << ", ";
			if(textureWidth <= nWidthCount)
			{
				nWidthCount = 0;
				myfile << "\n";
			}
		}
	}
	else if(GL_TEXTURE_3D == target)
	{
		GLuint fb;
		glGenFramebuffers(1, &fb);
		glBindFramebuffer(GL_FRAMEBUFFER, fb);
		for(int layer=0; layer<textureDepth; ++layer)
		{
			glFramebufferTexture3D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_3D, texture, 0, layer);
			glReadBuffer(GL_COLOR_ATTACHMENT0);
			glReadPixels(0, 0, textureWidth, textureHeight, GL_RGBA, GL_FLOAT, pixels);
			int nWidthCount = 0;
			for(int i=0; i<count; ++i)
			{
				++nWidthCount;
				myfile << (std::isnan(pixels[i]) ? 0.0f : pixels[i]) << ", ";
				if(textureWidth <= nWidthCount)
				{
					nWidthCount = 0;
					myfile << "\n";
				}
			}
		}
		glBindFramebuffer(GL_FRAMEBUFFER, 0);
		glDeleteFramebuffers(1, &fb);
	}
	glBindTexture(target, 0);

	myfile << "]\n}" << std::endl;
	myfile.close();
	delete pixels;
}

<|start_filename|>Resource/Shaders/simple.glsl<|end_filename|>
#include "scene_constants.glsl"

uniform mat4 model;
uniform mat4 view_projection;
uniform vec4 diffuse_color;

//----------- VERTEX_SHADER ---------------//

#ifdef VERTEX_SHADER
layout (location = 0) in vec3 vs_in_position;
layout (location = 1) in vec4 vs_in_color;
layout (location = 2) in vec3 vs_in_normal;
layout (location = 3) in vec3 vs_in_tangent;
layout (location = 4) in vec2 vs_in_tex_coord;

void main() {
    gl_Position = PROJECTION * VIEW * model * vec4(vs_in_position, 1.0);
}
#endif

//----------- FRAGMENT_SHADER ---------------//

#ifdef FRAGMENT_SHADER
layout (location = 0) out vec4 fs_output;

void main() {
    fs_output = vec4(1.0, 1.0, 0.0, 1.0);
}
#endif

<|start_filename|>Resource/Shaders/PCFKernels.glsl<|end_filename|>
//=================================================================================================
//
//	Shadows Sample
//  by MJP
//  http://mynameismjp.wordpress.com/
//
//  All code licensed under the MIT license
//
//=================================================================================================


const int PoissonSampleCount = 64;

// For Poisson Disk PCF sampling
const vec2 PoissonSamples[PoissonSampleCount] =
{
    vec2(-0.5119625f, -0.4827938f),
    vec2(-0.2171264f, -0.4768726f),
    vec2(-0.7552931f, -0.2426507f),
    vec2(-0.7136765f, -0.4496614f),
    vec2(-0.5938849f, -0.6895654f),
    vec2(-0.3148003f, -0.7047654f),
    vec2(-0.42215f, -0.2024607f),
    vec2(-0.9466816f, -0.2014508f),
    vec2(-0.8409063f, -0.03465778f),
    vec2(-0.6517572f, -0.07476326f),
    vec2(-0.1041822f, -0.02521214f),
    vec2(-0.3042712f, -0.02195431f),
    vec2(-0.5082307f, 0.1079806f),
    vec2(-0.08429877f, -0.2316298f),
    vec2(-0.9879128f, 0.1113683f),
    vec2(-0.3859636f, 0.3363545f),
    vec2(-0.1925334f, 0.1787288f),
    vec2(0.003256182f, 0.138135f),
    vec2(-0.8706837f, 0.3010679f),
    vec2(-0.6982038f, 0.1904326f),
    vec2(0.1975043f, 0.2221317f),
    vec2(0.1507788f, 0.4204168f),
    vec2(0.3514056f, 0.09865579f),
    vec2(0.1558783f, -0.08460935f),
    vec2(-0.0684978f, 0.4461993f),
    vec2(0.3780522f, 0.3478679f),
    vec2(0.3956799f, -0.1469177f),
    vec2(0.5838975f, 0.1054943f),
    vec2(0.6155105f, 0.3245716f),
    vec2(0.3928624f, -0.4417621f),
    vec2(0.1749884f, -0.4202175f),
    vec2(0.6813727f, -0.2424808f),
    vec2(-0.6707711f, 0.4912741f),
    vec2(0.0005130528f, -0.8058334f),
    vec2(0.02703013f, -0.6010728f),
    vec2(-0.1658188f, -0.9695674f),
    vec2(0.4060591f, -0.7100726f),
    vec2(0.7713396f, -0.4713659f),
    vec2(0.573212f, -0.51544f),
    vec2(-0.3448896f, -0.9046497f),
    vec2(0.1268544f, -0.9874692f),
    vec2(0.7418533f, -0.6667366f),
    vec2(0.3492522f, 0.5924662f),
    vec2(0.5679897f, 0.5343465f),
    vec2(0.5663417f, 0.7708698f),
    vec2(0.7375497f, 0.6691415f),
    vec2(0.2271994f, -0.6163502f),
    vec2(0.2312844f, 0.8725659f),
    vec2(0.4216993f, 0.9002838f),
    vec2(0.4262091f, -0.9013284f),
    vec2(0.2001408f, -0.808381f),
    vec2(0.149394f, 0.6650763f),
    vec2(-0.09640376f, 0.9843736f),
    vec2(0.7682328f, -0.07273844f),
    vec2(0.04146584f, 0.8313184f),
    vec2(0.9705266f, -0.1143304f),
    vec2(0.9670017f, 0.1293385f),
    vec2(0.9015037f, -0.3306949f),
    vec2(-0.5085648f, 0.7534177f),
    vec2(0.9055501f, 0.3758393f),
    vec2(0.7599946f, 0.1809109f),
    vec2(-0.2483695f, 0.7942952f),
    vec2(-0.4241052f, 0.5581087f),
    vec2(-0.1020106f, 0.6724468f),
};