import * as THREE from 'three';
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { RectAreaLightHelper } from "three/examples/jsm/helpers/RectAreaLightHelper";
/**
* 3d Object 物体
* https://threejs.org/docs/index.html#api/zh/objects/Bone
*/
export class ThreeDoc8Object {
constructor(canvasId) {
this.work(canvasId);
}
work(canvasId) {
// 创建 3d 场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x9e9e9e);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// 最后一步很重要,我们将renderer(渲染器)的dom元素(renderer.domElement)添加到我们的HTML文档中。这就是渲染器用来显示场景给我们看的<canvas>元素。
document.body.appendChild(renderer.domElement);
// AxesHelper 3个坐标轴的对象.
this.addAxesHelper(scene);
// 骨骼,什么的太难了,后续再看。。。Bone、Skeleton、SkinnedMesh
// 组
// let group = this.setGroup(scene);
// 实例化网格(InstancedMesh) - 不知为何会无效
// this.setInstancedMesh(scene)
// 线
// this.addLine(scene);
// 环线(LineLoop) - 什么区别?
// this.addLineLoop(scene);
// 线段(LineSegments) - 22一对,组成线段
// this.addLineSegments(scene);
// 多细节层次(LOD,Levels of Detail) - 没观察出来什么细节东西
// this.addLOD(scene);
// 网格(Mesh)
this.addMesh(scene);
// 精灵(Sprite)
this.addSprite(scene);
// 包围盒辅助线框对象
// const box = new THREE.BoxHelper(dodecahedron, 0xffff00);
// scene.add(box);
// 半球光(HemisphereLight)
this.addHemisphereLight(scene);
// 平面光光源(RectAreaLight)
this.addRectAreaLight(scene);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
// 设置相机位置
camera.position.x = 10;
camera.position.y = 10;
camera.position.z = 10;
// camera.lookAt(0, 0, 0);
// 添加控制器
let orb = new OrbitControls(camera, document.body);
orb.addEventListener('change', function () {
renderer.render(scene, camera);
});
renderer.render(scene, camera);
function animate() {
requestAnimationFrame(animate);
group.rotation.y += 0.01; // 组横向旋转
renderer.render(scene, camera);
}
// animate();
}
/**
* AxesHelper
* 用于简单模拟3个坐标轴的对象.
* 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
* AxesHelper( size : Number )
* size -- (可选的) 表示代表轴的线段长度. 默认为 1.
*/
addAxesHelper(scene) {
const axesHelper = new THREE.AxesHelper(12);
scene.add(axesHelper);
}
/**
* 半球光(HemisphereLight) - 喜欢这个光
* 光源直接放置于场景之上,光照颜色从天空光线颜色渐变到地面光线颜色。
* 半球光不能投射阴影。
*
* HemisphereLight( skyColor : Integer, groundColor : Integer, intensity : Float )
* skyColor - (可选参数) 天空中发出光线的颜色。 缺省值 0xffffff。
* groundColor - (可选参数) 地面发出光线的颜色。 缺省值 0xffffff。
* intensity - (可选参数) 光照强度。 缺省值 1。
*/
addHemisphereLight(scene) {
const light = new THREE.HemisphereLight(0xffffbb, 0x080820, 100);
scene.add(light);
light.position.set(0, -10, 0);
const helper = new THREE.HemisphereLightHelper(light, 3);
scene.add(helper);
}
/**
* 平面光光源(RectAreaLight) - 这个光源也不错!
* 平面光光源从一个矩形平面上均匀地发射光线。这种光源可以用来模拟像明亮的窗户或者条状灯光光源。
* 注意事项:
* 不支持阴影。
* 只支持 MeshStandardMaterial 和 MeshPhysicalMaterial 两种材质。
* 你必须在你的场景中加入 RectAreaLightUniformsLib ,并调用init()。
*
* RectAreaLight( color : Integer, intensity : Float, width : Float, height : Float )
* color - (可选参数) 十六进制数字表示的光照颜色。缺省值为 0xffffff (白色)
* intensity - (可选参数) 光源强度/亮度 。缺省值为 1。
* width - (可选参数) 光源宽度。缺省值为 10。
* height - (可选参数) 光源高度。缺省值为 10。
*/
addRectAreaLight(scene) {
const width = 20;
const height = 10;
const intensity = 100;
const rectLight = new THREE.RectAreaLight(0xffffff, intensity, width, height);
rectLight.position.set(10, 10, 0);
rectLight.lookAt(0, 0, 0);
scene.add(rectLight);
let rectLightHelper = new RectAreaLightHelper(rectLight);
scene.add(rectLightHelper);
}
/**
* 成组
*/
setGroup(scene) {
const lGeometry = new THREE.SphereGeometry(5, 32, 16);
const sGeometry = new THREE.SphereGeometry(0.5, 32, 16);
const material = new THREE.MeshStandardMaterial({ color: 0x049EF4 });
const cubeL = new THREE.Mesh(lGeometry, material);
cubeL.position.set(0, 0, 0);
const cubeS = new THREE.Mesh(sGeometry, material);
cubeS.position.set(-5, 0, -5);
//create a group and add the two cubes
//These cubes can now be rotated / scaled etc as a group
const group = new THREE.Group();
group.add(cubeL);
group.add(cubeS);
scene.add(group);
const box = new THREE.BoxHelper(group, 0xffff00);
scene.add(box);
return group;
}
/**
* 实例化网格(InstancedMesh)
* 一种具有实例化渲染支持的特殊版本的Mesh。你可以使用 InstancedMesh 来渲染大量具有相同几何体与材质、但具有不同世界变换的物体。
* 使用 InstancedMesh 将帮助你减少 draw call 的数量,从而提升你应用程序的整体渲染性能。
* InstancedMesh( geometry : BufferGeometry, material : Material, count : Integer )
* geometry - 一个 BufferGeometry 的实例。
* material - 一个 Material 的实例。默认为一个新的 MeshBasicMaterial 。
* count - 实例的数量
*/
setInstancedMesh(scene) {
const geometry = new THREE.SphereGeometry(0.5, 32, 16);
const material = new THREE.MeshStandardMaterial({ color: 0x049EF4 });
let mesh = new THREE.InstancedMesh(geometry, material, 10);
// mesh.instanceMatrix.setUsage( DynamicDrawUsage ); // will be updated every frame
scene.add(mesh);
}
/**
* 线(Line)
* 一条连续的线。
* Line( geometry : BufferGeometry, material : Material )
* geometry —— 表示线段的顶点,默认值是一个新的BufferGeometry。
* material —— 线的材质,默认值是一个新的具有随机颜色的LineBasicMaterial。
*/
addLine(scene) {
const material = new THREE.LineBasicMaterial({
color: 0x049EF4
});
const step = 3;
const points = [
new THREE.Vector3(-step, 0, 0),
new THREE.Vector3(0, step, 0),
new THREE.Vector3(step, 0, 0),
new THREE.Vector3(0, -step, 0),
new THREE.Vector3(-step, 0, 0),
new THREE.Vector3(0, 0, -step),
new THREE.Vector3(step, 0, 0),
new THREE.Vector3(0, 0, step),
new THREE.Vector3(0, -step, 0),
];
points.push(points);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(geometry, material);
const box = new THREE.BoxHelper(line, 0xffff00);
scene.add(box);
scene.add(line);
}
/**
* 环线(LineLoop)
* 一条头尾相接的连续的线。
*
* 它几乎和Line是相同的,唯一的区别是它在渲染时使用的是gl.LINE_LOOP, 而不是gl.LINE_STRIP, 它绘制一条直线到下一个顶点,
* 并将最后一个顶点连回第一个顶点。
* LineLoop( geometry : BufferGeometry, material : Material )
* geometry —— 表示环线上的点的顶点列表。
* material —— 线的材质,默认值是LineBasicMaterial。
* @param scene
*/
addLineLoop(scene) {
const material = new THREE.LineBasicMaterial({
color: 0x049EF4
});
const step = 3;
const points = [
new THREE.Vector3(-step, 0, 0),
new THREE.Vector3(0, step, 0),
new THREE.Vector3(step, 0, 0),
new THREE.Vector3(0, -step, 0),
];
points.push(points);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.LineLoop(geometry, material);
const box = new THREE.BoxHelper(line, 0xffff00);
scene.add(box);
scene.add(line);
}
/**
* 线段(LineSegments)
* 在若干对的顶点之间绘制的一系列的线。
*
* 它和Line几乎是相同的,唯一的区别是它在渲染时使用的是gl.LINES, 而不是gl.LINE_STRIP。
* LineSegments( geometry : BufferGeometry, material : Material )
* geometry —— 表示每条线段的两个顶点。
* material —— 线的材质,默认值是LineBasicMaterial。
* @param scene
*/
addLineSegments(scene) {
const material = new THREE.LineBasicMaterial({
color: 0x049EF4
});
const step = 3;
const points = [
new THREE.Vector3(-step, -step / 2, -step / 2),
new THREE.Vector3(step, -step / 2, -step / 2),
new THREE.Vector3(-step, step / 2, -step / 2),
new THREE.Vector3(step, step / 2, -step / 2),
new THREE.Vector3(-step, -step / 2, step / 2),
new THREE.Vector3(step, -step / 2, step / 2),
new THREE.Vector3(-step, step / 2, step / 2),
new THREE.Vector3(step, step / 2, step / 2),
new THREE.Vector3(-step / 2, step / 2, step),
new THREE.Vector3(-step / 2, step / 2, -step),
new THREE.Vector3(step / 2, step / 2, step),
new THREE.Vector3(step / 2, step / 2, -step),
new THREE.Vector3(-step / 2, -step / 2, step),
new THREE.Vector3(-step / 2, -step / 2, -step),
new THREE.Vector3(step / 2, -step / 2, step),
new THREE.Vector3(step / 2, -step / 2, -step),
new THREE.Vector3(step / 2, step, step / 2),
new THREE.Vector3(step / 2, -step, step / 2),
new THREE.Vector3(step / 2, step, -step / 2),
new THREE.Vector3(step / 2, -step, -step / 2),
new THREE.Vector3(-step / 2, step, step / 2),
new THREE.Vector3(-step / 2, -step, step / 2),
new THREE.Vector3(-step / 2, step, -step / 2),
new THREE.Vector3(-step / 2, -step, -step / 2),
];
points.push(points);
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.LineSegments(geometry, material);
const box = new THREE.BoxHelper(line, 0xffff00);
scene.add(box);
scene.add(line);
}
/**
* 多细节层次(LOD,Levels of Detail)
* 多细节层次 —— 在显示网格时,根据摄像机距离物体的距离,来使用更多或者更少的几何体来对其进行显示。
*
* 每一个级别都和一个几何体相关联,且在渲染时,可以根据给定的距离,来在这些级别对应的几何体之间进行切换。 通常情况下,你会创建多个几何体,
* 比如说三个,一个距离很远(低细节),一个距离适中(中等细节),还有一个距离非常近(高质量)。
* @param scene
*/
addLOD(scene) {
const lod = new THREE.LOD();
//Create spheres with 3 levels of detail and create new LOD levels for them
for (let i = 0; i < 3; i++) {
const geometry = new THREE.IcosahedronGeometry(10, 3 - i);
const material = new THREE.LineBasicMaterial({ color: 0x049EF4 });
const mesh = new THREE.Mesh(geometry, material);
lod.addLevel(mesh, i * 75);
}
scene.add(lod);
}
/**
* 网格(Mesh)
* 表示基于以三角形为polygon mesh(多边形网格)的物体的类。 同时也作为其他类的基类,例如SkinnedMesh。
* Mesh( geometry : BufferGeometry, material : Material )
* geometry —— (可选)BufferGeometry的实例,默认值是一个新的BufferGeometry。
* material —— (可选)一个Material,或是一个包含有Material的数组,默认是一个新的MeshBasicMaterial。
* @param scene
*/
addMesh(scene) {
const geometry = new THREE.SphereGeometry( 1, 32,16 );
const material = new THREE.MeshBasicMaterial( { color: 0x049EF4 } );
const mesh = new THREE.Mesh( geometry, material );
let edges = new THREE.EdgesHelper(mesh, 0x00ff00);
scene.add(edges);
scene.add( mesh );
}
/**
* 精灵(Sprite)
* 精灵是一个总是面朝着摄像机的平面,通常含有使用一个半透明的纹理。
* 精灵不会投射任何阴影,即使设置了 castShadow = true 也将不会有任何效果。
* Sprite( material : Material )
* material - (可选值)是SpriteMaterial的一个实例。 默认值是一个白色的SpriteMaterial。
* @param scene
*/
addSprite(scene) {
const map = new THREE.TextureLoader().load( "assets/floor-wb.png" );
const material = new THREE.SpriteMaterial( { map: map } );
const sprite = new THREE.Sprite( material );
sprite.scale.set(10, 10, 0);
scene.add( sprite );
}
}