画线
// 创建一个渲染器 设置背景的地方
const renderer = new THREE.WebGLRenderer();
// 设置渲染的大小区域 如果分辨率太低可以 /2 以一半来渲染
renderer.setSize( window.innerWidth, window.innerHeight );
// domElement是一个canvas,渲染器在其上面绘制输出,需加入页面之中
document.body.appendChild( renderer.domElement );
// 创建一个相机 PerspectiveCamera是透视摄像机
// 参数 视野角度 / 长宽比 / 近截面 / 远截面
const camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 500 );
// 下面2个属性是一起的
// 设置相机位置 x轴 y轴 z轴 对应焦点方向
// 就像把相机举起来拍摄一样,y相当抬起来的高度,抬的高才能看得清
camera.position.set( 0, 0, 100 );
// up:相机朝向,默认(0,1,0),可以理解为那一根坐标轴向上,必须写在kookat前面
// camera.up.x = 0;
// camera.up.y = 1;
// camera.up.z = 0;
// 设置相机焦点方向 x轴 y轴 z轴 相机往哪看
camera.lookAt( 0, 0, 0 );
// 创建一个场景
// 放置物品,灯光和摄像机的地方
const scene = new THREE.Scene();
// 绘制线框样式几何体的材质,就是线的颜色
const material = new THREE.LineBasicMaterial( { color: 0x0000ff } );
const points = [];
// Vector3表示一个三维向量(x,y,z)
points.push( new THREE.Vector3( -10, 0, 0 ) );
points.push( new THREE.Vector3( 0, 10, 0 ) );
points.push( new THREE.Vector3( 10, 0, 0 ) );
// 注意,线是画在每一对连续的顶点之间的,而不是在第一个顶点和最后一个顶点之间绘制线条(线条并未闭合)。
// setFromPoints ( points : Array ) : this
// 存储点的信息,更高效的向GPU传递数据
const geometry = new THREE.BufferGeometry().setFromPoints( points );
// 组成一条线
// BufferGeometry —— 表示线段的顶点,默认值是一个新的BufferGeometry
// material —— 线的材质,默认值是一个新的具有随机颜色的
const line = new THREE.Line( geometry, material );
// 添加到场景
scene.add( line );
// 调用渲染函数
renderer.render( scene, camera );
在Vu3中,如果想在指定的DOM上面显示:
<template>
<div class="three" ref="three" id="three">zdw</div>
</template>
<script lang="ts" setup>
// 引入对应模块
import { onMounted, ref } from "vue";
import * as THREE from "three";
// 获取DOM数据
const three = ref(null);
// 重点,因为setup在DOM渲染之前,获取不到,一直是null所以在生命周期执行绑定操作
onMounted(() => {
const content: any = three.value;
content.appendChild(renderer.domElement);
})
</script>
画一个立方体
// 创建一个纹理贴图,将其应用到一个表面,或者作为反射/折射贴图。
const texture = new THREE.TextureLoader();
// 加载一个资源
texture.load('xxxxxx',
// onLoad回调
function (texture: any) {
// 立即使用纹理进行材质创建
const material = new THREE.MeshBasicMaterial({
map: texture,
// 材质的颜色([Color](<> "Color")),默认值为白色 (0xffffff)。
color: 0xffffff,
// 不写,透明的图片会变灰色
transparent: true,
});
// 设置立体
// 设置x,y,z
const geometry = new THREE.BoxGeometry(20, 20, 20);
// 创建这个mesh对象
const cube = new THREE.Mesh(geometry, material);
// 设置场景x的位置
cube.position.x = -50;
// 设置场景x的位置 物体的远近
cube.position.z = 2;
// 设置场景x的位置
cube.position.y = 20;
// 将新创建的带贴图的几何体添加到场景内,我们就可以看到了
scene.add(cube);
// 调用渲染函数
renderer.render(scene, camera);
}
);
实现3个球体的滚动
// 老规矩,先引入
import * as THREE from "three"
// 获取ref绑定的DOM信息,详解见上面
const three = ref(null);
// 渲染器
const renderer = new THREE.WebGLRenderer();
const fov = 75;
const aspect = 2; // 相机默认值
const near = 0.1;
const far = 5;
// 创建一个相机 PerspectiveCamera是透视摄像机
// 参数 视野角度 / 长宽比 / 近截面 / 远截面
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// 改变相机z轴位置
camera.position.z = 2;
// 场景
const scene = new THREE.Scene();
const boxWidth = 1;
const boxHeight = 1;
const boxDepth = 1;
// 创建一个包含盒子信息的立方几何体
const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
const makeInstance = (geometry, color, x) => {
// 创建受灯光影响的MeshPhongMaterial
const material = new THREE.MeshPhongMaterial({color});
// 创建一个网格Mesh
const cube = new THREE.Mesh(geometry, material);
// 添加到场景
scene.add(cube);
// 在x轴上排成一条直线
cube.position.x = x;
return cube;
}
// 创建3个立方体
const cubes = [
makeInstance(geometry, 0x44aa88, 0),
makeInstance(geometry, 0x8844aa, -2),
makeInstance(geometry, 0xaa8844, 2),
];
// 封装滚动方法
const render = (time: number) => {
time *= 0.001; // 将时间单位变为秒
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);
const color = 0xFFFFFF;
const intensity = 1;
// 创建一盏平行光(颜色 / 光照强度)
const light = new THREE.DirectionalLight(color, intensity);
// 位于摄像机前面稍微左上方一点
light.position.set(-1, 2, 4);
// 放入场景
scene.add(light);
// 渲染
renderer.render(scene, camera);
绘制一个心型
import * as THREE from "three";
const three = ref(null);
const renderer = new THREE.WebGLRenderer();
const fov = 45;
const aspect = 2; // the canvas default
const near = 1;
const far = 1000;
// 参数 视野角度 / 长宽比 / 近截面 / 远截面
// fov表示摄像机视锥体垂直视野角度,最小值为0,最大值为180,默认值为50,实际项目中一般都定义45,因为45最接近人正常睁眼角度
// aspect表示摄像机视锥体长宽比,默认长宽比为1,即表示看到的是正方形,实际项目中使用的是屏幕的宽高比
// near表示摄像机视锥体近端面,这个值默认为0.1,实际项目中都会设置为1
// far表示摄像机视锥体远端面,默认为2000,这个值可以是无限的,说的简单点就是我们视觉所能看到的最远距离。
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
// camera.position.z = 80;
camera.position.set(-29, 12, 80);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0xAAAAAA);
// 搞两个光源
{
const color = 0xFFFFFF;
const intensity = 1;
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 = [];
const spread = 15;
// 设置图形的位置x,y轴
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,
});
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);
}
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 = {
// 用于沿着挤出样条的深度细分的点的数量,默认值为1。
steps: 2,
// 挤出的形状的深度,默认值为1。
depth: 2,
// 对挤出的形状应用是否斜角,默认值为true。
bevelEnabled: true,
// 设置原始形状上斜角的厚度。默认值为0.2。
bevelThickness: 1,
// 斜角与原始形状轮廓之间的延伸距离,默认值为bevelThickness-0.1。
bevelSize: 1,
// 斜角的分段层数,默认值为3。
bevelSegments: 2,
};
addSolidGeometry(-2, 1, new THREE.ExtrudeGeometry(shape, extrudeSettings));
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 render(time: number) {
time *= 0.01;
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);
绘制一个球体围着一个球体旋转
import * as THREE from "three";
const three = ref(null);
const scene = new THREE.Scene();
const renderer = new THREE.WebGLRenderer();
const fov = 40;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 1000;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 50, 0);
// 因为设置了set(5,5,5)导致sunMeshs局部空间5倍,导致earthMesh.position.x = 10;也是5倍
// camera.position.set(0, 150, 0);
camera.up.set(0, 0, 1);
camera.lookAt(0, 0, 0);
// 要更新旋转角度的对象数组,放入要旋转的物体
const objects = [];
// 一球多用
// 球体半径
const radius = 1;
// 水平分段数 半径+6 差不多就是球体
const widthSegments = 60;
// 垂直分段数
const heightSegments = 16;
// 生成球体
const sphereGeometry = new THREE.SphereGeometry(
radius,
widthSegments,
heightSegments
);
// 设置光源
{
const color = 0xffffff;
const intensity = 3;
const light = new THREE.PointLight(color, intensity);
scene.add(light);
}
// 画轨道
const geometry = new THREE.TorusGeometry( 10, 0.1, 10, 100 );
const material = new THREE.MeshBasicMaterial( { color: 0xdddddd } );
const torus = new THREE.Mesh( geometry, material );
torus.rotateZ(1.5)
torus.rotateY(1.5)
scene.add( torus );
// 新建一个包裹太阳和地球的盒子
const solarSystem = new THREE.Object3D();
scene.add(solarSystem);
objects.push(solarSystem);
// 设置有光泽的物体
const sunMaterial = new THREE.MeshPhongMaterial({emissive: 0xffff00});
const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial);
// 设置太阳的大小
sunMesh.scale.set(5, 5, 5);
solarSystem.add(sunMesh);
objects.push(sunMesh);
// 包裹地球和月亮的盒子
const earthOrbit = new THREE.Object3D();
earthOrbit.position.x = 10;
solarSystem.add(earthOrbit);
objects.push(earthOrbit);
// 设置地球
const earthMaterial = new THREE.MeshPhongMaterial({
color: 0x2233ff,
emissive: 0x112244,
});
const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial);
// earthMesh.position.x = 10;
// scene.add(earthMesh);
earthOrbit.add(earthMesh);
objects.push(earthMesh);
// 画轨道
const geometry2 = new THREE.TorusGeometry( 2, 0.1, 10, 100 );
const material2 = new THREE.MeshBasicMaterial( { color: 0xdddddd } );
const torus2 = new THREE.Mesh( geometry2, material2 );
torus2.rotateZ(1.5)
torus2.rotateY(1.5)
earthOrbit.add( torus2 );
// 月亮
const moonOrbit = new THREE.Object3D();
moonOrbit.position.x = 2;
earthOrbit.add(moonOrbit);
const moonMaterial = new THREE.MeshPhongMaterial({color: 0x888888, emissive: 0x222222});
const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial);
moonMesh.scale.set(.5, .5, .5);
moonOrbit.add(moonMesh);
objects.push(moonMesh);
function render(time) {
time *= 0.001;
// 旋转
objects.forEach((obj) => {
obj.rotation.y = time;
});
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
VR全景看房
import {onMounted, ref} from "vue";
import * as THREE from "three";
// 引入轨道操作器 轨道控制使摄像机可以围绕目标旋转。
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
const three = ref(null);
// 场景
const scene = new THREE.Scene();
// 初始化相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// 设置相机位置
camera.position.z = 0.1;
// 渲染器
const renderer = new THREE.WebGLRenderer();
// 加载图片
let loader = new THREE.TextureLoader();
// 统一处理图片数据,方便
const setImgs = (event) => {
let arr = []
event.forEach((item) => {
let texture = loader.load(`src/assets/VR/${item}.jpg`)
arr.push(new THREE.MeshBasicMaterial( {map: texture} ))
})
return arr;
}
// 房间图片
let homeObj = {
home1: {
materials: setImgs(["home_l", "home_r", "home_u", "home_d", "home_b", "home_f"]),
btnPosition: [-16, -8, -9]
},
home2: {
materials: setImgs(["home2_l", "home2_r", "home2_u", "home2_d", "home2_b", "home2_f"]),
btnPosition: [18, -8, -7]
},
}
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
// 定义当前房间名称的参数
let currentHome = 'home1';
// 鼠标点击事件
function onMouseDown(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
//将平面坐标系转为世界坐标系
raycaster.setFromCamera(mouse, camera);
//得到点击的几何体
var raycasters = raycaster.intersectObjects(scene.children);
if (raycasters[0].object.name === 'sanshazi') {
currentHome = currentHome === "home1" ? "home2" : "home1";
// 重新加载房间信息
initHome();
}
}
// 轨道控制器,说白了就是控制鼠标操作的
const controls = new OrbitControls(camera, renderer.domElement);
// 设置阻尼感,就是你的动作有一个过程
controls.enableDamping = true;
// 监听控制器的鼠标事件,执行渲染内容
controls.addEventListener('change', () => {
renderer.render(scene, camera)
})
//监视鼠标左键按下事件
window.addEventListener("mousedown", onMouseDown, false);
// 切换场景
const initHome = () => {
// 切换场景前把之前的物体清除掉
let homeMesh1 = scene.getObjectByName('ershazi')
let locationBtn = scene.getObjectByName('sanshazi')
scene.remove(homeMesh1, locationBtn)
// 创建一个矩形,贴上六张材质图片,模拟室内效果
let homeGeoMetry = new THREE.BoxGeometry(40, 40, 40);
let homeMesh = new THREE.Mesh(homeGeoMetry, homeObj[currentHome].materials);
homeMesh.castShadow = true
homeMesh.position.set(0, 0, 0);
// 设置-1.就相当于把相机反过来,从里面看
homeMesh.geometry.scale(1, 1, -1);
homeMesh.name = "ershazi";
// 你写好要放进去
scene.add(homeMesh);
// 添加一个圆形按钮,点击后跳转到其他房间场景
let planeGemetry = new THREE.CircleGeometry(1.2, 20);
let planeMaterial = new THREE.LineBasicMaterial({color: 0xffffff, side: THREE.DoubleSide});
let planeMesh = new THREE.Mesh(planeGemetry, planeMaterial);
planeMesh.position.set(...homeObj[currentHome].btnPosition)
planeMesh.rotateX(0.5 * Math.PI)
planeMesh.name = "sanshazi"
scene.add(planeMesh);
}
// 渲染效果
const render = () => {
renderer.render(scene, camera);
// 一帧一帧的执行动画
requestAnimationFrame(render);
};
onMounted(() => {
const content: any = three.value;
// 只渲染指定的地方
renderer.setSize(content.clientWidth, content.clientHeight)
content.appendChild(renderer.domElement);
initHome()
render();
})