scene.org File Archive

File download

<root>­/­parties­/­2023­/­sessions23­/­glsl/kamoshika_glsl_-12.txt

File size:
24 555 bytes (23.98K)
File date:
2023-04-30 12:51:04
Download count:
all-time: 209

Preview

precision highp float;
uniform vec2 resolution;
uniform vec2 mouse;
uniform float time;
uniform sampler2D backbuffer;
out vec4 outColor;

#define FC gl_FragCoord
#define o outColor

// v‚ðPI/4.‚¾‚¯‰ñ“]
#define rotpi4(v) (vec2((v).x + (v).y, -(v).x + (v).y) / sqrt(2.))

// ƒIƒuƒWƒFƒNƒg‚ÌŽí—Þ
#define FLOOR   1
#define WALL    2
#define CEILING 3
#define SYOBON  4

// •Ç‚Ì”z’uiƒ}ƒbƒvj‚́A‘Ίpü‚ÌTruchet tiles‚ðƒx[ƒX‚Æ‚µ‚Ä‚¢‚é
// ‚·‚ׂĂ̕ǂ̖@üƒxƒNƒgƒ‹‚Í(}1, 0, }1)‚Ì‚SŽí—Þ
// ¦ƒ~ƒjƒ}ƒbƒv‚Í45‹ŒX‚¢‚Ä‚¨‚èAŽÎ‚߉E‰º‚ªxŽ²‚̐³•ûŒüAŽÎ‚ߍ¶‰º‚ªzŽ²‚̐³•ûŒü‚Æ‚È‚Á‚Ä‚¢‚é

// •Çiƒ^ƒCƒ‹j‚ÌŽí—Þ
// Žl‹÷‚Ì‚Ý
#define FOURCORNERS   11.0
// Žl‹÷‚ƑΊpü(•½–Êx=z)
#define DIAGPLUS      12.0
// Žl‹÷‚ƑΊpü(•½–Êx=-z)
#define DIAGMINUS     13.0

//---------------GPU•‰‰×‚ÌŒˆ’è—vˆö--------------------------
const int numSamples = 50;  // ƒpƒXƒgƒŒ[ƒVƒ“ƒO‚̃Tƒ“ƒvƒ‹”
//----------------------------------------------------------

const int maxDepth = 3;     // ƒŒƒC‚ÌŒo˜H‚̐[‚³‚̍őå’li”½ŽË‰ñ”+1‚Ì’lj

const float sceneTime1 = 5.;  // ‰æ–ʃmƒCƒY‚̏I—¹ŽžŠÔ
const float sceneTime2 = 8.;  // ƒOƒŠƒbƒ`‚̏I—¹ŽžŠÔ
const float sceneTime3 = 10.; // HUD‚ÌŠJŽnŽžŠÔ
const float sceneTime4 = 11.; // ƒ^ƒCƒ}[AƒvƒŒƒCƒ„[iƒJƒƒ‰j‚̈ړ®‚ÌŠJŽnŽžŠÔ
const float sceneTime5 = 14.; // ƒVƒ‡ƒ{ƒ“‚̒ǐՂ̊JŽnŽžŠÔ

const float PI = acos(-1.); // ‰~Žü—¦
const float PI2 = acos(-1.) * 2.;

const float EPS = 0.0001;           // ƒŒƒCƒgƒŒ[ƒX‚È‚Ç‚ÉŽg‚¤”÷¬—Ê
const float FOV = 60. / 360. * PI;  // Ž‹–ìŠp(ƒ‰ƒWƒAƒ“) ”͈́F(0./360.*PI, 180./360.*PI)
const float wallRate = 0.7;         // •Ç‚ÌŠ„‡@”͈́F[0., 1.]
const float wallWidth = 0.1;        // •Ç‚ÌŒú‚³@”͈́F(0., 0.5)
const float mapSeed = 5.;           // ƒ}ƒbƒv¶¬‚ÉŽg‚¤—”‚̃V[ƒh
const float ceilHeight = 0.5;       // “Vˆä‚̍‚‚³
const float damageRate = 0.01;      // 1ƒtƒŒ[ƒ€‚ ‚½‚è‚̃_ƒ[ƒW—Ê
const float enemySize = 0.2;        // ƒVƒ‡ƒ{ƒ“i“Gj‚Ì‘å‚«‚³i‹…‚Ì”¼Œaj
vec3 enemyPos;                      // ƒVƒ‡ƒ{ƒ“‚̈ʒu
float enemyDirA;                    // ƒVƒ‡ƒ{ƒ“‚ÌŒü‚«(ƒ‰ƒWƒAƒ“)
float pathSeed = 0.;                // ƒpƒXƒgƒŒ[ƒVƒ“ƒO‚ÅŽg‚¤—”‚̃V[ƒh

// 1D‚Ì—”
float hash11(float p)
{
  return fract(sin(p) * 43758.5453);
}

// 2D‚Ì—”
float hash12(vec2 p)
{
  return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}

// 1D‚Ì—”(ƒV[ƒh‚ðXV)
float random() {
  return hash11(pathSeed++);
}

// 2ŽŸŒ³‚̉ñ“]s—ñ
mat2 rotate2D(float a) {
  float s = sin(a), c = cos(a);
  return mat2(c, s, -s, c);
}

const float segScale = 0.03; // ƒZƒOƒƒ“ƒg”Žš‚Ì‘å‚«‚³
// ”Žš‚̃ZƒOƒƒ“ƒg‚Ì‹——£ŠÖ”
float sdSeg(vec2 p) {
  p = abs(p);
  return max(p.x - segScale / 3., p.x + p.y - segScale);
}

