three.js particle

ness·2023년 3월 31일
0

intractive_study

목록 보기
1/1

GIS Developer 김형준 님의 유튜브 영상 을 보며 만들어본 three.js
의 particle 를 해 보았습니다.
우선 html 코드 입니다.

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="basic.css">
    <script type="importmap">
        {
            "imports": {
                "three": "../../build/three.module.js"
            }
        }
    </script>
</head>
<body>
    <div id="webgl-container"></div>
    <script type="module" src="basic.js" defer></script>
</body>
</html>

영상을 보면 시간이 지난 영상이라 three.js 에서 지원 하지 않는 것도 있고 버전 도 맞추기 위한 코트를 추가로 작성하였습니다.
html 에선

<script type="importmap">
        {
            "imports": {
                "three": "../../build/three.module.js"
            }
        }
</script>

위에 코드를 추가해 최신 three.js 를 사용 할 수 있게 해 주었습니다.

다음은 css 파일 입니다.

* {
    outline: none;
    margin: 0;
}

body {
    overflow: hidden;
}

#webgl-container {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}

css 파일은 간단한 새팅 이라고 보면 됨니다.
다음은 patcle 을 표현 하기 위한 three.js 코드 입니다.

//three.js를 사용 하게 하는 import 작업(three.js 라이브러리를 설치했다는 가정하에 사용 가능)
import * as THREE from '../build/three.module.js';
import { OrbitControls } from "../examples/jsm/controls/OrbitControls.js"

class Particle {
    constructor(scene, geometry, material, x, y) {
    //box 들의 좌표를 가져오는 함수
        const mesh = new THREE.Mesh(geometry, material);
        mesh.position.set(x, y, 0);
        scene.add(mesh);
        mesh.wrapper = this;
        this.awakenTime = undefined;
        this._mesh = mesh;
    }

    awake(time) {
        if(!this.awakenTime) {
            this.awakenTime = time;
        }
    }

    update(time) { //particle 이 마우스 의 좌표와 겹쳐 진 후에 box 들의 움직임 을 정의한 함수
        if(this.awakenTime) {
            const period = 6.0;
            const t = time - this.awakenTime;
            if(t >= period) this.awakenTime = undefined;

            this._mesh.rotation.x = THREE.MathUtils.lerp(0, Math.PI*2*period, t/period);
            let h_s, l;
            if(t < period/2) {
                h_s = THREE.MathUtils.lerp(0.0, 1.0, t/(period/2));
                l = THREE.MathUtils.lerp(0.1, 1.0, t/(period/2));
            } else {
                h_s = THREE.MathUtils.lerp(1.0, 0.0, t/(period/2.0) - 1);
                l = THREE.Math.lerp(1.0, 0.1, t/(period/2.0) - 1);    
            }

            this._mesh.material.color.setHSL(h_s, h_s, l);

            this._mesh.position.z = h_s * 15.0;
        }
    }    
}

class App {
    constructor() {
        const divContainer = document.querySelector("#webgl-container");
        this._divContainer = divContainer;

        const renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setPixelRatio(window.devicePixelRatio);
        divContainer.appendChild(renderer.domElement);

        this._renderer = renderer;

        const scene = new THREE.Scene();
        this._scene = scene;
		//불러올 함수들 명
        this._setupCamera();
        this._setupLight();
        this._setupModel();
        this._setupControls();
        this._setupPicking();

        window.onresize = this.resize.bind(this);
        this.resize();

        requestAnimationFrame(this.render.bind(this));
    }

    _setupControls() {
        new OrbitControls(this._camera, this._divContainer);
    }

    _setupPicking() { //마우스에 따라 box 가 인식하는 함수  
        const raycaster = new THREE.Raycaster();
        raycaster.cursorNormalizedPosition = undefined;
        this._divContainer.addEventListener("mousemove", this._onMouseMove.bind(this));
        this._raycaster = raycaster;
    }

    _onMouseMove(event) {
        const width = this._divContainer.clientWidth;
        const height = this._divContainer.clientHeight;

        const x = (event.offsetX / width) * 2 - 1;
        const y = -(event.offsetY / height) * 2 + 1;

        this._raycaster.cursorNormalizedPosition = { x, y };
    }
    
    _setupModel() { //particle 각각 의 객채를 구성 하는 함수
        const geometry = new THREE.BoxGeometry();

        for(let x=-20; x<=20; x+=1.1) {
            for(let y=-20; y<=20; y+=1.1) {
                const color = new THREE.Color();
                color.setHSL(0, 0, 0.1);
                const material = new THREE.MeshStandardMaterial({ color });
                
                new Particle(this._scene, geometry, material, x, y);
            }
        }
    }

    _setupCamera() { //보여주는 시점을 저으이 한 함수
        const camera = new THREE.PerspectiveCamera(
            75, 
            window.innerWidth / window.innerHeight, 
            0.1, 
            100
        );

        camera.position.z = 40;
        this._camera = camera;
    }

    _setupLight() { //물체를 볼수 있게 해주는 함수 입니다.
        const color = 0xffffff;
        const intensity = 1;
        const light = new THREE.DirectionalLight(color, intensity);
        light.position.set(-1, 2, 4);
        this._scene.add(light);
    }

    update(time) { //마우스의 위치의 따라 변화 하는 파티클을 움직이는 함수 입니다.
        time *= 0.001; // second unit

        if(this._raycaster && this._raycaster.cursorNormalizedPosition) { 
            this._raycaster.setFromCamera(this._raycaster.cursorNormalizedPosition, this._camera);
            const targets = this._raycaster.intersectObjects(this._scene.children);
            if(targets.length > 0) {
                const mesh = targets[0].object;
                const particle = mesh.wrapper;
                particle.awake(time); 
            }
        }

        this._scene.traverse((obj3d) => {
            if(obj3d instanceof THREE.Mesh) {
                obj3d.wrapper.update(time);
            }
        });
    }

    render(time) { //화면에 보여주는 함수
        this._renderer.render(this._scene, this._camera);   
        this.update(time);

        requestAnimationFrame(this.render.bind(this));
    }

    resize() {//반응형으로 만들기 위한 사이즈 크기 함수 
        const width = this._divContainer.clientWidth;
        const height = this._divContainer.clientHeight;

        this._camera.aspect = width / height;
        this._camera.updateProjectionMatrix();
        
        this._renderer.setSize(width, height);
    }
}
//App 이라는 클래스를 사용 하는 함수
window.onload = function () {
    new App();
}

링크텍스트

이상 입니다.

profile
null

0개의 댓글