본문 바로가기

three.js 3편 그림자 만들기

by Recstasy 2019. 4. 23.

threejs 3편. Web에서 그림자를 만들어보자


3D는 현실의 연장선이다. 상식적으로 생각해보면 답이 나온다. 그림자를 만드려면 반드시 필요한 항목이 뭘까? 


"라이팅"


라이팅과 그림자는 '붓과 종이'의 관계다. 붓으로 종이에 글씨나 그림을 그리는 것처럼, 라이팅으로 그림을 그린다고 생각하면 된다. 라이팅은 종류와 상황에 따라서 다양한 연출이 가능한데, 3D 공간에서 사용하는 라이팅은 크게 '간접광'과 '직접광'으로 나뉜다. 직접광은 말 그대로 광원을 직접적으로 비추는 광원들을 말하며, 간접광은 1차적으로 부딪쳐 나온 빛이 2차적으로 반사된 빛이다. 


three.js에서는 직접광, 간접광 모두 구현가능하다. 단, 간접광은 계산 시간(랜더링 시간)이 직접광보다 많이 필요하다. 우선 간단한 직접광을 위해서 라이팅을 만들어보자.


▷그림자에 필요한 5가지

three.js에서 그림자를 생성하려면, 다음 5가지를 설정해야 한다.  


1) 랜더러.shadowMap.enabled

2) 그림자를 받을 수 있는 쉐이더 설정

3) 라이트.castShadow

4) 라이트.shadow.mapSize

5) 오브젝트.castShadow


CG에서 그림자는 '하나의 그림'이다. 그림자를 그린다면, 종이가 필요하다. 그 종이가 바로 '그림자맵(쉐도우맵)'이다. CG에 사용되는 모든 오브젝트들은 맵을 가지고 있다. CG라이팅 쉐이딩을 공부하다보면, 뭔가 어려울 것 같은 용어들이 난무한다. 

``텍스처맵, 쉐도우맵, 노멀맵, 라이트맵, 디스플레이스먼트맵 등....`` 

CG에 관한 개념을 이해하지 않고, CG용어들을 접하다 보면 멀리가지 못한다. 'xxx맵'은 단순하다. 절대 어려운 개념이 아니다. 그냥 초등학교 때 배웠던 '전개도'라 생각하면 된다. 


위의 전개도에서 왼쪽에 있는 도형은 직육면체이다. CG용어로는, 사각형 폴리곤 모델링이다. 사각형 폴리곤의 겉면에 이름을 새기고 싶다면 어떻게 해야 할까? 전개도에 답이 있다. 폴리곤을 펼쳐서 각 해당부위에 색을 지정하거나 문양을 새기면 직육면체의 겉면도 바뀐다. 


그렇다. CG에서 말하는 'xxx맵'은 전개도이다. 텍스처맵색상이나 그림이 그려진 전개도이며, 노멀 or 디스맵문양을 새긴 전개도, 라이트, 쉐도우맵빛이나 그림자가 그려진 전개도이다. 즉, CG에서 '그림자'를 만든다는 것은, 물체의 전개도에 그림자를 그려 넣음을 의미한다. 앞서 만든 3D 씬에 위의 5가지를 적용시켜 그림자를 만들어보자.


▷그림자 생성 1::뼈대 파일 생성(그림자를 받을 수 있는 쉐이더 지정)

기본 뼈대 파일은 다음과 같다. 아래의 뼈대 파일은 복사해서 재사용하도록 하자. (익숙해질 때까지 반복적으로 코딩하는 연습이 필요하다)


[◇기본 뼈대 파일◇]

<script>

 (function(){

  init()

})()

 function init(){

   var scene = new THREE.Scene();

   var camera = new THREE.PerspectiveCamera(45,window.innerWidth/window.innerHeight,0.1,1000);

   var renderer = new THREE.WebGLRenderer();

        renderer.setClearColor(new THREE.Color(0x000000));

        renderer.setSize(window.innerWidth*0.383,window.innerHeight*0.383);

      

   var axes = new THREE.AxesHelper(20);

        axes.position.set(-20,3,0);


   var planeGeometry = new THREE.PlaneGeometry(50,20)

   var planeMaterial = new THREE.MeshLambertMaterial({

                  color:0xffffff

    })

   var plane = new THREE.Mesh(planeGeometry,planeMaterial);

        plane.rotation.x = -0.5*Math.PI;

        plane.position.set(10,15,4);


   var sphereGeometry = new THREE.SphereGeometry(4,20,20);

   var sphereMaterial = new THREE.MeshLambertMaterial({color:0x7777ff})

   var sphere = new THREE.Mesh(sphereGeometry,sphereMaterial);

        sphere.position.set(-5,22,8);

        

   var spotLight = new THREE.SpotLight(0xFFFFFF);

        spotLight.position.set(-10,40,10);

        

        scene.add(axes);

        scene.add(sphere);

        scene.add(plane);

        scene.add(spotLight);


        camera.position.set(-30,40,30);

        camera.lookAt(scene.position)

       

       document.getElementById('webgl-shadow').appendChild(renderer.domElement);

       renderer.render(scene,camera);

}