// "0"‚Ì‹——£ŠÖ”
float sd0(vec2 p) {
  p = abs(p);
  p.y -= segScale;
  p = p.y > p.x ? p.yx : p;
  p.x -= segScale;
  return sdSeg(p);
}

// "1"‚Ì‹——£ŠÖ”
float sd1(vec2 p) {
  p.y = abs(p.y);
  p -= segScale;
  return sdSeg(p);
}

// "2"‚Ì‹——£ŠÖ”
float sd2(vec2 p) {
  p = p.y < 0. ? -p : p;
  p.y = abs(p.y - segScale);
  p = p.y > p.x ? p.yx : p;
  p.x -= segScale;
  return sdSeg(p);
}

// "3"‚Ì‹——£ŠÖ”
float sd3(vec2 p) {
  p.y = abs(abs(p.y) - segScale);
  p = p.y > p.x ? p.yx : p;
  p.x -= segScale;
  return sdSeg(p);
}

// "4"‚Ì‹——£ŠÖ”
float sd4(vec2 p) {
  float d = sdSeg(p - vec2(-segScale, segScale));
  p.y = abs(p.y) - segScale;
  p = p.y < -p.x ? -p.yx : p;
  p.x -= segScale;
  return min(d, sdSeg(p));
}

// "5"‚Ì‹——£ŠÖ”
float sd5(vec2 p) {
  p = p.y < 0. ? -p : p;
  p.y = abs(p.y - segScale);
  p = p.y > -p.x ? -p.yx : p;
  p.x += segScale;
  return sdSeg(p);
}

// "6"‚Ì‹——£ŠÖ”
float sd6(vec2 p) {
  float d = sdSeg(p - vec2(segScale, -segScale));
  p.y = abs(abs(p.y) - segScale);
  p = p.y > -p.x ? -p.yx : p;
  p.x += segScale;
  return min(d, sdSeg(p));
}

// "7"‚Ì‹——£ŠÖ”
float sd7(vec2 p) {
  float d = sdSeg(p - vec2(segScale, -segScale));
  p.y -= segScale;
  p = p.y > p.x ? p.yx : p;
  p.x -= segScale;
  return min(d, sdSeg(p));
}

// "8"‚Ì‹——£ŠÖ”
float sd8(vec2 p) {
  p = abs(p);
  p.y = abs(p.y - segScale);
  p = p.y > p.x ? p.yx : p;
  p.x -= segScale;
  return sdSeg(p);
}

// "9"‚Ì‹——£ŠÖ”
float sd9(vec2 p) {
  float d = sdSeg(p - vec2(-segScale, segScale));
  p.y = abs(abs(p.y) - segScale);
  p = p.y > p.x ? p.yx : p;
  p.x -= segScale;
  return min(d, sdSeg(p));
}

// ”ŽšNi0`9j‚Ì‹——£ŠÖ”
float sdDigit(vec2 p, int N) {
  if(N == 0) return sd0(p);
  if(N == 1) return sd1(p);
  if(N == 2) return sd2(p);
  if(N == 3) return sd3(p);
  if(N == 4) return sd4(p);
  if(N == 5) return sd5(p);
  if(N == 6) return sd6(p);
  if(N == 7) return sd7(p);
  if(N == 8) return sd8(p);
  if(N == 9) return sd9(p);
  return 1e5;
}

// ƒ^ƒCƒ}[‚̐F
vec3 colTimer(vec3 col, vec2 uv, vec2 pos, float size, float Time) {
  uv -= pos;
  uv /= size;
  vec2 uvp = uv;
  uv.x -= uv.y * 0.2;
  
  float segGap = 0.002;
  float interval = 0.09;
  float interval2 = interval * 1.3;
  float ra = 0.01;
  
  float hundredths = fract(Time) * 100.;
  float seconds = mod(Time, 60.);
  float minutes = mod(Time / 60., 60.);
  float hours = mod(Time / 3600., 100.);
  
  int digit1 = int(mod(hundredths, 10.));
  int digit2 = int(hundredths / 10.);
  int digit3 = int(mod(seconds, 10.));
  int digit4 = int(seconds / 10.);
  int digit5 = int(mod(minutes, 10.));
  int digit6 = int(minutes / 10.);
  int digit7 = int(mod(hours, 10.));
  int digit8 = int(hours / 10.);
  
  float d = length(uvp - vec2(-0.159, -0.06)) - ra; // ƒsƒŠƒIƒh
  uvp -= vec2(-interval * 5.1, 0);
  uvp.x -= sign(uvp.y) * 0.007;
  uvp = abs(uvp) - vec2(interval * 1.15, 0.035);
  d = min(d, length(uvp) - ra); // ƒRƒƒ“
  
  vec2 uv12 = (uv - vec2(-0.036, -0.021)) * 1.5;
  d = min(d, sdDigit(uv12, digit1));
  
  uv.x += interval;
  uv12.x += interval;
  d = min(d, sdDigit(uv12, digit2));
  
  uv.x += interval2;
  d = min(d, sdDigit(uv, digit3));
  
  uv.x += interval;
  d = min(d, sdDigit(uv, digit4));
  
  uv.x += interval2;
  d = min(d, sdDigit(uv, digit5));
  
  uv.x += interval;
  d = min(d, sdDigit(uv, digit6));
  
  uv.x += interval2;
  d = min(d, sdDigit(uv, digit7));
  
  uv.x += interval;
  d = min(d, sdDigit(uv, digit8));
  
  col = mix(vec3(0.2), col, smoothstep(0.009, 0.0091, d + segGap)); // ˜g
  col += smoothstep(0.001, 0., d + segGap);
  
  return col;
}

