Initial commit: Minecraft 光追着色器包(从零实现的 path tracing)
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
// MinecraftPT — Diffuse Spatial Filter
|
||||
// Edge-avoiding A-trous wavelet spatial filter. Multi-pass (detail levels).
|
||||
|
||||
#ifndef DIFFUSE_SPATIAL_FILTER_GLSL
|
||||
#define DIFFUSE_SPATIAL_FILTER_GLSL
|
||||
|
||||
#include "/Lib/Utilities.glsl"
|
||||
|
||||
// Single A-trous pass at the given step size
|
||||
vec3 DiffuseSpatialPass(vec3 center, vec2 texelCoord, int step){
|
||||
vec3 result = center * 0.25;
|
||||
|
||||
// Center depth and normal for edge detection
|
||||
float depth = texelFetch(depthtex0, texelCoord * 2.0, 0).r;
|
||||
vec3 normal = DecodeNormal(texelFetch(colortex1, texelCoord * 2.0, 0).xy);
|
||||
vec3 centerAlbedo = texelFetch(colortex0, texelCoord * 2.0, 0).rgb;
|
||||
|
||||
// Reconstruct view position for depth edge weight
|
||||
vec4 viewPos = gbufferProjectionInverse * vec4(texelCoord * 2.0 / screenSize * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
|
||||
viewPos.xyz /= viewPos.w;
|
||||
|
||||
float weightSum = 0.25;
|
||||
|
||||
vec2 offsets[4] = vec2[4](vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0));
|
||||
|
||||
for (int i = 0; i < 4; i++){
|
||||
vec2 coord = texelCoord + offsets[i] * float(step);
|
||||
vec3 sampleColor = texelFetch(colortex7, ivec2(coord), 0).rgb;
|
||||
|
||||
float sampleDepth = texelFetch(depthtex0, coord * 2.0, 0).r;
|
||||
vec3 sampleNormal = DecodeNormal(texelFetch(colortex1, coord * 2.0, 0).xy);
|
||||
|
||||
// Edge-avoiding weights
|
||||
float depthWeight = exp(-abs(depth - sampleDepth) * PT_DIFFUSE_SPATIAL_FILTER_DEPTH_WEIGHT * 100.0);
|
||||
float normalWeight = pow(max(dot(normal, sampleNormal), 0.0), 8.0 * PT_DIFFUSE_SPATIAL_FILTER_NORMAL_WEIGHT + 1.0);
|
||||
float luminanceWeight = exp(-abs(luminance(center) - luminance(sampleColor)) * PT_DIFFUSE_SPATIAL_FILTER_LUMINANCE_WEIGHT * 10.0);
|
||||
|
||||
float weight = depthWeight * normalWeight * luminanceWeight;
|
||||
|
||||
result += sampleColor * weight;
|
||||
weightSum += weight;
|
||||
}
|
||||
|
||||
return result / weightSum;
|
||||
}
|
||||
|
||||
// Run the spatial filter chain (detail levels)
|
||||
vec3 DiffuseSpatialFilter(vec2 texelCoord){
|
||||
vec3 color = texelFetch(colortex7, ivec2(texelCoord), 0).rgb;
|
||||
|
||||
int levels = 1 + int(PT_DIFFUSE_SPATIAL_FILTER_DETAIL);
|
||||
|
||||
int step = 1;
|
||||
for (int i = 0; i < levels; i++){
|
||||
color = DiffuseSpatialPass(color, texelCoord, step);
|
||||
step *= 2;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
// MinecraftPT — Diffuse Temporal Filter
|
||||
// Reprojects the previous frame's denoised diffuse result using motion vectors
|
||||
// and accumulates with the current noisy frame.
|
||||
|
||||
#ifndef DIFFUSE_TEMPORAL_FILTER_GLSL
|
||||
#define DIFFUSE_TEMPORAL_FILTER_GLSL
|
||||
|
||||
#include "/Lib/Utilities.glsl"
|
||||
|
||||
vec3 DiffuseTemporalAccumulate(vec3 current, vec2 texelCoord, vec3 prevColor){
|
||||
vec2 motionVec = texelFetch(colortex10, ivec2(texelCoord), 0).xy;
|
||||
vec2 prevCoord = (texelCoord + 0.5) - motionVec;
|
||||
|
||||
// Validate reprojection: depth comparison
|
||||
float depth = texelFetch(depthtex0, texelCoord * 2.0, 0).r;
|
||||
float prevDepth = textureLod(prevDepth2D, prevCoord / (screenSize * 0.5), 0.0).r;
|
||||
|
||||
float depthValid = step(abs(depth - prevDepth), 0.05 * depth + 0.001);
|
||||
|
||||
// History confidence based on accumulation count
|
||||
float accum = 1.0 / float(PT_DIFFUSE_TEMPORAL_MAX_ACCUM);
|
||||
|
||||
// Sample previous
|
||||
vec3 history = textureLod(colortex7, prevCoord / (screenSize * 0.5), 0.0).rgb;
|
||||
|
||||
// Neighborhood clamp to reduce ghosting
|
||||
vec3 minColor = history;
|
||||
vec3 maxColor = history;
|
||||
for (int i = -1; i <= 1; i++){
|
||||
for (int j = -1; j <= 1; j++){
|
||||
vec3 c = textureLod(colortex7, (prevCoord + vec2(i, j)) / (screenSize * 0.5), 0.0).rgb;
|
||||
minColor = min(minColor, c);
|
||||
maxColor = max(maxColor, c);
|
||||
}}
|
||||
history = clamp(history, minColor, maxColor);
|
||||
|
||||
float blend = mix(accum, 1.0, depthValid * PT_DIFFUSE_TEMPORAL_HISTORY_FIX * 0.5);
|
||||
|
||||
return mix(current, history, blend * (1.0 - accum));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
// MinecraftPT — Diffuse Variance Estimation
|
||||
// Computes luminance moments to drive temporal accumulation and firefly rejection.
|
||||
|
||||
#ifndef DIFFUSE_VARIANCE_ESTIMATION_GLSL
|
||||
#define DIFFUSE_VARIANCE_ESTIMATION_GLSL
|
||||
|
||||
#include "/Lib/Utilities.glsl"
|
||||
|
||||
vec2 DiffuseEstimateVariance(vec3 color, vec2 texelCoord){
|
||||
float lum = luminance(color);
|
||||
float lum2 = lum * lum;
|
||||
|
||||
// Local variance from neighbors (5-tap)
|
||||
float variance = 0.0;
|
||||
for (int i = -1; i <= 1; i++){
|
||||
for (int j = -1; j <= 1; j++){
|
||||
if (i == 0 && j == 0) continue;
|
||||
vec3 c = texelFetch(colortex7, ivec2(texelCoord) + ivec2(i, j), 0).rgb;
|
||||
float l = luminance(c);
|
||||
variance += (l - lum) * (l - lum);
|
||||
}}
|
||||
variance /= 8.0;
|
||||
|
||||
// Clamp fireflies
|
||||
float firefly = step(1.0, lum / max(variance * 4.0, 0.001));
|
||||
|
||||
return vec2(lum, firefly);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,55 @@
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
// MinecraftPT — Specular Spatial Filter
|
||||
// Edge-avoiding spatial filter for specular reflections (fewer passes than diffuse).
|
||||
|
||||
#ifndef SPECULAR_SPATIAL_FILTER_GLSL
|
||||
#define SPECULAR_SPATIAL_FILTER_GLSL
|
||||
|
||||
#include "/Lib/Utilities.glsl"
|
||||
|
||||
vec3 SpecularSpatialPass(vec3 center, vec2 texelCoord, int step){
|
||||
vec3 result = center * 0.25;
|
||||
|
||||
float depth = texelFetch(depthtex0, texelCoord * 2.0, 0).r;
|
||||
vec3 normal = DecodeNormal(texelFetch(colortex1, texelCoord * 2.0, 0).xy);
|
||||
|
||||
float weightSum = 0.25;
|
||||
|
||||
vec2 offsets[4] = vec2[4](vec2(1.0, 0.0), vec2(-1.0, 0.0), vec2(0.0, 1.0), vec2(0.0, -1.0));
|
||||
|
||||
for (int i = 0; i < 4; i++){
|
||||
vec2 coord = texelCoord + offsets[i] * float(step);
|
||||
vec3 sampleColor = texelFetch(colortex11, ivec2(coord), 0).rgb;
|
||||
|
||||
float sampleDepth = texelFetch(depthtex0, coord * 2.0, 0).r;
|
||||
vec3 sampleNormal = DecodeNormal(texelFetch(colortex1, coord * 2.0, 0).xy);
|
||||
|
||||
float depthWeight = exp(-abs(depth - sampleDepth) * PT_DIFFUSE_SPATIAL_FILTER_DEPTH_WEIGHT * 100.0);
|
||||
float normalWeight = pow(max(dot(normal, sampleNormal), 0.0), 8.0 * PT_DIFFUSE_SPATIAL_FILTER_NORMAL_WEIGHT + 1.0);
|
||||
float luminanceWeight = exp(-abs(luminance(center) - luminance(sampleColor)) * PT_DIFFUSE_SPATIAL_FILTER_LUMINANCE_WEIGHT * 10.0);
|
||||
|
||||
float weight = depthWeight * normalWeight * luminanceWeight;
|
||||
|
||||
result += sampleColor * weight;
|
||||
weightSum += weight;
|
||||
}
|
||||
|
||||
return result / weightSum;
|
||||
}
|
||||
|
||||
vec3 SpecularSpatialFilter(vec2 texelCoord){
|
||||
vec3 color = texelFetch(colortex11, ivec2(texelCoord), 0).rgb;
|
||||
|
||||
int levels = 1 + int(PT_DIFFUSE_SPATIAL_FILTER_DETAIL) / 2;
|
||||
|
||||
int step = 1;
|
||||
for (int i = 0; i < levels; i++){
|
||||
color = SpecularSpatialPass(color, texelCoord, step);
|
||||
step *= 2;
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
// MinecraftPT — Specular Temporal Filter
|
||||
// Temporal accumulation for specular reflections with reprojection.
|
||||
|
||||
#ifndef SPECULAR_TEMPORAL_FILTER_GLSL
|
||||
#define SPECULAR_TEMPORAL_FILTER_GLSL
|
||||
|
||||
#include "/Lib/Utilities.glsl"
|
||||
|
||||
vec3 SpecularTemporalAccumulate(vec3 current, vec2 texelCoord){
|
||||
vec2 motionVec = texelFetch(colortex10, ivec2(texelCoord), 0).xy;
|
||||
vec2 prevCoord = (texelCoord + 0.5) - motionVec;
|
||||
|
||||
float depth = texelFetch(depthtex0, texelCoord * 2.0, 0).r;
|
||||
float prevDepth = textureLod(prevDepth2D, prevCoord / (screenSize * 0.5), 0.0).r;
|
||||
|
||||
float depthValid = step(abs(depth - prevDepth), 0.05 * depth + 0.001);
|
||||
|
||||
float accum = 1.0 / float(PT_DIFFUSE_TEMPORAL_MAX_ACCUM);
|
||||
|
||||
vec3 history = textureLod(colortex11, prevCoord / (screenSize * 0.5), 0.0).rgb;
|
||||
|
||||
// Neighborhood clamp
|
||||
vec3 minColor = history;
|
||||
vec3 maxColor = history;
|
||||
for (int i = -1; i <= 1; i++){
|
||||
for (int j = -1; j <= 1; j++){
|
||||
vec3 c = textureLod(colortex11, (prevCoord + vec2(i, j)) / (screenSize * 0.5), 0.0).rgb;
|
||||
minColor = min(minColor, c);
|
||||
maxColor = max(maxColor, c);
|
||||
}}
|
||||
history = clamp(history, minColor, maxColor);
|
||||
|
||||
float blend = mix(accum, 1.0, depthValid * PT_DIFFUSE_TEMPORAL_HISTORY_FIX * 0.5);
|
||||
|
||||
return mix(current, history, blend * (1.0 - accum));
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "/Lib/BasicFunctions/LightingConstants.glsl"
|
||||
// MinecraftPT — SampleIRC
|
||||
// Sampling the irradiance cache: a 3D grid of pre-computed diffuse irradiance
|
||||
// used to cheaply evaluate indirect diffuse light.
|
||||
|
||||
#ifndef SAMPLE_IRC_GLSL
|
||||
#define SAMPLE_IRC_GLSL
|
||||
|
||||
// Sample irradiance cache at a world position (trilinear)
|
||||
vec3 SampleIRC(vec3 worldPos){
|
||||
vec3 voxelPos = WorldToVoxel(worldPos);
|
||||
|
||||
vec3 coord = voxelPos / float(ircResolution);
|
||||
|
||||
if (clamp(coord, vec3(0.0), vec3(1.0)) == coord){
|
||||
coord *= float(ircResolution) - 1.0;
|
||||
|
||||
vec3 base = floor(coord);
|
||||
vec3 frac = coord - base;
|
||||
ivec3 i0 = ivec3(base);
|
||||
ivec3 i1 = min(i0 + 1, ivec3(ircResolution - 1));
|
||||
|
||||
vec3 c000 = texelFetch(irradianceCache3D, i0, 0).rgb;
|
||||
vec3 c100 = texelFetch(irradianceCache3D, ivec3(i1.x, i0.y, i0.z), 0).rgb;
|
||||
vec3 c010 = texelFetch(irradianceCache3D, ivec3(i0.x, i1.y, i0.z), 0).rgb;
|
||||
vec3 c110 = texelFetch(irradianceCache3D, ivec3(i1.x, i1.y, i0.z), 0).rgb;
|
||||
vec3 c001 = texelFetch(irradianceCache3D, ivec3(i0.x, i0.y, i1.z), 0).rgb;
|
||||
vec3 c101 = texelFetch(irradianceCache3D, ivec3(i1.x, i0.y, i1.z), 0).rgb;
|
||||
vec3 c011 = texelFetch(irradianceCache3D, ivec3(i0.x, i1.y, i1.z), 0).rgb;
|
||||
vec3 c111 = texelFetch(irradianceCache3D, i1, 0).rgb;
|
||||
|
||||
vec3 x00 = mix(c000, c100, frac.x);
|
||||
vec3 x10 = mix(c010, c110, frac.x);
|
||||
vec3 x01 = mix(c001, c101, frac.x);
|
||||
vec3 x11 = mix(c011, c111, frac.x);
|
||||
vec3 y0 = mix(x00, x10, frac.y);
|
||||
vec3 y1 = mix(x01, x11, frac.y);
|
||||
|
||||
return mix(y0, y1, frac.z);
|
||||
}
|
||||
|
||||
// Outside the cache — fall back to sky ambient
|
||||
return GetCelestialIrradiance() + GetAtmoIrradiance();
|
||||
}
|
||||
|
||||
// Normal-aware IRC sampling (bias by surface normal facing)
|
||||
vec3 SampleIRCNormal(vec3 worldPos, vec3 normal){
|
||||
vec3 irc = SampleIRC(worldPos);
|
||||
// Simple hemisphere shading via normal alignment with up
|
||||
float upFactor = normal.y * 0.5 + 0.5;
|
||||
return irc * (0.5 + 0.5 * upFactor);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,130 @@
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
// MinecraftPT — Shadow Tracing
|
||||
// Direct sunlight shadow rays: march from surface toward the sun through the
|
||||
// voxel grid. Soft penumbra via distance-weighted ray length.
|
||||
|
||||
#ifndef SHADOW_TRACING_GLSL
|
||||
#define SHADOW_TRACING_GLSL
|
||||
|
||||
#include "/Lib/PathTracing/Tracer/TracingUtilities.glsl"
|
||||
|
||||
float ShadowTracing(vec3 viewPos, vec3 worldPos, vec3 vertexNormal, vec3 lightVector, float lightMap){
|
||||
float shadow = 1.0;
|
||||
|
||||
vec3 voxelPos = WorldToVoxel(worldPos);
|
||||
voxelPos += vertexNormal * (-viewPos.z * 0.0003);
|
||||
|
||||
if (clamp(voxelPos, vec3(0.0), vec3(voxelResolution)) == voxelPos){
|
||||
lightMap = saturate(1.0 - lightMap * 2.0);
|
||||
vec2 shadowWeight = vec2(
|
||||
4.0 - 2.0 * saturate(lightMap - viewPos.z * 0.01),
|
||||
0.2 + 0.5 * saturate(lightMap - viewPos.z * 0.01)
|
||||
);
|
||||
|
||||
Ray ray = PackRay(voxelPos, lightVector);
|
||||
|
||||
vec3 voxelCoord = floor(ray.ori);
|
||||
vec3 totalStep = (ray.sdir * (voxelCoord - ray.ori + 0.5) + 0.5) * abs(ray.rdir);
|
||||
float rayLength = 0.0;
|
||||
vec3 tracingNext;
|
||||
|
||||
bool hit = false;
|
||||
|
||||
for (int i = 0; i < 64; i++){
|
||||
if (clamp(voxelCoord, vec3(0.0), vec3(voxelResolution - 0.5)) != voxelCoord) break;
|
||||
|
||||
vec4 voxelData = texelFetch(voxelData3D, ivec3(voxelCoord), 0);
|
||||
float voxelID = DecodeVoxelID(voxelData.z);
|
||||
|
||||
// Full block or cutout shape
|
||||
if (voxelID >= 999.0){
|
||||
hit = rayLength > 0.0;
|
||||
}else if (voxelID < 1000.0 && voxelID > 1.0){
|
||||
float rawID = 1000.0 - voxelID;
|
||||
rayLength = minVec3(totalStep);
|
||||
hit = HitShape_Lite(ray, voxelCoord, rawID, rayLength);
|
||||
}
|
||||
|
||||
if (hit){
|
||||
shadow = saturate((rayLength - shadowWeight.y) * shadowWeight.x);
|
||||
break;
|
||||
}
|
||||
|
||||
// Sparse skip
|
||||
float marker = voxelData.z;
|
||||
if (marker > 0.60 && marker < 0.92){
|
||||
float skipSize = marker > 0.90 ? 8.0 : (marker > 0.70 ? 4.0 : 2.0);
|
||||
vec3 nextBoundary = floor((voxelCoord + 1.0) / skipSize) * skipSize;
|
||||
vec3 distToBoundary = (nextBoundary - voxelCoord) * abs(ray.rdir);
|
||||
float tSkip = minVec3(distToBoundary) + 1e-4;
|
||||
rayLength += tSkip;
|
||||
vec3 stepVec = ray.sdir * abs(ray.rdir) * tSkip;
|
||||
ray.ori += stepVec;
|
||||
voxelCoord = floor(ray.ori);
|
||||
totalStep = (ray.sdir * (voxelCoord - ray.ori + 0.5) + 0.5) * abs(ray.rdir);
|
||||
continue;
|
||||
}
|
||||
|
||||
rayLength = minVec3(totalStep);
|
||||
tracingNext = step(totalStep, vec3(rayLength));
|
||||
voxelCoord += tracingNext * ray.sdir;
|
||||
totalStep += tracingNext * abs(ray.rdir);
|
||||
}
|
||||
}
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
// Simplified version for IRC / light sampling (no soft penumbra)
|
||||
float SimpleShadowTracing(vec3 voxelPos, vec3 lightVector){
|
||||
Ray ray = PackRay(voxelPos, lightVector);
|
||||
|
||||
vec3 voxelCoord = floor(ray.ori);
|
||||
vec3 totalStep = (ray.sdir * (voxelCoord - ray.ori + 0.5) + 0.5) * abs(ray.rdir);
|
||||
float rayLength = 0.0;
|
||||
vec3 tracingNext;
|
||||
|
||||
bool hit = false;
|
||||
|
||||
for (int i = 0; i < 64; i++){
|
||||
if (clamp(voxelCoord, vec3(0.0), vec3(voxelResolution - 0.5)) != voxelCoord) break;
|
||||
|
||||
vec4 voxelData = texelFetch(voxelData3D, ivec3(voxelCoord), 0);
|
||||
float voxelID = DecodeVoxelID(voxelData.z);
|
||||
|
||||
if (voxelID >= 999.0){
|
||||
hit = rayLength > 0.0;
|
||||
}else if (voxelID < 1000.0 && voxelID > 1.0){
|
||||
float rawID = 1000.0 - voxelID;
|
||||
rayLength = minVec3(totalStep);
|
||||
hit = HitShape_Lite(ray, voxelCoord, rawID, rayLength);
|
||||
}
|
||||
|
||||
if (hit) break;
|
||||
|
||||
// Sparse skip
|
||||
float marker = voxelData.z;
|
||||
if (marker > 0.60 && marker < 0.92){
|
||||
float skipSize = marker > 0.90 ? 8.0 : (marker > 0.70 ? 4.0 : 2.0);
|
||||
vec3 nextBoundary = floor((voxelCoord + 1.0) / skipSize) * skipSize;
|
||||
vec3 distToBoundary = (nextBoundary - voxelCoord) * abs(ray.rdir);
|
||||
float tSkip = minVec3(distToBoundary) + 1e-4;
|
||||
rayLength += tSkip;
|
||||
vec3 stepVec = ray.sdir * abs(ray.rdir) * tSkip;
|
||||
ray.ori += stepVec;
|
||||
voxelCoord = floor(ray.ori);
|
||||
totalStep = (ray.sdir * (voxelCoord - ray.ori + 0.5) + 0.5) * abs(ray.rdir);
|
||||
continue;
|
||||
}
|
||||
|
||||
rayLength = minVec3(totalStep);
|
||||
tracingNext = step(totalStep, vec3(rayLength));
|
||||
voxelCoord += tracingNext * ray.sdir;
|
||||
totalStep += tracingNext * abs(ray.rdir);
|
||||
}
|
||||
|
||||
return float(!hit);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,149 @@
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
// MinecraftPT — Specular Tracer
|
||||
// Traces GGX-sampled reflection/refraction rays through the voxel grid and
|
||||
// accumulates the reflected radiance (incl. light sphere contributions).
|
||||
|
||||
#ifndef SPECULAR_TRACER_GLSL
|
||||
#define SPECULAR_TRACER_GLSL
|
||||
|
||||
#include "/Lib/PathTracing/Tracer/TracingUtilities.glsl"
|
||||
#include "/Lib/BasicFunctions/LightingConstants.glsl"
|
||||
#include "/Lib/PathTracing/Tracer/ShadowTracing.glsl"
|
||||
#include "/Lib/PathTracing/Tracer/SampleIRC.glsl"
|
||||
|
||||
// Panorama: 3:2 cubemap cross. Map direction to face UV.
|
||||
float absX = abs(dir.x), absY = abs(dir.y), absZ = abs(dir.z);
|
||||
float maxAxis = max(absX, max(absY, absZ));
|
||||
|
||||
vec2 uv;
|
||||
vec3 tdir = dir / maxAxis;
|
||||
|
||||
if (maxAxis == absX){
|
||||
uv = vec2(tdir.y * 0.5 + 0.5, tdir.z * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(0.0, 0.5) * vec2(1.0/3.0, 1.0/2.0);
|
||||
if (tdir.x > 0.0){
|
||||
uv = vec2(tdir.y * 0.5 + 0.5, tdir.z * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(1.0/3.0, 0.5/2.0);
|
||||
}
|
||||
}else if (maxAxis == absY){
|
||||
uv = vec2(tdir.x * 0.5 + 0.5, tdir.z * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(2.0/3.0, 0.5/2.0);
|
||||
if (tdir.y > 0.0){
|
||||
uv = vec2(tdir.x * 0.5 + 0.5, tdir.z * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(1.0/3.0, 0.5/2.0);
|
||||
}
|
||||
}else{
|
||||
uv = vec2(tdir.x * 0.5 + 0.5, tdir.y * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(2.0/3.0, 1.5/2.0);
|
||||
if (tdir.z > 0.0){
|
||||
uv = vec2(tdir.x * 0.5 + 0.5, tdir.y * 0.5 + 0.5) / vec2(3.0, 2.0) + vec2(1.0/3.0, 1.5/2.0);
|
||||
}
|
||||
}
|
||||
|
||||
return textureLod(skyBox2D, uv, 0.0).rgb;
|
||||
}
|
||||
|
||||
// Trace a single specular ray and accumulate radiance.
|
||||
// origin: world pos (voxel space handled inside), dir: normalized direction
|
||||
vec3 SpecularTrace(vec3 voxelPos, vec3 dir, float maxDist, vec2 noise){
|
||||
vec3 result = vec3(0.0);
|
||||
|
||||
Ray ray = PackRay(voxelPos, dir);
|
||||
|
||||
vec3 voxelCoord = floor(ray.ori);
|
||||
vec3 totalStep = (ray.sdir * (voxelCoord - ray.ori + 0.5) + 0.5) * abs(ray.rdir);
|
||||
float rayLength = 0.0;
|
||||
vec3 tracingNext;
|
||||
|
||||
bool hit = false;
|
||||
|
||||
for (int i = 0; i < 128; i++){
|
||||
if (clamp(voxelCoord, vec3(0.0), vec3(voxelResolution - 0.5)) != voxelCoord){
|
||||
// Escaped the voxel grid — sample sky
|
||||
result = SampleSkyBox(dir);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rayLength > maxDist){
|
||||
result = SampleSkyBox(dir);
|
||||
break;
|
||||
}
|
||||
|
||||
vec4 voxelData = texelFetch(voxelData3D, ivec3(voxelCoord), 0);
|
||||
float voxelID = DecodeVoxelID(voxelData.z);
|
||||
|
||||
// Light source: accumulate sphere light
|
||||
if (IsLightSphere(voxelID)){
|
||||
result += HitLightShpereReflection(ray, voxelCoord, voxelID, rayLength);
|
||||
rayLength = minVec3(totalStep);
|
||||
tracingNext = step(totalStep, vec3(rayLength));
|
||||
voxelCoord += tracingNext * ray.sdir;
|
||||
totalStep += tracingNext * abs(ray.rdir);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (voxelID >= 999.0){ // Full block
|
||||
// Shade the hit point: sample albedo from atlas, apply lighting
|
||||
vec3 hitPos = ray.ori + ray.dir * rayLength;
|
||||
vec3 hitNormal = -step(vec3(rayLength), totalStep - abs(ray.rdir)) * ray.sdir;
|
||||
vec2 midCoord = voxelData.xy;
|
||||
vec3 albedo = SampleVoxelAlbedo(midCoord, hitPos, hitNormal);
|
||||
|
||||
// Direct light from sun
|
||||
vec3 sunColor = GetSunIrradiance();
|
||||
float sunShadow = SimpleShadowTracing(hitPos + hitNormal * 0.01, GetSunDirWorld());
|
||||
vec3 direct = albedo * sunColor * sunShadow * max(dot(hitNormal, GetSunDirWorld()), 0.0);
|
||||
|
||||
// Sky ambient from IRC
|
||||
vec3 irc = SampleIRC(hitPos);
|
||||
result += direct + irc * albedo * 0.5;
|
||||
|
||||
hit = true;
|
||||
break;
|
||||
}else if (voxelID < 1000.0 && voxelID > 1.0){
|
||||
// Cutout shape
|
||||
float rawID = 1000.0 - voxelID;
|
||||
rayLength = minVec3(totalStep);
|
||||
vec3 n;
|
||||
if (HitShape(ray, voxelCoord, rawID, rayLength, n)){
|
||||
vec3 hitPos = ray.ori + ray.dir * rayLength;
|
||||
vec2 midCoord = voxelData.xy;
|
||||
vec3 albedo = SampleVoxelAlbedo(midCoord, hitPos, n);
|
||||
|
||||
vec3 sunColor = GetSunIrradiance();
|
||||
float sunShadow = SimpleShadowTracing(hitPos + n * 0.01, GetSunDirWorld());
|
||||
vec3 direct = albedo * sunColor * sunShadow * max(dot(n, GetSunDirWorld()), 0.0);
|
||||
|
||||
vec3 irc = SampleIRC(hitPos);
|
||||
result += direct + irc * albedo * 0.5;
|
||||
|
||||
hit = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sparse skip
|
||||
float marker = voxelData.z;
|
||||
if (marker > 0.60 && marker < 0.92){
|
||||
float skipSize = marker > 0.90 ? 8.0 : (marker > 0.70 ? 4.0 : 2.0);
|
||||
vec3 nextBoundary = floor((voxelCoord + 1.0) / skipSize) * skipSize;
|
||||
vec3 distToBoundary = (nextBoundary - voxelCoord) * abs(ray.rdir);
|
||||
float tSkip = minVec3(distToBoundary) + 1e-4;
|
||||
rayLength += tSkip;
|
||||
vec3 stepVec = ray.sdir * abs(ray.rdir) * tSkip;
|
||||
ray.ori += stepVec;
|
||||
voxelCoord = floor(ray.ori);
|
||||
totalStep = (ray.sdir * (voxelCoord - ray.ori + 0.5) + 0.5) * abs(ray.rdir);
|
||||
continue;
|
||||
}
|
||||
|
||||
rayLength = minVec3(totalStep);
|
||||
tracingNext = step(totalStep, vec3(rayLength));
|
||||
voxelCoord += tracingNext * ray.sdir;
|
||||
totalStep += tracingNext * abs(ray.rdir);
|
||||
}
|
||||
|
||||
if (!hit){
|
||||
result = SampleSkyBox(dir);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,33 @@
|
||||
// MinecraftPT — Tracing Noise
|
||||
// Noise functions for ray tracing (hash-based, temporally stable).
|
||||
|
||||
#ifndef TRACING_NOISE_GLSL
|
||||
#define TRACING_NOISE_GLSL
|
||||
|
||||
// Per-pixel temporal noise, stable across frames
|
||||
float GetTracingNoise(vec2 screenPos, int frame, int offset){
|
||||
// Blue noise from texture + temporal interleave
|
||||
vec2 coord = (screenPos + 0.5) / screenSize;
|
||||
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;
|
||||
return fract(noise + (frame + offset) * 0.03125);
|
||||
}
|
||||
|
||||
vec2 GetTracingNoise2(vec2 screenPos, int frame, int offset){
|
||||
// 2D noise for ray directions
|
||||
vec2 coord = (screenPos + 0.5) / screenSize;
|
||||
coord = fract(coord * vec2(128.0, 128.0) + 0.5);
|
||||
vec2 texel = coord * vec2(127.0 / 128.0) + vec2(0.5 / 128.0);
|
||||
vec2 noise = textureLod(noisetex, texel, 0.0).rg;
|
||||
return fract(noise + (frame + offset) * 0.03125);
|
||||
}
|
||||
|
||||
// Golden-ratio based sample rotation (for progressive sampling)
|
||||
vec2 RotateNoise(vec2 uv, int sampleIndex, int totalSamples){
|
||||
float angle = 6.28318 * float(sampleIndex) / float(totalSamples);
|
||||
mat2 rot = mat2(cos(angle), sin(angle), -sin(angle), cos(angle));
|
||||
return rot * uv;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,187 @@
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
// MinecraftPT — Tracing Utilities
|
||||
// Core voxel ray marching helpers used by all tracers.
|
||||
|
||||
// Custom resource declarations (bound via shaders.properties)
|
||||
uniform sampler2D atlas2D;
|
||||
uniform sampler3D voxelData3D;
|
||||
uniform sampler2D skyBox2D;
|
||||
|
||||
// Sample the sky box panorama for a direction (for reflections / sky miss).
|
||||
// Layout must match SkyImage_CS: 3x2 tiles:
|
||||
// row 0: +X -X +Y row 1: -Y +Z -Z
|
||||
vec3 SampleSkyBox(vec3 dir){
|
||||
dir = normalize(dir);
|
||||
|
||||
float absX = abs(dir.x), absY = abs(dir.y), absZ = abs(dir.z);
|
||||
float maxAxis = max(absX, max(absY, absZ));
|
||||
|
||||
vec3 tdir = dir / maxAxis;
|
||||
|
||||
vec2 uv;
|
||||
vec2 tileOrigin;
|
||||
vec2 tile = vec2(1.0 / 3.0, 1.0 / 2.0);
|
||||
|
||||
if (maxAxis == absX){
|
||||
// +X (col 0) or -X (col 1), row 0
|
||||
tileOrigin = vec2(dir.x > 0.0 ? 0.0 : 1.0, 0.0);
|
||||
uv = vec2(tdir.y * 0.5 + 0.5, tdir.z * 0.5 + 0.5);
|
||||
}else if (maxAxis == absY){
|
||||
// +Y (col 2, row 0) or -Y (col 0, row 1)
|
||||
tileOrigin = dir.y > 0.0 ? vec2(2.0, 0.0) : vec2(0.0, 1.0);
|
||||
uv = vec2(tdir.x * 0.5 + 0.5, tdir.z * 0.5 + 0.5);
|
||||
}else{
|
||||
// +Z (col 1, row 1) or -Z (col 2, row 1)
|
||||
tileOrigin = vec2(dir.z > 0.0 ? 1.0 : 2.0, 1.0);
|
||||
uv = vec2(tdir.x * 0.5 + 0.5, tdir.y * 0.5 + 0.5);
|
||||
}
|
||||
|
||||
uv = uv * tile + tileOrigin * tile;
|
||||
|
||||
return textureLod(skyBox2D, uv, 0.0).rgb;
|
||||
}
|
||||
|
||||
|
||||
#ifndef TRACING_UTILITIES_GLSL
|
||||
#define TRACING_UTILITIES_GLSL
|
||||
|
||||
#include "/Lib/PathTracing/Voxelizer/VoxelProfile.glsl"
|
||||
#include "/Lib/PathTracing/Voxelizer/BlockShape.glsl"
|
||||
|
||||
// Convert world position to voxel grid coordinates
|
||||
vec3 WorldToVoxel(vec3 worldPos){
|
||||
return worldPos + cameraPositionFract + (voxelResolution * 0.5);
|
||||
}
|
||||
|
||||
// Sample the atlas for a hit voxel, returns albedo
|
||||
vec3 SampleVoxelAlbedo(vec2 midCoord, vec3 hitVoxelPos, vec3 hitNormal){
|
||||
// Reconstruct the face UV from hit position within voxel
|
||||
vec3 local = fract(hitVoxelPos);
|
||||
vec2 uv;
|
||||
if (abs(hitNormal.x) > 0.5){
|
||||
uv = vec2(local.z, local.y);
|
||||
}else if (abs(hitNormal.y) > 0.5){
|
||||
uv = vec2(local.x, local.z);
|
||||
}else{
|
||||
uv = vec2(local.x, local.y);
|
||||
}
|
||||
|
||||
// midCoord is the tile-aligned center UV; reconstruct pixel within tile
|
||||
vec2 tileCoord = midCoord;
|
||||
vec2 atlasSizeF = vec2(atlasSize);
|
||||
float tileW = 1.0 / atlasSizeF.x; // approximate; resolved via textureResolution
|
||||
vec2 pixelUV = tileCoord + (uv - 0.5) * tileW * 4.0;
|
||||
|
||||
return textureLod(atlas2D, pixelUV, 0.0).rgb;
|
||||
}
|
||||
|
||||
// Read a voxel's data
|
||||
vec4 ReadVoxel(ivec3 voxelCoord){
|
||||
return texelFetch(voxelData3D, voxelCoord, 0);
|
||||
}
|
||||
|
||||
// Test whether a voxel is empty (air or empty marker)
|
||||
bool IsVoxelEmpty(vec4 voxelData){
|
||||
float voxelID = DecodeVoxelID(voxelData.z);
|
||||
return voxelID <= 1.0;
|
||||
}
|
||||
|
||||
// Test whether a voxel is a light source (sphere light)
|
||||
bool IsLightSphere(float voxelID){
|
||||
return voxelID >= 239.0 && voxelID <= 290.0;
|
||||
}
|
||||
|
||||
// Core 3D-DDA march. Calls the callback-style inline logic via return.
|
||||
// Returns: 0 = no hit, 1 = hit, 2 = hit light
|
||||
int TraceRay(Ray ray, float maxDist, out float rayLength, out ivec3 hitVoxel, out float voxelID, out vec3 hitNormal){
|
||||
vec3 voxelPos = ray.ori;
|
||||
vec3 voxelCoord = floor(voxelPos);
|
||||
vec3 totalStep = (ray.sdir * (voxelCoord - voxelPos + 0.5) + 0.5) * abs(ray.rdir);
|
||||
rayLength = 0.0;
|
||||
vec3 tracingNext;
|
||||
hitNormal = vec3(0.0);
|
||||
|
||||
int result = 0;
|
||||
vec3 prevCoord = voxelCoord;
|
||||
|
||||
for (int i = 0; i < 128; i++){
|
||||
if (clamp(voxelCoord, vec3(0.0), vec3(voxelResolution - 0.5)) != voxelCoord) break;
|
||||
|
||||
vec4 voxelData = texelFetch(voxelData3D, ivec3(voxelCoord), 0);
|
||||
float id = DecodeVoxelID(voxelData.z);
|
||||
|
||||
if (id <= 1.0){
|
||||
// empty or full-block marker
|
||||
if (id >= 0.99 && id <= 1.0){
|
||||
// Full block
|
||||
if (rayLength > 0.0){
|
||||
hitVoxel = ivec3(voxelCoord);
|
||||
voxelID = 1.0;
|
||||
hitNormal = -step(vec3(rayLength), totalStep - abs(ray.rdir)) * ray.sdir;
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}else if (id >= 1000.0){
|
||||
// Encoded as full block: id = rawID + 1000
|
||||
float rawID = id - 1000.0;
|
||||
if (rawID <= 1.0){
|
||||
hitVoxel = ivec3(voxelCoord);
|
||||
voxelID = rawID;
|
||||
hitNormal = -step(vec3(rayLength), totalStep - abs(ray.rdir)) * ray.sdir;
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}else if (id > 1.0 && id < 1000.0){
|
||||
// Cutout shape: id = 1000 - rawID
|
||||
float rawID = 1000.0 - id;
|
||||
float t = minVec3(totalStep);
|
||||
rayLength = t;
|
||||
vec3 n;
|
||||
if (HitShape(ray, voxelCoord, rawID, rayLength, n)){
|
||||
hitVoxel = ivec3(voxelCoord);
|
||||
voxelID = rawID;
|
||||
hitNormal = n;
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
}else if (id >= 200.0 && id <= 290.0){
|
||||
// Light source — contribute and continue
|
||||
// (handled by caller; here just note it)
|
||||
hitVoxel = ivec3(voxelCoord);
|
||||
voxelID = id;
|
||||
result = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// Sparse tracing: skip empty markers
|
||||
float marker = voxelData.z;
|
||||
if (marker > 0.60 && marker < 0.92){
|
||||
// Hierarchical skip — approximate by stepping over the block size
|
||||
float skipSize = marker > 0.90 ? 8.0 : (marker > 0.70 ? 4.0 : 2.0);
|
||||
// find next boundary
|
||||
vec3 nextBoundary = floor((voxelCoord + 1.0) / skipSize) * skipSize;
|
||||
vec3 distToBoundary = (nextBoundary - voxelCoord) * abs(ray.rdir);
|
||||
float tSkip = minVec3(distToBoundary) + 1e-4;
|
||||
rayLength += tSkip;
|
||||
vec3 stepVec = ray.sdir * abs(ray.rdir) * tSkip;
|
||||
voxelPos += stepVec;
|
||||
voxelCoord = floor(voxelPos);
|
||||
totalStep = (ray.sdir * (voxelCoord - voxelPos + 0.5) + 0.5) * abs(ray.rdir);
|
||||
if (rayLength > maxDist) break;
|
||||
continue;
|
||||
}
|
||||
|
||||
rayLength = minVec3(totalStep);
|
||||
tracingNext = step(totalStep, vec3(rayLength));
|
||||
voxelCoord += tracingNext * ray.sdir;
|
||||
totalStep += tracingNext * abs(ray.rdir);
|
||||
|
||||
if (rayLength > maxDist) break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,223 @@
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
// MinecraftPT — Block Shape reconstruction
|
||||
// Exact AABB-based shapes for non-full-block voxel IDs, used during ray marching.
|
||||
|
||||
#ifndef BLOCK_SHAPE_GLSL
|
||||
#define BLOCK_SHAPE_GLSL
|
||||
|
||||
float SphereIntersectionLength(Ray ray, vec3 blockOrigin, vec3 sphereOrigin, float sphereRadius){
|
||||
sphereOrigin = -blockOrigin - sphereOrigin;
|
||||
|
||||
float b = dot(ray.dir, sphereOrigin);
|
||||
float c = dot(sphereOrigin, sphereOrigin) - sphereRadius * sphereRadius;
|
||||
float d = b * b - c;
|
||||
|
||||
float intersectionLength = 0.0;
|
||||
|
||||
if (d > 0.0){
|
||||
d = sqrt(d);
|
||||
intersectionLength = saturate(min(-b + d, d * 2.0));
|
||||
}
|
||||
|
||||
return intersectionLength;
|
||||
}
|
||||
|
||||
float BoxIntersectionLength(Ray ray, vec3 blockOrigin){
|
||||
vec3 boxMax = blockOrigin + 1.0;
|
||||
|
||||
vec3 t1 = ray.rdir * blockOrigin;
|
||||
vec3 t2 = ray.rdir * boxMax;
|
||||
|
||||
vec3 tMin = min(t1, t2);
|
||||
vec3 tMax = max(t1, t2);
|
||||
|
||||
float tEnter = maxVec3(tMin);
|
||||
float tExit = minVec3(tMax);
|
||||
|
||||
return max(tExit - tEnter, 0.0);
|
||||
}
|
||||
|
||||
// Light sphere colors for emissive blocks (IDs 200-270 range)
|
||||
vec3 LightShpereColor(float voxelID){
|
||||
const vec3 torchColor = pow(vec3(COLOR_TORCH_R, COLOR_TORCH_G, COLOR_TORCH_B), vec3(2.2)) * BRIGHTNESS_TORCH * SPHERELIGHT_BRIGHTNESS;
|
||||
const vec3 fireColor = pow(vec3(COLOR_FIRE_R, COLOR_FIRE_G, COLOR_FIRE_B), vec3(2.2)) * BRIGHTNESS_FIRE * SPHERELIGHT_BRIGHTNESS;
|
||||
const vec3 redstoneTorchColor = pow(vec3(COLOR_REDSTONETORCH_R, COLOR_REDSTONETORCH_G, COLOR_REDSTONETORCH_B), vec3(2.2)) * BRIGHTNESS_REDSTONETORCH * SPHERELIGHT_BRIGHTNESS;
|
||||
const vec3 amethystColor = pow(vec3(COLOR_AMETHYST_R, COLOR_AMETHYST_G, COLOR_AMETHYST_B), vec3(2.2)) * BRIGHTNESS_AMETHYST * SPHERELIGHT_BRIGHTNESS;
|
||||
const vec3 soultorchColor = pow(vec3(COLOR_SOULTORCH_R, COLOR_SOULTORCH_G, COLOR_SOULTORCH_B), vec3(2.2)) * BRIGHTNESS_SOULTORCH * SPHERELIGHT_BRIGHTNESS;
|
||||
const vec3 lightblockColor = pow(vec3(COLOR_LIGHTBLOCK_R, COLOR_LIGHTBLOCK_G, COLOR_LIGHTBLOCK_B), vec3(2.2)) * BRIGHTNESS_LIGHTBLOCK * SPHERELIGHT_BRIGHTNESS;
|
||||
const vec3 endrodColor = pow(vec3(COLOR_ENDROD_R, COLOR_ENDROD_G, COLOR_ENDROD_B), vec3(2.2)) * BRIGHTNESS_ENDROD * SPHERELIGHT_BRIGHTNESS;
|
||||
|
||||
vec3 shpereColor = vec3(0.0);
|
||||
|
||||
if (voxelID <= 244.0){
|
||||
if (voxelID == 242.0){ // Torch
|
||||
shpereColor = torchColor;
|
||||
}else if (voxelID == 243.0){ // Redstone Torch
|
||||
shpereColor = redstoneTorchColor;
|
||||
}else if (abs(voxelID - 240.0) < 1.5){ // Campfire 239 240
|
||||
shpereColor = fireColor;
|
||||
}else{
|
||||
shpereColor = soultorchColor;
|
||||
}
|
||||
|
||||
}else{
|
||||
if (voxelID == 245.0){ // Amethyst Cluster
|
||||
shpereColor = amethystColor;
|
||||
}else if (voxelID == 246.0){ // Soul Torch
|
||||
shpereColor = soultorchColor;
|
||||
}else if (voxelID == 247.0){ // Copper Lantern
|
||||
shpereColor = torchColor;
|
||||
}else if (abs(voxelID - 250.5) < 2.0){ // Candle & Sea Pickle
|
||||
shpereColor = torchColor * 0.8;
|
||||
}else if (abs(voxelID - 263.0) < 7.5){ // Light Block
|
||||
shpereColor = lightblockColor * (voxelID * (1.0 / 15.0) - (255.0 / 15.0));
|
||||
}else if (abs(voxelID - 280.0) < 1.5){ // End Rod
|
||||
shpereColor = endrodColor;
|
||||
}else{
|
||||
// Generic emissive (from vanilla light level blocks)
|
||||
shpereColor = torchColor * 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
return shpereColor;
|
||||
}
|
||||
|
||||
vec3 HitLightShpere(Ray ray, vec3 voxelCoord, float voxelID, float rayLength){
|
||||
vec3 shpereLighting = vec3(0.0);
|
||||
|
||||
vec3 blockOrigin = voxelCoord - ray.ori;
|
||||
float intersectionLength = SphereIntersectionLength(ray, blockOrigin, vec3(0.5), 0.5);
|
||||
|
||||
if (intersectionLength > 0.0)
|
||||
intersectionLength = intersectionLength * intersectionLength;
|
||||
shpereLighting = LightShpereColor(voxelID) * (intersectionLength * BLOCKLIGHT_BRIGHTNESS);
|
||||
|
||||
return shpereLighting;
|
||||
}
|
||||
|
||||
vec3 HitLightShpereReflection(Ray ray, vec3 voxelCoord, float voxelID, float rayLength){
|
||||
vec3 shpereLighting = vec3(0.0);
|
||||
|
||||
vec3 blockOrigin = voxelCoord - ray.ori;
|
||||
float intersectionLength = SphereIntersectionLength(ray, blockOrigin, vec3(0.5), 0.25);
|
||||
|
||||
if (intersectionLength > 0.0)
|
||||
intersectionLength = intersectionLength * intersectionLength;
|
||||
intersectionLength = intersectionLength * intersectionLength * 50.0;
|
||||
shpereLighting = LightShpereColor(voxelID) * (intersectionLength * BLOCKLIGHT_BRIGHTNESS);
|
||||
|
||||
return shpereLighting;
|
||||
}
|
||||
|
||||
bool IsHitBox(Ray ray, vec3 blockOrigin, vec3 boxOrigin, vec3 boxSize, inout float rayLength, inout vec3 hitNormal){
|
||||
vec3 boxMin = blockOrigin + boxOrigin;
|
||||
vec3 boxMax = boxMin + boxSize;
|
||||
|
||||
vec3 t1 = ray.rdir * boxMin;
|
||||
vec3 t2 = ray.rdir * boxMax;
|
||||
|
||||
vec3 tMin = min(t1, t2);
|
||||
vec3 tMax = max(t1, t2);
|
||||
|
||||
float tEnter = maxVec3(tMin);
|
||||
float tExit = minVec3(tMax);
|
||||
|
||||
bool hit = min(rayLength, tExit) >= tEnter && tExit >= 0.0;
|
||||
|
||||
if (hit){
|
||||
hitNormal = -step(vec3(tEnter), tMin) * ray.sdir;
|
||||
rayLength = tEnter;
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
// Full shape reconstruction for cutout blocks. voxelID here is the raw block ID
|
||||
// from block.properties (0-100 range).
|
||||
bool HitShape(Ray ray, vec3 voxelCoord, float voxelID, inout float rayLength, out vec3 hitNormal){
|
||||
vec3 blockOrigin = voxelCoord - ray.ori;
|
||||
hitNormal = vec3(0.0);
|
||||
|
||||
bool hit = false;
|
||||
|
||||
const float rotIndex[8] = float[8](1.0, 0.0, -1.0, 0.0, 0.0, 1.0, 0.0, -1.0);
|
||||
|
||||
if (voxelID == 2.0){ // Leaves — full block
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.0), vec3(1.0), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 4.0){ // Cross plant (X shape)
|
||||
vec3 ori0 = vec3(0.5);
|
||||
vec3 size0 = vec3(-1.0, 1.0, 0.25 / 16.0);
|
||||
vec3 ori1 = vec3(0.5);
|
||||
vec3 size1 = vec3(0.25 / 16.0, 1.0, -1.0);
|
||||
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(ori0.x, 0.0, ori0.z), vec3(size0.x, 1.0, size0.z), rayLength, hitNormal);
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(ori1.x, 0.0, ori1.z), vec3(size1.x, 1.0, size1.z), rayLength, hitNormal) || hit;
|
||||
|
||||
}else if (voxelID == 5.0){ // Torch
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.4375, 0.0, 0.4375), vec3(0.125, 0.5625, 0.125), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 6.0){ // Lantern
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.25, 0.1875, 0.25), vec3(0.5, 0.5625, 0.5), rayLength, hitNormal);
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.4375, 0.0, 0.4375), vec3(0.125, 0.1875, 0.125), rayLength, hitNormal) || hit;
|
||||
|
||||
}else if (voxelID == 10.0 || voxelID == 11.0){ // Glass pane / Iron bars
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(7.0 / 16.0, 0.0, 7.0 / 16.0), vec3(2.0 / 16.0, 1.0, 2.0 / 16.0), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID >= 12.0 && voxelID <= 13.0){ // Stairs & slabs simplified as full-ish
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.0), vec3(1.0), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID >= 14.0 && voxelID <= 16.0){ // Walls, fences, fence gates
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.25, 0.0, 0.25), vec3(0.5, 1.0, 0.5), rayLength, hitNormal);
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.0, 0.375, 0.0), vec3(1.0, 0.25, 1.0), rayLength, hitNormal) || hit;
|
||||
|
||||
}else if (voxelID == 17.0){ // Door
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.0, 0.0, 13.0 / 16.0), vec3(1.0, 1.0, 3.0 / 16.0), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 20.0){ // End rod
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.4375, 0.0, 0.4375), vec3(0.125, 1.0, 0.125), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 21.0){ // Chain
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.4375, 0.0, 0.4375), vec3(0.125, 1.0, 0.125), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 22.0){ // Amethyst cluster
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.25, 0.0, 0.25), vec3(0.5, 0.5, 0.5), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 27.0){ // Ladder
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.0, 0.0, 0.0), vec3(1.0, 1.0, 1.0), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 28.0 || voxelID == 29.0 || voxelID == 31.0){ // Sugar cane / Bamboo / Chorus
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.375, 0.0, 0.375), vec3(0.25, 1.0, 0.25), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 32.0){ // Coral
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.25, 0.0, 0.25), vec3(0.5, 0.625, 0.5), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 33.0){ // Pointed dripstone
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.375, 0.0, 0.375), vec3(0.25, 1.0, 0.25), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 35.0){ // Lightning rod
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.375, 0.0, 0.375), vec3(0.25, 1.0, 0.25), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 37.0){ // Snow layers
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.0, 0.0, 0.0), vec3(1.0, 0.125, 1.0), rayLength, hitNormal);
|
||||
|
||||
}else if (voxelID == 80.0){ // Cobweb
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.0), vec3(1.0), rayLength, hitNormal);
|
||||
|
||||
}else{
|
||||
// Default: full block
|
||||
hit = IsHitBox(ray, blockOrigin, vec3(0.0), vec3(1.0), rayLength, hitNormal);
|
||||
}
|
||||
|
||||
return hit;
|
||||
}
|
||||
|
||||
// Lightweight hit test (no normal) for shadow rays
|
||||
bool HitShape_Lite(Ray ray, vec3 voxelCoord, float voxelID, float rayLength){
|
||||
vec3 blockOrigin = voxelCoord - ray.ori;
|
||||
vec3 unused = vec3(0.0);
|
||||
return HitShape(ray, voxelCoord, voxelID, rayLength, unused);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,338 @@
|
||||
// MinecraftPT — Shadow + Voxelization pass
|
||||
// Renders the RTWSM shadow map (shadowcolor0) AND the voxel atlas (shadowcolor1),
|
||||
// which is then copied into the 3D voxel texture by VoxelData_Copy_CS.
|
||||
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
#include "/Lib/PathTracing/Voxelizer/VoxelProfile.glsl"
|
||||
#include "/Lib/RTWSM/SampleWarp.glsl"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Vertex Shader
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef PROGRAM_VSH
|
||||
|
||||
uniform mat4 shadowModelViewInverse;
|
||||
uniform mat4 shadowProjection;
|
||||
uniform vec3 cameraPositionFract;
|
||||
uniform float frameTimeCounter;
|
||||
uniform float wetness;
|
||||
|
||||
uniform sampler2D noisetex;
|
||||
|
||||
in vec4 mc_Entity;
|
||||
in vec4 at_midBlock;
|
||||
in vec2 mc_midTexCoord;
|
||||
|
||||
out vec3 g_color;
|
||||
out vec3 g_worldPos;
|
||||
out vec2 g_texcoord;
|
||||
|
||||
#ifdef PROGRAM_VOXEL
|
||||
flat out float g_voxelID;
|
||||
out float g_mcLightLevel;
|
||||
out vec3 g_voxelCoord;
|
||||
out float g_notInVoxel;
|
||||
out float g_normalInvalid;
|
||||
#endif
|
||||
|
||||
#include "/Lib/PathTracing/Voxelizer/VoxelProfile.glsl"
|
||||
#include "/Lib/RTWSM/SampleWarp.glsl"
|
||||
|
||||
#ifdef WAVING_PLANTS
|
||||
#include "/Lib/IndividualFunctions/WavingPlants.glsl"
|
||||
#endif
|
||||
|
||||
void main(){
|
||||
vec4 worldPos = shadowModelViewInverse * gl_ModelViewMatrix * gl_Vertex;
|
||||
|
||||
float skylightmap = saturate(float(gl_MultiTexCoord1.y - 8) / 232.0);
|
||||
|
||||
#ifdef WAVING_PLANTS
|
||||
#ifdef SHADOW_WAVING_PLANTS
|
||||
WavingPlants(worldPos, skylightmap);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
g_worldPos = worldPos.xyz;
|
||||
g_color = gl_Color.rgb;
|
||||
g_texcoord.xy = mat2(gl_TextureMatrix[0]) * gl_MultiTexCoord0.xy + gl_TextureMatrix[0][3].xy;
|
||||
|
||||
#ifdef PROGRAM_VOXEL
|
||||
vec3 worldNormal = mat3(shadowModelViewInverse) * normalize(gl_NormalMatrix * gl_Normal);
|
||||
|
||||
g_voxelID = mc_Entity.x;
|
||||
g_mcLightLevel = skylightmap;
|
||||
g_notInVoxel = step(5999.5, g_voxelID);
|
||||
g_notInVoxel += step(g_voxelID, 0.5);
|
||||
g_notInVoxel *= float(abs(g_voxelID - 8400.0) > 400.5);
|
||||
g_normalInvalid = step(maxVec3(abs(worldNormal)), 0.99);
|
||||
|
||||
g_voxelCoord = vec3(-2.0);
|
||||
|
||||
if (g_notInVoxel < 0.5){
|
||||
// Full block detection: verify vertices are on the block grid
|
||||
if (g_voxelID <= 1.0){
|
||||
vec3 vertexPos = gl_Vertex.xyz + cameraPositionFract;
|
||||
vertexPos = abs(vertexPos - round(vertexPos));
|
||||
float posInvalid = vertexPos.x + vertexPos.y + vertexPos.z;
|
||||
posInvalid = step(0.001, posInvalid);
|
||||
g_notInVoxel = posInvalid + g_normalInvalid;
|
||||
}
|
||||
|
||||
#ifdef PT_MIDBLOCK_TEMPFIX
|
||||
g_voxelCoord = g_worldPos + cameraPositionFract + (voxelResolution * 0.5) - worldNormal * 0.01;
|
||||
#else
|
||||
g_voxelCoord = g_worldPos + cameraPositionFract + (voxelResolution * 0.5) + at_midBlock.xyz * 0.015625;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
gl_Position = vec4(1.0); // set by geometry shader
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Geometry Shader
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef PROGRAM_GSH
|
||||
|
||||
layout(triangles) in;
|
||||
layout(triangle_strip, max_vertices = 6) out;
|
||||
|
||||
uniform mat4 shadowProjection;
|
||||
uniform ivec2 atlasSize;
|
||||
uniform int renderStage;
|
||||
|
||||
in vec3 g_color[];
|
||||
in vec3 g_worldPos[];
|
||||
in vec2 g_texcoord[];
|
||||
|
||||
#ifdef PROGRAM_VOXEL
|
||||
flat in float g_voxelID[];
|
||||
in float g_mcLightLevel[];
|
||||
in vec3 g_voxelCoord[];
|
||||
in float g_notInVoxel[];
|
||||
in float g_normalInvalid[];
|
||||
#endif
|
||||
|
||||
out vec3 v_color;
|
||||
out vec4 v_worldPos_voxelData_isWater_isVoxel;
|
||||
out vec2 v_texcoord_mcLightLevel;
|
||||
flat out vec2 v_midTexCoord;
|
||||
|
||||
#include "/Lib/PathTracing/Voxelizer/VoxelProfile.glsl"
|
||||
|
||||
void main(){
|
||||
v_midTexCoord = vec2(0.0);
|
||||
|
||||
vec3 posDiff = vec3(
|
||||
distance(g_worldPos[0], g_worldPos[1]),
|
||||
distance(g_worldPos[1], g_worldPos[2]),
|
||||
distance(g_worldPos[2], g_worldPos[0])
|
||||
);
|
||||
|
||||
// Emit the shadow map triangle (for RTWSM shadow sampling)
|
||||
{
|
||||
float bias = saturate(maxVec3(posDiff) * 0.5 - 1.0) * shadowProjection[0][0] * 0.3;
|
||||
|
||||
for (int i = 0; i < 3; i++){
|
||||
vec4 worldPos = shadowModelViewInverse * gl_in[i].gl_Position;
|
||||
gl_Position = gl_in[i].gl_Position;
|
||||
gl_Position.z += bias;
|
||||
|
||||
// Shift into the right square shadow region, then apply warp
|
||||
ShiftShadowNdcPos(gl_Position.xy);
|
||||
gl_Position.xy += SampleRTWWarpSmooth(gl_Position.xy * 0.5 + 0.5) * 2.0;
|
||||
|
||||
v_color = g_color[i];
|
||||
v_worldPos_voxelData_isWater_isVoxel = vec4(g_worldPos[i], 0.0);
|
||||
v_texcoord_mcLightLevel = g_texcoord[i];
|
||||
|
||||
EmitVertex();
|
||||
}
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
#ifdef PROGRAM_VOXEL
|
||||
|
||||
vec3 voxelCoord = floor(g_voxelCoord[0] * 0.33333333 + g_voxelCoord[1] * 0.33333333 + g_voxelCoord[2] * 0.33333333);
|
||||
|
||||
if (all(bvec3(
|
||||
clamp(voxelCoord, vec3(0.0), vec3(voxelResolution - 1.0)) == voxelCoord,
|
||||
g_notInVoxel[0] + g_notInVoxel[1] + g_notInVoxel[2] < 0.5,
|
||||
renderStage == MC_RENDER_STAGE_TERRAIN_SOLID || renderStage == MC_RENDER_STAGE_TERRAIN_TRANSLUCENT
|
||||
))){
|
||||
|
||||
vec2 atlasResolution = vec2(atlasSize);
|
||||
|
||||
vec2 maxTexCoord = max(g_texcoord[0].xy, max(g_texcoord[1].xy, g_texcoord[2].xy));
|
||||
vec2 minTexCoord = min(g_texcoord[0].xy, min(g_texcoord[1].xy, g_texcoord[2].xy));
|
||||
|
||||
v_midTexCoord = (maxTexCoord + minTexCoord) * 0.5;
|
||||
|
||||
vec2 coordSize = (maxTexCoord - minTexCoord) * atlasResolution;
|
||||
|
||||
#if TEXTURE_RESOLUTION == 0
|
||||
float coordMaxSize = maxVec3(vec3(
|
||||
maxVec2(abs(g_texcoord[0].xy - g_texcoord[1].xy) * atlasResolution) / max(posDiff.x, 1e-4),
|
||||
maxVec2(abs(g_texcoord[1].xy - g_texcoord[2].xy) * atlasResolution) / max(posDiff.y, 1e-4),
|
||||
maxVec2(abs(g_texcoord[0].xy - g_texcoord[2].xy) * atlasResolution) / max(posDiff.z, 1e-4)
|
||||
));
|
||||
|
||||
float textureResolution = floor(coordMaxSize + 0.5);
|
||||
#else
|
||||
float textureResolution = TEXTURE_RESOLUTION;
|
||||
#endif
|
||||
|
||||
float voxelID = g_voxelID[0];
|
||||
|
||||
float roundedResolution = round(log2(textureResolution));
|
||||
|
||||
vec2 atlasTiles = vec2(atlasSize) * exp2(-roundedResolution);
|
||||
|
||||
// Tile-aligned texel sampling for accurate atlas UV
|
||||
v_midTexCoord = (floor(v_midTexCoord * atlasTiles) + 0.5) / atlasTiles;
|
||||
|
||||
float skylight = saturate(SkyLightmapCurve(g_mcLightLevel[0] * 0.33333333 + g_mcLightLevel[1] * 0.33333333 + g_mcLightLevel[2] * 0.33333333));
|
||||
|
||||
float zOffset = g_normalInvalid[0] + g_normalInvalid[1] + g_normalInvalid[2];
|
||||
|
||||
coordSize /= textureResolution;
|
||||
|
||||
zOffset += saturate(coordSize.x * coordSize.y) * -0.2;
|
||||
|
||||
bool isCutout = voxelID == 2.0;
|
||||
|
||||
// Light blocks (8000-8400): encode block light level
|
||||
if (abs(voxelID - 8400.0) < 400.5){
|
||||
voxelID -= 8000.0;
|
||||
|
||||
if (voxelID > 499.5){
|
||||
// hardcoded light level block
|
||||
voxelID = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode: cutout shapes get 1000 - id, full blocks get id + 1000
|
||||
bool isShape = bool(
|
||||
uint(voxelID == 2.0) | // leaves
|
||||
uint(voxelID == 4.0) | // cross plants
|
||||
uint(voxelID == 5.0) | // torch
|
||||
uint(voxelID == 6.0) | // lantern
|
||||
uint(voxelID == 10.0) | // glass pane
|
||||
uint(voxelID == 11.0) | // iron bars
|
||||
uint(voxelID == 12.0) | // stairs
|
||||
uint(voxelID == 13.0) | // slabs
|
||||
uint(voxelID == 14.0) | // walls
|
||||
uint(voxelID == 15.0) | // fences
|
||||
uint(voxelID == 16.0) | // fence gates
|
||||
uint(voxelID == 17.0) | // doors
|
||||
uint(voxelID == 20.0) | // end rod
|
||||
uint(voxelID == 21.0) | // chain
|
||||
uint(voxelID == 22.0) | // amethyst
|
||||
uint(voxelID == 27.0) | // ladder
|
||||
uint(voxelID == 28.0) | // sugar cane
|
||||
uint(voxelID == 29.0) | // bamboo
|
||||
uint(voxelID == 31.0) | // chorus
|
||||
uint(voxelID == 32.0) | // coral
|
||||
uint(voxelID == 33.0) | // dripstone
|
||||
uint(voxelID == 35.0) | // lightning rod
|
||||
uint(voxelID == 36.0) | // pot
|
||||
uint(voxelID == 37.0) | // snow layers
|
||||
uint(voxelID == 80.0) // cobweb
|
||||
);
|
||||
|
||||
voxelID = isShape ? 1000.0 - voxelID : voxelID + 1000.0;
|
||||
|
||||
#ifdef PT_FULLBLOCK_VERIFICATION
|
||||
if (voxelID == 1001.0){
|
||||
if (abs(posDiff.x + posDiff.y + posDiff.z - 3.41421356) > 0.001){
|
||||
voxelID = 65536.0;
|
||||
zOffset = -0.49;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
vec2 voxelTexel = VoxelTexel_From_VoxelCoord(voxelCoord);
|
||||
const vec2[3] vertexOffset = vec2[3](vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.5, 1.0));
|
||||
|
||||
for (int i = 0; i < 3; i++){
|
||||
gl_Position = vec4((voxelTexel + vertexOffset[i]) * shadowPixelSize * 2.0 - 1.0, zOffset * 0.5 - 0.75, 1.0);
|
||||
|
||||
v_color = g_color[i];
|
||||
v_worldPos_voxelData_isWater_isVoxel = vec4(voxelID, roundedResolution, 0.0, 1.0);
|
||||
v_texcoord_mcLightLevel = vec2(skylight, 0.0);
|
||||
|
||||
EmitVertex();
|
||||
}
|
||||
|
||||
EndPrimitive();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Fragment Shader
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
#ifdef PROGRAM_FSH
|
||||
|
||||
#include "/Lib/PathTracing/Voxelizer/VoxelProfile.glsl"
|
||||
|
||||
layout(location = 0) out vec4 shadowbuffer0;
|
||||
layout(location = 1) out vec4 shadowbuffer1;
|
||||
|
||||
uniform mat4 shadowModelViewInverse;
|
||||
uniform vec3 cameraPosition;
|
||||
uniform ivec2 atlasSize;
|
||||
uniform int isEyeInWater;
|
||||
uniform vec2 screenSize;
|
||||
uniform vec2 pixelSize;
|
||||
uniform int frameCounter;
|
||||
uniform int renderStage;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D noisetex;
|
||||
uniform sampler2D pixelData2D;
|
||||
|
||||
#include "/Lib/BasicFunctions/TemporalNoise.glsl"
|
||||
#include "/Lib/RTWSM/SampleWarp.glsl"
|
||||
|
||||
in vec3 v_color;
|
||||
in vec4 v_worldPos_voxelData_isWater_isVoxel;
|
||||
in vec2 v_texcoord_mcLightLevel;
|
||||
flat in vec2 v_midTexCoord;
|
||||
|
||||
void main(){
|
||||
// Shadow map fragment
|
||||
if (v_worldPos_voxelData_isWater_isVoxel.w < 0.5){
|
||||
vec4 albedoTex = textureLod(tex, v_texcoord_mcLightLevel.xy, 0.0);
|
||||
|
||||
// Keep shadow fragments only in the right square region [W, 2W] x [0, W]
|
||||
if (gl_FragCoord.x < float(voxelWidth) || gl_FragCoord.x >= shadowSize || gl_FragCoord.y >= float(voxelWidth)
|
||||
|| albedoTex.a < 0.004) discard;
|
||||
|
||||
albedoTex.rgb *= v_color;
|
||||
|
||||
shadowbuffer0 = vec4(albedoTex);
|
||||
shadowbuffer1 = vec4(0.0);
|
||||
}
|
||||
|
||||
// Voxel atlas fragment
|
||||
if (v_worldPos_voxelData_isWater_isVoxel.w > 0.2){
|
||||
shadowbuffer0 = vec4(v_color.rgb, v_texcoord_mcLightLevel.x);
|
||||
|
||||
vec2 midCoord = saturate(v_midTexCoord * (65536.0 / 65535.0));
|
||||
float voxelID = saturate(v_worldPos_voxelData_isWater_isVoxel.x / 65535.0);
|
||||
float textureResolution = saturate(v_worldPos_voxelData_isWater_isVoxel.y / 255.0);
|
||||
float skylight = saturate(SkyLightmapCurve(v_texcoord_mcLightLevel.x * 1.07));
|
||||
|
||||
shadowbuffer1 = vec4(midCoord, voxelID, Pack2xU8_to_U16(vec2(textureResolution, skylight)));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,139 @@
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
// MinecraftPT — Voxel Profile
|
||||
// Voxel grid parameters derived from settings.
|
||||
//
|
||||
// The shadow framebuffer (shadowcolor0/1) is square, size S x S, and holds:
|
||||
// - the VOXEL ATLAS in the bottom-left region: texels [0, W) x [0, H)
|
||||
// where W = voxelWidth and H = ceil(N / W), N = Rx*Ry*Rz voxels.
|
||||
// Each voxel is one texel; linear packing: n = x + y*Rx + z*Rx*Ry.
|
||||
// - the SHADOW MAP in the right square region: texels [W, 2W) x [0, W).
|
||||
// The shadow camera NDC is shifted into this region (square -> square,
|
||||
// no aspect distortion). Effective shadow resolution = W.
|
||||
|
||||
#ifndef VOXEL_PROFILE_GLSL
|
||||
#define VOXEL_PROFILE_GLSL
|
||||
|
||||
// Shadow render distance -> shadow coverage
|
||||
#if SHADOW_RENDER_DISTANCE == 4
|
||||
const float shadowDistance = 64.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 6
|
||||
const float shadowDistance = 96.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 8
|
||||
const float shadowDistance = 128.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 12
|
||||
const float shadowDistance = 192.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 16
|
||||
const float shadowDistance = 256.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 24
|
||||
const float shadowDistance = 384.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 32
|
||||
const float shadowDistance = 512.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 48
|
||||
const float shadowDistance = 768.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 64
|
||||
const float shadowDistance = 1024.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 96
|
||||
const float shadowDistance = 1536.1;
|
||||
#elif SHADOW_RENDER_DISTANCE == 128
|
||||
const float shadowDistance = 2048.1;
|
||||
#else
|
||||
const float shadowDistance = 256.1;
|
||||
#endif
|
||||
|
||||
// Voxel grid resolution and coverage (blocks)
|
||||
#if PT_VOXEL_RESOLUTION == 4004
|
||||
const ivec3 voxelResolutionInt = ivec3(128);
|
||||
const float voxelDistance = 64.0;
|
||||
const int voxelWidth = 2048;
|
||||
#elif PT_VOXEL_RESOLUTION == 6004
|
||||
const ivec3 voxelResolutionInt = ivec3(192, 128, 192);
|
||||
const float voxelDistance = 96.0;
|
||||
const int voxelWidth = 2048;
|
||||
#elif PT_VOXEL_RESOLUTION == 8004
|
||||
const ivec3 voxelResolutionInt = ivec3(256, 128, 256);
|
||||
const float voxelDistance = 128.0;
|
||||
const int voxelWidth = 2048;
|
||||
#elif PT_VOXEL_RESOLUTION == 8006
|
||||
const ivec3 voxelResolutionInt = ivec3(256, 192, 256);
|
||||
const float voxelDistance = 128.0;
|
||||
const int voxelWidth = 4096;
|
||||
#elif PT_VOXEL_RESOLUTION == 8008
|
||||
const ivec3 voxelResolutionInt = ivec3(256);
|
||||
const float voxelDistance = 128.0;
|
||||
const int voxelWidth = 4096;
|
||||
#elif PT_VOXEL_RESOLUTION == 12004
|
||||
const ivec3 voxelResolutionInt = ivec3(384, 128, 384);
|
||||
const float voxelDistance = 192.0;
|
||||
const int voxelWidth = 4096;
|
||||
#elif PT_VOXEL_RESOLUTION == 12006
|
||||
const ivec3 voxelResolutionInt = ivec3(384, 192, 384);
|
||||
const float voxelDistance = 192.0;
|
||||
const int voxelWidth = 4096;
|
||||
#elif PT_VOXEL_RESOLUTION == 12008
|
||||
const ivec3 voxelResolutionInt = ivec3(384, 256, 384);
|
||||
const float voxelDistance = 192.0;
|
||||
const int voxelWidth = 6144;
|
||||
#elif PT_VOXEL_RESOLUTION == 16004
|
||||
const ivec3 voxelResolutionInt = ivec3(512, 128, 512);
|
||||
const float voxelDistance = 256.0;
|
||||
const int voxelWidth = 6144;
|
||||
#elif PT_VOXEL_RESOLUTION == 16008
|
||||
const ivec3 voxelResolutionInt = ivec3(512, 256, 512);
|
||||
const float voxelDistance = 256.0;
|
||||
const int voxelWidth = 6144;
|
||||
#elif PT_VOXEL_RESOLUTION == 16016
|
||||
const ivec3 voxelResolutionInt = ivec3(512);
|
||||
const float voxelDistance = 256.0;
|
||||
const int voxelWidth = 8192;
|
||||
#else
|
||||
const ivec3 voxelResolutionInt = ivec3(256, 192, 256);
|
||||
const float voxelDistance = 128.0;
|
||||
const int voxelWidth = 4096;
|
||||
#endif
|
||||
|
||||
const vec3 voxelResolution = vec3(voxelResolutionInt);
|
||||
|
||||
// Irradiance cache resolution (clamped to voxel grid)
|
||||
const int ircResolution = min(PT_IRC_RESOLUTION, voxelResolutionInt.x);
|
||||
|
||||
// Shadow framebuffer size (square)
|
||||
const int shadowMapResolution = voxelWidth * 2;
|
||||
const float shadowSize = float(shadowMapResolution);
|
||||
const float shadowPixelSize = 1.0 / shadowSize;
|
||||
const float shadowRatio = 0.5; // W / S
|
||||
|
||||
// Map 3D voxel coordinate to 2D atlas texel (linear packing)
|
||||
vec2 VoxelTexel_From_VoxelCoord(vec3 voxelCoord){
|
||||
float n = voxelCoord.x + voxelCoord.y * voxelResolution.x + voxelCoord.z * voxelResolution.x * voxelResolution.y;
|
||||
return vec2(mod(n, float(voxelWidth)), floor(n / float(voxelWidth)));
|
||||
}
|
||||
|
||||
// Shift shadow map NDC into the right square region [W, 2W] x [0, W]
|
||||
void ShiftShadowNdcPos(inout vec2 coord){
|
||||
coord = coord * shadowRatio + vec2(1.0 - shadowRatio, shadowRatio - 1.0);
|
||||
}
|
||||
|
||||
// Shift shadow map screen uv into the right square region
|
||||
void ShiftShadowScreenPos(inout vec2 coord){
|
||||
coord = coord * shadowRatio + vec2(1.0 - shadowRatio, 0.0);
|
||||
}
|
||||
|
||||
// Shift back from shadow region uv to standard shadow uv
|
||||
vec2 UnshiftShadowScreenPos(vec2 coord){
|
||||
return (coord - vec2(1.0 - shadowRatio, 0.0)) / shadowRatio;
|
||||
}
|
||||
|
||||
// Block ID encoding (16-bit range)
|
||||
// Full block: voxelID + 1000
|
||||
// Cutout shape: 1000 - voxelID
|
||||
// Empty markers: 0.91 (8^3), 0.71 (4^3), 0.61 (2^3)
|
||||
float EncodeVoxelID(float voxelID){
|
||||
return saturate((voxelID + 1000.0) / 65535.0);
|
||||
}
|
||||
|
||||
float DecodeVoxelID(float encoded){
|
||||
return abs(floor(encoded * 65535.0 - 999.9));
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user