</script>


스피어(구)폴리곤을 추가하였고, 위치값을 알기 위해서 axes객체를 추가하였다. 뼈대 파일을 만드는 과정에서 화면이 제대로 나오지 않는다면, position값을 움직여보자. 그림자를 받기 위해서 MeshBasicMaterial이 아닌 MeshLambertMaterial로 설정한 부분도 주의할 점이다. 뼈대 파일에서 라이트는 '집중조명' 스폿라이트를 생성했다. 스폿라이트는 특정 오브젝트에 광원을 집중할 수 있고, 그림자를 생성하기에 유리한 라이팅이다.


뼈대 파일을 만들었다면, 본격적으로 그림자를 생성하는 설정을 해보자.


▷그림자 생성 2::랜더러 설정

그림자를 생성하려면 랜더러에서 쉐도우맵을 켜야(?)한다. 그림자 맵을 랜더링 하겠다는 일종의 약속이다. 뼈대 파일의 renderer.setClearColor()와 renderer.setSize()아래에 renderer.shadowMap.enabled를 넣는다.

 

 renderer.setClearColor(new THREE.Color(0x000000));

 renderer.setSize(window.innerWidth*0.385 , window.innerHeight*0.385);

 renderer.shadowMap.enabled = true;


랜더러의 쉐도우맵을 켜도 아래처럼 아무 변화가 발생하지 않는다. 오브젝트(폴리곤)와 라이트에서 그림자를 설정하지 않았기 때문이다.


▷그림자 생성 3::라이팅 설정 

랜더러에서 그림자맵을 활성화 한 이후에는 라이트의 그림자를 활성화해야 한다. 라이트에서는 그림자맵의 생성 유무, 그림자 맵의 크기 등...을 설정할 수 있다. 뼈대 파일에서 'castShadow'항목을 'true'로 설정하자. 그림자맵의 사이즈로는 three.js의 api를 활용한다. 여기서 사이즈가 커지면 랜더링 시간이 길어진다는 점에 주의하자.

 spotLight.castShdow = true;

 spotLight.shadow.mapSize = new THREE.Vector2(1024,1024);

랜더러의 shadowMap.enabled, 라이트의 castShadow와 맵 설정까지 했지만 아직 그림자는 나타나지 않는다. 그 이유는, 오브젝트에서 아직 그림자 맵을 받아들일 준비가 되지 않았기 때문이다. 라이트의 그림자맵을 오브젝트가 제대로 받아야만 그림자가 나타난다.


▷그림자 생성 4::오브젝트 설정 

그림자를 받아들이는 오브젝트들(폴리곤)에는 그림자와 관련된 항목(properties)들이 있다. 오브젝트에서 그림자를 설정할 때, 반드시 알아둬야 할 부분은 '그림자를 생성하는 쪽'은 castShadow 설정을 해주고, 그림자를 받는 쪽은 'receiveShadow'설정을 해야한다는 점이다.


예시자료에서는 지면(plane)이 그림자를 받고 있고, Sphere(구)가 그림자를 생성하고 있다. 따라서 sphere는 castShadow를 'true'로 설정하고, plane은 'receiveShadow'를 'true'로 한다.

[plane]::그림자를 받는 물체


plane.receiveShadow = true;


[sphere]::그림자를 생성하는 물체

sphere.castShadow = true;


지면(plane)에 그림자를 받는 설정과 '구'에 그림자를 생성하는 설정을 마무리하면 드디어 그림자가 나타난다. 라이트를 이동하면 아래처럼 그림자가 길어지거나 짧아진다. 그림자의 밝기와 색상도 조절가능하다. 결론적으로 PC기반 CG프로그램에서 쉽게 생성할 수 있는 그림자를 Web에서 생성하려면 꽤 까다로운 조건들을 충족시켜야 한다. HDR을 활용한 간접광 포스팅에서 좀더 쉽게 그림자를 생성할 수 있는 방법을 연구해보기로 하고, 그림자에 대한 최종 정리를 하면 다음과 같다.


1) 랜더러 설정::renderer.shadowMap.enabled = true

2) 라이트 설정::

   - spotLight.castShadow = true

   - spotLight.shadowMapSize = new THREE.Vector2(1024,1024)

3) 오브젝트 설정::

   - 그림자를 생성하는 오브젝트 : 오브젝트명.castShadow = true

   - 그림자를 받는 오브젝트 : 오브젝트명.receiveShadow = true

4) 쉐이더 설정::

   - 그림자를 받을 수 있는 '램버트'와 같은 쉐이딩 설정


댓글

최신글 전체

이미지
제목
글쓴이
등록일