2022.05.17 - [공부/JS] - Three.js 따라 강의 - 00 먼저 알아야 할 것 들
2022.05.17 - [공부/JS] - Three.js 따라 강의 - 01 Threejs 란? (정육면체만들기)
2022.05.18 - [공부/threeJS] - Three.js 따라 강의 - 02 반응형 다자인 (화면 채우기
threejs 웹을 통하여 공부하며 개인적으로 정리한 내용을 개인경험을 추가하여 정리한 곳 입니다.
(예제가 실제 실행되지 않는 것을 고쳐 설명할경 정리하기 위하여 작성)
https://threejs.org/ 에서 제공 하는 Three.js Fundamentals 진행 이후,
유료 강의인 Three.js Journey에 대한 리뷰를 진행 할 예정입니다.
Learn Three.js의 경우 기본 이론의 심화 과정 느낌인데, 학생ID 이용시 무료 로 사용한 Ebook도 있습니다.
원시 모델(primitives)
- ThreeJS 로 표현 가능한 기본 모델들 입니다. 평면 부터 다각형 까지 기본 제공하는 도형을 사용하여, 대부분의 기본 적인 배치 및 구현이 가능합니다.
기본코드
아래 코드 부분에서 원시모델 수정 부분을 수정할 경우 테스트 가능
wire frame을 사용하여, 옵션에 따른 변화 확인 가능
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
html,
body {
margin: 0;
height: 100%;
}
#c {
width: 100%;
height: 100%;
display: block;
}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script type="importmap">{
"imports": {
"three": "https://threejs.org/build/three.module.js"
}
}</script><!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
<script type="module">
import * as THREE from 'three';
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas });
const fov = 75;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 50;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.z = 20;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xbbbbbb);
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
// 원시 모델 수정 부분
const width = 8; // ui: width
const height = 8; // ui: height
const depth = 8; // ui: depth
const widthSegments = 4; // ui: widthSegments
const heightSegments = 4; // ui: heightSegments
const depthSegments = 4; // ui: depthSegments
const geometry = new THREE.BoxGeometry(
width, height, depth,
widthSegments, heightSegments, depthSegments);
// 원시 모델 수정 부분
function makeInstance(geometry, color, x) {
const material = new THREE.MeshBasicMaterial({ color });
const cube = new THREE.Mesh(geometry, material);
const cubeline = new THREE.LineSegments(
new THREE.WireframeGeometry(geometry))
cubeline.material.depthTest = false;
cubeline.material.opacity = 0.25;
cubeline.material.transparent = true;
const roomGroup = new THREE.Group();
roomGroup.add(cubeline); // 주석처리할경우 사라짐
roomGroup.add(cube);
scene.add(roomGroup);
roomGroup.position.x = x;
return roomGroup;
}
let cubes = [
makeInstance(geometry, 0Xa4a4a4, 0),
// makeInstance(geometry, 0x8844aa, -2),
// makeInstance(geometry, 0xaa8844, 2),
];
console.log(cubes);
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const pixelRatio = window.devicePixelRatio;
const width = canvas.clientWidth * pixelRatio | 0;
const height = canvas.clientHeight * pixelRatio | 0;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render(time) {
time *= 0.001; // convert time to seconds
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
cubes.forEach((cube, ndx) => {
const speed = 1 + ndx * .1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
</body>
</html>
육면체 (BoxGeometry)
const width = 8; // ui: width
const height = 8; // ui: height
const depth = 8; // ui: depth
const geometry = new THREE.BoxGeometry(width, height, depth);
const width = 8; // ui: width
const height = 8; // ui: height
const depth = 8; // ui: depth
const widthSegments = 4; // ui: widthSegments
const heightSegments = 4; // ui: heightSegments
const depthSegments = 4; // ui: depthSegments
const geometry = new THREE.BoxGeometry(
width, height, depth,
widthSegments, heightSegments, depthSegments);
원 (CircleGeometry)
const radius = 7; // ui: radius
const segments = 24; // ui: segments
const geometry = new THREE.CircleGeometry(radius, segments);
const radius = 7; // ui: radius
const segments = 24; // ui: segments
const thetaStart = Math.PI * 0.25; // ui: thetaStart
const thetaLength = Math.PI * 1.5; // ui: thetaLength
const geometry = new THREE.CircleGeometry(
radius, segments, thetaStart, thetaLength);
원뿐 (ConeGeometry)
const radius = 6; // ui: radius
const height = 8; // ui: height
const radialSegments = 16; // ui: radialSegments
const geometry = new THREE.ConeGeometry(radius, height, radialSegments);
const radius = 6; // ui: radius
const height = 8; // ui: height
const radialSegments = 16; // ui: radialSegments
const heightSegments = 2; // ui: heightSegments
const openEnded = true; // ui: openEnded
const thetaStart = Math.PI * 0.25; // ui: thetaStart
const thetaLength = Math.PI * 1.5; // ui: thetaLength
const geometry = new THREE.ConeGeometry(
radius, height,
radialSegments, heightSegments,
openEnded,
thetaStart, thetaLength);
원통(CylinderGeometry)
const radiusTop = 4; // ui: radiusTop
const radiusBottom = 4; // ui: radiusBottom
const height = 8; // ui: height
const radialSegments = 12; // ui: radialSegments
const geometry = new THREE.CylinderGeometry(
radiusTop, radiusBottom, height, radialSegments);
const radiusTop = 4; // ui: radiusTop
const radiusBottom = 4; // ui: radiusBottom
const height = 8; // ui: height
const radialSegments = 12; // ui: radialSegments
const heightSegments = 2; // ui: heightSegments
const openEnded = false; // ui: openEnded
const thetaStart = Math.PI * 0.25; // ui: thetaStart
const thetaLength = Math.PI * 1.5; // ui: thetaLength
const geometry = new THREE.CylinderGeometry(
radiusTop, radiusBottom, height,
radialSegments, heightSegments,
openEnded,
thetaStart, thetaLength);
12면체 ( DodecahedronGeometry )
구의 기본형이라고 보시면 됩니다. 디테일 증가에 따라 구에 더 가까워 집니다.
const radius = 7; // ui: radius
const geometry = new THREE.DodecahedronGeometry(radius);
const radius = 7; // ui: radius
const detail = 2; // ui: detail
const geometry = new THREE.DodecahedronGeometry(radius, detail);
돌출형상(ExtrudeGeometry)
돌출 형상이라고하며, 다각형의 기본으로 다양한 다각형을 만들 수 있고, 경우에 따라 행렬에서좌표 값을 가져와 만들 수도 있습니다.
const shape = new THREE.Shape();
const x = -2.5;
const y = -5;
shape.moveTo(x + 2.5, y + 2.5);
shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
const extrudeSettings = {
steps: 2, // ui: steps
depth: 2, // ui: depth
bevelEnabled: true, // ui: bevelEnabled
bevelThickness: 1, // ui: bevelThickness
bevelSize: 1, // ui: bevelSize
bevelSegments: 2, // ui: bevelSegments
};
const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
위 이미지의 경우 하기 이미지를 작성 이후에, 포이트 별로 회전 시킨 모양이다.
const outline = new THREE.Shape([
[-2, -0.1], [2, -0.1], [2, 0.6],
[1.6, 0.6], [1.6, 0.1], [-2, 0.1],
].map(p => new THREE.Vector2(...p)));
const x = -2.5;
const y = -5;
const shape = new THREE.CurvePath();
const points = [
[x + 2.5, y + 2.5],
[x + 2.5, y + 2.5], [x + 2, y], [x, y],
[x - 3, y], [x - 3, y + 3.5], [x - 3, y + 3.5],
[x - 3, y + 5.5], [x - 1.5, y + 7.7], [x + 2.5, y + 9.5],
[x + 6, y + 7.7], [x + 8, y + 4.5], [x + 8, y + 3.5],
[x + 8, y + 3.5], [x + 8, y], [x + 5, y],
[x + 3.5, y], [x + 2.5, y + 2.5], [x + 2.5, y + 2.5],
].map(p => new THREE.Vector3(...p, 0));
for (let i = 0; i < points.length; i += 3) {
shape.add(new THREE.CubicBezierCurve3(...points.slice(i, i + 4)));
}
const extrudeSettings = {
steps: 100, // ui: steps
bevelEnabled: false,
extrudePath: shape,
};
const geometry = new THREE.ExtrudeGeometry(outline, extrudeSettings);
이십면체 ( IcosahedronGeometry )
const radius = 7; // ui: radius
const geometry = new THREE.IcosahedronGeometry(radius);
const radius = 7; // ui: radius
const detail = 2; // ui: detail
const geometry = new THREE.IcosahedronGeometry(radius, detail);
선반형상 (LatheGeometry)
const points = [];
for (let i = 0; i < 10; ++i) {
points.push(new THREE.Vector2(Math.sin(i * 0.2) * 3 + 3, (i - 5) * .8));
}
const geometry = new THREE.LatheGeometry(points);
const points = [];
for (let i = 0; i < 10; ++i) {
points.push(new THREE.Vector2(Math.sin(i * 0.2) * 3 + 3, (i - 5) * .8));
}
const segments = 12; // ui: segments
const phiStart = Math.PI * 0.25; // ui: phiStart
const phiLength = Math.PI * 1.5; // ui: phiLength
const geometry = new THREE.LatheGeometry(
points, segments, phiStart, phiLength);
팔면체 (OctahedronGeometry)
const radius = 7; // ui: radius
const geometry = new THREE.OctahedronGeometry(radius);
const radius = 7; // ui: radius
const detail = 2; // ui: detail
const geometry = new THREE.OctahedronGeometry(radius, detail);
2D 평면 ( PlaneGeometry )
const width = 9; // ui: width
const height = 9; // ui: height
const geometry = new THREE.PlaneGeometry(width, height);
const width = 9; // ui: width
const height = 9; // ui: height
const widthSegments = 2; // ui: widthSegments
const heightSegments = 2; // ui: heightSegments
const geometry = new THREE.PlaneGeometry(
width, height,
widthSegments, heightSegments);
다면체 (PolyhedronGeometry)
const verticesOfCube = [
-1, -1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1,
-1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1,
];
const indicesOfFaces = [
2, 1, 0, 0, 3, 2,
0, 4, 7, 7, 3, 0,
0, 1, 5, 5, 4, 0,
1, 2, 6, 6, 5, 1,
2, 3, 7, 7, 6, 2,
4, 5, 6, 6, 7, 4,
];
const radius = 7; // ui: radius
const detail = 2; // ui: detail
const geometry = new THREE.PolyhedronGeometry(
verticesOfCube, indicesOfFaces, radius, detail);
링 형상(Ring Geometry)
const innerRadius = 2; // ui: innerRadius
const outerRadius = 7; // ui: outerRadius
const thetaSegments = 18; // ui: thetaSegments
const geometry = new THREE.RingGeometry(
innerRadius, outerRadius, thetaSegments);
const innerRadius = 2; // ui: innerRadius
const outerRadius = 7; // ui: outerRadius
const thetaSegments = 18; // ui: thetaSegments
const phiSegments = 2; // ui: phiSegments
const thetaStart = Math.PI * 0.25; // ui: thetaStart
const thetaLength = Math.PI * 1.5; // ui: thetaLength
const geometry = new THREE.RingGeometry(
innerRadius, outerRadius,
thetaSegments, phiSegments,
thetaStart, thetaLength);
윤관선 형상 ( Shape Geometry)
const shape = new THREE.Shape();
const x = -2.5;
const y = -5;
shape.moveTo(x + 2.5, y + 2.5);
shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
const geometry = new THREE.ShapeGeometry(shape);
const shape = new THREE.Shape();
const x = -2.5;
const y = -5;
shape.moveTo(x + 2.5, y + 2.5);
shape.bezierCurveTo(x + 2.5, y + 2.5, x + 2, y, x, y);
shape.bezierCurveTo(x - 3, y, x - 3, y + 3.5, x - 3, y + 3.5);
shape.bezierCurveTo(x - 3, y + 5.5, x - 1.5, y + 7.7, x + 2.5, y + 9.5);
shape.bezierCurveTo(x + 6, y + 7.7, x + 8, y + 4.5, x + 8, y + 3.5);
shape.bezierCurveTo(x + 8, y + 3.5, x + 8, y, x + 5, y);
shape.bezierCurveTo(x + 3.5, y, x + 2.5, y + 2.5, x + 2.5, y + 2.5);
const curveSegments = 5; // ui: curveSegments
const geometry = new THREE.ShapeGeometry(shape, curveSegments);
구 형상 ( Sphere Geometry)
const radius = 7; // ui: radius
const widthSegments = 12; // ui: widthSegments
const heightSegments = 8; // ui: heightSegments
const geometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments);
const radius = 7; // ui: radius
const widthSegments = 12; // ui: widthSegments
const heightSegments = 8; // ui: heightSegments
const phiStart = Math.PI * 0.25; // ui: phiStart
const phiLength = Math.PI * 1.5; // ui: phiLength
const thetaStart = Math.PI * 0.25; // ui: thetaStart
const thetaLength = Math.PI * 0.5; // ui: thetaLength
const geometry = new THREE.SphereGeometry(
radius,
widthSegments, heightSegments,
phiStart, phiLength,
thetaStart, thetaLength);
사면체 (TetrahedronGeometry )
const radius = 7; // ui: radius
const geometry = new THREE.TetrahedronGeometry(radius);
const radius = 7; // ui: radius
const detail = 2; // ui: detail
const geometry = new THREE.TetrahedronGeometry(radius, detail);
문자 형상 (TextGeometry)
문자형상의 경우 부득이하게, Json 파일을 로드해야하므로, 별도의 코드가 필요하다.
아래 코드로 변경 및, 기존 cube 코드가 에러 안나게 교체 필요
function createMaterial() {
const material = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
});
const hue = Math.random();
const saturation = 1;
const luminance = .5;
material.color.setHSL(hue, saturation, luminance);
return material;
}
{
const loader = new THREE.FontLoader();
// promisify font loading
function loadFont(url) {
return new Promise((resolve, reject) => {
loader.load(url, resolve, undefined, reject);
});
}
async function doit() {
const font = await loadFont('./helvetiker_regular.typeface.json'); /* threejsfundamentals: url */
const geometry = new THREE.TextGeometry('three.js', {
font: font,
size: 3.0,
height: .2,
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.15,
bevelSize: .3,
bevelSegments: 5,
});
const mesh = new THREE.Mesh(geometry, createMaterial());
geometry.computeBoundingBox();
geometry.boundingBox.getCenter(mesh.position).multiplyScalar(-1);
const parent = new THREE.Object3D();
parent.add(mesh);
scene.add(parent);
}
doit();
}
원환체 형상 ( TorusGeometry)
const radius = 5; // ui: radius
const tubeRadius = 2; // ui: tubeRadius
const radialSegments = 8; // ui: radialSegments
const tubularSegments = 24; // ui: tubularSegments
const geometry = new THREE.TorusGeometry(
radius, tubeRadius,
radialSegments, tubularSegments);
원환체 매듭 형상 (TorusKnotGeometry)
const radius = 3.5; // ui: radius
const tubeRadius = 1.5; // ui: tubeRadius
const radialSegments = 8; // ui: radialSegments
const tubularSegments = 64; // ui: tubularSegments
const p = 2; // ui: p
const q = 3; // ui: q
const geometry = new THREE.TorusKnotGeometry(
radius, tubeRadius, tubularSegments, radialSegments, p, q);
튜브 형상 (TubeGeometry)
class CustomSinCurve extends THREE.Curve {
constructor(scale) {
super();
this.scale = scale;
}
getPoint(t) {
const tx = t * 3 - 1.5;
const ty = Math.sin(2 * Math.PI * t);
const tz = 0;
return new THREE.Vector3(tx, ty, tz).multiplyScalar(this.scale);
}
}
const path = new CustomSinCurve(4);
const tubularSegments = 20; // ui: tubularSegments
const radius = 1; // ui: radius
const radialSegments = 8; // ui: radialSegments
const closed = false; // ui: closed
const geometry = new THREE.TubeGeometry(
path, tubularSegments, radius, radialSegments, closed);
엣지 형상 (EdgesGeometry)
const size = 8;
const widthSegments = 2;
const heightSegments = 2;
const depthSegments = 2;
const boxGeometry = new THREE.BoxGeometry(
size, size, size,
widthSegments, heightSegments, depthSegments);
const geometry = new THREE.EdgesGeometry(boxGeometry);
const radius = 7;
const widthSegments = 6;
const heightSegments = 3;
const sphereGeometry = new THREE.SphereGeometry(
radius, widthSegments, heightSegments);
const thresholdAngle = 1; // ui: thresholdAngle
const geometry = new THREE.EdgesGeometry(sphereGeometry, thresholdAngle);
와이어프레임 ( WireframeGeometry)
const size = 8;
const widthSegments = 2; // ui: widthSegments
const heightSegments = 2; // ui: heightSegments
const depthSegments = 2; // ui: depthSegments
const geometry = new THREE.WireframeGeometry(
new THREE.BoxGeometry(
size, size, size,
widthSegments, heightSegments, depthSegments));
이상 기본 도형의 표현이며,
실제 Three.js Fundamentals의 경우 약간의 형상의 차이,
그냥 실행하면 안되는 경우 들로 인하여, 약간 코드적으로 수정하여 표현 하였습니다.
'공부 > threeJS' 카테고리의 다른 글
03_2 원시 모델(primitives)-회전 중앙 정렬 (0) | 2022.11.28 |
---|---|
Three.js 따라 강의 - 02 반응형 다자인 (화면 채우기 (0) | 2022.05.18 |
Three.js 따라 강의 - 01 Threejs 란? (정육면체만들기) (0) | 2022.05.17 |
Three.js 따라 강의 - 00 먼저 알아야 할 것 들 (0) | 2022.05.17 |