var m4 = twgl.m4;
var gl = twgl.getWebGLContext(document.getElementById("c"));
var programInfo = twgl.createProgramInfo(gl, ["vs", "fs"]);
// a unit quad
var arrays = {
position: {
numComponents: 2,
data: [
0, 0,
1, 0,
0, 1,
0, 1,
1, 0,
1, 1,
],
},
};
var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
// make the texture the same size as the canvas just to make it easy
var texWidth = gl.canvas.width;
var texHeight = gl.canvas.height;
// we're only using 1 texture so just make and bind it now
var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.LUMINANCE, texWidth, texHeight, 0,
gl.LUMINANCE, gl.UNSIGNED_BYTE, null);
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.NEAREST);
var destColumn = 0;
// We're using 1 byte wide texture pieces so we need to
// set UNPACK_ALIGNMENT to 1 as it defaults to 4
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
function addLineToTexture(lineData) {
gl.texSubImage2D(gl.TEXTURE_2D, 0, destColumn, 0, 1, texHeight,
gl.LUMINANCE, gl.UNSIGNED_BYTE, lineData);
// advance column and wrap back to 0
destColumn = (destColumn + 1) % texWidth;
}
// we pass in texWidth and texHeight because unlike images
// we can't look up the width and height of a texture
// we pass in targetWidth and targetHeight to tell it
// the size of the thing we're drawing too. We could look
// up the size of the canvas with gl.canvas.width and
// gl.canvas.height but maybe we want to draw to a framebuffer
// etc.. so might as well pass those in.
// srcX, srcY, srcWidth, srcHeight are in pixels
// computed from texWidth and texHeight
// dstX, dstY, dstWidth, dstHeight are in pixels
// computed from targetWidth and targetHeight
function drawImage(
tex, texWidth, texHeight,
srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight,
targetWidth, targetHeight) {
var mat = m4.identity();
var tmat = m4.identity();
var uniforms = {
matrix: mat,
textureMatrix: tmat,
texture: tex,
};
// these adjust the unit quad to generate texture coordinates
// to select part of the src texture
// NOTE: no check is done that srcX + srcWidth go outside of the
// texture or are in range in any way. Same for srcY + srcHeight
m4.translate(tmat, [srcX / texWidth, srcY / texHeight, 0], tmat);
m4.scale(tmat, [srcWidth / texWidth, srcHeight / texHeight, 1], tmat);
// these convert from pixels to clip space
m4.ortho(0, targetWidth, targetHeight, 0, -1, 1, mat)
// these move and scale the unit quad into the size we want
// in the target as pixels
m4.translate(mat, [dstX, dstY, 0], mat);
m4.scale(mat, [dstWidth, dstHeight, 1], mat);
gl.useProgram(programInfo.program);
twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo);
twgl.setUniforms(programInfo, uniforms);
twgl.drawBufferInfo(gl, gl.TRIANGLES, bufferInfo);
}
// Scroll constantly
function render() {
addLineToTexture(getNextLineData());
function drawFirstPart() {
var srcX = 0;
var srcY = 0;
var srcWidth = destColumn;
var srcHeight = texHeight;
var dstX = texWidth - destColumn;
var dstY = 0;
var dstWidth = destColumn;
var dstHeight = texHeight;
var targetWidth = gl.canvas.width;
var targetHeight = gl.canvas.height;
drawImage(
tex, texWidth, texHeight,
srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight,
targetWidth, targetHeight);
}
function drawSecondPart() {
var srcX = destColumn;
var srcY = 0;
var srcWidth = texWidth - destColumn + 1;
var srcHeight = texHeight;
var dstX = 0;
var dstY = 0;
var dstWidth = texWidth - destColumn + 1;
var dstHeight = texHeight;
var targetWidth = gl.canvas.width;
var targetHeight = gl.canvas.height;
drawImage(
tex, texWidth, texHeight,
srcX, srcY, srcWidth, srcHeight,
dstX, dstY, dstWidth, dstHeight,
targetWidth, targetHeight);
}
drawFirstPart();
drawSecondPart();
requestAnimationFrame(render);
}
render();
// =====================================================================
// Everything below this line represents stuff from the server.
// so it's mostly irrelevant to the answer
// this code just generates endless data
var count = 0;
var data;
function getNextLineData() {
if (!data) {
data = new Uint8Array(texHeight);
}
++count;
for (var ii = 0; ii < data.length; ++ii) {
data[ii] = 0;
}
addPoint(count, 0.010, 255, data);
addPoint(count, 0.031, 240, data);
addPoint(count, 0.023, 220, data);
addPoint(count, 0.013, 200, data);
return data;
}
function addPoint(count, mult, value, data) {
var s = Math.floor((Math.sin(count * mult) * 0.5 + 0.5) * (data.length - 1));
data[s] = value;
}
canvas { border: 1px solid red; }
<script src="https://twgljs.org/dist/twgl-full.min.js"></script>
<script id="vs" type="not-js">
// we will always pass a 0 to 1 unit quad
// and then use matrices to manipulate it
attribute vec4 position;
uniform mat4 matrix;
uniform mat4 textureMatrix;
varying vec2 texcoord;
void main () {
gl_Position = matrix * position;
texcoord = (textureMatrix * position).xy;
}
</script>
<script id="fs" type="not-js">
precision mediump float;
varying vec2 texcoord;
uniform sampler2D texture;
void main() {
gl_FragColor = texture2D(texture, texcoord);
}
</script>
<canvas id="c" width="500" height="150"></canvas>