const float serifThin = 4.; // •¶Žš‚̃ZƒŠƒt‚ׂ̍³
// •¶Žš‚̃ZƒŠƒt‚Ì‹——£ŠÖ”
float sdSerif(vec2 p, vec2 pos, float width, float height) {
  p -= pos;
  p.x = abs(p.x);
  float d = p.x - exp(-p.y * p.y * serifThin) - width;
  d = max(d, -p.y);
  return max(d, p.y - height);
}

// "Y"‚Ì‹——£ŠÖ”
float sdCY(vec2 p) {
  float d = sdSerif(p, vec2(0, -5.), 0.7, 4.5);
  p.y = -p.y;
  vec2 q = p;
  q.x += q.y * 0.6 - 0.8;
  d = min(d, sdSerif(q, vec2(0, -5.), 0.45, 6.));
  q = p;
  q.x -= q.y * 0.6 - 0.5;
  return min(d, sdSerif(q, vec2(0, -5.), 0.7, 5.8));
}

// "O"‚Ì‹——£ŠÖ”
float sdCO(vec2 p) {
  p.x *= 1.1;
  float d = length(p) - 5.;
  p.x *= 1.4;
  return max(d, 4.5 - length(p));
}

// "U"‚Ì‹——£ŠÖ”
float sdCU(vec2 p) {
  vec2 q = p;
  q.y += 2.2;
  q.y *= 1.2;
  float r1 = 3.7;
  float r2 = 2.6;
  float of = 0.25;
  q.y -= max(q.y, 0.);
  float d = length(q) - r1;
  q.x -= of;
  d = max(d, r2 - length(q));
  d = max(d, p.y - 5.);
  q = p;
  q.y = 5. - q.y;
  float temp = of - r2;
  d = min(d, sdSerif(q, vec2((temp - r1) * 0.5, 0), (temp + r1) * 0.5, 3.));
  temp = of + r2;
  return min(d, sdSerif(q, vec2((r1 + temp) * 0.5, 0), (r1 - temp) * 0.5, 3.));
}

// "D"‚Ì‹——£ŠÖ”
float sdCD(vec2 p) {
  p.y = abs(p.y);
  vec2 q = p;
  q.x -= min(q.x, 0.2);
  float d = length(q) - 5.;
  d = max(d, -p.x - 2.5);
  q = p;
  q.x += 1.;
  d = max(d, 4.5 - length(q));
  q.y = 5. - q.y;
  return min(d, sdSerif(q, vec2(-1.7, 0), 0.7, 6.));
}

// "I"‚Ì‹——£ŠÖ”
float sdCI(vec2 p) {
  p.y = 5. - abs(p.y);
  return sdSerif(p, vec2(0, 0), 0.7, 6.);
}

// "E"‚Ì‹——£ŠÖ”
float sdCE(vec2 p) {
  vec2 q = vec2(p.y, -p.x);
  float d = sdSerif(q, vec2(0.5, -4.), 0.42, 6.);
  q = p;
  q.y = 5. - abs(q.y);
  d = min(d, sdSerif(q, vec2(-1.5, 0), 0.7, 6.));
  q = vec2(p.y - 5., 4.5 - p.x);
  q.y -= q.x *.1;
  d = min(d, sdSerif(q, vec2(0, 0), 0.7, 6.));
  q = vec2(p.y + 5., 4.5 - p.x);
  q.y += q.x *.3;
  d = min(d, sdSerif(q, vec2(0, 0), 0.7, 6.));
  return max(d, abs(p.y) - 5.);
}

// Ž€–SŽž‚̃ƒbƒZ[ƒW‚̐F
vec3 colDeathMsg(vec3 col, vec2 uv, vec2 pos, float size) {
  uv -= pos;
  uv /= size * 0.05;
  uv.x += 30.;
  
  float d = sdCY(uv); // Y
  uv.x -= 10.;
  d = min(d, sdCO(uv)); // O
  uv.x -= 10.5;
  d = min(d, sdCU(uv)); // U
  uv.x -= 14.5;
  d = min(d, sdCD(uv)); // D
  uv.x -= 8.5;
  d = min(d, sdCI(uv)); // I
  uv.x -= 6.5;
  d = min(d, sdCE(uv)); // E
  uv.x -= 11.;
  d = min(d, sdCD(uv)); // D
  
  col *= mix(0.2, 1., smoothstep(5., 14., abs(uv.y)));
  col = mix(vec3(0.7,0.,0.) + col, col, smoothstep(0., 0.01, d));
  
  return col;
}

// •Ç‚ÌŽí—Þ
vec2 wallType(vec2 ID) {
  float hash = hash12(ID + mapSeed * PI);
  
  if(hash > wallRate) {
    return vec2(FOURCORNERS, 1.);
  }
  
  if(hash > wallRate * 0.5) {
    return vec2(DIAGPLUS, 1.);
  }
  
  return vec2(DIAGMINUS, -1.);
}

