wam
w__am 개발노트
wam
  • 분류 전체보기 (165)
    • CS 지식 (10)
      • 자료구조 (0)
      • 알고리즘 (0)
      • 컴퓨터 구조 (0)
      • 운영체제 (0)
      • 네트워크 (7)
      • 데이터베이스 (0)
      • 디자인 패턴 (3)
    • Frontend (131)
      • Three.js (64)
      • NPM (1)
      • Nest.js (19)
      • React (10)
      • Apollo (7)
      • TypeScript (2)
      • JavaScript (12)
      • HTML, CSS (1)
      • Jest (3)
      • E2E (5)
      • Cypress (7)
    • Database (12)
      • TypeORM (12)
    • IT 지식 (8)
      • 클라우드 서비스 (3)
      • 네트워크 (1)
      • 데이터 포맷 (2)
      • 기타 (2)
    • IT Book (2)
    • 유용한 사이트 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록
  • 🐱 Github

인기 글

태그

  • 원형적인 움직임
  • threejs 개발 할 때 도움을 줄 수 있는 유틸리티
  • 스코프
  • getdelta()
  • 함수 선언문
  • axeshelper
  • 렌더링 성능 최적화
  • 삼각함수
  • type-graphql
  • getelapsedtime()
  • react 성능 최적화
  • mapped types
  • 데이터 포맷
  • Decorators
  • three.js 구성 요소
  • 함수 표현식
  • reactive variables
  • API
  • math.sin()
  • math.cos()
  • joi 에러
  • 디자인 패턴
  • 함수의 범위
  • 초기 환경설정
  • 오프-프레미스(off-premise) 방식
  • isabstract
  • 함수 리터럴
  • e.preventdefault()
  • gridhelper
  • Interface

최근 글

관리자

글쓰기 / 스킨편집 / 관리자페이지
hELLO · Designed By 정상우.
wam

w__am 개발노트

Geometry 형태 조작하기
Frontend/Three.js

Geometry 형태 조작하기

2024. 8. 31. 22:40

 

SphereGeometry

https://threejs.org/docs/index.html#api/en/geometries/SphereGeometry

  • geometry가 가진 각 각의 정점, 점들에게 접근해 위치를 바꿔 줄 수 있다.

 

 

Vertex

Vertex는 3D 공간에서 한 점을 나타내는 개념

const geometry = new THREE.SphereGeometry( 15, 32, 16 ); 
const material = new THREE.MeshBasicMaterial( { color: 0xffff00 } ); 
const sphere = new THREE.Mesh( geometry, material ); 
scene.add( sphere );

SphereGeometry(radius : Float, widthSegments : Integer, heightSegments : Integer, phiStart : Float, phiLength : Float, thetaStart : Float, thetaLength : Float)

기본적으로, Vertex는 x, y, z 좌표를 가지며, 이 좌표들은 3D 객체의 모양을 정의하는 데 사용된다.

 

 

radius

  • 반지름 설정
  • 기본값은 1이다.

 

widthSegments

  • 구의 가로 방향(수평선 방향)의 분할 수를 설정
  • 기본값은 32이다.
  • 값이 클수록 구가 더 정교하게 표현된다.
  • 값이 작을수록 구의 모양이 각져 보일 수 있다.

 

heightSegments

  • 구의 세로 방향(수직선 방향)의 분할 수를 설정
  • 기본값은 16이다.
  • widthSegments와 마찬가지로, 값이 클수록 구의 세부 표현이 더 정교해진다.

 

phiStart

  • 구의 가로 방향 시작 각도를 설정
  • 기본값은 0이다.
  • 값의 범위는 0에서 2 * Math.PI (360도)이며, 이 값을 변경하면 구의 특정 부분만 렌더링 할 수 있다.

 

phiLength

  • 구의 가로 방향의 각도 범위를 설정
  • 기본값은 2 * Math.PI (360도)이다.
  • 이 값은 구의 가로 방향의 크기를 결정하며, 예를 들어 Math.PI로 설정하면 반 구 형태가 된다.

 

thetaStart

  • 구의 세로 방향 시작 각도를 설정
  • 기본값은 0이다.
  • 값의 범위는 0에서 Math.PI (180도)이며, 세로 방향으로 특정 부분만 렌더링 할 수 있다.

 

thetaLength

  • 구의 세로 방향의 각도 범위를 설정
  • 기본값은 Math.PI (180도)이다.
  • 이 값을 통해 구의 세로 방향의 크기를 결정할 수 있다. 예를 들어, Math.PI / 2로 설정하면 구의 상단 반만 렌더링 된다.

 

 

