Preview
precision highp float;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform sampler2D backbuffer;
out vec4 outColor;
const int maxDepth = 3; // CÌoHÌ[³ÌÅåli½Ëñ+1Ìlj
const int numSamples = 20; // pXg[VOÌTv
const float PI = acos(-1.); // ~ü¦
const float PI2 = acos(-1.) * 2.;
const float EPS = 0.0001; // Cg[XÈÇÉg¤÷¬Ê
const float FOV = 60.; // ìpidegreej ÍÍF(0., 180.)
const float fallInterval = 100.; // Voxelª¿ÄéÔu
const float fallSpeed = 4.; // Voxelª¿é¬³
const float heightRange = 5.; // Voxel̳ÌÍÍ
//const float bumpFactor = 0.7;
const float bumpFactor = 2.;
vec3 camPos; // JÌÀW
vec3 voxelID; // VoxelÌID
vec3 voxelNormal; // VoxelÌ@üxNg ((}1, 0, 0) or (0, }1, 0) or (0, 0, }1))
vec3 voxelPos; // VoxelÌ®ÀW
float pathSeed = 0.; // pXg[VOÅg¤ÌV[h
// 1DÌ
float hash11(float x) {
return fract(sin(x) * 43758.5453);
}
// 2DÌ
float hash12(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
// 3DÌ
float hash13(vec3 p) {
return fract(sin(dot(p, vec3(12.9898, 78.233, 52.178))) * 43758.5453);
}
// 1DÌ(V[hðXV)
float random() {
return hash11(pathSeed++);
}
// 2DÌñ]sñ
mat2 rotate2D(float a) {
float s = sin(a), c = cos(a);
return mat2(c, s, -s, c);
}
// HSVðRGBÉÏ·
vec3 hsv(float h, float s, float v) {
vec3 res = fract(h + vec3(0, 2, 1) / 3.);
res = clamp(abs(res * 6. - 3.) - 1., 0., 1.);
res = (res - 1.) * s + 1.;
res *= v;
return res;
}
// ÀWpÉVoxelª¶Ý·é©Û©
float map(vec3 p) {
float res = 1.;
vec3 ID = floor(p);
float a = abs(p.x);
ID.y -= floor(max(a * a * 0.1, 5.) - hash12(ID.xz) * heightRange);
float T = time * fallSpeed / fallInterval + hash12(ID.xz * 1.852813);
if(ID.y > 0.){
float tmp = ID.y / fallInterval + T;
if(fract(tmp) * fallInterval > 1.) {
res = 0.;
}
ID.y = floor(tmp);
} else {
ID.y += floor(T);
}
p += 0.5 - camPos;
if(dot(p, p) < 1.) {
res = 0.;
}
voxelID = ID;
return res;
}
// ref: "Voxel Edges" by iq
// https://www.shadertoy.com/view/4dfGzs
// ro©ç¨Ì\ÊÜÅòε½CÌ·³
float castRay(vec3 ro, vec3 rd, const int itr) {
//vec3 pos = floor(ro);
voxelPos = floor(ro);
vec3 ri = 1.0 / rd;
vec3 rs = sign(rd);
vec3 dis = (voxelPos - ro + 0.5 + rs * 0.5) * ri; // ro©çZÌ«EÜÅÌCÌ·³iX,Y,Zûüj
float res = -1.0;
vec3 mm = vec3(0);
vec3 tmp = vec3(0);
for(int i = 0; i < itr; i++) {
if(map(voxelPos) > 0.5) {
res = 1.0;
break;
}
mm = step(dis.xyz, dis.yzx) * step(dis.xyz, dis.zxy); // X,Y,Z̤¿AÇÌûüÌ×ÚZÉiÞ©
tmp = mm * rs;
dis += tmp * ri;
voxelPos += tmp;
}
voxelNormal = -tmp;
vec3 mini = (voxelPos - ro + 0.5 - 0.5 * rs) * ri;
float t = max(mini.x, max(mini.y, mini.z)); // ro©çVoxel\ÊÜÅLε½CÌ·³
return t * res;
}
// G~bVÌF
float emission() {
return step(hash13(voxelID * 1.552897), 0.08) * 10.;
}
// ¨ÌÌF
vec3 objColor(){
return hsv(hash13(voxelID * 1.347385), 0.6, 1.);
}
// VoxelÌÊ
float bumpFunc(vec3 p, vec3 n) {
n = abs(n);
vec2 uv = n.x > 0.5 ? p.yz : n.y > 0.5 ? p.xz : p.xy;
vec2 ID = floor(uv);
uv = fract(uv) - 0.5;
if(hash12(ID) < 0.5) {
uv.y = -uv.y;
}
if(uv.y < -uv.x) {
uv = -uv.yx;
}
float d = abs(length(uv - 0.5) - 0.5);
const float w = 0.15;
float tmp = w * w - d * d;
float res = tmp > 0. ? sqrt(tmp) : 0.;
return -res;
}
// ref: "Maze Lattice" by Shane
// https://www.shadertoy.com/view/llGGzh
// VoxelÌÊxNg
vec3 bumpMap(vec3 p, vec3 n) {
const vec2 e = vec2(.002, 0);
float ref = bumpFunc(p, n);
vec3 grad = (vec3(bumpFunc(p - e.xyy, n),
bumpFunc(p - e.yxy, n),
bumpFunc(p - e.yyx, n)) - ref) / e.x;
grad -= n * dot(n, grad);
return normalize(n + grad * bumpFactor);
}
// vðV¸ÆµÄÉÀWphi, theta¾¯ñ]³¹½xNg
// ¦øvÍ·³1ÌxNgÅ éKv è
vec3 jitter(vec3 v, float phi, float sinTheta, float cosTheta) {
vec3 xAxis = normalize(cross(v.yzx, v));
vec3 yAxis = cross(v, xAxis);
vec3 zAxis = v;
return (xAxis * cos(phi) + yAxis * sin(phi)) * sinTheta + zAxis * cosTheta;
}
// ref: "pXg[VO - Computer Graphics - memoRANDOM" by Shocker_0x15
// (Japanese article)
// https://rayspace.xyz/CG/contents/path_tracing/
// ref: "GLSL smallpt" by Zavie
// https://www.shadertoy.com/view/4sfGDB
// ref: "[SESSIONS] Syobon's Lobby" by Kamoshika (myself)
// https://www.shadertoy.com/view/ctt3zX
// pXg[VOžçêéF
vec3 pathTrace(vec3 ro, vec3 rd) {
vec3 acc = vec3(0);
vec3 mask = vec3(1);
// ÅÉJ©çCðòη
float t = castRay(ro, rd, 100);
if(t < 0.) { // Cª¨ÌÉÕ˵ȩÁ½
return vec3(0.);
}
ro += t * rd; // CÌ´_ð¨Ì\ÊÜÅißé
vec3 f = ro - voxelPos - 0.5;
vec3 n = normalize(f * pow(f, vec3(8.)));
vec3 bump = bumpMap(ro, voxelNormal);
n = normalize(n + bump);
// ¦{ÍTv̪¾¯J©çCðòη½ßAnumSamplesð©¯é
acc += mask * emission() * float(numSamples);
mask *= objColor();
// ÉA¨Ì\Ê©ç_ÉCðòη
vec3 ro0 = ro + n * EPS;
vec3 n0 = n;
vec3 mask0 = mask;
for(int i = 0; i < numSamples; i++) {
ro = ro0;
n = n0;
mask = mask0;
for(int depth = 1; depth < maxDepth; depth++) {
float ur = random(); // êl
// d_ITvOðs¤½ßA¼
Êàcosªzðgp·é
rd = jitter(n, random() * PI2, sqrt(1. - ur), sqrt(ur)); // ÌCÌûü
t = castRay(ro, rd, 15);
if(t < 0.) { // Cª¨ÌÉÕ˵ȩÁ½
break;
}
ro += t * rd;
f = ro - voxelPos - 0.5;
n = normalize(f * pow(f, vec3(8.)));
bump = bumpMap(ro, voxelNormal);
n = normalize(n + bump);
acc += mask * emission();
mask *= objColor();
ro += n * EPS; // »Ý̨Ì\Êðð¯é½ßÉAµ©¹é
}
}
acc /= float(numSamples);
acc = clamp(acc, 0., 1.);
return acc;
}
void main() {
vec2 uv = (gl_FragCoord.xy * 2. - resolution) / min(resolution.x, resolution.y); // ÀW̳K»
camPos = vec3(0.5, 7.5, -time * 2.); // JÌÊuiCÌ´_j
vec3 dir = normalize(vec3(0.5, -0.3, -1)); // JÌü«
dir.xy *= rotate2D(time * 0.2);
vec3 side = normalize(cross(dir, vec3(0, 1, 0)));
vec3 up = cross(side, dir);
vec3 rd = normalize(uv.x * side + uv.y * up + dir / tan(FOV / 360. * PI)); // CÌü«
// pXg[VOÅg¤ÌV[hðú»·é
float T = fract(time / 10.) * 500.;
pathSeed = hash12(gl_FragCoord.xy * PI) * 500.;
pathSeed += hash12(gl_FragCoord.xy + pathSeed + T) * 500.;
// pXg[VOð·é
vec3 col = vec3(0);
col += pathTrace(camPos, rd);
col = pow(col, vec3(1. / 2.2)); // K}â³
// pXg[VOžçê½F̪Uðḷé½ßÉOÌt[ÌFð¬·é
float tex = 0.6;
col = mix(col, texture(backbuffer, gl_FragCoord.xy / resolution).rgb, tex);
outColor = vec4(col, 1.);
}