Skip to content

Instantly share code, notes, and snippets.

@aez
Created June 25, 2015 04:01
Show Gist options
  • Save aez/93028db3413a78f8689b to your computer and use it in GitHub Desktop.
Save aez/93028db3413a78f8689b to your computer and use it in GitHub Desktop.
gpXdae
<script id="shader-fs" type="x-shader/x-fragment">
'
varying highp vec2 vTextureCoord;
varying highp vec3 vLighting;
#ifdef GL_ES
precision highp float;
#endif
uniform highp vec2 resolution;
uniform highp float time;
uniform sampler2D uSampler;
void main(void) {
highp vec2 cPos = -1.0 + 2.0 * gl_FragCoord.xy / resolution.xy;
float cLength = length(cPos);
highp vec2 uv = gl_FragCoord.xy/resolution.xy+(cPos/cLength)*cos(cLength*12.0-time*4.0)*0.03;
/*
highp vec2 uv = vec2(vTextureCoord.s, vTextureCoord.t);
*/
highp vec4 texelColor = texture2D(uSampler, uv);
//gl_FragColor = vec4(texelColor.rgb * vLighting, texelColor.a);
gl_FragColor = texelColor;
}
</script>
<script id="shader-vs" type="x-shader/x-vertex">
'
attribute highp vec3 aVertexNormal;
attribute highp vec3 aVertexPosition;
attribute highp vec2 aTextureCoord;
uniform highp mat4 uNormalMatrix;
uniform highp mat4 uMVMatrix;
uniform highp mat4 uPMatrix;
varying highp vec2 vTextureCoord;
varying highp vec3 vLighting;
void main(void) {
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
vTextureCoord = aTextureCoord;
// Apply lighting effect
highp vec3 ambientLight = vec3(0.6, 0.6, 0.6);
highp vec3 directionalLightColor = vec3(0.5, 0.5, 0.75);
highp vec3 directionalVector = vec3(0.85, 0.8, 0.75);
highp vec4 transformedNormal = uNormalMatrix * vec4(aVertexNormal, 1.0);
highp float directional = max(dot(transformedNormal.xyz, directionalVector), 0.0);
vLighting = ambientLight + (directionalLightColor * directional);
}
</script>
body
.original
h2 Original
video#video(controls autoplay muted loop)
source src="http://shortcodelic1.manuelmasiacsasi.netdna-cdn.com/themes/geode/wp-content/uploads/2014/04/milky-way-river-1280hd.mp4.mp4" type="video/mp4"
.processed
h2 Processed
canvas#glcanvas width=640 height=640*9/16
// WebGL video
// developed by working through https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Tutorial/Animating_textures_in_WebGL
// with fixes from http://stackoverflow.com/questions/13572304/video-texture-in-webgl-not-working#13580005
// CORS needs to be disabled (to load the video into texture)
// launch with
// open -a Google Chrome --args --disable-web-security
var canvas;
var gl;
var intervalID;
var time = 0;
var cubeVerticesBuffer;
var cubeVerticesNormalBuffer;
var cubeVerticesTextureCoordBuffer;
var cubeVerticesIndexBuffer;
var cubeVerticesIndexBuffer;
var cubeTexture;
var mvMatrix;
var shaderProgram;
var vertexPositionAttribute;
var vertexNormalAttribute;
var textureCoordAttribute;
var projectionMatrix;
var videoElement;
$(start);
//
// start
//
// Called when the canvas is created to get the ball rolling.
//
function start() {
canvas = document.getElementById("glcanvas");
videoElement = document.getElementById("video");
initWebGL(canvas); // Initialize the GL context
// Only continue if WebGL is available and working
if (gl) {
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
// Initialize the shaders; this is where all the lighting for the
// vertices and so forth is established.
initShaders();
// Here's where we call the routine that builds all the objects
// we'll be drawing.
initBuffers();
// Next, load and set up the textures we'll be using.
initTextures();
// Start listening for the canplaythrough event, so we don't
// start playing the video until we can do so without stuttering
videoElement.addEventListener("canplaythrough", startVideo, true);
// Start listening for the ended event, so we can stop the
// animation when the video is finished playing.
videoElement.addEventListener("ended", videoDone, true);
}
}
//
// initWebGL
//
// Initialize WebGL, returning the GL context or null if
// WebGL isn't available or could not be initialized.
//
function initWebGL() {
gl = null;
try {
gl = canvas.getContext("webgl");
}
catch(e) {
}
// If we don't have a GL context, give up now
if (!gl) {
alert("Unable to initialize WebGL. Your browser may not support it.");
}
}
//
// initBuffers
//
// Initialize the buffers we'll need. For this demo, we just have
// one object -- a simple two-dimensional cube.
//
function initBuffers() {
// Create a buffer for the cube's vertices.
cubeVerticesBuffer = gl.createBuffer();
// Select the cubeVerticesBuffer as the one to apply vertex
// operations to from here out.
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);
// Now create an array of vertices for the cube.
var vertices = [
// Front face
-1.0, -1.0, 1.0,
1.0, -1.0, 1.0,
1.0, 1.0, 1.0,
-1.0, 1.0, 1.0,
];
// Now pass the list of vertices into WebGL to build the shape. We
// do this by creating a Float32Array from the JavaScript array,
// then use it to fill the current vertex buffer.
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
// Set up the normals for the vertices, so that we can compute lighting.
cubeVerticesNormalBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
var vertexNormals = [
// Front
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
0.0, 0.0, 1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexNormals),
gl.STATIC_DRAW);
// Map the texture onto the cube's faces.
cubeVerticesTextureCoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
var textureCoordinates = [
// Front
0.0, 0.0,
1.0, 0.0,
1.0, 1.0,
0.0, 1.0,
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordinates),
gl.STATIC_DRAW);
// Build the element array buffer; this specifies the indices
// into the vertex array for each face's vertices.
cubeVerticesIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
// This array defines each face as two triangles, using the
// indices into the vertex array to specify each triangle's
// position.
var cubeVertexIndices = [
0, 1, 2, 0, 2, 3, // front
]
// Now send the element array to GL
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,
new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
}
//
// initTextures
//
// Initialize the textures we'll be using, then initiate a load of
// the texture images. The handleTextureLoaded() callback will finish
// the job; it gets called each time a texture finishes loading.
//
function initTextures() {
cubeTexture = gl.createTexture();
}
//
// updateTexture
//
// Update the texture to contain the latest frame from
// our video.
//
function updateTexture() {
gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA,
gl.UNSIGNED_BYTE, videoElement);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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.bindTexture(gl.TEXTURE_2D, null);
}
//
// startVideo
//
// Starts playing the video, so that it will start being used
// as our texture.
//
function startVideo() {
videoElement.play();
intervalID = setInterval(drawScene, 15);
}
//
// videoDone
//
// Called when the video is done playing; this will terminate
// the animation.
//
function videoDone() {
clearInterval(intervalID);
}
//
// drawScene
//
// Draw the scene.
//
function drawScene() {
updateTexture();
// Clear the canvas before we start drawing on it.
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
projectionMatrix = makeOrtho(-1, 1, -1, 1, 0.1, 100.0);
// Set the drawing position to the "identity" point, which is
// the center of the scene.
loadIdentity();
// Now move the drawing position a bit to where we want to start
// drawing the cube.
mvTranslate([0.0, 0.0, -3]);
// update the ripple effect uniforms
time = (time + 5) % 5000;
gl.uniform1f(gl.getUniformLocation(shaderProgram, "time"), time / 1000);
gl.uniform2f(gl.getUniformLocation(shaderProgram, "resolution"), 640, 480);
// Draw the square by binding the array buffer to the cube's vertic
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesBuffer);
gl.vertexAttribPointer(vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
// Set the texture coordinates attribute for the vertices.
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesTextureCoordBuffer);
gl.vertexAttribPointer(textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
// Bind the normals buffer to the shader attribute.
gl.bindBuffer(gl.ARRAY_BUFFER, cubeVerticesNormalBuffer);
gl.vertexAttribPointer(vertexNormalAttribute, 3, gl.FLOAT, false, 0, 0);
// Specify the texture to map onto the faces.
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, cubeTexture);
gl.uniform1i(gl.getUniformLocation(shaderProgram, "uSampler"), 0);
// Draw the cube.
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, cubeVerticesIndexBuffer);
setMatrixUniforms();
gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);
}
//
// initShaders
//
// Initialize the shaders, so WebGL knows how to light our scene.
//
function initShaders() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
// Create the shader program
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
// If creating the shader program failed, alert
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
console.log("Unable to initialize the shader program.");
}
gl.useProgram(shaderProgram);
vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
gl.enableVertexAttribArray(vertexPositionAttribute);
textureCoordAttribute = gl.getAttribLocation(shaderProgram, "aTextureCoord");
gl.enableVertexAttribArray(textureCoordAttribute);
vertexNormalAttribute = gl.getAttribLocation(shaderProgram, "aVertexNormal");
gl.enableVertexAttribArray(vertexNormalAttribute);
}
//
// getShader
//
// Loads a shader program by scouring the current document,
// looking for a script with the specified ID.
//
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
// Didn't find an element with the specified ID; abort.
if (!shaderScript) {
return null;
}
var theSource = $(`#${id}`).text();
// Now figure out what type of shader script we have,
// based on its MIME type.
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null; // Unknown shader type
}
// Send the source to the shader object
gl.shaderSource(shader, theSource);
// Compile the shader program
gl.compileShader(shader);
// See if it compiled successfully
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
console.log("An error occurred compiling the shaders: " + gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
//
// Matrix utility functions
//
function loadIdentity() {
mvMatrix = Matrix.I(4);
}
function multMatrix(m) {
mvMatrix = mvMatrix.x(m);
}
function mvTranslate(v) {
multMatrix(Matrix.Translation($V([v[0], v[1], v[2]])).ensure4x4());
}
function setMatrixUniforms() {
var pUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
gl.uniformMatrix4fv(pUniform, false, new Float32Array(projectionMatrix.flatten()));
var mvUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
gl.uniformMatrix4fv(mvUniform, false, new Float32Array(mvMatrix.flatten()));
var normalMatrix = mvMatrix.inverse();
normalMatrix = normalMatrix.transpose();
var nUniform = gl.getUniformLocation(shaderProgram, "uNormalMatrix");
gl.uniformMatrix4fv(nUniform, false, new Float32Array(normalMatrix.flatten()));
}
<script src="//cdnjs.cloudflare.com/ajax/libs/zepto/1.1.4/zepto.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/velocity/1.2.2/velocity.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/sylvester/0.1.3/sylvester.js"></script>
<script src="https://developer.mozilla.org/samples/webgl/sample8/glUtils.js"></script>
body {
font-family: sans-serif;
}
.original, .processed {
display: inline-block;
width: 100%;
video, canvas {
width: 100%;
height: auto;
}
}
@aez
Copy link
Author

aez commented Jun 25, 2015

An example of video passed through a shader program. 4k possible? Note the caveat in script:

// CORS needs to be disabled (to load the video into texture)
// launch with
// open -a Google Chrome --args --disable-web-security

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment