본문으로 건너뛰기

기본 변환과 애니메이션_WebGL 노트 10

무료2016-01-06#JS#WebGL#webgl transform#webgl translate#webgl rotate#webgl scale#webgl animation#webgl animate#webgl平移旋转缩放#webgl动画

canvas 그리기에는 편리한 변환 API 가 있지만, WebGL 에서는 직접 구현해야 함

앞에 쓰는 말

[이전 노트](/articles/glsl-es(opengl-es 着色器语言)-webgl 笔记 9/) 에서, GLSL ES 의 구문을 전면적으로 이해했고, 습득 필수인 WebGL 기초 지식은 거의 모두 갖췄습니다 (즉 API 는 이미 없습니다..). 앞으로의 내용은 기본적으로 수학이며, 변환은 그 중에서 가장 얕은 부분 중 하나입니다

一.변환의 수학 원리

한 마디 두 마디로는 설명할 수 없습니다. 예를 들어 translate(평행 이동) 는 이미지의 각 점의 좌표의 각 성분에 delta 를 추가하는 것입니다. 점 (0, 3, 2) 를 y 축 정방향으로 2 단위 길이 이동하면, 결과는 (0, 5, 2) 가 됩니다. 원리는 이ほど 간단하지만, 설명할 수 없는 것은 공식 유도와 행렬 표현에 관련되기 때문입니다. 다음과 ��습니다:

x' = x + deltaX;      how? why?      |1 0 0 tx|
y' = y + deltaY;    ------------>    |0 1 0 ty|
z' = z + deltaZ;                     |0 0 1 tz|
                                     |0 0 0 1 |

3 차원 좌표는 왜 4x4 행렬로 translate 변환을 나타낼 필요가 있는가? 이렇게 하는 장점은 무엇인가? rotate 와 scale 은 어떤가?

흥미가 있으면自行로摸索 (검색) 해보세요.완전한 프로세스는 공식 유도->동차 좌표->행렬 승법->변환 행렬->CTM 으로, CTM 을 이해하면, 변환의 수학 원리를 완전히 이해할 수 있습니다

P.S.너무 수학적인 것은暫時쓰고 싶지 않지만, 늦든 빠든 씁니다.따라서 여기서는 변환 행렬을 전개하지 않고, 나중에再说합니다

二.WebGL 변환 행렬

변환 행렬은 모두 같은 모양을 하고 있지 않습니다.아니요 아니요 아니요, 이전 노트에서 말했지만, GLSL ES 중의 행렬은열주순입니다.예를 들어 translate 행렬은 이렇게 됩니다:

Matrix4.prototype.setTranslate = function(x, y, z) {
    var e = this.elements;
    e[0] = 1;  e[4] = 0;  e[8]  = 0;  e[12] = x;
    e[1] = 0;  e[5] = 1;  e[9]  = 0;  e[13] = y;
    e[2] = 0;  e[6] = 0;  e[10] = 1;  e[14] = z;
    e[3] = 0;  e[7] = 0;  e[11] = 0;  e[15] = 1;
    return this;
};

P.S.이상의 코드는『WebGL 프로그래밍 가이드』소스 코드 cuon-matrix.js 에서 인용했습니다.API 스타일이 사용감이 좋으므로..이후는 직접 사용합니다

주의:WebGl 은 OpenGL 과 마찬가지로, 행렬 요소는 열주순으로 저장됩니다.실제 CSS 중의 transform: matrix(a,b,c,d,e,f) 도 열주순이지만, 선형 대수 중에서는 일반적으로 행주순에 익숙합니다

三.행렬 라이브러리

심플한 행렬 라이브러리는 필수로, 적어도 3 종류의 기본 변환과 행렬 승법을 서포트해야 합니다.물론 한 세트의 우호적인 (사용감이 좋은)API 가 있는 것이 베스트입니다

『WebGL 프로그래밍 가이드』가 제공하는 행렬 라이브러리 cuon-matrix.js 는 꽤 좋으며,小巧하고 가장 기본적인 기능만 제공합니다.만약 사용하기 어렵다고 느껴지면其它의 행렬 라이브러리를 찾는 것도 좋지만, Three.js 는 사용하지 않는 것을 권장합니다.기능이 너무 많고 완전하기 때문에, WebGL 을 깊이 이해하는 데 불리하기 때문입니다

