03.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>03</title>
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r125/build/three.js"></script>
</head>
<body>
<script src="03.js"></script>
</body>
</html>
03.js
/*
* 图元介绍
* 图元就是一些 3D 的形状,在运行时根据大量参数生成
* 对于大多数的 3D 应用来说,通常是使用 3D 建模软件创建 3D 模型,比如, Blender, Maya, Cinema4D
* 先介绍一些最基础的 3D 形状
* */
/**
* BoxGeometry, 立方几何体
* BoxGeometry(width : Float, height : Float, depth : Float, widthSegments : Integer, heightSegments : Integer, depthSegments : Integer)
* @param width X轴上面的宽度,默认值为1。
* @param height Y轴上面的高度,默认值为1。
* @param depth Z轴上面的深度,默认值为1。
* @param widthSegments (可选)宽度的分段数,默认值是1。分段就是将这个面 平分成几份
* @param heightSegments (可选)宽度的分段数,默认值是1。
* @param depthSegments (可选)宽度的分段数,默认值是1。
*/
/**
* CircleGeometry, 平面圆
* CircleGeometry(radius : Float, segments : Integer, thetaStart : Float, thetaLength : Float)
* @param radius 圆的半径,默认为 1
* @param segments 分段(三角面)的数量,最小值为 3,默认值为 8
* @param thetaStart 第一个分段的起始角度,默认为 0
* @param thetaLength 圆形扇区的中心角,默认是 2*PI, 构成一个完整的圆
*/
/**
* ConeGeometry, 锥体
* ConeGeometry(radius : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float)
* @param radius 圆锥底部的半径,默认值为 1
* @param height 圆锥的高度,默认值为 1
* @param radialSegments 圆锥侧面周围的分段数,默认为 8
* @param heightSegments 圆锥每个侧面的分段数,默认为 1
* @param openEnded 一个 boolean 值,指明该圆锥底面是封闭的还是开放的。默认是 false, 即是封闭的
* @param thetaStart 第一个分段的起始角度,默认为 0
* @param thetaLength 圆锥底面圆扇区的中心角,默认是 2*PI,这使其成为一个完整的圆锥
*/
/**
* Cylinder, 柱体
* CylinderGeometry(radiusTop : Float, radiusBottom : Float, height : Float, radialSegments : Integer, heightSegments : Integer, openEnded : Boolean, thetaStart : Float, thetaLength : Float)
* @param radiusTop 圆柱的顶部半径,默认是 1
* @param radiusBottom 圆柱的底部半径,默认是 1
* @param height 圆柱的高度, 默认是 1
* @param radialSegments 圆柱侧面周围的分段数,默认是 8
* @param heightSegments 圆柱侧面沿着其高度的分段数,默认是 1
* @param openEnded 指明底部是否是开放的,默认为 false, 即关闭的
* @param thetaStart 第一个分段的起始角度
* @param thetaLength 底面圆扇区的中心角
*/
/**
* DodecahedronGeometry, 十二面几何体
* DodecahedronGeometry(radius : Float, detail : Integer)
* @param radius 十二面体的半径,默认是 1
* @param detail 默认值为 0,将这个值设为一个大于 0 的数将会为它增加一些顶点,使其不再是一个十二面体
*/
/*
* 全局变量
* */
const renderer = new THREE.WebGLRenderer(); // 渲染器,因为不是针对某个部分进行渲染,故需要将渲染器加入到 document 的 body 中
document.body.appendChild(renderer.domElement);
renderer.setSize(window.innerWidth, window.innerHeight); // 全屏渲染
const camera = new THREE.PerspectiveCamera(40, 2, 0.1, 1000); // 相机
camera.position.z = 120;
const scene = new THREE.Scene(); // 场景
scene.background = new THREE.Color(0xAAAAAA); // 设置场景的背景色
{
const color = 0xFFFFFF;
const intensity = 1.5;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(-1, 2, 4);
scene.add(light);
}
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(1, -2, -4);
scene.add(light);
}
const objects = []; // 存放 Object3D 对象的数组
const spread = 15;
/*
* 传入 x, y 和一个 Object3D ,将其加入到场景中
* */
function addObject(x, y, obj) {
obj.position.x = x * spread; obj.position.y = y * spread;
scene.add(obj);
objects.push(obj);
}
/*
* 创建一个随机颜色的材料
* */
function createMaterial() {
const material = new THREE.MeshPhongMaterial({
side:THREE.DoubleSide, // 这个告诉 Three.js ,绘制该三角形的两个面,有些图形若不设置则反面看不到
});
const hue = Math.random();
const saturation = 1;
const luminance = .5;
material.color.setHSL(hue, saturation, luminance);
return material;
}
/*
* 传入一个几何体,内部生成一个网格对象,并加入到场景中
* */
function addSolidGeometry(x, y, geometry) {
const mesh = new THREE.Mesh(geometry, createMaterial());
addObject(x, y, mesh);
}
function addLineGeometry(x, y, geometry) {
const material = new THREE.LineBasicMaterial({color: 0x000000});
const mesh = new THREE.LineSegments(geometry, material);
addObject(x, y, mesh);
}
/*
* 根据不同浏览器的大小,重新设置渲染器的尺寸
* */
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;
}
function main() {
console.debug("begin~");
/* BoxGeometry 立方体 */
{
const width = 8;
const height = 8;
const depth = 8;
addSolidGeometry(-2, 2, new THREE.BoxGeometry(width, height, depth));
}
/* CircleGeometry 平面圆 */
{
const radius = 7;
const segments = 24;
addSolidGeometry(-1, 2, new THREE.CircleGeometry(radius, segments));
}
/* ConeGeometry 锥体 */
{
const radius = 6;
const height = 8;
const segments = 16;
addSolidGeometry(0, 2, new THREE.ConeGeometry(radius, height, segments));
}
/* CylinderGeometry 柱体 */
{
const radiusTop = 4;
const radiusBottom = 4;
const height = 8;
const radialSegments = 12;
addSolidGeometry(1, 2, new THREE.CylinderGeometry(radiusTop, radiusBottom, height, radialSegments));
}
/* DodecahedronGeometry 十二面体 */
{
const radius = 7;
addSolidGeometry(2, 2, new THREE.DodecahedronGeometry(radius));
}
/* 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,
depth: 2,
bevelEnabled: true,
bevelThickness: 1,
bevelSize: 1,
bevelSegments: 2,
};
addSolidGeometry(-2, 1, new THREE.ExtrudeGeometry(shape, extrudeSettings));
}
/* IcosahedronGeometry 二十面体 */
{
const radius = 7;
addSolidGeometry(-1, 1, new THREE.IcosahedronGeometry(radius));
}
/* 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));
}
addSolidGeometry(0, 1, new THREE.LatheGeometry(points));
}
/* OctahedronGeometry */
{
const radius = 7;
addSolidGeometry(1, 1, new THREE.OctahedronGeometry(radius));
}
/* ParametricGeometry */
{
function klein(v, u, target) {
u *= Math.PI;
v *= 2 * Math.PI;
u = u * 2;
let x;
let z;
if (u < Math.PI) {
x = 3 * Math.cos(u) * (1 + Math.sin(u)) + (2 * (1 - Math.cos(u) / 2)) * Math.cos(u) * Math.cos(v);
z = -8 * Math.sin(u) - 2 * (1 - Math.cos(u) / 2) * Math.sin(u) * Math.cos(v);
} else {
x = 3 * Math.cos(u) * (1 + Math.sin(u)) + (2 * (1 - Math.cos(u) / 2)) * Math.cos(v + Math.PI);
z = -8 * Math.sin(u);
}
const y = -2 * (1 - Math.cos(u) / 2) * Math.sin(v);
target.set(x, y, z).multiplyScalar(0.75);
}
const slices = 25;
const stacks = 25;
addSolidGeometry(2, 1, new THREE.ParametricGeometry(klein, slices, stacks));
}
/* PlaneGeometry */
{
const width = 9;
const height = 9;
const widthSegments = 2;
const heightSegments = 2;
addSolidGeometry(-2, 0, 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;
const detail = 2;
addSolidGeometry(-1, 0, new THREE.PolyhedronGeometry(verticesOfCube, indicesOfFaces, radius, detail));
}
/* RingGeometry */
{
const innerRadius = 2;
const outerRadius = 7;
const segments = 18;
addSolidGeometry(0, 0, new THREE.RingGeometry(innerRadius, outerRadius, segments));
}
/* ShapeGeometry */
{
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);
addSolidGeometry(1, 0, new THREE.ShapeGeometry(shape));
}
/* SphereGeometry */
{
const radius = 7;
const widthSegments = 12;
const heightSegments = 8;
addSolidGeometry(2, 0, new THREE.SphereGeometry(radius, widthSegments, heightSegments));
}
/* TetrahedronGeometry */
{
const radius = 7;
addSolidGeometry(-2, -1, new THREE.TetrahedronGeometry(radius));
}
/* TextGeometry */
{
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('https://threejsfundamentals.org/threejs/resources/threejs/fonts/helvetiker_regular.typeface.json');
const geometry = new THREE.TextGeometry('Samsung', {
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);
addObject(-1, -1, parent);
}
doit();
}
/* TorusGeometry */
{
const radius = 5;
const tubeRadius = 2;
const radialSegments = 8;
const tubularSegments = 24;
addSolidGeometry(0, -1, new THREE.TorusGeometry(radius, tubeRadius, radialSegments, tubularSegments));
}
/* TorusKnotGeometry */
{
const radius = 3.5;
const tube = 1.5;
const radialSegments = 8;
const tubularSegments = 64;
const p = 2;
const q = 3;
addSolidGeometry(1, -1, new THREE.TorusKnotGeometry(radius, tube, 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;
const radius = 1;
const radialSegments = 8;
const closed = false;
addSolidGeometry(2, -1, new THREE.TubeGeometry(path, tubularSegments, radius, radialSegments, closed));
}
/* EdgesGeometry */
{
const width = 8;
const height = 8;
const depth = 8;
const thresholdAngle = 15;
addLineGeometry(-1, -2, new THREE.EdgesGeometry(
new THREE.BoxGeometry(width, height, depth),
thresholdAngle));
}
/* WireframeGeometry */
{
const width = 8;
const height = 8;
const depth = 8;
addLineGeometry(1, -2, new THREE.WireframeGeometry(new THREE.BoxGeometry(width, height, depth)));
}
function render(time) {
time *= 0.001;
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
objects.forEach((obj, ndx) => {
const speed = .1 + ndx * .05;
const rot = time * speed;
obj.rotation.x = rot;
obj.rotation.y = rot;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
console.debug("end~");
}
main();