概要
wsl(wsl2じゃない)で、elixirやってみた。
Livebookで、kinoshadertoy書いてみた。
写真
setup
Mix.install([
{:kino, "~> 0.19"}
])
サンプルコード
defmodule KinoShadertoy do
use Kino.JS
use Kino.JS.Live
def new(shader_code) do
Kino.JS.Live.new(__MODULE__, shader_code)
end
@impl true
def init(shader_code, ctx) do
{:ok, assign(ctx, shader_code: shader_code)}
end
@impl true
def handle_connect(ctx) do
{:ok, ctx.assigns.shader_code, ctx}
end
asset "main.js" do
~S"""
export function init(ctx, shaderCode) {
function showError(title, details) {
ctx.root.innerHTML = `
<div style="background: #fee2e2; color: #991b1b; padding: 1rem; border-radius: 0.5rem; font-family: monospace; font-size: 14px; white-space: pre-wrap; border: 1px solid #fca5a5;">
<strong style="font-size: 16px;">${title}</strong><br/><br/>${details}
</div>
`;
}
const canvas = document.createElement("canvas");
canvas.width = 600;
canvas.height = 400;
const gl = canvas.getContext("webgl2");
if (!gl)
{
showError("WebGL 2 Not Supported", "Your browser or system does not support WebGL2.");
return;
}
const vsSource = `#version 300 es
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`;
const cleanShaderCode = shaderCode.replace(/\r\n/g, "\n");
const fsSource = `#version 300 es
precision highp float;
out vec4 fragColor;
uniform vec3 iResolution;
uniform float iTime;
${cleanShaderCode}
void main() {
mainImage(fragColor, gl_FragCoord.xy);
}
`;
function createShader(gl, type, source, shaderTypeName) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
{
const log = gl.getShaderInfoLog(shader);
showError(`GLSL Compilation Error (${shaderTypeName})`, log);
gl.deleteShader(shader);
return null;
}
return shader;
}
const vs = createShader(gl, gl.VERTEX_SHADER, vsSource, "Vertex Shader");
if (!vs)
return;
const fs = createShader(gl, gl.FRAGMENT_SHADER, fsSource, "Fragment Shader");
if (!fs)
return;
const program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS))
{
showError("Shader Program Link Error", gl.getProgramInfoLog(program));
return;
}
ctx.root.appendChild(canvas);
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1,
]), gl.STATIC_DRAW);
const positionLocation = gl.getAttribLocation(program, "position");
const iResolutionLocation = gl.getUniformLocation(program, "iResolution");
const iTimeLocation = gl.getUniformLocation(program, "iTime");
let startTime = performance.now();
function render() {
const time = (performance.now() - startTime) * 0.001;
gl.viewport(0, 0, canvas.width, canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.useProgram(program);
gl.uniform3f(iResolutionLocation, canvas.width, canvas.height, 1.0);
gl.uniform1f(iTimeLocation, time);
gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
requestAnimationFrame(render);
}
render();
}
"""
end
end
shader_code = """
precision mediump float;
//uniform vec3 iResolution;
//uniform float iTime;
uniform int iFrame;
#define SHAPE length(z.yz)
#define HIGH_QUAL
#ifdef HIGH_QUAL
#define MARCH_STEPS 199
#else
#define MARCH_STEPS 99
#endif
float k;// = 7.0 + 3.0 * sin(iTime * 0.15);
vec3 mcol = vec3(0.0);
void AbsBox(inout vec4 z) {
z.xyz = abs(z.xyz + 1.0) - 1.0;
z *= 1.5 / clamp(dot(z.xyz, z.xyz), 0.25, 1.0);
}
void Bulb(inout vec4 z, in vec4 c) {
k = 7.0 + 3.0 * sin(iTime * 0.15);
float r = length(z.xyz);
float zo = asin(z.z / r) * k + iTime * 0.15;
float zi = atan(z.y, z.x) * 7.0;
z = pow(r, k - 1.0) * vec4(r * vec3(cos(zo) * vec2(cos(zi), sin(zi)), sin(zo)), z.w * k) + c;
}
float DE(vec3 p) {
vec4 c = vec4(p, 1.0),
z = c;
Bulb(z, c);
float r0 = (length(z.xyz) - 1.15) / z.w;
z.xyz -= 1.0;
for (int i = 0; i < 7; i++) AbsBox(z);
float r = SHAPE;
mcol.rgb = vec3(1.0, 0.5, 0.2) + abs(sin(0.2 * r + 100.0 * z.yxz / z.w));
return 0.5 * max((r - 1.0) / z.w, -r0);
}
vec3 sky(vec3 rd, vec3 L) {
float d = 0.4 * dot(rd, L) + 0.6;
rd.y += sin(sqrt(clamp(-rd.y, 0.0, 0.9)) * 90.0) * 0.45 * max(-0.1, rd.y);
rd = abs(rd);
float y = max(0., L.y),
sun = max(1. - (1. + 10. * y + rd.y) * length(rd - L), 0.) + .3 * pow(1. - rd.y, 12.) * (1.6 - y);
return d * mix(vec3(0.3984, 0.5117, 0.7305), vec3(0.7031, 0.4687, 0.1055), sun) *((.5 + pow(y, .4)) * (1.5 - abs(L.y)) + pow(sun, 5.2) * y * (5. + 15.0 * y));
}
float rnd;
void randomize(in vec2 p) {
rnd = fract(float(iFrame) + sin(dot(p, vec2(13.3145, 117.7391))) * 42317.7654321);
}
vec3 scene(vec3 ro, vec3 rd) {
vec3 L = normalize(vec3(0.4, 0.025, 0.5));
vec3 bcol = sky(rd, L);
vec4 col = vec4(0.0);
float t = DE(ro) * rnd,
d,
od = 1.0,
px = 1.0 / iResolution.x;
ro += rd * t;
for (int i = 0; i < MARCH_STEPS; i++)
{
d = DE(ro);
if (d < px * t)
{
float dif = clamp(1.0 - d / od, 0.2, 1.0);
vec3 scol = mcol * dif * (1.3 - 0.3 * t);
#ifdef HIGH_QUAL
vec2 s = vec2(DE(ro + d * 4.0 * L), DE(ro + d * 16.0 * L));
scol *= clamp(0.5 * s.x / d + (s.y / d) / 8.0, 0.0, 1.0);
#endif
float alpha = (1.0 - col.w) * clamp(1.0 - d / (px * t), 0.0, 1.0);
col += vec4(clamp(scol, 0.0, 1.0), 1.0) * alpha;
if (col.w > 0.9) break;
}
t += d;
ro += rd * d;
od = d;
if (t > 6.0) break;
}
col.rgb += bcol * (1.0 - clamp(col.w, 0.0, 1.0));
return col.rgb;
}
mat3 lookat(vec3 fw) {
fw = normalize(fw);
vec3 rt = normalize(cross(fw, vec3(0.0, 1.0, 0.0)));
return mat3(rt, cross(rt, fw), fw);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
randomize(fragCoord);
float tim = iTime * 0.3,
r = 2.0 + cos(tim * 0.7);
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.x;
vec3 ro = vec3(sin(tim) * r, sin(tim * 0.4), cos(tim) * r);
vec3 rd = lookat(-ro) * normalize(vec3(uv, 1.0));
fragColor = vec4(scene(ro, rd) * 2.0, 1.0);
}
"""
KinoShadertoy.new(shader_code)
以上。
