#include "/Lib/Settings.glsl" // MinecraftPT — Utilities // Math helpers, noise functions, packing/unpacking, color space conversions #ifndef UTILITIES_GLSL #define UTILITIES_GLSL // --- Math helpers --- #define saturate(x) clamp(x, 0.0, 1.0) #define maxVec2(v) max(v.x, v.y) #define maxVec3(v) max(max(v.x, v.y), v.z) #define minVec3(v) min(min(v.x, v.y), v.z) #define fsign(x) ((x) >= 0.0 ? 1.0 : -1.0) #define sq(x) ((x) * (x)) #define cube(x) ((x) * (x) * (x)) // Gamma correction vec3 LinearToGamma(vec3 linear){ return pow(linear, vec3(1.0 / 2.2)); } vec3 GammaToLinear(vec3 gamma){ return pow(gamma, vec3(2.2)); } float LinearToGamma(float linear){ return pow(linear, 1.0 / 2.2); } float GammaToLinear(float gamma){ return pow(gamma, 2.2); } // Normal packing (octahedral encoding) vec2 EncodeNormal(vec3 n){ n /= abs(n.x) + abs(n.y) + abs(n.z); n.xy = n.z >= 0.0 ? n.xy : (1.0 - abs(n.yx)) * fsign(n.xy); return n.xy * 0.5 + 0.5; } vec3 DecodeNormal(vec2 e){ e = e * 2.0 - 1.0; vec3 n = vec3(e.x, e.y, 1.0 - abs(e.x) - abs(e.y)); float t = max(-n.z, 0.0); n.x += t * fsign(n.x); n.y += t * fsign(n.y); return normalize(n); } // Pack two 8-bit values into a 16-bit uint float Pack2xU8_to_U16(vec2 v){ v = floor(v * 255.0 + 0.5); return v.x * 256.0 + v.y; } vec2 Unpack2xU8_from_U16(float v){ return vec2(floor(v / 256.0), mod(v, 256.0)) / 255.0; } // Pack two 8-bit values with ID (8-bit + 8-bit) into float vec2 Unpack2xU8_ID_from_U16(float v){ return vec2(mod(v, 9362.0) / 9361.0, floor(v / 9362.0) / 10.0); } float Unpack2xU8_ID_Y_from_U16(float v){ return floor(v / 9362.0) / 10.0; } // Luminance float luminance(vec3 color){ return dot(color, vec3(0.2126, 0.7152, 0.0722)); } // Hash functions float hash1(vec2 p){ p = fract(p * vec2(443.8975, 397.2973)); p += dot(p, p + 19.19); return fract(p.x * p.y); } float hash1(vec3 p){ p = fract(p * vec3(443.8975, 397.2973, 491.1871)); p += dot(p, p + 19.19); return fract(p.x * p.y); } vec2 hash2(vec2 p){ vec3 p3 = fract(vec3(p.xyx) * vec3(443.8975, 397.2973, 491.1871)); p3 += dot(p3, p3.yxz + 19.19); return fract(p3.xy); } vec2 hash2(vec3 p){ vec3 p3 = fract(p * vec3(443.8975, 397.2973, 491.1871)); p3 += dot(p3, p3.yxz + 19.19); return fract(p3.xy); } vec3 hash3(vec2 p){ vec3 p3 = fract(vec3(p.xyx) * vec3(443.8975, 397.2973, 491.1871)); p3 += dot(p3, p3.yxz + 19.19); return fract(p3); } // Interleaved gradient noise (temporal) float InterleavedGradientNoise(vec2 pos, float index){ vec3 magic = vec3(0.06711056, 0.00583715, 52.9829189); return fract(magic.z * fract(dot(pos, magic.xy)) + index * 0.03125); } // Blue noise temporal (from noise.png) // Used as dithering / ray offset float BlueNoiseTemporal(vec2 screenPos, float frame){ vec2 coord = (screenPos + 0.5) / vec2(viewWidth, viewHeight); coord = fract(coord * vec2(128.0, 128.0) + 0.5); vec2 texel = coord * vec2(127.0 / 128.0) + vec2(0.5 / 128.0); float noise = textureLod(noisetex, texel, 0.0).r; // Temporal interleaving return fract(noise + frame * 0.03125); } // Sky lightmap curve (vanilla sky light attenuation) float SkyLightmapCurve(float skylight){ return skylight * skylight * (3.0 - 2.0 * skylight); } // Packing a ray for DDA traversal struct Ray{ vec3 ori; vec3 dir; vec3 rdir; vec3 sdir; }; Ray PackRay(vec3 origin, vec3 direction){ Ray ray; ray.ori = origin; ray.dir = direction; ray.rdir = 1.0 / direction; ray.sdir = fsign(direction); return ray; } // Smooth min/max float smoothMin(float a, float b, float k){ float h = max(k - abs(a - b), 0.0) / k; return min(a, b) - h * h * h * k * (1.0 / 6.0); } // Curve function for smooth transitions float curve(float x){ return x * x * (3.0 - 2.0 * x); } // Fresnel Schlick float FresnelSchlick(float cosTheta, float f0){ return f0 + (1.0 - f0) * pow(1.0 - cosTheta, 5.0); } vec3 FresnelSchlick(vec3 cosTheta, vec3 f0){ return f0 + (1.0 - f0) * pow(1.0 - cosTheta, vec3(5.0)); } // GGX normal distribution float GGX_D(float NdotH, float roughness){ float a = roughness * roughness; float a2 = a * a; float denom = NdotH * NdotH * (a2 - 1.0) + 1.0; return a2 / (3.14159 * denom * denom); } // Smith geometry (GGX) float Smith_G(float NdotV, float NdotL, float roughness){ float a = roughness * roughness; float k = a * 0.5; float g1 = NdotV / (NdotV * (1.0 - k) + k); float g2 = NdotL / (NdotL * (1.0 - k) + k); return g1 * g2; } // Importance sample GGX vec3 ImportanceSampleGGX(vec2 uv, vec3 N, float roughness){ float a = roughness * roughness; float phi = uv.x * 6.283185; float cosTheta = sqrt((1.0 - uv.y) / (1.0 + (a * a - 1.0) * uv.y)); float sinTheta = sqrt(1.0 - cosTheta * cosTheta); vec3 H = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta); // Tangent space to world vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); vec3 T = normalize(cross(up, N)); vec3 B = cross(N, T); return normalize(T * H.x + B * H.y + N * H.z); } // Cosine-weighted hemisphere sampling (for diffuse) vec3 SampleHemisphere(vec2 uv, vec3 N){ float phi = uv.x * 6.283185; float cosTheta = sqrt(uv.y); float sinTheta = sqrt(1.0 - uv.y); vec3 dir = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta); vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); vec3 T = normalize(cross(up, N)); vec3 B = cross(N, T); return normalize(T * dir.x + B * dir.y + N * dir.z); } #endif