// •Ç‚Ì‹——£ŠÖ”i2ŽŸŒ³j
// ‘Ίpü‚ÌTruchet tiles‚ðƒx[ƒX‚Æ‚µ‚Ä‚¢‚é
float sdWall(vec2 p) {
  vec2 ID = floor(p);
  vec2 q = fract(p) - 0.5;
  vec2 wType = wallType(ID);
  
  q.y *= wType.y;
  
  if(wType.x != FOURCORNERS) {
    // DIAGMINUS or DIAGPLUS
    return (0.5 - wallWidth - abs(fract(q.y - q.x) - 0.5)) / sqrt(2.);
  }
  
  // FOURCORNERS
  q = abs(q);
  return (1. - q.x - q.y - wallWidth) / sqrt(2.);
}

// •Ç‚Ì–@üƒxƒNƒgƒ‹i2ŽŸŒ³j
vec2 wallNormal(vec2 p) {
  vec2 e = vec2(0, 0.1);
  return normalize(vec2(sdWall(p + e.yx) - sdWall(p - e.yx),
                        sdWall(p + e.xy) - sdWall(p - e.xy)));
}

// ‰æ–ʏã‚̃~ƒjƒ}ƒbƒv‚̐F
vec3 colMiniMap(vec3 col, vec2 uv, vec2 pos, float size, vec2 ro, float dirA) {
  uv -= pos;
  uv /= size;
  
  float L = length(uv);
  float R = 0.4;
  
  col = mix(vec3(0.5), col, smoothstep(0., 0.02, abs(L - R))); // ŠÛ‚¢˜g
  if(L < R) {
    vec3 mapCol = vec3(0.7, 0.7, 0.9);
    uv.y = -uv.y;
    uv = rotpi4(uv); // PI/4.‚¾‚¯‰ñ“]
    uv *= 15.;
    
    col = mix(vec3(1.), col * mapCol, smoothstep(0., 0.01, sdWall(uv + ro))); // •Ç
     
    col =  mix(vec3(1, 1, 0), col, smoothstep(0.2, 0.25, length(uv))); // ƒvƒŒƒCƒ„[
    
    vec2 q = uv * rotate2D(dirA);
    float width = 0.02;
    float a = abs(atan(q.y, q.x));
    if(a < FOV + width && col.x < 0.99) {
      float cFOV = smoothstep(1., 6., length(q)) * smoothstep(0., width, abs(a - FOV));
      col = mix(vec3(0.6, 0.6, 0), col, cFOV); // Ž‹ŠE
    }
  }
  
  return col;
}

// HPƒo[‚̐F
vec3 colHPBar(vec3 col, vec2 uv, vec2 pos, vec2 size, float HP) {
  uv -= pos;
  
  vec2 uvb = uv;
  uv = abs(uv) - size;
  uvb /= size * 2.;
  uvb += 0.5;
  
  float d = max(uv.x, uv.y);
  
  if(d < 0.) {
    col = mix(vec3(0), col, 0.4);
    if(uvb.x < HP) {
      col = vec3(1, .0, .0);
      col += uvb.y * 1.5 - 0.75;
    }
  }
  
  col = mix(vec3(0.8), col, smoothstep(0., 0.01, abs(d))); // ˜g
  
  return col;
}

// HUD‚̐F
vec3 colHUD(vec3 col, vec2 uv, vec2 ro, float dirA, float HP) {
    col = colMiniMap(col, uv, vec2(0.8, -0.55), 1., ro, dirA); // ƒ~ƒjƒ}ƒbƒv‚ð•\Ž¦
    col = colHPBar(col, uv, vec2(0, 0.85), vec2(0.8, 0.05),  HP); // HPƒo[‚ð•\Ž¦
    
    float Time = max(time - sceneTime4, 0.);
    col = colTimer(col, uv, vec2(1.18, -0.03), 1., Time); // ƒ^ƒCƒ}[‚ð•\Ž¦
    
    if(HP >= damageRate) { // ¶‚«‚Ä‚¢‚éŠÔ‚ÍŽ€–SŽž‚̃ƒbƒZ[ƒW‚ð•\Ž¦‚µ‚È‚¢
      return col;
    }
    
    return colDeathMsg(col, uv, vec2(0, 0.4), 0.6); // Ž€–SŽž‚̃ƒbƒZ[ƒW‚ð•\Ž¦
}

// •¨‘Ì‚Ì•\–ʏã‚̍À•Wrp‚É‚¨‚¯‚é–@üƒxƒNƒgƒ‹
vec3 objNormal(vec3 rp, out int objType) {
  vec3 n = vec3(0, 1, 0);
  
  if(rp.y > ceilHeight - EPS) {
    objType = CEILING;
    return vec3(0, -1, 0);
  }
  if(rp.y < EPS) {
    objType = FLOOR;
    return vec3(0, 1, 0);
  }
  
  vec3 ep = rp - enemyPos;
  if(dot(ep, ep) < (enemySize + EPS) * (enemySize + EPS)) {
    objType = SYOBON;
    return ep / enemySize; // ‹…‘Ì‚È‚Ì‚Å”¼Œa‚ÅŠ„‚é‚Æ–@üƒxƒNƒgƒ‹‚É‚È‚é
  }
  
  vec2 frp = fract(rp.xz) - 0.5;
  vec2 ID = floor(rp.xz);
  vec2 wType = wallType(ID);
  
  objType = WALL;
  n.y = 0.;
  n.xz = -sign(frp) / sqrt(2.);
  
  if(wType.x == FOURCORNERS) {
    return n;
  }
  
  // DIAGMINUS or DIAGPLUS
  float temp = frp.y - wType.y * frp.x;
  float signNormal = sign(temp);
  n.xz = (abs(temp) < wallWidth + EPS) ? vec2(-wType.y * signNormal, signNormal) / sqrt(2.) : n.xz;
  
  return n;
}

