dev

vue3 + three.js shadows 예제 코드

뫼B우스 2023. 5. 9. 13:41
반응형

Vue 전문가라면 three.js의 예제 코드를 사용함에 그렇게 문제는 되지 않을 것이다. 

 

하지만  초급 Vue 개발자라면 three.js를 사용할 때 당황할 수도 있다.

 

이번에는 초급 Vue개발자가 three.js 예제 코드를 사용할 때 팁이 될 만한 정보를 공유하고자 한다. 

 

초급 Vue 개발자가 three.js로 개발시 기존 예제 코드 활용 방법

https://threejs.org/ 에 방문하면 다양한 정보를 얻을 것이다. 

 

 

Three.js – JavaScript 3D Library

 

threejs.org

 

위 사이트에서 예제도 함께 얻을 수 있는 방법이 있다.

Vue파일로 전환할 예제는 아래 캡처, 탱탱볼 예제이다.

 

출처:https://threejs.org/manual/#ko/shadows

 

위 출처 사이트에 들어가면 소스와 설명을 다 얻을 수 있으니 참고하면 된다. 

 

Vue파일로 전환하기 위해서는 먼저 새로운 Vue 파일을 하나 만들고 

 

캡처 사진 속 JS, HTML, CSS 버튼에 숨겨있는 소스를 복사하여 붙여 넣기를 하면 된다. 

 

 

아니면 export 버튼을 누르고 알맞는 도구에서 소스를 확인하여 붙여 넣어도 된다. 

 

<template>
 				//html 버튼 소스 붙여넣기
</template>
  
<script>
                //js 버튼 소스 붙여넣기(vue의 문법에 따라)  
</script>
  
<style scoped>
			    //css 버튼 소스 붙여넣기
</style>

 

위와 같이 새로 만든 Vue 파일에 해당 영역에 예제 코드를 붙여넣으면 완료된다. 

 

특히 <script> 영역에서는 Vue 스타일에 맞게 변경하여 코드를 붙여 넣어야 한다. 

 

붙여 넣기 완료된 코드는 다음과 같다.

테스트하면서 약간 수정한 부분이 있으니 찾아보면 도움이 될 수도 있을 것이다. 

 

붙여넣기 성공한 화면

<template>
 <div>
    <canvas id="c"></canvas>
 </div> 
</template>

<script>
import * as THREE from 'three';

export default {

    mounted(){
        this.initDraw()
    },
    methods: {
        initDraw(){
            const canvas = document.querySelector('#c');
            const renderer = new THREE.WebGLRenderer({canvas});

            const fov = 45;
            const aspect = 2;  
            const near = 0.1;
            const far = 100;
            const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
            camera.position.set(0, 10, 20);
            camera.lookAt(0, 0, 0);

            const scene = new THREE.Scene();
            scene.background = new THREE.Color('white');

            const loader = new THREE.TextureLoader();

            {
                const planeSize = 40;

                const texture = loader.load('https://threejs.org/manual/examples/resources/images/checker.png');
                texture.wrapS = THREE.RepeatWrapping;
                texture.wrapT = THREE.RepeatWrapping;
                texture.magFilter = THREE.NearestFilter;
                const repeats = planeSize / 2;
                texture.repeat.set(repeats, repeats);

                const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
                const planeMat = new THREE.MeshBasicMaterial({
                map: texture,
          //    side: THREE.DoubleSide,
                side: THREE.BackSide,
                });
          //    planeMat.color.setRGB(1.5, 1.5, 1.5);
                planeMat.color.setRGB(1, 1, 1);
                const mesh = new THREE.Mesh(planeGeo, planeMat);
                mesh.rotation.x = Math.PI * -.5;
                scene.add(mesh);
            }

            const shadowTexture = loader.load('https://threejs.org/manual/examples/resources/images/roundshadow.png');
            const sphereShadowBases = [];
            {
                const sphereRadius = 1;
                const sphereWidthDivisions = 32;
                const sphereHeightDivisions = 16;
                const sphereGeo = new THREE.SphereGeometry(sphereRadius, sphereWidthDivisions, sphereHeightDivisions);

                const planeSize = 1;
                const shadowGeo = new THREE.PlaneGeometry(planeSize, planeSize);

                const numSpheres = 17;
                for (let i = 0; i < numSpheres; ++i) {

                const base = new THREE.Object3D();
                scene.add(base);

               
                const shadowMat = new THREE.MeshBasicMaterial({
                    map: shadowTexture,
                    transparent: true,   
                    depthWrite: false,   
                });
                const shadowMesh = new THREE.Mesh(shadowGeo, shadowMat);
                shadowMesh.position.y = 0.001;  
                shadowMesh.rotation.x = Math.PI * -.5;
                const shadowSize = sphereRadius * 4;
                shadowMesh.scale.set(shadowSize, shadowSize, shadowSize);
                base.add(shadowMesh);

              
                const u = i / numSpheres;
                const sphereMat = new THREE.MeshPhongMaterial();
                sphereMat.color.setHSL(u, 1, .75);
                const sphereMesh = new THREE.Mesh(sphereGeo, sphereMat);
                sphereMesh.position.set(0, sphereRadius + 2, 0);
                base.add(sphereMesh);

     
                sphereShadowBases.push({base, sphereMesh, shadowMesh, y: sphereMesh.position.y});
                }
            }

            {
                const skyColor = 0xB1E1FF;  // light blue
                const groundColor = 0xB97A20;  // brownish orange
                const intensity = 0.25;
                const light = new THREE.HemisphereLight(skyColor, groundColor, intensity);
                scene.add(light);
            }

            {
                const color = 0xFFFFFF;
                const intensity = 0.75;
                const light = new THREE.DirectionalLight(color, intensity);
                light.position.set(0, 10, 5);
                light.target.position.set(-5, 0, 0);
                scene.add(light);
                scene.add(light.target);
            }

            function resizeRendererToDisplaySize(renderer) {
                const canvas = renderer.domElement;
                const width = canvas.clientWidth;
                const height = canvas.clientHeight;
                const needResize = canvas.width !== width || canvas.height !== height;
                if (needResize) {
                renderer.setSize(width, height, false);
                }
                return needResize;
            }

            function render(time) {
                time *= 0.001; 

                resizeRendererToDisplaySize(renderer);

                {
                const canvas = renderer.domElement;
                camera.aspect = canvas.clientWidth / canvas.clientHeight;
                camera.updateProjectionMatrix();
                }

                sphereShadowBases.forEach((sphereShadowBase, ndx) => {
                const {base, sphereMesh, shadowMesh, y} = sphereShadowBase;

                const u = ndx / sphereShadowBases.length;

                const speed = time * .2;
                const angle = speed + u * Math.PI * 2 * (ndx % 1 ? 1 : -1);
                const radius = Math.sin(speed - ndx) * 10;
                base.position.set(Math.cos(angle) * radius, 0, Math.sin(angle) * radius);

                const yOff = Math.abs(Math.sin(time * 2 + ndx));
                
                sphereMesh.position.y = y + THREE.MathUtils.lerp(-2, 2, yOff);
                shadowMesh.material.opacity = THREE.MathUtils.lerp(1, .25, yOff);
                });

                renderer.render(scene, camera);

                requestAnimationFrame(render);
            }

            requestAnimationFrame(render);            
        }
    }
}
</script>

<style scoped>

#c {
  width: 100%;
  height: 100%;
  display: block;
}
</style>
반응형