Skip to content

Instantly share code, notes, and snippets.

@eupston
Created March 19, 2023 02:22
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save eupston/f237a55cea936bdf819043ba1c651eb3 to your computer and use it in GitHub Desktop.
Save eupston/f237a55cea936bdf819043ba1c651eb3 to your computer and use it in GitHub Desktop.
ChatGPT Portal WebGL Shader
<!DOCTYPE html>
<html>
<head>
<style>
canvas { touch-action-delay: none; touch-action: none; }
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script>
const canvas = document.getElementById('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (!gl) {
alert('WebGL not supported in your browser');
}
// Vertex shader
const vertexShaderSource = `
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main() {
gl_Position = a_position;
v_texCoord = a_texCoord;
}
`;
// Fragment shader
const fragmentShaderSource = `
precision mediump float;
uniform sampler2D u_image;
uniform vec2 u_mouse;
uniform float u_time;
varying vec2 v_texCoord;
void main() {
vec2 pos = v_texCoord;
float dist = distance(pos, u_mouse);
float strength = 0.2 * sin(u_time) + 0.3;
float radius = 0.2;
if (dist < radius) {
float factor = smoothstep(radius, radius - strength, dist);
pos += (u_mouse - pos) * factor;
}
vec4 color = texture2D(u_image, pos);
gl_FragColor = color;
}
`;
// Compile shaders
function compileShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
const vertexShader = compileShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
const fragmentShader = compileShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
// Create and link the program
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(program));
}
gl.useProgram(program);
// Create buffer for the vertex positions
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positions = [
-1, -1,
1, -1,
-1, 1,
1, 1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
// Create buffer for the texture coordinates
const texCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
const texCoords = [
0, 0,
1, 0,
0, 1,
1, 1,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(texCoords), gl.STATIC_DRAW);
// Load the image
const image = new Image();
image.src = './surreal_mountains.png'
image.onload = () => {
// Resize the
const aspectRatio = image.width / image.height;
const windowAspectRatio = window.innerWidth / window.innerHeight;
if (aspectRatio > windowAspectRatio) {
canvas.width = window.innerWidth;
canvas.height = window.innerWidth / aspectRatio;
} else {
canvas.width = window.innerHeight * aspectRatio;
canvas.height = window.innerHeight;
}
canvas.style.position = "absolute";
canvas.style.left = (window.innerWidth - canvas.width) / 2 + "px";
canvas.style.top = (window.innerHeight - canvas.height) / 2 + "px";
// Create the texture
const texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 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);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
// Set up the attributes and uniforms
const positionAttribute = gl.getAttribLocation(program, 'a_position');
gl.enableVertexAttribArray(positionAttribute);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.vertexAttribPointer(positionAttribute, 2, gl.FLOAT, false, 0, 0);
const texCoordAttribute = gl.getAttribLocation(program, 'a_texCoord');
gl.enableVertexAttribArray(texCoordAttribute);
gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
gl.vertexAttribPointer(texCoordAttribute, 2, gl.FLOAT, false, 0, 0);
const imageUniform = gl.getUniformLocation(program, 'u_image');
gl.uniform1i(imageUniform, 0);
const mouseUniformLocation = gl.getUniformLocation(program, 'u_mouse');
const timeUniformLocation = gl.getUniformLocation(program, 'u_time');
// Set up the render loop
function render(time) {
time *= 0.002; // Convert to seconds
gl.uniform1f(timeUniformLocation, time);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clearColor(0, 0, 0, 0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
// Add mouse move listener
canvas.addEventListener('mousemove', (event) => {
const rect = canvas.getBoundingClientRect();
const x = (event.clientX - rect.left) / canvas.width;
const y = 1 - (event.clientY - rect.top) / canvas.height;
gl.uniform2f(mouseUniformLocation, x, y);
});
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment