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