// ƒŒƒC‚Æ‹…‚Ì•\–Ê‚ÌŒð·”»’èŠÖ”
// ref: sphIntersect() by iq
// https://iquilezles.org/articles/intersectors/
float sphIntersect(vec3 ro, vec3 rd, vec3 ce, float ra) {
  vec3 oc = ro - ce;
  float e = dot(oc, rd);
  float c = dot(oc, oc) - ra * ra;
  float h = e * e - c;
  if(h < 0.) return -1.0;
  return -e - sqrt(h);
}

// ƒGƒ~ƒbƒVƒ‡ƒ“‚̐F
vec3 emission(vec3 rp, int objType, out float ceilPattern) {
  if(objType != CEILING) { // ŒõŒ¹‚Í“Vˆä‚Ì‚Ý
    return vec3(0);
  }
  
  // ŒõŒ¹‚̃^ƒCƒ‹‚Ì’[‚ð•Ç‚ɍ‡‚킹‚é
  vec2 uv = rp.xz;
  uv /= wallWidth * sqrt(2.);
  uv = rotpi4(uv);
  uv -= 0.5;
  
  float hash = hash12(floor(uv));
  uv = abs(fract(uv) - 0.5);
  
  ceilPattern = smoothstep(0.5, 0.43, max(uv.x, uv.y));
  if(fract(hash + time * 0.05) < 0.05) { // “_–Å
    return vec3(ceilPattern * 10.);
  }
  
  return vec3(0);
}

// ƒVƒ‡ƒ{ƒ“‚Ì–Ú‚Ì‹——£ŠÖ”
float sdSyobonEye(vec2 p, vec2 pos, float radius) {
  p.x = abs(p.x);
  return length(p - pos) - radius;
}

// ƒVƒ‡ƒ{ƒ“‚Ì”û–Ñ‚Ì‹——£ŠÖ”
float sdSyobonEyebrow(vec2 p, vec2 pos, float L, float angle) {
  p.x = abs(p.x);
  p -= pos;
  p *= rotate2D(angle);
  p.x -= clamp(p.x, -L, L);
  return length(p);
}

// ƒVƒ‡ƒ{ƒ“‚ÌŒû‚Ì‹——£ŠÖ”
// reference: "Arc - exact" by iq
// https://iquilezles.org/articles/distfunctions2d/
float sdSyobonMouth(vec2 p, float posY, float radius) {
  p.x = abs(p.x);
  p.y -= posY;
  p *= rotate2D(0.5);
  
  float a = 1.1;
  float s = sin(a);
  float c = cos(a);
  vec2 cp = vec2(s, c) * radius;
  p -= vec2(cp.x, -cp.y);
  
  p.x = abs(p.x);
  
  if(p.y * s > p.x * c) {
    return length(p - cp);
  }
  return abs(length(p) - radius);
}

// ƒVƒ‡ƒ{ƒ“‚ÌŠç‚Ì‹——£ŠÖ”
float sdSyobon(vec2 p) {
  float d;
  d = sdSyobonEye(p, vec2(0.5, 0), 0.06);
  d = min(d, sdSyobonEyebrow(p, vec2(0.8, 0.3), 0.13, -0.5));
  return min(d, sdSyobonMouth(p, -0.3, 0.1));
}

// •¨‘̂̐F
vec3 objColor(vec3 rp, int objType, float ceilPattern) {
  if(objType == FLOOR) { // °
    return vec3(0.7, 0.4, 0.2);
  }
  
  if(objType == CEILING) { // “Vˆä
    return vec3(mix(0.5, 1.0, ceilPattern));
  }
  
  if(objType == WALL) { // •Ç
    if(abs(rp.y - ceilHeight * 0.5) > ceilHeight * 0.47) {
      return vec3(1.0);
    }
    rp.x = abs(fract(rp.x / wallWidth * 5. - 0.5) - 0.5); // c‚ÌŽÈ–Í—l‚É‚·‚é
    return mix(vec3(0.95), vec3(0.1, 0.6, 0.3), smoothstep(0., 0.2, rp.x));
  }
  
  vec3 p = rp - enemyPos;
  if(objType == SYOBON) { // ƒVƒ‡ƒ{ƒ“
    p.xz *= rotate2D(enemyDirA);
    if(p.x < 0.) {
      return vec3(1);
    }
    vec2 uv = p.zy;
    uv *= 1.3 / enemySize;
    float d = sdSyobon(uv);
    return vec3(smoothstep(0.05, 0.1, d));
  }
  
  return vec3(0);
}

// v‚ð“V’¸‚Æ‚µ‚ċɍÀ•Wphi, theta‚¾‚¯‰ñ“]‚³‚¹‚½ƒxƒNƒgƒ‹
// ¦ˆø”v‚Í’·‚³1‚̃xƒNƒgƒ‹‚Å‚ ‚é•K—v‚ ‚è
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;
}

