All entries

No still was sent with this entry.

Source

"use typeshade";

class Uniforms {
  time: f32
  resolution: vec2
  mouse: vec2
}

declare const u: uniform<Uniforms>;

// 전역 상태 변수 모방 (BP는 main에서 계산 후 할당)
let BP: vec2 = vec2(0.0);

const W: i32 = 5;

function bx(p: vec2, b: vec2): f32 {
  const d: vec2 = abs(p) - b;
  return length(max(d, vec2(0.0))) + min(max(d.x, d.y), 0.0);
}

function rt(a: f32): mat2 {
  const c: f32 = cos(a);
  const s: f32 = sin(a);
  return mat2(c, -s, s, c);
}

function S(p: vec2): f32 {
  return min(-bx(p, vec2(1.9, 0.85)),
         min(bx(p - BP, vec2(0.14, 0.35)),
         min(bx(rt(0.6) * (p - vec2(0.05, -0.7)), vec2(0.15)),
             bx(rt(-0.3) * (p - vec2(0.9, 0.1)), vec2(0.22, 0.05)))));
}

function G(p: vec2): f32 {
  const k: f32 = 1.7320508;
  let p_mod: vec2 = p - vec2(-0.6, -0.05);
  p_mod.x = abs(p_mod.x) - 0.3;
  p_mod.y = p_mod.y + (0.3 / k);
  if (p_mod.x + k * p_mod.y > 0.0) {
    p_mod = vec2(p_mod.x - k * p_mod.y, -k * p_mod.x - p_mod.y) / 2.0;
  }
  p_mod.x = p_mod.x - clamp(p_mod.x, -0.6, 0.0);
  return -length(p_mod) * sign(p_mod.y);
}

function gs(x: f32, m: f32, a: f32, b: f32): f32 {
  const denom: f32 = x < m ? a : b;
  const x_norm: f32 = (x - m) / denom;
  return exp(-0.5 * x_norm * x_norm);
}

function spec(l: f32): vec3 {
  const c: vec3 = vec3(
    1.056 * gs(l, 599.8, 37.9, 31.0) + 0.362 * gs(l, 442.0, 16.0, 26.7) - 0.065 * gs(l, 501.1, 20.4, 26.2),
    0.821 * gs(l, 568.8, 46.9, 40.5) + 0.286 * gs(l, 530.9, 16.3, 31.1),
    1.217 * gs(l, 437.0, 11.8, 36.0) + 0.681 * gs(l, 459.0, 26.0, 13.8)
  );
  
  // GLSL mat3 행/열 순서 1:1 매칭 변환
  const m3: mat3 = mat3(
     3.2406, -1.5372, -0.4986,
    -0.9689,  1.8758,  0.0415,
     0.0557, -0.204,   1.057
  );
  return max(m3 * c, vec3(0.0));
}

