Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View 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

View 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);
}

View 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);
}

View 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_

View 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

View 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;
}

View 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;
}

View 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

View 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;
}

View 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);
// }
// }

View 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

View 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_

View 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
View 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
}

View 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);
}

View 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

View 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);
}

View 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);
// }

View 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

View 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

View 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
);
}

View 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
View 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

View 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

View 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);
}

View 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));
}

View 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 }
};

View 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;
}