translation
geometry를 업로드하고 shader에서 translation을 수행하여 2D 객체를 이동
<script id="vertex-shader-2d" type="x-shader/x-vertex">
attribute vec2 a_position;
uniform vec2 u_resolution;
uniform vec2 u_translation;
void main() {
//Add in the translation.
vec2 position = a_position + u_translation;
//convert the position from pixels to 0.0 to 1.0
vec2 zeroToOne = position / u_resolution;
//convert from 0->1 to 0->2
vec2 zeroToTwo = zeroToOne * 2.0;
//convert from 0->2 to -1->+1 (clipspace)
vec2 clipSpace = zeroToTwo - 1.0;
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
}
</script>
<script id="fragment-shader-2d" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
void main() {
gl_FragColor = u_color;
}
"use strict";
function main() {
// Get A WebGL context
/** @type {HTMLCanvasElement} */
var canvas = document.querySelector("#canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d"]);
gl.useProgram(program);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
// lookup uniforms
var resolutionLocation = gl.getUniformLocation(program, "u_resolution");
var colorLocation = gl.getUniformLocation(program, "u_color");
var translationLocation = gl.getUniformLocation(program, "u_translation");
// Create a buffer to put positions in
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Put geometry data into buffer
setGeometry(gl);
var translation = [0, 0];
var color = [Math.random(), Math.random(), Math.random(), 1];
drawScene();
// Setup a ui.
webglLessonsUI.setupSlider("#x", {slide: updatePosition(0), max: gl.canvas.width });
webglLessonsUI.setupSlider("#y", {slide: updatePosition(1), max: gl.canvas.height});
function updatePosition(index) {
return function(event, ui) {
translation[index] = ui.value;
drawScene();
};
}
// Draw the scene.
function drawScene() {
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas.
gl.clear(gl.COLOR_BUFFER_BIT);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Turn on the attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
// set the resolution
gl.uniform2f(resolutionLocation, gl.canvas.width, gl.canvas.height);
// set the color
gl.uniform4fv(colorLocation, color);
// Set the translation.
gl.uniform2fv(translationLocation, translation);
// Draw the geometry.
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 18; // 6 triangles in the 'F', 3 points per triangle
gl.drawArrays(primitiveType, offset, count);
}
}
// Fill the buffer with the values that define a letter 'F'.
function setGeometry(gl) {
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
// left column
0, 0,
30, 0,
0, 150,
0, 150,
30, 0,
30, 150,
// top rung
30, 0,
100, 0,
30, 30,
30, 30,
100, 0,
100, 30,
// middle rung
30, 60,
67, 60,
30, 90,
30, 90,
67, 60,
67, 90,
]),
gl.STATIC_DRAW);
}
main();
Rotation
단위원의 점은 sine consine으로 표현
function printSineAndCosineForAnAngle(angleInDegrees) {
var angleInRadians = angleInDegrees * Math.PI / 180;
var s = Math.sin(angleInRadians);
var c = Math.cos(angleInRadians);
console.log("s = " + s + " c = " + c);
}
- degree보다는 radian이 수학에서 쉽게 표현할 수 있다. 360 degree = 2π radian , 90 degree = 1/2π radian
…
...
uniform vec2 u_rotation;
void main() {
// Rotate the position
vec2 rotatedPosition = vec2(
a_position.x * u_rotation.y + a_position.y * u_rotation.x,
a_position.y * u_rotation.y - a_position.x * u_rotation.x);
// Add in the translation.
vec2 position = rotatedPosition + u_translation;
...
}
Scale
uniform vec2 u_scale;
void main() {
// 위치 scale
vec2 scaledPosition = a_position * u_scale;
// 위치 회전
vec2 rotatedPosition = vec2(
scaledPosition.x * u_rotation.y + scaledPosition.y * u_rotation.x,
scaledPosition.y * u_rotation.y - scaledPosition.x * u_rotation.x);
// translation 추가
vec2 position = rotatedPosition + u_translation;
...
...
var scaleLocation = gl.getUniformLocation(program, "u_scale");
...
var scale = [1, 1];
...
// 장면 그리기
function drawScene() {
...
// translation 설정
gl.uniform2fv(translationLocation, translation);
// rotation 설정
gl.uniform2fv(rotationLocation, rotation);
// scale 설정
gl.uniform2fv(scaleLocation, scale);
// geometry 그리기
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 18; // 'F'의 삼각형 6개, 삼각형마다 점 3개
gl.drawArrays(primitiveType, offset, count);
}
- 음수로 크기를 조정하면 geometry가 뒤집힌다.
Matrices
Matrices의 위치를 이용하여 함수를 선언한 후 조합을 사용하여 표현
"use strict";
function main() {
// Get A WebGL context
/** @type {HTMLCanvasElement} */
var canvas = document.querySelector("#canvas");
var gl = canvas.getContext("webgl");
if (!gl) {
return;
}
// setup GLSL program
var program = webglUtils.createProgramFromScripts(gl, ["vertex-shader-2d", "fragment-shader-2d"]);
// look up where the vertex data needs to go.
var positionLocation = gl.getAttribLocation(program, "a_position");
// lookup uniforms
var colorLocation = gl.getUniformLocation(program, "u_color");
var matrixLocation = gl.getUniformLocation(program, "u_matrix");
// Create a buffer to put positions in
var positionBuffer = gl.createBuffer();
// Bind it to ARRAY_BUFFER (think of it as ARRAY_BUFFER = positionBuffer)
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Put geometry data into buffer
setGeometry(gl);
var translation = [100, 150];
var angleInRadians = 0;
var scale = [1, 1];
var color = [Math.random(), Math.random(), Math.random(), 1];
drawScene();
// Setup a ui.
webglLessonsUI.setupSlider("#x", {value: translation[0], slide: updatePosition(0), max: gl.canvas.width });
webglLessonsUI.setupSlider("#y", {value: translation[1], slide: updatePosition(1), max: gl.canvas.height});
webglLessonsUI.setupSlider("#angle", {slide: updateAngle, max: 360});
webglLessonsUI.setupSlider("#scaleX", {value: scale[0], slide: updateScale(0), min: -5, max: 5, step: 0.01, precision: 2});
webglLessonsUI.setupSlider("#scaleY", {value: scale[1], slide: updateScale(1), min: -5, max: 5, step: 0.01, precision: 2});
function updatePosition(index) {
return function(event, ui) {
translation[index] = ui.value;
drawScene();
};
}
function updateAngle(event, ui) {
var angleInDegrees = 360 - ui.value;
angleInRadians = angleInDegrees * Math.PI / 180;
drawScene();
}
function updateScale(index) {
return function(event, ui) {
scale[index] = ui.value;
drawScene();
};
}
// Draw the scene.
function drawScene() {
webglUtils.resizeCanvasToDisplaySize(gl.canvas);
// Tell WebGL how to convert from clip space to pixels
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
// Clear the canvas.
gl.clear(gl.COLOR_BUFFER_BIT);
// Tell it to use our program (pair of shaders)
gl.useProgram(program);
// Turn on the attribute
gl.enableVertexAttribArray(positionLocation);
// Bind the position buffer.
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
// Tell the attribute how to get data out of positionBuffer (ARRAY_BUFFER)
var size = 2; // 2 components per iteration
var type = gl.FLOAT; // the data is 32bit floats
var normalize = false; // don't normalize the data
var stride = 0; // 0 = move forward size * sizeof(type) each iteration to get the next position
var offset = 0; // start at the beginning of the buffer
gl.vertexAttribPointer(
positionLocation, size, type, normalize, stride, offset);
// set the color
gl.uniform4fv(colorLocation, color);
// Compute the matrices
var matrix = m3.projection(gl.canvas.clientWidth, gl.canvas.clientHeight);
matrix = m3.translate(matrix, translation[0], translation[1]);
matrix = m3.rotate(matrix, angleInRadians);
matrix = m3.scale(matrix, scale[0], scale[1]);
// Set the matrix.
gl.uniformMatrix3fv(matrixLocation, false, matrix);
// Draw the geometry.
var primitiveType = gl.TRIANGLES;
var offset = 0;
var count = 18; // 6 triangles in the 'F', 3 points per triangle
gl.drawArrays(primitiveType, offset, count);
}
}
var m3 = {
projection: function(width, height) {
// Note: This matrix flips the Y axis so that 0 is at the top.
return [
2 / width, 0, 0,
0, -2 / height, 0,
-1, 1, 1
];
},
identity: function() {
return [
1, 0, 0,
0, 1, 0,
0, 0, 1,
];
},
translation: function(tx, ty) {
return [
1, 0, 0,
0, 1, 0,
tx, ty, 1,
];
},
rotation: function(angleInRadians) {
var c = Math.cos(angleInRadians);
var s = Math.sin(angleInRadians);
return [
c,-s, 0,
s, c, 0,
0, 0, 1,
];
},
scaling: function(sx, sy) {
return [
sx, 0, 0,
0, sy, 0,
0, 0, 1,
];
},
multiply: function(a, b) {
var a00 = a[0 * 3 + 0];
var a01 = a[0 * 3 + 1];
var a02 = a[0 * 3 + 2];
var a10 = a[1 * 3 + 0];
var a11 = a[1 * 3 + 1];
var a12 = a[1 * 3 + 2];
var a20 = a[2 * 3 + 0];
var a21 = a[2 * 3 + 1];
var a22 = a[2 * 3 + 2];
var b00 = b[0 * 3 + 0];
var b01 = b[0 * 3 + 1];
var b02 = b[0 * 3 + 2];
var b10 = b[1 * 3 + 0];
var b11 = b[1 * 3 + 1];
var b12 = b[1 * 3 + 2];
var b20 = b[2 * 3 + 0];
var b21 = b[2 * 3 + 1];
var b22 = b[2 * 3 + 2];
return [
b00 * a00 + b01 * a10 + b02 * a20,
b00 * a01 + b01 * a11 + b02 * a21,
b00 * a02 + b01 * a12 + b02 * a22,
b10 * a00 + b11 * a10 + b12 * a20,
b10 * a01 + b11 * a11 + b12 * a21,
b10 * a02 + b11 * a12 + b12 * a22,
b20 * a00 + b21 * a10 + b22 * a20,
b20 * a01 + b21 * a11 + b22 * a21,
b20 * a02 + b21 * a12 + b22 * a22,
];
},
translate: function(m, tx, ty) {
return m3.multiply(m, m3.translation(tx, ty));
},
rotate: function(m, angleInRadians) {
return m3.multiply(m, m3.rotation(angleInRadians));
},
scale: function(m, sx, sy) {
return m3.multiply(m, m3.scaling(sx, sy));
},
};
// Fill the buffer with the values that define a letter 'F'.
function setGeometry(gl) {
gl.bufferData(
gl.ARRAY_BUFFER,
new Float32Array([
// left column
0, 0,
30, 0,
0, 150,
0, 150,
30, 0,
30, 150,
// top rung
30, 0,
100, 0,
30, 30,
30, 30,
100, 0,
100, 30,
// middle rung
30, 60,
67, 60,
30, 90,
30, 90,
67, 60,
67, 90,
]),
gl.STATIC_DRAW);
}
main();
출처
https://webglfundamentals.org/webgl/lessons/ko/webgl-2d-translation.html
https://webglfundamentals.org/webgl/lessons/ko/webgl-2d-rotation.html
https://webglfundamentals.org/webgl/lessons/ko/webgl-2d-matrices.html