noe.sh/-~-/pfp/index.html

337 lines
8.7 KiB
HTML

<!DOCTYPE html>
<title>noe pfp generator</title>
<link rel="preconnect" href="https://fonts.bunny.net" />
<link
href="https://fonts.bunny.net/css?family=atkinson-hyperlegible:400,400i,700,700i"
rel="stylesheet"
/>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/3.4.2/gl-matrix-min.js"
referrerpolicy="no-referrer"
></script>
<style>
:root {
font-family: "Atkinson Hyperlegible", sans-serif;
background-color: black;
color: #efefef;
}
a {
color: gold;
}
section {
padding: 2em;
}
#error {
color: pink;
}
#draw {
border: 3px solid grey;
}
.num {
width: 4em;
}
</style>
<main>
<p id="error"></p>
<noscript>this tool uses webgl, so js must be enabled. sorry!</noscript>
<section>
albedo <input type="file" id="albedo" accept="image/*" /><br />
x power
<input type="number" class="num" id="x-pow" value="1" step="0.1" /> y power
<input type="number" class="num" id="y-pow" value="1" step="0.1" /><br />
<button id="run-frame">Run Frame</button>
</section>
<section>
<canvas width="256" height="256" id="draw" />
</section>
<section>
<p>This tool is what it uses to generate its profile pictures.</p>
<p>
In short, it takes the "albedo" texture that you upload, applies the "<a
href="/-~-/pfp/noegram.png"
>noegram </a
>" pattern over the top, where the overlay switches to a random UV offset
per line, causing a glitch-like effect.
</p>
<p>
<img
src="/-~-/pfp/noegram.png"
id="noegram"
width="256"
height="256"
alt="noegram"
/>
</p>
</section>
</main>
<script type="text/x-vertex-shader" id="vertexShader">
in vec4 a_vertex;
in vec2 a_uv0;
uniform mat4 u_model_view;
uniform mat4 u_projection;
out vec2 uv0;
void main() {
gl_Position = u_projection * u_model_view * a_vertex;
uv0 = a_uv0;
}
</script>
<script type="text/x-fragment-shader" id="fragmentShader">
uniform vec2 u_noe_power;
uniform vec2 u_noise_offset;
uniform sampler2D u_texture_0; // albedo
uniform sampler2D u_texture_1; // noegram
uniform sampler2D u_texture_2; // noise
in vec2 uv0;
out vec4 fragColor;
vec2 sawtooth(vec2 inp) {
return 2.0 * (inp - floor(0.5 + inp));
}
void main() {
vec4 noegram = texture(u_texture_1, uv0);
vec4 albedo = texture(u_texture_0, uv0 / abs(sin(sawtooth(uv0) * noegram.x)));
fragColor = vec4(albedo.rgb, 1.0);
}
</script>
<script defer async>
(() => {
const { mat4, vec2 } = glMatrix;
const xPowEl = document.querySelector("#x-pow");
const yPowEl = document.querySelector("#y-pow");
const getPower = () => {
return [xPowEl.value, yPowEl.value];
};
const canvasEl = document.querySelector("#draw");
const albedoEl = document.querySelector("#albedo");
const renderEl = document.querySelector("#run-frame");
const gl = canvasEl.getContext("webgl2");
if (gl === null) {
document.querySelector("#error").innerHTML = "WebGL2 context failed.";
}
const makeTexture = async (blob, texture) => {
const image = await createImageBitmap(blob, {
imageOrientation: "flipY",
});
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA,
image.width,
image.height,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
image
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); // configurable?
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
};
const albedoTex = gl.createTexture();
let noegramOk = false;
const noegramEl = document.querySelector("#noegram");
const noegramTex = gl.createTexture();
noegramEl.onload = async () => {
await makeTexture(noegramEl, noegramTex);
noegramOk = true;
};
gl.getExtension("EXT_texture_filter_anisotropic");
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
const projectionMatrix = mat4.create();
mat4.perspective(projectionMatrix, 45, 1, 0.1, 100);
const modelViewMatrix = mat4.create();
mat4.translate(modelViewMatrix, modelViewMatrix, [0, 0, -1.79]);
const clear = () => {
gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque
gl.clearDepth(1.0); // Clear everything
gl.enable(gl.DEPTH_TEST); // Enable depth testing
gl.depthFunc(gl.LEQUAL); // Near things obscure far things
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
};
const initBuffer = (array) => {
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
return buffer;
};
// Plane
const vertexPositions = initBuffer(
new Float32Array([-1.0, -1.0, +1.0, -1.0, -1.0, +1.0, +1.0, +1.0])
);
const textureCoords = initBuffer(
new Float32Array([0, 0, 1, 0, 0, 1, 1, 1])
);
// Shaders
const program = gl.createProgram();
const addShader = (type, source) => {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
throw new Error(
"An error occurred compiling the shaders: " +
gl.getShaderInfoLog(shader)
);
}
gl.attachShader(program, shader);
};
addShader(
gl.VERTEX_SHADER,
`#version 300 es\nprecision highp float;\n${
document.querySelector("#vertexShader").innerHTML
}`
);
addShader(
gl.FRAGMENT_SHADER,
`#version 300 es\nprecision highp float;\n${
document.querySelector("#fragmentShader").innerHTML
}`
);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
throw new Error(
"Unable to initialize the shader program: " +
gl.getProgramInfoLog(program)
);
}
const bindAttrib = (buffer, attribute) => {
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(
gl.getAttribLocation(program, attribute),
2,
gl.FLOAT,
false,
0,
0
);
gl.enableVertexAttribArray(gl.getAttribLocation(program, attribute));
};
bindAttrib(vertexPositions, "a_vertex");
bindAttrib(textureCoords, "a_uv0");
const renderFrame = () => {
clear();
gl.bindVertexArray(vao);
// Shader activation
gl.useProgram(program);
gl.uniformMatrix4fv(
gl.getUniformLocation(program, "u_model_view"),
false,
modelViewMatrix
);
gl.uniformMatrix4fv(
gl.getUniformLocation(program, "u_projection"),
false,
projectionMatrix
);
// Verts and UVs
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositions);
gl.vertexAttribPointer(
gl.getAttribLocation(program, "a_vertex"),
2,
gl.FLOAT,
false,
0,
0
);
gl.enableVertexAttribArray(gl.getAttribLocation(program, "a_vertex"));
gl.bindBuffer(gl.ARRAY_BUFFER, textureCoords);
gl.vertexAttribPointer(
gl.getAttribLocation(program, "a_uv0"),
2,
gl.FLOAT,
false,
0,
0
);
gl.enableVertexAttribArray(gl.getAttribLocation(program, "a_uv0"));
// Textures
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, albedoTex);
gl.uniform1i(gl.getUniformLocation(program, "u_texture_0"), 0);
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, noegramTex);
gl.uniform1i(gl.getUniformLocation(program, "u_texture_1"), 1);
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, noegramTex); // TODO: replace with noise
gl.uniform1i(gl.getUniformLocation(program, "u_texture_2"), 2);
// draw!
gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositions);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
const err = gl.getError();
if (err !== 0) {
console.log({ err });
throw new Error(`webgl failure: ${err}`);
}
};
renderEl.addEventListener("click", async () => {
if (albedoEl.files.length === 0) {
console.warn("cannot render, albedo not loaded yet");
return;
}
await makeTexture(albedoEl.files[0], albedoTex);
if (!noegramOk) {
console.warn("cannot render, noegram.png not loaded yet");
return;
}
console.log("running frame");
renderFrame();
});
})();
</script>