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
+205
View File
@@ -0,0 +1,205 @@
// MinecraftPT texture generator
// Generates noise.png (blue noise), CloudNoise 3D bins, and ripple.png
const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const outDir = path.join(__dirname, 'texture');
fs.mkdirSync(outDir, { recursive: true });
// ---------- PNG encoder (minimal, non-interlaced RGBA8) ----------
function crc32(buf) {
let table = crc32.table;
if (!table) {
table = crc32.table = new Int32Array(256);
for (let n = 0; n < 256; n++) {
let c = n;
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
table[n] = c;
}
}
let crc = -1;
for (let i = 0; i < buf.length; i++) crc = (crc >>> 8) ^ table[(crc ^ buf[i]) & 0xff];
return (crc ^ -1) >>> 0;
}
function chunk(type, data) {
const len = Buffer.alloc(4);
len.writeUInt32BE(data.length);
const typeBuf = Buffer.from(type, 'ascii');
const crc = Buffer.alloc(4);
crc.writeUInt32BE(crc32(Buffer.concat([typeBuf, data])));
return Buffer.concat([len, typeBuf, data, crc]);
}
function encodePNG(width, height, rgba) {
const sig = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
const ihdr = Buffer.alloc(13);
ihdr.writeUInt32BE(width, 0);
ihdr.writeUInt32BE(height, 4);
ihdr[8] = 8; // bit depth
ihdr[9] = 6; // color type RGBA
const raw = Buffer.alloc((width * 4 + 1) * height);
for (let y = 0; y < height; y++) {
raw[y * (width * 4 + 1)] = 0; // filter none
rgba.copy(raw, y * (width * 4 + 1) + 1, y * width * 4, (y + 1) * width * 4);
}
const idat = zlib.deflateSync(raw, { level: 9 });
return Buffer.concat([sig, chunk('IHDR', ihdr), chunk('IDAT', idat), chunk('IEND', Buffer.alloc(0))]);
}
// ---------- Hashing ----------
function hash1(x, y, z, seed) {
let h = (x * 374761393 + y * 668265263 + z * 2246822519 + seed * 3266489917) | 0;
h = (h ^ (h >>> 13)) | 0;
h = Math.imul(h, 1274126177);
h = (h ^ (h >>> 16)) >>> 0;
return h / 4294967296;
}
// ---------- 1. Blue noise 128x128 ----------
{
const S = 128;
const buf = Buffer.alloc(S * S * 4);
const noise = new Float32Array(S * S);
// white noise
for (let y = 0; y < S; y++)
for (let x = 0; x < S; x++)
noise[y * S + x] = hash1(x, y, 7, 12345);
// high-pass: n - blur(n), renormalize -> bluish
const blurred = new Float32Array(S * S);
for (let y = 0; y < S; y++) {
for (let x = 0; x < S; x++) {
let sum = 0;
for (let dy = -1; dy <= 1; dy++)
for (let dx = -1; dx <= 1; dx++) {
const xx = (x + dx + S) % S;
const yy = (y + dy + S) % S;
sum += noise[yy * S + xx];
}
blurred[y * S + x] = sum / 9;
}
}
let min = 1e9, max = -1e9;
for (let i = 0; i < S * S; i++) {
const v = noise[i] - blurred[i];
noise[i] = v;
if (v < min) min = v;
if (v > max) max = v;
}
for (let y = 0; y < S; y++) {
for (let x = 0; x < S; x++) {
const v = (noise[y * S + x] - min) / (max - min);
const p = y * S * 4 + x * 4;
buf[p] = Math.round(v * 255);
buf[p + 1] = Math.round(v * 255);
buf[p + 2] = Math.round(v * 255);
buf[p + 3] = 255;
}
}
fs.writeFileSync(path.join(outDir, 'noise.png'), encodePNG(S, S, buf));
console.log('noise.png generated');
}
// ---------- 2. 3D value noise (128^3 RGBA8) ----------
function make3DNoise(W, H, D, seed) {
const data = Buffer.alloc(W * H * D * 4);
// Generate 3 octaves of value noise at lattice 8, 4, 2
const octaves = [
{ lat: 8, amp: 0.6 },
{ lat: 4, amp: 0.3 },
{ lat: 2, amp: 0.1 },
];
for (let z = 0; z < D; z++) {
for (let y = 0; y < H; y++) {
for (let x = 0; x < W; x++) {
let v = 0;
for (const o of octaves) {
const L = o.lat;
const fx = (x / W) * L;
const fy = (y / H) * L;
const fz = (z / D) * L;
const x0 = Math.floor(fx), y0 = Math.floor(fy), z0 = Math.floor(fz);
const x1 = x0 + 1, y1 = y0 + 1, z1 = z0 + 1;
const tx = fx - x0, ty = fy - y0, tz = fz - z0;
const sx = tx * tx * (3 - 2 * tx);
const sy = ty * ty * (3 - 2 * ty);
const sz = tz * tz * (3 - 2 * tz);
const c000 = hash1(x0, y0, z0, seed + o.lat);
const c100 = hash1(x1, y0, z0, seed + o.lat);
const c010 = hash1(x0, y1, z0, seed + o.lat);
const c110 = hash1(x1, y1, z0, seed + o.lat);
const c001 = hash1(x0, y0, z1, seed + o.lat);
const c101 = hash1(x1, y0, z1, seed + o.lat);
const c011 = hash1(x0, y1, z1, seed + o.lat);
const c111 = hash1(x1, y1, z1, seed + o.lat);
const x00 = c000 + (c100 - c000) * sx;
const x10 = c010 + (c110 - c010) * sx;
const x01 = c001 + (c101 - c001) * sx;
const x11 = c011 + (c111 - c011) * sx;
const y0v = x00 + (x10 - x00) * sy;
const y1v = x01 + (x11 - x01) * sy;
v += (y0v + (y1v - y0v) * sz) * o.amp;
}
const idx = (z * H + y) * W * 4 + x * 4;
data[idx] = Math.round(v * 255);
data[idx + 1] = Math.round(v * 255);
data[idx + 2] = Math.round(v * 255);
data[idx + 3] = 255;
}
}
}
return data;
}
{
const W = 128, H = 128, D = 128;
const data = make3DNoise(W, H, D, 4242);
fs.writeFileSync(path.join(outDir, 'CloudNoise_128_128_128.bin'), data);
console.log('CloudNoise_128_128_128.bin generated');
}
{
const W = 32, H = 32, D = 32;
const data = make3DNoise(W, H, D, 7777);
// RGB8 only
const rgb = Buffer.alloc(W * H * D * 3);
for (let i = 0; i < W * H * D; i++) {
rgb[i * 3] = data[i * 4];
rgb[i * 3 + 1] = data[i * 4 + 1];
rgb[i * 3 + 2] = data[i * 4 + 2];
}
fs.writeFileSync(path.join(outDir, 'CloudNoise2_32_32_32.bin'), rgb);
console.log('CloudNoise2_32_32_32.bin generated');
}
// ---------- 3. Ripple 128x128 ----------
{
const S = 128;
const buf = Buffer.alloc(S * S * 4);
for (let y = 0; y < S; y++) {
for (let x = 0; x < S; x++) {
const dx = (x - S / 2) / S;
const dy = (y - S / 2) / S;
const dist = Math.sqrt(dx * dx + dy * dy);
// concentric ripple
const n = hash1(x, y, 11, 999);
const ripple = 0.5 + 0.5 * Math.sin(dist * 40 + n * 6.28);
const p = (y * S + x) * 4;
buf[p] = Math.round(ripple * 255);
buf[p + 1] = Math.round(ripple * 255);
buf[p + 2] = 128;
buf[p + 3] = 255;
}
}
fs.writeFileSync(path.join(outDir, 'ripple.png'), encodePNG(S, S, buf));
console.log('ripple.png generated');
}
console.log('All textures generated in', outDir);