From f963da80f355f0bfd8de71e5395e3d558066ab2a Mon Sep 17 00:00:00 2001 From: redstrate <54911369+redstrate@users.noreply.github.com> Date: Sun, 9 May 2021 20:04:41 -0400 Subject: [PATCH] Add shaders back (sorry) --- engine/shaders/PrismEditoroptions.json | 1 + engine/shaders/atmosphere.glsl | 107 ++ engine/shaders/billboard.frag.glsl | 17 + engine/shaders/billboard.vert.glsl | 25 + engine/shaders/blend.frag.glsl | 16 + engine/shaders/blend.vert.glsl | 14 + engine/shaders/brdf.frag.glsl | 48 + engine/shaders/brdf.vert.glsl | 6 + engine/shaders/color.frag.glsl | 10 + engine/shaders/color.vert.glsl | 10 + engine/shaders/common.nocompile.glsl | 161 ++ engine/shaders/debug.frag.glsl | 10 + engine/shaders/debug.vert.glsl | 10 + engine/shaders/dof.frag.glsl | 28 + engine/shaders/dof.vert.glsl | 35 + engine/shaders/edge.frag.glsl | 16 + engine/shaders/edge.vert.glsl | 13 + engine/shaders/filter.frag.glsl | 51 + engine/shaders/filter.vert.glsl | 13 + engine/shaders/font.glsl | 5 + engine/shaders/gaussian.frag.glsl | 34 + engine/shaders/gaussian.vert.glsl | 6 + engine/shaders/histogram-average.comp.glsl | 44 + engine/shaders/histogram.comp.glsl | 49 + engine/shaders/imgui.frag.glsl | 10 + engine/shaders/imgui.ini | 883 +++++++++++ engine/shaders/imgui.vert.glsl | 16 + engine/shaders/irradiance.frag.glsl | 36 + engine/shaders/irradiance.vert.glsl | 12 + engine/shaders/mesh.vert.nocompile.glsl | 123 ++ engine/shaders/omnishadow.frag.glsl | 14 + engine/shaders/post.frag.glsl | 159 ++ engine/shaders/post.vert.glsl | 25 + engine/shaders/rendering.nocompile.glsl | 305 ++++ .../shaders/scenecapture.vert.nocompile.glsl | 58 + engine/shaders/shadow.frag.glsl | 5 + engine/shaders/shadow.vert.nocompile.glsl | 43 + engine/shaders/sky.frag.glsl | 47 + engine/shaders/sky.vert.glsl | 12 + engine/shaders/smaa.glsl | 1407 +++++++++++++++++ engine/shaders/smaa_common.glsl | 12 + engine/shaders/text.frag.glsl | 15 + engine/shaders/text.vert.glsl | 61 + engine/shaders/ui.frag.glsl | 22 + engine/shaders/ui.vert.glsl | 38 + 45 files changed, 4032 insertions(+) create mode 100644 engine/shaders/PrismEditoroptions.json create mode 100644 engine/shaders/atmosphere.glsl create mode 100644 engine/shaders/billboard.frag.glsl create mode 100644 engine/shaders/billboard.vert.glsl create mode 100644 engine/shaders/blend.frag.glsl create mode 100644 engine/shaders/blend.vert.glsl create mode 100644 engine/shaders/brdf.frag.glsl create mode 100644 engine/shaders/brdf.vert.glsl create mode 100644 engine/shaders/color.frag.glsl create mode 100644 engine/shaders/color.vert.glsl create mode 100644 engine/shaders/common.nocompile.glsl create mode 100644 engine/shaders/debug.frag.glsl create mode 100644 engine/shaders/debug.vert.glsl create mode 100644 engine/shaders/dof.frag.glsl create mode 100644 engine/shaders/dof.vert.glsl create mode 100644 engine/shaders/edge.frag.glsl create mode 100644 engine/shaders/edge.vert.glsl create mode 100644 engine/shaders/filter.frag.glsl create mode 100644 engine/shaders/filter.vert.glsl create mode 100644 engine/shaders/font.glsl create mode 100644 engine/shaders/gaussian.frag.glsl create mode 100644 engine/shaders/gaussian.vert.glsl create mode 100644 engine/shaders/histogram-average.comp.glsl create mode 100644 engine/shaders/histogram.comp.glsl create mode 100644 engine/shaders/imgui.frag.glsl create mode 100644 engine/shaders/imgui.ini create mode 100644 engine/shaders/imgui.vert.glsl create mode 100644 engine/shaders/irradiance.frag.glsl create mode 100644 engine/shaders/irradiance.vert.glsl create mode 100644 engine/shaders/mesh.vert.nocompile.glsl create mode 100644 engine/shaders/omnishadow.frag.glsl create mode 100644 engine/shaders/post.frag.glsl create mode 100644 engine/shaders/post.vert.glsl create mode 100644 engine/shaders/rendering.nocompile.glsl create mode 100644 engine/shaders/scenecapture.vert.nocompile.glsl create mode 100644 engine/shaders/shadow.frag.glsl create mode 100644 engine/shaders/shadow.vert.nocompile.glsl create mode 100644 engine/shaders/sky.frag.glsl create mode 100644 engine/shaders/sky.vert.glsl create mode 100644 engine/shaders/smaa.glsl create mode 100644 engine/shaders/smaa_common.glsl create mode 100644 engine/shaders/text.frag.glsl create mode 100644 engine/shaders/text.vert.glsl create mode 100644 engine/shaders/ui.frag.glsl create mode 100644 engine/shaders/ui.vert.glsl diff --git a/engine/shaders/PrismEditoroptions.json b/engine/shaders/PrismEditoroptions.json new file mode 100644 index 0000000..10f0797 --- /dev/null +++ b/engine/shaders/PrismEditoroptions.json @@ -0,0 +1 @@ +{"files":[],"height":856,"width":1269,"x":336,"y":437} \ No newline at end of file diff --git a/engine/shaders/atmosphere.glsl b/engine/shaders/atmosphere.glsl new file mode 100644 index 0000000..3ad481f --- /dev/null +++ b/engine/shaders/atmosphere.glsl @@ -0,0 +1,107 @@ +#define PI 3.141592 +#define iSteps 16 +#define jSteps 8 + +vec2 rsi(vec3 r0, vec3 rd, float sr) { + // ray-sphere intersection that assumes + // the sphere is centered at the origin. + // No intersection when result.x > result.y + float a = dot(rd, rd); + float b = 2.0 * dot(rd, r0); + float c = dot(r0, r0) - (sr * sr); + float d = (b*b) - 4.0*a*c; + if (d < 0.0) return vec2(1e5,-1e5); + return vec2( + (-b - sqrt(d))/(2.0*a), + (-b + sqrt(d))/(2.0*a) + ); +} + +vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) { + // Normalize the sun and view directions. + pSun = normalize(pSun); + r = normalize(r); + + // Calculate the step size of the primary ray. + vec2 p = rsi(r0, r, rAtmos); + if (p.x > p.y) return vec3(0,0,0); + p.y = min(p.y, rsi(r0, r, rPlanet).x); + float iStepSize = (p.y - p.x) / float(iSteps); + + // Initialize the primary ray time. + float iTime = 0.0; + + // Initialize accumulators for Rayleigh and Mie scattering. + vec3 totalRlh = vec3(0,0,0); + vec3 totalMie = vec3(0,0,0); + + // Initialize optical depth accumulators for the primary ray. + float iOdRlh = 0.0; + float iOdMie = 0.0; + + // Calculate the Rayleigh and Mie phases. + float mu = dot(r, pSun); + float mumu = mu * mu; + float gg = g * g; + float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu); + float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (pow(1.0 + gg - 2.0 * mu * g, 1.5) * (2.0 + gg)); + + // Sample the primary ray. + for (int i = 0; i < iSteps; i++) { + + // Calculate the primary ray sample position. + vec3 iPos = r0 + r * (iTime + iStepSize * 0.5); + + // Calculate the height of the sample. + float iHeight = length(iPos) - rPlanet; + + // Calculate the optical depth of the Rayleigh and Mie scattering for this step. + float odStepRlh = exp(-iHeight / shRlh) * iStepSize; + float odStepMie = exp(-iHeight / shMie) * iStepSize; + + // Accumulate optical depth. + iOdRlh += odStepRlh; + iOdMie += odStepMie; + + // Calculate the step size of the secondary ray. + float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps); + + // Initialize the secondary ray time. + float jTime = 0.0; + + // Initialize optical depth accumulators for the secondary ray. + float jOdRlh = 0.0; + float jOdMie = 0.0; + + // Sample the secondary ray. + for (int j = 0; j < jSteps; j++) { + + // Calculate the secondary ray sample position. + vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5); + + // Calculate the height of the sample. + float jHeight = length(jPos) - rPlanet; + + // Accumulate the optical depth. + jOdRlh += exp(-jHeight / shRlh) * jStepSize; + jOdMie += exp(-jHeight / shMie) * jStepSize; + + // Increment the secondary ray time. + jTime += jStepSize; + } + + // Calculate attenuation. + vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh))); + + // Accumulate scattering. + totalRlh += odStepRlh * attn; + totalMie += odStepMie * attn; + + // Increment the primary ray time. + iTime += iStepSize; + + } + + // Calculate and return the final color. + return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie); +} diff --git a/engine/shaders/billboard.frag.glsl b/engine/shaders/billboard.frag.glsl new file mode 100644 index 0000000..27ecec2 --- /dev/null +++ b/engine/shaders/billboard.frag.glsl @@ -0,0 +1,17 @@ +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outColor; + +layout(push_constant, binding = 1) uniform readonly PushConstant{ + mat4 mvp; + vec4 color; +}; + +layout (binding = 2) uniform sampler2D colorSampler; + +void main() { + if(inUV.y < 0.0 || inUV.x > 1.0) + discard; + + outColor = texture(colorSampler, inUV) * color; +} diff --git a/engine/shaders/billboard.vert.glsl b/engine/shaders/billboard.vert.glsl new file mode 100644 index 0000000..da8fffc --- /dev/null +++ b/engine/shaders/billboard.vert.glsl @@ -0,0 +1,25 @@ +layout (location = 0) out vec2 outUV; + +layout(push_constant, binding = 1) uniform readonly PushConstant{ + mat4 mvp; + vec4 color; +}; + +layout(std430, binding = 3) buffer readonly SceneInformation { + mat4 view; +}; + +void main() { + vec2 p = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + outUV = vec2(p.x, 1.0 - p.y); + + p = p * 2.0f + -1.0f; + + const vec3 right = {view[0][0], view[1][0], view[2][0]}; + const vec3 up = {view[0][1], view[1][1], view[2][1]}; + + vec3 position = right * p.x * 0.25 + up * p.y * 0.25; + + gl_Position = mvp * vec4(position, 1.0); +} + diff --git a/engine/shaders/blend.frag.glsl b/engine/shaders/blend.frag.glsl new file mode 100644 index 0000000..0c9b477 --- /dev/null +++ b/engine/shaders/blend.frag.glsl @@ -0,0 +1,16 @@ +#define SMAA_INCLUDE_PS 1 +#include "smaa_common.glsl" + +layout(location = 0) in vec2 inUV; +layout(location = 1) in vec4 inOffset[3]; +layout(location = 5) in vec2 inPixUV; + +layout(location = 0) out vec4 outColor; + +layout(binding = 0) uniform sampler2D edgeSampler; +layout(binding = 1) uniform sampler2D areaSampler; +layout(binding = 3) uniform sampler2D searchSampler; + +void main() { + outColor = SMAABlendingWeightCalculationPS(inUV, inPixUV, inOffset, edgeSampler, areaSampler, searchSampler, ivec4(0)); +} diff --git a/engine/shaders/blend.vert.glsl b/engine/shaders/blend.vert.glsl new file mode 100644 index 0000000..076c6b6 --- /dev/null +++ b/engine/shaders/blend.vert.glsl @@ -0,0 +1,14 @@ +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 +#include "smaa_common.glsl" + +layout(location = 0) out vec2 outUV; +layout(location = 1) out vec4 outOffset[3]; +layout(location = 5) out vec2 outPixUV; + +void main() { + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = correctionMatrix * vec4(outUV * 2.0 + -1.0, 0.0, 1.0); + + SMAABlendingWeightCalculationVS(outUV, outPixUV, outOffset); +} diff --git a/engine/shaders/brdf.frag.glsl b/engine/shaders/brdf.frag.glsl new file mode 100644 index 0000000..2a7e841 --- /dev/null +++ b/engine/shaders/brdf.frag.glsl @@ -0,0 +1,48 @@ +#include "common.nocompile.glsl" + +layout(location = 0) in vec2 inUV; + +layout(location = 0) out vec2 outColor; + +vec2 IntegrateBRDF(float NdotV, float roughness) { + vec3 V; + V.x = sqrt(1.0 - NdotV*NdotV); + V.y = 1.0; + V.z = NdotV; + + float A = 0.0; + float B = 0.0; + + const vec3 N = vec3(0.0, 0.0, 1.0); + + const uint SAMPLE_COUNT = 1024u; + for(uint i = 0u; i < SAMPLE_COUNT; ++i) { + // generates a sample vector that's biased towards the + // preferred alignment direction (importance sampling). + const vec2 Xi = hammersley(i, SAMPLE_COUNT); + const vec3 H = importance_sample_ggx(Xi, N, roughness); + const vec3 L = 2.0 * dot(V, H) * H - V; + + const float NdotL = max(L.z, 0.0); + const float NdotH = max(H.z, 0.0); + const float VdotH = max(dot(V, H), 0.0); + + if(NdotL > 0.0) { + const float G = geometry_smith(N, V, L, roughness); + const float G_Vis = (G * VdotH) / (NdotH * NdotV); + const float Fc = pow(1.0 - VdotH, 5.0); + + A += (1.0 - Fc) * G_Vis; + B += Fc * G_Vis; + } + } + + A /= float(SAMPLE_COUNT); + B /= float(SAMPLE_COUNT); + + return vec2(A, B); +} + +void main() { + outColor = IntegrateBRDF(inUV.x, inUV.y); +} diff --git a/engine/shaders/brdf.vert.glsl b/engine/shaders/brdf.vert.glsl new file mode 100644 index 0000000..113c0d4 --- /dev/null +++ b/engine/shaders/brdf.vert.glsl @@ -0,0 +1,6 @@ +layout(location = 0) out vec2 outUV; + +void main() { + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0 + -1.0, 0.0, 1.0); +} diff --git a/engine/shaders/color.frag.glsl b/engine/shaders/color.frag.glsl new file mode 100644 index 0000000..9cfa7ba --- /dev/null +++ b/engine/shaders/color.frag.glsl @@ -0,0 +1,10 @@ +layout (location = 0) out vec4 outColor; + +layout(push_constant, binding = 1) uniform PushConstant{ + mat4 mvp; + vec4 color; +}; + +void main() { + outColor = color; +} diff --git a/engine/shaders/color.vert.glsl b/engine/shaders/color.vert.glsl new file mode 100644 index 0000000..ad0b093 --- /dev/null +++ b/engine/shaders/color.vert.glsl @@ -0,0 +1,10 @@ +layout (location = 0) in vec3 inPosition; + +layout(push_constant, binding = 1) uniform PushConstant{ + mat4 mvp; + vec4 color; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); +} diff --git a/engine/shaders/common.nocompile.glsl b/engine/shaders/common.nocompile.glsl new file mode 100644 index 0000000..68129c5 --- /dev/null +++ b/engine/shaders/common.nocompile.glsl @@ -0,0 +1,161 @@ +const float PI = 3.14159265359; +const float EPSILON = 0.005; + +const vec2 PoissonOffsets[64] = { + vec2(0.0617981, 0.07294159), + vec2(0.6470215, 0.7474022), + vec2(-0.5987766, -0.7512833), + vec2(-0.693034, 0.6913887), + vec2(0.6987045, -0.6843052), + vec2(-0.9402866, 0.04474335), + vec2(0.8934509, 0.07369385), + vec2(0.1592735, -0.9686295), + vec2(-0.05664673, 0.995282), + vec2(-0.1203411, -0.1301079), + vec2(0.1741608, -0.1682285), + vec2(-0.09369049, 0.3196758), + vec2(0.185363, 0.3213367), + vec2(-0.1493771, -0.3147511), + vec2(0.4452095, 0.2580113), + vec2(-0.1080467, -0.5329178), + vec2(0.1604507, 0.5460774), + vec2(-0.4037193, -0.2611179), + vec2(0.5947998, -0.2146744), + vec2(0.3276062, 0.9244621), + vec2(-0.6518704, -0.2503952), + vec2(-0.3580975, 0.2806469), + vec2(0.8587891, 0.4838005), + vec2(-0.1596546, -0.8791054), + vec2(-0.3096867, 0.5588146), + vec2(-0.5128918, 0.1448544), + vec2(0.8581337, -0.424046), + vec2(0.1562584, -0.5610626), + vec2(-0.7647934, 0.2709858), + vec2(-0.3090832, 0.9020988), + vec2(0.3935608, 0.4609676), + vec2(0.3929337, -0.5010948), + vec2(-0.8682281, -0.1990303), + vec2(-0.01973724, 0.6478714), + vec2(-0.3897587, -0.4665619), + vec2(-0.7416366, -0.4377831), + vec2(-0.5523247, 0.4272514), + vec2(-0.5325066, 0.8410385), + vec2(0.3085465, -0.7842533), + vec2(0.8400612, -0.200119), + vec2(0.6632416, 0.3067062), + vec2(-0.4462856, -0.04265022), + vec2(0.06892014, 0.812484), + vec2(0.5149567, -0.7502338), + vec2(0.6464897, -0.4666451), + vec2(-0.159861, 0.1038342), + vec2(0.6455986, 0.04419327), + vec2(-0.7445076, 0.5035095), + vec2(0.9430245, 0.3139912), + vec2(0.0349884, -0.7968109), + vec2(-0.9517487, 0.2963554), + vec2(-0.7304786, -0.01006928), + vec2(-0.5862702, -0.5531025), + vec2(0.3029106, 0.09497032), + vec2(0.09025345, -0.3503742), + vec2(0.4356628, -0.0710125), + vec2(0.4112572, 0.7500054), + vec2(0.3401214, -0.3047142), + vec2(-0.2192158, -0.6911137), + vec2(-0.4676369, 0.6570358), + vec2(0.6295372, 0.5629555), + vec2(0.1253822, 0.9892166), + vec2(-0.1154335, 0.8248222), + vec2(-0.4230408, -0.7129914) +}; + +// GGX/Trowbridge-Reitz Normal Distribution +float ggx_distribution(const vec3 N, const vec3 H, const float roughness) { + const float roughness_squared = roughness * roughness; + + const float NdotH = dot(N, H); + + const float denominator = (NdotH * NdotH) * (roughness_squared - 1.0) + 1.0; + + return roughness_squared / (PI * (denominator * denominator)); +} + +// Slick Geometry +float geometry_slick_direct(const vec3 N, const vec3 V, const float roughness) { + const float NdotV = clamp(dot(N, V), 0.0, 1.0); + const float denominator = NdotV * (1.0 - roughness) + roughness; + + return NdotV / denominator; +} + +// GGX Smith Geometry, using GGX slick but combining both the view direction and the light direction +float geometry_smith(const vec3 N, const vec3 V, const vec3 L, float roughness) { + const float dotNV = max(dot(N, V), 0.0); + const float dotNL = max(dot(N, L), 0.0); + + const float k = (roughness * roughness) / 2.0; + const float GL = dotNL / (dotNL * (1.0 - k) + k); + const float GV = dotNV / (dotNV * (1.0 - k) + k); + + return GL * GV; +} + +// Fresnel Shlick +vec3 fresnel_schlick(const float cos_theta, const vec3 F0) { + return F0 + (1.0 - F0) * pow(1.0 - cos_theta, 5.0); +} + +vec3 fresnel_schlick_roughness(float cosTheta, vec3 F0, float roughness) { + return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); +} + +// efficient VanDerCorpus calculation from http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html +float radical_inverse(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + + return float(bits) * 2.3283064365386963e-10; +} + +vec2 hammersley(const uint i, const uint N) { + return vec2(float(i) / float(N), radical_inverse(i)); +} + +vec3 importance_sample_ggx(const vec2 Xi, const vec3 N, const float roughness) { + const float a = roughness*roughness; + + const float phi = 2.0 * PI * Xi.x; + const float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y)); + const float sinTheta = sqrt(1.0 - cosTheta*cosTheta); + + // from spherical coordinates to cartesian coordinates - halfway vector + vec3 H; + H.x = cos(phi) * sinTheta; + H.y = sin(phi) * sinTheta; + H.z = cosTheta; + + // from tangent-space H vector to world-space sample vector + const vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + const vec3 tangent = normalize(cross(up, N)); + const vec3 bitangent = cross(N, tangent); + + return normalize(tangent * H.x + bitangent * H.y + N * H.z); +} + +vec3 from_linear_to_srgb(const vec3 linearRGB) { + bvec3 cutoff = lessThan(linearRGB, vec3(0.0031308)); + vec3 higher = vec3(1.055)*pow(linearRGB, vec3(1.0/2.4)) - vec3(0.055); + vec3 lower = linearRGB * vec3(12.92); + + return mix(higher, lower, cutoff); +} + +vec3 from_srgb_to_linear(const vec3 sRGB) { + bvec3 cutoff = lessThan(sRGB, vec3(0.04045)); + vec3 higher = pow((sRGB + vec3(0.055))/vec3(1.055), vec3(2.4)); + vec3 lower = sRGB/vec3(12.92); + + return mix(higher, lower, cutoff); +} diff --git a/engine/shaders/debug.frag.glsl b/engine/shaders/debug.frag.glsl new file mode 100644 index 0000000..157b36b --- /dev/null +++ b/engine/shaders/debug.frag.glsl @@ -0,0 +1,10 @@ +layout(location = 0) out vec4 outColor; + +layout(push_constant, binding = 1) uniform PushConstant { + mat4 mvp; + vec4 color; +}; + +void main() { + outColor = color; +} diff --git a/engine/shaders/debug.vert.glsl b/engine/shaders/debug.vert.glsl new file mode 100644 index 0000000..ad0b093 --- /dev/null +++ b/engine/shaders/debug.vert.glsl @@ -0,0 +1,10 @@ +layout (location = 0) in vec3 inPosition; + +layout(push_constant, binding = 1) uniform PushConstant{ + mat4 mvp; + vec4 color; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); +} diff --git a/engine/shaders/dof.frag.glsl b/engine/shaders/dof.frag.glsl new file mode 100644 index 0000000..e147b5a --- /dev/null +++ b/engine/shaders/dof.frag.glsl @@ -0,0 +1,28 @@ +layout(location = 0) in vec2 inUV; +layout(location = 1) in flat ivec2 inPixel; +layout(location = 2) in float depth; + +layout(location = 0) out vec4 outColor; + +layout(rgba32f, binding = 0) uniform image2D color_sampler; +layout(binding = 3) uniform sampler2D aperture_sampler; + +layout(push_constant, binding = 2) uniform readonly PushConstant{ + vec4 params; +}; + +void main() { + // far field mode + if(params.y == 0) { + if(depth < 0.98) + discard; + } + + if(inUV.y > 1.0 || inUV.x > 1.0) + discard; + + outColor = vec4(imageLoad(color_sampler, inPixel).rgb, 1.0); + if(params.y == 0) { + outColor = outColor * texture(aperture_sampler, inUV); + } +} diff --git a/engine/shaders/dof.vert.glsl b/engine/shaders/dof.vert.glsl new file mode 100644 index 0000000..8024370 --- /dev/null +++ b/engine/shaders/dof.vert.glsl @@ -0,0 +1,35 @@ +layout(constant_id = 0) const int width = 25; +layout(constant_id = 1) const int height = 25; + +layout(location = 0) out vec2 outUV; +layout(location = 1) out flat ivec2 outPixel; +layout(location = 2) out float outDepth; + +layout(binding = 1) uniform sampler2D depth_sampler; + +layout(push_constant, binding = 2) uniform readonly PushConstant{ + vec4 params; +}; + +void main() { + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + + ivec2 pixel = ivec2(gl_InstanceIndex % width, gl_InstanceIndex / width); + outPixel = pixel; + + const float depth = texture(depth_sampler, vec2(pixel) / vec2(width, height)).r; + outDepth = depth; + + vec2 pos = vec2(outUV * 2.0 + -1.0); + + // far field mode + if(params.y == 0) { + pos *= params.x * depth; + } + + pos += vec2(pixel.x, pixel.y); + pos *= 2.0 / vec2(width, height); + pos += vec2(-1, -1); + + gl_Position = vec4(pos.x, pos.y, 0.0, 1.0); +} diff --git a/engine/shaders/edge.frag.glsl b/engine/shaders/edge.frag.glsl new file mode 100644 index 0000000..5f58c5f --- /dev/null +++ b/engine/shaders/edge.frag.glsl @@ -0,0 +1,16 @@ +#define SMAA_INCLUDE_PS 1 +#include "smaa_common.glsl" + +layout(location = 0) in vec2 inUV; +layout(location = 1) in vec4 inOffset[3]; + +layout(location = 0) out vec4 outColor; + +layout(binding = 0) uniform sampler2D imageSampler; +layout(binding = 1) uniform sampler2D depthSampler; + +void main() { + vec2 edge = SMAALumaEdgeDetectionPS(inUV, inOffset, imageSampler, depthSampler); + + outColor = vec4(edge, 0.0, 1.0); +} diff --git a/engine/shaders/edge.vert.glsl b/engine/shaders/edge.vert.glsl new file mode 100644 index 0000000..57b3973 --- /dev/null +++ b/engine/shaders/edge.vert.glsl @@ -0,0 +1,13 @@ +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 +#include "smaa_common.glsl" + +layout(location = 0) out vec2 outUV; +layout(location = 1) out vec4 outOffset[3]; + +void main() { + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = correctionMatrix * vec4(outUV * 2.0 + -1.0, 0.0, 1.0); + + SMAAEdgeDetectionVS(outUV, outOffset); +} diff --git a/engine/shaders/filter.frag.glsl b/engine/shaders/filter.frag.glsl new file mode 100644 index 0000000..37241a3 --- /dev/null +++ b/engine/shaders/filter.frag.glsl @@ -0,0 +1,51 @@ +#include "common.nocompile.glsl" + +layout (constant_id = 0) const int texture_size = 512; + +layout(location = 0) in vec3 inPos; +layout(location = 0) out vec4 outColor; + +layout(binding = 2) uniform samplerCube environmentSampler; + +layout(push_constant, binding = 1) uniform readonly PushConstant{ + mat4 mvp; + float roughness; +}; + +void main() { + vec3 N = normalize(inPos); + + // make the simplyfying assumption that V equals R equals the normal + const vec3 R = N; + const vec3 V = R; + + const uint SAMPLE_COUNT = 1024u; + vec3 prefilteredColor = vec3(0.0); + float totalWeight = 0.0; + + for(uint i = 0u; i < SAMPLE_COUNT; ++i) { + // generates a sample vector that's biased towards the preferred alignment direction (importance sampling). + const vec2 Xi = hammersley(i, SAMPLE_COUNT); + const vec3 H = importance_sample_ggx(Xi, N, roughness); + const vec3 L = normalize(2.0 * dot(V, H) * H - V); + + float NdotL = max(dot(N, L), 0.0); + if(NdotL > 0.0) { + // sample from the environment's mip level based on roughness/pdf + const float D = geometry_slick_direct(N, H, roughness); + const float NdotH = max(dot(N, H), 0.0); + const float HdotV = max(dot(H, V), 0.0); + const float pdf = D * NdotH / (4.0 * HdotV) + 0.0001; + + const float saTexel = 4.0 * PI / (6.0 * texture_size * texture_size); + const float saSample = 1.0 / (float(SAMPLE_COUNT) * pdf + 0.0001); + + const float mipLevel = roughness == 0.0 ? 0.0 : 0.5 * log2(saSample / saTexel); + + prefilteredColor += textureLod(environmentSampler, L, mipLevel).rgb * NdotL; + totalWeight += NdotL; + } + } + + outColor = vec4(prefilteredColor / totalWeight, 1.0); +} diff --git a/engine/shaders/filter.vert.glsl b/engine/shaders/filter.vert.glsl new file mode 100644 index 0000000..d213b6d --- /dev/null +++ b/engine/shaders/filter.vert.glsl @@ -0,0 +1,13 @@ +layout(location = 0) in vec3 inPosition; + +layout(location = 0) out vec3 outPos; + +layout(push_constant, binding = 1) uniform readonly PushConstant{ + mat4 mvp; + float roughness; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); + outPos = inPosition; +} diff --git a/engine/shaders/font.glsl b/engine/shaders/font.glsl new file mode 100644 index 0000000..dbe02fa --- /dev/null +++ b/engine/shaders/font.glsl @@ -0,0 +1,5 @@ +uint getLower(const uint val) { return val & uint(0xFFFF); } +uint getUpper(const uint val) { return val >> 16 & uint(0xFFFF); } +float fixed_to_float(const uint val) { return float(val)/32.0; } +vec2 fixed2_to_vec2(const uint val) { return vec2(fixed_to_float(getLower(val)), fixed_to_float(getUpper(val))); } +vec2 uint_to_vec2(const uint val) { return vec2(getLower(val), getUpper(val)); } diff --git a/engine/shaders/gaussian.frag.glsl b/engine/shaders/gaussian.frag.glsl new file mode 100644 index 0000000..7390260 --- /dev/null +++ b/engine/shaders/gaussian.frag.glsl @@ -0,0 +1,34 @@ +layout (location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outColor; + +layout(push_constant, binding = 1) uniform PushConstants { + int horizontal; +}; + +layout (binding = 0) uniform sampler2D image; + +const float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); + +void main() { + vec2 tex_offset = 1.0 / textureSize(image, 0); // gets size of single texel + vec3 result = texture(image, inUV).rgb * weight[0]; // current fragment's contribution + if(horizontal == 1) + { + for(int i = 1; i < 5; ++i) + { + result += max(texture(image, inUV + vec2(tex_offset.x * i, 0.0)).rgb * weight[i], vec3(0.0)); + result += max(texture(image, inUV - vec2(tex_offset.x * i, 0.0)).rgb * weight[i], vec3(0.0)); + } + } + else + { + for(int i = 1; i < 5; ++i) + { + result += max(texture(image, inUV + vec2(0.0, tex_offset.y * i)).rgb * weight[i], vec3(0.0)); + result += max(texture(image, inUV - vec2(0.0, tex_offset.y * i)).rgb * weight[i], vec3(0.0)); + } + } + + outColor = vec4(result, 1.0); +} diff --git a/engine/shaders/gaussian.vert.glsl b/engine/shaders/gaussian.vert.glsl new file mode 100644 index 0000000..71efeb2 --- /dev/null +++ b/engine/shaders/gaussian.vert.glsl @@ -0,0 +1,6 @@ +layout (location = 0) out vec2 outUV; + +void main() { + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); +} diff --git a/engine/shaders/histogram-average.comp.glsl b/engine/shaders/histogram-average.comp.glsl new file mode 100644 index 0000000..0531c8a --- /dev/null +++ b/engine/shaders/histogram-average.comp.glsl @@ -0,0 +1,44 @@ +#include "common.nocompile.glsl" + +layout(local_size_x = 256, local_size_y = 1) in; + +layout(r16f, binding = 0) uniform image2D target_image; + +// adapated from https://bruop.github.io/exposure/ and http://www.alextardif.com/HistogramLuminance.html + +shared uint histogram_shared[256]; + +layout(std430, binding = 1) buffer HistogramBuffer { + uint histogram[]; +}; + +layout(push_constant, binding = 2) uniform readonly PushConstant{ + vec4 params; +}; + +void main() { + uint count_for_this_bin = histogram[gl_LocalInvocationIndex]; + histogram_shared[gl_LocalInvocationIndex] = count_for_this_bin * gl_LocalInvocationIndex; + + groupMemoryBarrier(); + + histogram[gl_LocalInvocationIndex] = 0; + + for(uint cutoff = (256 >> 1); cutoff > 0; cutoff >>= 1) { + if(uint(gl_LocalInvocationIndex) < cutoff) { + histogram_shared[gl_LocalInvocationIndex] += histogram_shared[gl_LocalInvocationIndex + cutoff]; + } + + groupMemoryBarrier(); + } + + if(gl_LocalInvocationIndex == 0) { + float weightedLogAverage = (histogram_shared[0] / max(params.w - float(count_for_this_bin), 1.0)) - 1.0; + + float weightedAvgLum = exp2(weightedLogAverage / 254.0 * params.y + params.x); + + float lumLastFrame = imageLoad(target_image, ivec2(0, 0)).x; + float adaptedLum = lumLastFrame + (weightedAvgLum - lumLastFrame) * params.z; + imageStore(target_image, ivec2(0, 0), vec4(adaptedLum, 0.0, 0.0, 0.0)); + } +} diff --git a/engine/shaders/histogram.comp.glsl b/engine/shaders/histogram.comp.glsl new file mode 100644 index 0000000..abeff53 --- /dev/null +++ b/engine/shaders/histogram.comp.glsl @@ -0,0 +1,49 @@ +#include "common.nocompile.glsl" + +layout(local_size_x = 16, local_size_y = 16) in; + +layout(rgba32f, binding = 0) uniform image2D hdr_image; + +// adapated from https://bruop.github.io/exposure/ and http://www.alextardif.com/HistogramLuminance.html + +// Taken from RTR vol 4 pg. 278 +#define RGB_TO_LUM vec3(0.2125, 0.7154, 0.0721) + +shared uint histogram_shared[256]; + +layout(std430, binding = 1) buffer HistogramBuffer { + uint histogram[]; +}; + +layout(push_constant, binding = 2) uniform readonly PushConstant{ + vec4 params; +}; + +uint color_to_bin(const vec3 hdr_color, const float min_log_lum, const float inverse_log_lum_range) { + const float lum = dot(hdr_color, RGB_TO_LUM); + + if (lum < EPSILON) { + return 0; + } + + const float log_lum = clamp((log2(lum) - min_log_lum) * inverse_log_lum_range, 0.0, 1.0); + + return uint(log_lum * 254.0 + 1.0); +} + +void main() { + histogram_shared[gl_LocalInvocationIndex] = 0; + groupMemoryBarrier(); + + uvec2 dim = uvec2(params.zw); + if(gl_GlobalInvocationID.x < dim.x && gl_GlobalInvocationID.y < dim.y) { + vec3 hdr_color = imageLoad(hdr_image, ivec2(gl_GlobalInvocationID.xy)).rgb; + uint bin_index = color_to_bin(hdr_color, params.x, params.y); + + atomicAdd(histogram_shared[bin_index], 1); + } + + groupMemoryBarrier(); + + atomicAdd(histogram[gl_LocalInvocationIndex], histogram_shared[gl_LocalInvocationIndex]); +} diff --git a/engine/shaders/imgui.frag.glsl b/engine/shaders/imgui.frag.glsl new file mode 100644 index 0000000..3db777c --- /dev/null +++ b/engine/shaders/imgui.frag.glsl @@ -0,0 +1,10 @@ +layout(location = 0) in vec4 in_color; +layout(location = 1) in vec2 in_uv; + +layout(location = 0) out vec4 out_color; + +layout(binding = 2) uniform sampler2D bound_texture; + +void main() { + out_color = in_color * texture(bound_texture, in_uv); +} diff --git a/engine/shaders/imgui.ini b/engine/shaders/imgui.ini new file mode 100644 index 0000000..5b93529 --- /dev/null +++ b/engine/shaders/imgui.ini @@ -0,0 +1,883 @@ +[Window][dockspace] +Pos=0,0 +Size=1269,856 +Collapsed=0 + +[Window][Debug##Default] +Pos=60,60 +Size=400,400 +Collapsed=0 + +[Window][General] +Pos=572,328 +Size=118,78 +Collapsed=0 + +[Window][Lighting] +Pos=388,328 +Size=316,282 +Collapsed=0 + +[Window][Assets] +Pos=58,328 +Size=264,269 +Collapsed=0 + +[Window][Rendering] +Pos=742,163 +Size=391,675 +Collapsed=0 + +[Window][Shader Editor] +Pos=314,328 +Size=594,516 +Collapsed=0 + +[Window][###3399662586] +Pos=0,23 +Size=2560,1354 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2188575507216] +Pos=0,69 +Size=706,1308 +Collapsed=0 +DockId=0x00000001,0 + +[Window][Node Editor##2188575507216] +Pos=708,69 +Size=1852,1308 +Collapsed=0 +DockId=0x00000002,0 + +[Window][###2131036754] +Pos=0,23 +Size=1403,1001 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2166618067536] +Pos=0,69 +Size=573,955 +Collapsed=0 +DockId=0x00000003,0 + +[Window][Node Editor##2166618067536] +Pos=575,69 +Size=828,955 +Collapsed=0 +DockId=0x00000004,0 + +[Window][###640863982] +Pos=0,23 +Size=2325,1206 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1963238371632] +Pos=0,69 +Size=1071,1160 +Collapsed=0 +DockId=0x00000005,0 + +[Window][Node Editor##1963238371632] +Pos=1073,69 +Size=1252,1160 +Collapsed=0 +DockId=0x00000006,0 + +[Window][###3475923717] +Pos=0,23 +Size=2309,1167 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1954532842000] +Pos=0,69 +Size=939,1121 +Collapsed=0 +DockId=0x00000007,0 + +[Window][Node Editor##1954532842000] +Pos=941,69 +Size=1368,1121 +Collapsed=0 +DockId=0x00000008,0 + +[Window][###4219310467] +Pos=0,23 +Size=1321,961 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##3026265820288] +Pos=0,69 +Size=550,915 +Collapsed=0 +DockId=0x00000009,0 + +[Window][Node Editor##3026265820288] +Pos=552,69 +Size=769,915 +Collapsed=0 +DockId=0x0000000A,0 + +[Window][Viewport Debug] +Pos=54,159 +Size=276,213 +Collapsed=0 + +[Window][###1639542653] +Pos=0,23 +Size=1305,922 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2457333664704] +Pos=0,69 +Size=390,876 +Collapsed=0 +DockId=0x0000000B,0 + +[Window][Node Editor##2457333664704] +Pos=392,69 +Size=913,876 +Collapsed=0 +DockId=0x0000000C,0 + +[Window][###7867686] +Pos=0,23 +Size=1289,883 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1728288164608] +Pos=0,69 +Size=514,837 +Collapsed=0 +DockId=0x0000000D,0 + +[Window][Node Editor##1728288164608] +Pos=516,69 +Size=773,837 +Collapsed=0 +DockId=0x0000000E,0 + +[Window][###80130164] +Pos=0,23 +Size=1273,844 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2421556308144] +Pos=0,69 +Size=546,798 +Collapsed=0 +DockId=0x0000000F,0 + +[Window][Node Editor##2421556308144] +Pos=548,69 +Size=725,798 +Collapsed=0 +DockId=0x00000010,0 + +[Window][###1380425459] +Pos=0,23 +Size=1257,805 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2882527943952] +Pos=0,69 +Size=527,759 +Collapsed=0 +DockId=0x00000011,0 + +[Window][Node Editor##2882527943952] +Pos=529,69 +Size=728,759 +Collapsed=0 +DockId=0x00000012,0 + +[Window][###15550599] +Pos=0,23 +Size=1241,766 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2140943368864] +Pos=0,69 +Size=470,720 +Collapsed=0 +DockId=0x00000013,0 + +[Window][Node Editor##2140943368864] +Pos=472,69 +Size=769,720 +Collapsed=0 +DockId=0x00000014,0 + +[Window][###855862377] +Pos=0,23 +Size=1225,727 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1960826238912] +Pos=0,69 +Size=529,681 +Collapsed=0 +DockId=0x00000015,0 + +[Window][Node Editor##1960826238912] +Pos=531,69 +Size=694,681 +Collapsed=0 +DockId=0x00000016,0 + +[Window][###1366933659] +Pos=0,23 +Size=1209,688 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2043695810320] +Pos=0,69 +Size=514,642 +Collapsed=0 +DockId=0x00000017,0 + +[Window][Node Editor##2043695810320] +Pos=516,69 +Size=693,642 +Collapsed=0 +DockId=0x00000018,0 + +[Window][###3514825541] +Pos=0,23 +Size=1193,649 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2749126682304] +Pos=0,69 +Size=502,603 +Collapsed=0 +DockId=0x00000019,0 + +[Window][Node Editor##2749126682304] +Pos=504,69 +Size=689,603 +Collapsed=0 +DockId=0x0000001A,0 + +[Window][###1825763505] +Pos=0,23 +Size=1177,610 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2223184439600] +Pos=0,69 +Size=470,564 +Collapsed=0 +DockId=0x0000001B,0 + +[Window][Node Editor##2223184439600] +Pos=472,69 +Size=705,564 +Collapsed=0 +DockId=0x0000001C,0 + +[Window][###3315743509] +Pos=0,23 +Size=1177,610 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1366031075664] +Pos=0,69 +Size=470,564 +Collapsed=0 +DockId=0x0000001D,0 + +[Window][Node Editor##1366031075664] +Pos=472,69 +Size=705,564 +Collapsed=0 +DockId=0x0000001E,0 + +[Window][###3922641702] +Pos=0,23 +Size=1177,610 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2619500768784] +Pos=0,69 +Size=470,564 +Collapsed=0 +DockId=0x0000001F,0 + +[Window][Node Editor##2619500768784] +Pos=472,69 +Size=705,564 +Collapsed=0 +DockId=0x00000020,0 + +[Window][###2825189723] +Pos=0,23 +Size=1177,610 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2036095658144] +Pos=0,69 +Size=470,564 +Collapsed=0 +DockId=0x00000021,0 + +[Window][Node Editor##2036095658144] +Pos=472,69 +Size=705,564 +Collapsed=0 +DockId=0x00000022,0 + +[Window][###1934464755] +Pos=0,23 +Size=1177,610 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2537240157504] +Pos=0,69 +Size=507,564 +Collapsed=0 +DockId=0x00000023,0 + +[Window][Node Editor##2537240157504] +Pos=509,69 +Size=668,564 +Collapsed=0 +DockId=0x00000024,0 + +[Window][###1411417784] +Pos=0,23 +Size=1177,610 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1683547285216] +Pos=0,69 +Size=492,564 +Collapsed=0 +DockId=0x00000025,0 + +[Window][Node Editor##1683547285216] +Pos=494,69 +Size=683,564 +Collapsed=0 +DockId=0x00000026,0 + +[Window][###2587986607] +Pos=0,23 +Size=1161,571 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1897204863728] +Pos=0,69 +Size=468,525 +Collapsed=0 +DockId=0x00000027,0 + +[Window][Node Editor##1897204863728] +Pos=470,69 +Size=691,525 +Collapsed=0 +DockId=0x00000028,0 + +[Window][###3653812825] +Pos=0,23 +Size=1161,571 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2613249096368] +Pos=0,69 +Size=474,525 +Collapsed=0 +DockId=0x00000029,0 + +[Window][Node Editor##2613249096368] +Pos=476,69 +Size=685,525 +Collapsed=0 +DockId=0x0000002A,0 + +[Window][###77913483] +Pos=0,23 +Size=1161,571 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2302295717920] +Pos=0,69 +Size=428,525 +Collapsed=0 +DockId=0x0000002B,0 + +[Window][Node Editor##2302295717920] +Pos=430,69 +Size=731,525 +Collapsed=0 +DockId=0x0000002C,0 + +[Window][###721801312] +Pos=0,23 +Size=1145,532 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2037941513520] +Pos=0,69 +Size=457,486 +Collapsed=0 +DockId=0x0000002D,0 + +[Window][Node Editor##2037941513520] +Pos=459,69 +Size=686,486 +Collapsed=0 +DockId=0x0000002E,0 + +[Window][###1412428632] +Pos=0,23 +Size=1129,493 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1503006543840] +Pos=0,69 +Size=450,447 +Collapsed=0 +DockId=0x0000002F,0 + +[Window][Node Editor##1503006543840] +Pos=452,69 +Size=677,447 +Collapsed=0 +DockId=0x00000030,0 + +[Window][###3228816453] +Pos=0,23 +Size=1113,454 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2001252665648] +Pos=0,69 +Size=444,408 +Collapsed=0 +DockId=0x00000031,0 + +[Window][Node Editor##2001252665648] +Pos=446,69 +Size=667,408 +Collapsed=0 +DockId=0x00000032,0 + +[Window][###1653928366] +Pos=0,23 +Size=1097,415 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1816252674560] +Pos=0,69 +Size=438,369 +Collapsed=0 +DockId=0x00000033,0 + +[Window][Node Editor##1816252674560] +Pos=440,69 +Size=657,369 +Collapsed=0 +DockId=0x00000034,0 + +[Window][###2684603124] +Pos=0,23 +Size=1081,376 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1659712093824] +Pos=0,69 +Size=431,330 +Collapsed=0 +DockId=0x00000035,0 + +[Window][Node Editor##1659712093824] +Pos=433,69 +Size=648,330 +Collapsed=0 +DockId=0x00000036,0 + +[Window][###1858491124] +Pos=0,23 +Size=1257,753 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2084309375392] +Pos=0,69 +Size=570,707 +Collapsed=0 +DockId=0x00000037,0 + +[Window][Node Editor##2084309375392] +Pos=572,69 +Size=685,707 +Collapsed=0 +DockId=0x00000038,0 + +[Window][###64184015] +Pos=0,23 +Size=1241,714 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2640759981280] +Pos=0,69 +Size=495,668 +Collapsed=0 +DockId=0x00000039,0 + +[Window][Node Editor##2640759981280] +Pos=497,69 +Size=744,668 +Collapsed=0 +DockId=0x0000003A,0 + +[Window][###3217874280] +Pos=0,23 +Size=1225,675 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2222288851680] +Pos=0,69 +Size=489,629 +Collapsed=0 +DockId=0x0000003B,0 + +[Window][Node Editor##2222288851680] +Pos=491,69 +Size=734,629 +Collapsed=0 +DockId=0x0000003C,0 + +[Window][###3475910530] +Pos=0,23 +Size=1209,636 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2396817209888] +Pos=0,69 +Size=482,590 +Collapsed=0 +DockId=0x0000003D,0 + +[Window][Node Editor##2396817209888] +Pos=484,69 +Size=725,590 +Collapsed=0 +DockId=0x0000003E,0 + +[Window][###1340129530] +Pos=0,23 +Size=1193,597 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2450858409376] +Pos=0,69 +Size=476,551 +Collapsed=0 +DockId=0x0000003F,0 + +[Window][Node Editor##2450858409376] +Pos=478,69 +Size=715,551 +Collapsed=0 +DockId=0x00000040,0 + +[Window][###2143743140] +Pos=0,23 +Size=1177,558 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1798101048720] +Pos=0,69 +Size=470,512 +Collapsed=0 +DockId=0x00000041,0 + +[Window][Node Editor##1798101048720] +Pos=472,69 +Size=705,512 +Collapsed=0 +DockId=0x00000042,0 + +[Window][###2177287079] +Pos=0,23 +Size=1161,519 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2295890153920] +Pos=0,69 +Size=463,473 +Collapsed=0 +DockId=0x00000043,0 + +[Window][Node Editor##2295890153920] +Pos=465,69 +Size=696,473 +Collapsed=0 +DockId=0x00000044,0 + +[Window][###3232862005] +Pos=0,23 +Size=1145,480 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2628918357312] +Pos=0,69 +Size=457,434 +Collapsed=0 +DockId=0x00000045,0 + +[Window][Node Editor##2628918357312] +Pos=459,69 +Size=686,434 +Collapsed=0 +DockId=0x00000046,0 + +[Window][###2059956889] +Pos=0,23 +Size=1129,441 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2715707453056] +Pos=0,69 +Size=450,395 +Collapsed=0 +DockId=0x00000047,0 + +[Window][Node Editor##2715707453056] +Pos=452,69 +Size=677,395 +Collapsed=0 +DockId=0x00000048,0 + +[Window][###2130128028] +Pos=0,23 +Size=1113,402 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2672087001216] +Pos=0,69 +Size=444,356 +Collapsed=0 +DockId=0x00000049,0 + +[Window][Node Editor##2672087001216] +Pos=446,69 +Size=667,356 +Collapsed=0 +DockId=0x0000004A,0 + +[Window][###2294877407] +Pos=0,23 +Size=1097,363 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##2232566216816] +Pos=0,69 +Size=438,317 +Collapsed=0 +DockId=0x0000004B,0 + +[Window][Node Editor##2232566216816] +Pos=440,69 +Size=657,317 +Collapsed=0 +DockId=0x0000004C,0 + +[Window][###631377610] +Pos=0,23 +Size=1098,641 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1450197369568] +Pos=0,69 +Size=438,595 +Collapsed=0 +DockId=0x0000004D,0 + +[Window][Node Editor##1450197369568] +Pos=440,69 +Size=658,595 +Collapsed=0 +DockId=0x0000004E,0 + +[Window][###2173537760] +Pos=0,23 +Size=1269,833 +Collapsed=0 +DockId=0xECD066A1,0 + +[Window][Viewport##1978914352432] +Pos=0,69 +Size=506,787 +Collapsed=0 +DockId=0x0000004F,0 + +[Window][Node Editor##1978914352432] +Pos=508,69 +Size=761,787 +Collapsed=0 +DockId=0x00000050,0 + +[Docking][Data] +DockSpace ID=0x00780D26 Pos=0,69 Size=1289,837 Split=X + DockNode ID=0x0000000D Parent=0x00780D26 SizeRef=514,906 Selected=0x784F3CEF + DockNode ID=0x0000000E Parent=0x00780D26 SizeRef=773,906 CentralNode=1 Selected=0xD281DAEE +DockSpace ID=0x00ED4887 Pos=0,69 Size=1241,720 Split=X + DockNode ID=0x00000013 Parent=0x00ED4887 SizeRef=470,789 Selected=0x544B780A + DockNode ID=0x00000014 Parent=0x00ED4887 SizeRef=769,789 CentralNode=1 Selected=0xFE859E0B +DockSpace ID=0x03D35ECF Pos=0,69 Size=1241,668 Split=X + DockNode ID=0x00000039 Parent=0x03D35ECF SizeRef=495,737 Selected=0x7711AB0B + DockNode ID=0x0000003A Parent=0x03D35ECF SizeRef=744,737 CentralNode=1 Selected=0xDDDF4D0A +DockSpace ID=0x04A4DD8B Pos=0,69 Size=1161,525 Split=X + DockNode ID=0x0000002B Parent=0x04A4DD8B SizeRef=428,594 Selected=0x65CD1E66 + DockNode ID=0x0000002C Parent=0x04A4DD8B SizeRef=731,594 CentralNode=1 Selected=0xCF03F867 +DockSpace ID=0x04C6B074 Pos=0,69 Size=1273,798 Split=X + DockNode ID=0x0000000F Parent=0x04C6B074 SizeRef=546,867 Selected=0x019EDEE7 + DockNode ID=0x00000010 Parent=0x04C6B074 SizeRef=725,867 CentralNode=1 Selected=0xAB5038E6 +DockSpace ID=0x25A20ECA Pos=0,69 Size=1098,595 Split=X + DockNode ID=0x0000004D Parent=0x25A20ECA SizeRef=438,664 Selected=0xDDEC97D3 + DockNode ID=0x0000004E Parent=0x25A20ECA SizeRef=658,664 CentralNode=1 Selected=0x772271D2 +DockSpace ID=0x2632CEEE Pos=0,69 Size=2325,1160 Split=X + DockNode ID=0x00000005 Parent=0x2632CEEE SizeRef=1071,1338 Selected=0x08A3B986 + DockNode ID=0x00000006 Parent=0x2632CEEE SizeRef=1471,1338 CentralNode=1 Selected=0xA26D5F87 +DockSpace ID=0x2B05D060 Pos=0,69 Size=1145,486 Split=X + DockNode ID=0x0000002D Parent=0x2B05D060 SizeRef=457,555 Selected=0x4B3701F4 + DockNode ID=0x0000002E Parent=0x2B05D060 SizeRef=686,555 CentralNode=1 Selected=0xE1F9E7F5 +DockSpace ID=0x33036C69 Pos=0,69 Size=1225,681 Split=X + DockNode ID=0x00000015 Parent=0x33036C69 SizeRef=529,750 Selected=0x9091EC88 + DockNode ID=0x00000016 Parent=0x33036C69 SizeRef=694,750 CentralNode=1 Selected=0x3A5F0A89 +DockSpace ID=0x4FE0C0FA Pos=0,69 Size=1193,551 Split=X + DockNode ID=0x0000003F Parent=0x4FE0C0FA SizeRef=476,620 Selected=0x05098D0C + DockNode ID=0x00000040 Parent=0x4FE0C0FA SizeRef=715,620 CentralNode=1 Selected=0xAFC76B0D +DockSpace ID=0x5179C09B Pos=0,69 Size=1209,642 Split=X + DockNode ID=0x00000017 Parent=0x5179C09B SizeRef=514,711 Selected=0x71B28A9C + DockNode ID=0x00000018 Parent=0x5179C09B SizeRef=693,711 CentralNode=1 Selected=0xDB7C6C9D +DockSpace ID=0x52479EF3 Pos=0,69 Size=1257,759 Split=X + DockNode ID=0x00000011 Parent=0x52479EF3 SizeRef=527,828 Selected=0xDCCFB527 + DockNode ID=0x00000012 Parent=0x52479EF3 SizeRef=728,828 CentralNode=1 Selected=0x76015326 +DockSpace ID=0x542086B8 Pos=0,69 Size=1177,564 Split=X + DockNode ID=0x00000025 Parent=0x542086B8 SizeRef=492,633 Selected=0x4491C4AC + DockNode ID=0x00000026 Parent=0x542086B8 SizeRef=683,633 CentralNode=1 Selected=0xEE5F22AD +DockSpace ID=0x542FF358 Pos=0,69 Size=1129,447 Split=X + DockNode ID=0x0000002F Parent=0x542FF358 SizeRef=450,516 Selected=0x857BE1DB + DockNode ID=0x00000030 Parent=0x542FF358 SizeRef=677,516 CentralNode=1 Selected=0x2FB507DA +DockSpace ID=0x61B96F7D Pos=0,69 Size=1305,876 Split=X + DockNode ID=0x0000000B Parent=0x61B96F7D SizeRef=390,945 Selected=0x374C8694 + DockNode ID=0x0000000C Parent=0x61B96F7D SizeRef=913,945 CentralNode=1 Selected=0x9D826095 +DockSpace ID=0x6294F1AE Pos=0,69 Size=1097,369 Split=X + DockNode ID=0x00000033 Parent=0x6294F1AE SizeRef=438,438 Selected=0xC8273756 + DockNode ID=0x00000034 Parent=0x6294F1AE SizeRef=657,438 CentralNode=1 Selected=0x62E9D157 +DockSpace ID=0x6CD2F0B1 Pos=0,69 Size=1177,564 Split=X + DockNode ID=0x0000001B Parent=0x6CD2F0B1 SizeRef=470,633 Selected=0x3F77C45B + DockNode ID=0x0000001C Parent=0x6CD2F0B1 SizeRef=705,633 CentralNode=1 Selected=0x95B9225A +DockSpace ID=0x6EC652F4 Pos=0,69 Size=1257,707 Split=X + DockNode ID=0x00000037 Parent=0x6EC652F4 SizeRef=570,360 Selected=0xD4AF7A84 + DockNode ID=0x00000038 Parent=0x6EC652F4 SizeRef=685,360 CentralNode=1 Selected=0x7E619C85 +DockSpace ID=0x734D96F3 Pos=0,69 Size=1177,564 Split=X + DockNode ID=0x00000023 Parent=0x734D96F3 SizeRef=507,633 Selected=0x239E3697 + DockNode ID=0x00000024 Parent=0x734D96F3 SizeRef=668,633 CentralNode=1 Selected=0x8950D096 +DockSpace ID=0x7AC87299 Pos=0,69 Size=1129,395 Split=X + DockNode ID=0x00000047 Parent=0x7AC87299 SizeRef=450,464 Selected=0xD25BE5A9 + DockNode ID=0x00000048 Parent=0x7AC87299 SizeRef=677,464 CentralNode=1 Selected=0x789503A8 +DockSpace ID=0x7EF72C9C Pos=0,69 Size=1113,356 Split=X + DockNode ID=0x00000049 Parent=0x7EF72C9C SizeRef=444,425 Selected=0x6A831A07 + DockNode ID=0x0000004A Parent=0x7EF72C9C SizeRef=667,425 CentralNode=1 Selected=0xC04DFC06 +DockSpace ID=0x7F050A52 Pos=0,69 Size=1403,955 Split=X + DockNode ID=0x00000003 Parent=0x7F050A52 SizeRef=573,1024 Selected=0xD1314001 + DockNode ID=0x00000004 Parent=0x7F050A52 SizeRef=828,1024 CentralNode=1 Selected=0x7BFFA600 +DockSpace ID=0x7FC6ECA4 Pos=0,69 Size=1177,512 Split=X + DockNode ID=0x00000041 Parent=0x7FC6ECA4 SizeRef=470,581 Selected=0xE2FB66F2 + DockNode ID=0x00000042 Parent=0x7FC6ECA4 SizeRef=705,581 CentralNode=1 Selected=0x483580F3 +DockSpace ID=0x818D8DE0 Window=0x63F524C4 Pos=0,69 Size=1269,787 Split=X + DockNode ID=0x0000004F Parent=0x818D8DE0 SizeRef=506,856 Selected=0x5DEC4685 + DockNode ID=0x00000050 Parent=0x818D8DE0 SizeRef=761,856 CentralNode=1 Selected=0xF722A084 +DockSpace ID=0x81C6C3A7 Pos=0,69 Size=1161,473 Split=X + DockNode ID=0x00000043 Parent=0x81C6C3A7 SizeRef=463,542 Selected=0x0C540046 + DockNode ID=0x00000044 Parent=0x81C6C3A7 SizeRef=696,542 CentralNode=1 Selected=0xA69AE647 +DockSpace ID=0x88C90CDF Pos=0,69 Size=1097,317 Split=X + DockNode ID=0x0000004B Parent=0x88C90CDF SizeRef=438,386 Selected=0xC0DE3BE7 + DockNode ID=0x0000004C Parent=0x88C90CDF SizeRef=657,386 CentralNode=1 Selected=0x6A10DDE6 +DockSpace ID=0x9A418AAF Pos=0,69 Size=1161,525 Split=X + DockNode ID=0x00000027 Parent=0x9A418AAF SizeRef=468,594 Selected=0x45986F52 + DockNode ID=0x00000028 Parent=0x9A418AAF SizeRef=691,594 CentralNode=1 Selected=0xEF568953 +DockSpace ID=0xA003CAF4 Pos=0,69 Size=1081,330 Split=X + DockNode ID=0x00000035 Parent=0xA003CAF4 SizeRef=431,399 Selected=0x317E7079 + DockNode ID=0x00000036 Parent=0xA003CAF4 SizeRef=648,399 CentralNode=1 Selected=0x9BB09678 +DockSpace ID=0xA864F95B Pos=0,69 Size=1177,564 Split=X + DockNode ID=0x00000021 Parent=0xA864F95B SizeRef=470,633 Selected=0x9537757F + DockNode ID=0x00000022 Parent=0xA864F95B SizeRef=705,633 CentralNode=1 Selected=0x3FF9937E +DockSpace ID=0xBFCCDD68 Pos=0,69 Size=1225,629 Split=X + DockNode ID=0x0000003B Parent=0xBFCCDD68 SizeRef=489,698 Selected=0xDB776EE8 + DockNode ID=0x0000003C Parent=0xBFCCDD68 SizeRef=734,698 CentralNode=1 Selected=0x71B988E9 +DockSpace ID=0xC073D445 Pos=0,69 Size=1113,408 Split=X + DockNode ID=0x00000031 Parent=0xC073D445 SizeRef=444,477 Selected=0xEBC4F3E3 + DockNode ID=0x00000032 Parent=0xC073D445 SizeRef=667,477 CentralNode=1 Selected=0x410A15E2 +DockSpace ID=0xC0B18F35 Pos=0,69 Size=1145,434 Split=X + DockNode ID=0x00000045 Parent=0xC0B18F35 SizeRef=457,503 Selected=0x24E2F739 + DockNode ID=0x00000046 Parent=0xC0B18F35 SizeRef=686,503 CentralNode=1 Selected=0x8E2C1138 +DockSpace ID=0xC5A23B15 Pos=0,69 Size=1177,564 Split=X + DockNode ID=0x0000001D Parent=0xC5A23B15 SizeRef=470,633 Selected=0x2231437F + DockNode ID=0x0000001E Parent=0xC5A23B15 SizeRef=705,633 CentralNode=1 Selected=0x88FFA57E +DockSpace ID=0xCAA2BBFA Pos=0,69 Size=2560,1308 Split=X + DockNode ID=0x00000001 Parent=0xCAA2BBFA SizeRef=706,681 Selected=0xEB4F1F22 + DockNode ID=0x00000002 Parent=0xCAA2BBFA SizeRef=1852,681 CentralNode=1 Selected=0x4181F923 +DockSpace ID=0xCF2E2F82 Pos=0,69 Size=1209,590 Split=X + DockNode ID=0x0000003D Parent=0xCF2E2F82 SizeRef=482,659 Selected=0xBEBBC524 + DockNode ID=0x0000003E Parent=0xCF2E2F82 SizeRef=725,659 CentralNode=1 Selected=0x14752325 +DockSpace ID=0xCF2E6305 Pos=0,69 Size=2309,1121 Split=X + DockNode ID=0x00000007 Parent=0xCF2E6305 SizeRef=939,1190 Selected=0x4DF09DD9 + DockNode ID=0x00000008 Parent=0xCF2E6305 SizeRef=1368,1190 CentralNode=1 Selected=0xE73E7BD8 +DockSpace ID=0xD17FFB45 Pos=0,69 Size=1193,603 Split=X + DockNode ID=0x00000019 Parent=0xD17FFB45 SizeRef=502,672 Selected=0xB2A3F5C9 + DockNode ID=0x0000001A Parent=0xD17FFB45 SizeRef=689,672 CentralNode=1 Selected=0x186D13C8 +DockSpace ID=0xD9C8C259 Pos=0,69 Size=1161,525 Split=X + DockNode ID=0x00000029 Parent=0xD9C8C259 SizeRef=474,594 Selected=0x7D6E76D1 + DockNode ID=0x0000002A Parent=0xD9C8C259 SizeRef=685,594 CentralNode=1 Selected=0xD7A090D0 +DockSpace ID=0xE9CEC326 Pos=0,69 Size=1177,564 Split=X + DockNode ID=0x0000001F Parent=0xE9CEC326 SizeRef=470,633 Selected=0xF049D9DB + DockNode ID=0x00000020 Parent=0xE9CEC326 SizeRef=705,633 CentralNode=1 Selected=0x5A873FDA +DockSpace ID=0xECD066A1 Window=0xD970E9D7 Pos=0,23 Size=1269,833 CentralNode=1 Selected=0x63F524C4 +DockSpace ID=0xFB7D9183 Pos=0,69 Size=1321,915 Split=X + DockNode ID=0x00000009 Parent=0xFB7D9183 SizeRef=550,1108 Selected=0x942261E1 + DockNode ID=0x0000000A Parent=0xFB7D9183 SizeRef=769,1108 CentralNode=1 Selected=0x3EEC87E0 + diff --git a/engine/shaders/imgui.vert.glsl b/engine/shaders/imgui.vert.glsl new file mode 100644 index 0000000..ead22e0 --- /dev/null +++ b/engine/shaders/imgui.vert.glsl @@ -0,0 +1,16 @@ +layout(location = 0) in vec2 in_position; +layout(location = 1) in vec2 in_uv; +layout(location = 2) in vec4 in_color; + +layout(location = 0) out vec4 out_color; +layout(location = 1) out vec2 out_uv; + +layout(std430, push_constant, binding = 1) uniform PushConstant { + mat4 projection; +}; + +void main() { + gl_Position = projection * vec4(in_position, 0.0, 1.0); + out_color = in_color; + out_uv = in_uv; +} diff --git a/engine/shaders/irradiance.frag.glsl b/engine/shaders/irradiance.frag.glsl new file mode 100644 index 0000000..1cceff7 --- /dev/null +++ b/engine/shaders/irradiance.frag.glsl @@ -0,0 +1,36 @@ +layout(location = 0) in vec3 inPos; +layout(location = 0) out vec4 outColor; + +layout(binding = 2) uniform samplerCube environmentSampler; + +const float PI = 3.14159265359; + +void main() { + vec3 N = normalize(inPos); + + vec3 irradiance = vec3(0.0); + + // tangent space calculation from origin point + vec3 up = vec3(0.0, 1.0, 0.0); + vec3 right = cross(up, N); + up = cross(N, right); + + float sampleDelta = 0.025; + float nrSamples = 0.0f; + for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta) + { + for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta) + { + // spherical to cartesian (in tangent space) + vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)); + // tangent space to world + vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; + + irradiance += texture(environmentSampler, sampleVec).rgb * cos(theta) * sin(theta); + nrSamples++; + } + } + irradiance = PI * irradiance * (1.0 / float(nrSamples)); + + outColor = vec4(irradiance, 1.0); +} diff --git a/engine/shaders/irradiance.vert.glsl b/engine/shaders/irradiance.vert.glsl new file mode 100644 index 0000000..c1947fa --- /dev/null +++ b/engine/shaders/irradiance.vert.glsl @@ -0,0 +1,12 @@ +layout(location = 0) in vec3 inPosition; + +layout(location = 0) out vec3 outPos; + +layout(push_constant, binding = 1) uniform readonly PushConstant{ + mat4 mvp; +}; + +void main() { + gl_Position = mvp * vec4(inPosition, 1.0); + outPos = inPosition; +} diff --git a/engine/shaders/mesh.vert.nocompile.glsl b/engine/shaders/mesh.vert.nocompile.glsl new file mode 100644 index 0000000..76aa7fc --- /dev/null +++ b/engine/shaders/mesh.vert.nocompile.glsl @@ -0,0 +1,123 @@ +layout (constant_id = 0) const int max_materials = 25; +layout (constant_id = 1) const int max_lights = 25; +layout (constant_id = 2) const int max_spot_lights = 4; +layout (constant_id = 3) const int max_probes = 4; + +layout (location = 0) in vec3 inPosition; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV; +layout (location = 3) in vec3 inTangent; +layout (location = 4) in vec3 inBitangent; + +#ifdef BONE +layout (location = 5) in ivec4 inBoneID; +layout (location = 6) in vec4 inBoneWeight; +#endif + +layout (location = 0) out vec3 outFragPos; +layout (location = 1) out vec3 outNormal; +layout (location = 2) out vec2 outUV; +layout (location = 4) out vec4 fragPosLightSpace; +layout (location = 5) out mat3 outTBN; +layout (location = 14) out vec4 fragPostSpotLightSpace[max_spot_lights]; + +struct Material { + vec4 color, info; +}; + +struct Light { + vec4 positionType; + vec4 directionPower; + vec4 color; +}; + +struct Probe { + vec4 position, size; +}; + +layout(std430, binding = 1) buffer readonly SceneInformation { + vec4 options; + vec4 camPos; + mat4 vp, lightSpace; + mat4 spotLightSpaces[max_spot_lights]; + Material materials[max_materials]; + Light lights[max_lights]; + Probe probes[max_probes]; + int numLights; +} scene; + +#ifdef CUBEMAP +layout(push_constant, binding = 0) uniform readonly PushConstant{ + mat4 model, view; +}; +#else +layout(push_constant, binding = 0) uniform readonly PushConstant{ + mat4 model; +}; +#endif + +const mat4 biasMat = mat4( + 0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.5, 0.5, 0.0, 1.0); + +#ifdef BONE +layout(std430, binding = 14) buffer readonly BoneInformation { + mat4 bones[128]; +}; +#endif + +void main() { + const mat3 mat = mat3(model); + const vec3 T = normalize(mat * inTangent); + const vec3 N = normalize(mat * inNormal); + const vec3 B = normalize(mat * inBitangent); + const mat3 TBN = mat3(T, B, N); + +#ifdef BONE + mat4 BoneTransform = bones[inBoneID[0]] * inBoneWeight[0]; + BoneTransform += bones[inBoneID[1]] * inBoneWeight[1]; + BoneTransform += bones[inBoneID[2]] * inBoneWeight[2]; + BoneTransform += bones[inBoneID[3]] * inBoneWeight[3]; + + BoneTransform = model * BoneTransform; + + vec4 bPos = BoneTransform * vec4(inPosition, 1.0); + vec4 bNor = BoneTransform * vec4(inNormal, 0.0); + + gl_Position = scene.vp * bPos; + outFragPos = vec3(model * vec4(inPosition, 1.0)); + outNormal = bNor.xyz; + outUV = inUV; + fragPosLightSpace = (biasMat * scene.lightSpace) * bPos; + + for(int i = 0; i < max_spot_lights; i++) { + fragPostSpotLightSpace[i] = (biasMat * scene.spotLightSpaces[i]) * bPos; + } +#else +#ifdef CUBEMAP + gl_Position = scene.vp * view * model * vec4(inPosition, 1.0); + outFragPos = vec3(model * vec4(inPosition, 1.0)); + outNormal = mat3(model) * inNormal; + outUV = inUV; + fragPosLightSpace = (biasMat * scene.lightSpace * model) * vec4(inPosition, 1.0); + + for(int i = 0; i < max_spot_lights; i++) { + fragPostSpotLightSpace[i] = (biasMat * scene.spotLightSpaces[i] * model) * vec4(inPosition, 1.0); + } +#else + gl_Position = scene.vp * model * vec4(inPosition, 1.0); + + outFragPos = vec3(model * vec4(inPosition, 1.0)); + outNormal = mat3(model) * inNormal; + outUV = inUV; + fragPosLightSpace = (biasMat * scene.lightSpace * model) * vec4(inPosition, 1.0); + + for(int i = 0; i < max_spot_lights; i++) { + fragPostSpotLightSpace[i] = (biasMat * scene.spotLightSpaces[i] * model) * vec4(inPosition, 1.0); + } +#endif +#endif + outTBN = TBN; +} diff --git a/engine/shaders/omnishadow.frag.glsl b/engine/shaders/omnishadow.frag.glsl new file mode 100644 index 0000000..61e4d2c --- /dev/null +++ b/engine/shaders/omnishadow.frag.glsl @@ -0,0 +1,14 @@ +layout (constant_id = 0) const int max_lights = 25; + +layout (location = 0) in vec3 inPos; +layout (location = 1) flat in int index; + +layout (location = 0) out float outFragColor; + +layout(std430, binding = 2) buffer readonly LightInformation { + vec3 light_locations[max_lights]; +}; + +void main() { + outFragColor = length(inPos - light_locations[index]); +} diff --git a/engine/shaders/post.frag.glsl b/engine/shaders/post.frag.glsl new file mode 100644 index 0000000..1d5026d --- /dev/null +++ b/engine/shaders/post.frag.glsl @@ -0,0 +1,159 @@ +#define SMAA_RT_METRICS viewport + +#define SMAA_PRESET_ULTRA 1 +#define SMAA_GLSL_4 1 +#define SMAA_INCLUDE_VS 0 +#define SMAA_INCLUDE_PS 1 + +layout(std430, push_constant, binding = 4) uniform PushConstant { + vec4 viewport; + vec4 options; + vec4 transform_ops; +}; + +#include "smaa.glsl" +#include "common.nocompile.glsl" + +layout (location = 0) in vec2 inUV; +layout(location = 1) in vec4 inOffset; + +layout (location = 0) out vec4 outColor; + +layout (binding = 1) uniform sampler2D colorSampler; +layout (binding = 2) uniform sampler2D backSampler; +layout (binding = 3) uniform sampler2D blendSampler; +layout (binding = 5) uniform sampler2D sobelSampler; +layout (binding = 6) uniform sampler2D averageLuminanceSampler; +layout (binding = 7) uniform sampler2D farFieldSampler; + +float calculate_sobel() { + float x = 1.0 / viewport.z; + float y = 1.0 / viewport.w; + vec4 horizEdge = vec4( 0.0 ); + horizEdge -= texture( sobelSampler, vec2( inUV.x - x, inUV.y - y ) ) * 1.0; + horizEdge -= texture( sobelSampler, vec2( inUV.x - x, inUV.y ) ) * 2.0; + horizEdge -= texture( sobelSampler, vec2( inUV.x - x, inUV.y + y ) ) * 1.0; + horizEdge += texture( sobelSampler, vec2( inUV.x + x, inUV.y - y ) ) * 1.0; + horizEdge += texture( sobelSampler, vec2( inUV.x + x, inUV.y ) ) * 2.0; + horizEdge += texture( sobelSampler, vec2( inUV.x + x, inUV.y + y ) ) * 1.0; + vec4 vertEdge = vec4( 0.0 ); + vertEdge -= texture( sobelSampler, vec2( inUV.x - x, inUV.y - y ) ) * 1.0; + vertEdge -= texture( sobelSampler, vec2( inUV.x , inUV.y - y ) ) * 2.0; + vertEdge -= texture( sobelSampler, vec2( inUV.x + x, inUV.y - y ) ) * 1.0; + vertEdge += texture( sobelSampler, vec2( inUV.x - x, inUV.y + y ) ) * 1.0; + vertEdge += texture( sobelSampler, vec2( inUV.x , inUV.y + y ) ) * 2.0; + vertEdge += texture( sobelSampler, vec2( inUV.x + x, inUV.y + y ) ) * 1.0; + return sqrt((horizEdge.rgb * horizEdge.rgb) + (vertEdge.rgb * vertEdge.rgb)).r; +} + +// adapted from https://bruop.github.io/exposure/ + +vec3 convertRGB2XYZ(vec3 _rgb) +{ + // Reference: + // RGB/XYZ Matrices + // http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html + vec3 xyz; + xyz.x = dot(vec3(0.4124564, 0.3575761, 0.1804375), _rgb); + xyz.y = dot(vec3(0.2126729, 0.7151522, 0.0721750), _rgb); + xyz.z = dot(vec3(0.0193339, 0.1191920, 0.9503041), _rgb); + return xyz; +} + +vec3 convertXYZ2RGB(vec3 _xyz) +{ + vec3 rgb; + rgb.x = dot(vec3( 3.2404542, -1.5371385, -0.4985314), _xyz); + rgb.y = dot(vec3(-0.9692660, 1.8760108, 0.0415560), _xyz); + rgb.z = dot(vec3( 0.0556434, -0.2040259, 1.0572252), _xyz); + return rgb; +} + +vec3 convertXYZ2Yxy(vec3 _xyz) +{ + // Reference: + // http://www.brucelindbloom.com/index.html?Eqn_XYZ_to_xyY.html + float inv = 1.0/dot(_xyz, vec3(1.0, 1.0, 1.0) ); + return vec3(_xyz.y, _xyz.x*inv, _xyz.y*inv); +} + +vec3 convertYxy2XYZ(vec3 _Yxy) +{ + // Reference: + // http://www.brucelindbloom.com/index.html?Eqn_xyY_to_XYZ.html + vec3 xyz; + xyz.x = _Yxy.x*_Yxy.y/_Yxy.z; + xyz.y = _Yxy.x; + xyz.z = _Yxy.x*(1.0 - _Yxy.y - _Yxy.z)/_Yxy.z; + return xyz; +} + +vec3 convertRGB2Yxy(vec3 _rgb) +{ + return convertXYZ2Yxy(convertRGB2XYZ(_rgb) ); +} + +vec3 convertYxy2RGB(vec3 _Yxy) +{ + return convertXYZ2RGB(convertYxy2XYZ(_Yxy) ); +} + +float reinhard2(float _x, float _whiteSqr) +{ + return (_x * (1.0 + _x/_whiteSqr) ) / (1.0 + _x); +} + +void main() { + // passthrough + if(options.w == 1) { + outColor = texture(colorSampler, inUV); + return; + } + + bool enable_dof = options.w == 2; + vec3 sceneColor = vec3(0); + if(enable_dof) { + sceneColor = texture(farFieldSampler, inUV).rgb; + sceneColor += texture(colorSampler, inUV).rgb; + } else { + if(options.x == 1) // enable AA + sceneColor = SMAANeighborhoodBlendingPS(inUV, inOffset, colorSampler, blendSampler).rgb; + else + sceneColor = texture(colorSampler, inUV).rgb; + } + + float sobel = 0.0; + if(textureSize(sobelSampler, 0).x > 1) + sobel = calculate_sobel(); + + vec3 sobelColor = vec3(0, 1, 1); + + vec3 hdrColor = sceneColor; // fading removed + + float avg_lum = texture(averageLuminanceSampler, inUV).r; + + vec3 transformed_color = hdrColor; + + if(transform_ops.y == 1) { + transformed_color = vec3(1.0) - exp(-transformed_color * options.z); + } else if(transform_ops.y == 2) { + vec3 Yxy = convertRGB2Yxy(transformed_color); + + // hard-coded for now + float whitePoint = 4.0; + + float lp = Yxy.x / (9.6 * avg_lum + 0.0001); + + // Replace this line with other tone mapping functions + // Here we applying the curve to the luminance exclusively + Yxy.x = reinhard2(lp, whitePoint); + + transformed_color = convertYxy2RGB(Yxy); + } + + if(transform_ops.x == 1) { + transformed_color = from_linear_to_srgb(transformed_color); + } + + outColor = vec4(transformed_color + (sobelColor * sobel), 1.0); +} diff --git a/engine/shaders/post.vert.glsl b/engine/shaders/post.vert.glsl new file mode 100644 index 0000000..3a38dbe --- /dev/null +++ b/engine/shaders/post.vert.glsl @@ -0,0 +1,25 @@ +#define SMAA_RT_METRICS viewport + +#define SMAA_PRESET_ULTRA 1 +#define SMAA_GLSL_4 1 +#define SMAA_INCLUDE_VS 1 +#define SMAA_INCLUDE_PS 0 + +layout(std430, push_constant, binding = 4) uniform readonlPushConstant { + vec4 viewport; + float fade; + vec4 transform_ops; +}; + +#include "smaa.glsl" + +layout (location = 0) out vec2 outUV; +layout(location = 1) out vec4 outOffset; + +void main() { + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); + outUV.y = 1.0 - outUV.y; + + SMAANeighborhoodBlendingVS(outUV, outOffset); +} diff --git a/engine/shaders/rendering.nocompile.glsl b/engine/shaders/rendering.nocompile.glsl new file mode 100644 index 0000000..966ac03 --- /dev/null +++ b/engine/shaders/rendering.nocompile.glsl @@ -0,0 +1,305 @@ +struct ComputedSurfaceInfo { + vec3 N; + vec3 V; + vec3 F0, diffuse_color; + + float NdotV; + + float roughness, metallic; +}; + +ComputedSurfaceInfo compute_surface(const vec3 diffuse, const vec3 normal, const float metallic, const float roughness) { + ComputedSurfaceInfo info; + + info.N = normalize(normal); + info.V = normalize(scene.camPos.xyz - in_frag_pos); + info.NdotV = max(dot(info.N, info.V), 0.0); + + info.metallic = metallic; + info.roughness = max(0.0001, roughness); + + info.F0 = 0.16 * (1.0 - info.metallic) + diffuse * info.metallic; + info.diffuse_color = (1.0 - info.metallic) * diffuse; + + return info; +} + +struct SurfaceBRDF { + vec3 diffuse, specular; + float NdotL; +}; + +SurfaceBRDF brdf(const vec3 L, const ComputedSurfaceInfo surface_info) { + SurfaceBRDF info; + + // half-vector + const vec3 H = normalize(surface_info.V + L); + + // fresnel reflectance function + const vec3 F = fresnel_schlick(surface_info.NdotV, surface_info.F0); + + // geometry function + const float D = ggx_distribution(surface_info.N, H, surface_info.roughness); + + // normal distribution function + const float G = geometry_smith(surface_info.N, surface_info.V, L, surface_info.roughness); + + const vec3 numerator = F * G * D; + const float denominator = 4.0 * surface_info.NdotV * clamp(dot(surface_info.N, L), 0.0, 1.0); + + // cook-torrance specular brdf + info.specular = numerator / (denominator + 0.001); + + // lambertian diffuse + info.diffuse = surface_info.diffuse_color * (1.0 / PI); + + info.NdotL = clamp(dot(surface_info.N, L), 0.0, 1.0); + + return info; +} + +struct ComputedLightInformation { + vec3 direction; + float radiance; +}; + +float pcf_sun(const vec4 shadowCoords, const float uvRadius) { + float sum = 0; + for(int i = 0; i < 16; i++) { + const float z = texture(sun_shadow, shadowCoords.xy + PoissonOffsets[i] * uvRadius).r; + sum += (z < (shadowCoords.z - 0.005)) ? 0.0 : 1.0; + } + + return sum / 16; +} + +#ifdef SHADOW_FILTER_PCSS +float search_width(const float uvLightSize, const float receiverDistance) { + return uvLightSize * (receiverDistance - 0.1f) / receiverDistance; +} + +float penumbra_size(const float zReceiver, const float zBlocker) { + return (zReceiver - zBlocker) / zBlocker; +} + +const int numBlockerSearchSamples = 16; + +void blocker_distance_sun(const vec3 shadowCoords, const float uvLightSize, inout float avgBlockerDistance, inout int blockers) { + const float searchWidth = search_width(uvLightSize, shadowCoords.z); + + float blockerSum = 0.0; + blockers = 0; + + for(int i = 0; i < numBlockerSearchSamples; i++) { + const float z = texture(sun_shadow, + shadowCoords.xy + PoissonOffsets[i] * searchWidth).r; + if(z < shadowCoords.z) { + blockerSum += z; + blockers++; + } + } + + avgBlockerDistance = blockerSum / blockers; +} + +float pcss_sun(const vec4 shadowCoords, float light_size_uv) { + float average_blocker_depth = 0.0; + int num_blockers = 0; + blocker_distance_sun(shadowCoords.xyz, light_size_uv, average_blocker_depth, num_blockers); + if(num_blockers < 1) + return 1.0; + + const float penumbraWidth = penumbra_size(-shadowCoords.z, average_blocker_depth); + + const float uvRadius = penumbraWidth * light_size_uv * 0.1 / shadowCoords.z; + return pcf_sun(shadowCoords, uvRadius); +} +#endif + +ComputedLightInformation calculate_sun(Light light) { + ComputedLightInformation light_info; + light_info.direction = normalize(light.positionType.xyz - vec3(0)); + + float shadow = 1.0; + if(light.shadowsEnable.x == 1.0) { + const vec4 shadowCoords = fragPosLightSpace / fragPosLightSpace.w; + + if(shadowCoords.z > -1.0 && shadowCoords.z < 1.0) { +#ifdef SHADOW_FILTER_NONE + shadow = (texture(sun_shadow, shadowCoords.xy).r < shadowCoords.z - 0.005) ? 0.0 : 1.0; +#endif +#ifdef SHADOW_FILTER_PCF + shadow = pcf_sun(shadowCoords, 0.1); +#endif +#ifdef SHADOW_FILTER_PCSS + shadow = pcss_sun(shadowCoords, light.shadowsEnable.y); +#endif + } + } + + light_info.radiance = light.directionPower.w * shadow; + + return light_info; +} + +float pcf_spot(const vec4 shadowCoords, const int index, const float uvRadius) { + float sum = 0; + for(int i = 0; i < 16; i++) { + const float z = texture(spot_shadow, vec3(shadowCoords.xy + PoissonOffsets[i] * uvRadius, index)).r; + sum += (z < (shadowCoords.z - 0.001)) ? 0.0 : 1.0; + } + + return sum / 16; +} + +#ifdef SHADOW_FILTER_PCSS +void blocker_distance_spot(const vec3 shadowCoords, const int index, const float uvLightSize, inout float avgBlockerDistance, inout int blockers) { + const float searchWidth = search_width(uvLightSize, shadowCoords.z); + + float blockerSum = 0.0; + blockers = 0; + + for(int i = 0; i < numBlockerSearchSamples; i++) { + const float z = texture(spot_shadow, + vec3(shadowCoords.xy + PoissonOffsets[i] * searchWidth, index)).r; + if(z < shadowCoords.z) { + blockerSum += z; + blockers++; + } + } + + avgBlockerDistance = blockerSum / blockers; +} + +float pcss_spot(const vec4 shadowCoords, const int index, float light_size_uv) { + float average_blocker_depth = 0.0; + int num_blockers = 0; + blocker_distance_spot(shadowCoords.xyz, index, light_size_uv, average_blocker_depth, num_blockers); + if(num_blockers < 1) + return 1.0; + + const float penumbraWidth = penumbra_size(-shadowCoords.z, average_blocker_depth); + + const float uvRadius = penumbraWidth * light_size_uv * 0.1 / shadowCoords.z; + return pcf_spot(shadowCoords, index, uvRadius); +} + +#endif + +int last_spot_light = 0; + +ComputedLightInformation calculate_spot(Light light) { + ComputedLightInformation light_info; + light_info.direction = normalize(light.positionType.xyz - in_frag_pos); + + float shadow = 1.0; + if(light.shadowsEnable.x == 1.0) { + const vec4 shadowCoord = fragPostSpotLightSpace[last_spot_light] / fragPostSpotLightSpace[last_spot_light].w; + +#ifdef SHADOW_FILTER_NONE + shadow = (texture(spot_shadow, vec3(shadowCoord.xy, last_spot_light)).r < shadowCoord.z) ? 0.0 : 1.0; +#endif +#ifdef SHADOW_FILTER_PCF + shadow = pcf_spot(shadowCoord, last_spot_light, 0.01); +#endif +#ifdef SHADOW_FILTER_PCSS + shadow = pcss_spot(shadowCoord, last_spot_light, light.shadowsEnable.y); +#endif + + last_spot_light++; + } + + const float inner_cutoff = light.colorSize.w + radians(5); + const float outer_cutoff = light.colorSize.w; + + const float theta = dot(light_info.direction, normalize(light.directionPower.xyz)); + const float epsilon = inner_cutoff - outer_cutoff; + const float intensity = clamp((theta - outer_cutoff) / epsilon, 0.0, 1.0); + + light_info.radiance = light.directionPower.w * shadow * intensity; + + return light_info; +} + +#ifdef POINT_SHADOWS_SUPPORTED + +float pcf_point(const vec3 shadowCoords, const int index, const float uvRadius) { + float sum = 0; + for(int i = 0; i < 16; i++) { + const float z = texture(point_shadow, vec4(shadowCoords.xyz + vec3(PoissonOffsets[i].xy, PoissonOffsets[i].x) * uvRadius, index)).r; + sum += (z < length(shadowCoords) - 0.05) ? 0.0 : 1.0; + } + + return sum / 16; +} + +#ifdef SHADOW_FILTER_PCSS +void blocker_distance_point(const vec3 shadowCoords, const int index, const float uvLightSize, inout float avgBlockerDistance, inout int blockers) { + const float searchWidth = search_width(uvLightSize, length(shadowCoords)); + + float blockerSum = 0.0; + blockers = 0; + + for(int i = 0; i < numBlockerSearchSamples; i++) { + const float z = texture(point_shadow, + vec4(shadowCoords + vec3(PoissonOffsets[i], PoissonOffsets[i].x) * searchWidth, index)).r; + if(z < length(shadowCoords)) { + blockerSum += z; + blockers++; + } + } + + avgBlockerDistance = blockerSum / blockers; +} + +float pcss_point(const vec3 shadowCoords, const int index, float light_size_uv) { + float average_blocker_depth = 0.0; + int num_blockers = 0; + blocker_distance_point(shadowCoords.xyz, index, light_size_uv, average_blocker_depth, num_blockers); + if(num_blockers < 1) + return 1.0; + + const float depth = length(shadowCoords); + const float penumbraWidth = penumbra_size(-depth, average_blocker_depth); + + const float uvRadius = penumbraWidth * light_size_uv; + return pcf_point(shadowCoords, index, uvRadius); +} +#endif + +#endif + +int last_point_light = 0; + +ComputedLightInformation calculate_point(Light light) { + ComputedLightInformation light_info; + light_info.direction = normalize(light.positionType.xyz - in_frag_pos); + + const vec3 lightVec = in_frag_pos - light.positionType.xyz; + + // Check if fragment is in shadow + float shadow = 1.0; +#ifdef POINT_SHADOWS_SUPPORTED + if(light.shadowsEnable.x == 1.0) { +#ifdef SHADOW_FILTER_NONE + const float sampledDist = texture(point_shadow, vec4(lightVec, last_point_light++)).r; + const float dist = length(lightVec); + + shadow = (dist <= sampledDist + 0.05) ? 1.0 : 0.0; +#endif +#ifdef SHADOW_FILTER_PCF + shadow = pcf_point(lightVec, last_point_light++, 1.0); +#endif +#ifdef SHADOW_FILTER_PCSS + shadow = pcss_point(lightVec, last_point_light++, light.shadowsEnable.y); +#endif + } +#endif + + const float distance = length(light.positionType.xyz - in_frag_pos); + const float attenuation = 1.0 / (distance * distance); + + light_info.radiance = attenuation * light.directionPower.w * shadow; + + return light_info; +} diff --git a/engine/shaders/scenecapture.vert.nocompile.glsl b/engine/shaders/scenecapture.vert.nocompile.glsl new file mode 100644 index 0000000..c3a799d --- /dev/null +++ b/engine/shaders/scenecapture.vert.nocompile.glsl @@ -0,0 +1,58 @@ +layout (constant_id = 0) const int max_materials = 25; +layout (constant_id = 1) const int max_lights = 25; +layout (constant_id = 2) const int max_spot_lights = 4; + +layout (location = 0) in vec3 inPosition; +layout (location = 1) in vec3 inNormal; +layout (location = 2) in vec2 inUV; + +layout (location = 0) out vec3 outFragPos; +layout (location = 1) out vec3 outNormal; +layout (location = 2) out vec2 outUV; +layout (location = 3) out flat int outMaterialId; +layout (location = 4) out vec4 fragPosLightSpace; +layout (location = 5) out vec4 fragPostSpotLightSpace[max_spot_lights]; + +struct Material { + vec4 color, info; +}; + +struct Light { + vec4 positionType; + vec4 directionPower; + vec4 color; +}; + +layout(std430, binding = 5) buffer readonly SceneInformation { + vec4 camPos; + mat4 projection, lightSpace; + vec4 skyColor; + mat4 spotLightSpaces[max_spot_lights]; + Material materials[max_materials]; + Light lights[max_lights]; + int numLights; +} scene; + +layout(push_constant, binding = 6) uniform readonly PushConstant{ + mat4 model, view; + int materialOffset; +}; + +const mat4 biasMat = mat4( + 0.5, 0.0, 0.0, 0.0, + 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.5, 0.5, 0.0, 1.0); + +void main() { + gl_Position = scene.projection * view * model * vec4(inPosition, 1.0); + outFragPos = vec3(model * vec4(inPosition, 1.0)); + outNormal = mat3(model) * inNormal; + outUV = inUV; + outMaterialId = materialOffset; + fragPosLightSpace = (biasMat * scene.lightSpace * model) * vec4(inPosition, 1.0); + + for(int i = 0; i < max_spot_lights; i++) { + fragPostSpotLightSpace[i] = (biasMat * scene.spotLightSpaces[i] * model) * vec4(inPosition, 1.0); + } +} diff --git a/engine/shaders/shadow.frag.glsl b/engine/shaders/shadow.frag.glsl new file mode 100644 index 0000000..2cb1536 --- /dev/null +++ b/engine/shaders/shadow.frag.glsl @@ -0,0 +1,5 @@ +layout (location = 0) in vec3 inPos; +layout (location = 1) flat in int index; + +void main() { +} diff --git a/engine/shaders/shadow.vert.nocompile.glsl b/engine/shaders/shadow.vert.nocompile.glsl new file mode 100644 index 0000000..164e28f --- /dev/null +++ b/engine/shaders/shadow.vert.nocompile.glsl @@ -0,0 +1,43 @@ +layout (constant_id = 0) const int max_lights = 25; + +layout (location = 0) in vec3 inPosition; + +#ifdef BONE +layout (location = 4) in ivec4 inBoneID; +layout (location = 5) in vec4 inBoneWeight; +#endif + +layout (location = 0) out vec3 outPos; +layout (location = 1) flat out int index; + +layout(push_constant, binding = 0) uniform PushConstant { + mat4 mvp, model; +}; + +#ifdef BONE +layout(std430, binding = 14) buffer readonly BoneInformation { + mat4 bones[128]; +}; +#endif + +layout(std430, binding = 2) buffer readonly LightInformation { + vec3 light_locations[max_lights]; +}; + +void main() { +#ifdef BONE + mat4 BoneTransform; + + BoneTransform = bones[inBoneID[0]] * inBoneWeight[0]; + BoneTransform += bones[inBoneID[1]] * inBoneWeight[1]; + BoneTransform += bones[inBoneID[2]] * inBoneWeight[2]; + BoneTransform += bones[inBoneID[3]] * inBoneWeight[3]; + + gl_Position = mvp * BoneTransform * vec4(inPosition, 1.0); + outPos = vec3(model * vec4(inPosition, 1.0)); +#else + gl_Position = mvp * vec4(inPosition, 1.0); + outPos = vec3(model * vec4(inPosition, 1.0)); +#endif + index = gl_BaseInstance; +} diff --git a/engine/shaders/sky.frag.glsl b/engine/shaders/sky.frag.glsl new file mode 100644 index 0000000..3b8e634 --- /dev/null +++ b/engine/shaders/sky.frag.glsl @@ -0,0 +1,47 @@ +#include "atmosphere.glsl" + +layout (location = 0) in vec2 in_uv; + +layout (location = 0) out vec4 out_color; + +layout(push_constant, binding = 1) uniform PushConstant{ + mat4 view; + vec4 sun_position_fov; + float aspect; +}; + +vec3 sky_ray(const vec2 uv) { + const float d = 0.5 / tan(sun_position_fov.w / 2.0); + return normalize(vec3((uv.x - 0.5) * aspect, + uv.y - 0.5, + d)); +} + +void main() { + const vec3 color = atmosphere( + // ray direction + normalize(mat3(view) * sky_ray(in_uv)), + // ray origin + vec3(0, 6372e3, 0), + // position of the sun in world space (this will be normalized) + sun_position_fov.xyz, + // intensity of the sun + 22.0, + // radius of the plant in meters + 6371e3, + // radius of the atmosphere in meters + 6471e3, + // Rayleigh scattering coefficient + vec3(5.5e-6, 13.0e-6, 22.4e-6), + // Mie scattering coefficient + 21e-6, + // Rayleigh scale height + 8e3, + // Mie scale height + 1.2e3, + // Mie preferred scattering direction + 0.758 + ); + + out_color = vec4(color, 1.0); +} diff --git a/engine/shaders/sky.vert.glsl b/engine/shaders/sky.vert.glsl new file mode 100644 index 0000000..cef4fd1 --- /dev/null +++ b/engine/shaders/sky.vert.glsl @@ -0,0 +1,12 @@ +layout (location = 0) out vec2 out_uv; + +layout(push_constant, binding = 1) uniform PushConstant{ + mat4 view; + vec4 sun_position_fov; + float aspect; +}; + +void main() { + out_uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(out_uv * 2.0f + -1.0f, 1.0f, 1.0f); +} diff --git a/engine/shaders/smaa.glsl b/engine/shaders/smaa.glsl new file mode 100644 index 0000000..af0e352 --- /dev/null +++ b/engine/shaders/smaa.glsl @@ -0,0 +1,1407 @@ +/** + * Copyright (C) 2013 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2013 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2013 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2013 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2013 Diego Gutierrez (diegog@unizar.es) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + * do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +/** + * _______ ___ ___ ___ ___ + * / || \/ | / \ / \ + * | (---- | \ / | / ^ \ / ^ \ + * \ \ | |\/| | / /_\ \ / /_\ \ + * ----) | | | | | / _____ \ / _____ \ + * |_______/ |__| |__| /__/ \__\ /__/ \__\ + * + * E N H A N C E D + * S U B P I X E L M O R P H O L O G I C A L A N T I A L I A S I N G + * + * http://www.iryoku.com/smaa/ + * + * Hi, welcome aboard! + * + * Here you'll find instructions to get the shader up and running as fast as + * possible. + * + * IMPORTANTE NOTICE: when updating, remember to update both this file and the + * precomputed textures! They may change from version to version. + * + * The shader has three passes, chained together as follows: + * + * |input|------------------· + * v | + * [ SMAA*EdgeDetection ] | + * v | + * |edgesTex| | + * v | + * [ SMAABlendingWeightCalculation ] | + * v | + * |blendTex| | + * v | + * [ SMAANeighborhoodBlending ] <------· + * v + * |output| + * + * Note that each [pass] has its own vertex and pixel shader. Remember to use + * oversized triangles instead of quads to avoid overshading along the + * diagonal. + * + * You've three edge detection methods to choose from: luma, color or depth. + * They represent different quality/performance and anti-aliasing/sharpness + * tradeoffs, so our recommendation is for you to choose the one that best + * suits your particular scenario: + * + * - Depth edge detection is usually the fastest but it may miss some edges. + * + * - Luma edge detection is usually more expensive than depth edge detection, + * but catches visible edges that depth edge detection can miss. + * + * - Color edge detection is usually the most expensive one but catches + * chroma-only edges. + * + * For quickstarters: just use luma edge detection. + * + * The general advice is to not rush the integration process and ensure each + * step is done correctly (don't try to integrate SMAA T2x with predicated edge + * detection from the start!). Ok then, let's go! + * + * 1. The first step is to create two RGBA temporal render targets for holding + * |edgesTex| and |blendTex|. + * + * In DX10 or DX11, you can use a RG render target for the edges texture. + * In the case of NVIDIA GPUs, using RG render targets seems to actually be + * slower. + * + * On the Xbox 360, you can use the same render target for resolving both + * |edgesTex| and |blendTex|, as they aren't needed simultaneously. + * + * 2. Both temporal render targets |edgesTex| and |blendTex| must be cleared + * each frame. Do not forget to clear the alpha channel! + * + * 3. The next step is loading the two supporting precalculated textures, + * 'areaTex' and 'searchTex'. You'll find them in the 'Textures' folder as + * C++ headers, and also as regular DDS files. They'll be needed for the + * 'SMAABlendingWeightCalculation' pass. + * + * If you use the C++ headers, be sure to load them in the format specified + * inside of them. + * + * You can also compress 'areaTex' and 'searchTex' using BC5 and BC4 + * respectively, if you have that option in your content processor pipeline. + * When compressing then, you get a non-perceptible quality decrease, and a + * marginal performance increase. + * + * 4. All samplers must be set to linear filtering and clamp. + * + * After you get the technique working, remember that 64-bit inputs have + * half-rate linear filtering on GCN. + * + * If SMAA is applied to 64-bit color buffers, switching to point filtering + * when accesing them will increase the performance. Search for + * 'SMAASamplePoint' to see which textures may benefit from point + * filtering, and where (which is basically the color input in the edge + * detection and resolve passes). + * + * 5. All texture reads and buffer writes must be non-sRGB, with the exception + * of the input read and the output write in + * 'SMAANeighborhoodBlending' (and only in this pass!). If sRGB reads in + * this last pass are not possible, the technique will work anyway, but + * will perform antialiasing in gamma space. + * + * IMPORTANT: for best results the input read for the color/luma edge + * detection should *NOT* be sRGB. + * + * 6. Before including SMAA.h you'll have to setup the render target metrics, + * the target and any optional configuration defines. Optionally you can + * use a preset. + * + * You have the following targets available: + * SMAA_HLSL_3 + * SMAA_HLSL_4 + * SMAA_HLSL_4_1 + * SMAA_GLSL_3 * + * SMAA_GLSL_4 * + * + * * (See SMAA_INCLUDE_VS and SMAA_INCLUDE_PS below). + * + * And four presets: + * SMAA_PRESET_LOW (%60 of the quality) + * SMAA_PRESET_MEDIUM (%80 of the quality) + * SMAA_PRESET_HIGH (%95 of the quality) + * SMAA_PRESET_ULTRA (%99 of the quality) + * + * For example: + * #define SMAA_RT_METRICS float4(1.0 / 1280.0, 1.0 / 720.0, 1280.0, 720.0) + * #define SMAA_HLSL_4 + * #define SMAA_PRESET_HIGH + * #include "SMAA.h" + * + * Note that SMAA_RT_METRICS doesn't need to be a macro, it can be a + * uniform variable. The code is designed to minimize the impact of not + * using a constant value, but it is still better to hardcode it. + * + * Depending on how you encoded 'areaTex' and 'searchTex', you may have to + * add (and customize) the following defines before including SMAA.h: + * #define SMAA_AREATEX_SELECT(sample) sample.rg + * #define SMAA_SEARCHTEX_SELECT(sample) sample.r + * + * If your engine is already using porting macros, you can define + * SMAA_CUSTOM_SL, and define the porting functions by yourself. + * + * 7. Then, you'll have to setup the passes as indicated in the scheme above. + * You can take a look into SMAA.fx, to see how we did it for our demo. + * Checkout the function wrappers, you may want to copy-paste them! + * + * 8. It's recommended to validate the produced |edgesTex| and |blendTex|. + * You can use a screenshot from your engine to compare the |edgesTex| + * and |blendTex| produced inside of the engine with the results obtained + * with the reference demo. + * + * 9. After you get the last pass to work, it's time to optimize. You'll have + * to initialize a stencil buffer in the first pass (discard is already in + * the code), then mask execution by using it the second pass. The last + * pass should be executed in all pixels. + * + * + * After this point you can choose to enable predicated thresholding, + * temporal supersampling and motion blur integration: + * + * a) If you want to use predicated thresholding, take a look into + * SMAA_PREDICATION; you'll need to pass an extra texture in the edge + * detection pass. + * + * b) If you want to enable temporal supersampling (SMAA T2x): + * + * 1. The first step is to render using subpixel jitters. I won't go into + * detail, but it's as simple as moving each vertex position in the + * vertex shader, you can check how we do it in our DX10 demo. + * + * 2. Then, you must setup the temporal resolve. You may want to take a look + * into SMAAResolve for resolving 2x modes. After you get it working, you'll + * probably see ghosting everywhere. But fear not, you can enable the + * CryENGINE temporal reprojection by setting the SMAA_REPROJECTION macro. + * Check out SMAA_DECODE_VELOCITY if your velocity buffer is encoded. + * + * 3. The next step is to apply SMAA to each subpixel jittered frame, just as + * done for 1x. + * + * 4. At this point you should already have something usable, but for best + * results the proper area textures must be set depending on current jitter. + * For this, the parameter 'subsampleIndices' of + * 'SMAABlendingWeightCalculationPS' must be set as follows, for our T2x + * mode: + * + * @SUBSAMPLE_INDICES + * + * | S# | Camera Jitter | subsampleIndices | + * +----+------------------+---------------------+ + * | 0 | ( 0.25, -0.25) | float4(1, 1, 1, 0) | + * | 1 | (-0.25, 0.25) | float4(2, 2, 2, 0) | + * + * These jitter positions assume a bottom-to-top y axis. S# stands for the + * sample number. + * + * More information about temporal supersampling here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * c) If you want to enable spatial multisampling (SMAA S2x): + * + * 1. The scene must be rendered using MSAA 2x. The MSAA 2x buffer must be + * created with: + * - DX10: see below (*) + * - DX10.1: D3D10_STANDARD_MULTISAMPLE_PATTERN or + * - DX11: D3D11_STANDARD_MULTISAMPLE_PATTERN + * + * This allows to ensure that the subsample order matches the table in + * @SUBSAMPLE_INDICES. + * + * (*) In the case of DX10, we refer the reader to: + * - SMAA::detectMSAAOrder and + * - SMAA::msaaReorder + * + * These functions allow to match the standard multisample patterns by + * detecting the subsample order for a specific GPU, and reordering + * them appropriately. + * + * 2. A shader must be run to output each subsample into a separate buffer + * (DX10 is required). You can use SMAASeparate for this purpose, or just do + * it in an existing pass (for example, in the tone mapping pass, which has + * the advantage of feeding tone mapped subsamples to SMAA, which will yield + * better results). + * + * 3. The full SMAA 1x pipeline must be run for each separated buffer, storing + * the results in the final buffer. The second run should alpha blend with + * the existing final buffer using a blending factor of 0.5. + * 'subsampleIndices' must be adjusted as in the SMAA T2x case (see point + * b). + * + * d) If you want to enable temporal supersampling on top of SMAA S2x + * (which actually is SMAA 4x): + * + * 1. SMAA 4x consists on temporally jittering SMAA S2x, so the first step is + * to calculate SMAA S2x for current frame. In this case, 'subsampleIndices' + * must be set as follows: + * + * | F# | S# | Camera Jitter | Net Jitter | subsampleIndices | + * +----+----+--------------------+-------------------+----------------------+ + * | 0 | 0 | ( 0.125, 0.125) | ( 0.375, -0.125) | float4(5, 3, 1, 3) | + * | 0 | 1 | ( 0.125, 0.125) | (-0.125, 0.375) | float4(4, 6, 2, 3) | + * +----+----+--------------------+-------------------+----------------------+ + * | 1 | 2 | (-0.125, -0.125) | ( 0.125, -0.375) | float4(3, 5, 1, 4) | + * | 1 | 3 | (-0.125, -0.125) | (-0.375, 0.125) | float4(6, 4, 2, 4) | + * + * These jitter positions assume a bottom-to-top y axis. F# stands for the + * frame number. S# stands for the sample number. + * + * 2. After calculating SMAA S2x for current frame (with the new subsample + * indices), previous frame must be reprojected as in SMAA T2x mode (see + * point b). + * + * e) If motion blur is used, you may want to do the edge detection pass + * together with motion blur. This has two advantages: + * + * 1. Pixels under heavy motion can be omitted from the edge detection process. + * For these pixels we can just store "no edge", as motion blur will take + * care of them. + * 2. The center pixel tap is reused. + * + * Note that in this case depth testing should be used instead of stenciling, + * as we have to write all the pixels in the motion blur pass. + * + * That's it! + */ + +//----------------------------------------------------------------------------- +// SMAA Presets + +/** + * Note that if you use one of these presets, the following configuration + * macros will be ignored if set in the "Configurable Defines" section. + */ + +#if defined(SMAA_PRESET_LOW) +#define SMAA_THRESHOLD 0.15 +#define SMAA_MAX_SEARCH_STEPS 4 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_MEDIUM) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 8 +#define SMAA_DISABLE_DIAG_DETECTION +#define SMAA_DISABLE_CORNER_DETECTION +#elif defined(SMAA_PRESET_HIGH) +#define SMAA_THRESHOLD 0.1 +#define SMAA_MAX_SEARCH_STEPS 16 +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#define SMAA_CORNER_ROUNDING 25 +#elif defined(SMAA_PRESET_ULTRA) +#define SMAA_THRESHOLD 0.05 +#define SMAA_MAX_SEARCH_STEPS 32 +#define SMAA_MAX_SEARCH_STEPS_DIAG 16 +#define SMAA_CORNER_ROUNDING 25 +#endif + +//----------------------------------------------------------------------------- +// Configurable Defines + +/** + * SMAA_THRESHOLD specifies the threshold or sensitivity to edges. + * Lowering this value you will be able to detect more edges at the expense of + * performance. + * + * Range: [0, 0.5] + * 0.1 is a reasonable value, and allows to catch most visible edges. + * 0.05 is a rather overkill value, that allows to catch 'em all. + * + * If temporal supersampling is used, 0.2 could be a reasonable value, as low + * contrast edges are properly filtered by just 2x. + */ +#ifndef SMAA_THRESHOLD +#define SMAA_THRESHOLD 0.1 +#endif + +/** + * SMAA_DEPTH_THRESHOLD specifies the threshold for depth edge detection. + * + * Range: depends on the depth range of the scene. + */ +#ifndef SMAA_DEPTH_THRESHOLD +#define SMAA_DEPTH_THRESHOLD (0.1 * SMAA_THRESHOLD) +#endif + +/** + * SMAA_MAX_SEARCH_STEPS specifies the maximum steps performed in the + * horizontal/vertical pattern searches, at each side of the pixel. + * + * In number of pixels, it's actually the double. So the maximum line length + * perfectly handled by, for example 16, is 64 (by perfectly, we meant that + * longer lines won't look as good, but still antialiased). + * + * Range: [0, 112] + */ +#ifndef SMAA_MAX_SEARCH_STEPS +#define SMAA_MAX_SEARCH_STEPS 16 +#endif + +/** + * SMAA_MAX_SEARCH_STEPS_DIAG specifies the maximum steps performed in the + * diagonal pattern searches, at each side of the pixel. In this case we jump + * one pixel at time, instead of two. + * + * Range: [0, 20] + * + * On high-end machines it is cheap (between a 0.8x and 0.9x slower for 16 + * steps), but it can have a significant impact on older machines. + * + * Define SMAA_DISABLE_DIAG_DETECTION to disable diagonal processing. + */ +#ifndef SMAA_MAX_SEARCH_STEPS_DIAG +#define SMAA_MAX_SEARCH_STEPS_DIAG 8 +#endif + +/** + * SMAA_CORNER_ROUNDING specifies how much sharp corners will be rounded. + * + * Range: [0, 100] + * + * Define SMAA_DISABLE_CORNER_DETECTION to disable corner processing. + */ +#ifndef SMAA_CORNER_ROUNDING +#define SMAA_CORNER_ROUNDING 25 +#endif + +/** + * If there is an neighbor edge that has SMAA_LOCAL_CONTRAST_FACTOR times + * bigger contrast than current edge, current edge will be discarded. + * + * This allows to eliminate spurious crossing edges, and is based on the fact + * that, if there is too much contrast in a direction, that will hide + * perceptually contrast in the other neighbors. + */ +#ifndef SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR +#define SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR 2.0 +#endif + +/** + * Predicated thresholding allows to better preserve texture details and to + * improve performance, by decreasing the number of detected edges using an + * additional buffer like the light accumulation buffer, object ids or even the + * depth buffer (the depth buffer usage may be limited to indoor or short range + * scenes). + * + * It locally decreases the luma or color threshold if an edge is found in an + * additional buffer (so the global threshold can be higher). + * + * This method was developed by Playstation EDGE MLAA team, and used in + * Killzone 3, by using the light accumulation buffer. More information here: + * http://iryoku.com/aacourse/downloads/06-MLAA-on-PS3.pptx + */ +#ifndef SMAA_PREDICATION +#define SMAA_PREDICATION 0 +#endif + +/** + * Threshold to be used in the additional predication buffer. + * + * Range: depends on the input, so you'll have to find the magic number that + * works for you. + */ +#ifndef SMAA_PREDICATION_THRESHOLD +#define SMAA_PREDICATION_THRESHOLD 0.01 +#endif + +/** + * How much to scale the global threshold used for luma or color edge + * detection when using predication. + * + * Range: [1, 5] + */ +#ifndef SMAA_PREDICATION_SCALE +#define SMAA_PREDICATION_SCALE 2.0 +#endif + +/** + * How much to locally decrease the threshold. + * + * Range: [0, 1] + */ +#ifndef SMAA_PREDICATION_STRENGTH +#define SMAA_PREDICATION_STRENGTH 0.4 +#endif + +/** + * Temporal reprojection allows to remove ghosting artifacts when using + * temporal supersampling. We use the CryEngine 3 method which also introduces + * velocity weighting. This feature is of extreme importance for totally + * removing ghosting. More information here: + * http://iryoku.com/aacourse/downloads/13-Anti-Aliasing-Methods-in-CryENGINE-3.pdf + * + * Note that you'll need to setup a velocity buffer for enabling reprojection. + * For static geometry, saving the previous depth buffer is a viable + * alternative. + */ +#ifndef SMAA_REPROJECTION +#define SMAA_REPROJECTION 0 +#endif + +/** + * SMAA_REPROJECTION_WEIGHT_SCALE controls the velocity weighting. It allows to + * remove ghosting trails behind the moving object, which are not removed by + * just using reprojection. Using low values will exhibit ghosting, while using + * high values will disable temporal supersampling under motion. + * + * Behind the scenes, velocity weighting removes temporal supersampling when + * the velocity of the subsamples differs (meaning they are different objects). + * + * Range: [0, 80] + */ +#ifndef SMAA_REPROJECTION_WEIGHT_SCALE +#define SMAA_REPROJECTION_WEIGHT_SCALE 30.0 +#endif + +/** + * On some compilers, discard and texture cannot be used in vertex shaders. Thus, they need + * to be compiled separately. + */ +#ifndef SMAA_INCLUDE_VS +#define SMAA_INCLUDE_VS 1 +#endif +#ifndef SMAA_INCLUDE_PS +#define SMAA_INCLUDE_PS 1 +#endif + +//----------------------------------------------------------------------------- +// Texture Access Defines + +#ifndef SMAA_AREATEX_SELECT +#if defined(SMAA_HLSL_3) +#define SMAA_AREATEX_SELECT(sample) sample.ra +#else +#define SMAA_AREATEX_SELECT(sample) sample.rg +#endif +#endif + +#ifndef SMAA_SEARCHTEX_SELECT +#define SMAA_SEARCHTEX_SELECT(sample) sample.r +#endif + +#ifndef SMAA_DECODE_VELOCITY +#define SMAA_DECODE_VELOCITY(sample) sample.rg +#endif + +//----------------------------------------------------------------------------- +// Non-Configurable Defines + +#define SMAA_AREATEX_MAX_DISTANCE 16 +#define SMAA_AREATEX_MAX_DISTANCE_DIAG 20 +#define SMAA_AREATEX_PIXEL_SIZE (1.0 / float2(160.0, 560.0)) +#define SMAA_AREATEX_SUBTEX_SIZE (1.0 / 7.0) +#define SMAA_SEARCHTEX_SIZE float2(66.0, 33.0) +#define SMAA_SEARCHTEX_PACKED_SIZE float2(64.0, 16.0) +#define SMAA_CORNER_ROUNDING_NORM (float(SMAA_CORNER_ROUNDING) / 100.0) + +//----------------------------------------------------------------------------- +// Porting Functions + +#if defined(SMAA_HLSL_3) +#ifndef SMAA_FLIP_Y +#define SMAA_FLIP_Y 0 +#endif // SMAA_FLIP_Y +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroPoint(tex, coord) tex2Dlod(tex, float4(coord, 0.0, 0.0)) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex2Dlod(tex, float4(coord + offset * SMAA_RT_METRICS.xy, 0.0, 0.0)) +#define SMAASample(tex, coord) tex2D(tex, coord) +#define SMAASamplePoint(tex, coord) tex2D(tex, coord) +#define SMAASampleOffset(tex, coord, offset) tex2D(tex, coord + offset * SMAA_RT_METRICS.xy) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#endif +#if defined(SMAA_HLSL_4) || defined(SMAA_HLSL_4_1) +#ifndef SMAA_FLIP_Y +#define SMAA_FLIP_Y 0 +#endif // SMAA_FLIP_Y +SamplerState LinearSampler { Filter = MIN_MAG_LINEAR_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +SamplerState PointSampler { Filter = MIN_MAG_MIP_POINT; AddressU = Clamp; AddressV = Clamp; }; +#define SMAATexture2D(tex) Texture2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) tex.SampleLevel(LinearSampler, coord, 0) +#define SMAASampleLevelZeroPoint(tex, coord) tex.SampleLevel(PointSampler, coord, 0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) tex.SampleLevel(LinearSampler, coord, 0, offset) +#define SMAASample(tex, coord) tex.Sample(LinearSampler, coord) +#define SMAASamplePoint(tex, coord) tex.Sample(PointSampler, coord) +#define SMAASampleOffset(tex, coord, offset) tex.Sample(LinearSampler, coord, offset) +#define SMAA_FLATTEN [flatten] +#define SMAA_BRANCH [branch] +#define SMAATexture2DMS2(tex) Texture2DMS tex +#define SMAALoad(tex, pos, sample) tex.Load(pos, sample) +#if defined(SMAA_HLSL_4_1) +#define SMAAGather(tex, coord) tex.Gather(LinearSampler, coord, 0) +#endif +#endif +#if defined(SMAA_GLSL_3) || defined(SMAA_GLSL_4) +#ifndef SMAA_FLIP_Y +#define SMAA_FLIP_Y 1 +#endif // SMAA_FLIP_Y + +#define SMAATexture2D(tex) sampler2D tex +#define SMAATexturePass2D(tex) tex +#define SMAASampleLevelZero(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroPoint(tex, coord) textureLod(tex, coord, 0.0) +#define SMAASampleLevelZeroOffset(tex, coord, offset) textureLodOffset(tex, coord, 0.0, offset) +#define SMAASample(tex, coord) texture(tex, coord) +#define SMAASamplePoint(tex, coord) texture(tex, coord) +#define SMAASampleOffset(tex, coord, offset) texture(tex, coord, offset) +#define SMAA_FLATTEN +#define SMAA_BRANCH +#define lerp(a, b, t) mix(a, b, t) +#define saturate(a) clamp(a, 0.0, 1.0) +#if defined(SMAA_GLSL_4) +#define mad(a, b, c) fma(a, b, c) +#define SMAAGather(tex, coord) textureGather(tex, coord) +#else +#define mad(a, b, c) (a * b + c) +#endif +#define float2 vec2 +#define float3 vec3 +#define float4 vec4 +#define int2 ivec2 +#define int3 ivec3 +#define int4 ivec4 +#define bool2 bvec2 +#define bool3 bvec3 +#define bool4 bvec4 +#endif + +#if !defined(SMAA_HLSL_3) && !defined(SMAA_HLSL_4) && !defined(SMAA_HLSL_4_1) && !defined(SMAA_GLSL_3) && !defined(SMAA_GLSL_4) && !defined(SMAA_CUSTOM_SL) +#error you must define the shading language: SMAA_HLSL_*, SMAA_GLSL_* or SMAA_CUSTOM_SL +#endif + + +#if SMAA_FLIP_Y + +#define API_V_DIR(v) -(v) +#define API_V_COORD(v) (1.0 - v) +#define API_V_BELOW(v1, v2) v1 < v2 +#define API_V_ABOVE(v1, v2) v1 > v2 + +#else // VULKAN_FLIP + +#define API_V_DIR(v) v +#define API_V_COORD(v) v +#define API_V_BELOW(v1, v2) v1 > v2 +#define API_V_ABOVE(v1, v2) v1 < v2 + +#endif // VULKAN_FLIP + + +//----------------------------------------------------------------------------- +// Misc functions + +#if SMAA_INCLUDE_PS +/** + * Gathers current pixel, and the top-left neighbors. + */ +float3 SMAAGatherNeighbours(float2 texcoord, + float4 offset[3], + SMAATexture2D(tex)) { + #ifdef SMAAGather + + #if SMAA_FLIP_Y + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, 0.5)).zwy; + #else // SMAA_FLIP_Y + return SMAAGather(tex, texcoord + SMAA_RT_METRICS.xy * float2(-0.5, -0.5)).grb; + #endif // SMAA_FLIP_Y + + #else // SMAAGather + float P = SMAASamplePoint(tex, texcoord).r; + float Pleft = SMAASamplePoint(tex, offset[0].xy).r; + float Ptop = SMAASamplePoint(tex, offset[0].zw).r; + return float3(P, Pleft, Ptop); + #endif +} + +/** + * Adjusts the threshold by means of predication. + */ +float2 SMAACalculatePredicatedThreshold(float2 texcoord, + float4 offset[3], + SMAATexture2D(predicationTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(predicationTex)); + float2 delta = abs(neighbours.xx - neighbours.yz); + float2 edges = step(SMAA_PREDICATION_THRESHOLD, delta); + return SMAA_PREDICATION_SCALE * SMAA_THRESHOLD * (1.0 - SMAA_PREDICATION_STRENGTH * edges); +} + +#endif // SMAA_INCLUDE_PS + +/** + * Conditional move: + */ +void SMAAMovc(bool2 cond, inout float2 variable, float2 value) { + SMAA_FLATTEN if (cond.x) variable.x = value.x; + SMAA_FLATTEN if (cond.y) variable.y = value.y; +} + +void SMAAMovc(bool4 cond, inout float4 variable, float4 value) { + SMAAMovc(cond.xy, variable.xy, value.xy); + SMAAMovc(cond.zw, variable.zw, value.zw); +} + + +#if SMAA_INCLUDE_VS +//----------------------------------------------------------------------------- +// Vertex Shaders + +/** + * Edge Detection Vertex Shader + */ +void SMAAEdgeDetectionVS(float2 texcoord, + out float4 offset[3]) { + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-1.0, 0.0, 0.0, API_V_DIR(-1.0)), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, API_V_DIR(1.0)), texcoord.xyxy); + offset[2] = mad(SMAA_RT_METRICS.xyxy, float4(-2.0, 0.0, 0.0, API_V_DIR(-2.0)), texcoord.xyxy); +} + +/** + * Blend Weight Calculation Vertex Shader + */ +void SMAABlendingWeightCalculationVS(float2 texcoord, + out float2 pixcoord, + out float4 offset[3]) { + pixcoord = texcoord * SMAA_RT_METRICS.zw; + + // We will use these offsets for the searches later on (see @PSEUDO_GATHER4): + offset[0] = mad(SMAA_RT_METRICS.xyxy, float4(-0.25, API_V_DIR(-0.125), 1.25, API_V_DIR(-0.125)), texcoord.xyxy); + offset[1] = mad(SMAA_RT_METRICS.xyxy, float4(-0.125, API_V_DIR(-0.25), -0.125, API_V_DIR(1.25)), texcoord.xyxy); + + // And these for the searches, they indicate the ends of the loops: + offset[2] = mad(SMAA_RT_METRICS.xxyy, + float4(-2.0, 2.0, API_V_DIR(-2.0), API_V_DIR(2.0)) * float(SMAA_MAX_SEARCH_STEPS), + float4(offset[0].xz, offset[1].yw)); +} + +/** + * Neighborhood Blending Vertex Shader + */ +void SMAANeighborhoodBlendingVS(float2 texcoord, + out float4 offset) { + offset = mad(SMAA_RT_METRICS.xyxy, float4( 1.0, 0.0, 0.0, API_V_DIR(1.0)), texcoord.xyxy); +} +#endif // SMAA_INCLUDE_VS + +#if SMAA_INCLUDE_PS +//----------------------------------------------------------------------------- +// Edge Detection Pixel Shaders (First Pass) + +/** + * Luma Edge Detection + * + * IMPORTANT NOTICE: luma edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAALumaEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, SMAATexturePass2D(predicationTex)); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate lumas: + float3 weights = float3(0.2126, 0.7152, 0.0722); + float L = dot(SMAASamplePoint(colorTex, texcoord).rgb, weights); + + float Lleft = dot(SMAASamplePoint(colorTex, offset[0].xy).rgb, weights); + float Ltop = dot(SMAASamplePoint(colorTex, offset[0].zw).rgb, weights); + + // We do the usual threshold: + float4 delta; + delta.xy = abs(L - float2(Lleft, Ltop)); + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float Lright = dot(SMAASamplePoint(colorTex, offset[1].xy).rgb, weights); + float Lbottom = dot(SMAASamplePoint(colorTex, offset[1].zw).rgb, weights); + delta.zw = abs(L - float2(Lright, Lbottom)); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float Lleftleft = dot(SMAASamplePoint(colorTex, offset[2].xy).rgb, weights); + float Ltoptop = dot(SMAASamplePoint(colorTex, offset[2].zw).rgb, weights); + delta.zw = abs(float2(Lleft, Ltop) - float2(Lleftleft, Ltoptop)); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Color Edge Detection + * + * IMPORTANT NOTICE: color edge detection requires gamma-corrected colors, and + * thus 'colorTex' should be a non-sRGB texture. + */ +float2 SMAAColorEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(colorTex) + #if SMAA_PREDICATION + , SMAATexture2D(predicationTex) + #endif + ) { + // Calculate the threshold: + #if SMAA_PREDICATION + float2 threshold = SMAACalculatePredicatedThreshold(texcoord, offset, predicationTex); + #else + float2 threshold = float2(SMAA_THRESHOLD, SMAA_THRESHOLD); + #endif + + // Calculate color deltas: + float4 delta; + float3 C = SMAASamplePoint(colorTex, texcoord).rgb; + + float3 Cleft = SMAASamplePoint(colorTex, offset[0].xy).rgb; + float3 t = abs(C - Cleft); + delta.x = max(max(t.r, t.g), t.b); + + float3 Ctop = SMAASamplePoint(colorTex, offset[0].zw).rgb; + t = abs(C - Ctop); + delta.y = max(max(t.r, t.g), t.b); + + // We do the usual threshold: + float2 edges = step(threshold, delta.xy); + + // Then discard if there is no edge: + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + // Calculate right and bottom deltas: + float3 Cright = SMAASamplePoint(colorTex, offset[1].xy).rgb; + t = abs(C - Cright); + delta.z = max(max(t.r, t.g), t.b); + + float3 Cbottom = SMAASamplePoint(colorTex, offset[1].zw).rgb; + t = abs(C - Cbottom); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the maximum delta in the direct neighborhood: + float2 maxDelta = max(delta.xy, delta.zw); + + // Calculate left-left and top-top deltas: + float3 Cleftleft = SMAASamplePoint(colorTex, offset[2].xy).rgb; + t = abs(C - Cleftleft); + delta.z = max(max(t.r, t.g), t.b); + + float3 Ctoptop = SMAASamplePoint(colorTex, offset[2].zw).rgb; + t = abs(C - Ctoptop); + delta.w = max(max(t.r, t.g), t.b); + + // Calculate the final maximum delta: + maxDelta = max(maxDelta.xy, delta.zw); + float finalDelta = max(maxDelta.x, maxDelta.y); + + // Local contrast adaptation: + edges.xy *= step(finalDelta, SMAA_LOCAL_CONTRAST_ADAPTATION_FACTOR * delta.xy); + + return edges; +} + +/** + * Depth Edge Detection + */ +float2 SMAADepthEdgeDetectionPS(float2 texcoord, + float4 offset[3], + SMAATexture2D(depthTex)) { + float3 neighbours = SMAAGatherNeighbours(texcoord, offset, SMAATexturePass2D(depthTex)); + float2 delta = abs(neighbours.xx - float2(neighbours.y, neighbours.z)); + float2 edges = step(SMAA_DEPTH_THRESHOLD, delta); + + if (dot(edges, float2(1.0, 1.0)) == 0.0) + discard; + + return edges; +} + +//----------------------------------------------------------------------------- +// Diagonal Search Functions + +#if !defined(SMAA_DISABLE_DIAG_DETECTION) + +/** + * Allows to decode two binary values from a bilinear-filtered access. + */ +float2 SMAADecodeDiagBilinearAccess(float2 e) { + // Bilinear access for fetching 'e' have a 0.25 offset, and we are + // interested in the R and G edges: + // + // +---G---+-------+ + // | x o R x | + // +-------+-------+ + // + // Then, if one of these edge is enabled: + // Red: (0.75 * X + 0.25 * 1) => 0.25 or 1.0 + // Green: (0.75 * 1 + 0.25 * X) => 0.75 or 1.0 + // + // This function will unpack the values (mad + mul + round): + // wolframalpha.com: round(x * abs(5 * x - 5 * 0.75)) plot 0 to 1 + e.r = e.r * abs(5.0 * e.r - 5.0 * 0.75); + return round(e); +} + +float4 SMAADecodeDiagBilinearAccess(float4 e) { + e.rb = e.rb * abs(5.0 * e.rb - 5.0 * 0.75); + return round(e); +} + +/** + * These functions allows to perform diagonal pattern searches. + */ +float2 SMAASearchDiag1(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + dir.y = API_V_DIR(dir.y); + float4 coord = float4(texcoord, -1.0, 1.0); + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +float2 SMAASearchDiag2(SMAATexture2D(edgesTex), float2 texcoord, float2 dir, out float2 e) { + dir.y = API_V_DIR(dir.y); + float4 coord = float4(texcoord, -1.0, 1.0); + coord.x += 0.25 * SMAA_RT_METRICS.x; // See @SearchDiag2Optimization + float3 t = float3(SMAA_RT_METRICS.xy, 1.0); + while (coord.z < float(SMAA_MAX_SEARCH_STEPS_DIAG - 1) && + coord.w > 0.9) { + coord.xyz = mad(t, float3(dir, 1.0), coord.xyz); + + // @SearchDiag2Optimization + // Fetch both edges at once using bilinear filtering: + e = SMAASampleLevelZero(edgesTex, coord.xy).rg; + e = SMAADecodeDiagBilinearAccess(e); + + // Non-optimized version: + // e.g = SMAASampleLevelZero(edgesTex, coord.xy).g; + // e.r = SMAASampleLevelZeroOffset(edgesTex, coord.xy, int2(1, 0)).r; + + coord.w = dot(e, float2(0.5, 0.5)); + } + return coord.zw; +} + +/** + * Similar to SMAAArea, this calculates the area corresponding to a certain + * diagonal distance and crossing edges 'e'. + */ +float2 SMAAAreaDiag(SMAATexture2D(areaTex), float2 dist, float2 e, float offset) { + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE_DIAG, SMAA_AREATEX_MAX_DISTANCE_DIAG), e, dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Diagonal areas are on the second half of the texture: + texcoord.x += 0.5; + + // Move to proper place, according to the subpixel offset: + texcoord.y += SMAA_AREATEX_SUBTEX_SIZE * offset; + + texcoord.y = API_V_COORD(texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +/** + * This searches for diagonal patterns and returns the corresponding weights. + */ +float2 SMAACalculateDiagWeights(SMAATexture2D(edgesTex), SMAATexture2D(areaTex), float2 texcoord, float2 e, float4 subsampleIndices) { + float2 weights = float2(0.0, 0.0); + + // Search for the line ends: + float4 d; + float2 end; + if (e.r > 0.0) { + d.xz = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, 1.0), end); + d.x += float(end.y > 0.9); + } else + d.xz = float2(0.0, 0.0); + d.yw = SMAASearchDiag1(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, -1.0), end); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x + 0.25, API_V_DIR(d.x), d.y, API_V_DIR(-d.y - 0.25)), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.xy = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).rg; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).rg; + c.yxwz = SMAADecodeDiagBilinearAccess(c.xyzw); + + // Non-optimized version: + // float4 coords = mad(float4(-d.x, d.x, d.y, -d.y), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + // float4 c; + // c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + // c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, 0)).r; + // c.z = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).g; + // c.w = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, -1)).r; + + // Merge crossing edges at each side into a single value: + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.z); + } + + // Search for the line ends: + d.xz = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(-1.0, -1.0), end); + if (SMAASampleLevelZeroOffset(edgesTex, texcoord, int2(1, 0)).r > 0.0) { + d.yw = SMAASearchDiag2(SMAATexturePass2D(edgesTex), texcoord, float2(1.0, 1.0), end); + d.y += float(end.y > 0.9); + } else + d.yw = float2(0.0, 0.0); + + SMAA_BRANCH + if (d.x + d.y > 2.0) { // d.x + d.y + 1 > 3 + // Fetch the crossing edges: + float4 coords = mad(float4(-d.x, API_V_DIR(-d.x), d.y, API_V_DIR(d.y)), SMAA_RT_METRICS.xyxy, texcoord.xyxy); + float4 c; + c.x = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2(-1, 0)).g; + c.y = SMAASampleLevelZeroOffset(edgesTex, coords.xy, int2( 0, API_V_DIR(-1))).r; + c.zw = SMAASampleLevelZeroOffset(edgesTex, coords.zw, int2( 1, 0)).gr; + float2 cc = mad(float2(2.0, 2.0), c.xz, c.yw); + + // Remove the crossing edge if we didn't found the end of the line: + SMAAMovc(bool2(step(0.9, d.zw)), cc, float2(0.0, 0.0)); + + // Fetch the areas for this line: + weights += SMAAAreaDiag(SMAATexturePass2D(areaTex), d.xy, cc, subsampleIndices.w).gr; + } + + return weights; +} +#endif + +//----------------------------------------------------------------------------- +// Horizontal/Vertical Search Functions + +/** + * This allows to determine how much length should we add in the last step + * of the searches. It takes the bilinearly interpolated edge (see + * @PSEUDO_GATHER4), and adds 0, 1 or 2, depending on which edges and + * crossing edges are active. + */ +float SMAASearchLength(SMAATexture2D(searchTex), float2 e, float offset) { + // The texture is flipped vertically, with left and right cases taking half + // of the space horizontally: + float2 scale = SMAA_SEARCHTEX_SIZE * float2(0.5, -1.0); + float2 bias = SMAA_SEARCHTEX_SIZE * float2(offset, 1.0); + + // Scale and bias to access texel centers: + scale += float2(-1.0, 1.0); + bias += float2( 0.5, -0.5); + + // Convert from pixel coordinates to texcoords: + // (We use SMAA_SEARCHTEX_PACKED_SIZE because the texture is cropped) + scale *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + bias *= 1.0 / SMAA_SEARCHTEX_PACKED_SIZE; + + float2 coord = mad(scale, e, bias); + coord.y = API_V_COORD(coord.y); + + // Lookup the search texture: + return SMAA_SEARCHTEX_SELECT(SMAASampleLevelZero(searchTex, coord)); +} + +/** + * Horizontal/vertical search functions for the 2nd pass. + */ +float SMAASearchXLeft(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + /** + * @PSEUDO_GATHER4 + * This texcoord has been offset by (-0.25, -0.125) in the vertex shader to + * sample between edge, thus fetching four edges in a row. + * Sampling with different offsets in each direction allows to disambiguate + * which edges are active from the four fetched ones. + */ + float2 e = float2(0.0, 1.0); + while (texcoord.x > end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0), 3.25); + return mad(SMAA_RT_METRICS.x, offset, texcoord.x); + + // Non-optimized version: + // We correct the previous (-0.25, -0.125) offset we applied: + // texcoord.x += 0.25 * SMAA_RT_METRICS.x; + + // The searches are bias by 1, so adjust the coords accordingly: + // texcoord.x += SMAA_RT_METRICS.x; + + // Disambiguate the length added by the last step: + // texcoord.x += 2.0 * SMAA_RT_METRICS.x; // Undo last step + // texcoord.x -= SMAA_RT_METRICS.x * (255.0 / 127.0) * SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.0); + // return mad(SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchXRight(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(0.0, 1.0); + while (texcoord.x < end && + e.g > 0.8281 && // Is there some edge not activated? + e.r == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(2.0, 0.0), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.x, offset, texcoord.x); +} + +float SMAASearchYUp(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (API_V_BELOW(texcoord.y, end) && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(-float2(0.0, API_V_DIR(2.0)), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.0), 3.25); + return mad(SMAA_RT_METRICS.y, API_V_DIR(offset), texcoord.y); +} + +float SMAASearchYDown(SMAATexture2D(edgesTex), SMAATexture2D(searchTex), float2 texcoord, float end) { + float2 e = float2(1.0, 0.0); + while (API_V_ABOVE(texcoord.y, end) && + e.r > 0.8281 && // Is there some edge not activated? + e.g == 0.0) { // Or is there a crossing edge that breaks the line? + e = SMAASampleLevelZero(edgesTex, texcoord).rg; + texcoord = mad(float2(0.0, API_V_DIR(2.0)), SMAA_RT_METRICS.xy, texcoord); + } + float offset = mad(-(255.0 / 127.0), SMAASearchLength(SMAATexturePass2D(searchTex), e.gr, 0.5), 3.25); + return mad(-SMAA_RT_METRICS.y, API_V_DIR(offset), texcoord.y); +} + +/** + * Ok, we have the distance and both crossing edges. So, what are the areas + * at each side of current edge? + */ +float2 SMAAArea(SMAATexture2D(areaTex), float2 dist, float e1, float e2, float offset) { + // Rounding prevents precision errors of bilinear filtering: + float2 texcoord = mad(float2(SMAA_AREATEX_MAX_DISTANCE, SMAA_AREATEX_MAX_DISTANCE), round(4.0 * float2(e1, e2)), dist); + + // We do a scale and bias for mapping to texel space: + texcoord = mad(SMAA_AREATEX_PIXEL_SIZE, texcoord, 0.5 * SMAA_AREATEX_PIXEL_SIZE); + + // Move to proper place, according to the subpixel offset: + texcoord.y = mad(SMAA_AREATEX_SUBTEX_SIZE, offset, texcoord.y); + + texcoord.y = API_V_COORD(texcoord.y); + + // Do it! + return SMAA_AREATEX_SELECT(SMAASampleLevelZero(areaTex, texcoord)); +} + +//----------------------------------------------------------------------------- +// Corner Detection Functions + +void SMAADetectHorizontalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; // Reduce blending for pixels in the center of a line. + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, API_V_DIR(1))).r; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, API_V_DIR(1))).r; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(0, API_V_DIR(-2))).r; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(1, API_V_DIR(-2))).r; + + weights *= saturate(factor); + #endif +} + +void SMAADetectVerticalCornerPattern(SMAATexture2D(edgesTex), inout float2 weights, float4 texcoord, float2 d) { + #if !defined(SMAA_DISABLE_CORNER_DETECTION) + float2 leftRight = step(d.xy, d.yx); + float2 rounding = (1.0 - SMAA_CORNER_ROUNDING_NORM) * leftRight; + + rounding /= leftRight.x + leftRight.y; + + float2 factor = float2(1.0, 1.0); + factor.x -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2( 1, 0)).g; + factor.x -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2( 1, API_V_DIR(1))).g; + factor.y -= rounding.x * SMAASampleLevelZeroOffset(edgesTex, texcoord.xy, int2(-2, 0)).g; + factor.y -= rounding.y * SMAASampleLevelZeroOffset(edgesTex, texcoord.zw, int2(-2, API_V_DIR(1))).g; + + weights *= saturate(factor); + #endif +} + +//----------------------------------------------------------------------------- +// Blending Weight Calculation Pixel Shader (Second Pass) + +float4 SMAABlendingWeightCalculationPS(float2 texcoord, + float2 pixcoord, + float4 offset[3], + SMAATexture2D(edgesTex), + SMAATexture2D(areaTex), + SMAATexture2D(searchTex), + float4 subsampleIndices) { // Just pass zero for SMAA 1x, see @SUBSAMPLE_INDICES. + float4 weights = float4(0.0, 0.0, 0.0, 0.0); + + float2 e = SMAASample(edgesTex, texcoord).rg; + + SMAA_BRANCH + if (e.g > 0.0) { // Edge at north + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + // Diagonals have both north and west edges, so searching for them in + // one of the boundaries is enough. + weights.rg = SMAACalculateDiagWeights(SMAATexturePass2D(edgesTex), SMAATexturePass2D(areaTex), texcoord, e, subsampleIndices); + + // We give priority to diagonals, so if we find a diagonal we skip + // horizontal/vertical processing. + SMAA_BRANCH + if (weights.r == -weights.g) { // weights.r + weights.g == 0.0 + #endif + + float2 d; + + // Find the distance to the left: + float3 coords; + coords.x = SMAASearchXLeft(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].xy, offset[2].x); + coords.y = offset[1].y; // offset[1].y = texcoord.y - 0.25 * SMAA_RT_METRICS.y (@CROSSING_OFFSET) + d.x = coords.x; + + // Now fetch the left crossing edges, two at a time using bilinear + // filtering. Sampling at -0.25 (see @CROSSING_OFFSET) enables to + // discern what value each edge has: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).r; + + // Find the distance to the right: + coords.z = SMAASearchXRight(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[0].zw, offset[2].y); + d.y = coords.z; + + // We want the distances to be in pixel units (doing this here allow to + // better interleave arithmetic and memory accesses): + d = abs(round(mad(SMAA_RT_METRICS.zz, d, -pixcoord.xx))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the right crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.zy, int2(1, 0)).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + weights.rg = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.y); + + // Fix corners: + coords.y = texcoord.y; + SMAADetectHorizontalCornerPattern(SMAATexturePass2D(edgesTex), weights.rg, coords.xyzy, d); + + #if !defined(SMAA_DISABLE_DIAG_DETECTION) + } else + e.r = 0.0; // Skip vertical processing. + #endif + } + + SMAA_BRANCH + if (e.r > 0.0) { // Edge at west + float2 d; + + // Find the distance to the top: + float3 coords; + coords.y = SMAASearchYUp(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].xy, offset[2].z); + coords.x = offset[0].x; // offset[1].x = texcoord.x - 0.25 * SMAA_RT_METRICS.x; + d.x = coords.y; + + // Fetch the top crossing edges: + float e1 = SMAASampleLevelZero(edgesTex, coords.xy).g; + + // Find the distance to the bottom: + coords.z = SMAASearchYDown(SMAATexturePass2D(edgesTex), SMAATexturePass2D(searchTex), offset[1].zw, offset[2].w); + d.y = coords.z; + + // We want the distances to be in pixel units: + d = abs(round(mad(SMAA_RT_METRICS.ww, d, -pixcoord.yy))); + + // SMAAArea below needs a sqrt, as the areas texture is compressed + // quadratically: + float2 sqrt_d = sqrt(d); + + // Fetch the bottom crossing edges: + float e2 = SMAASampleLevelZeroOffset(edgesTex, coords.xz, int2(0, API_V_DIR(1))).g; + + // Get the area for this direction: + weights.ba = SMAAArea(SMAATexturePass2D(areaTex), sqrt_d, e1, e2, subsampleIndices.x); + + // Fix corners: + coords.x = texcoord.x; + SMAADetectVerticalCornerPattern(SMAATexturePass2D(edgesTex), weights.ba, coords.xyxz, d); + } + + return weights; +} + +//----------------------------------------------------------------------------- +// Neighborhood Blending Pixel Shader (Third Pass) + +float4 SMAANeighborhoodBlendingPS(float2 texcoord, + float4 offset, + SMAATexture2D(colorTex), + SMAATexture2D(blendTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + // Fetch the blending weights for current pixel: + float4 a; + a.x = SMAASample(blendTex, offset.xy).a; // Right + a.y = SMAASample(blendTex, offset.zw).g; // Top + a.wz = SMAASample(blendTex, texcoord).xz; // Bottom / Left + + // Is there any blending weight with a value greater than 0.0? + SMAA_BRANCH + if (dot(a, float4(1.0, 1.0, 1.0, 1.0)) < 1e-5) { + float4 color = SMAASampleLevelZero(colorTex, texcoord); + + #if SMAA_REPROJECTION + float2 velocity = SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, texcoord)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } else { + bool h = max(a.x, a.z) > max(a.y, a.w); // max(horizontal) > max(vertical) + + // Calculate the blending offsets: + float4 blendingOffset = float4(0.0, API_V_DIR(a.y), 0.0, API_V_DIR(a.w)); + float2 blendingWeight = a.yw; + SMAAMovc(bool4(h, h, h, h), blendingOffset, float4(a.x, 0.0, a.z, 0.0)); + SMAAMovc(bool2(h, h), blendingWeight, a.xz); + blendingWeight /= dot(blendingWeight, float2(1.0, 1.0)); + + // Calculate the texture coordinates: + float4 blendingCoord = mad(blendingOffset, float4(SMAA_RT_METRICS.xy, -SMAA_RT_METRICS.xy), texcoord.xyxy); + + // We exploit bilinear filtering to mix current pixel with the chosen + // neighbor: + float4 color = blendingWeight.x * SMAASampleLevelZero(colorTex, blendingCoord.xy); + color += blendingWeight.y * SMAASampleLevelZero(colorTex, blendingCoord.zw); + + #if SMAA_REPROJECTION + // Antialias velocity for proper reprojection in a later stage: + float2 velocity = blendingWeight.x * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.xy)); + velocity += blendingWeight.y * SMAA_DECODE_VELOCITY(SMAASampleLevelZero(velocityTex, blendingCoord.zw)); + + // Pack velocity into the alpha channel: + color.a = sqrt(5.0 * length(velocity)); + #endif + + return color; + } +} + +//----------------------------------------------------------------------------- +// Temporal Resolve Pixel Shader (Optional Pass) + +float4 SMAAResolvePS(float2 texcoord, + SMAATexture2D(currentColorTex), + SMAATexture2D(previousColorTex) + #if SMAA_REPROJECTION + , SMAATexture2D(velocityTex) + #endif + ) { + #if SMAA_REPROJECTION + // Velocity is assumed to be calculated for motion blur, so we need to + // inverse it for reprojection: + float2 velocity = -SMAA_DECODE_VELOCITY(SMAASamplePoint(velocityTex, texcoord).rg); + + // Fetch current pixel: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + + // Reproject current coordinates and fetch previous pixel: + float4 previous = SMAASamplePoint(previousColorTex, texcoord + velocity); + + // Attenuate the previous pixel if the velocity is different: + float delta = abs(current.a * current.a - previous.a * previous.a) / 5.0; + float weight = 0.5 * saturate(1.0 - sqrt(delta) * SMAA_REPROJECTION_WEIGHT_SCALE); + + // Blend the pixels according to the calculated weight: + return lerp(current, previous, weight); + #else + // Just blend the pixels: + float4 current = SMAASamplePoint(currentColorTex, texcoord); + float4 previous = SMAASamplePoint(previousColorTex, texcoord); + return lerp(current, previous, 0.5); + #endif +} + +//----------------------------------------------------------------------------- +// Separate Multisamples Pixel Shader (Optional Pass) + +#ifdef SMAALoad +void SMAASeparatePS(float4 position, + float2 texcoord, + out float4 target0, + out float4 target1, + SMAATexture2DMS2(colorTexMS)) { + int2 pos = int2(position.xy); + target0 = SMAALoad(colorTexMS, pos, 0); + target1 = SMAALoad(colorTexMS, pos, 1); +} +#endif + +//----------------------------------------------------------------------------- +#endif // SMAA_INCLUDE_PS diff --git a/engine/shaders/smaa_common.glsl b/engine/shaders/smaa_common.glsl new file mode 100644 index 0000000..46a2fa5 --- /dev/null +++ b/engine/shaders/smaa_common.glsl @@ -0,0 +1,12 @@ +#define SMAA_RT_METRICS viewport + +#define SMAA_PRESET_ULTRA 1 +#define SMAA_GLSL_4 1 +#define SMAA_PREDICATION 1 + +layout(std430, push_constant, binding = 2) uniform PushConstant { + vec4 viewport; + mat4 correctionMatrix; +}; + +#include "smaa.glsl" diff --git a/engine/shaders/text.frag.glsl b/engine/shaders/text.frag.glsl new file mode 100644 index 0000000..27a632a --- /dev/null +++ b/engine/shaders/text.frag.glsl @@ -0,0 +1,15 @@ +layout(location = 0) in vec2 inUV; + +layout (location = 0) out vec4 outColor; + +layout (binding = 3) uniform sampler2D fontSampler; + +vec4 toSRGB(vec4 v) { + return vec4(pow(v.rgb, vec3(2.2)), v.a); +} + +void main() { + outColor = vec4(vec3(texture(fontSampler, inUV).r), texture(fontSampler, inUV).r); + + outColor = toSRGB(outColor); +} diff --git a/engine/shaders/text.vert.glsl b/engine/shaders/text.vert.glsl new file mode 100644 index 0000000..2a2ac86 --- /dev/null +++ b/engine/shaders/text.vert.glsl @@ -0,0 +1,61 @@ +#extension GL_GOOGLE_include_directive : require +#include "font.glsl" + +layout(location = 0) out vec2 outUV; + +struct GlyphInstance { + uint position, index, instance; +}; + +struct StringInstance { + uint xy; +}; + +struct GlyphMetric { + uint x0_y0, x1_y1; + float xoff, yoff; + float xoff2, yoff2; +}; + +layout(std430, binding = 0) buffer readonly InstanceBuffer { + GlyphInstance instances[]; +}; + +layout(std430, binding = 1) buffer readonly MetricBuffer { + GlyphMetric metrics[]; +}; + +layout(std430, binding = 2) buffer readonly StringBuffer { + StringInstance strings[]; +}; + +layout(push_constant, binding = 4) uniform readonly PushConstant{ + vec2 screenSize; + mat4 mvp; +}; + +void main() { + const GlyphInstance instance = instances[gl_InstanceIndex]; + const GlyphMetric metric = metrics[getLower(instance.index)]; + + vec2 p = vec2(gl_VertexIndex % 2, gl_VertexIndex / 2); + + const vec2 p0 = uint_to_vec2(metric.x0_y0); + const vec2 p1 = uint_to_vec2(metric.x1_y1); + + outUV = (p0 + (p1 - p0) * p) / vec2(2048, 1132); + + p *= vec2(metric.xoff2 - metric.xoff, metric.yoff2 - metric.yoff); + p += vec2(metric.xoff, metric.yoff); + p += fixed2_to_vec2(instance.position); + p += vec2(0, 18.2316); + + const StringInstance string = strings[instance.instance]; + + p += fixed2_to_vec2(string.xy); + p *= vec2(1, -1); + p *= 2.0 / screenSize; + p += vec2(-1, 1); + + gl_Position = mvp * vec4(p, 0.0, 1.0); +} diff --git a/engine/shaders/ui.frag.glsl b/engine/shaders/ui.frag.glsl new file mode 100644 index 0000000..bbef2cc --- /dev/null +++ b/engine/shaders/ui.frag.glsl @@ -0,0 +1,22 @@ +layout (location = 0) in vec2 inUV; +layout (location = 1) in vec4 inColor; + +layout (location = 0) out vec4 outColor; + +layout (binding = 2) uniform sampler2D colorSampler; + +vec4 toSRGB(vec4 v) { + return vec4(pow(v.rgb, vec3(2.2)), v.a); +} + +void main() { + if(inColor.a == 0.0) + discard; + + if(textureSize(colorSampler, 0).x > 1) + outColor = texture(colorSampler, inUV); + else + outColor = inColor; + + outColor = toSRGB(outColor); +} diff --git a/engine/shaders/ui.vert.glsl b/engine/shaders/ui.vert.glsl new file mode 100644 index 0000000..c88733d --- /dev/null +++ b/engine/shaders/ui.vert.glsl @@ -0,0 +1,38 @@ +#extension GL_GOOGLE_include_directive : require +#include "font.glsl" + +layout (location = 0) out vec2 outUV; +layout (location = 1) out vec4 outColor; + +struct ElementInstance { + vec4 color; + uint position, size; + uint padding[2]; +}; + +layout(std430, binding = 0) buffer readonly ElementBuffer { + ElementInstance elements[]; +}; + +layout(push_constant, binding = 1) uniform readonly PushConstant{ + vec2 screenSize; + mat4 mvp; +}; + +void main() { + const ElementInstance instance = elements[gl_InstanceIndex]; + + vec2 p = vec2(gl_VertexIndex % 2, gl_VertexIndex / 2); + outUV = p; + + p *= fixed2_to_vec2(instance.size); + p += fixed2_to_vec2(instance.position); + p *= vec2(1, -1); + p *= 2.0 / screenSize; + p += vec2(-1, 1); + + outColor = instance.color; + + gl_Position = mvp * vec4(p, 0.0, 1.0); +} +