Initial commit: Minecraft 光追着色器包(从零实现的 path tracing)
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
// MinecraftPT — DiffuseTracing_FS
|
||||
// One diffuse path-tracing ray per pixel at half resolution.
|
||||
// Output: colortex6 = noisy diffuse irradiance, colortex10 = motion vectors.
|
||||
|
||||
#include "/Lib/Settings.glsl"
|
||||
#include "/Lib/Utilities.glsl"
|
||||
#include "/Lib/BasicFunctions/LightingConstants.glsl"
|
||||
#include "/Lib/GbufferData.glsl"
|
||||
#include "/Lib/PathTracing/Tracer/TracingNoise.glsl"
|
||||
#include "/Lib/PathTracing/Tracer/TracingUtilities.glsl"
|
||||
#include "/Lib/PathTracing/Tracer/ShadowTracing.glsl"
|
||||
#include "/Lib/PathTracing/Tracer/SampleIRC.glsl"
|
||||
#include "/Lib/PathTracing/Tracer/SpecularTracer.glsl"
|
||||
#include "/Lib/BasicFunctions/TemporalNoise.glsl"
|
||||
|
||||
uniform sampler2D colortex0; // albedo
|
||||
uniform sampler2D colortex1; // normals
|
||||
uniform sampler2D colortex3; // lightmap
|
||||
|
||||
layout(location = 0) out vec4 diffuseOut; // colortex6
|
||||
layout(location = 1) out vec4 motionOut; // colortex10
|
||||
|
||||
vec3 TraceDiffuseRay(vec3 origin, vec3 dir, float maxDist){
|
||||
vec3 result = vec3(0.0);
|
||||
|
||||
Ray ray = PackRay(origin, 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;
|
||||
|
||||
for (int i = 0; i < 128; i++){
|
||||
if (clamp(voxelCoord, vec3(0.0), vec3(voxelResolution - 0.5)) != voxelCoord){
|
||||
// Miss: sample sky
|
||||
result += SampleSkyBox(dir) * (1.0 / 3.14159265);
|
||||
break;
|
||||
}
|
||||
|
||||
if (rayLength > maxDist){
|
||||
result += SampleSkyBox(dir) * (1.0 / 3.14159265);
|
||||
break;
|
||||
}
|
||||
|
||||
vec4 voxelData = texelFetch(voxelData3D, ivec3(voxelCoord), 0);
|
||||
float voxelID = DecodeVoxelID(voxelData.z);
|
||||
|
||||
// Light sphere contribution
|
||||
if (IsLightSphere(voxelID)){
|
||||
result += HitLightShpere(ray, voxelCoord, voxelID, rayLength);
|
||||
rayLength = minVec3(totalStep);
|
||||
tracingNext = step(totalStep, vec3(rayLength));
|
||||
voxelCoord += tracingNext * ray.sdir;
|
||||
totalStep += tracingNext * abs(ray.rdir);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hit = false;
|
||||
vec3 hitNormal = vec3(0.0);
|
||||
|
||||
if (voxelID >= 999.0){
|
||||
hit = rayLength > 0.0;
|
||||
hitNormal = -step(vec3(rayLength), totalStep - abs(ray.rdir)) * ray.sdir;
|
||||
}else if (voxelID < 1000.0 && voxelID > 1.0){
|
||||
float rawID = 1000.0 - voxelID;
|
||||
rayLength = minVec3(totalStep);
|
||||
hit = HitShape(ray, voxelCoord, rawID, rayLength, hitNormal);
|
||||
}
|
||||
|
||||
if (hit){
|
||||
vec3 hitPos = ray.ori + ray.dir * rayLength;
|
||||
vec2 midCoord = voxelData.xy;
|
||||
vec3 albedo = SampleVoxelAlbedo(midCoord, hitPos, hitNormal);
|
||||
float skylight = Unpack2xU8_Y_from_U16(voxelData.w);
|
||||
|
||||
// Direct sun at hit point
|
||||
vec3 sun = GetSunIrradiance();
|
||||
float shadow = SimpleShadowTracing(hitPos + hitNormal * 0.01, GetSunDirWorld());
|
||||
float NdotL = max(dot(hitNormal, GetSunDirWorld()), 0.0);
|
||||
|
||||
result += albedo * sun * shadow * NdotL;
|
||||
|
||||
// Sky light at hit point (from voxel skylight)
|
||||
result += albedo * GetAtmoIrradiance() * skylight * 0.5;
|
||||
|
||||
// One-bounce: sample IRC
|
||||
result += albedo * SampleIRC(hitPos) * 0.5;
|
||||
|
||||
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 result;
|
||||
}
|
||||
|
||||
void main(){
|
||||
// Half resolution texel
|
||||
ivec2 texelCoord = ivec2(gl_FragCoord.xy);
|
||||
ivec2 fullCoord = texelCoord * 2;
|
||||
|
||||
// Skip sky
|
||||
float depth = texelFetch(depthtex0, fullCoord, 0).r;
|
||||
if (depth >= 1.0){
|
||||
diffuseOut = vec4(0.0);
|
||||
motionOut = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
vec4 albedoData = texelFetch(colortex0, fullCoord, 0);
|
||||
vec4 normalData = texelFetch(colortex1, fullCoord, 0);
|
||||
|
||||
vec3 worldNormal = DecodeNormal(normalData.xy);
|
||||
vec3 viewNormal = mat3(gbufferModelView) * worldNormal;
|
||||
|
||||
// Reconstruct view position
|
||||
vec4 viewPos = gbufferProjectionInverse * vec4(vec2(fullCoord) / screenSize * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
|
||||
viewPos.xyz /= viewPos.w;
|
||||
|
||||
vec3 worldPos = gbufferModelViewInverse[3].xyz + viewPos.xyz;
|
||||
|
||||
vec2 noise = GetTracingNoise2(vec2(texelCoord), frameCounter, 0);
|
||||
|
||||
// Cosine-weighted hemisphere sampling
|
||||
vec3 dir = SampleHemisphere(noise, worldNormal);
|
||||
|
||||
// Only trace if there's anything to bounce (skip pure black)
|
||||
vec3 traceResult = TraceDiffuseRay(WorldToVoxel(worldPos + worldNormal * 0.05), dir, PT_DIFFUSE_TRACING_DISTANCE);
|
||||
|
||||
diffuseOut = vec4(traceResult, 1.0);
|
||||
|
||||
// Motion vectors (full res reprojection)
|
||||
vec4 prevViewPos = gbufferPreviousProjection * gbufferPreviousModelView * vec4(worldPos, 1.0);
|
||||
prevViewPos.xyz /= prevViewPos.w;
|
||||
vec2 prevScreen = (prevViewPos.xy * 0.5 + 0.5) * screenSize;
|
||||
|
||||
vec2 motion = vec2(texelCoord) - prevScreen * 0.5;
|
||||
|
||||
motionOut = vec4(motion, 0.0, 1.0);
|
||||
}
|
||||
Reference in New Issue
Block a user