// ro‚©‚畨‘Ì•\–Ê‚Ü‚Å”ò‚΂µ‚½ƒŒƒC‚Ì’·‚³
// 2D‚ÌVoxel Traversal‚ðƒx[ƒX‚Æ‚µ‚Ä‚¢‚é
float castRay(vec3 ro, vec3 rd, const int itr) {
  float t = 0.; // ƒŒƒC‚Ì’·‚³
  vec2 ri = 1.0 / rd.xz;
  vec2 rs = sign(rd.xz);
  
  // •Ç‚𖳎‹‚µ‚½ƒŒƒC‚Ì’·‚³
  float tLimit = (rd.y > 0.) ? (ceilHeight - ro.y) : -ro.y;
  tLimit = (rd.y == 0.) ? 1e5 : tLimit / rd.y; // “Vˆä‚Ü‚½‚͏°‚܂ł̃ŒƒC‚Ì’·‚³
  
  // ƒVƒ‡ƒ{ƒ“‚܂ł̃ŒƒC‚Ì’·‚³
  float tEnemy = sphIntersect(ro, rd, enemyPos, enemySize);
  tLimit = (tEnemy > 0.) ? min(tLimit, tEnemy) : tLimit; // ƒŒƒC‚ªƒVƒ‡ƒ{ƒ“‚ɏՓ˂µ‚½‚çtLimit‚ðXV
  
  vec2 ID = floor(ro.xz); // ƒZƒ‹‚ÌIDiƒŒƒC‚̍À•W‚̐®”•”•ªj
  
  for(int i = 0; i < itr; i++) {
    vec2 rp = ro.xz + rd.xz * t;  // ƒŒƒC‚̐æ’[‚̍À•W
    vec2 wType = wallType(ID);    // •Ç‚ÌŽí—Þ
    vec2 frp = rp - ID - 0.5;     // ƒŒƒC‚̐æ’[‚̍À•W‚̏¬”•”•ª-0.5
    float tWall = 1e5;            // ŽŸ‚̕ǂ܂ł̃ŒƒC‚Ì’·‚³
    
    vec2 v = (0.5 * rs - frp) * ri; // ŽŸ‚̃Zƒ‹‚Ì‹«ŠE‚܂ł̃ŒƒC‚Ì’·‚³(X•ûŒü‚ÆZ•ûŒü)
    vec2 vCell = vec2(step(v.x, v.y), step(v.y, v.x)); // X•ûŒü‚ÆZ•ûŒü‚Ì‚¤‚¿A‚Ç‚¿‚ç‚̃Zƒ‹‚ɐi‚Þ‚©
    float tCell = dot(v, vCell); // ŽŸ‚̃Zƒ‹‚Ì‹«ŠE‚܂ł̃ŒƒC‚Ì’·‚³iv.x or v.yj
    
    vec2 normal = -sign(frp + tCell * rd.xz); // ’·‚³‚ðŒvŽZŽ®‚É”½‰fÏ‚Ý‚Ì‚½‚ßnormalize()‚Í•s—v
    float tWallFourCorners = (wallWidth - 1. - dot(frp, normal)) / dot(rd.xz, normal); // Žl‹÷‚̕ǂ܂ł̃ŒƒC‚Ì’·‚³
    tWall = (tWallFourCorners > 0.) ? tWallFourCorners : tWall; // tWall‚ðXV‚·‚é
    
    float signNormal = sign(frp.y - wType.y * frp.x);
    normal = vec2(-wType.y * signNormal, signNormal); // ’·‚³‚ðŒvŽZŽ®‚É”½‰fÏ‚Ý‚Ì‚½‚ßnormalize()‚Í•s—v
    float tWallDiag = (wallWidth - dot(frp, normal)) / dot(rd.xz, normal); // ‘Ίpü‚̕ǂ܂ł̃ŒƒC‚Ì’·‚³
    tWall = (tWallDiag > 0. && wType.x != FOURCORNERS) ? min(tWall, tWallDiag) : tWall; // •Ç‚ÌŽí—Þ‚ª‘Ίp‚̏ꍇ‚ÍtWall‚ðXV‚·‚é
    
    if(tWall < tCell) { // ŽŸ‚̃Zƒ‹‚Ì‹«ŠE‚æ‚è‚àA•Ç‚Ì•û‚ª‹ß‚¢
      return min(t + tWall, tLimit);
    }
    
    // ŽŸ‚̃Zƒ‹‚Ì‹«ŠE‚ɐi‚Þ
    t += tCell;
    ID += vCell * rs;
    
    if(t >= tLimit) { // ŽŸ‚̃Zƒ‹‚Ì‹«ŠE‚æ‚è‚àA“Vˆä or ° or ƒVƒ‡ƒ{ƒ“‚Ì•û‚ª‹ß‚¢
      return tLimit;
    }
  }
  
  return -1.0;
}

// ref: "ƒpƒXƒgƒŒ[ƒVƒ“ƒO - 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
// ¦ŽQl‚É‚Í‚µ‚Ä‚¢‚邪ƒR[ƒh‚͑啝‚ɈقȂé

