본문 바로가기
자바스크립트와 캔버스

3차원 웹 캔버스, 원터치 버튼 이동! - 24번째 시간

three.js 버전이 변경되어 소스가 일부 수정되었습니다. ( 2020. 4. 12 )
본문과 일부 일치하지 않는 부분이 있을 수 있으며 아래 URL 에서 작동을 확인해보실 수 있습니다.
http://dreamplan7.cafe24.com/canvas2/three012.html

https://youtu.be/cnARSpNaAV0

웹의 장점은 빠른 찾기 기능입니다.

지금도 언제든지 크레이의 블로그의 메뉴에서 찾고 싶은 메뉴를 클릭하면

언제든지 해당 메뉴 위치로 이동하지요.

3차원 웹에서도 특정지점으로 이동하기 위해 마우스로 낑낑거리며 이동할 수도 있지만,

기왕이면 원클릭으로 한번에 이동하는 기능이 있으면 어떨까요?

이미 영상에서 보신 것처럼 가능합니다.

다만 3차원웹이기 때문에 2차원 웹과는 제공해야 할 정보가 좀 다르다고 할까요?

이를 위해서 카메라의 보는 시점 x, y, z 좌표와 바라볼 좌표 x, y, z 가 필요하지요.

사실 3차원 좌표는 느낌으로 가늠하기란 정말 어려운데요.

먼저 지금 카메라의 3차원 좌표 지점을 늘 화면에 표시하면 어떨까요?

그래서 적당한 위치의 좌표를 메모했다가 사용하면 좋지 않겠습니까? :)

사실 이 글자 부분은 캔바스가 아닙니다.

웹의 div 레이어를 캔바스 위에 조그맣게 띄워놓은 것이지요.

이제 하나씩 과정을 살펴보겠습니다.

먼저 준비작업을 해볼까요?

지난 소스에 이어,

three011.html 파일은 three012.html 파일로,

three011.js 파일은 three012.js 파일로 서버에서 복사해주신 다음

three012.html 파일의 소스를 약간 수정하겠습니다.

아래 부분을

<script src="js/three011.js"></script>

이렇게 바꿔주세요.

<script src="js/three012.js"></script>

그리고 바로 아래에 다음 소스를 추가해줍니다.

<div id=monitor>모니터</div>

하나 더, 스타일을 추가합니다.

아래와 같은 소스를 찾으신 다음

canvas { width: 100%; height: 100% }

그 아랫 부분에 아래 소스를 추가해 주세요.

이 스타일 태그는 id 값이 'monitor'인 항목의 css 를 직접 정의합니다.

#monitor { 
	position: fixed;
	bottom: 0;
    right: 0;
	color:white;
	padding:5px;
	text-align:right;
	font-size:10pt;
}

그리고 three012.html 페이지를 띄워보면,

화면 오른쪽 아랫 부분에 '모니터'라는 한글이 보이실 겁니다.

아직은 아무런 변화가 없지만 앞으로 이 부분에 카메라의 좌표가 나오게 할 겁니다.

참고로, position: fixed 는 화면이 스크롤되든 어떻든간에 항상 고정된 위치에 표시하는 css 속성이고 bottom: 과 함께 여백값을 주면 화면 아래에서 얼마만큼 떨어뜨려 표시할 것인지를 정의하는 속성입니다.

동일한 의미로 right: 속성도 오른쪽에서 얼마만큼 뗄 것인지를 정의하는 속성이고,

color: 는 글자의 색상, padding: 은 div 영역 내에서 글자의 상하좌우 여백을 정의하고,

font-size: 는 글자의 크기를 지정하는 속성이지요.


이제, 이 부분에 카메라의 좌표를 표시하도록 하겠습니다.

three012.js 파일에서 다음 소스를 찾아서,

renderer.render( scene, camera );

그 아랫 부분에 아래 소스를 추가하도록 하겠습니다.

document.getElementById('monitor').innerText = 
	camera.position.x.toFixed(1) + ", " + 
	camera.position.y.toFixed(1) + ", " + 
	camera.position.z.toFixed(1);

그러면 오른쪽 아래 좌표가 이렇게 변경됩니다.

이 좌표가 카메라의 x, y, z 좌표가 됩니다

정말로 그런지 한번 마우스로 화면을 돌려볼까요?

오! 마우스를 돌려보니 좌표가 바뀌었군요!

이 좌표가 바로 현재 카메라의 눈 지점입니다. 이걸 메모해 주시면 이 방향에서 바라보는 좌표를 얻을 수 있습니다.

그렇다면 카메라의 바라보는 지점은 어디일까요?

사실 카메라의 바라보는 지점은 '무한대'이기 때문에 어느 한 지점을 특정 지을 수 없습니다. 바로 1미터 앞이 바라보는 방향일 수도 있고 5미터 또는 10미터, 10.1 미터일 수도 있어서 그렇지요.

굳이 정의하자면, 바라보면서 처음 물건으로 인하여 막히는 지점을 정의할 수도 있지만

이 지점을 찾는 방법은 레이캐스팅(Ray Casting)이라고 해서 차후에 다뤄보도록 하겠습니다.

