Initial commit: Minecraft 光追着色器包(从零实现的 path tracing)
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user