// ƒpƒXƒgƒŒ[ƒVƒ“ƒO‚Å“¾‚ç‚ê‚éF
vec3 pathTrace(vec3 ro, vec3 rd) {
  vec3 acc = vec3(0);
  vec3 mask = vec3(1);
  
  // Å‰‚ɃJƒƒ‰‚©‚烌ƒC‚ð”ò‚΂·
  float t = castRay(ro, rd, 20);
  if(t < 0.) { // ƒŒƒC‚ª•¨‘̂ɏՓ˂µ‚È‚©‚Á‚½
    return vec3(0.1, 0.6, 0.3) * 0.1;
  }
  ro += t * rd; // ƒŒƒC‚ÌŒ´“_‚𕨑̕\–ʂ܂Ői‚ß‚é
  int objType;
  float ceilPattern;
  vec3 n = objNormal(ro, objType);
  // ¦–{—ˆ‚̓Tƒ“ƒvƒ‹”‚Ì•ª‚¾‚¯ƒJƒƒ‰‚©‚烌ƒC‚ð”ò‚΂·‚½‚߁AnumSamples‚ð‚©‚¯‚é
  acc += mask * emission(ro, objType, ceilPattern) * float(numSamples);
  mask *= objColor(ro, objType, ceilPattern);
  
  // ŽŸ‚Ɂ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“_“IƒTƒ“ƒvƒŠƒ“ƒO‚ðs‚¤‚½‚߁A”¼‹…–Ê“àcos•ª•z‚ðŽg—p‚·‚é
      rd = jitter(n, random() * PI2, sqrt(1. - ur), sqrt(ur)); // ŽŸ‚̃ŒƒC‚Ì•ûŒü
      
      t = castRay(ro, rd, 4);
      if(t < 0.) { // ƒŒƒC‚ª•¨‘̂ɏՓ˂µ‚È‚©‚Á‚½
        break;
      }
      ro += t * rd;
      n = objNormal(ro, objType);
      acc += mask * emission(ro, objType, ceilPattern);
      mask *= objColor(ro, objType, ceilPattern);
      ro += n * EPS; // Œ»Ý‚Ì•¨‘Ì•\–Ê‚ð”ð‚¯‚é‚½‚߂ɁA­‚µ•‚‚©‚¹‚é
    }
  }
  
  acc /= float(numSamples);
  acc = clamp(acc, 0., 1.);
  
  return acc;
}

// ƒJƒƒ‰‚̈ʒu‚ÆŒü‚«‚ð‰Šú‰»EXV‚·‚é
void doCamera(in vec2 uv, out vec3 ro, out float dirA, out vec3 dir, out vec3 rd) {
  // ƒJƒƒ‰‚̍À•W‚ð‰Šú‰»
  ro = vec3(wallWidth + 0.2, ceilHeight * 0.5, 0.);
  
  // ƒJƒƒ‰‚ÌŒü‚«‚ð‰Šú‰»
  dir.y = 0.;
  dirA = 0.;
  dir.xz = vec2(cos(dirA), sin(dirA));
  
  if(time > sceneTime4) {
    // ƒJƒƒ‰‚ÌŒü‚«‚ðƒ}ƒEƒX‚Å•Ï‚¦‚é
    dir.y = (mouse.y - 0.5) * 2.;
    dirA = mouse.x * PI2 * 4.;
    dir.xz = vec2(cos(dirA), sin(dirA));
    dir = normalize(dir);
    
    // ‘OƒtƒŒ[ƒ€‚̃Jƒƒ‰‚̈ʒu‚ðŽæ“¾
    ro.xz = texture(backbuffer, vec2(0.5, 0.5) / resolution).rg;
    
    // ƒJƒƒ‰‚ðˆÚ“®‚³‚¹‚é
    vec2 v = dir.xz;
    vec2 n = wallNormal(ro.xz);
    float d = sdWall(ro.xz);
    if(d < 0.) { // •Ç‚É–„‚Ü‚Á‚Ä‚¢‚é‚Ì‚Å–@ü•ûŒü‚ɐi‚ñ‚Å”²‚¯o‚·
      v = n;
    } else if(d < 0.15 && dot(v, n) < 0.) { // •Ç‚É‹ß‚¢ê‡A•Ç‚Æ•½s‚É‚µ‚©i‚ß‚È‚¢‚悤‚É‚·‚é
      vec2 nv = vec2(n.y, -n.x);
      v = nv * dot(nv, v);
    }
    ro.xz += v * 0.01;
    ro.y += abs(sin(fract(time * 4.) * PI) * 0.01); // ƒvƒŒƒCƒ„[‚ª‘–‚Á‚Ä‚¢‚é‚悤‚ɁAƒJƒƒ‰‚ðã‰º‚É—h‚ç‚·
  }
  
  // ‰æ–Ê‚ÌuvÀ•W‚ƃJƒƒ‰‚ÌŒü‚«‚©‚烌ƒCƒxƒNƒgƒ‹‚ð‹‚ß‚é
  vec3 side = normalize(cross(dir, vec3(0, 1, 0)));
  vec3 up = cross(side, dir);
  rd = normalize(vec3(uv.x * side + uv.y * up + dir / tan(FOV)));
}

// ƒVƒ‡ƒ{ƒ“‚̈ʒu‚ÆŒü‚«‚ð‰Šú‰»EXV‚·‚é
void doEnemy() {
  enemyPos.y = abs(sin(time * 3.)) * 0.1 + enemySize; // °‚̏ã‚Å’µ‚Ë‚é
  enemyPos.xz = vec2(1e5);
  enemyDirA = 0.;
  if(time > sceneTime5) { // ƒvƒŒƒCƒ„[‚ð’ǐÕ
    vec3 texE = texture(backbuffer, vec2(200.5, 0.5) / resolution).rgb; // ƒvƒŒƒCƒ„[‚̉ߋŽ‚̈ʒuEŒü‚«‚ðŽæ“¾
    enemyPos.xz = texE.rg;
    enemyDirA = texE.b;
  }
}