sagment들을 나누고 점들의 위치를 랜덤 하게 일정 범위에서 움직이게 해 주기

 

표면 처리

const material = new THREE.MeshStandardMaterial({
  color: "orangered",
  side: THREE.DoubleSide,
  flatShading: true
});

  • 각 삼각형 면이 단일한 색으로 채워져, 각 면이 더 명확하게 드러나는 효과를 준다.
  • 면이 평평하게 보이기 때문에 모서리(edge)가 더 뚜렷하게 보이며, 객체가 각진 느낌을 준다.

 

 

geometry 속성 확인

const geometry = new THREE.SphereGeometry(5, 64, 64);
console.log("📢 [ex02_geometry_vertex.js:43]", geometry);

  • 정점 위치를 담고 있는 부분 attributes/position
  • Float32 Array라고 되어 있다.
    • 배열은 배열인데 일반적으로 자바스크립트에서 쓰던 배열과 조금 다르다.
    • 타입화 배열, 형식화 배열이라 부르며 특정 형식만 들어간다. 그러기 때문에 성능이 굉장히 빠르다.
    • 수많은 점들의 위치 정보 XYZ값을 다 갖고 있어야 하기 때문에 Float32 Array로 되어 있다.
    • 콘솔로 geometry.attributes.position.array로 접근하면 위치 정보 값을 볼 수 있다.

 

 

위치정보 배열을 이용해서 모양 적용

루프 패턴

const positionArray = geometry.attributes.position.array;
for (let i = 0; i < positionArray.length; i += 3) {
  positionArray[i],
  positionArray[i + 1],
  positionArray[i + 2]
}
  • 인덱스 값이 3의 배수일 때 실행 하도록 하기, i를 3씩 늘리기
  • 인덱스 값이 i라고 할 때 x, y, z 값을 i, i+1, i+2 값으로 두면 된다.

루프 한 번 돌 때마다 정점(Vertex) 한 개의 X, Y, Z 좌표를 랜덤으로 조정하기

 

 

한쪽으로 쏠리는 현상

const positionArray = geometry.attributes.position.array; // 위치 정보 배열 값
for (let i = 0; i < positionArray.length; i += 3) {
    positionArray[i] += Math.random();
}

그림을 보게 되면 x축으로 한쪽으로 쏠려있다. 이유는 랜덤 값이 0 ~ 1 사이인데 이 값은 모두 양수이다. 양수 음수 값이 골고루 랜덤 되어야 한다.

 

 

랜덤 값을 양수, 음수 모두 사용하기

const positionArray = geometry.attributes.position.array; // 위치 정보 배열 값
for (let i = 0; i < positionArray.length; i += 3) {
    positionArray[i] += Math.random() - 0.5
}

Math.random 0 ~ 1 사이

(Math.random() -0.5) -0.5 ~ 0.5 사이

 

 

표면 완만하게 만들기

const positionArray = geometry.attributes.position.array; // 위치 정보 배열 값
for (let i = 0; i < positionArray.length; i += 3) {
     positionArray[i] += (Math.random() - 0.5) * 0.2;
}

 

 

X, Y, Z 값에 적용

  /* vertex 조정 : 처음 위치를 랜덤으로 셋팅 */
  const positionArray = geometry.attributes.position.array; // 위치 정보 배열 값
  for (let i = 0; i < positionArray.length; i += 3) {
    // 정점(Vertex) 한 개의 x, y, z 좌표를 랜덤으로 조정
    positionArray[i] += (Math.random() - 0.5) * 0.2;
    positionArray[i + 1] += (Math.random() - 0.5) * 0.2;
    positionArray[i + 2] += (Math.random() - 0.5) * 0.2;
  }
  • positionArray [i] = positionArray[i] + (Math.random() - 0.5) * 0.2;
  • 축약 코드 positionArray[i] += (Math.random() - 0.5) * 0.2;

 

 

애니메이션 넣기

 

삼각 함수 사용하기

  • sin 그래프는 위처럼 생겼다.
  • X축은 각도이다.
    • -360도 ~ 360도까지 표시되었는데 각도가 커졌다가 줄어들었다가 반복한다.
    • x 값이 늘어나는 값을 주면 된다.
  • Y값은 진폭이다.
    • sin() 호출 결과 자체가 된다.

 

 

업데이트 값 넣어주기

