diff --git a/README.md b/README.md index 76a4b4a824230d1db28cf75bc08e5ee618e9cc65..51870ce9e866bf856384f3538eaa38f1c2fe066c 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,35 @@ # CG Tutorial +## 아이템 선정 + +그림자는 세상의 빛과 물체에 대한 표현을 더욱 풍요롭게 해주는 요소이다. + +밋밋한 그림도 그림자가 추가됨에 따라 다른 느낌을 준다. + +게임개발을 지망하고 있는 학생으로서 그래픽의 풍요로움은 곧 로망이었으며, 그림자를 표현해보고자 하는 생각이 있었다. + +왜 하필 pointlightshadow였냐 하면은, directionallight는 태양을 모델링하며, 면 광원은 그림자 경계를 더 실사있게 표현함으로써 현실세계의 느낌을 받게한다. 하지만 지나친 실사 묘사는 게임으로 한다면 오히려 몰입감을 떨어트리는 요소가 된다. 비실사, 실사의 중간에서 오묘하게 줄다리기를 해야 게임으로서 보는 재미가 증가한다. + +그림자의 뚜렷한 경계를 나타내는 pointlight를 이용하면 실제적이면서 비실사의 그래픽을 나타낼 수 있었기 때문에 이를 선택하게 되었다. + +## 개발 과정 + +three.js에서 제공하는 복잡한 기능을 어떻게든 사용해 보아야겠다는 생각은 하지 않았다. 선택한 광원의 특징이 어떻게 단순한 기능을 통해 나타나는지를 중점으로 두었다. + +three.js document의 코드를 기반으로, 각각의 기능을 추가, 변경하고 각각의 결과를 비교해봄으로써 기능에 대해 체감할 수 있는 형태로 제작하였다. + +## 느낀 점 + +그래픽이 직접 변화하는 것을 눈으로 보는 것이 그래픽스를 배우는 데에 큰 즐거움이라 생각한다. 그렇기에 인터렉티브한 요소를 집어넣지 못한 것이 아쉬울 따름이다. + +그리고, pointlight와 shadow자체의 이론적 관계보다 three.js에서 이를 처리하는 방식과 관련하여 학습하는 부분이 어려웠다. +perspective camera의 경우 pointlight에선 항상 아래를 바라본다. 이 방향을 움직일 수 없어 결국 도형 전체를 움직일 수 밖에 없었고, 값의 조절이 직관적이지 않게 되어버렸다. + +정말 많은 경우의 수를 떠올려봤는데, 이를 막상 표현하고자하니 어려움이 있었다. 수학적으로 그래픽을 설계해야하는 점이 어려웠다. +또한 그래픽스 파이프라인과 transform 순서와 같은 요소를 적절히 사용하지 않은점이 아쉬움에 남는다. +다만 transform순서와 같은 경우는 쉽게 추가할 수 있을 것으로 예상된다. + +## Reference + +* Hwan prefessor three.js example code +* three.js document pointlightshadow code \ No newline at end of file diff --git a/script.js b/script.js index 9b7577bf7eafa7165ac4d48cc6d00c05d97daed6..967a339b14506130c4ccb00a7adfe566ee20251a 100644 --- a/script.js +++ b/script.js @@ -3,12 +3,12 @@ const h_scr2 = window.innerWidth; const v_scr2 = window.innerHeight; const scene2 = new THREE.Scene(); const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); -camera2.position.z = 10; // (0,0,z)에서 (0,0,0)을 보고있는 것 +camera2.position.z = 10; const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas0}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; -renderer2.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap +renderer2.shadowMap.type = THREE.PCFSoftShadowMap; const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); light2.position.set( 0, 0, 0 ); diff --git a/script1.js b/script1.js index c50cb5ff53394c6cd35697c183719717573d20ce..a3ec9405f89eb693b6a9c5f73452feaf2609d89b 100644 --- a/script1.js +++ b/script1.js @@ -3,7 +3,8 @@ const function_1 = function(){ const v_scr2 = window.innerHeight; const scene2 = new THREE.Scene(); const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); - camera2.position.z = 10; // (0,0,z)에서 (0,0,0)을 보고있는 것 + camera2.position.z = 10; + camera2.lookAt(0,0,0); const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas1}); renderer2.setSize( h_scr2, v_scr2 ); diff --git a/script2.js b/script2.js index 1faebf4c08ced88fb69ad3749f22c15672a491c7..48c6b0ab9c035526a97be947ef0690c8b4ee0360 100644 --- a/script2.js +++ b/script2.js @@ -6,35 +6,22 @@ const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); camera2.position.y = 15; camera2.lookAt(0,0,0); -//Create a WebGLRenderer and turn on shadows in the renderer const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); -//const light2 = new THREE.DirectionalLight( 0xffffff, 1); -//light2.position.set( 0, 0, 1 ); -//directioanl은 항상 위에서 온다. -//light2.rotaion.x = 90; -light2.castShadow = true; // default false -//light2.autoUpdate = false; -scene2.add( light2 ); -//Set up shadow properties for the light -//light2.shadow.mapSize.width = 128; // default -//light2.shadow.mapSize.height = 128; // default -//light2.shadow.camera.near = 0.5; // default -//light2.shadow.camera.far = 500; // default +light2.castShadow = true; +scene2.add( light2 ); -//Create a sphere that cast shadows (but does not receive them) const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 ); const sphereMaterial = new THREE.MeshPhongMaterial( { color: 0xffff00, shininess : 90.0 }); const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); -sphere.castShadow = true; //default is false -sphere.receiveShadow = false; //default +sphere.castShadow = true; +sphere.receiveShadow = false; scene2.add( sphere ); -//Create a plane that receives shadows (but does not cast them) const planeGeometry = new THREE.PlaneGeometry( 60, 60, 32, 32 ); const planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } ) const plane = new THREE.Mesh( planeGeometry, planeMaterial ); @@ -43,7 +30,6 @@ plane.rotation.x = -1.57; plane.position.set(0,-5,0); scene2.add( plane ); -//Create a helper for the shadow camera (optional) const helper = new THREE.CameraHelper( light2.shadow.camera ); scene2.add( helper ); diff --git a/script3.js b/script3.js index 97a317d44bbc260ac85a3907fe5e85dce2e85545..b738002e19e8b8e5bfc0ec557756cdf13eaf3d2b 100644 --- a/script3.js +++ b/script3.js @@ -6,36 +6,25 @@ const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); camera2.position.y = 15; camera2.lookAt(0,0,0); -//Create a WebGLRenderer and turn on shadows in the renderer const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas2}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); -//const light2 = new THREE.DirectionalLight( 0xffffff, 1); -//light2.position.set( 0, 0, 1 ); -//directioanl은 항상 위에서 온다. -//light2.rotaion.x = 90; -light2.castShadow = true; // default false + +light2.castShadow = true; light2.position.set(0,5,0); -//light2.autoUpdate = false; -scene2.add( light2 ); -//Set up shadow properties for the light -//light2.shadow.mapSize.width = 128; // default -//light2.shadow.mapSize.height = 128; // default -//light2.shadow.camera.near = 0.5; // default -//light2.shadow.camera.far = 500; // default +scene2.add( light2 ); -//Create a sphere that cast shadows (but does not receive them) const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 ); const sphereMaterial = new THREE.MeshPhongMaterial( { color: 0xffff00, shininess : 90.0 }); const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); -sphere.castShadow = true; //default is false -sphere.receiveShadow = false; //default +sphere.castShadow = true; +sphere.receiveShadow = false; scene2.add( sphere ); -//Create a plane that receives shadows (but does not cast them) + const planeGeometry = new THREE.PlaneGeometry( 60, 60, 32, 32 ); const planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } ) const plane = new THREE.Mesh( planeGeometry, planeMaterial ); diff --git a/script4.js b/script4.js index f665704952bf9f0113e1b044e990edbae7d4536f..deb3f4151be0c59607cad68a280e49f00d1c2be7 100644 --- a/script4.js +++ b/script4.js @@ -6,35 +6,24 @@ const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); camera2.position.y = 15; camera2.lookAt(0,0,0); -//Create a WebGLRenderer and turn on shadows in the renderer const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas4}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); -//const light2 = new THREE.DirectionalLight( 0xffffff, 1); -//light2.position.set( 0, 0, 1 ); -//directioanl은 항상 위에서 온다. -//light2.rotaion.x = 90; -light2.castShadow = true; // default false -//light2.autoUpdate = false; +light2.castShadow = true; + scene2.add( light2 ); -//Set up shadow properties for the light -//light2.shadow.mapSize.width = 128; // default -//light2.shadow.mapSize.height = 128; // default light2.shadow.camera.near = 20; -//light2.shadow.camera.far = 500; // default -//Create a sphere that cast shadows (but does not receive them) const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 ); const sphereMaterial = new THREE.MeshPhongMaterial( { color: 0xffff00, shininess : 90.0 }); const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); -sphere.castShadow = true; //default is false -sphere.receiveShadow = false; //default +sphere.castShadow = true; +sphere.receiveShadow = false; scene2.add( sphere ); -//Create a plane that receives shadows (but does not cast them) const planeGeometry = new THREE.PlaneGeometry( 60, 60, 32, 32 ); const planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } ) const plane = new THREE.Mesh( planeGeometry, planeMaterial ); @@ -43,7 +32,6 @@ plane.rotation.x = -1.57; plane.position.set(0,-5,0); scene2.add( plane ); -//Create a helper for the shadow camera (optional) const helper = new THREE.CameraHelper( light2.shadow.camera ); scene2.add( helper ); diff --git a/script5.js b/script5.js index f0e9735beb44c7825dc3e666cf97ec6ab17cbb3f..ab4914bbfd88c5f8f2ac04dbbd5463082d329154 100644 --- a/script5.js +++ b/script5.js @@ -6,35 +6,27 @@ const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); camera2.position.y = 15; camera2.lookAt(0,0,0); -//Create a WebGLRenderer and turn on shadows in the renderer const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas5}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); -//const light2 = new THREE.DirectionalLight( 0xffffff, 1); -//light2.position.set( 0, 0, 1 ); -//directioanl은 항상 위에서 온다. -//light2.rotaion.x = 90; -light2.castShadow = true; // default false + +light2.castShadow = true; scene2.add( light2 ); -//Set up shadow properties for the light -light2.shadow.mapSize.width = 128; // default -light2.shadow.mapSize.height = 128; // default -//light2.shadow.camera.near = 0.5; // default -//light2.shadow.camera.far = 500; // default +light2.shadow.mapSize.width = 128; +light2.shadow.mapSize.height = 128; + -//Create a sphere that cast shadows (but does not receive them) const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 ); const sphereMaterial = new THREE.MeshPhongMaterial( { color: 0xffff00, shininess : 90.0 }); const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); -sphere.castShadow = true; //default is false -sphere.receiveShadow = false; //default +sphere.castShadow = true; +sphere.receiveShadow = false; scene2.add( sphere ); -//Create a plane that receives shadows (but does not cast them) const planeGeometry = new THREE.PlaneGeometry( 60, 60, 32, 32 ); const planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } ) const plane = new THREE.Mesh( planeGeometry, planeMaterial ); diff --git a/script6.js b/script6.js index b5079e62c4aaebe9e1c8ae337446bada6790e39c..55a96fde37afd2cfd34dae6ddef800840dd45a06 100644 --- a/script6.js +++ b/script6.js @@ -6,35 +6,26 @@ const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); camera2.position.y = 15; camera2.lookAt(0,0,0); -//Create a WebGLRenderer and turn on shadows in the renderer const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas6}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); -//const light2 = new THREE.DirectionalLight( 0xffffff, 1); -//light2.position.set( 0, 0, 1 ); -//directioanl은 항상 위에서 온다. -//light2.rotaion.x = 90; -light2.castShadow = true; // default false +light2.castShadow = true; scene2.add( light2 ); +light2.shadow.mapSize.width = 1024; +light2.shadow.mapSize.height = 1024; -//Set up shadow properties for the light -light2.shadow.mapSize.width = 1024; // default -light2.shadow.mapSize.height = 1024; // default -//light2.shadow.camera.near = 0.5; // default -//light2.shadow.camera.far = 500; // default -//Create a sphere that cast shadows (but does not receive them) const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 ); const sphereMaterial = new THREE.MeshPhongMaterial( { color: 0xffff00, shininess : 90.0 }); const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); -sphere.castShadow = true; //default is false -sphere.receiveShadow = false; //default +sphere.castShadow = true; +sphere.receiveShadow = false; scene2.add( sphere ); -//Create a plane that receives shadows (but does not cast them) + const planeGeometry = new THREE.PlaneGeometry( 60, 60, 32, 32 ); const planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } ) const plane = new THREE.Mesh( planeGeometry, planeMaterial ); diff --git a/script7.js b/script7.js index 918af98b9b428af8d37497ebab695dc7f92780ae..bf6ab3eddff34fba53d4cc3a20fcc2aeae61dbfb 100644 --- a/script7.js +++ b/script7.js @@ -6,36 +6,26 @@ const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); camera2.position.y = 15; camera2.lookAt(0,0,0); -//Create a WebGLRenderer and turn on shadows in the renderer const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas7}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); -//const light2 = new THREE.DirectionalLight( 0xffffff, 1); -//light2.position.set( 0, 0, 1 ); -//directioanl은 항상 위에서 온다. -//light2.rotaion.x = 90; -light2.castShadow = true; // default false +light2.castShadow = true; scene2.add( light2 ); -//Set up shadow properties for the light -light2.shadow.mapSize.width = 1024; // default -light2.shadow.mapSize.height = 1024; // default +light2.shadow.mapSize.width = 1024; +light2.shadow.mapSize.height = 1024; light2.shadow.radius = 20; -//light2.shadow.camera.near = 0.5; // default -//light2.shadow.camera.far = 500; // default -//Create a sphere that cast shadows (but does not receive them) const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 ); const sphereMaterial = new THREE.MeshPhongMaterial( { color: 0xffff00, shininess : 90.0 }); const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); -sphere.castShadow = true; //default is false -sphere.receiveShadow = false; //default +sphere.castShadow = true; +sphere.receiveShadow = false; scene2.add( sphere ); -//Create a plane that receives shadows (but does not cast them) const planeGeometry = new THREE.PlaneGeometry( 60, 60, 32, 32 ); const planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } ) const plane = new THREE.Mesh( planeGeometry, planeMaterial ); diff --git a/script8.js b/script8.js index 8a746e879d091d1f423d5ce495f5cd475e2b4d7a..1f9f83b43c98b8bfb5a87542607324bd2aeb23e3 100644 --- a/script8.js +++ b/script8.js @@ -6,36 +6,28 @@ const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); camera2.position.y = 15; camera2.lookAt(0,0,0); -//Create a WebGLRenderer and turn on shadows in the renderer const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas8}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); -//const light2 = new THREE.DirectionalLight( 0xffffff, 1); -//light2.position.set( 0, 0, 1 ); -//directioanl은 항상 위에서 온다. -//light2.rotaion.x = 90; -light2.castShadow = true; // default false + +light2.castShadow = true; scene2.add( light2 ); -//Set up shadow properties for the light -light2.shadow.mapSize.width = 1024; // default -light2.shadow.mapSize.height = 1024; // default +light2.shadow.mapSize.width = 1024; +light2.shadow.mapSize.height = 1024; light2.shadow.radius = 100; -//light2.shadow.camera.near = 0.5; // default -//light2.shadow.camera.far = 500; // default -//Create a sphere that cast shadows (but does not receive them) const sphereGeometry = new THREE.SphereGeometry( 5, 32, 32 ); const sphereMaterial = new THREE.MeshPhongMaterial( { color: 0xff0000, shininess : 90.0 }); const sphere = new THREE.Mesh( sphereGeometry, sphereMaterial ); -sphere.castShadow = true; //default is false -sphere.receiveShadow = false; //default +sphere.castShadow = true; +sphere.receiveShadow = false; scene2.add( sphere ); -//Create a plane that receives shadows (but does not cast them) + const planeGeometry = new THREE.PlaneGeometry( 60, 60, 32, 32 ); const planeMaterial = new THREE.MeshStandardMaterial( { color: 0xffffff } ) const plane = new THREE.Mesh( planeGeometry, planeMaterial ); diff --git a/script9.js b/script9.js index d798c8a44c210a1834aebc4984fc960fcdf898a4..252c064ed040ce641daeb908a518aded96c9fb44 100644 --- a/script9.js +++ b/script9.js @@ -1,41 +1,26 @@ -const function1 = function(){ -//그림자옵션이 꺼질 때의 점광원 테스트 -//그 테스트는 구에서 시행할 것 -//그림자로 인해 도형이 어두워지는 것과 그냥 빛을 받지않아 도형이 어두워진 것을 표현하는 것과의 차이를 분석할 것. -//그렇게 점광원과 점광원 그림자에 대한 테스트를 진행한 후 점광원 그림자 관련 함수들을 사용해보면서 변화를 설명할 것. -//변수명의 뒤에는 종류와 무관하게 파일 번호를 입력할 것. +const function9 = function(){ + const h_scr2 = window.innerWidth; const v_scr2 = window.innerHeight; -//const h_scr = 800; -//const v_scr = 750; const scene2 = new THREE.Scene(); const camera2 = new THREE.PerspectiveCamera(120, h_scr2/v_scr2, 0.1, 1000); -camera2.position.z = 10; // (0,0,z)에서 (0,0,0)을 보고있는 것 +camera2.position.z = 10; camera2.position.x = 30; -//Create a WebGLRenderer and turn on shadows in the renderer -const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas2}); +const renderer2 = new THREE.WebGLRenderer({canvas: HelloCanvas9}); renderer2.setSize( h_scr2, v_scr2 ); renderer2.shadowMap.enabled = true; renderer2.shadowMap.type = THREE.PCFSoftShadowMap; // default THREE.PCFShadowMap -//Create a PointLight and turn on shadows for the light + const light2 = new THREE.PointLight( 0xffffff, 1, 100 ); -//const light2 = new THREE.DirectionalLight( 0xffffff, 1); + light2.position.set( 0, 0, 0 ); -//light2.position.set( 0, 0, 1 ); -//directioanl은 항상 위에서 온다. -//light2.rotaion.x = 90; -light2.castShadow = true; // default false + +light2.castShadow = true; scene2.add( light2 ); -//Set up shadow properties for the light -light2.shadow.mapSize.width = 512; // default -light2.shadow.mapSize.height = 512; // default -light2.shadow.camera.near = 0.5; // default -light2.shadow.camera.far = 500; // default -//Create a sphere that cast shadows (but does not receive them) const sphereGeometry2 = new THREE.SphereGeometry( 1, 32, 32 ); const sphereMaterial2 = new THREE.MeshPhongMaterial( { color: 0xffff00, shininess : 90.0 }); @@ -43,23 +28,17 @@ const sphere2 = new THREE.Mesh( sphereGeometry2, sphereMaterial2 ); sphere2.castShadow = true; //default is false sphere2.receiveShadow = true; //default sphere2.position.z = -5 -//sphere2.position.z = -5 scene2.add( sphere2 ); -//Create a sphere that cast shadows (but does not receive them) + const sphereGeometry2_1 = new THREE.SphereGeometry( 5, 32, 32 ); const sphereMaterial2_1 = new THREE.MeshPhongMaterial( { color: 0xffff00, shininess : 90.0 }); const sphere2_1 = new THREE.Mesh( sphereGeometry2_1, sphereMaterial2_1 ); -sphere2_1.castShadow = true; //default is false -sphere2_1.receiveShadow = true; //default +sphere2_1.castShadow = true; sphere2_1.position.z = -15 -//sphere2_1.position.z = -15 scene2.add( sphere2_1 ); - - -//Create a plane that receives shadows (but does not cast them) const planeGeometry2 = new THREE.PlaneGeometry( 60, 60, 32, 32 ); const planeMaterial2 = new THREE.MeshStandardMaterial( { color: 0xffffff } ) const plane2 = new THREE.Mesh( planeGeometry2, planeMaterial2 ); @@ -67,7 +46,6 @@ plane2.receiveShadow = true; plane2.position.z = -30; scene2.add( plane2 ); -//Create a helper for the shadow camera (optional) const helper2 = new THREE.CameraHelper( light2.shadow.camera ); scene2.add( helper2 ); @@ -101,4 +79,4 @@ const animate2 = function () { //animate2(); renderer2.render( scene2, camera2 ); } -function1(); \ No newline at end of file +function9(); \ No newline at end of file diff --git a/second.html b/second.html index 322fc3fb2f10e00668b015a83c2d2694afbb25e5..fdd8f6243cbee2435981c469f78ac89984a1a0c8 100644 --- a/second.html +++ b/second.html @@ -139,5 +139,12 @@ <canvas id="HelloCanvas8"></canvas> <script src="script8.js"></script> </p> + <h4> + 번외 : 두번째 구의 receiveShadow를 꺼버렸을 때, 물리적으로 불가능한 상황이 연출된다. + </h4> + <p> + <canvas id="HelloCanvas9"></canvas> + <script src="script9.js"></script> + </p> </body> </html>