forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
140
leenkx/Shaders/std/brdf.glsl
Normal file
140
leenkx/Shaders/std/brdf.glsl
Normal file
@ -0,0 +1,140 @@
|
||||
#ifndef _BRDF_GLSL_
|
||||
#define _BRDF_GLSL_
|
||||
|
||||
// http://xlgames-inc.github.io/posts/improvedibl/
|
||||
// http://blog.selfshadow.com/publications/s2013-shading-course/
|
||||
vec3 f_schlick(const vec3 f0, const float vh) {
|
||||
return f0 + (1.0 - f0) * exp2((-5.55473 * vh - 6.98316) * vh);
|
||||
}
|
||||
|
||||
float v_smithschlick(const float nl, const float nv, const float a) {
|
||||
return 1.0 / ((nl * (1.0 - a) + a) * (nv * (1.0 - a) + a));
|
||||
}
|
||||
|
||||
//Uncorrelated masking/shadowing (info below) function
|
||||
//Because it is uncorrelated, G1(NdotL, a) gives us shadowing, and G1(NdotV, a) gives us masking function.
|
||||
//Approximation from: https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2017/Presentations/Hammon_Earl_PBR_Diffuse_Lighting.pdf
|
||||
float g1_approx(const float NdotX, const float alpha)
|
||||
{
|
||||
return (2.0 * NdotX) * (1.0 / (NdotX * (2.0 - alpha) + alpha));
|
||||
}
|
||||
|
||||
//Uncorrelated masking-shadowing function
|
||||
//Approximation from: https://ubm-twvideo01.s3.amazonaws.com/o1/vault/gdc2017/Presentations/Hammon_Earl_PBR_Diffuse_Lighting.pdf
|
||||
float g2_approx(const float NdotL, const float NdotV, const float alpha)
|
||||
{
|
||||
vec2 helper = (2.0 * vec2(NdotL, NdotV)) * (1.0 / (vec2(NdotL, NdotV) * (2.0 - alpha) + alpha));
|
||||
return max(helper.x * helper.y, 0.0); //This can go negative, let's fix that
|
||||
}
|
||||
|
||||
float d_ggx(const float nh, const float a) {
|
||||
float a2 = a * a;
|
||||
float denom = nh * nh * (a2 - 1.0) + 1.0;
|
||||
denom = max(denom * denom, 0.00006103515625 /* 2^-14 = smallest possible half float value, prevent div by zero */);
|
||||
return a2 * (1.0 / 3.1415926535) / denom;
|
||||
}
|
||||
|
||||
vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const float nh, const float nv, const float vh) {
|
||||
float a = roughness * roughness;
|
||||
return d_ggx(nh, a) * g2_approx(nl, nv, a) * f_schlick(f0, vh) / max(4.0 * nv, 1e-5); //NdotL cancels out later
|
||||
}
|
||||
|
||||
// John Hable - Optimizing GGX Shaders
|
||||
// http://filmicworlds.com/blog/optimizing-ggx-shaders-with-dotlh/
|
||||
vec3 specularBRDFb(const vec3 f0, const float roughness, const float dotNL, const float dotNH, const float dotLH) {
|
||||
// D
|
||||
const float pi = 3.1415926535;
|
||||
float alpha = roughness * roughness;
|
||||
float alphaSqr = alpha * alpha;
|
||||
float denom = dotNH * dotNH * (alphaSqr - 1.0) + 1.0;
|
||||
float D = alphaSqr / (pi * denom * denom);
|
||||
// F
|
||||
const float F_a = 1.0;
|
||||
float F_b = pow(1.0 - dotLH, 5.0);
|
||||
// V
|
||||
float vis;
|
||||
float k = alpha / 2.0;
|
||||
float k2 = k * k;
|
||||
float invK2 = 1.0 - k2;
|
||||
vis = 1.0 / (dotLH * dotLH * invK2 + k2);
|
||||
vec2 FV_helper = vec2((F_a - F_b) * vis, F_b * vis);
|
||||
|
||||
vec3 FV = f0 * FV_helper.x + FV_helper.y;
|
||||
vec3 specular = clamp(dotNL, 0.0, 1.0) * D * FV;
|
||||
return specular / 4.0; // TODO: get rid of / 4.0
|
||||
}
|
||||
|
||||
vec3 orenNayarDiffuseBRDF(const vec3 albedo, const float roughness, const float nv, const float nl, const float vh) {
|
||||
float a = roughness * roughness;
|
||||
float s = a;
|
||||
float s2 = s * s;
|
||||
float vl = 2.0 * vh * vh - 1.0; // Double angle identity
|
||||
float Cosri = vl - nv * nl;
|
||||
float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
|
||||
float test = 1.0;
|
||||
if (Cosri >= 0.0) test = (1.0 / (max(nl, nv)));
|
||||
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
|
||||
return albedo * max(0.0, nl) * (C1 + C2) * (1.0 + roughness * 0.5);
|
||||
}
|
||||
|
||||
vec3 lambertDiffuseBRDF(const vec3 albedo, const float nl) {
|
||||
return albedo * nl;
|
||||
}
|
||||
|
||||
vec3 surfaceAlbedo(const vec3 baseColor, const float metalness) {
|
||||
return mix(baseColor, vec3(0.0), metalness);
|
||||
}
|
||||
|
||||
vec3 surfaceF0(const vec3 baseColor, const float metalness) {
|
||||
return mix(vec3(0.04), baseColor, metalness);
|
||||
}
|
||||
|
||||
float getMipFromRoughness(const float roughness, const float numMipmaps) {
|
||||
// First mipmap level = roughness 0, last = roughness = 1
|
||||
return roughness * numMipmaps;
|
||||
}
|
||||
|
||||
float wardSpecular(vec3 N, vec3 H, float dotNL, float dotNV, float dotNH, vec3 fiberDirection, float shinyParallel, float shinyPerpendicular) {
|
||||
if(dotNL < 0.0 || dotNV < 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
// fiberDirection - parse from rotation
|
||||
// shinyParallel - roughness
|
||||
// shinyPerpendicular - anisotropy
|
||||
|
||||
vec3 fiberParallel = normalize(fiberDirection);
|
||||
vec3 fiberPerpendicular = normalize(cross(N, fiberDirection));
|
||||
float dotXH = dot(fiberParallel, H);
|
||||
float dotYH = dot(fiberPerpendicular, H);
|
||||
const float PI = 3.1415926535;
|
||||
float coeff = sqrt(dotNL/dotNV) / (4.0 * PI * shinyParallel * shinyPerpendicular);
|
||||
float theta = (pow(dotXH/shinyParallel, 2.0) + pow(dotYH/shinyPerpendicular, 2.0)) / (1.0 + dotNH);
|
||||
return clamp(coeff * exp(-2.0 * theta), 0.0, 1.0);
|
||||
}
|
||||
|
||||
// https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
|
||||
// vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV) {
|
||||
// const vec4 c0 = { -1, -0.0275, -0.572, 0.022 };
|
||||
// const vec4 c1 = { 1, 0.0425, 1.04, -0.04 };
|
||||
// vec4 r = Roughness * c0 + c1;
|
||||
// float a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
|
||||
// vec2 AB = vec2( -1.04, 1.04 ) * a004 + r.zw;
|
||||
// return SpecularColor * AB.x + AB.y;
|
||||
// }
|
||||
// float EnvBRDFApproxNonmetal(float Roughness, float NoV) {
|
||||
// // Same as EnvBRDFApprox( 0.04, Roughness, NoV )
|
||||
// const vec2 c0 = { -1, -0.0275 };
|
||||
// const vec2 c1 = { 1, 0.0425 };
|
||||
// vec2 r = Roughness * c0 + c1;
|
||||
// return min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
|
||||
// }
|
||||
float D_Approx(const float Roughness, const float RoL) {
|
||||
float a = Roughness * Roughness;
|
||||
float a2 = a * a;
|
||||
float rcp_a2 = 1.0 / a2;//rcp(a2);
|
||||
// 0.5 / ln(2), 0.275 / ln(2)
|
||||
float c = 0.72134752 * rcp_a2 + 0.39674113;
|
||||
return rcp_a2 * exp2( c * RoL - c );
|
||||
}
|
||||
|
||||
#endif
|
18
leenkx/Shaders/std/clusters.glsl
Normal file
18
leenkx/Shaders/std/clusters.glsl
Normal file
@ -0,0 +1,18 @@
|
||||
|
||||
const vec3 clusterSlices = vec3(16, 16, 16);
|
||||
|
||||
int getClusterI(vec2 tc, float viewz, vec2 cameraPlane) {
|
||||
int sliceZ = 0;
|
||||
float cnear = clusterNear + cameraPlane.x;
|
||||
if (viewz >= cnear) {
|
||||
float z = log(viewz - cnear + 1.0) / log(cameraPlane.y - cnear + 1.0);
|
||||
sliceZ = int(z * (clusterSlices.z - 1)) + 1;
|
||||
}
|
||||
// address gap between near plane and cluster near offset
|
||||
else if (viewz >= cameraPlane.x) {
|
||||
sliceZ = 1;
|
||||
}
|
||||
return int(tc.x * clusterSlices.x) +
|
||||
int(int(tc.y * clusterSlices.y) * clusterSlices.x) +
|
||||
int(sliceZ * clusterSlices.x * clusterSlices.y);
|
||||
}
|
144
leenkx/Shaders/std/colorgrading.glsl
Normal file
144
leenkx/Shaders/std/colorgrading.glsl
Normal file
@ -0,0 +1,144 @@
|
||||
// Colorgrading library functions - Inspired by UE4
|
||||
//No specific license (maybe zlib), but just do whatever
|
||||
|
||||
#define LUMINANCE_PRESERVATION 0.75
|
||||
#define EPSILON 1e-10
|
||||
#define LUMA1 0.2722287168
|
||||
#define LUMA2 0.6740817658
|
||||
#define LUMA3 0.0536895174
|
||||
|
||||
float saturate(float v) { return clamp(v, 0.0, 1.0); }
|
||||
vec2 saturate(vec2 v) { return clamp(v, vec2(0.0), vec2(1.0)); }
|
||||
vec3 saturate(vec3 v) { return clamp(v, vec3(0.0), vec3(1.0)); }
|
||||
vec4 saturate(vec4 v) { return clamp(v, vec4(0.0), vec4(1.0)); }
|
||||
|
||||
float LumaKey (vec3 color) {
|
||||
return dot(color, vec3(LUMA1, LUMA2, LUMA3));
|
||||
}
|
||||
|
||||
vec3 ColorTemperatureToRGB(float temperatureInKelvins)
|
||||
{
|
||||
vec3 retColor;
|
||||
|
||||
temperatureInKelvins = clamp(temperatureInKelvins, 1000.0, 40000.0) / 100.0;
|
||||
|
||||
if (temperatureInKelvins <= 66.0)
|
||||
{
|
||||
retColor.r = 1.0;
|
||||
retColor.g = saturate(0.39008157876901960784 * log(temperatureInKelvins) - 0.63184144378862745098);
|
||||
}
|
||||
else
|
||||
{
|
||||
float t = temperatureInKelvins - 60.0;
|
||||
retColor.r = saturate(1.29293618606274509804 * pow(t, -0.1332047592));
|
||||
retColor.g = saturate(1.12989086089529411765 * pow(t, -0.0755148492));
|
||||
}
|
||||
|
||||
if (temperatureInKelvins >= 66.0)
|
||||
retColor.b = 1.0;
|
||||
else if(temperatureInKelvins <= 19.0)
|
||||
retColor.b = 0.0;
|
||||
else
|
||||
retColor.b = saturate(0.54320678911019607843 * log(temperatureInKelvins - 10.0) - 1.19625408914);
|
||||
|
||||
return retColor;
|
||||
}
|
||||
|
||||
float Luminance(vec3 color)
|
||||
{
|
||||
float fmin = min(min(color.r, color.g), color.b);
|
||||
float fmax = max(max(color.r, color.g), color.b);
|
||||
return (fmax + fmin) / 2.0;
|
||||
}
|
||||
|
||||
vec3 HUEtoRGB(float H)
|
||||
{
|
||||
float R = abs(H * 6.0 - 3.0) - 1.0;
|
||||
float G = 2.0 - abs(H * 6.0 - 2.0);
|
||||
float B = 2.0 - abs(H * 6.0 - 4.0);
|
||||
return saturate(vec3(R,G,B));
|
||||
}
|
||||
|
||||
vec3 HSLtoRGB(in vec3 HSL)
|
||||
{
|
||||
vec3 RGB = HUEtoRGB(HSL.x);
|
||||
float C = (1.0 - abs(2.0 * HSL.z - 1.0)) * HSL.y;
|
||||
return (RGB - 0.5) * C + vec3(HSL.z);
|
||||
}
|
||||
|
||||
vec3 RGBtoHCV(vec3 RGB)
|
||||
{
|
||||
vec4 P = (RGB.g < RGB.b) ? vec4(RGB.bg, -1.0, 2.0/3.0) : vec4(RGB.gb, 0.0, -1.0/3.0);
|
||||
vec4 Q = (RGB.r < P.x) ? vec4(P.xyw, RGB.r) : vec4(RGB.r, P.yzx);
|
||||
float C = Q.x - min(Q.w, Q.y);
|
||||
float H = abs((Q.w - Q.y) / (6.0 * C + EPSILON) + Q.z);
|
||||
return vec3(H, C, Q.x);
|
||||
}
|
||||
|
||||
vec3 RGBtoHSL(vec3 RGB)
|
||||
{
|
||||
vec3 HCV = RGBtoHCV(RGB);
|
||||
float L = HCV.z - HCV.y * 0.5;
|
||||
float S = HCV.y / (1.0 - abs(L * 2.0 - 1.0) + EPSILON);
|
||||
return vec3(HCV.x, S, L);
|
||||
}
|
||||
|
||||
|
||||
vec3 ToneColorCorrection(vec3 Color, vec3 ColorSaturation, vec3 ColorContrast, vec3 ColorGamma, vec3 ColorGain, vec3 ColorOffset) {
|
||||
//First initialize the colorluma key
|
||||
float ColorLuma = LumaKey(Color);
|
||||
//Add the saturation with the above key
|
||||
Color = max(vec3(0,0,0), mix(ColorLuma.xxx, Color, ColorSaturation));
|
||||
//Contrast with slight color correction (0.18 coefficient)
|
||||
float ContrastCorrectionCoefficient = 0.18;
|
||||
Color = pow(Color * (1.0 / ContrastCorrectionCoefficient), ColorContrast) * ContrastCorrectionCoefficient;
|
||||
//Gamma
|
||||
Color = pow(Color, 1.0 / ColorGamma);
|
||||
//Gain and Offset
|
||||
Color = Color.rgb * ColorGain + (ColorOffset - 1);
|
||||
//Return the color corrected profile
|
||||
return Color;
|
||||
}
|
||||
|
||||
vec3 FinalizeColorCorrection(vec3 Color, mat3 ColorSaturation, mat3 ColorContrast, mat3 ColorGamma, mat3 ColorGain, mat3 ColorOffset, vec2 Toneweights) {
|
||||
|
||||
float CCShadowsMax = Toneweights.x;
|
||||
float CCHighlightsMin = Toneweights.y;
|
||||
|
||||
//First initialize the colorluma key and set color correction weights
|
||||
float ColorLuma = LumaKey(Color);
|
||||
float CCWeightShadows = 1 - smoothstep(0, CCShadowsMax, ColorLuma);
|
||||
float CCWeightHighlights = smoothstep(CCHighlightsMin, 1, ColorLuma);
|
||||
float CCWeightMidtones = 1 - CCWeightShadows - CCWeightHighlights;
|
||||
|
||||
vec3 CCColorShadows = ToneColorCorrection (
|
||||
Color,
|
||||
ColorSaturation[0],
|
||||
ColorContrast[0],
|
||||
ColorGamma[0],
|
||||
ColorGain[0],
|
||||
ColorOffset[0]
|
||||
);
|
||||
|
||||
vec3 CCColorMidtones = ToneColorCorrection (
|
||||
Color,
|
||||
ColorSaturation[1],
|
||||
ColorContrast[1],
|
||||
ColorGamma[1],
|
||||
ColorGain[1],
|
||||
ColorOffset[1]
|
||||
);
|
||||
|
||||
vec3 CCColorHighlights = ToneColorCorrection (
|
||||
Color,
|
||||
ColorSaturation[2],
|
||||
ColorContrast[2],
|
||||
ColorGamma[2],
|
||||
ColorGain[2],
|
||||
ColorOffset[2]
|
||||
);
|
||||
|
||||
vec3 CombinedCCProfile = CCColorShadows * CCWeightShadows + CCColorMidtones * CCWeightMidtones + CCColorHighlights * CCWeightHighlights;
|
||||
|
||||
return vec3(CombinedCCProfile);
|
||||
}
|
338
leenkx/Shaders/std/conetrace.glsl
Normal file
338
leenkx/Shaders/std/conetrace.glsl
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
Copyright (c) 2024 Turánszki János
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of 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.
|
||||
|
||||
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.
|
||||
*/
|
||||
#ifndef _CONETRACE_GLSL_
|
||||
#define _CONETRACE_GLSL_
|
||||
|
||||
#include "std/voxels_constants.glsl"
|
||||
|
||||
// References
|
||||
// https://github.com/Friduric/voxel-cone-tracing
|
||||
// https://github.com/Cigg/Voxel-Cone-Tracing
|
||||
// https://github.com/GreatBlambo/voxel_cone_tracing/
|
||||
// http://simonstechblog.blogspot.com/2013/01/implementing-voxel-cone-tracing.html
|
||||
// http://leifnode.com/2015/05/voxel-cone-traced-global-illumination/
|
||||
// http://www.seas.upenn.edu/%7Epcozzi/OpenGLInsights/OpenGLInsights-SparseVoxelization.pdf
|
||||
// https://research.nvidia.com/sites/default/files/publications/GIVoxels-pg2011-authors.pdf
|
||||
|
||||
const float MAX_DISTANCE = voxelgiRange;
|
||||
|
||||
#ifdef _VoxelGI
|
||||
uniform sampler3D dummy;
|
||||
|
||||
vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) {
|
||||
vec4 col = vec4(0.0);
|
||||
vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
|
||||
vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
tc = tc * 0.5 + 0.5;
|
||||
tc = clamp(tc, half_texel, 1.0 - half_texel);
|
||||
tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT);
|
||||
tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount;
|
||||
|
||||
if (precomputed_direction == 0) {
|
||||
col = direction_weight.x * textureLod(voxels, vec3(tc.x + face_offset.x, tc.y, tc.z), 0)
|
||||
+ direction_weight.y * textureLod(voxels, vec3(tc.x + face_offset.y, tc.y, tc.z), 0)
|
||||
+ direction_weight.z * textureLod(voxels, vec3(tc.x + face_offset.z, tc.y, tc.z), 0);
|
||||
}
|
||||
else
|
||||
col = textureLod(voxels, tc, 0);
|
||||
|
||||
col *= step_dist / float(clipmaps[int(clipmap_index * 10)]);
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
#endif
|
||||
#ifdef _VoxelAOvar
|
||||
float sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) {
|
||||
float opac = 0.0;
|
||||
vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
|
||||
vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
tc = tc * 0.5 + 0.5;
|
||||
tc = clamp(tc, half_texel, 1.0 - half_texel);
|
||||
tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT);
|
||||
tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount;
|
||||
|
||||
if (precomputed_direction == 0) {
|
||||
opac = direction_weight.x * textureLod(voxels, vec3(tc.x + face_offset.x, tc.y, tc.z), 0).r
|
||||
+ direction_weight.y * textureLod(voxels, vec3(tc.x + face_offset.y, tc.y, tc.z), 0).r
|
||||
+ direction_weight.z * textureLod(voxels, vec3(tc.x + face_offset.z, tc.y, tc.z), 0).r;
|
||||
}
|
||||
else
|
||||
opac = textureLod(voxels, tc, 0).r;
|
||||
|
||||
opac *= step_dist / float(clipmaps[int(clipmap_index * 10)]);
|
||||
|
||||
return opac;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _VoxelGI
|
||||
vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const int precomputed_direction, const bool use_sdf, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) {
|
||||
vec4 sampleCol = vec4(0.0);
|
||||
float voxelSize0 = float(clipmaps[0]) * 2.0;
|
||||
float dist = voxelSize0;
|
||||
float step_dist = dist;
|
||||
vec3 samplePos;
|
||||
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
|
||||
int clipmap_index0 = 0;
|
||||
|
||||
vec3 aniso_direction = -dir;
|
||||
vec3 face_offset = vec3(
|
||||
aniso_direction.x > 0.0 ? 0 : 1,
|
||||
aniso_direction.y > 0.0 ? 2 : 3,
|
||||
aniso_direction.z > 0.0 ? 4 : 5
|
||||
) / (6 + DIFFUSE_CONE_COUNT);
|
||||
vec3 direction_weight = abs(dir);
|
||||
|
||||
float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
|
||||
while (sampleCol.a < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
|
||||
vec4 mipSample = vec4(0.0);
|
||||
float diam = max(voxelSize0, dist * coneCoefficient);
|
||||
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
|
||||
float clipmap_index = floor(lod);
|
||||
float clipmap_blend = fract(lod);
|
||||
vec3 p0 = start_pos + dir * dist;
|
||||
|
||||
samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
|
||||
samplePos = samplePos * 0.5 + 0.5;
|
||||
|
||||
if (any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0)))) {
|
||||
clipmap_index0++;
|
||||
continue;
|
||||
}
|
||||
|
||||
mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight);
|
||||
|
||||
if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
vec4 mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight);
|
||||
mipSample = mix(mipSample, mipSampleNext, smoothstep(0.0, 1.0, clipmap_blend));
|
||||
}
|
||||
|
||||
sampleCol += (1.0 - sampleCol.a) * mipSample;
|
||||
|
||||
float stepSizeCurrent = step_size;
|
||||
if (use_sdf) {
|
||||
// half texel correction is applied to avoid sampling over current clipmap:
|
||||
const vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel);
|
||||
tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap
|
||||
float sdf = textureLod(voxelsSDF, tc0, 0).r;
|
||||
stepSizeCurrent = max(step_size, sdf - diam);
|
||||
}
|
||||
step_dist = diam * stepSizeCurrent;
|
||||
dist += step_dist;
|
||||
}
|
||||
return sampleCol;
|
||||
}
|
||||
|
||||
vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
|
||||
float sum = 0.0;
|
||||
vec4 amount = vec4(0.0);
|
||||
for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) {
|
||||
vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i];
|
||||
const float cosTheta = dot(normal, coneDir);
|
||||
if (cosTheta <= 0)
|
||||
continue;
|
||||
int precomputed_direction = 6 + i;
|
||||
amount += traceCone(voxels, dummy, origin, normal, coneDir, precomputed_direction, false, DIFFUSE_CONE_APERTURE, 1.0, clipmaps) * cosTheta;
|
||||
sum += cosTheta;
|
||||
}
|
||||
|
||||
amount /= sum;
|
||||
amount.rgb = max(amount.rgb, vec3(0.0));
|
||||
amount.a = clamp(amount.a, 0.0, 1.0);
|
||||
|
||||
return amount * voxelgiOcc;
|
||||
}
|
||||
|
||||
vec4 traceSpecular(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 viewDir, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) {
|
||||
vec3 specularDir = reflect(-viewDir, normal);
|
||||
vec3 P = origin + specularDir * ((BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5)) * voxelgiStep;
|
||||
vec4 amount = traceCone(voxels, voxelsSDF, P, normal, specularDir, 0, true, roughness, voxelgiStep, clipmaps);
|
||||
|
||||
amount.rgb = max(vec3(0.0), amount.rgb);
|
||||
amount.a = clamp(amount.a, 0.0, 1.0);
|
||||
|
||||
return amount * voxelgiOcc;
|
||||
}
|
||||
|
||||
vec4 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sampler3D voxelsSDF, const vec3 viewDir, const float ior, const float roughness, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) {
|
||||
const float transmittance = 1.0;
|
||||
vec3 refractionDir = refract(-viewDir, normal, 1.0 / ior);
|
||||
vec3 P = origin + refractionDir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep;
|
||||
vec4 amount = transmittance * traceCone(voxels, voxelsSDF, P, normal, refractionDir, 0, true, roughness, voxelgiStep, clipmaps);
|
||||
|
||||
amount.rgb = max(vec3(0.0), amount.rgb);
|
||||
amount.a = clamp(amount.a, 0.0, 1.0);
|
||||
|
||||
return amount * voxelgiOcc;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _VoxelAOvar
|
||||
float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const vec3 dir, const int precomputed_direction, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) {
|
||||
float sampleCol = 0.0;
|
||||
float voxelSize0 = float(clipmaps[0]) * 2.0;
|
||||
float dist = voxelSize0;
|
||||
float step_dist = dist;
|
||||
vec3 samplePos;
|
||||
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
|
||||
int clipmap_index0 = 0;
|
||||
|
||||
vec3 aniso_direction = -dir;
|
||||
vec3 face_offset = vec3(
|
||||
aniso_direction.x > 0.0 ? 0 : 1,
|
||||
aniso_direction.y > 0.0 ? 2 : 3,
|
||||
aniso_direction.z > 0.0 ? 4 : 5
|
||||
) / (6 + DIFFUSE_CONE_COUNT);
|
||||
vec3 direction_weight = abs(dir);
|
||||
|
||||
float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
|
||||
while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
|
||||
float mipSample = 0.0;
|
||||
float diam = max(voxelSize0, dist * coneCoefficient);
|
||||
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
|
||||
float clipmap_index = floor(lod);
|
||||
float clipmap_blend = fract(lod);
|
||||
vec3 p0 = start_pos + dir * dist;
|
||||
|
||||
samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x);
|
||||
samplePos = samplePos * 0.5 + 0.5;
|
||||
|
||||
if ((any(notEqual(clamp(samplePos, 0.0, 1.0), samplePos)))) {
|
||||
clipmap_index0++;
|
||||
continue;
|
||||
}
|
||||
|
||||
mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight);
|
||||
|
||||
if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight);
|
||||
mipSample = mix(mipSample, mipSampleNext, clipmap_blend);
|
||||
}
|
||||
|
||||
sampleCol += (1.0 - sampleCol) * mipSample;
|
||||
|
||||
step_dist = diam * step_size;
|
||||
dist += step_dist;
|
||||
}
|
||||
return sampleCol;
|
||||
}
|
||||
|
||||
|
||||
float traceAO(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
|
||||
float sum = 0.0;
|
||||
float amount = 0.0;
|
||||
for (int i = 0; i < DIFFUSE_CONE_COUNT; i++) {
|
||||
vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i];
|
||||
int precomputed_direction = 6 + i;
|
||||
const float cosTheta = dot(normal, coneDir);
|
||||
if (cosTheta <= 0)
|
||||
continue;
|
||||
amount += traceConeAO(voxels, origin, normal, coneDir, precomputed_direction, DIFFUSE_CONE_APERTURE, 1.0, clipmaps) * cosTheta;
|
||||
sum += cosTheta;
|
||||
}
|
||||
amount /= sum;
|
||||
amount = clamp(amount, 0.0, 1.0);
|
||||
return amount * voxelgiOcc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _VoxelShadow
|
||||
float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 origin, const vec3 n, const vec3 dir, const float aperture, const float step_size, const float clipmaps[voxelgiClipmapCount * 10]) {
|
||||
float sampleCol = 0.0;
|
||||
float voxelSize0 = float(clipmaps[0]) * 2.0;
|
||||
float dist = voxelSize0;
|
||||
float step_dist = dist;
|
||||
vec3 samplePos;
|
||||
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
|
||||
int clipmap_index0 = 0;
|
||||
|
||||
vec3 aniso_direction = -dir;
|
||||
vec3 face_offset = vec3(
|
||||
aniso_direction.x > 0.0 ? 0 : 1,
|
||||
aniso_direction.y > 0.0 ? 2 : 3,
|
||||
aniso_direction.z > 0.0 ? 4 : 5
|
||||
) / (6 + DIFFUSE_CONE_COUNT);
|
||||
vec3 direction_weight = abs(dir);
|
||||
float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
|
||||
while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
|
||||
float mipSample = 0.0;
|
||||
float diam = max(voxelSize0, dist * coneCoefficient);
|
||||
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
|
||||
float clipmap_index = floor(lod);
|
||||
float clipmap_blend = fract(lod);
|
||||
vec3 p0 = start_pos + dir * dist;
|
||||
|
||||
samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x);
|
||||
samplePos = samplePos * 0.5 + 0.5;
|
||||
|
||||
if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) {
|
||||
clipmap_index0++;
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef _VoxelAOvar
|
||||
mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight);
|
||||
#else
|
||||
mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight).a;
|
||||
#endif
|
||||
|
||||
if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
#ifdef _VoxelAOvar
|
||||
float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight);
|
||||
#else
|
||||
float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight).a;
|
||||
#endif
|
||||
mipSample = mix(mipSample, mipSampleNext, clipmap_blend);
|
||||
}
|
||||
|
||||
sampleCol += (1.0 - sampleCol) * mipSample;
|
||||
|
||||
float stepSizeCurrent = step_size;
|
||||
|
||||
// half texel correction is applied to avoid sampling over current clipmap:
|
||||
const vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel);
|
||||
tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap
|
||||
float sdf = textureLod(voxelsSDF, tc0, 0.0).r;
|
||||
stepSizeCurrent = max(step_size, sdf - diam);
|
||||
|
||||
step_dist = diam * stepSizeCurrent;
|
||||
dist += step_dist;
|
||||
}
|
||||
return sampleCol;
|
||||
}
|
||||
|
||||
|
||||
float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel) {
|
||||
vec3 P = origin + dir * (BayerMatrix8[int(pixel.x) % 8][int(pixel.y) % 8] - 0.5) * voxelgiStep;
|
||||
float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, DIFFUSE_CONE_APERTURE, voxelgiStep, clipmaps);
|
||||
amount = clamp(amount, 0.0, 1.0);
|
||||
return amount * voxelgiOcc;
|
||||
}
|
||||
#endif
|
||||
#endif // _CONETRACE_GLSL_
|
56
leenkx/Shaders/std/denoise.glsl
Normal file
56
leenkx/Shaders/std/denoise.glsl
Normal file
@ -0,0 +1,56 @@
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Copyright (c) 2018-2019 Michele Morrone
|
||||
// All rights reserved.
|
||||
//
|
||||
// https://michelemorrone.eu - https://BrutPitt.com
|
||||
//
|
||||
// me@michelemorrone.eu - brutpitt@gmail.com
|
||||
// twitter: @BrutPitt - github: BrutPitt
|
||||
//
|
||||
// https://github.com/BrutPitt/glslSmartDeNoise/
|
||||
//
|
||||
// This software is distributed under the terms of the BSD 2-Clause license
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
#ifndef _DENOISE_GLSL_
|
||||
#define _DENOISE_GLSL_
|
||||
|
||||
#define INV_SQRT_OF_2PI 0.39894228040143267793994605993439
|
||||
#define INV_PI 0.31830988618379067153776752674503
|
||||
|
||||
vec4 smartDeNoise(sampler2D tex, vec2 uv, float sigma, float kSigma, float threshold) {
|
||||
float radius = round(kSigma*sigma);
|
||||
float radQ = radius * radius;
|
||||
|
||||
float invSigmaQx2 = .5 / (sigma * sigma); // 1.0 / (sigma^2 * 2.0)
|
||||
float invSigmaQx2PI = INV_PI * invSigmaQx2; // 1.0 / (sqrt(PI) * sigma)
|
||||
|
||||
float invThresholdSqx2 = .5 / (threshold * threshold); // 1.0 / (sigma^2 * 2.0)
|
||||
float invThresholdSqrt2PI = INV_SQRT_OF_2PI / threshold; // 1.0 / (sqrt(2*PI) * sigma)
|
||||
|
||||
vec4 centrPx = texture(tex,uv);
|
||||
|
||||
float zBuff = 0.0;
|
||||
vec4 aBuff = vec4(0.0);
|
||||
vec2 size = vec2(textureSize(tex, 0));
|
||||
|
||||
for(float x=-radius; x <= radius; x++) {
|
||||
float pt = sqrt(radQ-x*x); // pt = yRadius: have circular trend
|
||||
for(float y=-pt; y <= pt; y++) {
|
||||
vec2 d = vec2(x,y)/size;
|
||||
|
||||
float blurFactor = exp( -dot(d , d) * invSigmaQx2 ) * invSigmaQx2;
|
||||
|
||||
vec4 walkPx = texture(tex,uv+d);
|
||||
|
||||
vec4 dC = walkPx-centrPx;
|
||||
float deltaFactor = exp( -dot(dC, dC) * invThresholdSqx2) * invThresholdSqrt2PI * blurFactor;
|
||||
|
||||
zBuff += deltaFactor;
|
||||
aBuff += deltaFactor*walkPx;
|
||||
}
|
||||
}
|
||||
return aBuff/zBuff;
|
||||
}
|
||||
|
||||
#endif
|
92
leenkx/Shaders/std/dof.glsl
Normal file
92
leenkx/Shaders/std/dof.glsl
Normal file
@ -0,0 +1,92 @@
|
||||
// DoF with bokeh GLSL shader by Martins Upitis (martinsh) (devlog-martinsh.blogspot.com)
|
||||
// Creative Commons Attribution 3.0 Unported License
|
||||
|
||||
#include "compiled.inc"
|
||||
#include "std/math.glsl"
|
||||
|
||||
// const float compoDOFDistance = 10.0; // Focal distance value in meters
|
||||
// const float compoDOFLength = 160.0; // Focal length in mm 18-200
|
||||
// const float compoDOFFstop = 128.0; // F-stop value
|
||||
|
||||
const int samples = 6; // Samples on the first ring
|
||||
const int rings = 6; // Ring count
|
||||
const vec2 focus = vec2(0.5, 0.5);
|
||||
const float coc = 0.11; // Circle of confusion size in mm (35mm film = 0.03mm)
|
||||
const float maxblur = 1.0;
|
||||
const float threshold = 0.5; // Highlight threshold
|
||||
const float gain = 2.0; // Highlight gain
|
||||
const float bias = 0.5; // Bokeh edge bias
|
||||
const float fringe = 0.7; // Bokeh chromatic aberration/fringing
|
||||
const float namount = 0.0001; // Dither amount
|
||||
|
||||
vec3 color(vec2 coords, const float blur, const sampler2D tex, const vec2 texStep) {
|
||||
vec3 col = vec3(0.0);
|
||||
col.r = textureLod(tex, coords + vec2(0.0, 1.0) * texStep * fringe * blur, 0.0).r;
|
||||
col.g = textureLod(tex, coords + vec2(-0.866, -0.5) * texStep * fringe * blur, 0.0).g;
|
||||
col.b = textureLod(tex, coords + vec2(0.866, -0.5) * texStep * fringe * blur, 0.0).b;
|
||||
|
||||
const vec3 lumcoeff = vec3(0.299, 0.587, 0.114);
|
||||
float lum = dot(col.rgb, lumcoeff);
|
||||
float thresh = max((lum - threshold) * gain, 0.0);
|
||||
return col + mix(vec3(0.0), col, thresh * blur);
|
||||
}
|
||||
|
||||
vec3 dof(
|
||||
const vec2 texCoord,
|
||||
const float gdepth,
|
||||
const sampler2D tex,
|
||||
const sampler2D gbufferD,
|
||||
const vec2 texStep,
|
||||
const vec2 cameraProj,
|
||||
const bool autoFocus,
|
||||
const float DOFDistance,
|
||||
const float DOFLength,
|
||||
const float DOFFStop) {
|
||||
|
||||
float depth = linearize(gdepth, cameraProj);
|
||||
float fDepth = 0.0;
|
||||
|
||||
if(autoFocus) {
|
||||
fDepth = linearize(textureLod(gbufferD, focus, 0.0).r * 2.0 - 1.0, cameraProj);
|
||||
} else {
|
||||
fDepth = DOFDistance;
|
||||
}
|
||||
|
||||
float f = DOFLength; // Focal length in mm
|
||||
float d = fDepth * 1000.0; // Focal plane in mm
|
||||
float o = depth * 1000.0; // Depth in mm
|
||||
float a = (o * f) / (o - f);
|
||||
float b = (d * f) / (d - f);
|
||||
float c = (d - f) / (d * DOFFStop * coc);
|
||||
float blur = abs(a - b) * c;
|
||||
blur = clamp(blur, 0.0, 1.0);
|
||||
|
||||
vec2 noise = rand2(texCoord) * namount * blur;
|
||||
float w = (texStep.x) * blur * maxblur + noise.x;
|
||||
float h = (texStep.y) * blur * maxblur + noise.y;
|
||||
vec3 col = vec3(0.0);
|
||||
if (blur < 0.05) {
|
||||
col = textureLod(tex, texCoord, 0.0).rgb;
|
||||
}
|
||||
else {
|
||||
col = textureLod(tex, texCoord, 0.0).rgb;
|
||||
float s = 1.0;
|
||||
int ringsamples;
|
||||
|
||||
for (int i = 1; i <= rings; ++i) {
|
||||
ringsamples = i * samples;
|
||||
for (int j = 0 ; j < ringsamples; ++j) {
|
||||
float step = PI2 / float(ringsamples);
|
||||
float pw = (cos(float(j) * step) * float(i));
|
||||
float ph = (sin(float(j) * step) * float(i));
|
||||
float p = 1.0;
|
||||
// if (pentagon) p = penta(vec2(pw, ph));
|
||||
col += color(texCoord + vec2(pw * w, ph * h), blur, tex, texStep) * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
||||
s += 1.0 * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
||||
}
|
||||
}
|
||||
col /= s;
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
60
leenkx/Shaders/std/filters.glsl
Normal file
60
leenkx/Shaders/std/filters.glsl
Normal file
@ -0,0 +1,60 @@
|
||||
|
||||
vec4 cubicCatmullrom(float x) {
|
||||
const float s = 0.5;
|
||||
float x2 = x * x;
|
||||
float x3 = x2 * x;
|
||||
vec4 w;
|
||||
w.x = -s*x3 + 2*s*x2 - s*x + 0;
|
||||
w.y = (2-s)*x3 + (s-3)*x2 + 1;
|
||||
w.z = (s-2)*x3 + (3-2*s)*x2 + s*x + 0;
|
||||
w.w = s*x3 - s*x2 + 0;
|
||||
return w;
|
||||
}
|
||||
|
||||
vec4 cubic(float v) {
|
||||
vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v;
|
||||
vec4 s = n * n * n;
|
||||
float x = s.x;
|
||||
float y = s.y - 4.0 * s.x;
|
||||
float z = s.z - 4.0 * s.y + 6.0 * s.x;
|
||||
float w = 6.0 - x - y - z;
|
||||
return vec4(x, y, z, w) * (1.0/6.0);
|
||||
}
|
||||
|
||||
vec3 textureBicubic(sampler2D tex, vec2 tc, vec2 texStep) {
|
||||
// http://www.java-gaming.org/index.php?topic=35123.0
|
||||
vec2 texSize = 1.0 / texStep;
|
||||
tc = tc * texSize - 0.5;
|
||||
|
||||
vec2 fxy = fract(tc);
|
||||
tc -= fxy;
|
||||
vec4 xcubic = cubic(fxy.x);
|
||||
vec4 ycubic = cubic(fxy.y);
|
||||
|
||||
vec4 c = tc.xxyy + vec2(-0.5, 1.5).xyxy;
|
||||
vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw);
|
||||
vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s;
|
||||
offset *= texStep.xxyy;
|
||||
vec3 sample0 = texture(tex, offset.xz).rgb;
|
||||
vec3 sample1 = texture(tex, offset.yz).rgb;
|
||||
vec3 sample2 = texture(tex, offset.xw).rgb;
|
||||
vec3 sample3 = texture(tex, offset.yw).rgb;
|
||||
|
||||
float sx = s.x / (s.x + s.y);
|
||||
float sy = s.z / (s.z + s.w);
|
||||
|
||||
return mix(mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy);
|
||||
}
|
||||
|
||||
vec4 textureSS(sampler2D tex, vec2 tc, vec2 texStep) {
|
||||
vec4 col = texture(tex, tc);
|
||||
col += texture(tex, tc + vec2(1.5, 0.0) * texStep);
|
||||
col += texture(tex, tc + vec2(-1.5, 0.0) * texStep);
|
||||
col += texture(tex, tc + vec2(0.0, 1.5) * texStep);
|
||||
col += texture(tex, tc + vec2(0.0, -1.5) * texStep);
|
||||
col += texture(tex, tc + vec2(1.5, 1.5) * texStep);
|
||||
col += texture(tex, tc + vec2(-1.5, -1.5) * texStep);
|
||||
col += texture(tex, tc + vec2(1.5, -1.5) * texStep);
|
||||
col += texture(tex, tc + vec2(-1.5, 1.5) * texStep);
|
||||
return col / 9.0;
|
||||
}
|
173
leenkx/Shaders/std/gbuffer.glsl
Normal file
173
leenkx/Shaders/std/gbuffer.glsl
Normal file
@ -0,0 +1,173 @@
|
||||
#ifndef _GBUFFER_GLSL_
|
||||
#define _GBUFFER_GLSL_
|
||||
|
||||
vec2 octahedronWrap(const vec2 v) {
|
||||
return (1.0 - abs(v.yx)) * (vec2(v.x >= 0.0 ? 1.0 : -1.0, v.y >= 0.0 ? 1.0 : -1.0));
|
||||
}
|
||||
|
||||
vec3 getNor(const vec2 enc) {
|
||||
vec3 n;
|
||||
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
||||
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
|
||||
n = normalize(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
vec3 getPosView(const vec3 viewRay, const float depth, const vec2 cameraProj) {
|
||||
float linearDepth = cameraProj.y / (cameraProj.x - depth);
|
||||
//float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
||||
return viewRay * linearDepth;
|
||||
}
|
||||
|
||||
vec3 getPos(const vec3 eye, const vec3 eyeLook, const vec3 viewRay, const float depth, const vec2 cameraProj) {
|
||||
// eyeLook, viewRay should be normalized
|
||||
float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
||||
float viewZDist = dot(eyeLook, viewRay);
|
||||
vec3 wposition = eye + viewRay * (linearDepth / viewZDist);
|
||||
return wposition;
|
||||
}
|
||||
|
||||
vec3 getPosNoEye(const vec3 eyeLook, const vec3 viewRay, const float depth, const vec2 cameraProj) {
|
||||
// eyeLook, viewRay should be normalized
|
||||
float linearDepth = cameraProj.y / ((depth * 0.5 + 0.5) - cameraProj.x);
|
||||
float viewZDist = dot(eyeLook, viewRay);
|
||||
vec3 wposition = viewRay * (linearDepth / viewZDist);
|
||||
return wposition;
|
||||
}
|
||||
|
||||
#if defined(HLSL) || defined(METAL)
|
||||
vec3 getPos2(const mat4 invVP, const float depth, vec2 coord) {
|
||||
coord.y = 1.0 - coord.y;
|
||||
#else
|
||||
vec3 getPos2(const mat4 invVP, const float depth, const vec2 coord) {
|
||||
#endif
|
||||
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
||||
pos = invVP * pos;
|
||||
pos.xyz /= pos.w;
|
||||
return pos.xyz;
|
||||
}
|
||||
|
||||
#if defined(HLSL) || defined(METAL)
|
||||
vec3 getPosView2(const mat4 invP, const float depth, vec2 coord) {
|
||||
coord.y = 1.0 - coord.y;
|
||||
#else
|
||||
vec3 getPosView2(const mat4 invP, const float depth, const vec2 coord) {
|
||||
#endif
|
||||
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
||||
pos = invP * pos;
|
||||
pos.xyz /= pos.w;
|
||||
return pos.xyz;
|
||||
}
|
||||
|
||||
#if defined(HLSL) || defined(METAL)
|
||||
vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, vec2 coord) {
|
||||
coord.y = 1.0 - coord.y;
|
||||
#else
|
||||
vec3 getPos2NoEye(const vec3 eye, const mat4 invVP, const float depth, const vec2 coord) {
|
||||
#endif
|
||||
vec4 pos = vec4(coord * 2.0 - 1.0, depth, 1.0);
|
||||
pos = invVP * pos;
|
||||
pos.xyz /= pos.w;
|
||||
return pos.xyz - eye;
|
||||
}
|
||||
|
||||
float packFloat(const float f1, const float f2) {
|
||||
return floor(f1 * 100.0) + min(f2, 1.0 - 1.0 / 100.0);
|
||||
}
|
||||
|
||||
vec2 unpackFloat(const float f) {
|
||||
return vec2(floor(f) / 100.0, fract(f));
|
||||
}
|
||||
|
||||
float packFloat2(const float f1, const float f2) {
|
||||
// Higher f1 = less precise f2
|
||||
return floor(f1 * 255.0) + min(f2, 1.0 - 1.0 / 100.0);
|
||||
}
|
||||
|
||||
vec2 unpackFloat2(const float f) {
|
||||
return vec2(floor(f) / 255.0, fract(f));
|
||||
}
|
||||
|
||||
vec4 encodeRGBM(const vec3 rgb) {
|
||||
const float maxRange = 6.0;
|
||||
float maxRGB = max(rgb.x, max(rgb.g, rgb.b));
|
||||
float m = maxRGB / maxRange;
|
||||
m = ceil(m * 255.0) / 255.0;
|
||||
return vec4(rgb / (m * maxRange), m);
|
||||
}
|
||||
|
||||
vec3 decodeRGBM(const vec4 rgbm) {
|
||||
const float maxRange = 6.0;
|
||||
return rgbm.rgb * rgbm.a * maxRange;
|
||||
}
|
||||
|
||||
vec2 signNotZero(vec2 v)
|
||||
{
|
||||
return vec2((v.x >= 0.0) ? +1.0 : -1.0, (v.y >= 0.0) ? +1.0 : -1.0);
|
||||
}
|
||||
|
||||
vec2 encode_oct(vec3 v)
|
||||
{
|
||||
// Project the sphere onto the octahedron, and then onto the xy plane
|
||||
vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z)));
|
||||
// Reflect the folds of the lower hemisphere over the diagonals
|
||||
return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p;
|
||||
}
|
||||
|
||||
vec3 decode_oct(vec2 e)
|
||||
{
|
||||
vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y));
|
||||
if (v.z < 0) v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy);
|
||||
return normalize(v);
|
||||
}
|
||||
|
||||
uint encNor(vec3 n) {
|
||||
ivec3 nor = ivec3(n * 255.0f);
|
||||
uvec3 norSigns;
|
||||
norSigns.x = (nor.x >> 5) & 0x04000000;
|
||||
norSigns.y = (nor.y >> 14) & 0x00020000;
|
||||
norSigns.z = (nor.z >> 23) & 0x00000100;
|
||||
nor = abs(nor);
|
||||
uint val = norSigns.x | (nor.x << 18) | norSigns.y | (nor.y << 9) | norSigns.z | nor.z;
|
||||
return val;
|
||||
}
|
||||
|
||||
vec3 decNor(uint val) {
|
||||
uvec3 nor;
|
||||
nor.x = (val >> 18) & 0x000000ff;
|
||||
nor.y = (val >> 9) & 0x000000ff;
|
||||
nor.z = val & 0x000000ff;
|
||||
uvec3 norSigns;
|
||||
norSigns.x = (val >> 25) & 0x00000002;
|
||||
norSigns.y = (val >> 16) & 0x00000002;
|
||||
norSigns.z = (val >> 7) & 0x00000002;
|
||||
norSigns = 1 - norSigns;
|
||||
vec3 normal = vec3(nor) / 255.0f;
|
||||
normal *= norSigns;
|
||||
return normal;
|
||||
}
|
||||
|
||||
/**
|
||||
Packs a float in [0, 1] and an integer in [0..15] into a single 16 bit float value.
|
||||
**/
|
||||
float packFloatInt16(const float f, const uint i) {
|
||||
const uint numBitFloat = 12;
|
||||
const float maxValFloat = float((1 << numBitFloat) - 1);
|
||||
|
||||
const uint bitsInt = i << numBitFloat;
|
||||
const uint bitsFloat = uint(f * maxValFloat);
|
||||
|
||||
return float(bitsInt | bitsFloat);
|
||||
}
|
||||
|
||||
void unpackFloatInt16(const float val, out float f, out uint i) {
|
||||
const uint numBitFloat = 12;
|
||||
const float maxValFloat = float((1 << numBitFloat) - 1);
|
||||
|
||||
const uint bitsValue = uint(val);
|
||||
|
||||
i = bitsValue >> numBitFloat;
|
||||
f = (bitsValue & ~(0xF << numBitFloat)) / maxValFloat;
|
||||
}
|
||||
|
||||
#endif
|
32
leenkx/Shaders/std/ies.glsl
Normal file
32
leenkx/Shaders/std/ies.glsl
Normal file
@ -0,0 +1,32 @@
|
||||
|
||||
uniform sampler2D texIES;
|
||||
|
||||
float iesAttenuation(vec3 l) {
|
||||
|
||||
const float PI = 3.1415926535;
|
||||
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
|
||||
// Sample direction into light space
|
||||
// vec3 iesSampleDirection = mul(light.worldToLight , -L);
|
||||
// Cartesian to spherical
|
||||
// Texture encoded with cos( phi ), scale from -1 - >1 to 0 - >1
|
||||
// float phiCoord = (iesSampleDirection.z * 0.5f) + 0.5f;
|
||||
// float theta = atan2 (iesSampleDirection.y , iesSampleDirection .x);
|
||||
// float thetaCoord = theta * (1.0 / (PI * 2.0));
|
||||
// float iesProfileScale = texture(texIES, vec2(thetaCoord, phiCoord)).r;
|
||||
// return iesProfileScale;
|
||||
|
||||
// 1D texture
|
||||
// vec3 pl = normalize(p - lightPos);
|
||||
// float f = asin(dot(pl, l)) / PI + 0.5;
|
||||
// return texture(texIES, vec2(f, 0.0)).r;
|
||||
|
||||
// 1D texture
|
||||
// float cosTheta = dot(lightToPos, lightDir);
|
||||
// float angle = acos(cosTheta) * (1.0 / PI);
|
||||
// return texture(texIES, vec2(angle, 0.0), 0.0).r;
|
||||
|
||||
// Based on https://github.com/tobspr/RenderPipeline
|
||||
float hor = acos(l.z) / PI;
|
||||
float vert = atan(l.x, l.y) * (1.0 / (PI * 2.0)) + 0.5;
|
||||
return texture(texIES, vec2(hor, vert)).r;
|
||||
}
|
84
leenkx/Shaders/std/imageatomic.glsl
Normal file
84
leenkx/Shaders/std/imageatomic.glsl
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
// Courtesy of
|
||||
// https://github.com/GreatBlambo/voxel_cone_tracing
|
||||
// https://www.seas.upenn.edu/~pcozzi/OpenGLInsights/OpenGLInsights-SparseVoxelization.pdf
|
||||
|
||||
|
||||
uint convVec4ToRGBA8(vec4 val) {
|
||||
vec4 col = vec4(val) * 255;
|
||||
return (uint(col.w) & 0x000000FF) << 24U
|
||||
| (uint(col.z) & 0x000000FF) << 16U
|
||||
| (uint(col.y) & 0x000000FF) << 8U
|
||||
| (uint(col.x) & 0x000000FF);
|
||||
}
|
||||
|
||||
vec4 convRGBA8ToVec4(uint val) {
|
||||
uvec4 col = uvec4(
|
||||
float((val & 0x000000FF)),
|
||||
float((val & 0x0000FF00) >> 8U),
|
||||
float((val & 0x00FF0000) >> 16U),
|
||||
float((val & 0xFF000000) >> 24U));
|
||||
return vec4(col) / 255;
|
||||
}
|
||||
|
||||
// uint encUnsignedNibble(uint m, uint n) {
|
||||
// return (m & 0xFEFEFEFE)
|
||||
// | (n & 0x00000001)
|
||||
// | (n & 0x00000002) << 7U
|
||||
// | (n & 0x00000004) << 14U
|
||||
// | (n & 0x00000008) << 21U;
|
||||
// }
|
||||
|
||||
// uint decUnsignedNibble(uint m) {
|
||||
// return (m & 0x00000001)
|
||||
// | (m & 0x00000100) >> 7U
|
||||
// | (m & 0x00010000) >> 14U
|
||||
// | (m & 0x01000000) >> 21U;
|
||||
// }
|
||||
|
||||
// void imageAtomicRGBA8Avg(layout(r32ui) uimage3D img, ivec3 coords, vec4 val) {
|
||||
// // LSBs are used for the sample counter of the moving average.
|
||||
// val *= 255.0;
|
||||
// uint newVal = encUnsignedNibble(convVec4ToRGBA8(val), 1);
|
||||
// uint prevStoredVal = 0;
|
||||
// uint currStoredVal;
|
||||
// int counter = 0;
|
||||
// // Loop as long as destination value gets changed by other threads
|
||||
// while ((currStoredVal = imageAtomicCompSwap(img, coords, prevStoredVal, newVal)) != prevStoredVal && counter < 16) {
|
||||
// vec4 rval = convRGBA8ToVec4(currStoredVal & 0xFEFEFEFE);
|
||||
// uint n = decUnsignedNibble(currStoredVal);
|
||||
// rval = rval * n + val;
|
||||
// rval /= ++n;
|
||||
// rval = round(rval / 2) * 2;
|
||||
// newVal = encUnsignedNibble(convVec4ToRGBA8(rval), n);
|
||||
// prevStoredVal = currStoredVal;
|
||||
// counter++;
|
||||
// }
|
||||
// }
|
||||
|
||||
// void imageAtomicFloatAdd(layout(r32ui) coherent volatile uimage3D imgUI, ivec3 coords, float val) {
|
||||
// uint newVal = floatBitsToUint(val);
|
||||
// uint prevVal = 0;
|
||||
// uint curVal;
|
||||
// // Loop as long as destination value gets changed by other threads
|
||||
// while ((curVal = imageAtomicCompSwap(imgUI, coords, prevVal, newVal)) != prevVal) {
|
||||
// prevVal = curVal;
|
||||
// newVal = floatBitsToUint((val + uintBitsToFloat(curVal)));
|
||||
// }
|
||||
// }
|
||||
|
||||
// void imageAtomicRGBA8Avg( layout ( r32ui ) coherent volatile uimage3D imgUI , ivec3 coords , vec4 val ) {
|
||||
// val.rgb *= 255.0f; // Optimise following calculations
|
||||
// uint newVal = convVec4ToRGBA8(val);
|
||||
// uint prevStoredVal = 0;
|
||||
// uint curStoredVal;
|
||||
// // Loop as long as destination value gets changed by other threads
|
||||
// while ((curStoredVal = imageAtomicCompSwap(imgUI, coords, prevStoredVal, newVal)) != prevStoredVal) {
|
||||
// prevStoredVal = curStoredVal;
|
||||
// vec4 rval = convRGBA8ToVec4(curStoredVal);
|
||||
// rval.xyz = (rval.xyz * rval.w) ; // Denormalize
|
||||
// vec4 curValF = rval + val; // Add new value
|
||||
// curValF.xyz /= (curValF.w); // Renormalize
|
||||
// newVal = convVec4ToRGBA8(curValF);
|
||||
// }
|
||||
// }
|
239
leenkx/Shaders/std/light.glsl
Normal file
239
leenkx/Shaders/std/light.glsl
Normal file
@ -0,0 +1,239 @@
|
||||
#ifndef _LIGHT_GLSL_
|
||||
#define _LIGHT_GLSL_
|
||||
|
||||
#include "compiled.inc"
|
||||
#include "std/brdf.glsl"
|
||||
#include "std/math.glsl"
|
||||
#ifdef _ShadowMap
|
||||
#include "std/shadows.glsl"
|
||||
#endif
|
||||
#ifdef _VoxelShadow
|
||||
#include "std/conetrace.glsl"
|
||||
//!uniform sampler2D voxels_shadows;
|
||||
#endif
|
||||
#ifdef _LTC
|
||||
#include "std/ltc.glsl"
|
||||
#endif
|
||||
#ifdef _LightIES
|
||||
#include "std/ies.glsl"
|
||||
#endif
|
||||
#ifdef _SSRS
|
||||
#include "std/ssrs.glsl"
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
#include "std/light_common.glsl"
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
#ifndef _LTC
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform sampler2D shadowMapSpotTransparent[1];
|
||||
uniform mat4 LWVPSpot[1];
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
uniform samplerCube shadowMapPointTransparent[1];
|
||||
uniform vec2 lightProj;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
#ifdef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlas;
|
||||
//!uniform sampler2D shadowMapAtlasTransparent;
|
||||
#endif
|
||||
uniform vec2 lightProj;
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
uniform sampler2D shadowMapAtlasPointTransparent;
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
uniform samplerCube shadowMapPointTransparent[4];
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
uniform sampler2D shadowMapAtlasSpotTransparent;
|
||||
#endif
|
||||
#else
|
||||
uniform sampler2DShadow shadowMapSpot[4];
|
||||
uniform sampler2D shadowMapSpotTransparent[4];
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _LTC
|
||||
uniform vec3 lightArea0;
|
||||
uniform vec3 lightArea1;
|
||||
uniform vec3 lightArea2;
|
||||
uniform vec3 lightArea3;
|
||||
uniform sampler2D sltcMat;
|
||||
uniform sampler2D sltcMag;
|
||||
#ifdef _ShadowMap
|
||||
#ifndef _Spot
|
||||
#ifdef _SinglePoint
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform sampler2D shadowMapSpotTransparent[1];
|
||||
uniform mat4 LWVPSpot[1];
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
|
||||
uniform sampler2D shadowMapSpotTransparent[maxLightsCluster];
|
||||
uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
|
||||
const vec3 albedo, const float rough, const float spec, const vec3 f0
|
||||
#ifdef _ShadowMap
|
||||
, int index, float bias, bool receiveShadow, bool transparent
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, const bool isSpot, const float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
|
||||
#endif
|
||||
#ifdef _VoxelShadow
|
||||
, sampler3D voxels, sampler3D voxelsSDF, float clipmaps[10 * voxelgiClipmapCount]
|
||||
#endif
|
||||
#ifdef _MicroShadowing
|
||||
, float occ
|
||||
#endif
|
||||
#ifdef _SSRS
|
||||
, sampler2D gbufferD, mat4 invVP, vec3 eye
|
||||
#endif
|
||||
) {
|
||||
vec3 ld = lp - p;
|
||||
vec3 l = normalize(ld);
|
||||
vec3 h = normalize(v + l);
|
||||
float dotNH = max(0.0, dot(n, h));
|
||||
float dotVH = max(0.0, dot(v, h));
|
||||
float dotNL = max(0.0, dot(n, l));
|
||||
|
||||
#ifdef _LTC
|
||||
float theta = acos(dotNV);
|
||||
vec2 tuv = vec2(rough, theta / (0.5 * PI));
|
||||
tuv = tuv * LUT_SCALE + LUT_BIAS;
|
||||
vec4 t = textureLod(sltcMat, tuv, 0.0);
|
||||
mat3 invM = mat3(
|
||||
vec3(1.0, 0.0, t.y),
|
||||
vec3(0.0, t.z, 0.0),
|
||||
vec3(t.w, 0.0, t.x));
|
||||
float ltcspec = ltcEvaluate(n, v, dotNV, p, invM, lightArea0, lightArea1, lightArea2, lightArea3);
|
||||
ltcspec *= textureLod(sltcMag, tuv, 0.0).a;
|
||||
float ltcdiff = ltcEvaluate(n, v, dotNV, p, mat3(1.0), lightArea0, lightArea1, lightArea2, lightArea3);
|
||||
vec3 direct = albedo * ltcdiff + ltcspec * spec * 0.05;
|
||||
#else
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
#endif
|
||||
|
||||
direct *= attenuate(distance(p, lp));
|
||||
direct *= lightCol;
|
||||
|
||||
#ifdef _MicroShadowing
|
||||
direct *= clamp(dotNL + 2.0 * occ * occ - 1.0, 0.0, 1.0);
|
||||
#endif
|
||||
|
||||
#ifdef _SSRS
|
||||
direct *= traceShadowSS(l, p, gbufferD, invVP, eye);
|
||||
#endif
|
||||
|
||||
#ifdef _VoxelShadow
|
||||
direct *= (1.0 - traceShadow(p, n, voxels, voxelsSDF, l, clipmaps, gl_FragCoord.xy).r) * voxelgiShad;
|
||||
#endif
|
||||
|
||||
#ifdef _LTC
|
||||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return direct;
|
||||
#endif
|
||||
|
||||
#ifdef _Spot
|
||||
if (isSpot) {
|
||||
direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend);
|
||||
|
||||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= shadowTest(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
|
||||
#else
|
||||
shadowMapAtlas, shadowMapAtlasTransparent
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, bias, transparent
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0], shadowMapSpotTransparent[0], lPos.xyz / lPos.w, bias, transparent);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], shadowMapSpotTransparent[1], lPos.xyz / lPos.w, bias, transparent);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], shadowMapSpotTransparent[2], lPos.xyz / lPos.w, bias, transparent);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], shadowMapSpotTransparent[3], lPos.xyz / lPos.w, bias, transparent);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return direct;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _LightIES
|
||||
direct *= iesAttenuation(-l);
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
#ifndef _Spot
|
||||
direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= PCFFakeCube(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
|
||||
#else
|
||||
shadowMapAtlas, shadowMapAtlasTransparent
|
||||
#endif
|
||||
, ld, -l, bias, lightProj, n, index, transparent
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0], shadowMapPointTransparent[0], ld, -l, bias, lightProj, n, transparent);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], shadowMapPointTransparent[1], ld, -l, bias, lightProj, n, transparent);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], shadowMapPointTransparent[2], ld, -l, bias, lightProj, n, transparent);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], shadowMapPointTransparent[3], ld, -l, bias, lightProj, n, transparent);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return direct;
|
||||
}
|
||||
|
||||
#endif
|
31
leenkx/Shaders/std/light_common.glsl
Normal file
31
leenkx/Shaders/std/light_common.glsl
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef _LIGHT_COMMON_GLSL_
|
||||
#define _LIGHT_COMMON_GLSL_
|
||||
|
||||
#ifdef _Spot
|
||||
float spotlightMask(const vec3 dir, const vec3 spotDir, const vec3 right, const vec2 scale, const float spotSize, const float spotBlend) {
|
||||
// Project the fragment's light dir to the z axis in the light's local space
|
||||
float localZ = dot(spotDir, dir);
|
||||
|
||||
if (localZ < 0) {
|
||||
return 0.0; // Discard opposite cone
|
||||
}
|
||||
|
||||
vec3 up = cross(spotDir, right);
|
||||
|
||||
// Scale the incoming light direction to treat the spotlight's ellipse as if
|
||||
// it was 1 unit away from the light source, this way the smoothstep below
|
||||
// works independently of the distance
|
||||
vec3 scaledDir = dir.xyz / localZ;
|
||||
|
||||
// Project to right and up vectors to apply axis scale
|
||||
float localX = dot(scaledDir, right) / scale.x;
|
||||
float localY = dot(scaledDir, up) / scale.y;
|
||||
|
||||
// Inverse of length of vector from ellipse to light (scaledDir.z == 1.0)
|
||||
float ellipse = inversesqrt(localX * localX + localY * localY + 1.0);
|
||||
|
||||
return smoothstep(0.0, 1.0, (ellipse - spotSize) / spotBlend);
|
||||
}
|
||||
#endif // _Spot
|
||||
|
||||
#endif // _LIGHT_COMMON_GLSL_
|
135
leenkx/Shaders/std/light_mobile.glsl
Normal file
135
leenkx/Shaders/std/light_mobile.glsl
Normal file
@ -0,0 +1,135 @@
|
||||
#ifndef _LIGHT_MOBILE_GLSL_
|
||||
#define _LIGHT_MOBILE_GLSL_
|
||||
|
||||
#include "compiled.inc"
|
||||
#include "std/brdf.glsl"
|
||||
#ifdef _ShadowMap
|
||||
#include "std/shadows.glsl"
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
#include "std/light_common.glsl"
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot[1];
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
uniform vec2 lightProj;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
#ifdef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlas;
|
||||
#endif
|
||||
uniform vec2 lightProj;
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
#endif
|
||||
#else
|
||||
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
|
||||
const vec3 albedo, const float rough, const float spec, const vec3 f0
|
||||
#ifdef _ShadowMap
|
||||
, int index, float bias, bool receiveShadow
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
|
||||
#endif
|
||||
) {
|
||||
vec3 ld = lp - p;
|
||||
vec3 l = normalize(ld);
|
||||
vec3 h = normalize(v + l);
|
||||
float dotNH = max(0.0, dot(n, h));
|
||||
float dotVH = max(0.0, dot(v, h));
|
||||
float dotNL = max(0.0, dot(n, l));
|
||||
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
|
||||
direct *= lightCol;
|
||||
direct *= attenuate(distance(p, lp));
|
||||
|
||||
#ifdef _Spot
|
||||
if (isSpot) {
|
||||
direct *= spotlightMask(l, spotDir, right, scale, spotSize, spotBlend);
|
||||
|
||||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= shadowTest(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, bias
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
return direct;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
#ifndef _Spot
|
||||
direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= PCFFakeCube(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasPoint
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
, ld, -l, bias, lightProj, n, index
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
return direct;
|
||||
}
|
||||
|
||||
#endif
|
157
leenkx/Shaders/std/ltc.glsl
Normal file
157
leenkx/Shaders/std/ltc.glsl
Normal file
@ -0,0 +1,157 @@
|
||||
// Linearly Transformed Cosines
|
||||
// https://eheitzresearch.wordpress.com/415-2/
|
||||
|
||||
const float LUT_SIZE = 64.0;
|
||||
const float LUT_SCALE = (LUT_SIZE - 1.0) / LUT_SIZE;
|
||||
const float LUT_BIAS = 0.5 / LUT_SIZE;
|
||||
|
||||
vec3 L0;
|
||||
vec3 L1;
|
||||
vec3 L2;
|
||||
vec3 L3;
|
||||
vec3 L4;
|
||||
|
||||
float integrateEdge(vec3 v1, vec3 v2) {
|
||||
float cosTheta = dot(v1, v2);
|
||||
float theta = acos(cosTheta);
|
||||
float res = cross(v1, v2).z * ((theta > 0.001) ? theta / sin(theta) : 1.0);
|
||||
return res;
|
||||
}
|
||||
|
||||
int clipQuadToHorizon(/*inout vec3 L[5], out int n*/) {
|
||||
int n = 0;
|
||||
// Detect clipping config
|
||||
int config = 0;
|
||||
if (L0.z > 0.0) config += 1;
|
||||
if (L1.z > 0.0) config += 2;
|
||||
if (L2.z > 0.0) config += 4;
|
||||
if (L3.z > 0.0) config += 8;
|
||||
|
||||
// Clip
|
||||
if (config == 0) {
|
||||
// Clip all
|
||||
}
|
||||
else if (config == 1) { // V1 clip V2 V3 V4
|
||||
n = 3;
|
||||
L1 = -L1.z * L0 + L0.z * L1;
|
||||
L2 = -L3.z * L0 + L0.z * L3;
|
||||
}
|
||||
else if (config == 2) { // V2 clip V1 V3 V4
|
||||
n = 3;
|
||||
L0 = -L0.z * L1 + L1.z * L0;
|
||||
L2 = -L2.z * L1 + L1.z * L2;
|
||||
}
|
||||
else if (config == 3) { // V1 V2 clip V3 V4
|
||||
n = 4;
|
||||
L2 = -L2.z * L1 + L1.z * L2;
|
||||
L3 = -L3.z * L0 + L0.z * L3;
|
||||
}
|
||||
else if (config == 4) { // V3 clip V1 V2 V4
|
||||
n = 3;
|
||||
L0 = -L3.z * L2 + L2.z * L3;
|
||||
L1 = -L1.z * L2 + L2.z * L1;
|
||||
}
|
||||
else if (config == 5) { // V1 V3 clip V2 V4) impossible
|
||||
n = 0;
|
||||
}
|
||||
else if (config == 6) { // V2 V3 clip V1 V4
|
||||
n = 4;
|
||||
L0 = -L0.z * L1 + L1.z * L0;
|
||||
L3 = -L3.z * L2 + L2.z * L3;
|
||||
}
|
||||
else if (config == 7) { // V1 V2 V3 clip V4
|
||||
n = 5;
|
||||
L4 = -L3.z * L0 + L0.z * L3;
|
||||
L3 = -L3.z * L2 + L2.z * L3;
|
||||
}
|
||||
else if (config == 8) { // V4 clip V1 V2 V3
|
||||
n = 3;
|
||||
L0 = -L0.z * L3 + L3.z * L0;
|
||||
L1 = -L2.z * L3 + L3.z * L2;
|
||||
L2 = L3;
|
||||
}
|
||||
else if (config == 9) { // V1 V4 clip V2 V3
|
||||
n = 4;
|
||||
L1 = -L1.z * L0 + L0.z * L1;
|
||||
L2 = -L2.z * L3 + L3.z * L2;
|
||||
}
|
||||
else if (config == 10) { // V2 V4 clip V1 V3) impossible
|
||||
n = 0;
|
||||
}
|
||||
else if (config == 11) { // V1 V2 V4 clip V3
|
||||
n = 5;
|
||||
L4 = L3;
|
||||
L3 = -L2.z * L3 + L3.z * L2;
|
||||
L2 = -L2.z * L1 + L1.z * L2;
|
||||
}
|
||||
else if (config == 12) { // V3 V4 clip V1 V2
|
||||
n = 4;
|
||||
L1 = -L1.z * L2 + L2.z * L1;
|
||||
L0 = -L0.z * L3 + L3.z * L0;
|
||||
}
|
||||
else if (config == 13) { // V1 V3 V4 clip V2
|
||||
n = 5;
|
||||
L4 = L3;
|
||||
L3 = L2;
|
||||
L2 = -L1.z * L2 + L2.z * L1;
|
||||
L1 = -L1.z * L0 + L0.z * L1;
|
||||
}
|
||||
else if (config == 14) { // V2 V3 V4 clip V1
|
||||
n = 5;
|
||||
L4 = -L0.z * L3 + L3.z * L0;
|
||||
L0 = -L0.z * L1 + L1.z * L0;
|
||||
}
|
||||
else if (config == 15) { // V1 V2 V3 V4
|
||||
n = 4;
|
||||
}
|
||||
|
||||
if (n == 3) L3 = L0;
|
||||
if (n == 4) L4 = L0;
|
||||
return n;
|
||||
}
|
||||
|
||||
float ltcEvaluate(vec3 N, vec3 V, float dotNV, vec3 P, mat3 Minv, vec3 points0, vec3 points1, vec3 points2, vec3 points3) {
|
||||
// Construct orthonormal basis around N
|
||||
vec3 T1, T2;
|
||||
T1 = normalize(V - N * dotNV);
|
||||
T2 = cross(N, T1);
|
||||
|
||||
// Rotate area light in (T1, T2, R) basis
|
||||
Minv = Minv * transpose(mat3(T1, T2, N));
|
||||
|
||||
// Polygon (allocate 5 vertices for clipping)
|
||||
// vec3 L[5];
|
||||
L0 = Minv * (points0 - P);
|
||||
L1 = Minv * (points1 - P);
|
||||
L2 = Minv * (points2 - P);
|
||||
L3 = Minv * (points3 - P);
|
||||
L4 = vec3(0.0);
|
||||
|
||||
// int n;
|
||||
int n = clipQuadToHorizon(/*L, n*/);
|
||||
|
||||
if (n == 0) return 0.0;
|
||||
|
||||
// Project onto sphere
|
||||
L0 = normalize(L0);
|
||||
L1 = normalize(L1);
|
||||
L2 = normalize(L2);
|
||||
L3 = normalize(L3);
|
||||
L4 = normalize(L4);
|
||||
|
||||
// Integrate
|
||||
float sum = 0.0;
|
||||
|
||||
sum += integrateEdge(L0, L1);
|
||||
sum += integrateEdge(L1, L2);
|
||||
sum += integrateEdge(L2, L3);
|
||||
|
||||
if (n >= 4) sum += integrateEdge(L3, L4);
|
||||
if (n == 5) sum += integrateEdge(L4, L0);
|
||||
|
||||
#ifdef _TwoSidedAreaLight
|
||||
return abs(sum);
|
||||
#else
|
||||
return max(0.0, -sum);
|
||||
#endif
|
||||
}
|
41
leenkx/Shaders/std/mapping.glsl
Normal file
41
leenkx/Shaders/std/mapping.glsl
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
https://github.com/JonasFolletete/glsl-triplanar-mapping
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Jonas Folletête
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of 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.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
vec3 blendNormal(vec3 normal) {
|
||||
vec3 blending = abs(normal);
|
||||
blending = normalize(max(blending, 0.00001));
|
||||
blending /= vec3(blending.x + blending.y + blending.z);
|
||||
return blending;
|
||||
}
|
||||
|
||||
vec3 triplanarMapping (sampler2D ImageTexture, vec3 normal, vec3 position) {
|
||||
vec3 normalBlend = blendNormal(normal);
|
||||
vec3 xColor = texture(ImageTexture, position.yz).rgb;
|
||||
vec3 yColor = texture(ImageTexture, position.xz).rgb;
|
||||
vec3 zColor = texture(ImageTexture, position.xy).rgb;
|
||||
|
||||
return (xColor * normalBlend.x + yColor * normalBlend.y + zColor * normalBlend.z);
|
||||
}
|
48
leenkx/Shaders/std/math.glsl
Normal file
48
leenkx/Shaders/std/math.glsl
Normal file
@ -0,0 +1,48 @@
|
||||
|
||||
#ifndef _MATH_GLSL_
|
||||
#define _MATH_GLSL_
|
||||
|
||||
float hash(const vec2 p) {
|
||||
float h = dot(p, vec2(127.1, 311.7));
|
||||
return fract(sin(h) * 43758.5453123);
|
||||
}
|
||||
|
||||
vec2 envMapEquirect(const vec3 normal) {
|
||||
const float PI = 3.1415926535;
|
||||
const float PI2 = PI * 2.0;
|
||||
float phi = acos(normal.z);
|
||||
float theta = atan(-normal.y, normal.x) + PI;
|
||||
return vec2(theta / PI2, phi / PI);
|
||||
}
|
||||
|
||||
float rand(const vec2 co) { // Unreliable
|
||||
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
vec2 rand2(const vec2 coord) {
|
||||
const float width = 1100.0;
|
||||
const float height = 500.0;
|
||||
float noiseX = ((fract(1.0 - coord.s * (width / 2.0)) * 0.25) + (fract(coord.t * (height / 2.0)) * 0.75)) * 2.0 - 1.0;
|
||||
float noiseY = ((fract(1.0 - coord.s * (width / 2.0)) * 0.75) + (fract(coord.t * (height / 2.0)) * 0.25)) * 2.0 - 1.0;
|
||||
return vec2(noiseX, noiseY);
|
||||
}
|
||||
|
||||
float linearize(const float depth, vec2 cameraProj) {
|
||||
// to viewz
|
||||
return cameraProj.y / (depth - cameraProj.x);
|
||||
}
|
||||
|
||||
float attenuate(const float dist) {
|
||||
// float attenuate(float dist, float constant, float linear, float quadratic) {
|
||||
return 1.0 / (dist * dist);
|
||||
// 1.0 / (constant * 1.0)
|
||||
// 1.0 / (linear * dist)
|
||||
// 1.0 / (quadratic * dist * dist);
|
||||
}
|
||||
|
||||
float safe_acos(const float x) {
|
||||
// acos is undefined if |x| > 1
|
||||
return acos(clamp(x, -1.0, 1.0));
|
||||
}
|
||||
|
||||
#endif
|
53
leenkx/Shaders/std/morph_target.glsl
Normal file
53
leenkx/Shaders/std/morph_target.glsl
Normal file
@ -0,0 +1,53 @@
|
||||
uniform sampler2D morphDataPos;
|
||||
uniform sampler2D morphDataNor;
|
||||
uniform vec2 morphScaleOffset;
|
||||
uniform vec2 morphDataDim;
|
||||
uniform vec4 morphWeights[8];
|
||||
|
||||
void getMorphedVertex(vec2 uvCoord, inout vec3 A){
|
||||
for(int i = 0; i<8; i++ )
|
||||
{
|
||||
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
|
||||
uvCoord.y - (i * 4 + 1) * morphDataDim.y,
|
||||
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
|
||||
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
|
||||
|
||||
vec3 morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.x)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += morphWeights[i].x * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += morphWeights[i].y * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.z)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += morphWeights[i].z * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, tempCoordY.w)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += morphWeights[i].w * morph;
|
||||
}
|
||||
}
|
||||
|
||||
void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor){
|
||||
|
||||
for(int i = 0; i<8; i++ )
|
||||
{
|
||||
vec4 tempCoordY = vec4( uvCoord.y - (i * 4) * morphDataDim.y,
|
||||
uvCoord.y - (i * 4 + 1) * morphDataDim.y,
|
||||
uvCoord.y - (i * 4 + 2) * morphDataDim.y,
|
||||
uvCoord.y - (i * 4 + 3) * morphDataDim.y);
|
||||
|
||||
vec3 norm = oldNor + morphWeights[i].x * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.x)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + morphWeights[i].y * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.y)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + morphWeights[i].z * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.z)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + morphWeights[i].w * (texture(morphDataNor, vec2(uvCoord.x, tempCoordY.w)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
}
|
||||
|
||||
morphNor = normalize(morphNor);
|
||||
}
|
31
leenkx/Shaders/std/normals.glsl
Normal file
31
leenkx/Shaders/std/normals.glsl
Normal file
@ -0,0 +1,31 @@
|
||||
// http://www.thetenthplanet.de/archives/1180
|
||||
mat3 cotangentFrame(const vec3 n, const vec3 p, const vec2 duv1, const vec2 duv2) {
|
||||
// Get edge vectors of the pixel triangle
|
||||
vec3 dp1 = dFdx(p);
|
||||
vec3 dp2 = dFdy(p);
|
||||
|
||||
// Solve the linear system
|
||||
vec3 dp2perp = cross(dp2, n);
|
||||
vec3 dp1perp = cross(n, dp1);
|
||||
vec3 t = dp2perp * duv1.x + dp1perp * duv2.x;
|
||||
vec3 b = dp2perp * duv1.y + dp1perp * duv2.y;
|
||||
|
||||
// Construct a scale-invariant frame
|
||||
float invmax = inversesqrt(max(dot(t, t), dot(b, b)));
|
||||
return mat3(t * invmax, b * invmax, n);
|
||||
}
|
||||
|
||||
mat3 cotangentFrame(const vec3 n, const vec3 p, const vec2 texCoord) {
|
||||
return cotangentFrame(n, p, dFdx(texCoord), dFdy(texCoord));
|
||||
}
|
||||
|
||||
// vec3 perturbNormal(vec3 n, vec3 v, vec2 texCoord) {
|
||||
// Assume N, the interpolated vertex normal and V, the view vector (vertex to eye)
|
||||
// vec3 map = texture(snormal, texCoord).xyz * (255.0 / 127.0) - (128.0 / 127.0);
|
||||
// WITH_NORMALMAP_2CHANNEL
|
||||
// map.z = sqrt(1.0 - dot(map.xy, map.xy));
|
||||
// WITH_NORMALMAP_GREEN_UP
|
||||
// map.y = -map.y;
|
||||
// mat3 TBN = cotangentFrame(n, -v, texCoord);
|
||||
// return normalize(TBN * map);
|
||||
// }
|
228
leenkx/Shaders/std/resample.glsl
Normal file
228
leenkx/Shaders/std/resample.glsl
Normal file
@ -0,0 +1,228 @@
|
||||
#ifndef _RESAMPLE_GLSL_
|
||||
#define _RESAMPLE_GLSL_
|
||||
|
||||
float karisWeight(const vec3 value) {
|
||||
// Using brightness instead of luma
|
||||
return 1.0 / (1.0 + max(value.r, max(value.g, value.b)));
|
||||
}
|
||||
|
||||
/**
|
||||
Downsampling using a 4x4 box filter.
|
||||
**/
|
||||
vec3 downsample_box_filter(const sampler2D tex, const vec2 texCoord, const vec2 texelSize) {
|
||||
vec4 delta = texelSize.xyxy * vec4(-0.5, -0.5, 0.5, 0.5);
|
||||
|
||||
vec3 result;
|
||||
result = textureLod(tex, texCoord + delta.xy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta.zy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta.xw, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta.zw, 0.0).rgb;
|
||||
|
||||
return result * (1.0 / 4.0);
|
||||
}
|
||||
|
||||
vec3 downsample_box_filter_anti_flicker(const sampler2D tex, const vec2 texCoord, const vec2 texelSize) {
|
||||
vec4 delta = texelSize.xyxy * vec4(-0.5, -0.5, 0.5, 0.5);
|
||||
|
||||
vec3 bl = textureLod(tex, texCoord + delta.xy, 0.0).rgb;
|
||||
vec3 br = textureLod(tex, texCoord + delta.zy, 0.0).rgb;
|
||||
vec3 tl = textureLod(tex, texCoord + delta.xw, 0.0).rgb;
|
||||
vec3 tr = textureLod(tex, texCoord + delta.zw, 0.0).rgb;
|
||||
|
||||
// Weighted averaging technique by Brian Karis to reduce fireflies:
|
||||
// http://graphicrants.blogspot.com/2013/12/tone-mapping.html
|
||||
float blWeight = karisWeight(bl);
|
||||
float brWeight = karisWeight(br);
|
||||
float tlWeight = karisWeight(tl);
|
||||
float trWeight = karisWeight(tr);
|
||||
|
||||
return (bl * blWeight + br * brWeight + tl * tlWeight + tr * trWeight) / (blWeight + brWeight + tlWeight + trWeight);
|
||||
}
|
||||
|
||||
/**
|
||||
Downsample using the "dual filtering" technique from "Bandwidth-Efficient Rendering"
|
||||
by Marius Bjørge, Siggraph 2015:
|
||||
https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_marius_2D00_slides.pdf
|
||||
**/
|
||||
vec3 downsample_dual_filter(const sampler2D tex, const vec2 texCoord, const vec2 texelSize) {
|
||||
vec3 delta = texelSize.xyx * vec3(0.5, 0.5, -0.5);
|
||||
|
||||
vec3 result;
|
||||
result = textureLod(tex, texCoord, 0.0).rgb * 4.0;
|
||||
result += textureLod(tex, texCoord - delta.xy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord - delta.zy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta.zy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta.xy, 0.0).rgb;
|
||||
|
||||
return result * (1.0 / 8.0);
|
||||
}
|
||||
|
||||
vec3 downsample_dual_filter_anti_flicker(const sampler2D tex, const vec2 texCoord, const vec2 texelSize) {
|
||||
vec3 delta = texelSize.xyx * vec3(0.5, 0.5, -0.5);
|
||||
|
||||
vec3 c = textureLod(tex, texCoord, 0.0).rgb;
|
||||
vec3 bl = textureLod(tex, texCoord - delta.xy, 0.0).rgb;
|
||||
vec3 br = textureLod(tex, texCoord - delta.zy, 0.0).rgb;
|
||||
vec3 tl = textureLod(tex, texCoord + delta.zy, 0.0).rgb;
|
||||
vec3 tr = textureLod(tex, texCoord + delta.xy, 0.0).rgb;
|
||||
|
||||
float cWeight = karisWeight(c) * 4.0;
|
||||
float blWeight = karisWeight(bl);
|
||||
float brWeight = karisWeight(br);
|
||||
float tlWeight = karisWeight(tl);
|
||||
float trWeight = karisWeight(tr);
|
||||
|
||||
return (c * cWeight + bl * blWeight + br * brWeight + tl * tlWeight + tr * trWeight) / (cWeight + blWeight + brWeight + tlWeight + trWeight);
|
||||
}
|
||||
|
||||
/**
|
||||
Downsample using the approach from "NEXT GENERATION POST PROCESSING IN CALL OF DUTY: ADVANCED WARFARE"
|
||||
by Jorge Jimenez, SIGGRAPH 2014: https://advances.realtimerendering.com/s2014/#_NEXT_GENERATION_POST
|
||||
**/
|
||||
vec3 downsample_13_tap(const sampler2D tex, const vec2 texCoord, const vec2 texelSize) {
|
||||
/*
|
||||
| TL T TR |
|
||||
| tl tr |
|
||||
| L C R |
|
||||
| bl br |
|
||||
| BL B BR |
|
||||
*/
|
||||
|
||||
vec4 delta = texelSize.xyxy * vec4(1.0, 1.0, -1.0, 0.0);
|
||||
vec4 deltaHalf = delta * 0.5;
|
||||
|
||||
// TODO investigate if sampling in morton order is faster here
|
||||
|
||||
vec3 TL = textureLod(tex, texCoord + delta.zy, 0.0).rgb;
|
||||
vec3 T = textureLod(tex, texCoord + delta.wy, 0.0).rgb;
|
||||
vec3 TR = textureLod(tex, texCoord + delta.xy, 0.0).rgb;
|
||||
|
||||
vec3 L = textureLod(tex, texCoord + delta.zw, 0.0).rgb;
|
||||
vec3 C = textureLod(tex, texCoord, 0.0).rgb;
|
||||
vec3 R = textureLod(tex, texCoord + delta.xw, 0.0).rgb;
|
||||
|
||||
vec3 BL = textureLod(tex, texCoord - delta.xy, 0.0).rgb;
|
||||
vec3 B = textureLod(tex, texCoord - delta.wy, 0.0).rgb;
|
||||
vec3 BR = textureLod(tex, texCoord - delta.zy, 0.0).rgb;
|
||||
|
||||
vec3 tl = textureLod(tex, texCoord + deltaHalf.zy, 0.0).rgb;
|
||||
vec3 tr = textureLod(tex, texCoord + deltaHalf.xy, 0.0).rgb;
|
||||
vec3 bl = textureLod(tex, texCoord - deltaHalf.xy, 0.0).rgb;
|
||||
vec3 br = textureLod(tex, texCoord - deltaHalf.zy, 0.0).rgb;
|
||||
|
||||
vec3 result;
|
||||
result = C * 0.125;
|
||||
result += (TL + TR + BL + BR) * 0.03125;
|
||||
result += (T + L + R + B) * 0.0625;
|
||||
result += (tl + tr + bl + br) * 0.125;
|
||||
return result;
|
||||
}
|
||||
|
||||
vec3 downsample_13_tap_anti_flicker(const sampler2D tex, const vec2 texCoord, const vec2 texelSize) {
|
||||
vec4 delta = texelSize.xyxy * vec4(1.0, 1.0, -1.0, 0.0);
|
||||
vec4 deltaHalf = delta * 0.5;
|
||||
|
||||
vec3 TL = textureLod(tex, texCoord + delta.zy, 0.0).rgb;
|
||||
vec3 T = textureLod(tex, texCoord + delta.wy, 0.0).rgb;
|
||||
vec3 TR = textureLod(tex, texCoord + delta.xy, 0.0).rgb;
|
||||
|
||||
vec3 L = textureLod(tex, texCoord + delta.zw, 0.0).rgb;
|
||||
vec3 C = textureLod(tex, texCoord, 0.0).rgb;
|
||||
vec3 R = textureLod(tex, texCoord + delta.xw, 0.0).rgb;
|
||||
|
||||
vec3 BL = textureLod(tex, texCoord - delta.xy, 0.0).rgb;
|
||||
vec3 B = textureLod(tex, texCoord - delta.wy, 0.0).rgb;
|
||||
vec3 BR = textureLod(tex, texCoord - delta.zy, 0.0).rgb;
|
||||
|
||||
vec3 tl = textureLod(tex, texCoord + deltaHalf.zy, 0.0).rgb;
|
||||
vec3 tr = textureLod(tex, texCoord + deltaHalf.xy, 0.0).rgb;
|
||||
vec3 bl = textureLod(tex, texCoord - deltaHalf.xy, 0.0).rgb;
|
||||
vec3 br = textureLod(tex, texCoord - deltaHalf.zy, 0.0).rgb;
|
||||
|
||||
// Apply Karis average to groups of four sampled values, adapted from
|
||||
// Jimenez 2014. The similar but faster implementation from
|
||||
// https://learnopengl.com/Guest-Articles/2022/Phys.-Based-Bloom
|
||||
// doesn't work as it doesn't really reduce fireflies :/
|
||||
|
||||
float TLWeight = karisWeight(TL);
|
||||
float TWeight = karisWeight(T);
|
||||
float TRWeight = karisWeight(TR);
|
||||
|
||||
float LWeight = karisWeight(L);
|
||||
float CWeight = karisWeight(C);
|
||||
float RWeight = karisWeight(R);
|
||||
|
||||
float BLWeight = karisWeight(BL);
|
||||
float BWeight = karisWeight(B);
|
||||
float BRWeight = karisWeight(BR);
|
||||
|
||||
float tlWeight = karisWeight(tl);
|
||||
float trWeight = karisWeight(tr);
|
||||
float blWeight = karisWeight(bl);
|
||||
float brWeight = karisWeight(br);
|
||||
|
||||
vec3 result;
|
||||
result = 0.125 * (TL * TLWeight + T * TWeight + L * LWeight + C * CWeight) / (TLWeight + TWeight + LWeight + CWeight);
|
||||
result += 0.125 * (T * TWeight + TR * TRWeight + C * CWeight + R * RWeight) / (TWeight + TRWeight + CWeight + RWeight);
|
||||
result += 0.125 * (L * LWeight + C * CWeight + BL * BLWeight + B * BWeight) / (LWeight + CWeight + BLWeight + BWeight);
|
||||
result += 0.125 * (C * CWeight + R * RWeight + B * BWeight + R * RWeight) / (CWeight + RWeight + BWeight + RWeight);
|
||||
result += 0.5 * (tl * tlWeight + tr * trWeight + bl * blWeight + br * brWeight) / (tlWeight + trWeight + blWeight + brWeight);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
vec3 upsample_4tap_bilinear(const sampler2D tex, const vec2 texCoord, const vec2 texelSize, const float sampleScale) {
|
||||
vec3 delta = texelSize.xyx * vec3(1.0, 1.0, -1.0) * sampleScale;
|
||||
|
||||
vec3 result;
|
||||
result = textureLod(tex, texCoord - delta.xy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord - delta.zy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta.zy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta.xy, 0.0).rgb;
|
||||
|
||||
return result * (1.0 / 4.0);
|
||||
}
|
||||
|
||||
vec3 upsample_dual_filter(const sampler2D tex, const vec2 texCoord, const vec2 texelSize, const float sampleScale) {
|
||||
vec2 delta = texelSize * sampleScale;
|
||||
|
||||
vec3 result;
|
||||
result = textureLod(tex, texCoord + vec2(-delta.x * 2.0, 0.0), 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + vec2(-delta.x, delta.y), 0.0).rgb * 2.0;
|
||||
result += textureLod(tex, texCoord + vec2(0.0, delta.y * 2.0), 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta, 0.0).rgb * 2.0;
|
||||
result += textureLod(tex, texCoord + vec2(delta.x * 2.0, 0.0), 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + vec2(delta.x, -delta.y), 0.0).rgb * 2.0;
|
||||
result += textureLod(tex, texCoord + vec2(0.0, -delta.y * 2.0), 0.0).rgb;
|
||||
result += textureLod(tex, texCoord - delta, 0.0).rgb * 2.0;
|
||||
|
||||
return result * (1.0 / 12.0);
|
||||
}
|
||||
|
||||
/**
|
||||
3x3 (9-tap) tent/bartlett filter, which approximates gaussian blur if applied repeatedly:
|
||||
- Wojciech Jarosz: Fast Image Convolutions
|
||||
http://elynxsdk.free.fr/ext-docs/Blur/Fast_box_blur.pdf
|
||||
- Martin Kraus, Magnus Strengert: Pyramid Filters Based on Bilinear Interpolation
|
||||
https://www.cs.cit.tum.de/fileadmin/w00cfj/cg/Research/Publications/2007/Pyramid_Filters/GRAPP07.pdf
|
||||
**/
|
||||
vec3 upsample_tent_filter_3x3(const sampler2D tex, const vec2 texCoord, const vec2 texelSize, const float sampleScale) {
|
||||
vec4 delta = texelSize.xyxy * vec4(1.0, 1.0, -1.0, 0.0) * sampleScale;
|
||||
|
||||
vec3 result;
|
||||
result = textureLod(tex, texCoord - delta.xy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord - delta.wy, 0.0).rgb * 2.0;
|
||||
result += textureLod(tex, texCoord - delta.zy, 0.0).rgb;
|
||||
|
||||
result += textureLod(tex, texCoord + delta.zw, 0.0).rgb * 2.0;
|
||||
result += textureLod(tex, texCoord , 0.0).rgb * 4.0;
|
||||
result += textureLod(tex, texCoord + delta.xw, 0.0).rgb * 2.0;
|
||||
|
||||
result += textureLod(tex, texCoord + delta.zy, 0.0).rgb;
|
||||
result += textureLod(tex, texCoord + delta.wy, 0.0).rgb * 2.0;
|
||||
result += textureLod(tex, texCoord + delta.xy, 0.0).rgb;
|
||||
|
||||
return result * (1.0 / 16.0);
|
||||
}
|
||||
|
||||
#endif
|
406
leenkx/Shaders/std/shadows.glsl
Normal file
406
leenkx/Shaders/std/shadows.glsl
Normal file
@ -0,0 +1,406 @@
|
||||
//Transparent shadow implemented by Yvain Douard with reference:
|
||||
//https://wickedengine.net/2018/01/easy-transparent-shadow-maps/comment-page-1/
|
||||
|
||||
#ifndef _SHADOWS_GLSL_
|
||||
#define _SHADOWS_GLSL_
|
||||
|
||||
#include "compiled.inc"
|
||||
|
||||
#ifdef _CSM
|
||||
uniform vec4 casData[shadowmapCascades * 4 + 4];
|
||||
#endif
|
||||
|
||||
#ifdef _SMSizeUniform
|
||||
uniform vec2 smSizeUniform;
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _Clusters
|
||||
#ifdef _ShadowMapAtlas
|
||||
uniform vec4 pointLightDataArray[maxLightsCluster * 6];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec20.pdf // p:168
|
||||
// https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/5337472/
|
||||
vec2 sampleCube(vec3 dir, out int faceIndex) {
|
||||
vec3 dirAbs = abs(dir);
|
||||
float ma;
|
||||
vec2 uv;
|
||||
if(dirAbs.z >= dirAbs.x && dirAbs.z >= dirAbs.y) {
|
||||
faceIndex = dir.z < 0.0 ? 5 : 4;
|
||||
ma = 0.5 / dirAbs.z;
|
||||
uv = vec2(dir.z < 0.0 ? -dir.x : dir.x, -dir.y);
|
||||
}
|
||||
else if(dirAbs.y >= dirAbs.x) {
|
||||
faceIndex = dir.y < 0.0 ? 3 : 2;
|
||||
ma = 0.5 / dirAbs.y;
|
||||
uv = vec2(dir.x, dir.y < 0.0 ? -dir.z : dir.z);
|
||||
}
|
||||
else {
|
||||
faceIndex = dir.x < 0.0 ? 1 : 0;
|
||||
ma = 0.5 / dirAbs.x;
|
||||
uv = vec2(dir.x < 0.0 ? dir.z : -dir.z, -dir.y);
|
||||
}
|
||||
// downscale uv a little to hide seams
|
||||
// transform coordinates from clip space to texture space
|
||||
#ifndef _FlipY
|
||||
return uv * 0.9976 * ma + 0.5;
|
||||
#else
|
||||
#ifdef HLSL
|
||||
return uv * 0.9976 * ma + 0.5;
|
||||
#else
|
||||
return vec2(uv.x * ma, uv.y * -ma) * 0.9976 + 0.5;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 PCF(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec2 uv, const float compare, const vec2 smSize, const bool transparent) {
|
||||
vec3 result = vec3(0.0);
|
||||
result.x = texture(shadowMap, vec3(uv + (vec2(-1.0, -1.0) / smSize), compare));
|
||||
result.x += texture(shadowMap, vec3(uv + (vec2(-1.0, 0.0) / smSize), compare));
|
||||
result.x += texture(shadowMap, vec3(uv + (vec2(-1.0, 1.0) / smSize), compare));
|
||||
result.x += texture(shadowMap, vec3(uv + (vec2(0.0, -1.0) / smSize), compare));
|
||||
result.x += texture(shadowMap, vec3(uv, compare));
|
||||
result.x += texture(shadowMap, vec3(uv + (vec2(0.0, 1.0) / smSize), compare));
|
||||
result.x += texture(shadowMap, vec3(uv + (vec2(1.0, -1.0) / smSize), compare));
|
||||
result.x += texture(shadowMap, vec3(uv + (vec2(1.0, 0.0) / smSize), compare));
|
||||
result.x += texture(shadowMap, vec3(uv + (vec2(1.0, 1.0) / smSize), compare));
|
||||
result = result.xxx / 9.0;
|
||||
|
||||
if (transparent == false) {
|
||||
vec4 shadowmap_transparent = texture(shadowMapTransparent, uv);
|
||||
if (shadowmap_transparent.a < compare)
|
||||
result *= shadowmap_transparent.rgb;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float lpToDepth(vec3 lp, const vec2 lightProj) {
|
||||
lp = abs(lp);
|
||||
float zcomp = max(lp.x, max(lp.y, lp.z));
|
||||
zcomp = lightProj.x - lightProj.y / zcomp;
|
||||
return zcomp * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
vec3 PCFCube(samplerCubeShadow shadowMapCube, samplerCube shadowMapCubeTransparent, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const bool transparent) {
|
||||
const float s = shadowmapCubePcfSize; // TODO: incorrect...
|
||||
float compare = lpToDepth(lp, lightProj) - bias * 1.5;
|
||||
ml = ml + n * bias * 20;
|
||||
#ifdef _InvY
|
||||
ml.y = -ml.y;
|
||||
#endif
|
||||
vec3 result = vec3(0.0);
|
||||
result.x = texture(shadowMapCube, vec4(ml, compare));
|
||||
result.x += texture(shadowMapCube, vec4(ml + vec3(s, s, s), compare));
|
||||
result.x += texture(shadowMapCube, vec4(ml + vec3(-s, s, s), compare));
|
||||
result.x += texture(shadowMapCube, vec4(ml + vec3(s, -s, s), compare));
|
||||
result.x += texture(shadowMapCube, vec4(ml + vec3(s, s, -s), compare));
|
||||
result.x += texture(shadowMapCube, vec4(ml + vec3(-s, -s, s), compare));
|
||||
result.x += texture(shadowMapCube, vec4(ml + vec3(s, -s, -s), compare));
|
||||
result.x += texture(shadowMapCube, vec4(ml + vec3(-s, s, -s), compare));
|
||||
result.x += texture(shadowMapCube, vec4(ml + vec3(-s, -s, -s), compare));
|
||||
result = result.xxx / 9.0;
|
||||
|
||||
if (transparent == false) {
|
||||
vec4 shadowmap_transparent = texture(shadowMapCubeTransparent, ml);
|
||||
if (shadowmap_transparent.a < compare)
|
||||
result *= shadowmap_transparent.rgb;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
// transform "out-of-bounds" coordinates to the correct face/coordinate system
|
||||
// https://www.khronos.org/opengl/wiki/File:CubeMapAxes.png
|
||||
vec2 transformOffsetedUV(const int faceIndex, out int newFaceIndex, vec2 uv) {
|
||||
if (uv.x < 0.0) {
|
||||
if (faceIndex == 0) { // X+
|
||||
newFaceIndex = 4; // Z+
|
||||
}
|
||||
else if (faceIndex == 1) { // X-
|
||||
newFaceIndex = 5; // Z-
|
||||
}
|
||||
else if (faceIndex == 2) { // Y+
|
||||
newFaceIndex = 1; // X-
|
||||
}
|
||||
else if (faceIndex == 3) { // Y-
|
||||
newFaceIndex = 1; // X-
|
||||
}
|
||||
else if (faceIndex == 4) { // Z+
|
||||
newFaceIndex = 1; // X-
|
||||
}
|
||||
else { // Z-
|
||||
newFaceIndex = 0; // X+
|
||||
}
|
||||
uv = vec2(1.0 + uv.x, uv.y);
|
||||
}
|
||||
else if (uv.x > 1.0) {
|
||||
if (faceIndex == 0) { // X+
|
||||
newFaceIndex = 5; // Z-
|
||||
}
|
||||
else if (faceIndex == 1) { // X-
|
||||
newFaceIndex = 4; // Z+
|
||||
}
|
||||
else if (faceIndex == 2) { // Y+
|
||||
newFaceIndex = 0; // X+
|
||||
}
|
||||
else if (faceIndex == 3) { // Y-
|
||||
newFaceIndex = 0; // X+
|
||||
}
|
||||
else if (faceIndex == 4) { // Z+
|
||||
newFaceIndex = 0; // X+
|
||||
}
|
||||
else { // Z-
|
||||
newFaceIndex = 1; // X-
|
||||
}
|
||||
uv = vec2(1.0 - uv.x, uv.y);
|
||||
}
|
||||
else if (uv.y < 0.0) {
|
||||
if (faceIndex == 0) { // X+
|
||||
newFaceIndex = 2; // Y+
|
||||
}
|
||||
else if (faceIndex == 1) { // X-
|
||||
newFaceIndex = 2; // Y+
|
||||
}
|
||||
else if (faceIndex == 2) { // Y+
|
||||
newFaceIndex = 5; // Z-
|
||||
}
|
||||
else if (faceIndex == 3) { // Y-
|
||||
newFaceIndex = 4; // Z+
|
||||
}
|
||||
else if (faceIndex == 4) { // Z+
|
||||
newFaceIndex = 2; // Y+
|
||||
}
|
||||
else { // Z-
|
||||
newFaceIndex = 2; // Y+
|
||||
}
|
||||
uv = vec2(uv.x, 1.0 + uv.y);
|
||||
}
|
||||
else if (uv.y > 1.0) {
|
||||
if (faceIndex == 0) { // X+
|
||||
newFaceIndex = 3; // Y-
|
||||
}
|
||||
else if (faceIndex == 1) { // X-
|
||||
newFaceIndex = 3; // Y-
|
||||
}
|
||||
else if (faceIndex == 2) { // Y+
|
||||
newFaceIndex = 4; // Z+
|
||||
}
|
||||
else if (faceIndex == 3) { // Y-
|
||||
newFaceIndex = 5; // Z-
|
||||
}
|
||||
else if (faceIndex == 4) { // Z+
|
||||
newFaceIndex = 3; // Y-
|
||||
}
|
||||
else { // Z-
|
||||
newFaceIndex = 3; // Y-
|
||||
}
|
||||
uv = vec2(uv.x, 1.0 - uv.y);
|
||||
} else {
|
||||
newFaceIndex = faceIndex;
|
||||
}
|
||||
// cover corner cases too
|
||||
return uv;
|
||||
}
|
||||
|
||||
vec3 PCFFakeCube(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 lp, vec3 ml, const float bias, const vec2 lightProj, const vec3 n, const int index, const bool transparent) {
|
||||
const vec2 smSize = smSizeUniform; // TODO: incorrect...
|
||||
const float compare = lpToDepth(lp, lightProj) - bias * 1.5;
|
||||
ml = ml + n * bias * 20;
|
||||
|
||||
int faceIndex = 0;
|
||||
const int lightIndex = index * 6;
|
||||
const vec2 uv = sampleCube(ml, faceIndex);
|
||||
|
||||
vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
|
||||
vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
|
||||
vec3 result = vec3(0.0);
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
// soft shadowing
|
||||
int newFaceIndex = 0;
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 0.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, 1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, -1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(-1.0, -1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(0.0, 1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, -1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, 0.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
uvtiled = transformOffsetedUV(faceIndex, newFaceIndex, vec2(uv + (vec2(1.0, 1.0) / smSize)));
|
||||
pointLightTile = pointLightDataArray[lightIndex + newFaceIndex];
|
||||
uvtiled = pointLightTile.z * uvtiled + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
result.x += texture(shadowMap, vec3(uvtiled, compare));
|
||||
|
||||
result = result.xxx / 9.0;
|
||||
|
||||
pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
|
||||
uvtiled = pointLightTile.z * uv + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
|
||||
if (transparent == false) {
|
||||
vec4 shadowmap_transparent = texture(shadowMapTransparent, uvtiled);
|
||||
if (shadowmap_transparent.a < compare)
|
||||
result *= shadowmap_transparent.rgb;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 shadowTest(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 lPos, const float shadowsBias, const bool transparent) {
|
||||
#ifdef _SMSizeUniform
|
||||
vec2 smSize = smSizeUniform;
|
||||
#else
|
||||
const vec2 smSize = shadowmapSize;
|
||||
#endif
|
||||
if (lPos.x < 0.0 || lPos.y < 0.0 || lPos.x > 1.0 || lPos.y > 1.0) return vec3(1.0);
|
||||
return PCF(shadowMap, shadowMapTransparent, lPos.xy, lPos.z - shadowsBias, smSize, transparent);
|
||||
}
|
||||
|
||||
#ifdef _CSM
|
||||
mat4 getCascadeMat(const float d, out int casi, out int casIndex) {
|
||||
const int c = shadowmapCascades;
|
||||
|
||||
// Get cascade index
|
||||
// TODO: use bounding box slice selection instead of sphere
|
||||
const vec4 ci = vec4(float(c > 0), float(c > 1), float(c > 2), float(c > 3));
|
||||
// int ci;
|
||||
// if (d < casData[c * 4].x) ci = 0;
|
||||
// else if (d < casData[c * 4].y) ci = 1 * 4;
|
||||
// else if (d < casData[c * 4].z) ci = 2 * 4;
|
||||
// else ci = 3 * 4;
|
||||
// Splits
|
||||
vec4 comp = vec4(
|
||||
float(d > casData[c * 4].x),
|
||||
float(d > casData[c * 4].y),
|
||||
float(d > casData[c * 4].z),
|
||||
float(d > casData[c * 4].w));
|
||||
casi = int(min(dot(ci, comp), c));
|
||||
|
||||
// Get cascade mat
|
||||
casIndex = casi * 4;
|
||||
|
||||
return mat4(
|
||||
casData[casIndex ],
|
||||
casData[casIndex + 1],
|
||||
casData[casIndex + 2],
|
||||
casData[casIndex + 3]);
|
||||
|
||||
// if (casIndex == 0) return mat4(casData[0], casData[1], casData[2], casData[3]);
|
||||
// ..
|
||||
}
|
||||
|
||||
vec3 shadowTestCascade(sampler2DShadow shadowMap, sampler2D shadowMapTransparent, const vec3 eye, const vec3 p, const float shadowsBias, const bool transparent) {
|
||||
#ifdef _SMSizeUniform
|
||||
vec2 smSize = smSizeUniform;
|
||||
#else
|
||||
const vec2 smSize = shadowmapSize * vec2(shadowmapCascades, 1.0);
|
||||
#endif
|
||||
const int c = shadowmapCascades;
|
||||
float d = distance(eye, p);
|
||||
|
||||
int casi;
|
||||
int casIndex;
|
||||
mat4 LWVP = getCascadeMat(d, casi, casIndex);
|
||||
|
||||
vec4 lPos = LWVP * vec4(p, 1.0);
|
||||
lPos.xyz /= lPos.w;
|
||||
|
||||
vec3 visibility = vec3(1.0);
|
||||
if (lPos.w > 0.0) visibility = PCF(shadowMap, shadowMapTransparent, lPos.xy, lPos.z - shadowsBias, smSize, transparent);
|
||||
|
||||
// Blend cascade
|
||||
// https://github.com/TheRealMJP/Shadows
|
||||
const float blendThres = 0.15;
|
||||
float nextSplit = casData[c * 4][casi];
|
||||
float splitSize = casi == 0 ? nextSplit : nextSplit - casData[c * 4][casi - 1];
|
||||
float splitDist = (nextSplit - d) / splitSize;
|
||||
if (splitDist <= blendThres && casi != c - 1) {
|
||||
int casIndex2 = casIndex + 4;
|
||||
mat4 LWVP2 = mat4(
|
||||
casData[casIndex2 ],
|
||||
casData[casIndex2 + 1],
|
||||
casData[casIndex2 + 2],
|
||||
casData[casIndex2 + 3]);
|
||||
|
||||
vec4 lPos2 = LWVP2 * vec4(p, 1.0);
|
||||
lPos2.xyz /= lPos2.w;
|
||||
vec3 visibility2 = vec3(1.0);
|
||||
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap, shadowMapTransparent, lPos2.xy, lPos2.z - shadowsBias, smSize, transparent);
|
||||
|
||||
float lerpAmt = smoothstep(0.0, blendThres, splitDist);
|
||||
return mix(visibility2, visibility, lerpAmt);
|
||||
}
|
||||
return visibility;
|
||||
|
||||
// Visualize cascades
|
||||
// if (ci == 0) albedo.rgb = vec3(1.0, 0.0, 0.0);
|
||||
// if (ci == 4) albedo.rgb = vec3(0.0, 1.0, 0.0);
|
||||
// if (ci == 8) albedo.rgb = vec3(0.0, 0.0, 1.0);
|
||||
// if (ci == 12) albedo.rgb = vec3(1.0, 1.0, 0.0);
|
||||
}
|
||||
#endif
|
||||
#endif
|
30
leenkx/Shaders/std/shirr.glsl
Normal file
30
leenkx/Shaders/std/shirr.glsl
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
vec3 shIrradiance(const vec3 nor, const vec4 shirr[7]) {
|
||||
const float c1 = 0.429043;
|
||||
const float c2 = 0.511664;
|
||||
const float c3 = 0.743125;
|
||||
const float c4 = 0.886227;
|
||||
const float c5 = 0.247708;
|
||||
// TODO: Use padding for 4th component and pass shirr[].xyz directly
|
||||
vec3 cl00 = vec3(shirr[0].x, shirr[0].y, shirr[0].z);
|
||||
vec3 cl1m1 = vec3(shirr[0].w, shirr[1].x, shirr[1].y);
|
||||
vec3 cl10 = vec3(shirr[1].z, shirr[1].w, shirr[2].x);
|
||||
vec3 cl11 = vec3(shirr[2].y, shirr[2].z, shirr[2].w);
|
||||
vec3 cl2m2 = vec3(shirr[3].x, shirr[3].y, shirr[3].z);
|
||||
vec3 cl2m1 = vec3(shirr[3].w, shirr[4].x, shirr[4].y);
|
||||
vec3 cl20 = vec3(shirr[4].z, shirr[4].w, shirr[5].x);
|
||||
vec3 cl21 = vec3(shirr[5].y, shirr[5].z, shirr[5].w);
|
||||
vec3 cl22 = vec3(shirr[6].x, shirr[6].y, shirr[6].z);
|
||||
return (
|
||||
c1 * cl22 * (nor.y * nor.y - (-nor.z) * (-nor.z)) +
|
||||
c3 * cl20 * nor.x * nor.x +
|
||||
c4 * cl00 -
|
||||
c5 * cl20 +
|
||||
2.0 * c1 * cl2m2 * nor.y * (-nor.z) +
|
||||
2.0 * c1 * cl21 * nor.y * nor.x +
|
||||
2.0 * c1 * cl2m1 * (-nor.z) * nor.x +
|
||||
2.0 * c2 * cl11 * nor.y +
|
||||
2.0 * c2 * cl1m1 * (-nor.z) +
|
||||
2.0 * c2 * cl10 * nor.x
|
||||
);
|
||||
}
|
30
leenkx/Shaders/std/skinning.glsl
Normal file
30
leenkx/Shaders/std/skinning.glsl
Normal file
@ -0,0 +1,30 @@
|
||||
// Geometric Skinning with Approximate Dual Quaternion Blending, Kavan
|
||||
// Based on https://github.com/tcoppex/aer-engine/blob/master/demos/aura/data/shaders/Skinning.glsl
|
||||
uniform vec4 skinBones[skinMaxBones * 3];
|
||||
|
||||
void getSkinningDualQuat(const ivec4 bone, vec4 weight, out vec4 A, inout vec4 B, out vec4 S) {
|
||||
// Retrieve the real and dual part of the dual-quaternions
|
||||
ivec4 bonei = bone * 3;
|
||||
mat4 matA = mat4(
|
||||
skinBones[bonei.x],
|
||||
skinBones[bonei.y],
|
||||
skinBones[bonei.z],
|
||||
skinBones[bonei.w]);
|
||||
mat4 matB = mat4(
|
||||
skinBones[bonei.x + 1],
|
||||
skinBones[bonei.y + 1],
|
||||
skinBones[bonei.z + 1],
|
||||
skinBones[bonei.w + 1]);
|
||||
//Apply scaling first
|
||||
S = skinBones[bonei.x + 2];
|
||||
// Handles antipodality by sticking joints in the same neighbourhood
|
||||
// weight.xyz *= sign(matA[3] * mat3x4(matA)).xyz;
|
||||
weight.xyz *= sign(matA[3] * matA).xyz;
|
||||
// Apply weights
|
||||
A = matA * weight; // Real part
|
||||
B = matB * weight; // Dual part
|
||||
// Normalize
|
||||
float invNormA = 1.0 / length(A);
|
||||
A *= invNormA;
|
||||
B *= invNormA;
|
||||
}
|
155
leenkx/Shaders/std/sky.glsl
Normal file
155
leenkx/Shaders/std/sky.glsl
Normal file
@ -0,0 +1,155 @@
|
||||
/* Various sky functions
|
||||
* =====================
|
||||
*
|
||||
* Nishita model is based on https://github.com/wwwtyro/glsl-atmosphere (Unlicense License)
|
||||
*
|
||||
* Changes to the original implementation:
|
||||
* - r and pSun parameters of nishita_atmosphere() are already normalized
|
||||
* - Some original parameters of nishita_atmosphere() are replaced with pre-defined values
|
||||
* - Implemented air, dust and ozone density node parameters (see Blender source)
|
||||
* - Replaced the inner integral calculation with a LUT lookup
|
||||
*
|
||||
* Reference for the sun's limb darkening and ozone calculations:
|
||||
* [Hill] Sebastien Hillaire. Physically Based Sky, Atmosphere and Cloud Rendering in Frostbite
|
||||
* (https://media.contentapi.ea.com/content/dam/eacom/frostbite/files/s2016-pbs-frostbite-sky-clouds-new.pdf)
|
||||
*
|
||||
* Cycles code used for reference: blender/intern/sky/source/sky_nishita.cpp
|
||||
* (https://github.com/blender/blender/blob/4429b4b77ef6754739a3c2b4fabd0537999e9bdc/intern/sky/source/sky_nishita.cpp)
|
||||
*/
|
||||
|
||||
#ifndef _SKY_GLSL_
|
||||
#define _SKY_GLSL_
|
||||
|
||||
#include "std/math.glsl"
|
||||
|
||||
uniform sampler2D nishitaLUT;
|
||||
uniform vec2 nishitaDensity;
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.141592
|
||||
#endif
|
||||
#ifndef HALF_PI
|
||||
#define HALF_PI 1.570796
|
||||
#endif
|
||||
|
||||
#define nishita_iSteps 16
|
||||
|
||||
// These values are taken from Cycles code if they
|
||||
// exist there, otherwise they are taken from the example
|
||||
// in the glsl-atmosphere repo
|
||||
#define nishita_sun_intensity 22.0
|
||||
#define nishita_atmo_radius 6420e3
|
||||
#define nishita_rayleigh_scale 8e3
|
||||
#define nishita_rayleigh_coeff vec3(5.5e-6, 13.0e-6, 22.4e-6)
|
||||
#define nishita_mie_scale 1.2e3
|
||||
#define nishita_mie_coeff 2e-5
|
||||
#define nishita_mie_dir 0.76 // Aerosols anisotropy ("direction")
|
||||
#define nishita_mie_dir_sq 0.5776 // Squared aerosols anisotropy
|
||||
|
||||
// Values from [Hill: 60]
|
||||
#define sun_limb_darkening_col vec3(0.397, 0.503, 0.652)
|
||||
|
||||
vec3 nishita_lookupLUT(const float height, const float sunTheta) {
|
||||
vec2 coords = vec2(
|
||||
sqrt(height * (1 / nishita_atmo_radius)),
|
||||
0.5 + 0.5 * sign(sunTheta - HALF_PI) * sqrt(abs(sunTheta * (1 / HALF_PI) - 1))
|
||||
);
|
||||
return textureLod(nishitaLUT, coords, 0.0).rgb;
|
||||
}
|
||||
|
||||
/* See raySphereIntersection() in leenkx/Sources/renderpath/Nishita.hx */
|
||||
vec2 nishita_rsi(const vec3 r0, const vec3 rd, const float sr) {
|
||||
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 the ray does not intersect the sphere
|
||||
return (d < 0.0) ? vec2(1e5,-1e5) : vec2((-b - sqrt(d))/(2.0*a), (-b + sqrt(d))/(2.0*a));
|
||||
}
|
||||
|
||||
/*
|
||||
* r: normalized ray direction
|
||||
* r0: ray origin
|
||||
* pSun: normalized sun direction
|
||||
* rPlanet: planet radius
|
||||
*/
|
||||
vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const float rPlanet) {
|
||||
// Calculate the step size of the primary ray
|
||||
vec2 p = nishita_rsi(r0, r, nishita_atmo_radius);
|
||||
if (p.x > p.y) return vec3(0.0);
|
||||
p.y = min(p.y, nishita_rsi(r0, r, rPlanet).x);
|
||||
float iStepSize = (p.y - p.x) / float(nishita_iSteps);
|
||||
|
||||
// Primary ray time
|
||||
float iTime = 0.0;
|
||||
|
||||
// Accumulators for Rayleigh and Mie scattering.
|
||||
vec3 totalRlh = vec3(0,0,0);
|
||||
vec3 totalMie = vec3(0,0,0);
|
||||
|
||||
// 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 pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
|
||||
float pMie = 3.0 / (8.0 * PI) * ((1.0 - nishita_mie_dir_sq) * (mumu + 1.0)) / (pow(1.0 + nishita_mie_dir_sq - 2.0 * mu * nishita_mie_dir, 1.5) * (2.0 + nishita_mie_dir_sq));
|
||||
|
||||
// Sample the primary ray
|
||||
for (int i = 0; i < nishita_iSteps; i++) {
|
||||
|
||||
// Calculate the primary ray sample position and height
|
||||
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
|
||||
float iHeight = length(iPos) - rPlanet;
|
||||
|
||||
// Calculate the optical depth of the Rayleigh and Mie scattering for this step
|
||||
float odStepRlh = exp(-iHeight / nishita_rayleigh_scale) * nishitaDensity.x * iStepSize;
|
||||
float odStepMie = exp(-iHeight / nishita_mie_scale) * nishitaDensity.y * iStepSize;
|
||||
|
||||
// Accumulate optical depth
|
||||
iOdRlh += odStepRlh;
|
||||
iOdMie += odStepMie;
|
||||
|
||||
// Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the
|
||||
// inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith).
|
||||
float sunTheta = safe_acos(dot(normalize(iPos), normalize(pSun)));
|
||||
vec3 jAttn = nishita_lookupLUT(iHeight, sunTheta);
|
||||
|
||||
// Calculate attenuation
|
||||
vec3 iAttn = exp(-(
|
||||
nishita_mie_coeff * iOdMie
|
||||
+ nishita_rayleigh_coeff * iOdRlh
|
||||
// + 0 for ozone
|
||||
));
|
||||
vec3 attn = iAttn * jAttn;
|
||||
|
||||
// Apply dithering to reduce visible banding
|
||||
attn *= 0.98 + rand(r.xy) * 0.04;
|
||||
|
||||
// Accumulate scattering
|
||||
totalRlh += odStepRlh * attn;
|
||||
totalMie += odStepMie * attn;
|
||||
|
||||
iTime += iStepSize;
|
||||
}
|
||||
|
||||
return nishita_sun_intensity * (pRlh * nishita_rayleigh_coeff * totalRlh + pMie * nishita_mie_coeff * totalMie);
|
||||
}
|
||||
|
||||
vec3 sun_disk(const vec3 n, const vec3 light_dir, const float disk_size, const float intensity) {
|
||||
// Normalized SDF
|
||||
float dist = distance(n, light_dir) / disk_size;
|
||||
|
||||
// Darken the edges of the sun
|
||||
// [Hill: 28, 60] (according to [Nec96])
|
||||
float invDist = 1.0 - dist;
|
||||
float mu = sqrt(invDist * invDist);
|
||||
vec3 limb_darkening = 1.0 - (1.0 - pow(vec3(mu), sun_limb_darkening_col));
|
||||
|
||||
return 1 + (1.0 - step(1.0, dist)) * nishita_sun_intensity * intensity * limb_darkening;
|
||||
}
|
||||
|
||||
#endif
|
36
leenkx/Shaders/std/ssrs.glsl
Normal file
36
leenkx/Shaders/std/ssrs.glsl
Normal file
@ -0,0 +1,36 @@
|
||||
#ifndef _SSRS_GLSL_
|
||||
#define _SSRS_GLSL_
|
||||
|
||||
#include "std/gbuffer.glsl"
|
||||
|
||||
uniform mat4 VP;
|
||||
|
||||
vec2 getProjectedCoord(vec3 hitCoord) {
|
||||
vec4 projectedCoord = VP * vec4(hitCoord, 1.0);
|
||||
projectedCoord.xy /= projectedCoord.w;
|
||||
projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
|
||||
#if defined(HLSL) || defined(METAL)
|
||||
projectedCoord.y = 1.0 - projectedCoord.y;
|
||||
#endif
|
||||
return projectedCoord.xy;
|
||||
}
|
||||
|
||||
float getDeltaDepth(vec3 hitCoord, sampler2D gbufferD, mat4 invVP, vec3 eye) {
|
||||
vec2 texCoord = getProjectedCoord(hitCoord);
|
||||
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
||||
vec3 wpos = getPos2(invVP, depth, texCoord);
|
||||
float d1 = length(eye - wpos);
|
||||
float d2 = length(eye - hitCoord);
|
||||
return d1 - d2;
|
||||
}
|
||||
|
||||
float traceShadowSS(vec3 dir, vec3 hitCoord, sampler2D gbufferD, mat4 invVP, vec3 eye) {
|
||||
dir *= ssrsRayStep;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
hitCoord += dir;
|
||||
if (getDeltaDepth(hitCoord, gbufferD, invVP, eye) > 0.0) return 1.0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
#endif
|
26
leenkx/Shaders/std/sss.glsl
Normal file
26
leenkx/Shaders/std/sss.glsl
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
// Separable SSS Transmittance Function, ref to sss_pass
|
||||
vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sampler2DShadow shadowMap) {
|
||||
const float translucency = 1.0;
|
||||
vec4 shrinkedPos = vec4(p - 0.005 * n, 1.0);
|
||||
vec4 shadowPos = LWVP * shrinkedPos;
|
||||
float scale = 8.25 * (1.0 - translucency) / (sssWidth / 10.0);
|
||||
float d1 = texture(shadowMap, vec3(shadowPos.xy / shadowPos.w, shadowPos.z)).r; // 'd1' has a range of 0..1
|
||||
float d2 = shadowPos.z; // 'd2' has a range of 0..'lightFarPlane'
|
||||
d1 *= lightFar; // So we scale 'd1' accordingly:
|
||||
float d = scale * abs(d1 - d2);
|
||||
|
||||
float dd = -d * d;
|
||||
vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) +
|
||||
vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) +
|
||||
vec3(0.118, 0.198, 0.0) * exp(dd / 0.187) +
|
||||
vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) +
|
||||
vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) +
|
||||
vec3(0.078, 0.0, 0.0) * exp(dd / 7.41);
|
||||
return profile * clamp(0.3 + dot(l, -n), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 SSSSTransmittanceCube(float translucency, vec4 shadowPos, vec3 n, vec3 l, float lightFar) {
|
||||
// TODO
|
||||
return vec3(0.0);
|
||||
}
|
38
leenkx/Shaders/std/tonemap.glsl
Normal file
38
leenkx/Shaders/std/tonemap.glsl
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
vec3 uncharted2Tonemap(const vec3 x) {
|
||||
const float A = 0.15;
|
||||
const float B = 0.50;
|
||||
const float C = 0.10;
|
||||
const float D = 0.20;
|
||||
const float E = 0.02;
|
||||
const float F = 0.30;
|
||||
return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F;
|
||||
}
|
||||
|
||||
vec3 tonemapUncharted2(const vec3 color) {
|
||||
const float W = 11.2;
|
||||
const float exposureBias = 2.0;
|
||||
vec3 curr = uncharted2Tonemap(exposureBias * color);
|
||||
vec3 whiteScale = 1.0 / uncharted2Tonemap(vec3(W));
|
||||
return curr * whiteScale;
|
||||
}
|
||||
|
||||
// Based on Filmic Tonemapping Operators http://filmicgames.com/archives/75
|
||||
vec3 tonemapFilmic(const vec3 color) {
|
||||
vec3 x = max(vec3(0.0), color - 0.004);
|
||||
return (x * (6.2 * x + 0.5)) / (x * (6.2 * x + 1.7) + 0.06);
|
||||
}
|
||||
|
||||
// https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
|
||||
vec3 acesFilm(const vec3 x) {
|
||||
const float a = 2.51;
|
||||
const float b = 0.03;
|
||||
const float c = 2.43;
|
||||
const float d = 0.59;
|
||||
const float e = 0.14;
|
||||
return clamp((x * (a * x + b)) / (x * (c * x + d ) + e), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 tonemapReinhard(const vec3 color) {
|
||||
return color / (color + vec3(1.0));
|
||||
}
|
56
leenkx/Shaders/std/voxels_constants.glsl
Normal file
56
leenkx/Shaders/std/voxels_constants.glsl
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
Copyright (c) 2024 Turánszki János
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of 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.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
const int DIFFUSE_CONE_COUNT = 16;
|
||||
const float DIFFUSE_CONE_APERTURE = radians(45.0);
|
||||
|
||||
const vec3 DIFFUSE_CONE_DIRECTIONS[16] = {
|
||||
vec3(0.0000, 0.0000, 1.0000), // Central direction
|
||||
vec3(0.3827, 0.0000, 0.9239), // Ring 1
|
||||
vec3(-0.3827, 0.0000, 0.9239),
|
||||
vec3(0.0000, 0.3827, 0.9239),
|
||||
vec3(0.0000, -0.3827, 0.9239),
|
||||
vec3(0.2706, 0.2706, 0.9239), // Ring 2
|
||||
vec3(-0.2706, 0.2706, 0.9239),
|
||||
vec3(0.2706, -0.2706, 0.9239),
|
||||
vec3(-0.2706, -0.2706, 0.9239),
|
||||
vec3(0.1802, 0.3604, 0.9239), // Ring 3
|
||||
vec3(-0.1802, 0.3604, 0.9239),
|
||||
vec3(0.1802, -0.3604, 0.9239),
|
||||
vec3(-0.1802, -0.3604, 0.9239),
|
||||
vec3(0.3604, 0.1802, 0.9239),
|
||||
vec3(-0.3604, 0.1802, 0.9239),
|
||||
vec3(0.3604, -0.1802, 0.9239)
|
||||
};
|
||||
|
||||
|
||||
const float BayerMatrix8[8][8] =
|
||||
{
|
||||
{ 1.0 / 65.0, 49.0 / 65.0, 13.0 / 65.0, 61.0 / 65.0, 4.0 / 65.0, 52.0 / 65.0, 16.0 / 65.0, 64.0 / 65.0 },
|
||||
{ 33.0 / 65.0, 17.0 / 65.0, 45.0 / 65.0, 29.0 / 65.0, 36.0 / 65.0, 20.0 / 65.0, 48.0 / 65.0, 32.0 / 65.0 },
|
||||
{ 9.0 / 65.0, 57.0 / 65.0, 5.0 / 65.0, 53.0 / 65.0, 12.0 / 65.0, 60.0 / 65.0, 8.0 / 65.0, 56.0 / 65.0 },
|
||||
{ 41.0 / 65.0, 25.0 / 65.0, 37.0 / 65.0, 21.0 / 65.0, 44.0 / 65.0, 28.0 / 65.0, 40.0 / 65.0, 24.0 / 65.0 },
|
||||
{ 3.0 / 65.0, 51.0 / 65.0, 15.0 / 65.0, 63.0 / 65.0, 2.0 / 65.0, 50.0 / 65.0, 14.0 / 65.0, 62.0 / 65.0 },
|
||||
{ 35.0 / 65.0, 19.0 / 65.0, 47.0 / 65.0, 31.0 / 65.0, 34.0 / 65.0, 18.0 / 65.0, 46.0 / 65.0, 30.0 / 65.0 },
|
||||
{ 11.0 / 65.0, 59.0 / 65.0, 7.0 / 65.0, 55.0 / 65.0, 10.0 / 65.0, 58.0 / 65.0, 6.0 / 65.0, 54.0 / 65.0 },
|
||||
{ 43.0 / 65.0, 27.0 / 65.0, 39.0 / 65.0, 23.0 / 65.0, 42.0 / 65.0, 26.0 / 65.0, 38.0 / 65.0, 22.0 / 65.0 }
|
||||
};
|
26
leenkx/Shaders/std/vr.glsl
Normal file
26
leenkx/Shaders/std/vr.glsl
Normal file
@ -0,0 +1,26 @@
|
||||
uniform mat4 U; // Undistortion
|
||||
uniform float maxRadSq;
|
||||
|
||||
// GoogleVR Distortion using Vertex Displacement
|
||||
float distortionFactor(const float rSquared) {
|
||||
float ret = 0.0;
|
||||
ret = rSquared * (ret + U[1][1]);
|
||||
ret = rSquared * (ret + U[0][1]);
|
||||
ret = rSquared * (ret + U[3][0]);
|
||||
ret = rSquared * (ret + U[2][0]);
|
||||
ret = rSquared * (ret + U[1][0]);
|
||||
ret = rSquared * (ret + U[0][0]);
|
||||
return ret + 1.0;
|
||||
}
|
||||
// Convert point from world space to undistorted camera space
|
||||
vec4 undistort(const mat4 WV, vec4 pos) {
|
||||
// Go to camera space
|
||||
pos = WV * pos;
|
||||
const float nearClip = 0.1;
|
||||
if (pos.z <= -nearClip) { // Reminder: Forward is -Z
|
||||
// Undistort the point's coordinates in XY
|
||||
float r2 = clamp(dot(pos.xy, pos.xy) / (pos.z * pos.z), 0.0, maxRadSq);
|
||||
pos.xy *= distortionFactor(r2);
|
||||
}
|
||||
return pos;
|
||||
}
|
Reference in New Issue
Block a user