// uv‚ð‚¸‚ç‚µ‚ăOƒŠƒbƒ`
vec2 glitch(vec2 uv, float rate) {
  float T = fract(time / 10.) * 10.;
  float vx = hash12(vec2(floor(uv.y * 20.), T));
  float vy = hash12(vec2(floor(uv.y * 20.), T + 500.));
  float v = floor(hash12(vec2(floor(uv.y * 6. - T * 3.))) * 6.) - 2.5;
  vec2 shift = vec2(vx, vy) * v / 2.5;
  uv -= shift * rate;
  return uv;
}

void main(){
  vec2 uv = (FC.xy * 2. - resolution) / min(resolution.x, resolution.y); // À•W‚̐³‹K‰»
  
  // HP‚ª0‚É‚È‚Á‚½‚ç‰æ–Ê‚ð’âŽ~‚µ‚āA‘S‚Ă̏ˆ—‚ðI—¹‚·‚é
  float HP = 1.; // HP‚ð‰Šú‰»
  if(time > sceneTime5) {
    //HP = texture2D(backbuffer, vec2(0.5, 1.5) / resolution).a;
    HP = texture(backbuffer, (resolution / 2. + 0.5) / resolution).a;
    if(HP < damageRate) {
      outColor = texture(backbuffer, FC.xy / resolution);
      return;
    }
  }
  
  float T = fract(time / 10.) * 500.;
  // Å‰‚ÌŽžŠÔ‚͉æ–ʂɃmƒCƒY‚ð•\Ž¦‚·‚é
  if(time + hash11(T) * 2.5 < sceneTime1) {
    vec2 i = floor(uv * 100.);
    i += hash12(i) * 500.;
    o = vec4(vec3(hash12(i + T)), 1.);
    return;
  }
  // ƒOƒŠƒbƒ`‚̃GƒtƒFƒNƒg‚ð‚©‚¯‚é
  uv = glitch(uv, smoothstep(sceneTime2 + EPS, sceneTime1, time));
  
  vec3 ro;  // ƒJƒƒ‰‚̈ʒuiƒŒƒC‚ÌŒ´“_j
  vec3 rd;  // ƒŒƒC‚ÌŒü‚«
  vec3 dir; // ƒJƒƒ‰‚ÌŒü‚«
  float dirA; // ƒJƒƒ‰‚ÌŒü‚«iXZ•½–Ê“à‚Å‚ÌŠp“xiƒ‰ƒWƒAƒ“jj
  doCamera(uv, ro, dirA, dir, rd); // ƒJƒƒ‰‚̈ʒuEŒü‚«‚ð‰Šú‰»EXV‚·‚é
  doEnemy(); // ƒVƒ‡ƒ{ƒ“‚̈ʒuEŒü‚«‚ð‰Šú‰»EXV‚·‚é
  
  // ƒpƒXƒgƒŒ[ƒVƒ“ƒO‚ÅŽg‚¤—”‚̃V[ƒh‚ð‰Šú‰»‚·‚é
  pathSeed = hash12(FC.xy * PI) * 500.;
  pathSeed += hash12(FC.xy + pathSeed + T) * 500.;
  
  // ƒpƒXƒgƒŒ[ƒVƒ“ƒO‚ð‚·‚é
  vec3 col = vec3(0);
  col = pathTrace(ro, rd);
  col = pow(col, vec3(1. / 2.2)); // ƒKƒ“ƒ}•â³
  
  // ƒpƒXƒgƒŒ[ƒVƒ“ƒO‚Å“¾‚ç‚ꂽF‚Ì•ªŽU‚ð’ጸ‚·‚邽‚ß‚É‘O‚̃tƒŒ[ƒ€‚̐F‚ð‡¬‚·‚é
  float tex = 0.7;
  col *= 1. - tex;
  col += texture(backbuffer, FC.xy / resolution).rgb * tex;
  
  // ƒ_ƒ[ƒW‚̏ˆ—
  if(time > sceneTime5) {
     // “G‚ɐڋ߂·‚é‚ƃ_ƒ[ƒW‚ðŽó‚¯‚é
    if(length(enemyPos - ro) < enemySize + 0.2) {
      HP = max(HP - damageRate, 0.); // HPŒ¸­
      col = mix(col, vec3(1, 0, 0), 0.15); // ƒ_ƒ[ƒWƒGƒtƒFƒNƒg
    }
  }
  
  // HUD‚ð•\Ž¦
  if(time > sceneTime3) {
    col = colHUD(col, uv, ro.xz, dirA, HP);
  }
  
  col = clamp(col, 0., 1.);
  o.rgb = col;
  
  // ƒvƒŒƒCƒ„[iƒJƒƒ‰j‚̈ʒuEŒü‚«EHP‚ð•Û‘¶
  if(FC.y < 0.6) {
    if(FC.x < 0.6) {
      o.rgb = vec3(ro.xz, dirA); // ƒvƒŒƒCƒ„[‚̈ʒu‚ÆŒü‚«‚ð•Û‘¶
    } else {
      // ƒvƒŒƒCƒ„[‚̉ߋŽ‚̏ó‘Ô‚ðƒVƒ‡ƒ{ƒ“‚̏ó‘Ô‚Æ‚µ‚ÄŽg‚¤‚½‚߁A‚¸‚炵‚ĉߋŽ‚̏ó‘Ô‚ð•ÛŽ‚·‚é
      o.rgb = texture(backbuffer, vec2(FC.x - 1., 0.5) / resolution).rgb;
    }
  }
  //if(FC.x < 0.6 && FC.y > 0.6 && FC.y < 1.6) {
  o.a = HP; // HP‚ð•Û‘¶
  //}
}