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 |