참고로 기본적인 카메라가 바라보는 지점은 (0, 0, 0)입니다.

이 좌표를 우선 메모해 놓으시기 바랍니다.

자, 이제 원 클릭으로 카메라를 이동하는 기능을 살펴보겠습니다.

먼저 아까 추가했던 아래 소스 부분의 그 아랫 부분에

<div id=monitor>모니터</div>

다음과 같은 레이어를 추가해보겠습니다. 3개의 버튼입니다.

<div id=button1 class=button onclick="moveCam(9, 6, 9.1, 0, 0, 0)">집앞</div>
<div id=button2 class=button onclick="moveCam(85, 116, 160, 3, 16, 63)">산뒤</div>
<div id=button3 class=button onclick="moveCam(-22, 9, -119, 0, 0, 0)">멀리보기</div>

그리고 스타일 태그에서도 다음 소스를 추가해 주세요.

.button
{
	position:fixed;
	width:100px;
	height:20px;
	border:1px solid white;
	cursor:pointer;
	background-color:rgba(101,77,165,0.7);
	padding:5px;
	text-align:center;
	color:white;
}
#button1 { 
	left: 5px;
	top: 5px;
}
#button2 { 
	left: 5px;
	top: 45px;
}
#button3 { 
	left: 5px;
	top: 85px;
}

그러면 이런 요소가 화면에 나타나실 겁니다.

역시 캔버스와는 관련없는 DIV 레이어 요소입니다.

3개의 div 모두에 class=button 이라는 부분이 있는데요.

이 부분은 단지 css 스타일 태그 적용을 위해서 정의한 부분입니다.

중복을 최소화 하기 위해서 그런것이지요.

css 관련 설명은 건너뛰도록 하겠습니다.

2가지만 설명드리자면,

배경색상을 결정하는 속성은 아래와 같은데요.

background-color:rgba(101, 77, 165, 0.7);

앞의 101. 77, 165 는 RGB 색상 코드값이고 0~255범위를 기준으로 합니다.

그리고 마지막의 0.7은 70%의 투명도 정도를 의미합니다.

그래서 뒤로 캔버스가 비쳐보이는 것이지요.

cursor:pointer; 라는 속성은 마우스를 갖다 대면 아래 그림과 같이 손모양으로 바뀌는 기능을 의미합니다.

이제 이 div 요소를 클릭하면, onclick 이벤트에 의해서 moveCam() 함수가 실행이 될텐데요. 사실 아직 눌러봐야 아무 변화가 없습니다. :D :D :D

<div id=button1 class=button onclick="moveCam(9, 6, 9.1, 0, 0, 0)">집앞</div>

카메라 움직임을 담당하는 moveCam()이란 함수를 아직 만든적이 없기 때문이지요.

이 함수는 three.js 내장함수가 아닙니다.

이제, three012.js 파일을 열어,

함수를 추가해볼까요?

기왕 해주는 김에 몇가지 더 수정해봅니다.

맨 윗 줄에 이런 소스가 있을텐데요.

var framesPerSecond=30;

그 아랫 줄에 다음 소스를 추가해 줍니다.

// 태양 초기 위치
parameters.azimuth+=0.2;

controls.enableDamping = true;
controls.dampingFactor = 0.1;

function moveCam(eye_x, eye_y, eye_z, target_x, target_y, target_z)
{
	camera.position.set ( eye_x, eye_y, eye_z );
	controls.target.set( target_x, target_y, target_z );
}

그리고 다음과 같은 소스를 찾아서,

parameters.azimuth+=0.001;

이렇게 바꿔 줍니다.

parameters.azimuth+=0.00001;

밤낮이 너무 빨라서 변화하는 시간을 좀 조정해보았는데요.

태양이 움직이는 속도를 지난 시간의 100분의 1로 낮춘 것이 이 부분입니다.

parameters.azimuth+=0.00001;

그리고 태양의 시작위치를 새벽에서 오전 10시정도로 바꾼 것이 이 부분이고,

parameters.azimuth+=0.2;

다음 소스는 그냥 부가적인 겁니다. 마우스로 화면을 움직일 때 스르륵 부드럽게 움직이게 적용한 것입니다.

controls.enableDamping = true;
controls.dampingFactor = 0.1;

참고로 dampingFactor 0.1 수치를 더 낮추면 마우스를 움직이다가 놓아도 한참동안 더 움직입니다.

이제 다음 소스가 진짜인데요.

카메라의 위치를 바꿔주는 함수입니다.

주어지는 파라미터 eye_x, eye_y, eye_z 는 카메라의 보는 시점의 x, y, z좌표를 의미하고

다음으로 주어지는 파라미터 target_x, target_y, target_z 는 카메라가 바라보는 지점 x, y, z 좌표를 의미합니다, 보통 0, 0, 0을 주면 집이 있는 중앙지점을 향해 바라보게 되지요.

function moveCam(eye_x, eye_y, eye_z, target_x, target_y, target_z)
{
	camera.position.set ( eye_x, eye_y, eye_z );
	controls.target.set( target_x, target_y, target_z );
}

