script:
声明 threejs 并且在页面中创建画布
// 从 'three' 模块导入所有的 THREE 对象,通常用于进行 3D 渲染和处理场景、相机、光照等
import * as THREE from "three";
// 创建一个 WebGL 渲染器实例,用于在浏览器中使用 WebGL 渲染 3D 内容
const renderer = new THREE.WebGLRenderer();
// 设置渲染器的尺寸为当前浏览器窗口的宽度和高度,使渲染的内容能够填满整个浏览器窗口
renderer.setSize(window.innerWidth, window.innerHeight);
// 将渲染器生成的 <canvas> 元素(renderer.domElement)添加到网页的 <body> 元素中,
// 使得渲染内容能够显示在网页上
document.body.appendChild(renderer.domElement);
此时终端运行
parcel ./src/index.html
在浏览器中打开随机生成的地址,会看到一个黑色的画布
1. 场景和相机
// 创建一个场景实例,所有的 3D 对象、光源、相机等都将被添加到场景中
const scene = new THREE.Scene();
// 创建一个透视相机(PerspectiveCamera),用于模拟人眼的视角效果
// 参数:
// - 75: 相机的视野角度(Field of View,FOV),决定了相机所能看到的视野范围。
// - window.innerWidth / window.innerHeight: 相机的纵横比(aspect ratio),通常是窗口的宽高比。
// - 0.1: 近剪裁面(near clipping plane),相机能看到的最短距离。
// - 1000: 远剪裁面(far clipping plane),相机能看到的最远距离。
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
// 调用渲染器的 `render` 方法,渲染场景(scene)并使用相机(camera)来查看场景内容。
// `render` 方法的第一个参数是场景,第二个参数是相机。
renderer.render(scene, camera);
2. 坐标:
// 创建一个 AxesHelper 实例,生成一个坐标轴辅助工具。
// 参数 5 表示坐标轴的长度,X、Y、Z 轴的长度为 5 单位。
// 默认情况下,X 轴为红色,Y 轴为绿色,Z 轴为蓝色。
const axesHelper = new THREE.AxesHelper(5);
// 将坐标轴辅助工具添加到场景中,
// 这样在渲染时,坐标轴会显示在场景中,帮助你理解 3D 场景的坐标系方向。
scene.add(axesHelper);
// 设置相机的位置,使其沿 Z 轴偏移 5 个单位。
// camera.position.z 影响相机在 Z 轴上的位置,Z 轴是指向场景的前后方向。
// 设置为 5 使得相机稍微远离原点,能够看到场景中的内容。
camera.position.z = 5;
// 设置相机在 Y 轴上的位置。
// camera.position.y 影响相机在 Y 轴上的位置,Y 轴通常表示场景的上下方向。
// 设置为 2,使相机略微偏高于场景的水平面,这有助于更好地查看场景中的内容。
camera.position.y = 2;
在场景中添加一个坐标轴辅助工具,用来可视化 X、Y、Z 轴。
设置相机的位置,使得相机从稍远的位置(Z=5)并稍高的位置(Y=2)查看场景,以确保能够看到场景中的内容。
我们可以简写:
camera.position.set(0, 2, 5);
完整的代码:
import * as THREE from "three";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(0, 2, 5);
renderer.render(scene, camera);
3. 几何体:
// 创建一个立方体几何体(BoxGeometry),
// BoxGeometry 表示一个长方体(立方体是特殊的长方体),
// 该构造函数没有参数,表示默认大小为 1x1x1。
const boxGeometry = new THREE.BoxGeometry();
// 创建一个基本材质(MeshBasicMaterial),
// MeshBasicMaterial 是最简单的材质,它没有光照效果,始终呈现指定的颜色。
// 这里设置颜色为绿色(0x00ff00)。
const boxMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00, // 设置材质的颜色为绿色
});
// 创建一个网格对象(Mesh),由几何体和材质组成。
// 将之前定义的 boxGeometry(几何体)和 boxMaterial(材质)传给 Mesh 来创建一个可渲染的 3D 对象。
const box = new THREE.Mesh(boxGeometry, boxMaterial);
// 将创建的 box(立方体)添加到场景中,这样它就能显示在渲染结果中。
// 你可以在场景中添加多个物体,然后通过相机和渲染器来渲染它们。
scene.add(box);
3.1 几何体的旋转:
// 旋转立方体(box)绕 X 轴旋转。
// box.rotation.x 设置的是绕 X 轴旋转的角度,单位是弧度。
// 这里设置为 5 弧度,表示绕 X 轴旋转 5 弧度。
box.rotation.x = 5;
// 旋转立方体(box)绕 Y 轴旋转。
// box.rotation.y 设置的是绕 Y 轴旋转的角度,单位是弧度。
// 这里设置为 5 弧度,表示绕 Y 轴旋转 5 弧度。
box.rotation.y = 5;
3.2 让几何体动起来:
// 定义一个动画函数 animate,用于更新物体的状态并重新渲染场景。
// 该函数会在每一帧调用,执行物体的旋转以及渲染操作。
function animate() {
// 每次调用时,增加 box 的旋转角度,使其继续旋转。
// 绕 X 轴的旋转角度每次增加 0.01 弧度
box.rotation.x += 0.01;
// 绕 Y 轴的旋转角度每次增加 0.01 弧度
box.rotation.y += 0.01;
// 使用渲染器重新渲染场景。
// `scene` 是我们创建的 3D 场景,`camera` 是相机对象,确定渲染时的视角。
renderer.render(scene, camera);
}
// 使用渲染器的 `setAnimationLoop` 方法来启动动画循环。
// `animate` 函数会在每一帧自动调用,从而更新物体的状态并渲染场景。
// 这是 `THREE.js` 中的推荐方式,可以保证在浏览器帧率变化时流畅地播放动画。
renderer.setAnimationLoop(animate);
完整的代码:
import * as THREE from "three";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(0, 2, 5);
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
3.3 让场景和几何体跟随鼠标旋转角度并且鼠标滚轮放大缩小:
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
……
const orbit = new OrbitControls(camera,renderer.domElement);
……
orbit.update();
完整的代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera,renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(0, 2, 5);
orbit.update();
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
3.4 创建基础地面或背景
// 创建一个平面几何体(PlaneGeometry),宽度和高度都为 30 单位。
// PlaneGeometry 创建一个矩形平面,可以指定宽度和高度。
const planeGeometry = new THREE.PlaneGeometry(30, 30);
// 创建一个基本材质(MeshBasicMaterial),表示平面的材质。
// MeshBasicMaterial 是最简单的材质,颜色不受光照影响,始终为指定的颜色。
// 这里设置颜色为白色(0xffffff)。
const planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff, // 设置平面材质的颜色为白色
});
// 创建一个网格(Mesh)对象,将几何体和材质结合起来。
// 这里将创建一个平面网格,材质为白色,大小为 30x30 单位。
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
// 将平面网格(plane)添加到场景中,这样它才能被渲染出来。
// 将场景中的物体(如平面)添加到渲染循环后,渲染器才能显示出来。
scene.add(plane);
4. 场景 - 网格
// 创建一个 GridHelper 网格辅助工具。
// GridHelper 是一个用于在场景中显示网格的辅助工具,通常用于帮助开发者理解坐标系和物体的位置。
// 默认情况下,GridHelper 会在场景中显示一个 XY 平面的网格,尺寸为 10x10 单位,细分为 10 行/列。
const gridHelper = new THREE.GridHelper();
// 将网格辅助工具添加到场景中。
// 这样网格就会在渲染时显示出来,帮助开发者更容易理解场景中的物体位置和尺度。
scene.add(gridHelper);
4.1
旋转基础地面和网格一致
plane.rotation.x = -0.5 * Math.PI;
让(基础地面)材质同时渲染几何体的正面和反面
const planeMaterial = new THREE.MeshBasicMaterial({
side: THREE.DoubleSide,
});
完整的代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera, renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(-10, 30, 30);
orbit.update();
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
const planeGeometry = new THREE.PlaneGeometry(30, 30);
const planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
const gridHelper = new THREE.GridHelper(30, 30);
scene.add(gridHelper);
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
4.2 创建球体
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
4.3 线框模式 wireframe
完整的代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera, renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(-10, 30, 30);
orbit.update();
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
wireframe: true
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
const planeGeometry = new THREE.PlaneGeometry(30, 30);
const planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
const gridHelper = new THREE.GridHelper(30);
scene.add(gridHelper);
function animate(time) {
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
4.4 比较常见的材质类型:
-
MeshBasicMaterial
:最简单的材质,不受光照影响。 -
MeshLambertMaterial
:适用于没有反射的表面,受光照影响。 -
MeshPhongMaterial
:适用于带有高光效果的表面,支持反射和光照。 -
MeshStandardMaterial
:基于物理渲染(PBR),提供真实的光照和反射效果。 -
MeshPhysicalMaterial
:在MeshStandardMaterial
的基础上,提供更多物理属性的控制。 -
ShaderMaterial
:自定义着色器材质,适用于特殊渲染效果。 -
LineBasicMaterial
:用于绘制线条,不受光照影响。 -
PointsMaterial
:用于粒子系统,适用于粒子效果。
4.5 几何体的位置:
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshLambertMaterial({
color: 0x0000ff,
wireframe: false
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
sphere.position.x = -10
也可以直接用set去描述,三个值,分别是x,y,z
sphere.position.set(-10, 0, 0);
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
wireframe: false,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
sphere.position.set(-10, 10, 0);
5. Threejs 添加 GUI 工具
npm install dat.gui
5.1 如何使用?
导入GUI
import * as dat from "dat.gui";
const gui = new dat.GUI();
const options = {
sphereColor:'#0000ff'
};
gui.addColor(options,'sphereColor').onChange((e) => {
sphere.material.color.set(e);
})
此时,刷新浏览器就会看到右上角出现一个可以选择颜色的功能
完整代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as dat from "dat.gui";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera, renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(-10, 30, 30);
orbit.update();
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
wireframe: false,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
sphere.position.set(-10, 10, 0);
const gui = new dat.GUI();
const options = {
sphereColor:'#0000ff'
};
gui.addColor(options,'sphereColor').onChange((e) => {
sphere.material.color.set(e);
})
const planeGeometry = new THREE.PlaneGeometry(30, 30);
const planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
const gridHelper = new THREE.GridHelper(30);
scene.add(gridHelper);
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
我们也可以继续拓展其他的功能,比如我们想要实现一个切换轮廓的效果
const options = {
sphereColor: "#0000ff",
wireframe: false,
};
gui.addColor(options, "sphereColor").onChange((e) => {
sphere.material.color.set(e);
});
gui.add(options, "wireframe").onChange((e) => {
sphere.material.wireframe = e;
});
5.2 小球的运动
原理非常简单,我们只需要控制几何体的x y z轴,给他们动态绑定变化的数值就可以实现了
假设我们希望做一个有规律的运动轨迹,
let step = 0;
let speed = 0.01;
function animate() {
step += speed;
const value = 10 * Math.abs(Math.sin(step));
console.log(value);
requestAnimationFrame(animate);
}
animate();
每次打印的 value
值随着 step
的递增而变化,形成一种波动模式
完整代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as dat from "dat.gui";
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera, renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(-10, 30, 30);
orbit.update();
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
wireframe: false,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
sphere.position.set(-10, 10, 0);
const gui = new dat.GUI();
const options = {
sphereColor: "#0000ff",
wireframe: false,
speed: 0.01,
};
gui.addColor(options, "sphereColor").onChange((e) => {
sphere.material.color.set(e);
});
gui.add(options, "wireframe").onChange((e) => {
sphere.material.wireframe = e;
});
gui.add(options, "speed", 0, 0.1);
const planeGeometry = new THREE.PlaneGeometry(30, 30);
const planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
const gridHelper = new THREE.GridHelper(30);
scene.add(gridHelper);
let step = 0;
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
step += options.speed;
sphere.position.y = 10 * Math.abs(Math.sin(step));
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
6 场景 - 光照
// 创建环境光,给场景提供基础的环境光照明
const ambientLight = new THREE.AmbientLight(0x333333); // 灰色环境光
scene.add(ambientLight);
// 创建平行光,模拟太阳等远距离光源
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 白色平行光,强度为 1
scene.add(directionalLight);
需要将材质改成 MeshStandardMaterial 物理渲染类型,因为基于物理渲染(PBR),提供真实的光照和反射效果
需要注意的是:MeshBasicMaterial 对任何类型的光照都不起作用
6.1 常见的光照类型:
-
环境光(AmbientLight)
用于提供全局光照。 -
平行光(DirectionalLight)
模拟太阳光、远距离的光源。 -
点光源(PointLight)
用于模拟从一点向四面八方散射的光源。 -
聚光灯(SpotLight)
创建聚焦的光束,适合小范围照明。 -
半球光(HemisphereLight)
模拟自然环境中光源的渐变效果。 -
矩形区域光(RectAreaLight)
模拟面光源效果,适合灯具、窗口等场景。 -
光探针(LightProbe)
用于增强全局光照和环境光的模拟。
6.2 添加光源辅助器
// 添加平行光的帮助器,帮助可视化光源的方向
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
const dLightHelper = new THREE.DirectionalLightHelper(directionalLight);
scene.add(dLightHelper);
6.3 设置平行光的位置
// 设置平行光的位置
directionalLight.position.set(-30, 50, 0); // 设置光源的位置(负的 x 值表示从左侧照射)
const ambinetLight = new THREE.AmbientLight(0x333333);
scene.add(ambinetLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
scene.add(directionalLight);
directionalLight.position.set(-30, 50, 0);
const dLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5);
scene.add(dLightHelper);
6.4 设置阴影
默认情况下,阴影是关闭的
-
启用渲染器的阴影功能 (
renderer.shadowMap.enabled = true
)。 -
启用光源的阴影(
light.castShadow = true
)。 -
启用物体投射和接收阴影(
object.castShadow = true
和object.receiveShadow = true
)。 -
配置阴影的质量、范围和外观。
const renderer = new THREE.WebGLRenderer();
// 启用阴影映射
renderer.shadowMap.enabled = true;
// 设置阴影类型(可选,PCFSoftShadowMap 使阴影更柔和)
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
document.body.appendChild(renderer.domElement);
6.5 启用光源的阴影
平行光(DirectionalLight
)、点光源(PointLight
)和聚光灯(SpotLight
)都可以投射阴影。需要为光源启用阴影。
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 5);
directionalLight.castShadow = true; // 启用阴影
scene.add(directionalLight);
6.6 启用物体的阴影
物体本身也需要配置为能够 投射和接收阴影
。如果不设置这些属性,阴影将不会显示在物体上。
// 创建一个简单的立方体物体
const cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
const cubeMaterial = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
// 启用立方体投射阴影和接收阴影
cube.castShadow = true; // 立方体会投射阴影
cube.receiveShadow = true; // 立方体会接收阴影
scene.add(cube);
6.7 配置阴影的接收物体
与投射阴影的物体不同,接收阴影的物体也需要启用接收阴影的设置。这对于像地面、墙壁等物体是必要的。
const groundGeometry = new THREE.PlaneGeometry(100, 100);
// 使用 ShadowMaterial,可以让阴影在地面上可见
const groundMaterial = new THREE.ShadowMaterial({ opacity: 0.5 });
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
// 将地面旋转到水平位置
ground.rotation.x = -Math.PI / 2;
// 地面的位置稍微低于其他物体
ground.position.y = -1;
// 地面接收阴影
ground.receiveShadow = true;
scene.add(ground);
完整代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as dat from "dat.gui";
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera, renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(-10, 30, 30);
orbit.update();
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshBasicMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshStandardMaterial({
color: 0x0000ff,
wireframe: false,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
sphere.position.set(-10, 10, 0);
sphere.castShadow = true;
const ambinetLight = new THREE.AmbientLight(0x333333);
scene.add(ambinetLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
scene.add(directionalLight);
directionalLight.position.set(-30, 50, 0);
directionalLight.castShadow = true;
const dLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5);
scene.add(dLightHelper);
const gui = new dat.GUI();
const options = {
sphereColor: "#0000ff",
wireframe: false,
speed: 0.01,
};
gui.addColor(options, "sphereColor").onChange((e) => {
sphere.material.color.set(e);
});
gui.add(options, "wireframe").onChange((e) => {
sphere.material.wireframe = e;
});
gui.add(options, "speed", 0, 0.1);
const planeGeometry = new THREE.PlaneGeometry(30, 30);
const planeMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
plane.receiveShadow = true;
const gridHelper = new THREE.GridHelper(30);
scene.add(gridHelper);
let step = 0;
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
step += options.speed;
sphere.position.y = 10 * Math.abs(Math.sin(step));
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
运行结果:
6.8 创建并添加平行光阴影相机的帮助器,帮助可视化阴影相机的范围
// 创建并添加平行光的帮助器,帮助可视化光源的方向
const dLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5); // 帮助器长度为 5
scene.add(dLightHelper);
// 创建并添加平行光阴影相机的帮助器,帮助可视化阴影相机的范围
const dLightShadowHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
scene.add(dLightShadowHelper);
directionalLight.shadow.camera.bottom = -12;
7. 聚光灯 - SpotLight
const spotLight = new THREE.SpotLight(0xffffff);
scene.add(spotLight);
new THREE.SpotLight(0xffffff, 6400);
通过设置第二个值来调整光源的强度
完整代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as dat from "dat.gui";
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera, renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(-10, 30, 30);
orbit.update();
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshStandardMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshStandardMaterial({
color: 0x0000ff,
wireframe: false,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
sphere.position.set(-10, 10, 0);
sphere.castShadow = true;
const ambinetLight = new THREE.AmbientLight(0x333333);
scene.add(ambinetLight);
const spotLight = new THREE.SpotLight(0xffffff, 6400);
scene.add(spotLight);
spotLight.position.set(-50, 50, 0);
const sLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(sLightHelper);
const gui = new dat.GUI();
const options = {
sphereColor: "#0000ff",
wireframe: false,
speed: 0.01,
};
gui.addColor(options, "sphereColor").onChange((e) => {
sphere.material.color.set(e);
});
gui.add(options, "wireframe").onChange((e) => {
sphere.material.wireframe = e;
});
gui.add(options, "speed", 0, 0.1);
const planeGeometry = new THREE.PlaneGeometry(30, 30);
const planeMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
plane.receiveShadow = true;
const gridHelper = new THREE.GridHelper(30);
scene.add(gridHelper);
let step = 0;
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
step += options.speed;
sphere.position.y = 10 * Math.abs(Math.sin(step));
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
7.1 设置阴影:
const spotLight = new THREE.SpotLight(0xffffff, 6400);
scene.add(spotLight);
spotLight.position.set(-50, 50, 0);
spotLight.castShadow = true;
7.2 设置聚光灯的角度,影响光束的宽度
const spotLight = new THREE.SpotLight(0xffffff, 6400);
scene.add(spotLight);
spotLight.position.set(-50, 50, 0);
spotLight.castShadow = true;
spotLight.angle = 0.2;
完整代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as dat from "dat.gui";
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera, renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(-10, 30, 30);
orbit.update();
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshStandardMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshStandardMaterial({
color: 0x0000ff,
wireframe: false,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
sphere.position.set(-10, 10, 0);
sphere.castShadow = true;
const ambinetLight = new THREE.AmbientLight(0x333333);
scene.add(ambinetLight);
const spotLight = new THREE.SpotLight(0xffffff, 6400);
scene.add(spotLight);
spotLight.position.set(-50, 50, 0);
spotLight.castShadow = true;
const sLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(sLightHelper);
const gui = new dat.GUI();
const options = {
sphereColor: "#0000ff",
wireframe: false,
speed: 0.01,
angle: 0.2,
penumbra: 0,
intensity: 6500,
};
gui.addColor(options, "sphereColor").onChange((e) => {
sphere.material.color.set(e);
});
gui.add(options, "wireframe").onChange((e) => {
sphere.material.wireframe = e;
});
gui.add(options, "speed", 0, 0.1);
gui.add(options, "angle", 0, 1);
gui.add(options, "penumbra", 0, 1);
gui.add(options, "intensity", 0, 20000);
const planeGeometry = new THREE.PlaneGeometry(30, 30);
const planeMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
plane.receiveShadow = true;
const gridHelper = new THREE.GridHelper(30);
scene.add(gridHelper);
let step = 0;
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
step += options.speed;
sphere.position.y = 10 * Math.abs(Math.sin(step));
spotLight.angle = options.angle;
spotLight.penumbra = options.penumbra;
spotLight.intensity = options.intensity;
sLightHelper.update();
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);
运行效果:
8. new THREE.Fog 和 new THREE.FogExp2
都是用来模拟场景中的雾霾效果的,它们通过不同的方式实现雾霾的效果。它们的使用方式很简单,都可以通过在场景中添加雾霾效果来增加画面的深度感和气氛感。具体差异在于它们模拟雾霾的方式不同。
const scene = new THREE.Scene();
// 创建线性雾霾
const fog = new THREE.Fog(0x000000, 1, 100); // (颜色, 近裁剪距离, 远裁剪距离)
scene.fog = fog;
const scene = new THREE.Scene();
// 创建指数衰减雾霾
const fogExp2 = new THREE.FogExp2(0x000000, 0.1); // (颜色, 衰减系数)
scene.fog = fogExp2;
主要区别:
THREE.Fog
是 线性衰减,雾霾的浓度是线性变化的。THREE.FogExp2
是 指数衰减,雾霾的浓度随着距离的增加呈指数增长,通常更符合自然界中雾霾或烟雾的扩散特性。
使用场景:
THREE.Fog
:适合用来模拟较为均匀的雾霾效果,例如模拟远处被雾霾笼罩的场景。THREE.FogExp2
:适合用来模拟浓重的雾霾、烟雾等效果,常用于游戏或科幻场景中,能够更好地表现烟雾、废墟等复杂的环境效果。
9. setClearColor - 设置渲染器背景色
用于设置渲染器的背景色。通常,渲染器的背景颜色是画布的清除色,这个颜色会在每次渲染帧开始时清除画布,通常作为场景的背景颜色。
renderer.setClearColor(0xffcc00);
完整代码:
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import * as dat from "dat.gui";
const renderer = new THREE.WebGLRenderer();
renderer.shadowMap.enabled = true;
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera, renderer.domElement);
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
camera.position.set(-10, 30, 30);
orbit.update();
const boxGeometry = new THREE.BoxGeometry();
const boxMaterial = new THREE.MeshStandardMaterial({
color: 0x00ff00,
});
const box = new THREE.Mesh(boxGeometry, boxMaterial);
scene.add(box);
const sphereGeometry = new THREE.SphereGeometry(4);
const sphereMaterial = new THREE.MeshStandardMaterial({
color: 0x0000ff,
wireframe: false,
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(sphere);
sphere.position.set(-10, 10, 0);
sphere.castShadow = true;
const ambinetLight = new THREE.AmbientLight(0x333333);
scene.add(ambinetLight);
const spotLight = new THREE.SpotLight(0xffffff, 6400);
scene.add(spotLight);
spotLight.position.set(-50, 50, 0);
spotLight.castShadow = true;
const sLightHelper = new THREE.SpotLightHelper(spotLight);
scene.add(sLightHelper);
// scene.fog = new THREE.Fog(0xffffff, 0, 2000);
scene.fog = new THREE.FogExp2(0xffffff, 0.01);
renderer.setClearColor(0xffcc00);
const gui = new dat.GUI();
const options = {
sphereColor: "#0000ff",
wireframe: false,
speed: 0.01,
angle: 0.2,
penumbra: 0,
intensity: 6500,
};
gui.addColor(options, "sphereColor").onChange((e) => {
sphere.material.color.set(e);
});
gui.add(options, "wireframe").onChange((e) => {
sphere.material.wireframe = e;
});
gui.add(options, "speed", 0, 0.1);
gui.add(options, "angle", 0, 1);
gui.add(options, "penumbra", 0, 1);
gui.add(options, "intensity", 0, 20000);
const planeGeometry = new THREE.PlaneGeometry(30, 30);
const planeMaterial = new THREE.MeshStandardMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
plane.receiveShadow = true;
const gridHelper = new THREE.GridHelper(30);
scene.add(gridHelper);
let step = 0;
function animate(time) {
box.rotation.x = time / 1000;
box.rotation.y = time / 1000;
step += options.speed;
sphere.position.y = 10 * Math.abs(Math.sin(step));
spotLight.angle = options.angle;
spotLight.penumbra = options.penumbra;
spotLight.intensity = options.intensity;
sLightHelper.update();
renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);