이후의 예에서는 cuon-matrix.js 를 사용합니다.구체적인 용법은 다음과 같습니다:

// 4x4 행렬을 작성하고, 디폴트로 단위 행렬에填充
var m4 = new Matrix4();
// m4.elements === [
// 1, 0, 0, 0,
// 0, 1, 0, 0,
// 0, 0, 1, 0,
// 0, 0, 0, 1]

// 평행 이동 행렬에填充하고, 파라미터는 deltaX, deltaY, deltaZ
m4.setTranslate(0.25, 0.25, 0.0);
// m4.elements === [
// 1, 0, 0, 0,
// 0, 1, 0, 0,
// 0, 0, 1, 0,
// 0.25, 0.25, 0, 1]

// 평행 이동의基礎上에서 회전
m4.rotate(30, 0, 0, 1);    // 반시계 방향 30 도, (0, 0, 1) 은 회전축 z 축
// m4.elements === [
// 0.8660253882408142, 0.5, 0, 0,
// -0.5, 0.8660253882408142, 0, 0,
// 0, 0, 1, 0,
// 0.25, 0.25, 0, 1]

// ...
// setXXX 는填充
// xxx 는 CTM 을 구하는, 즉 m4 = m4 x new Matrix4().setXXX

매우 편리하고 실용적인 기능도 있습니다.예를 들어:

// 복사
var newM4 = new Matrix4(oldM4);
// 행렬 승법, 승적을 m4_1 중에 배치
m4_1.multiply(m4_2);
// 행렬 전치
m4.transpose();

四.기본 변환

변환은 이미지상의 각 점의 좌표에 대해 같은 연산을 수행하는 것입니다.프래그먼트는 정점에서 생성되므로, 정점에 대해 연산을 수행하면 충분합니다.즉 정점 셰이더만을修改하면 됩니다.다음과 같습니다:

// 정점 셰이더 소스 프로그램
var vsSrc = 'attribute vec4 a_Position;' +
    'uniform mat4 u_transformMatrix;' +
    'void main() {' +
    'gl_Position = u_transformMatrix * a_Position;' +   // 좌표를 설정
    // 일점小技巧, 변환 전의 삼각형을 표시하기 위해
    'if (u_transformMatrix == mat4(0.0)) {gl_Position = a_Position;}' +
'}';

행렬 타입 uniform 변수 u_transformMatrix 를 선언하고, 변환 행렬을 받아들이기 위해 사용합니다.다음에 값을 전달할 필요가 있습니다

먼저 변환 행렬이 필요합니다.지금 하나 만듭니다:

// 변환
var transformMatrix = new Matrix4();
// 먼저 평행 이동
transformMatrix.setTranslate(0.25, 0.25, 0.0);  // 우상에 평행 이동
// 다음에 회전
transformMatrix.rotate(30, 0, 0, 1);    // 반시계 방향 30 도, (0, 0, 1) 은 회전축 z 축
// 다음에 스케일링
transformMatrix.scale(0.5, 0.5, 1);     // x, y 를 절반 스케일링, z 는 불변

이로써 매우 복잡한 변환 행렬이できました.다음에 uniform 변수에 값을 대입하는 방법을 생각합니다.다음과 같습니다:

// 변환 행렬을 정점 셰이더에 전달
var u_transformMatrix = gl.getUniformLocation(glUtil.program, 'u_transformMatrix');
gl.uniformMatrix4fv(u_transformMatrix, false, transformMatrix.elements);

mat 타입 셰이더 변수에 값을 대입하려면 gl.uniformMatrix4fv 를 사용합니다.파라미터의 의미는 다음과 같습니다:

gl.uniformMatrix4fv(location, transpose, array)
---
transpose   WebGL 중에서는 false 만 가능, 행렬이 전치 행렬인지 아닌지를 나타낸다.WebGL 은 행렬 전치 방법을 제공하지 않는다
array       타입화 배열, 4x4 행렬은 열주순으로 저장

마지막에 draw 하면 완료입니다

五.애니메이션

애니메이션은不断에 소거하고 재그리기로 발생하는 시각 효과입니다.예를 들어 회전은 회전 각도를不断에 증분하여 그리기로 실현됩니다.유창함을 보증하려면 각도가 균일하게 증분하는 것을 보증해야 합니다.따라서 애니메이션 실현의 열쇠는 균일한 변화를 보증하는 것이 되며, 단순한 setInterval 에서는 불충분합니다.따라서 시간 제어를 추가할 필요가 있습니다.구체적으로는 다음과 같습니다:

// 변환
var angle = 0;
var ROTATE_SPEED = 60;  // 60 도/초
var transformMatrix = new Matrix4();
var now;
var lastTime;
var delta = 0;
var u_transformMatrix = gl.getUniformLocation(glUtil.program, 'u_transformMatrix');
// draw
function draw() {
    now = Date.now();
    delta = (now - lastTime) * ROTATE_SPEED / 1000;
    // 각도 증분
    angle += delta;
    angle %= 360;
    // console.log(angle);
    // 회전
    transformMatrix.setRotate(angle, 0.0, 0.0, 1.0);
    // 회전 행렬을 정점 셰이더에 전달
    gl.uniformMatrix4fv(u_transformMatrix, false, transformMatrix.elements);
    // clear
    gl.clear(gl.COLOR_BUFFER_BIT);
    // 변환 후의 삼각형을 그리기
    gl.drawArrays(gl.TRIANGLES, 0, arrVtx.length / 2);

    // 시간을 기록
    lastTime = now;

    // 지연 재귀
    window.requestAnimationFrame(draw);
}
// 애니메이션
window.requestAnimationFrame(draw);
lastTime = Date.now();

회전 속도는 도/초이며, 차회의 그리기 시에 몇 도 회전했는지는 시간에 기반하여 계산됩니다.이로 인해 회전 각도가 균일하게 변화하는 것이 보증됩니다

주의:requestAnimationFramesetTimeout/setInterval 과 유사하지만, 차이는 requestAnimationFrame탭 페이지가 액티브 상태의 때만 유효 라는 것입니다.setTimeout/setInterval 과 다르며, 퍼포먼스도 더 높습니다

P.S.본예 중에서는 물론 탭 페이지 비액티브 상태의 애니메이션 정지는 알 수 없습니다.시간은에 기반하여 각도를 계산하고 있으므로, 도중에 한 단계 정지해도, 액티브를 회복했을 때 큰 각도를 회전하여, 도중에 정지한 부분을 쫓아갑니다.css3 애니메이션의 내부 원리도 이렇게 되어 있습니다:

CSS3 애니메이션은 Tab切り替え戻り時, 애니메이션 표현은暂停하지 않는다;Chrome frames 툴로 테스트하여 발견한 바, Tab切り替え後, 계산 렌더링 그리기는 모두 정지하며, Tab切り替え戻り時は내장 JS 로 애니메이션 위치를 계산하여 재그리기를 실현하고, 애니메이션이暂停하지 않는感觉을 조성한다

P.S.CSS3 애니메이션 그렇게 강하다, requestAnimationFrame 은 아직 무엇의役に立つ? « 張鑫旭 - 鑫空間 - 鑫生活 에서 인용.requestAnimationFrame 에 관한 상세 정보는 이 기사를 참조하세요.매우 가치가 있습니다

六.DEMO

이상의 코드를 포함한 완전한 예는, 이하를 참조하세요:

console 에서 각 스텝의 변환 후의 행렬의 값을 확인할 수 있습니다

七.정리

변환 행렬은 모델 행렬이라고도 불리며, 3D 중에서 불가결한 mvp 는 (Model Matrix, View Matrix, Projection Matrix) 입니다

至此, WebGL 2D 는 종료되었습니다.잘 생각해 보면 아무것도 배우지 않은 것처럼 보입니다.대부분의 API 는 3D 측에 있는가?아니요, 대부분의常用 API 는 모두 사용했습니다.남은 것은 조금役に立つ것과 매우 고도의 기능 (예를 들어 동적 텍스처) 뿐입니다.3D 씬은 거의 모두 행렬로「搓」出来的 것이며, 사용 가능한 API 는 이미 없습니다.Camera.setPosition(0, 0, 3) 과 같은 신기한 방법은 없습니다

시점이란 무엇인가, 행렬입니다.조명이란 무엇인가, 행렬입니다.그럼 그림자는?행렬입니다.안개는?모두 행렬입니다。。。

참고 자료

  • 『WebGL 프로그래밍 가이드』

댓글

아직 댓글이 없습니다

댓글 작성