๐ป ๊ทธ๋ฆผ์๋ฅผ ๋ง๋๋ ์๋ฆฌ
three.js
์์๋ ๊ทธ๋ฆผ์๋ฅผ ๋ง๋ค๊ธฐ ์ํด Shadow Map
์ ์ด์ฉํฉ๋๋ค.
Shadow Map
์ด๋ ๊ทธ๋ฆผ์ ์ ๋ณด๋ฅผ ์ ์ฅํ texture
๋ฅผ ์๋ฏธํ๋ฉฐ, ๋ง๋ค์ด์ง๋ ๊ณผ์ ๊ณผ ์ด๋ฅผ ์ด์ฉํด ๊ทธ๋ฆผ์๋ฅผ ๋ ๋๋งํ๋ ๊ณผ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
scene
์ ๋ ๋๋งํ๊ธฐ ์ง์ ์,light
์ ์์ ์์ ๋ ๋๋ง์ ๋จผ์ ํฉ๋๋ค.์ด๋,
mesh
๋ค์material
์MeshDepthMaterial
๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค.Light
์ ์์ ์์ ๋ ๋๋ง๋scene
์Shadow Map
์ด๋ผ๊ณ ๋ถ๋ฆฌ๋texture
์ ์ ์ฅ๋ฉ๋๋ค.๋ง๋ค์ด์ง
Shadow Map
๋ค์ ์ข ํฉํด ์ต์ขscene
์ ๋ ๋๋งํฉ๋๋ค.
20๊ฐ์ mesh
์ 5๊ฐ์ light
๋ค์ด ์๋ค๋ฉด, ์ด 6๋ฒ ๋ ๋๋ง์ด ์ผ์ด๋๊ฒ ๋ฉ๋๋ค. 5๊ฐ์ light
์์ ์์ ํ๋ฒ์ฉ, ์ด ๊ณผ์ ์์ ๋์จ Shadow Map
๋ค์ ํฉ์ณ ์ต์ข
์ ์ผ๋ก ํ๋ฒ ๋ ๋๋ง์ด ๋ฐ์ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
๋ง์ฝ ์กฐ๋ช
ํ๋๋ฅผ ๋ ์ถ๊ฐํ๋ค๋ฉด, 7๋ฒ ๋ ๋๋ง ํ์ ์ต์ข
๊ฒฐ๊ณผ๊ฐ ๋์ค๊ฒ ๋ฉ๋๋ค.
๋ค์์ light
๋ณ๋ก ๋ง๋ค์ด์ง Shadow Map
์ ์๊ฐํํ ์์์
๋๋ค.
shadow_map_viewer
๐ป ๊ทธ๋ฆผ์ ๋ง๋ค๊ธฐ
๊ทธ๋ฆผ์๋ฅผ ๋ง๋ค๊ธฐ ์ํด์๋ ๋จผ์ renderer
์ shadowMap
์ ํ์ฑํ ์์ผ์ฃผ์ด์ผ ํฉ๋๋ค.
1
renderer.shadowMap.enabled = true;
๋ํ Object3D
๊ฐ ๊ทธ๋ฆผ์๋ฅผ ๋ง๋๋์ง(Shadow Map
์์ฑ), ๋ง๋ค์ด์ง ๊ทธ๋ฆผ์๊ฐ ๋งฝํ๋์ง์ ๋ฐ๋ผ castShadow
์ receiveShadow
์ต์
์ ํ์ฑํ ์์ผ์ฃผ์ด์ผ ํฉ๋๋ค.
1
2
mesh1.castShadow = true;
mesh2.receiveShadow = true;
์ถ๊ฐ๋ก ์์ ์ดํด๋ณธ ์กฐ๋ช
๋ค์ค DirectionalLight
, SpotLight
, PointLight
๋ง ๊ทธ๋ฆผ์๋ฅผ ๋ง๋ค ์ ์์ต๋๋ค.
๐จโ๐ป DirectionalLight
1
2
3
4
5
6
7
8
9
10
11
12
// ...์๋ต...
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.castShadow = true;
// ...์๋ต...
planeMesh.receiveShadow = true;
cubeMesh.castShadow = true;
cubeMesh.receiveShadow = true;
sphereMesh.castShadow = true;
sphereMesh.receiveShadow = true;
// ...์๋ต...
์ ์ฝ๋๋ DirectionalLight
๋ฅผ ์ด์ฉํ์ฌ Mesh
์ ๊ทธ๋ฆผ์๋ค์ ์์ฑํ๋ ์ฝ๋์
๋๋ค.
๊ทธ๋ฆผ์๋ฅผ ๋ง๋ค๊ธฐ ์ํด์ ์กฐ๋ช
์ castShadow
์ต์
์ ํ์ฑํ ํด์ค๊ฒ์ ์ ์ ์์ต๋๋ค. ๋ํ ๊ทธ๋ฆผ์๊ฐ ๋งฝํ planeMesh
์๋ receiveShadow
์ต์
์, ๊ทธ๋ฆผ์๊ฐ ์์ฑ๋ cubeMesh
์ sphereMesh
์๋ receiveShadow
์ต์
์ ํ์ฑํ ํ์ต๋๋ค.
์๋๋ ์์ฝ๋๋ฅผ ์คํ์ํจ ๊ฒฐ๊ณผ์ ๋๋ค.
๐ ๊ทธ๋ฆผ์ ์ต์ ํํ๊ธฐ
์กฐ๋ช
์ ์ํด ๋ง๋ค์ด์ง ๊ทธ๋ฆผ์๋ ์ํฉ์ ๋ฐ๋ผ ์ต์ ํ๋ฅผ ํด์ผํ ํ์๊ฐ ์์ต๋๋ค.
์ ๊ฒฐ๊ณผ์์๋ ๊ทธ๋ฆผ์๊ฐ ์๋ ค ์์ฑ๋ ๊ฒ์ ๋ณผ ์ ์๋๋ฐ, ์๋์์ ์ดํด๋ณผ ์์๋ฅผ ๋ฐ๋ผ ์ต์ ํ๋ฅผ ์์ผ๋ณด๊ฒ ์ต๋๋ค.
camera
Shadow Map
์ ๋ง๋ค๋light
์ ์์ ์์ ๋ ๋๋ง์ ์งํํ๋๋ฐ, ์ด๋light
์camera
๋ฅผ ์ด์ฉํ๊ฒ ๋ฉ๋๋ค. ์ดcamera
๋scene
์ ๋ ๋๋งํ๋camera
์ ๊ฐ์ ๊ฐ์ฒด์ ๋๋ค. ๋ฐ๋ผ์CameraHelper
๋ก ์๊ฐํ ํ ์ ์์ผ๋ฉฐ, ๊ทธ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค.์ ๊ฒฐ๊ณผ๋ฅผ ํตํด,
DirectionalLight
์camera
๋OrthographicCamera
์์ ์ ์ ์์ต๋๋ค.camera
๋ฅผ ํตํด ์ต์ ํ ํ ์ ์๋ ์์๋ ๋ ๋๋ง ๋ฒ์ ์ ๋๋ค. ๋ ๋๋ง ๋ฒ์๊ฐ ์์์ง ์๋ก, ๊ทธ๋ฆผ์๋ ์ ๊ตํด์ง์ง๋ง, ๋๋ฌด ์์์ง๋ฉด ๊ทธ๋ฆผ์๊ฐ ์๋ฆฌ๊ฒ ๋ฉ๋๋ค.
์ ์ด๋ฏธ์ง์์๋ ํ์์ ํด๋นํ๋ ๋ฒ์๋ฅผ ๋ํ๋ณด๊ฒ ์ต๋๋ค.1 2 3 4 5 6
// ...์๋ต... directionalLight.shadow.camera.top = 3; directionalLight.shadow.camera.bottom = -8; directionalLight.shadow.camera.left = -7; directionalLight.shadow.camera.right = 7; // ...์๋ต...3
์ถ๊ฐ๋ก
near
,far
๋ ๋ฒ์์ ๋ง๊ฒ ์กฐ์ ํด๋ณด๊ฒ ์ต๋๋ค.1 2 3 4
// ...์๋ต... directionalLight.shadow.camera.near = 1; directionalLight.shadow.camera.far = 15; // ...์๋ต...
Render Size
Shadow Map
์ ์ผ์ข ์texture
๋ก, ํฌ๊ธฐ๋ฅผ ์ง์ ํ ์ ์์ต๋๋ค.
๊ฐ์ด ํด์๋ก, ๊ทธ๋ฆผ์๊ฐ ์ ๊ตํด์ง์ง๋ง, ์ฐ์ฐ๋์ด ๋ง์์ ธ ์ฑ๋ฅ์ด ๋จ์ด์ง ์ ์์ต๋๋ค. ๋ํ ๊ฐ์mipmap
์์ฑ์ ์ํด2
์ ๊ฑฐ๋ญ์ ๊ณฑ์ผ๋ก ์ค์ ํด์ฃผ์ด์ผ ํฉ๋๋ค.1 2 3 4
// ...์๋ต... directionalLight.shadow.mapSize.width = 1024; directionalLight.shadow.mapSize.height = 1024; // ...์๋ต...
Shadow Map Type
ํ์ ํน์ง BasicShadowMap ์ฑ๋ฅ์ ์ข์ง๋ง, ํ์ง์ด ๋ฎ์ต๋๋ค. PCFShadowMap ๊ธฐ๋ณธ๊ฐ์ผ๋ก, ๋ถ๋๋ฌ์ด ํ ๋๋ฆฌ๋ฅผ ๊ฐ์ง ๊ทธ๋ฆผ์๋ฅผ ์ป์ ์ ์์ง๋ง ์ฑ๋ฅ์ ์กฐ๊ธ ๋จ์ด์ง๋๋ค. PCFSoftShadowMap ๋ถ๋๋ฌ์ด ๊ทธ๋ฆผ์๋ฅผ ์ป์ ์ ์์ผ๋ฉฐ,
ํนํ ์ ํด์๋์Shadow Map
์ผ ๋, ๋์ฑ ๋ถ๋๋ฌ์ ์ง๋๋ค. ์ฑ๋ฅ์ ๋ฎ์ต๋๋ค.VSMShadowMap ํด๋น ํ์ ์ ์ฌ์ฉํ๋ ค๋ฉด,
receiveShadow
๊ฐ ํ์ฑํ๋ ๊ฐ์ฒด๋ค์castShadow
์ต์ ์ ํ์ฑํ ํด์ผํฉ๋๋ค.blur
radius
์์ฑ์ ํตํด ๊ทธ๋ฆผ์ ํ ๋๋ฆฌ์ ํ๋ฆฐ(blur
) ํจ๊ณผ๋ฅผ ์ค ์ ์์ต๋๋ค.
๊ฐ์ด1
๋ณด๋ค ํฌ๋ฉด ํจ๊ณผ๊ฐ ์ ์ฉ๋๋ฉฐ,Shadow Map
์type
์ดPCFSoftShadowMap
๋BasicShadowMap
์ผ ๊ฒฝ์ฐ๋ ์ ์ฉ๋์ง ์์ต๋๋ค.1 2 3
// ...์๋ต... directionalLight.shadow.radius = 10; // ...์๋ต...
๐จโ๐ป SpotLight
1
2
3
4
// ...์๋ต...
const spotLight = new THREE.SpotLight(0xffffff, 1);
spotLight.castShadow = true;
// ...์๋ต...
์ ์ฝ๋๋ฅผ ์ด์ฉํ์ฌ SpotLight
์ ๊ทธ๋ฆผ์๋ฅผ ์์ฑํ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๐ ๊ทธ๋ฆผ์ ์ต์ ํํ๊ธฐ
๊ทธ๋ฆผ์๋ฅผ ์ต์ ํํ๋ ๋ฐฉ๋ฒ์ DirectionalLight
์ ๊ฑฐ์ ๋น์ทํฉ๋๋ค.
์ด ๋ถ๋ถ์์๋ ๋ค๋ฅธ์ ์ ์์ฃผ๋ก ์์ ํ๊ฒ ์ต๋๋ค.
camera
SpotLight
๋ ๊ทธ๋ฆผ์๋ฅผ ๋ง๋ค๊ธฐ ์ํดPerspectiveCamera
๋ฅผ ์ฌ์ฉํฉ๋๋ค.CameraHelper
๋ฅผ ํตํด ์๊ฐํํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค.SpotLight
์camera
์fov
๋angle
๊ณผ ์ฐ๊ฒฐ๋์ด ์์ผ๋ฉฐ,aspect
๋Shadow Map
์ ํฌ๊ธฐ์ ๋ฐ๋ผ ์๋์ผ๋ก ์ ํด์ง๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ผ์near
์far
๋ฅผ ์กฐ์ ํ ์ ์์ต๋๋ค.
๐จโ๐ป PointLight
1
2
3
4
// ...์๋ต...
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.castShadow = true;
// ...์๋ต...
์ ์ฝ๋๋ฅผ ์คํํ ๊ฒฐ๊ณผ๋ ์๋์ ๊ฐ์ต๋๋ค.
๐ ๊ทธ๋ฆผ์ ์ต์ ํํ๊ธฐ
camera
PointLight
๋ ๊ทธ๋ฆผ์๋ฅผ ๋ง๋ค๊ธฐ ์ํดPerspectiveCamera
๋ฅผ ์ฌ์ฉํฉ๋๋ค.
PointLight
camera
์near
์far
์์ฑ์ ์กฐ์ ํด ๋ ๋๋ง ๋ฒ์๋ฅผ ์ค์ ํ ์ ์์ต๋๋ค.SpotLight
์ ๊ฐ์camera
๋ฅผ ์ฌ์ฉํ์ง๋ง, ์ฐจ์ด์ ์PointLight
๋ ๋ชจ๋ ๋ฐฉํฅ์ผ๋ก ๋น์ ๋ฐ์ฐํ๊ธฐ ๋๋ฌธ์ ์ ์ก๋ฉด์ฒด์ ๊ฐ๋ฉด์ ์กฐ๋ช ์ ๋์๊ฒ๊ณผ ๊ฐ์ต๋๋ค. ์ฆPointLight
๋ ์ ์ก๋ฉด์ฒด ๊ฐ ๋ฐฉํฅ์ผ๋ก 6๋ฒ์ ๋ ๋๋งํด์ผ ํจ์ผ๋ก ๋ค๋ฅธ ์กฐ๋ช ๋ณด๋ค ์ฑ๋ฅ์ด ๋จ์ด์ง๋๋ค.
๐ป ๊ฐ์ง ๊ทธ๋ฆผ์ ๋ง๋ค๊ธฐ
์์์ ์ดํด๋ณด์๋ฏ์ด, ๊ทธ๋ฆผ์๋ฅผ ๋ง๋๋ ๊ณผ์ ์ ๊น๋ค๋กญ๊ณ ๋น์ฉ๋ ํฝ๋๋ค.
๊ทธ๋ฆผ์ ํํ์ texture
๋ฅผ ์ด์ฉํ๋ฉด, ๋ณต์กํ ์ฐ์ฐ ์์ด๋ ๊ทธ๋ฆผ์๊ฐ ์๊ธฐ๋ ํจ๊ณผ๋ฅผ ๋ผ ์ ์์ต๋๋ค. ํด๋น texture
๋ฅผ ์
ํ PlaneGeometry
๋ฅผ ๊ทธ๋ฆผ์๊ฐ ์๊ฒจ์ผํ๋ ์์น์ ๋๋ฉด ๋ฉ๋๋ค.
์ texture
๋ฅผ ์ด์ฉํด์ ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// ...์๋ต...
// Fake Shadow
const fakeShadowGeometry = new THREE.PlaneGeometry(6, 6);
const fakeShadowMaterial = new THREE.MeshBasicMaterial({
alphaMap: fakeShadowTexture,
transparent: true,
color: 0x000000,
});
const fakeShadow = new THREE.Mesh(fakeShadowGeometry, fakeShadowMaterial);
fakeShadow.position.set(0, 0.01, 0);
fakeShadow.rotation.set(-(Math.PI * 0.5), 0, 0);
// ...์๋ต...
์ ์ฝ๋๋ PlaneGeometry
์ ๊ทธ๋ฆผ์ texture
๋ฅผ ์ ์ฉ์ํจ ํ, ์๊ณ๋ฐฉํฅ์ผ๋ก ํ์ ์ํค๋ ์ฝ๋์
๋๋ค.
y
์ถ์ 0
๋ณด๋ค ๋ฏธ์ธํ๊ฒ ํฌ๊ฒ ์ค์ ํด์ค ์ด์ ๋, z-fighting
๋๋ฌธ์
๋๋ค.
์ ์ฝ๋๋ฅผ ์คํ์ํจ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
Sphere
๋ถ๋ถ์ ๊ทธ๋ฆผ์๊ฐ ์๊ฒผ์์ ์ ์ ์์ต๋๋ค.
์ด ๋ฐฉ์์ ๋ฌธ์ ์ ์, ๊ทธ๋ฆผ์๊ฐ ์์ง์ด์ง ์๋๋ค๋ ์ ์
๋๋ค. Light
์ ์ํ ๊ทธ๋ฆผ์๊ฐ ์๋๊ธฐ ๋๋ฌธ์ mesh
๊ฐ ์์ง์ฌ๋ ๊ทธ๋ฆผ์๋ ์๋์ ์๋ฆฌ๋ฅผ ์ ์งํฉ๋๋ค. ๋ํ, mesh
์ ๊ฑฐ๋ฆฌ์ ์๊ด์์ด ํญ์ ๊ฐ์ ๊ทธ๋ฆผ์๊ฐ ๋์ต๋๋ค.
๋จผ์ mesh
๊ฐ ์ง๋ฉด์์ ๋ฉ์ด์ง์ ๋ฐ๋ผ, ๊ทธ๋ฆผ์๊ฐ ๋ค๋ฅด๊ฒ ๋ ๋๋ง ๋ ์ ์๋๋ก ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ...์๋ต...
const animation = () => {
const elapsedTime = clock.getElapsedTime();
sphere.position.y = Math.abs(Math.sin(elapsedTime * 3) * 5) + 3;
fakeShadow.material.opacity = 1 - sphere.position.y * 0.05;
renderer.render(scene, camera);
requestAnimationFrame(animation);
};
requestAnimationFrame(animation);
// ...์๋ต...
Sphere
๊ฐ ๋ฉ์ด์ง์ ๋ฐ๋ผ(y
์ขํ๊ฐ ์ปค์ง์ ๋ฐ๋ผ), ๊ทธ๋ฆผ์์ ํฌ๋ช
๋๋ 0
์ ๊ฐ๊น์์ ธ์ผ ํจ์ผ๋ก 1
์์ ๋นผ์ค๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.
์ ์ฝ๋๋ฅผ ์คํ์ํจ ๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
๋ค์์ผ๋ก Mesh
์ ์์ง์๊ณผ ๊ทธ๋ฆผ์ texture
๋ฅผ ์ ์ฉํ Mesh
๊ฐ์ ์์น๋ฅผ ๋๊ธฐํ ์์ผ๋ณด๊ฒ ์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// ...์๋ต...
const animation = () => {
const elapsedTime = clock.getElapsedTime();
sphere.position.x = Math.sin(elapsedTime * 5);
sphere.position.z = Math.cos(elapsedTime * 5);
sphere.position.y = Math.abs(Math.sin(elapsedTime * 3) * 5) + 3;
fakeShadow.position.x = sphere.position.x;
fakeShadow.position.z = sphere.position.z;
fakeShadow.material.opacity = 1 - sphere.position.y * 0.05;
renderer.render(scene, camera);
requestAnimationFrame(animation);
};
requestAnimationFrame(animation);
// ...์๋ต...
Sphere
์ ์์ด๋์ ์ํด ๋ฐ๋ ์ขํ๋ฅผ, ๊ทธ๋ฆผ์ texture
๊ฐ ์ ์ฉ๋ mesh
์ ์ ์ฉํด ์ค์ผ๋ก์จ ๋ mesh
์ฌ์ด์ ์์น๋ฅผ ๋๊ธฐํ ํด์ฃผ๋ ์ฝ๋์
๋๋ค.
๊ฒฐ๊ณผ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.