WebGL을 사용한 애니메이션 및 상호 작용 기법 탐색 (사례 연구)

아래의 글은 번역을 하였음을 사전에 알려 드립니다
출처 : http://zuu.kr/izge
작성자 : Karim Maaloul

코드펜에서 짧은 WebGL을 작업을 하다 이 사이트에서(https://moments.epic.net/)
다양한 애니메이션 및 인터렉션을 찾아내기 시작하였다.

각 게임마다 게임 플레이는 매우 다르지만, 모든 실험은 하나의 원칙을 공유합니다.

각 캐릭터의 행동은 프로그래밍 방식으로 사용자 입력에 응답합니다.
(미리 계산 된 애니메이션 없음)
모든 동작은 런타임에 정의됩니다.
단 몇 줄의 코드만으로 이러한 캐릭터에 생명을 불어 넣는 것이 주요 과제였습니다.

제약 조건

주로 three.js(https://threejs.org/) 및 GreenSock 라이브러리(https://greensock.com/)를 사용하여 제작 된 이 실험은 3D 또는 애니메이션 소프트웨어를 사용하지 않고 손으로 직접 코딩했습니다.

이 과정은 한 번에 한 큐브 씩 프로그래밍 방식으로 문자를 형성하는 것으로 구성됩니다. 대부분의 노력은 코드의 값을 미세 조정하여 비율, 위치 및 전체 렌더링을 수정 한 다음 마지막으로 사용자 입력 (마우스 이동, 클릭, 드래그 등)에 따라 각 파트를 이동하는 데 소요되었습니다.

이 프로세스의 이점은 분명하지 않습니다. 하지만 텍스트 편집기 만 사용하여 전체 실험을 만들 수 있으므로 많은 도구를 사용하여 자산을 내보내고 문자의 속성을 조정하는 모든 노력을 피할 수 있습니다. Codepen에서 제공하는 실시간 미리보기를 활용 하면 전체 프로세스가 매우 유연 해졌 습니다.

즉, 프로세스는 관리하기 쉽도록 자체적 인 제약 조건을 갖게되었습니다. 가능한 한 적은 수의 파트로 문자를 작성해야했습니다. 각 부분은 매우 낮은 수의 정점으로 구성됩니다. 애니메이션은 제한된 수의 행동을 목표로해야했습니다.

참고 : 이 과정은 3D 앱이 마음에 들면 모델을 만들 때 사용하는 것이 좋습니다. 가능한 한 효과적 이도록 자신의 기술간에 올바른 균형을 찾는 것입니다. 필자의 경우 모든 프로세스를 하나의 도구로 유지할 때 훨씬 효율적입니다.

img1

제약 조건을 기회로 전환

이 과정에서 요구되는 미니멀리즘은 궁극적으로 각 행동 (편안함, 기쁨, 실망 등)을 묘사하는 가장 정확한 동작을 찾는 좋은 기회였습니다.

모든 입방체와 모든 움직임에 대해 질문을 받았다. 나는 이것을 정말로 필요로 하는가? 그것은 경험을 더 좋게 만들지 않습니까, 아니면 그냥 열렬한 캐릭터 디자이너의 변덕입니까?

나는 매우 단순한 장난감으로 끝을 맺었습니다. 모두 조용하고 미니멀 한 환경에 살고있었습니다.

프로그래밍 방식으로 사물을 애니메이션화하는 것은 아마도 가장 큰 도전이었습니다. 애니메이션 소프트웨어 나 시각적 타임 라인없이 자연 및 유기적인 움직임을 어떻게 조성합니까? 이 애니메이션을 자연스러운 상태에서 사용자 입력에 어떻게 응답합니까?

img2

1 단계 : 관측

이 실험을 시작하기 전에 나는 전하고 싶었던 느낌을 관찰하고 기억하고 고안하는 데 시간을 보냈다.

Chill the Lion을 만들 때까지, 나의 개를 애 태우는 것은 영감의 위대한 원천이되었습니다. 나는 그가 즐거움을 위해 눈을 감은 방법을 관찰하고 그의 목에 긁힘을달라고 요청했다. 프로그래밍 방식으로이를 번역 할 올바른 알고리즘을 찾는 것은 공감과 기본 수학 기술을 혼합 한 것입니다.
img3

“paranoid 새”(아래)에 대해, 나는 한순간의 표정을 가진 불편한 외모를 모방 한 것을 기억하고, 얼마나 많은 시간이 눈과 머리 움직임을 분리했는지 알아내어 설득력있는 행동을 보이도록 노력했습니다.
img4

그러나 때때로, 당신은 당신의 자신의 경험에 의지 할 수 없습니다. 시각적 영감은 때때로 특정 형태을 파악하는 데 필요합니다. 다행히도, 어떤 종류의 미묘한 표현을 찾을 수있는 Giphy 가 있습니다. YouTube와 Vimeo에서 올바른 움직임을 찾기 위해 많은 시간을 보냈습니다.

실행주기 관찰

행복의 순간에 만들어야 할 가장 까다로운 애니메이션 중 하나는 토끼가 늑대에게서 달아나는https://moments.epic.net/#rabbit) 것이 었습니다.
img5

이를 달성하기 위해서는 먼저 실행주기가 어떻게 작동하는지 이해하는 것이 중요합니다. Giphy에서 사용할 수있는 가장 흥미로운 슬로우 모션 GIF를 살펴 보았습니다.
img6

이 GIF에서 흥미로운 점은 달리기는 다리를 움직이는 것만이 아니라는 것입니다.
그것은 몸 전체에 관한 것입니다. 가장 작은 부분을 포함하여 완벽한 동기화로 움직입니다. 속도와 중력의 느김을 높이기 위해 참여하는 귀, 입, 심지어 혀를 보면 알 수 있습니다.

진실은 동물과 달리기가 가능한 한 많은 실행주기가 있다는 것입니다. 달리기 사이클을 파헤 치고 싶다면 좀 더 정확한 참조 정보를 얻는 것이 좋지 않을 것입니다. 두 가지 유용한 자료는 Pinterest 의 “Run Cycle”컬렉션(https://www.pinterest.co.kr/c3ck1na/run-cycle/?lp=true) 과 멋진 “ Quadruped Locomotion Tutorial” (https://vimeo.com/202205778)비디오입니다.

이러한 연구를 보면 각 실행주기의 역학이보다 명확 해집니다. 당신의 두뇌는 신체의 각 부분 사이의 관계를 파악하기 시작할 것이고, 경기의 순서와 리듬은 순환적이고 반복적이며 재현 가능한 형태를 나타낼 것입니다.

이제 이를 달성하기위한 기술적 인 솔루션이 필요합니다.

관찰 자동화

하나의 핸들을 돌릴 때 복잡한 동작을하는 기계식 장난감에 흥미를 느낀 저는 유사한 기술을 시도하고 타임 라인과 키 프레임보다 프로세스에 더 적합한 코드 기반 솔루션을 탐색하려고했습니다.

단순하거나 복잡한 루핑 동작은 전적으로 한주기의 진행에 달려 있다고 생각 합니다 .
img7

달리기 사이클에서, 이는 각 다리, 귀, 눈, 몸체 및 머리의 움직임이 동일한 주주기에 의해 유발된다는 것을 의미합니다. 어떤 경우에는 생성 된 회전이 수평 이동으로 변환되고 다른 경우에는 수직으로 변환됩니다.

원형 운동을 선형 운동으로 변환 할 때 삼각법이 최선의 선택 인 것 같습니다.

2 단계 : 무기를 연마하고 삼각법을 배우십시오.

도망 가지 마! 여기에 필요한 삼각법의 종류는 매우 기본입니다. 대부분의 수식은 다음과 같습니다.

x = cos(angle)*distance; y = sin(angle)*distance;

이것은 기본적으로 점의 극좌표 (각도, 거리)를 직교 좌표 (x, y)로 변환하는 데 사용됩니다.

각도를 변화시킴으로써 우리는 점을 중심 주위로 회전시킬 수 있습니다.
img9

삼각법 덕분에 우리는 수식의 다른 값으로 놀아서 훨씬 더 정교한 운동을 할 수 있습니다. 이 기술의 아름다움은 당신이 운동에서 얻는 부드러움입니다.

여기 몇 가지 예가 있습니다.
img10

실습!

삼각법을 이해하려면 손을 더럽혀야 합니다.
경험이없는 이론은 단순한 지적 놀이이다.

위의 수식 중 일부를 구현하려면 기본 환경을 설정해야합니다. 이 작업은 canvas, SVG 또는 three.js , PixiJS 또는 BabylonJS 와 같은 그래픽 API가있는 라이브러리를 사용하여 수행 할 수 있습니다 .

매우 기본적인 three.js 설정을 살펴 보겠습니다.

먼저 최신 버전의 three.js를 다운로드하고 라이브러리를 html머리글로 가져옵니다 .

<script type="text/javascript" src="js/three.js"></script>

HTML

<div id="world"></div>

CSS

#world {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
background: #ffffff;}

JAVASCRIPT

// Initialize variables.
var scene, camera, renderer, WIDTH, HEIGHT;
var PI = Math.PI;
var angle = 0;
var radius = 10;
var cube;
var cos = Math.cos;
var sin = Math.sin;

function init(event) {
  // Get the container that will hold the animation.
  var container = document.getElementById('world');

  // Get window size.
  HEIGHT = window.innerHeight;
  WIDTH = window.innerWidth;

  // Create a three.js scene; set up the camera and the renderer.
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera( 50, WIDTH / HEIGHT, 1, 2000 );
  camera.position.z = 100;
  renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
  renderer.setSize(WIDTH, HEIGHT);
  renderer.setPixelRatio(window.devicePixelRatio ? window.devicePixelRatio : 1);
  container.appendChild(renderer.domElement);

  // Create the cube.
  var geom = new THREE.CubeGeometry(16,8,8, 1);
  var material = new THREE.MeshStandardMaterial({
    color: 0x401A07
  });
  cube = new THREE.Mesh(geom, material);

  // Add the cube to the scene.
  scene.add(cube);

  // Create and add a light source.
  var globalLight = new THREE.AmbientLight(0xffffff, 1);
  scene.add(globalLight);

  // Listen to the window resize.
  window.addEventListener('resize', handleWindowResize, false);

  // Start a loop that will render the animation in each frame.
  loop();
}

function handleWindowResize() {
  // If the window is resized, we have to update the camera aspect ratio.
  HEIGHT = window.innerHeight;
  WIDTH = window.innerWidth;
  renderer.setSize(WIDTH, HEIGHT);
  camera.aspect = WIDTH / HEIGHT;
  camera.updateProjectionMatrix();
}

function loop(){
  // Call the update function in each frame to update the cube position.
  update();

  // Render the scene in each frame.
  renderer.render(scene, camera);

  // Call the loop function in next frame.
  requestAnimationFrame(loop);
}

// Initialize the demo when the page is loaded.
window.addEventListener('load', init, false);``

여기서는 기본적으로 장면, 카메라, 조명 및 큐브를 만들었습니다. 그런 다음 각 프레임에서 큐브의 위치를 ​​업데이트하는 루프를 시작했습니다.

이제 우리는 update()함수 를 추가 할 필요가 있습니다. 삼각 함수 공식을 삽입하여 다음과 같이 플레이 할 수 있습니다

function update(){
  // The angle is incremented by 0.1 every frame. Try higher values for faster animation.
  angle += .1;

  // Try modifying the angle and/or radius for a different movement.
  cube.position.x = cos(angle) * radius;
  cube.position.y = sin(angle) * radius;

  // You might want to use the same principle on the rotation property of an object. Uncomment the next line to see what happens.
  //cube.rotation.z = cos(angle) * PI/4;

  //Or vary the scale. Note that 1 is added as an offset to avoid a negative scale value.
  //cube.scale.y = 1 + cos(angle) * .5;

  /*
  Your turn! You might want to:
  - comment or uncomment the lines above to try new combinations,
  - replace cos by sin and vice versa,
  - replace radius with an other cyclic function.
  For example :
  cube.position.x = cos(angle) * (sin(angle) *radius);
  ...

  */
}

잃어버린 느낌이라면 Codepen 에서 사용할 준비가 된 것 입니다. 사인 및 코사인 함수를 사용하여 큐브를 다른 방식으로 이동하고 애니메이션에 삼각법을 활용하는 방법을 더 잘 이해할 수 있습니다.

또는 다음 데모로 이동하여 자신의 걷기 또는 달리기 사이클을 시작하기위한 출발점으로 사용할 수 있습니다.

삼각법을 사용하여 걷기 또는 달리기 사이클을 만드는 방법

이제는 코드를 사용하여 큐브를 움직이는 방법을 배웠고 동일한 원리를 사용하여 단계별로 간단한 걷기 사이클을 만들 계획입니다.

우리는 이전과 같은 설정을 주로 사용합니다. 가장 큰 차이점은 다른 신체 부위를 만들기 위해 더 많은 큐브가 필요하다는 것입니다.

three.js를 사용하면 다른 그룹 안에 오브젝트 그룹을 내장 할 수 있습니다. 예를 들어 다리, 팔, 머리를 지탱하는 몸체 그룹을 만들 수 있습니다.

우리의 주인공이 어떻게 만들어 졌는지 보자.

Hero = function() {
  // This will be incremented later at each frame and will be used as the rotation angle of the cycle.
  this.runningCycle = 0;

  // Create a mesh that will hold the body.
  this.mesh = new THREE.Group();
  this.body = new THREE.Group();
  this.mesh.add(this.body);

  // Create the different parts and add them to the body.
  var torsoGeom = new THREE.CubeGeometry(8,8,8, 1);//
  this.torso = new THREE.Mesh(torsoGeom, blueMat);
  this.torso.position.y = 8;
  this.torso.castShadow = true;
  this.body.add(this.torso);

  var handGeom = new THREE.CubeGeometry(3,3,3, 1);
  this.handR = new THREE.Mesh(handGeom, brownMat);
  this.handR.position.z=7;
  this.handR.position.y=8;
  this.body.add(this.handR);

  this.handL = this.handR.clone();
  this.handL.position.z = - this.handR.position.z;
  this.body.add(this.handL);

  var headGeom = new THREE.CubeGeometry(16,16,16, 1);//
  this.head = new THREE.Mesh(headGeom, blueMat);
  this.head.position.y = 21;
  this.head.castShadow = true;
  this.body.add(this.head);

  var legGeom = new THREE.CubeGeometry(8,3,5, 1);

  this.legR = new THREE.Mesh(legGeom, brownMat);
  this.legR.position.x = 0;
  this.legR.position.z = 7;
  this.legR.position.y = 0;
  this.legR.castShadow = true;
  this.body.add(this.legR);

  this.legL = this.legR.clone();
  this.legL.position.z = - this.legR.position.z;
  this.legL.castShadow = true;
  this.body.add(this.legL);

  // Ensure that every part of the body casts and receives shadows.
  this.body.traverse(function(object) {
    if (object instanceof THREE.Mesh) {
      object.castShadow = true;
      object.receiveShadow = true;
    }
  });
}

이제 이 캐릭터를 장면에 추가해야합니다.

function createHero() {
  hero = new Hero();
  scene.add(hero.mesh);
}

이것은 three.js로 간단한 캐릭터를 만드는 방법입니다. three.js를 사용하여 문자를 만드는 방법에 대해 자세히 알고 싶으면 Codrops에 대한 자세한 자습서 를 읽어보십시오 .

이 몸체를 만든 후에는 간단한 걷기 사이클에 도달 할 때까지 점차적으로이 모든 부분을 하나씩 움직이게 할 것입니다.

전체 논리는 객체 의 run함수에 위치 Hero합니다.

Hero.prototype.run = function(){

  // Increment the angle.
  this.runningCycle += .03;
  var t = this.runningCycle;

  // Ensure that the angle we will use is between 0 and 2 Pi.
  t = t % (2*PI);

  // Amplitude is used as the main radius of the legs movement.
  var amp = 4;

  // Update the position and rotation of every part of the body.
  this.legR.position.x =  Math.cos(t) * amp;
  this.legR.position.y = Math.max (0, - Math.sin(t) * amp);

  this.legL.position.x =  Math.cos(t + PI) * amp;
  this.legL.position.y = Math.max (0, - Math.sin(t + PI) * amp);

  if (t<PI){
    this.legR.rotation.z = Math.cos(t * 2 + PI/2) * PI/4;
    this.legL.rotation.z = 0;
  } else{
    this.legR.rotation.z = 0;
    this.legL.rotation.z = Math.cos(t * 2 + PI/2) *  PI/4;
  }

  this.torso.position.y = 8 - Math.cos(  t * 2 ) * amp * .2;
  this.torso.rotation.y = -Math.cos( t + PI ) * amp * .05;

  this.head.position.y = 21 - Math.cos(  t * 2 ) * amp * .3;
  this.head.rotation.x = Math.cos( t ) * amp * .02;
  this.head.rotation.y =  Math.cos( t ) * amp * .01;

  this.handR.position.x = -Math.cos( t ) * amp;
  this.handR.rotation.z = -Math.cos( t ) * PI/8;
  this.handL.position.x = -Math.cos( t + PI) * amp;
  this.handL.rotation.z = -Math.cos( t + PI) * PI/8;
}

이 코드 라인은 가장 흥미로운 부분이지만 Codepen에서 워킹 사이클 의 전체 코드 를 찾을 수 있습니다 .(https://codepen.io/Yakudoo/pen/YxMJMv?editors=0010)

따라하기 쉽도록 다음과 같은 데모를 만들었습니다.이 데모는 동작을 분해하고 이동되는 신체 부분과 각 단계에서 사용되는 수식을 강조 표시합니다.

https://codepen.io/Yakudoo/pen/qXaNeN

네 차례 야!

나는 토끼와 놀지 않고 너를 보내지 않을 것이다.

아래의 Codepen을 사용하면 몸의 각 부분마다 다른 각도 오프셋과 다른 진폭을 적용 할 수 있습니다. 보다 빠른 결과를 위해주기의 속도를 수정할 수도 있습니다.

이 사람에 대해 다른 실행주기를 알아낼 수 있습니까? 재미있어!
https://codepen.io/Yakudoo/pen/PjaeGV

결론

코드 기반 애니메이션이 부 자연스러운 움직임을 일으킨다 고 생각할 수도 있습니다. 오히려 나는 매우 유연한 방식으로 움직임을 조정할 수있는 좋은 기회를 제공하여 자신의 성격에 맞는 설득력있는 행동을 쉽게 취할 수 있다고 믿습니다.

행복의 순간 (Moments of Happiness) 은 다양한 실험의 모음이며 각 실험마다 고유 한 문제가 있습니다. 이 기사에서는 실행주기를 만드는 데 사용 된 솔루션을 자세히 설명했습니다. 내 Codepen 페이지에서이 모든 실험을 원하는대로 사용할 수 있습니다. 자유롭게 놀고 대화 형 장난감을 만들 수 있습니다.