Initial commit: Minecraft 光追着色器包(从零实现的 path tracing)

This commit is contained in:
WpyQwq
2026-09-19 12:06:22 +08:00
commit 59e30822f8
314 changed files with 9259 additions and 0 deletions
@@ -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