function draw() {
  // 경과 시간, 처음 실행된 시점이 0에서 초 단위로 시간 값이 리턴
  const time = clock.getElapsedTime();

  for (let i = 0; i < positionArray.length; i += 3) {
    positionArray[i] += Math.sin(time);  
  }

	// 위치 값 업데이트
  geometry.attributes.position.needsUpdate = true;

  renderer.render(scene, camera);
  renderer.setAnimationLoop(draw);
}
  • time은 경과 시간, 처음 실행된 시점이 0에서 초 단위로 시간 값이 리턴한다.
  • x축은 늘어나는 값을 받아야 하기 때문에 경과시간을 각도 값을 주기
  • 변화한 값은 한 번 보여주기 때문에 업데이트를 해줘야 반복해서 보여 줄 수 있다.

 

 

y의 폭 이 크다

 const time = clock.getElapsedTime();
  • y 값은 진폭을 하는 부분이고 time으로 y 값을 주었다.
  • 진폭이 크기 때문에 위 이미지처럼 되는 것이다. 진폭을 줄여주자
  • y 값이라는 것이 sin()의 호출 결과 자체이고 여기에 0.x을 곱해주거나 값을 나눠주게 되면 값이 작아지게 된다.

 

 

y 값의 진폭 줄여주기

function draw() {
  // 경과 시간, 처음 실행된 시점이 0에서 초 단위로 시간 값이 리턴
  const time = clock.getElapsedTime();

  for (let i = 0; i < positionArray.length; i += 3) {
    positionArray[i] += Math.sin(time) * 0.002;  
  }

	// 위치 값 업데이트
  geometry.attributes.position.needsUpdate = true;

  renderer.render(scene, camera);
  renderer.setAnimationLoop(draw);
}

positionArray[i] += Math.sin(time) * 0.002

 

 

속도를 빠르게 해 주기

function draw() {
  // 경과 시간, 처음 실행된 시점이 0에서 초 단위로 시간 값이 리턴
  const time = clock.getElapsedTime() * 3;

	for (let i = 0; i < positionArray.length; i += 3) {
    positionArray[i] += Math.sin(time) * 0.002;
  }

  // 위치 값 업데이트
  geometry.attributes.position.needsUpdate = true;

  renderer.render(scene, camera);
  renderer.setAnimationLoop(draw);
}
  • x 값은 각도이고 각도로 사용하고 있는 것은 경과 시간이다.
  • 각도 변화를 빠르게 한다면 각도 변화가 빠르게 된다.
  • 시간 속도를 곱해주어 각도 변화를 빠르게 해 주면 속도가 빠르게 진행된다.

 

 

랜덤 된 값 저장

  /* vertex 조정 : 처음 위치를 랜덤으로 셋팅 */
  const positionArray = geometry.attributes.position.array; // 위치 정보 배열 값
  const randomArray = [];
  for (let i = 0; i < positionArray.length; i += 3) {
    // 정점(Vertex) 한 개의 x, y, z 좌표를 랜덤으로 조정
    const range = 0.5;
    const smoothness = 0.2;

    positionArray[i] += (Math.random() - range) * smoothness;
    positionArray[i + 1] += (Math.random() - range) * smoothness;
    positionArray[i + 2] += (Math.random() - range) * smoothness;

    randomArray[i] = (Math.random() - range) * smoothness;
    randomArray[i + 1] = (Math.random() - range) * smoothness;
    randomArray[i + 2] = (Math.random() - range) * smoothness;
  }

각 각 점들에 적용된 값 자체가 랜덤으로 고정된 상태에서 움직여야 한다.

positionArray 값과 대응되는 배열 완성하기

 

 

표면 움직이기, 랜덤값 적용

  function draw() {
    const speed = 3;
    const amplitude = 0.001; // 진폭

    // 경과 시간, 처음 실행된 시점이 0에서 초 단위로 시간 값이 리턴
    const time = clock.getElapsedTime() * speed;

    for (let i = 0; i < positionArray.length; i += 3) {
      positionArray[i] += Math.sin(time + randomArray[i] * 100) * amplitude;
      positionArray[i + 1] +=
        Math.sin(time + randomArray[i + 1] * 100) * amplitude;
      positionArray[i + 2] +=
        Math.sin(time + randomArray[i + 2] * 100) * amplitude;
    }

    // 위치 값 업데이트
    geometry.attributes.position.needsUpdate = true;

    renderer.render(scene, camera);
    renderer.setAnimationLoop(draw);
  }

  • 랜덤된 값 저장한 값을 이용해 표면을 움직이기
  • time 경과시간에 따라 움직임이 느려지고 빨라진다.
  • 점 별로 늘어나는 속도에 랜덤으로 차이를 주면 불규칙적인 움직임이 된다.
  • positionArray 값이 랜덤 한 값으로 변경되면서 불규칙하게 움직이게 되는 것이다.

 

 

