dev

Vue3 + three.js optimization 예제 코드

뫼B우스 2023. 5. 11. 20:13
반응형

이번에는 three.js optimization 예제 코드를 Vue3에 적용하여 실행해 보는 예제 코드를 공유한다. 
물론 Vue와 Three.js의 입문 또는 초급자를 위한 예제이다. 
 
개발을 잘하기 위해서는 기존에 있는 예제 코드를 열심히 작성하고 실행해 보아야 한다는게 개인 생각이다. 
따라서 Vue와 Three.js를 잘하기 위해서는 많은 예제 코드를 가지고 놀아야 한다고 생각한다. 
 
그럼 three.js optimization 예제를 Vue에 적용시켜보기로 한다. 
 
기존 예제는 지구본에 인구 통계 데이터를 표현하는 것이다. 
 
https://threejs.org/manual/#ko/optimize-lots-of-objects
 
사이트에 해당 코드를 잘 설명하고 있으니 참고하길 바란다. 
 

출처:https://threejs.org/manual/#ko/optimize-lots-of-objects

 
지난번 shadow 예제 코드를 적용해 봤듯이 
이번에도 위 이미지에 예제 코드가 포함되어 있다. 
 
이미지 상단 js, html, css, export 버튼을 누르면 예제 소스를 볼 수 있다. 
2023.05.09 - [dev] - vue3 + three.js shadows 예제 코드

vue3 + three.js shadows 예제 코드

Vue 전문가라면 three.js의 예제 코드를 사용함에 그렇게 문제는 되지 않을 것이다. 하지만 초급 Vue 개발자라면 three.js를 사용할 때 당황할 수도 있다. 이번에는 초급 Vue개발자가 three.js 예제 코드를

datapuzzler.tistory.com

여기에 캡처한 이미지가 있으니 참고하면 된다. 
 

Vue3 + three.js 예제 코드 작성

 
새로운 Vue파일을 생성한 다음 

<templete>
    // html code 복사
</templete>
<script>
    // js code 복사, 단 Vue 스타일로 수정 필요
</script>
<style>
   // css code 복사
</style>

 
작업을 완료한 후 실행하면 다음과 같은 결과를 볼 수 있다. 
 

아래 전체 소스를 공유한다. 
three.js 예제 결과와 공유한 소스의 결과가 다르다. 
무엇이 바뀌었는지 찾아보는 것도 재미있을 것 같다.  
 

<template>
    <div>
      <canvas id="c"></canvas>
    </div>
</template>
  
<script>
  
  import * as THREE from 'three';
  import * as BufferGeometryUtils from 'three/addons/utils/BufferGeometryUtils.js';
  import {OrbitControls} from 'three/addons/controls/OrbitControls.js';
  
  export default {
      mounted(){          
        this.initDraw()
      },
      methods:{

          initDraw(){
            
            const canvas = document.querySelector('#c');
            const renderer = new THREE.WebGLRenderer({canvas});

            const fov = 60;
            const aspect = 2;  
            const near = 0.1;
            const far = 10;
            const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
            camera.position.z = 2.5;

            const controls = new OrbitControls(camera, canvas);
            controls.enableDamping = true;
            controls.enablePan = false;
            controls.minDistance = 1.2;
            controls.maxDistance = 4;
            controls.update();

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

            {
                const loader = new THREE.TextureLoader();
                const texture = loader.load('https://threejs.org/manual/examples/resources/images/world.jpg', render);
                const geometry = new THREE.SphereGeometry(1, 64, 32);
                const material = new THREE.MeshBasicMaterial({map: texture});
                scene.add(new THREE.Mesh(geometry, material));
            }

            async function loadFile(url) {
                const req = await fetch(url);
                return req.text();
            }

            function parseData(text) {
                const data = [];
                const settings = {data};
                let max;
                let min;
               
                text.split('\n').forEach((line) => {
                
                const parts = line.trim().split(/\s+/);
                if (parts.length === 2) {
                    
                    settings[parts[0]] = parseFloat(parts[1]);
                } else if (parts.length > 2) {

                    const values = parts.map((v) => {
                    const value = parseFloat(v);
                    if (value === settings.NODATA_value) {
                        return undefined;
                    }
                    max = Math.max(max === undefined ? value : max, value);
                    min = Math.min(min === undefined ? value : min, value);
                    return value;
                    });
                    data.push(values);
                }
                });
                return Object.assign(settings, {min, max});
            }

            function addBoxes(file) {
                const {min, max, data} = file;
                const range = max - min;


                const lonHelper = new THREE.Object3D();
                scene.add(lonHelper);
                 const latHelper = new THREE.Object3D();
                lonHelper.add(latHelper);
                const positionHelper = new THREE.Object3D();
                positionHelper.position.z = 1;
                latHelper.add(positionHelper);
                const originHelper = new THREE.Object3D();
                originHelper.position.z = 0.5;
                positionHelper.add(originHelper);

                const lonFudge = Math.PI * .5;
                const latFudge = Math.PI * -0.135;
                const geometries = [];
                data.forEach((row, latNdx) => {
                row.forEach((value, lonNdx) => {
                    if (value === undefined) {
                    return;
                    }
                    const amount = (value - min) / range;

                    const boxWidth = 1;
                    const boxHeight = 1;
                    const boxDepth = 1;
                    const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);


                    lonHelper.rotation.y = THREE.MathUtils.degToRad(lonNdx + file.xllcorner) + lonFudge;
                    latHelper.rotation.x = THREE.MathUtils.degToRad(latNdx + file.yllcorner) + latFudge;


                    positionHelper.scale.set(0.005, 0.005, THREE.MathUtils.lerp(0.01, 0.5, amount));
                    originHelper.updateWorldMatrix(true, false);
                    geometry.applyMatrix4(originHelper.matrixWorld);

                    geometries.push(geometry);
                });
                });

                const mergedGeometry = BufferGeometryUtils.mergeBufferGeometries(
                    geometries, false);
                const material = new THREE.MeshBasicMaterial({color:'#FFC300'});
                const mesh = new THREE.Mesh(mergedGeometry, material);
                scene.add(mesh);
            }

            loadFile('https://threejs.org/manual/examples/resources/data/gpw/gpw_v4_basic_demographic_characteristics_rev10_a000_014mt_2010_cntm_1_deg.asc')
                .then(parseData)
                .then(addBoxes)
                .then(render);

            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;
            }

            let renderRequested = false;

            function render() {
                renderRequested = undefined;

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

                controls.update();
                renderer.render(scene, camera);
            }
            render();

            function requestRenderIfNotRequested() {
                if (!renderRequested) {
                renderRequested = true;
                requestAnimationFrame(render);
                }
            }

            controls.addEventListener('change', requestRenderIfNotRequested);
            window.addEventListener('resize', requestRenderIfNotRequested);            

      }
    }
  }
  </script>
  
  <style scoped>
  #c {
  width: 100%;
  height: 100%;
  display: block;
}
  </style>

 

반응형