지난 시간에는 HTML 표준 기술 캔버스에서 자바스크립트를 이용하여 3차원 모양의 정육면체를 회전시켜보는 예제를 보았었습니다 :)
https://blog.naver.com/ephraimdrlee/221570755838
3차원 그래픽이란 무엇일까요?
현실 세계에서 사물들이 원근감 있게 보이듯이
컴퓨터 화면에서도 사물들이 원근감 있게 보이도록 장면을 구성하고,
그 결과를 컴퓨터가 뽑아내도록 하는 것입니다.
보통은 3D 맥스나 마야, 블렌더와 같은 모델링 툴에서나 볼 수 있고,
그 결과를 3D 게임과 같은 클라이언트 프로그램의 전유물이었으나,
드디어 웹까지 그 영역이 확대가 된 것이지요.
아래 URL 에 접속해보시면, 로봇이 걸어가는 장면이 나오는데,
오른쪽 선택상자에서 몇가지를 건드려보시면 약간 재미난 장면을 보실 수 있습니다.
https://threejs.org/examples/#webgl_animation_skinning_morph
"이 기술은 ActiveX 인가요?"
ActiveX 가 아닙니다.
HTML 표준 CANVAS 기술인데, 이미 꽤 오래전에 준비는 되어 있었으나
브라우저 호환성 문제로 그동안은 정형화된 기술로 각광받을 수가 없었던 것이지요.
하지만 요즈음에 IE8 구형브라우저가 한계인,
윈도우XP 사용자는 거의 찾아볼 수 없고 캔바스를 지원하는 크롬브라우저까지 나왔으니
윈도우XP 사용자는 이제 크롬브라우저를 사용하면 해결되는 문제라,
캔버스는 완전한 표준 기술로 사용해도 무방할 것으로 생각됩니다.
웹에서 3D 그래픽이라니 완전 매력적이라고 생각되지 않나요? :)
3D 그래픽에 대한 이해를 돕기 위해,
먼저 3D 좌표계에 대한 내용을 정리해보도록 하겠습니다.
평지가 있습니다.
평지는 바둑판 모양처럼 격자로 선이 그려져 있는데,
땅 주인이 땅의 모서리로부터
가로 방향으로 1부터 시작해서 번호를 매기고,
세로 방향으로도 동일하게 1부터 시작해서 번호를 매겼습니다.
가로 방향을 X 라고 하고, 세로 방향을 Y 라고 합시다.
크레이가 이 중 5번째 칸, 6번째 줄에 서있다면
크레이가 서 있는 지점을 ( 5, 6 ) 좌표에 있다고 할 수 있겠지요.
만일 5번째 칸, 7번째와 8번째 줄의 가운데 선다면 ( 5, 7.5 ) 좌표라고 소숫점을 넣어 부를 수도 있을 겁니다.
이 것이 2D좌표계입니다. XY좌표계라고 하지요.
3D 좌표계는 여기에 높이라는 개념이 추가됩니다.
HTML 표준 기술 캔버스는 기본적으로 X는 가로 방향, Y는 높이, Z 는 세로방향인데요.
카메라 각도를 조정하는 것으로 Y를 세로방향, Z를 높이로 바꿀 수도 있습니다.
우선은 기본대로 가는 것이 좋기 때문에 기본대로 하도록 하겠습니다 :)
크레이가 5번째 칸, 6번째 줄, 3층에 있다면 좌표는 어떻게 될까요?
( 5, 6, 3 ) 이라고 답하신다면 땡~ 입니다.
( 5, 3, 6 ) 정답입니다. 높이 Y가 3이고 세로방향 Z 가 6이기 때문이지요.
이 것이 3차원 좌표계입니다.
아울러 3차원 공간에서는 "카메라"라는 개념이 있습니다.
3차원 구성 공간에서 사물을 보기 위해서는 보는 시점인 사람의 눈에 해당하는
카메라가 필수입니다.
아래와 같이 카메라가 물체를 바라본다고 칠 때,
실제 카메라 안에 비치는 모양은 아래와 같습니다
마치 영화를 찍을 때 방송국의 스튜디오처럼 카메라가 위치를 이동하거나 회전, 줌인등을 하면, 카메라의 사각형 공간에 비쳐 보이는 물체가 동적으로 움직이는 것처럼 보이는 것이지요.
캔버스 기술에서도 이에 해당하는 3차원 공간을 볼 카메라를 배치할 수가 있는데요.
지난번 다루었던 소스를 단위 단위별로 한번 살펴볼까요?
카메라를 배치하기 전 2가지를 먼저 해주어야 합니다.
1) 3차원 공간 라이브러리인 three.js 를 HTML 문서에 포함한다.
2) 공허한 3차원 공간을 만든다.
1) three.js 라이브러리 추가
three.js 는 캔버스에서 3차원 공간을 표현하기에 아주 적절한 자바스크립트 라이브러리입니다. 그것도 마치 jQuery 같은 거대한 라이브러리이지요.
순수 자바스크립트로도 구성 가능하지만, three.js를 사용할 때 3차원 공간 제어는 더욱 쉬워지는데요.
먼저 html 소스에 아래 부분을 포함하는 것으로 시작합니다
<script src="https://threejs.org/build/three.min.js"></script>
2) 스크립트 안에서 공허한 3차원 공간을 만든다.
3차원 공간을 three.js 에서는 Scene(장면)이라고도 하는데요.
아래와 같은 정의를 해주는 것으로 3차원의 빈 공간이 하나 생겨납니다.
var scene = new THREE.Scene();
3) 카메라를 만들고 배치합니다.
카메라도 카메라 나름대로 여러가지 옵션을 주어가면서 만들 수가 있습니다.
가장 기본적인 카메라를 만드는 소스는 아래와 같은데요.
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
앞에서부터 파라미터의 배치 순서를 설명드리자면
1번째 파라미터 45는 시야각도입니다.
카메라에서부터 물체를 바라보는 각도인데요.
45도는 3인칭 시점에서 영화를 보는 듯한 아주 적당한 각도입니다. :)
2째 파라미터는 종횡비라고 해서 가로세로 비율입니다.
그냥 파라미터 그대로 window.innerWidth/window.innerHeight 로 사용하시는게 정신건강상 좋습니다 :)
3번째와 4번째 파라미터는 근거리, 원거리 커팅이라고 하는데요.
해당 범위에서 벗어나는 물체의 부분은 잘려서 표현됩니다.
이런 식으로요 :)
1). 2). 3)번은 누가 뭐래도 필수입니다.
하지만 이 것만으로는 아직 부족합니다.
이제 렌더링 엔진이라는 놈이 등장할 차례인데요.
렌더링 엔진은 금방 만들어낸 3차원 공간, 카메라를 가지고 실제 화면을 만들어내는
기술덩어리라고 보시면 됩니다. 자동차의 엔진처럼 엔진이라는 명칭이 붙을 만큼 강력한 놈이지요.
아래 소스는 렌더링 엔진을 생성하고, html 문서에 전체 화면으로 이를 추가해주는 소스입니다. 만일 화면 크기를 고정으로 정하고 싶다면 setSize 부분을 손봐주시면 되겠지요.
// 렌더러 정의 및 크기 지정, 문서에 추가하기
var renderer = new THREE.WebGLRenderer( { antialias: true, preserveDrawingBuffer: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
그리고 이렇게 만들어진 렌더링 엔진은 브라우저 화면에 지속해서 보여주는 액션을 취해주어야 합니다. 지속적으로 보여주는 액션을 수행하는 소스는 아래와 같은데요.
var framesPerSecond=60;
// 에니메이션 효과를 자동으로 주기 위한 보조 기능입니다.
var animate = function () {
// 프레임 처리
setTimeout(function() {
requestAnimationFrame(animate);
}, 1000 / framesPerSecond);
// 랜더링을 수행합니다.
renderer.render( scene, camera );
};
// animate()함수를 최초에 한번은 수행해주어야 합니다.
animate();
framesPerSecond 는 초당 몇번씩 장면을 갱신하며 보여줄 것인지를 먼저 정의하는 것입니다.
그래서 animate 라는 함수를 마지막 줄에 한번만 실행해주면
animate 함수 내에서 setTimeout 함수에 의해 자기 자신을 무한 호출해주게 되지요.
참고로 requestAnimationFrame 함수는 전용함수이니 꼭 이 명칭 그대로 사용해주어야 하며 변경해서는 안됩니다.
rendere.render ( scene, camera ) 가 실제적으로 렌더링 엔진이 scene 장면을 카메라의 위치와 바라보는 방향으로 장면을 만들어서 보여주는 부분을 실행하는 것입니다.
이 기본틀이 되는 소스가 바로 아래와 같습니다
<html>
<head>
<title>3차원 캔바스 예제 1</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="https://threejs.org/build/three.min.js"></script>
<script>
// ==========================
// 초기화 부분 시작 ( 이 부분은 문서에서 한번만 수행되면 됩니다 )
// ==========================
// 3차원 세계
var scene = new THREE.Scene();
// 카메라 ( 카메라 수직 시야 각도, 가로세로 종횡비율, 시야거리 시작지점, 시야거리 끝지점
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
// 렌더러 정의 및 크기 지정, 문서에 추가하기
var renderer = new THREE.WebGLRenderer( { antialias: true, preserveDrawingBuffer: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var framesPerSecond=60;
// 에니메이션 효과를 자동으로 주기 위한 보조 기능입니다.
var animate = function () {
// 프레임 처리
setTimeout(function() {
requestAnimationFrame(animate);
}, 1000 / framesPerSecond);
// 랜더링을 수행합니다.
renderer.render( scene, camera );
};
// animate()함수를 최초에 한번은 수행해주어야 합니다.
animate();
</script>
</body>
</html>
하지만 이 소스를 실행하더라도 시커먼 화면에 아무것도 보이지 않습니다.
빛도 없고 사물도 없기 때문입니다.
그래서 보통 빛과 사물 등을 생성하는 부분은 이 소스 아랫 부분에 위치합니다.
document.body.appendChild( renderer.domElement );
: // 여기에 사물들을 생성
그리고 움직임을 담당하는 부분은 이 소스 아랫 부분에 위치하는 것이지요.
setTimeout(function() {
requestAnimationFrame(animate);
}, 1000 / framesPerSecond);
: // 여기에서 움직임을 처리
오늘은 여기까지 이해하는 것으로 마치도록 하겠습니다.
하지만 아무 결과물 없이 끝나면 다들 아쉬울테니 새로운 결과물을 하나 뽑아보도록 하겠습니다.
묻지도 따지지도 말고,
아래 소스는 사물들을 생성하는 부분에 배치하고,
// 빛을 생성해서
var light1 = new THREE.PointLight( 0xffffff, 1, 100 );
// 위치를 적당한 지점에 놓고
light1.position.set( 5, 5, 5 );
// 장면에 추가합니다.
scene.add( light1 );
// 빛을 또한 생성해서
var light2 = new THREE.PointLight( 0xffFFFF, 1, 100 );
// 위치를 적당한 지점에 놓고
light2.position.set( 7, -5, 6 );
// 장면에 추가합니다.
scene.add( light2 );
// 빛을 또한 생성해서
var light3 = new THREE.PointLight( 0xffFFFF, 1, 100 );
// 위치를 적당한 지점에 놓고
light3.position.set( -7, 3, 3 );
// 장면에 추가합니다.
scene.add( light3 );
var floor = new THREE.Mesh(
new THREE.BoxGeometry( 10, 0.0, 10 ),
new THREE.MeshStandardMaterial({ color: 0xffcc99 })
)
scene.add ( floor );
var cube=Array();
for(i=0,x=0,z=0;i<20;++i)
{
height = Math.random() * 2 + 1;
// 정육면체 하나 만들기
cube.push( new THREE.Mesh(
new THREE.BoxGeometry( 0.6, height, 0.6 ),
new THREE.MeshStandardMaterial({ color: Math.round(Math.random() * 0xffffff)})
));
cube[i].position.x=(x-2.5);
cube[i].position.y=height/2;
cube[i].position.z=z;
x++;
if(i%5==4){ z++; x=0; }
// 생성한 모델을 장면에 추가합니다.
scene.add( cube[i] );
}
// 카메라의 Z좌표를 물체에서 7 정도 떨어진 지점에 위치합니다.
camera.position.z = 7;
camera.position.y = 5;
camera.rotation.x = -35 * ( Math.PI / 180 );
아래 소스는 움직임을 관장하는 부분에 넣어보도록 할까요?
for(i=0;i<20;++i){
cube[i].rotation.y += 0.3 * ( Math.PI / 180 );
}
최종 소스는 아래와 같습니다.
<html>
<head>
<title>3차원 캔바스 예제 1</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="https://threejs.org/build/three.min.js"></script>
<script>
// ==========================
// 초기화 부분 시작 ( 이 부분은 문서에서 한번만 수행되면 됩니다 )
// ==========================
// 3차원 세계
var scene = new THREE.Scene();
// 카메라 ( 카메라 수직 시야 각도, 가로세로 종횡비율, 시야거리 시작지점, 시야거리 끝지점
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth/window.innerHeight, 0.1, 1000 );
// 렌더러 정의 및 크기 지정, 문서에 추가하기
var renderer = new THREE.WebGLRenderer( { antialias: true, preserveDrawingBuffer: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// 빛을 생성해서
var light1 = new THREE.PointLight( 0xffffff, 1, 100 );
// 위치를 적당한 지점에 놓고
light1.position.set( 5, 5, 5 );
// 장면에 추가합니다.
scene.add( light1 );
// 빛을 또한 생성해서
var light2 = new THREE.PointLight( 0xffFFFF, 1, 100 );
// 위치를 적당한 지점에 놓고
light2.position.set( 7, -5, 6 );
// 장면에 추가합니다.
scene.add( light2 );
// 빛을 또한 생성해서
var light3 = new THREE.PointLight( 0xffFFFF, 1, 100 );
// 위치를 적당한 지점에 놓고
light3.position.set( -7, 3, 3 );
// 장면에 추가합니다.
scene.add( light3 );
var floor = new THREE.Mesh(
new THREE.BoxGeometry( 10, 0.0, 10 ),
new THREE.MeshStandardMaterial({ color: 0xffcc99 })
)
scene.add ( floor );
var cube=Array();
for(i=0,x=0,z=0;i<20;++i)
{
height = Math.random() * 2 + 1;
// 정육면체 하나 만들기
cube.push( new THREE.Mesh(
new THREE.BoxGeometry( 0.6, height, 0.6 ),
new THREE.MeshStandardMaterial({ color: Math.round(Math.random() * 0xffffff)})
));
cube[i].position.x=(x-2.5);
cube[i].position.y=height/2;
cube[i].position.z=z;
x++;
if(i%5==4){ z++; x=0; }
// 생성한 모델을 장면에 추가합니다.
scene.add( cube[i] );
}
// 카메라의 Z좌표를 물체에서 7 정도 떨어진 지점에 위치합니다.
camera.position.z = 7;
camera.position.y = 5;
camera.rotation.x = -35 * ( Math.PI / 180 );
// ==========================
// 초기화 부분 끝
// ==========================
var framesPerSecond=60;
// 에니메이션 효과를 자동으로 주기 위한 보조 기능입니다.
var animate = function () {
// 프레임 처리
setTimeout(function() {
requestAnimationFrame(animate);
}, 1000 / framesPerSecond);
for(i=0;i<20;++i){
cube[i].rotation.y += 0.3 * ( Math.PI / 180 );
}
// 랜더링을 수행합니다.
renderer.render( scene, camera );
};
// animate()함수를 최초에 한번은 수행해주어야 합니다.
animate();
</script>
</body>
</html>
그리고 실행해보시면 3차원 막대 그래프가 랜덤하게 산발하듯이 솟아오르고 천천히 회전하는 것을 보실 수 있을 겁니다.
결과를 볼 수 있는 웹페이지 또한 아래와 같이 마련되어 있습니다.
http://dreamplan7.cafe24.com/canvas/three002.php
다음 시간에는 이 후 단계에 대한 설명을 진행하도록 하겠습니다.
여기까지 읽어주신 모든 분들께 감사드립니다 :)
수고하셨습니다~
다음강좌 보러 가기 / https://itadventure.tistory.com/46
'자바스크립트와 캔버스' 카테고리의 다른 글
3차원 웹, 그림같은 집을 지어요, 캔버스와 함께하는 자바스크립트, 19번째 시간 (12) | 2019.07.06 |
---|---|
3차원 웹, 빛과 그림자, 캔버스와 함께하는 자바스크립트, 18번째 시간 (4) | 2019.07.04 |
3차원 웹, 밝은 빛을 보라, 캔버스와 함께하는 자바스크립트, 17번째 시간 (2) | 2019.07.02 |
3차원 웹, 마우스로 움직이다, 캔버스와 함께하는 자바스크립트, 16번째 시간 (4) | 2019.06.30 |
3차원 웹, 이미지를 부르다, 캔버스와 함께하는 자바스크립트, 15번째 시간 (2) | 2019.06.28 |