만약 메모하셨던, 좌표지점을 카메라의 눈 지점으로 바라보게 하시려면

아래 소스에서 밑줄친 부분의 좌표값을 바꿔주시면 됩니다 :)

<div id=button1 class=button onclick="moveCam(9, 6, 9.1, 0, 0, 0)">집앞</div>

버튼은 얼마든지 추가하실 수 있으니,

다양한 좌표를 실습해 보시는 건 어떨른지요?

이번 시간에 변경된 소스는 three012.html 파일과 three012.js 파일입니다.

관련 소스를 게제합니다.

<three012.html>

<html>
	<head>
		<title>3차원웹 캔버스</title>
		<style>
			body { margin: 0; }
			canvas { width: 100%; height: 100% }
			#monitor { 
				position: fixed;
				bottom: 0;
		    right: 0;
				color:white;
				padding:5px;
				text-align:right;
				font-size:10pt;
			}
			.button
			{
				position:fixed;
				width:100px;
				height:20px;
				border:1px solid white;
				cursor:pointer;
				background-color:rgba(101,77,165,0.7);
				padding:5px;
				text-align:center;
				color:white;
			}
			#button1 { 
				left: 5px;
				top: 5px;
			}
			#button2 { 
				left: 5px;
				top: 45px;
			}
			#button3 { 
				left: 5px;
				top: 85px;
			}
		</style>
	</head>
	<body>
		<script src="https://threejs.org/build/three.min.js"></script>
		<script src="js/OrbitControls.js"></script>
		<script src="js/ColladaLoader.js"></script>
		<script src="js/Sky.js"></script>		
		<script src="js/Water.js"></script>
		<!-- 이 위쪽은 three.js 라이브러리 -->
		<script src="js/CrayCommon.js"></script>
		<script src="js/LoadCamera.js"></script>
		<script src="js/LoadLight.js"></script>
		<script src="js/LoadSkySea.js"></script>
		<script src="js/LoadModel.js"></script>
		<script src="js/three012.js"></script>
		<div id=monitor>모니터</div>
		<div id=button1 class=button onclick="moveCam(9, 6, 9.1, 0, 0, 0)">집앞</div>
		<div id=button2 class=button onclick="moveCam(85, 116, 160, 3, 16, 63)">산뒤</div>
		<div id=button3 class=button onclick="moveCam(-22, 9, -119, 0, 0, 0)">멀리보기</div>		
	</body>
</html>

<js/three012.js>

var framesPerSecond=30;

// 태양 초기 위치
parameters.azimuth+=0.2;

controls.enableDamping = true;
controls.dampingFactor = 0.1;

function moveCam(eye_x, eye_y, eye_z, target_x, target_y, target_z)
{
	camera.position.set ( eye_x, eye_y, eye_z );
	controls.target.set( target_x, target_y, target_z );
}

// 에니메이션 효과를 자동으로 주기 위한 보조 기능입니다.
var animate = function () {
	// 프레임 처리
	setTimeout(function() {
		 requestAnimationFrame(animate); 
	}, 1000 / framesPerSecond);

	if(home_mesh.obj != undefined)
	{
		home_mesh.obj.rotation.set(-90 * PI_PER_180, 0, -45 * PI_PER_180); 
	}

	water.material.uniforms[ 'time' ].value += 1.0 / 60.0;

	parameters.azimuth+=0.00001;

	phi = 2 * Math.PI * ( parameters.azimuth - 0.5 );

	light_sun.position.x = parameters.distance * Math.cos( phi );
	light_sun.position.y = parameters.distance * Math.sin( phi ) * Math.sin( theta );
	light_sun.position.z = parameters.distance * Math.sin( phi ) * Math.cos( theta );

	sky.material.uniforms['sunPosition'].value = light_sun.position.copy( light_sun.position );
	water.material.uniforms['sunDirection'].value.copy( light_sun.position ).normalize();

	cubeCamera.update( renderer, sky );
	controls.update();

	// 랜더링을 수행합니다.
	renderer.render( scene, camera );

	document.getElementById('monitor').innerText = 
		camera.position.x.toFixed(1) + ", " + 
		camera.position.y.toFixed(1) + ", " + 
		camera.position.z.toFixed(1);
};

// animate()함수를 최초에 한번은 수행해주어야 합니다.
animate();

아울러 본 예제의 실행화면을 직접 볼 수 있는 페이지는 아래와 같습니다.

http://dreamplan7.cafe24.com/canvas2/three012.html

 

3차원웹 캔버스

 

dreamplan7.cafe24.com

수고하셨습니다.

여기까지 시청해주셔서 감사합니다 :)

다음 강좌로 가는 길! / https://itadventure.tistory.com/65

 

3차원 웹, 창공을 누비는 카메라! - 25번째 시간

사람이 새처럼 하늘을 날아다닐 수 있다면 얼마나 좋을까요? 어디든지 순식간에 휙 날아갈 수 있게 말이죠. ​ 위험도 없고 염려없이 하늘을 날 수 있다면 마치 천사와 같은 삶이 아닐지요. 성서에서 말하는 천국..

itadventure.tistory.com