[連載 GLSL 物語] チカラが欲しいか……（第三話）

## 権造さん

左「バカなッ！」

左利きは思わず叫んだ。

見たこともないほどのたけのこの軍勢に、反射的に、悲鳴のような叫び声を上げてしまったのだ。

プレッツェルがガクガクと震え、歩くこともできない。

一歩、また一歩と確実に近づいてくるたけのこの軍勢……

左利きは、さっきまで自暴自棄になっていたことも忘れ、ただただ恐怖に身を震わせていた。

たけのこ隊の隊長「ムッ！？」

軍勢を率いて陣頭指揮に立っていたたけのこ軍の隊長は、草原の片隅で腰が抜けて動けなくなっているきのこを見つけ号令を掛けた。

隊長「全軍、止まれ！」

```glsl
precision mediump float;
uniform vec2  resolution;
uniform vec2  mouse;
uniform float time;
uniform sampler2D backbuffer;

struct Intersect{
	float dist;
	vec3  color;
};
const float PI = 3.1415926;
const float PI2 = PI * 2.0;
const vec3 cColor = vec3(0.25, 0.1, 0.0);
const vec3 fColor = vec3(0.2, 0.7, 0.1);
const vec3 kColor = vec3(0.9, 0.8, 0.4);

float waveline(vec2 p){
	float f = smoothstep(0.975, 1.0, cos(p.x * 2.5) - (p.y * 2.5) - 0.75);
	float g = smoothstep(0.975, 1.0, cos(p.x * PI + PI) - (p.y * 4.0) - 0.5);
	float h = smoothstep(0.975, 1.0, cos(p.x * 2.5) - (p.y * 2.5) + 0.5);
	return mix(mix(mix(0.0, 0.3, h), 0.4, g), 0.6, f);
}
float dChoco(vec3 p){
	float e = 1.0 - p.y * 0.7;
	float w = waveline(vec2(atan(p.z, p.x) / PI, p.y + 0.25)) * 0.15;
	return length((p + vec3(0.0, -0.5, 0.0)) * vec3(e, 0.5, e)) + p.y - 0.5 + step(p.y, -1.2) - w;
}
float dFloor(vec3 p){
	return dot(p, vec3(0.0, 1.0, 0.0)) + 1.5;
}
float dCylinder(vec3 p, vec2 r){
	vec2 d = abs(vec2(length(p.xz), p.y)) - r;
	return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - 0.25 + step(-1.0, p.y);
}
Intersect distanceHub(vec3 p){
	vec3 q = vec3(0.0);
	if(p.z < -6.0){
		q = vec3(mod(p, 3.0) - 1.5);
	}else{
		q = p;
	}
	q = vec3(q.x, p.y, q.z);
	float choco = dChoco(q);
	float flor = dFloor(q);
	float cylin = dCylinder(q, vec2(0.5, 2.0));
	Intersect i;
	i.dist = min(cylin, min(choco, flor));
	i.color = cylin < min(choco, flor) ? kColor : choco < flor ? cColor : fColor;
	return i;
}
vec3 genNormal(vec3 p){
	float d = 0.001;
	return normalize(vec3(
		distanceHub(p + vec3(  d, 0.0, 0.0)).dist - distanceHub(p + vec3( -d, 0.0, 0.0)).dist,
		distanceHub(p + vec3(0.0,   d, 0.0)).dist - distanceHub(p + vec3(0.0,  -d, 0.0)).dist,
		distanceHub(p + vec3(0.0, 0.0,   d)).dist - distanceHub(p + vec3(0.0, 0.0,  -d)).dist
	));
}

void main(){
	vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
	vec3 cPos = vec3(0.0,  3.0, 12.0);
	vec3 cDir = vec3(0.0,  0.0, -1.0);
	vec3 cUp  = vec3(0.0,  1.0,  0.0);
	vec3 cSide = cross(cDir, cUp);
	float targetDepth = 2.0;
	vec3 ray = normalize(cSide * p.x + cUp * p.y + cDir * targetDepth);

	float dist = 0.0;
	float rLen = 0.0;
	vec3  rPos = cPos;
	Intersect intersect;
	for(int i = 0; i < 512; ++i){
		intersect = distanceHub(rPos);
		rLen += intersect.dist * 0.3;
		rPos = cPos + ray * rLen;
	}

	if(abs(intersect.dist) < 0.001){
		float fog = smoothstep(0.0, 100.0, length(rPos - cPos));
		vec3  normal = genNormal(rPos);
		vec3  light = normalize(vec3(mouse + 2.0, 3.0));
		float diff = max(dot(normal, light), 0.1);
		vec3  eye = reflect(normalize(rPos - cPos), normal);
		float speculer = clamp(dot(eye, light), 0.0, 1.0);
		vec3 specColor = pow(speculer, 20.0) + cColor;
		gl_FragColor = vec4(vec3(diff) * intersect.color + fog * vec3(3.0, 1.0, 0.8) + specColor, 1.0);
	}else{
		float s = 0.1 / length(p - vec2(0.0, 0.75));
		gl_FragColor = vec4(vec3(1.0) - pow(min(s, 1.0), 2.0) * vec3(0.0, 0.65, 0.85), 1.0);
	}
}
```

