ShaderCoder / utils /html_utils.py
Vipitis's picture
utils package for html and tree
3f8d823
raw
history blame
6.69 kB
def construct_embed(source_url):
shader_id = source_url.split("/")[-1]
return f'<iframe width="640" height="360" frameborder="0" src="https://www.shadertoy.com/embed/{shader_id}?gui=true&t=0&paused=true&muted=true" allowfullscreen></iframe>'
def make_iframe(shader_code): #keep a single function?
script = make_script(shader_code)
return f"""<iframe width="640" height="420" srcdoc=\'{script}\' allowfullscreen></iframe>"""
def make_script(shader_code):
# code copied and fixed(escaping single quotes to double quotes!!!) from https://webglfundamentals.org/webgl/webgl-shadertoy.html
script = ("""
<!-- Licensed under a BSD license. See license.html for license -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>WebGL - Shadertoy</title>
<link type="text/css" href="https://webglfundamentals.org/webgl/resources/webgl-tutorials.css" rel="stylesheet" />
<style>
.divcanvas {
position: relative;
display: inline-block;
}
canvas {
display: block;
}
.playpause {
position: absolute;
left: 10px;
top: 10px;
width: 100%;
height: 100%;
font-size: 60px;
justify-content: center;
align-items: center;
color: rgba(255, 255, 255, 0.3);
transition: opacity 0.2s ease-in-out;
}
.playpausehide,
.playpause:hover {
opacity: 0;
}
.iframe .divcanvas {
display: block;
}
</style>
</head>
<body>
<div class="divcanvas">
<canvas id="canvas"></canvas>
<div class="playpause">▶</div>
</div>
\nblank canvas here indicates that some of the shadertoy specific functions are not yet supported with this implementation (like #define I believe). you can always copy and paste the code into a shadertoy.com window to try.
</body>
<!--
for most samples webgl-utils only provides shader compiling/linking and
canvas resizing because why clutter the examples with code thats the same in every sample.
See https://webglfundamentals.org/webgl/lessons/webgl-boilerplate.html
and https://webglfundamentals.org/webgl/lessons/webgl-resizing-the-canvas.html
for webgl-utils, m3, m4, and webgl-lessons-ui.
-->
<script src="https://webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<script>
"use strict";
function main() {
// Get A WebGL context
/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("#canvas");
const gl = canvas.getContext("webgl");
if (!gl) {
return;
}
const vs = `
// an attribute will receive data from a buffer
attribute vec4 a_position;
// all shaders have a main function
void main() {
// gl_Position is a special variable a vertex shader
// is responsible for setting
gl_Position = a_position;
}
`;
const fs = `
precision highp float;
uniform vec2 iResolution;
uniform vec2 iMouse;
uniform float iTime;
""" + shader_code + """
void main() {
mainImage(gl_FragColor, gl_FragCoord.xy);
}
`;
// setup GLSL program
const program = webglUtils.createProgramFromSources(gl, [vs, fs]);
// look up where the vertex data needs to go.
const positionAttributeLocation = gl.getAttribLocation(program, "a_position");
// look up uniform locations
const resolutionLocation = gl.getUniformLocation(program, "iResolution");
const mouseLocation = gl.getUniformLocation(program, "iMouse");
const timeLocation = gl.getUniformLocation(program, "iTime");
// Create a buffer to put three 2d clip space points in
const positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// fill it with a 2 triangles that cover clipspace
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
-1, -1, // first triangle
1, -1,
-1, 1,
-1, 1, // second triangle
1, -1,
1, 1,
]), gl.STATIC_DRAW);
const playpauseElem = document.querySelector(".playpause");
const inputElem = document.querySelector(".divcanvas");
inputElem.addEventListener("mouseover", requestFrame);
inputElem.addEventListener("mouseout", cancelFrame);
let mouseX = 0;
let mouseY = 0;
function setMousePosition(e) {
const rect = inputElem.getBoundingClientRect();
mouseX = e.clientX - rect.left;
mouseY = rect.height - (e.clientY - rect.top) - 1; // bottom is 0 in WebGL
}
inputElem.addEventListener("mousemove", setMousePosition);
inputElem.addEventListener("touchstart", (e) => {
e.preventDefault();
playpauseElem.classList.add("playpausehide");
requestFrame();
}, {passive: false});
inputElem.addEventListener("touchmove", (e) => {
e.preventDefault();
setMousePosition(e.touches[0]);
}, {passive: false});
inputElem.addEventListener("touchend", (e) => {
e.preventDefault();
playpauseElem.classList.remove("playpausehide");
cancelFrame();
}, {passive: false});
let requestId;
function requestFrame() {
if (!requestId) {
requestId = requestAnimationFrame(render);
}
}
function cancelFrame() {
if (requestId) {
cancelAnimationFrame(requestId);
requestId = undefined;
}
}
let then = 0;
let time = 0;
function render(now) {
requestId = undefined;
now *= 0.001; // convert to seconds
const elapsedTime = Math.min(now - then, 0.1);
time += elapsedTime;
then = now;
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Turn on the attribute
gl.enableVertexAttribArray(positionAttributeLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
gl.vertexAttribPointer(
positionAttributeLocation,
2, // 2 components per iteration
gl.FLOAT, // the data is 32bit floats
false, // dont normalize the data
0, // 0 = move forward size * sizeof(type) each iteration to get the next position
0, // start at the beginning of the buffer
);
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
gl.uniform2f(mouseLocation, mouseX, mouseY);
gl.uniform1f(timeLocation, time);
gl.drawArrays(
gl.TRIANGLES,
0, // offset
6, // num vertices to process
);
requestFrame();
}
requestFrame();
requestAnimationFrame(cancelFrame);
}
main();
</script>
</html>
""")
return script