let transform = false;
let scaleX = 1;
let scaleY = 0.7;
let rotation = 30 * Math.PI/180;
// Setup WebGL
const canvas = document.getElementById('canvas');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const gl = document.getElementById('canvas').getContext('webgl2');
gl.clearColor(0, 0, 0, 0.0);
const vs =
`#version 300 es
precision highp float;
in vec2 a_position;
in vec2 a_texcoord;
uniform mat4 u_matrix;
out vec2 v_texcoord;
void main() {
gl_Position = u_matrix * vec4(a_position, 0.0, 1.0);
v_texcoord = a_texcoord;
}`;
const fs =
`#version 300 es
precision highp float;
in vec2 v_texcoord;
uniform sampler2D u_texture;
out vec4 outColor;
void main() {
outColor = texture(u_texture, v_texcoord);
}`;
// Setup program with shaders
const program = gl.createProgram();
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vs);
gl.compileShader(vertexShader);
gl.attachShader(program, vertexShader);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fs);
gl.compileShader(fragmentShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
if (!gl.getShaderParameter(vertexShader, gl.COMPILE_STATUS)) {
console.error('ERROR compiling vertex shader', gl.getShaderInfoLog(vertexShader))
};
if (!gl.getShaderParameter(fragmentShader, gl.COMPILE_STATUS)) {
console.error('ERROR compiling fragment shader', gl.getShaderInfoLog(fragmentShader))
};
// Setup attributes
const positionLoc = gl.getAttribLocation(program, 'a_position');
const texcoordLoc = gl.getAttribLocation(program, 'a_texcoord');
// Setup uniforms
const matrixLoc = gl.getUniformLocation(program, 'u_matrix');
const textureLoc = gl.getUniformLocation(program, "u_texture");
const vao = gl.createVertexArray();
gl.bindVertexArray(vao);
// Create position buffer
const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
const positionData = new Float32Array([
0, 0,
0, 1,
1, 0,
1, 0,
0, 1,
1, 1
]);
gl.bufferData(gl.ARRAY_BUFFER, positionData, gl.STATIC_DRAW);
gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(
positionLoc, // attribute
2, // size
gl.FLOAT, // type
false, // normalize
0, // stride
0); // offset
// Create texcoord buffer
const texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
const texcoords = new Float32Array([
0, 0,
0, 1,
1, 0,
1, 0,
0, 1,
1, 1,
]);
gl.bufferData(gl.ARRAY_BUFFER, texcoords, gl.STATIC_DRAW);
gl.enableVertexAttribArray(texcoordLoc);
gl.vertexAttribPointer(
texcoordLoc, // attribute
2, // size
gl.FLOAT, // type
true, // normalize
0, // stride
0); // offset
// Setup texture
let tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 255, 255]));
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
let texWidth;
let texHeight;
let img = new Image();
img.addEventListener('load', function() {
texWidth = img.width;
texHeight = img.height;
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.generateMipmap(gl.TEXTURE_2D);
requestAnimationFrame(render);
});
img.src = 'https://i.imgur.com/3VirGxb.png';
img.crossOrigin = 'anonymous';
const draw = function() {
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
let matrix = new Float32Array([
2 / gl.canvas.width, 0, 0, 0,
0, -2 / gl.canvas.height, 0, 0,
0, 0, 1, 0,
-1, 1, 0, 1
]);
if (transform === true) {
matrix = m4.scale(matrix, scaleX, scaleY, 1);
matrix = m4.zRotate(matrix, rotation)
};
matrix = m4.translate(matrix,
dstX,
dstY,
0);
if (transform === true) {
matrix = m4.zRotate(matrix, -rotation)
matrix = m4.scale(matrix, scaleX, 1/scaleY, 1);
};
// Scale unit squad from 1x1 to texWidth and texHeight
matrix = m4.scale(matrix, texWidth, texHeight, 1);
gl.uniformMatrix4fv(matrixLoc, false, matrix);
let texUnit = 0;
gl.uniform1i(textureLoc, texUnit);
gl.activeTexture(gl.TEXTURE0 + texUnit);
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.drawArrays(
gl.TRIANGLES, // method
0, // offset
6, // number of vertices per instance
1 // number of instances
);
}
let dstX = 100;
let dstY = gl.canvas.height/2;
let incX = 1;
let incY = 1;
const update = function() {
dstX += incX;
dstY += incY;
if (dstX < 0) {
incX = 1;
}
if (dstX >= gl.canvas.width - texWidth) {
incX = -1;
}
if (dstY < 0) {
incY = 1;
}
if (dstY >= gl.canvas.height - texHeight) {
incY = -1;
}
}
const render = function() {
update();
draw();
requestAnimationFrame(render);
};
document.addEventListener('click', function(event) {
if (transform === true) {
transform = false;
document.getElementById('header').textContent = 'Transform off (click to change)';
} else if (transform === false) {
transform = true;
document.getElementById('header').textContent = 'Transform on (click to change)';
}
});
body {
margin: 0;
}
h1 {
font-family: arial;
position: absolute;
margin: 0;
}
<h1 id="header">Transform off (click to change)</h1>
<canvas id="canvas"></canvas>
<script src='https://webgl2fundamentals.org/webgl/resources/m4.js'></script>