@fragment
export function main(@location(0) uv: vec2): vec4 {
  const res: vec2 = u.resolution;
  // 정규화 화면 좌표 p 구성
  const p: vec2 = ((uv * u.resolution) - 0.5 * res) / res.y * 2.0;
  const e: vec2 = vec2(0.002, 0.0);
  const E: vec2 = vec2(-1.7, -0.28);

  // 마우스 인터랙션 바인딩 및 가두기
  if (u.mouse.x > 0.0) {
    BP = clamp(((u.mouse * u.resolution) - 0.5 * res) / res.y * 2.0, vec2(-1.72, -0.49), vec2(1.72, 0.49));
  } else {
    BP = vec2(0.85, -0.5);
  }

  const t: f32 = u.time;
  const d: f32 = S(p);
  const sol: f32 = step(d, 0.0);
  let h: f32 = 0.0;
  let k: f32 = 0.0;
  const a: f32 = 0.32 + 0.05 * sin(t * 0.4);

  const N: vec2 = normalize(vec2(
    S(p + e) - S(p - e),
    S(p + vec2(e.y, e.x)) - S(p - vec2(e.y, e.x))
  ));
  const q: vec2 = p + sol * N * (0.03 - d);
  let C: vec3 = vec3(0.01);
  let B: vec3 = vec3(0.0);

  // 프리즘 스펙트럼 광선 분산 루프
  for (let i: i32 = 0; i < W; i++) {
    const lam: f32 = 400.0 + 300.0 * (f32(i) + 0.5) / f32(W);
    const l: f32 = lam * 0.001;
    const nu: f32 = 1.5 + 0.03 / (l * l);
    let fl: f32 = 1.0;
    let cum: f32 = 0.0;
    const col: vec3 = spec(lam) * 2.8 / f32(W);
    
    let o: vec2 = E;
    let r: vec2 = vec2(cos(a), sin(a));
    let x: vec2 = o;
    let g: vec2 = vec2(0.0);
    let in_refract: bool = false;
    let hit: i32 = 0;

    // 바운스 레이 트레이싱 루프
    for (let s: i32 = 0; s < 8; s++) {
      k = 0.0;
      for (let j: i32 = 0; j < 48; j++) {
        x = o + r * k;
        h = in_refract ? -G(x) : min(G(x), S(x));
        if (h < 0.0015 || k > 5.0) { break; }
        k = k + h;
      }
      if (h > 0.02 || k > 5.0) { break; }
      
      const ni: f32 = in_refract ? nu : 1.0;
      const pa: vec2 = p - o;
      const u_dot: f32 = clamp(dot(pa, r), 0.0, k);
      const dv: vec2 = pa - r * u_dot;
      
      B = B + col * fl * 0.0002 / (dot(dv, dv) + 0.0002);
      
      for (let j: i32 = 0; j < 1; j++) {
        const sp: f32 = fract(t * 0.22 + f32(j) * 0.5) * 7.0 - cum;
        if (sp > 0.0 && sp < k * ni) {
          const pp: vec2 = p - o - r * sp / ni;
          B = B + col * fl * 0.0008 / (dot(pp, pp) + 0.0008) * 4.0;
        }
      }
      cum = cum + k * ni;

      if (in_refract || G(x) < S(x)) {
        g = normalize(vec2(
          G(x + e) - G(x - e),
          G(x + vec2(e.y, e.x)) - G(x - vec2(e.y, e.x))
        ));
        if (in_refract) { g = -g; }
        
        let f: vec2 = refract(r, g, in_refract ? nu : 1.0 / nu);
        if (dot(f, f) < 0.5) {
          f = reflect(r, g);
        } else {
          in_refract = !in_refract;
        }
        r = f;
        o = x + r * 0.006;
      } else {
        g = normalize(vec2(
          S(x + e) - S(x - e),
          S(x + vec2(e.y, e.x)) - S(x - vec2(e.y, e.x))
        ));
        B = B + col * fl * 0.0012 / (dot(p - x, p - x) + 0.0012) * 2.0;
        
        if (hit < 3) {
          const v_dir: vec2 = x + g * 0.03 - q;
          const D: f32 = length(v_dir) + 0.0001;
          let vis: f32 = 1.0;
          let tt: f32 = 0.012;
          const v_norm: vec2 = v_dir / D;
          
          for (let j: i32 = 0; j < 14; j++) {
            h = S(q + v_norm * tt);
            vis = min(vis, D * h / (0.05 * tt));
            tt = tt + clamp(h, 0.015, 0.2);
            if (vis < 0.01 || tt > D - 0.03) { break; }
          }
          
          C = C + col * fl * clamp(vis, 0.0, 1.0) * max(dot(g, -v_norm), 0.0)
               * mix(1.0, max(dot(N, v_norm), 0.0), sol) * mix(0.5, 1.0, sol) * 0.25 / (D + 0.2);
        }
        r = reflect(r, g);
        o = x + g * 0.006;
        fl = fl * 0.55;
        hit = hit + 1;
      }
    }
  }

  const gl: f32 = G(p);
  C = C * mix(1.0, 0.9 * exp(4.0 * d), sol);
  C = C + vec3(0.5, 0.7, 1.0) * (0.03 * step(gl, 0.0) + 0.08 * exp(-abs(gl) * 80.0)) + B
     + 0.3 * 0.0004 / (dot(p - E, p - E) + 0.0004);

  return vec4(pow(1.0 - exp(-2.0 * C), vec3(0.4545)), 1.0);
}