웹3.0이 대두되고 있지만 웹의 강력한 힘은 여전히 '반응형'이다. 사용자의 동작에 따라 웹은 '반응'하고, 사용자가 원하는 결과(아웃풋)가 곧 (웹)기술의 혁신이다. 그런데 3D로 제작되는 웹은 사용자와 반응하는 수준에 그치지 않고, 가상 현실을 만들어낸다. 이 같은 가상현실에서 필수적인 기술은 '컨트롤'이다.
오브젝트를 생성하는 컨트롤러 만들기
사용자가 특정 동작을 웹에 지시하려면 'dat.GUI()'가 필요하다.
이를 위해, 'controls 객체'를 생성한 후 dat.GUI()와 연결한다. controls객체에 매서드를 생성하는 부분이 '사용자 컨트롤'의 핵심이다.
1) controls 프로토타입 객체 생성하기
controls 객체에서 'addCube', 'removeCube'매서드를 추가한다. 프로퍼티(항목)로는 numberOfObjects를 지정한다.
let controls = new functioin(){
this.rotationSpeed = 0.02;
this.numberOfObjects = scene.children.length;
//매서드 추가
this.removeCube = function(){
let allChildren = scene.children;
let lastObject = allChildren[allChildren.length - 1];
if(lastObject instanceof THREE.Mesh){
scene.remove(lastObject);
this.numberOfObjects = scene.children.length;
}
}
}
rotationSpeed 항목은 cube의 속도를 조절하며, numberOfObject는 scene의 모든 요소들의 개수를 기록한다. scene의 요소들의 개수를 알 수 있어야만 특정 오브젝트를 삭제 혹은 변형을 줄 수 있으므로 위의 코드에서 length는 중요한 부분이다. ('allChildren'은 배열)
또, lastObject는 마지막에 생성된 오브젝트를 의미한다. allChildren의 index값에 'allChildren.length - 1'을 넣으면, 마지막에 생성된 오브젝트를 선택할 수 있다. 그리고 if문을 통해 lastObject의 데이터를 instanceof로 검색하여, THREE.Mesh 특성을 갖는 항목을 scene.remove()매서드로 제거할 수 있다.
마지막으로 scene.children.length를 통해 numberOfObjects값을 현재 상태로 리셋하는 부분에 주의하자.
2) addCube매서드
let controls = new function(){
this.rotationSpeed = 0.02;
..
(중략)
this.addCube = function(){
let cubeSize = Math.ceil((Math.random()*3));
let cubeGeometry = new THREE.BoxGeometry(cubeSize,cubeSize,cubeSize);
let cubeMaterial = new THREE.MeshLambertMaterial({ color:0xffffff*Math.random() })
let cube = new THREE.Mesh(cubeGeometry,cubeMaterial);
cube.castShadow = true;
cube.name = "cube-"+scene.children.length;
cube.position.x = -10 + Math.round((Math.random()*planeGeometry.parameters.width))
cube.position.y = Math.round((Math.random()*5));
cube.position.z = -20 + Math.round((Math.random()*planeGeometry.parameters.height));
scene.add(cube);
this.numberOfObjects = scene.children.length;
}
this.outputObjects = function(){
console.log('scene.children');
}
}
addCube를 추가하는 방법은 cube를 처음부터 생성하는 three.js 문법과 거의 일치한다. 단, scene.children.length와 같은 three.js API와 관련된 부분과 Math.round, Math.ceil(올림) 등.... 랜덤 & 반올림과 같은 함수를 사용하여 무작위로 오브젝트를 생성하게끔 구현했다.
3) gui 추가
controls 프로토타입 객체는 dat.gui()함수의 항목으로 넘겨준다. 이때 gui.add().listen()과 같은 방식으로 체인을 만들면, 아래와 같이 이벤트를 생성할 수 있다. .listen()이벤트는 numberOfObjects 데이터가 변할 때마다 자동으로 상태를 체크한다.
let gui = new dat.GUI();
gui.add(controls,'rotationSpeed',0,0.5);
gui.add(controls, 'addCube');
gui.add(controls, 'removeCube');
gui.add(controls, 'outputObjects');
gui.add(controls, 'numberOfObjects').listen();
4) .traverse()매서드 활용
three.js라이브러리에서 제공하는 .traverse()매서드는 forEach()구문과 비슷하다. traverse()함수는 scene의 child항목들을 반복적으로 검사하는 기능을 수행한다. traverse()함수 내부에 THREE.Mesh나 THREE.SpotLight 등... three.js의 요소들을 넣으면 특정 항목에 영향값을 줄 수 있다.
function render(){
trackballControls.update(clock.getDelta());
// ..
// (중략)
scene.traverse(function(e){
if(e instanceof THREE.Mesh && e != plane){
e.rotation.x += controls.rotationSpeed;
e.rotation.y += controls.rotatioinSpeed;
e.rotation.z += controls.rotationSpeed;
}
})
requestAnimationFrame(render);
renderer.render(scene,camera);
}
scene내의 요소들을 검사한 후, THREE.Mesh중에서 plane이 아닌 경우에는 회전을 하도록 구현한다. 최종 코드를 구현하면, 아래와 같다. (gui.dat 컨트롤러는 우측 상단에 위치)
//최종코드
(function () {
init();
})()
function init() {
var scene;
var camera;
var renderer;
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(new THREE.Color(0x000000));
renderer.setSize(window.innerWidth * 0.375, window.innerHeight * 0.375);
renderer.shadowMap.enabled = true;
var planeGeometry = new THREE.PlaneGeometry(60, 40, 1, 1);
var planeMaterial = new THREE.MeshLambertMaterial({
color: 0xffffff
});
var plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.position.set(0, 0, 0);
plane.rotation.x = -0.5 * Math.PI;
plane.receiveShadow = true;
plane.castShadow = true;
scene.add(plane);
var axes = new THREE.AxesHelper(5);
axes.position.set(-5, 10, 5);
scene.add(axes);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(-40, 60, -10);
spotLight.castShadow = true;
spotLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
scene.add(spotLight);
camera.position.set(-30, 40, 30);
camera.lookAt(scene.position);
document.getElementById('webgl-slc').appendChild(renderer.domElement);
//사용자 반응 var step = 0;
var controls = new function () {
this.rotationSpeed = 0.02;
this.numberOfObjects = scene.children.length;
this.removeCube = function () {
var allChildren = scene.children;
var lastObject = allChildren[allChildren.length - 1]
if (lastObject instanceof THREE.Mesh) {
scene.remove(lastObject);
this.numberOfObjects = scene.children.length;
}
};
this.addCube = function () {
var cubeSize = Math.ceil((Math.random() * 3));
var cubeGeometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
var cubeMaterial = new THREE.MeshLambertMaterial({
color: Math.random() * 0xffffff
});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.name = "cube-" + scene.children.length;
cube.position.x = -50 + Math.round((Math.random() * planeGeometry.parameters.width));
cube.position.y = Math.round((Math.random() * 5));
cube.position.z = -50 + Math.round((Math.random() * planeGeometry.parameters.height));
scene.add(cube);
this.numberOfObjects = scene.children.length;
}
this.outputObjects = function () {
console.log(scene.children);
}
}
var gui = new dat.GUI({
autoPlace: false,
});
wdGUI.append( gui.domElement );
gui.domElement.id = 'webdol-gui';
gui.add(controls, 'rotationSpeed', 0, 0.5);
gui.add(controls, 'addCube');
gui.add(controls, 'removeCube');
gui.add(controls, 'outputObjects');
gui.add(controls, 'numberOfObjects').listen();
render()
function render() {
scene.traverse(function (e) {
if (e instanceof THREE.Mesh && e != plane) {
e.rotation.x += controls.rotationSpeed;
e.rotation.y += controls.rotationSpeed;
e.rotation.z += controls.rotationSpeed;
}
})
requestAnimationFrame(render);
renderer.render(scene, camera);
}
}
'웹개발 자료실 > three.js 프론트개발 Code' 카테고리의 다른 글
『Firebase Storage + GLTF 3D Model import Test』 (0) | 2021.03.29 |
---|---|
『firebase Storage + fbx Loader Test』 (2) | 2021.03.26 |
「three.js」키보드로 3D 오브젝트 움직이기 (0) | 2019.05.06 |
「three.js」웹에서 파편을 3D로 구현해보자 (0) | 2019.05.03 |
「three.js」웹에서 3D애니메이팅을 구현해보자 (0) | 2019.04.24 |
댓글