전체 코드

import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";

/* 주제: Geometry 정점(Vertex) position 이용하기 */

export default function example() {
  /* Renderer 만들기 : html에 캔버스 미리 만들기 */
  const canvas = document.querySelector("#three-canvas");
  const renderer = new THREE.WebGLRenderer({
    canvas,
    antialias: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(window.devicePixelRatio > 1 ? 2 : 1);

  /* Scene 만들기 */
  const scene = new THREE.Scene();

  /* Camera 만들기 */
  const camera = new THREE.PerspectiveCamera(
    75,
    window.innerWidth / window.innerHeight,
    0.1,
    1000
  );
  camera.position.z = 10;
  scene.add(camera);

  /* Light 만들기 */
  const ambientLight = new THREE.AmbientLight("white", 0.5);
  scene.add(ambientLight);

  const directionalLight = new THREE.DirectionalLight("white", 1);
  directionalLight.position.x = 1;
  directionalLight.position.z = 2;
  scene.add(directionalLight);

  /* Controls 만들기 */
  new OrbitControls(camera, renderer.domElement); // 마우스를 이용해  3D 객체를 회전, 확대, 축소 가능

  /* Messh 만들기 */
  const geometry = new THREE.SphereGeometry(5, 64, 64);
 	// const geometry = new THREE.PlaneGeometry(10, 10, 32, 32);
  // console.log("📢 [ex02_geometry_vertex.js]", geometry);

  const material = new THREE.MeshStandardMaterial({
    color: "orangered",
    side: THREE.DoubleSide,
    flatShading: true
  });

  const mesh = new THREE.Mesh(geometry, material);

  scene.add(mesh);

  /* vertex 조정 : 처음 위치를 랜덤으로 셋팅 */
  const positionArray = geometry.attributes.position.array; // 위치 정보 배열 값
  const randomArray = [];

  // 랜덤 값 생성 함수
  function getRandomAdjustment() {
    // 범위와 부드러움 설정
    const range = 0.5;
    const smoothness = 0.2;

    return (Math.random() - range) * smoothness;
  }

  // 정점(Vertex) 한 개의 x, y, z 좌표를 랜덤으로 조정
  for (let i = 0; i < positionArray.length; i += 3) {
    const positionOffsetX = getRandomAdjustment();
    const positionOffsetY = getRandomAdjustment();
    const positionOffsetZ = getRandomAdjustment();

    // 위치 배열 업데이트
    positionArray[i] += positionOffsetX;
    positionArray[i + 1] += positionOffsetY;
    positionArray[i + 2] += positionOffsetZ;

    // 랜덤 배열에 값 저장
    randomArray[i] = positionOffsetX;
    randomArray[i + 1] = positionOffsetY;
    randomArray[i + 2] = positionOffsetZ;
  }

  /* 그리기 */
  const clock = new THREE.Clock();

  function draw() {
    // 경과 시간, 처음 실행된 시점이 0에서 초 단위로 시간 값이 리턴
    const speed = 3;
    const time = clock.getElapsedTime() * speed;
    const range = 100;

    for (let i = 0; i < positionArray.length; i += 3) {
      const randomX = randomArray[i] * range;
      const randomY = randomArray[i + 1] * range;
      const randomZ = randomArray[i + 2] * range;

      const amplitude = 0.001; // 진폭
      positionArray[i] += Math.sin(time + randomX) * amplitude;
      positionArray[i + 1] += Math.sin(time + randomY) * amplitude;
      positionArray[i + 2] += Math.sin(time + randomZ) * amplitude;
    }

    // 위치 값 업데이트
    geometry.attributes.position.needsUpdate = true;

    renderer.render(scene, camera);
    renderer.setAnimationLoop(draw);
  }

  function setSize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.render(scene, camera);
  }

  /* 이벤트 */
  window.addEventListener("resize", setSize);

  draw();
}

 

 

 

저작자표시 변경금지 (새창열림)

'Frontend > Three.js' 카테고리의 다른 글

카메라 컨트롤, TrackballControls  (0) 2024.09.04
카메라 컨트롤, OrbitControls  (0) 2024.09.03
여러가지 Geometry 살펴보기  (0) 2024.08.29
그룹 만들기(Scene Graph)  (0) 2024.08.28
회전  (0) 2024.08.28
    'Frontend/Three.js' 카테고리의 다른 글
    • 카메라 컨트롤, TrackballControls
    • 카메라 컨트롤, OrbitControls
    • 여러가지 Geometry 살펴보기
    • 그룹 만들기(Scene Graph)
    wam
    wam

    티스토리툴바