（[GLSL Editor](https://doxas.org/work/glsl_editor/) に貼り付けて実行してね！）

![takenoco_02.jpg](https://qiita-image-store.s3.amazonaws.com/0/39638/6e62b0ae-82e5-d804-a19f-d545a2a9b9c3.jpeg)

左「あ……ああああ……」

隊長「なんだ貴様は？ 軍人ではなさそうだが……きのこの国の一般市民か」

左利きは恐怖に震え言葉を発することができずにいた。喉から出てくるのは、呻きのような情けない声ばかりだ。

隊長「軍人ではないきのこを手に掛けるのは気が引けるな。おい貴様、今すぐどこかへ消えるのなら見逃してやる。さっさと…………む？」

隊長は高圧的な喋り方でなにかを言いかけていたが、左利きの顔をまじまじと正面から見つめた。

隊長「貴様……どこかで以前に会ったか？」

左利きは突然の問いかけに、なんとか声を捻り出して答えた。

左「う……な、なんのことだ。お……おれにはたけのこの知り合いなど居ない！（きのこにも友達はいないけど……）」

左利きのどこか悲痛な訴えを聞いても、たけのこ隊長は視線を左利きの顔からそらさない。

隊長「うーむ、貴様、父親の名はなんという。まさかとは思うが、お前の父親は『たけのこ狩り名人の権造さん』ではないか？」

たけのこ狩り名人の権造――

それは、大乱の英雄であり、対たけのこ戦において無敵の強さを誇ったと言われる伝説の戦士……間違いなく、左利きの実の父の名だった。突然に父の名を語られ、左利きの顔には驚きの色がありありと浮かび上がる。

隊長「やはり……どうりで、あの男に似ているはずだ……」

たけのこ隊長はどこかさみしげな、遠い目になって続けた。

隊長「貴様個人に恨みは無いが、権造さんの倅ならば、ここで殺しておかなければ後々やっかいなことになるかもしれん。覚悟してもらおう」

左利きが状況を把握できていないうちに隊長は勝手に話を進めていく！

```glsl
precision mediump float;
uniform vec2  resolution;
uniform vec2  mouse;
uniform float time;
uniform sampler2D backbuffer;

struct Intersect{
	float dist;
	vec3  color;
};
const float PI = 3.1415926;
const float PI2 = PI * 2.0;
const vec3 cColor = vec3(0.3, 0.1, 0.0);
const vec3 kColor = vec3(1.0, 0.8, 0.4);

float rnd(vec2 n) {
	return fract(sin(dot(n, vec2(12.9898, 4.1414))) * 43758.5453);
}
float noise(vec2 p){
	vec2 v = floor(p);
	vec2 u = fract(p);
	u = u * u * (3.0 - 2.0 * u);
	float r = mix(
		mix(rnd(v), rnd(v + vec2(1.0, 0.0)), u.x),
		mix(rnd(v + vec2(0.0, 1.0)), rnd(v + vec2(1.0, 1.0)), u.x),
		u.y
	);
	return r * r;
}
float snoise(vec2 p){
	float n = 0.0;
	for(float i = 0.0; i < 6.0; ++i){
		float v = pow(2.0, 2.0 + i);
		float w = pow(2.0, -1.0 - i);
		n += noise(p * v) * w;
	}
	return n;
}
float seamless(vec2 p, vec2 q, vec2 r){
	return snoise(vec2(p.x,	   p.y	  )) *		q.x  *		q.y  +
		   snoise(vec2(p.x,	   p.y + r.y)) *		q.x  * (1.0 - q.y) +
		   snoise(vec2(p.x + r.x, p.y	  )) * (1.0 - q.x) *		q.y  +
		   snoise(vec2(p.x + r.x, p.y + r.y)) * (1.0 - q.x) * (1.0 - q.y);
}
float waveline(vec2 p){
	float f = smoothstep(0.975, 1.0, cos(p.x * 2.5) - (p.y * 2.5) - 0.75);
	float g = smoothstep(0.975, 1.0, cos(p.x * PI + PI) - (p.y * 4.0) - 0.5);
	float h = smoothstep(0.975, 1.0, cos(p.x * 2.5) - (p.y * 2.5) + 0.5);
	return mix(mix(mix(0.0, 0.3, h), 0.6, g), 0.9, f);
}
float dChoco(vec3 p){
	float rad = -time * 0.2;
	mat3 m = mat3(cos(rad), 0.0, -sin(rad), 0.0, 1.0, 0.0, sin(rad), 0.0, cos(rad));
	vec3 q = m * p;
	float e = 1.0 - p.y * 0.7;
	float w = waveline(vec2(atan(q.z, q.x) / PI, p.y + 0.25)) * 0.15;
	return length((q + vec3(0.0, -0.5, 0.0)) * vec3(e, 0.5, e)) + p.y - 0.5 + step(p.y, -1.2) * 2.0 - w;
}
float dCylinder(vec3 p, vec2 r){
	vec2 d = abs(vec2(length(p.xz), p.y)) - r;
	return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - 0.25 + step(-1.0, p.y) + step(p.y, -1.5);
}
Intersect distanceHub(vec3 p){
	float choco = dChoco(p);
	float cylin = dCylinder(p, vec2(0.5, 2.0));
	Intersect i;
	i.dist = min(cylin, choco);
	i.color = choco < cylin ? cColor : kColor;
	return i;
}
vec3 genNormal(vec3 p){
	float d = 0.001;
	return normalize(vec3(
		distanceHub(p + vec3(  d, 0.0, 0.0)).dist - distanceHub(p + vec3( -d, 0.0, 0.0)).dist,
		distanceHub(p + vec3(0.0,   d, 0.0)).dist - distanceHub(p + vec3(0.0,  -d, 0.0)).dist,
		distanceHub(p + vec3(0.0, 0.0,   d)).dist - distanceHub(p + vec3(0.0, 0.0,  -d)).dist
	));
}

void main(){
	vec2 p = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
	vec3 cPos = vec3(0.0,  0.0,  3.0);
	vec3 cDir = vec3(0.0,  0.0, -1.0);
	vec3 cUp  = vec3(0.0,  1.0,  0.0);
	vec3 cSide = cross(cDir, cUp);
	float targetDepth = 1.0;
	vec3 ray = normalize(cSide * p.x + cUp * p.y + cDir * targetDepth);

	float dist = 0.0;
	float rLen = 0.0;
	vec3  rPos = cPos;
	Intersect intersect;
	for(int i = 0; i < 256; ++i){
		intersect = distanceHub(rPos);
		rLen += intersect.dist * 0.2;
		rPos = cPos + ray * rLen;
	}

	if(abs(intersect.dist) < 0.001){
		float fog = smoothstep(0.0, 15.0, length(rPos - cPos));
		vec3  normal = genNormal(rPos);
		vec3  light = normalize(vec3(mouse + 2.0, 3.0));
		float diff = max(dot(normal, light), 0.05);
		vec3  eye = reflect(normalize(rPos - cPos), normal);
		float speculer = clamp(dot(eye, light), 0.0, 1.0);
		vec3 specColor = pow(speculer, 20.0) + cColor;
		gl_FragColor = vec4(vec3(diff) * intersect.color - fog + specColor, 1.0);
	}else{
		vec2 c = gl_FragCoord.xy / resolution;
		vec2 t = vec2(c.s, c.t - (3.0 / resolution.y));
		vec4 texColor = texture2D(backbuffer, t);
		float n = seamless(p - vec2(0.0, time), p / 2.0, vec2(2.0));
		float f = min(0.1 / abs(p.x), 1.0) * min(1.0 - p.y, 1.0);
		vec3 fire = texColor.rgb * (vec3(1.0, 0.3, 0.0) + pow(n * 3.0, 2.0)) * 0.95 * f;
		gl_FragColor = vec4(fire, 1.0);
	}
}
```

（[GLSL Editor](https://doxas.org/work/glsl_editor/) に貼り付けて実行してね！）

![takenoco_03.jpg](https://qiita-image-store.s3.amazonaws.com/0/39638/95d5a4d5-4d15-2bf6-1e31-b370bce0ed64.jpeg)

隊長「部下たちには手出しはさせん。俺が直々に貴様を葬ってやろう」

左「まさか……あんた親父となにか因縁があるのか！」

隊長「問答無用！」

メラメラと燃え上がる闘志を身にまとったたけのこ隊長が左利きに襲いかかる！

to be continued!

※この物語はフィクションです。実在の人物や団体などとは関係ありません。