Three.js解决方案

重要通知

基本概况

Three.js由来自西班牙巴塞罗那的Ricardo Cabello在2010四月于GitHub首次发布。同时贡献了包括基于 WebGL 的 3D 引擎 Three.js、js 性能监控工具 Stats.js、纹理生成工具 Texgen.js

相关类库

常见案例

  • 参考游戏:http://hexgl.bkcore.com/play/

  • 用Three.js打造酷炫3D个人网站(含源码):https://juejin.cn/post/6985033373857579045

  • 各种图形图案:https://threejs.org/examples/#webgl_buffergeometry_lines_indexed

  • WebGL开发案例:http://www.tiyiweb.com/

  • littleworkshop(经典THREE 3D | 超级厉害的公司技术):https://www.littleworkshop.fr/

  • 高清城市系统:https://demos.littleworkshop.fr/demos/infinitown/

  • Three.js游戏案例:https://phoboslab.org/wipeout/

  • 书籍 Physically Based Rendering in Filament:https://google.github.io/filament/Filament.html

  • 经典游戏实现案例:https://phoboslab.org/wipeout/

  • 日月星辰动态 | 元宇宙的入口 | 超级炫酷:https://www.nationalgeographic.com/science/graphics/cassini-saturn-nasa-3d-grand-tour

  • three.js后期之自定义shader通道实现扫光效果:https://www.cnblogs.com/eco-just/p/11667713.html

  • 用three.js实现炫酷的城市扫光效果:https://blog.csdn.net/zhgu1992/article/details/104227100

  • GitHub:https://github.com/ma77os/InteractiveLandscape

  • 示例:https://tympanus.net/Development/InteractiveLandscape/

  • 暮志未晚技术博客:https://www.wjceo.com/

  • 3D案例:https://2912401452.github.io/

  • VR看房:http://test.mangoya.cn/Aimee/DEMO/3Drotate/demo/Sphere.html

  • 经典案例:https://threejs.org/examples/#webgl_helpers

  • 郭隆邦:http://www.yanhuangxueyuan.com/

  • https://duoduoview.com/example/access/

  • 游戏:https://github.com/Kingles/gamelab.js

  • WebGL中文网:http://www.hewebgl.com/

  • HexGL by BKcore:http://hexgl.bkcore.com/play/

  • https://codeartemis.github.io/

  • 使用THREE.js制作一款3D游戏:https://blog.csdn.net/qq_40748336/article/details/82946851

  • 日环食大挑战:https://quincychen.cn/threejs-experience/

  • IFE-WebGL:https://husterxsp.github.io/IFE-WebGL/

  • 游戏案例:https://husterxsp.github.io/IFE-WebGL/task8/

  • 车模:https://threejs.org/examples/webgl_materials_car.html

坐标辅助线系统

坐标系

正面面对电脑屏幕

  • X坐标:前后 | 正数-前 | 负数-后
  • Y坐标:上下 | 正数-上 | 负数-下
  • Z坐标:左右 | 正数-左 | 负数-右

坐标轴 | THREE.AxesHelper

作用:绘制模拟3个坐标轴的对象,红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.

var axesHelper = new THREE.AxesHelper(
  size: Number  // [可选] 表示三条坐标轴的绘制长度,默认为 1。
);

scene.add(axesHelper);
axesHelper.position.set(0, 0, 0);  // 设置坐标轴辅助中心点,默认为场景中心

THREE.GridHelper | 坐标格辅助对象

作用:坐标格辅助对象,坐标格实际上是2维线数组。

var gridHelper = new THREE.GridHelper( 
  size: number,            // 坐标格整体长宽尺寸大小,默认为 10。
  divisions: Number,       // 在坐标格整体长宽尺寸大小确定的基础上,设置坐标格的细分个数,默认为 10个。
  colorCenterLine: Color,  // 中线颜色. 值可以为 Color 类型, 16进制 和 CSS 颜色名. 默认为 0x444444
  colorGrid: Color         // 坐标格网格线颜色. 值可以为 Color 类型, 16进制 和 CSS 颜色名. 默认为 0x888888
)

scene.add(gridHelper);
gridHelper.position.set(0, 0, 0);  // 设置坐标格辅助中心点,默认为场景中心

BoxHelper

基础函数

console.log(new THREE.Vector2( 0, 1 ));        // {x: 0, y: 1}
console.log(new THREE.Vector3( 0, 1, 0 ));     // {x: 0, y: 1, z: 0}
console.log(new THREE.Vector4( 0, 1, 0, 0 ));  // {x: 0, y: 1, z: 0, w: 0}

Math工具

基础函数

console.log(new THREE.Color());  // {}
console.log(new THREE.Color( 0xff0000 ));           // {r: 1, g: 0, b: 0}
console.log(new THREE.Color("rgb(255, 0, 0)"));     // {r: 1, g: 0, b: 0}
console.log(new THREE.Color("rgb(100%, 0%, 0%)"));  // {r: 1, g: 0, b: 0}
console.log(new THREE.Color( 'skyblue' ));          // {r: 0.5294117647058824, g: 0.807843137254902, b: 0.9215686274509803}
console.log(new THREE.Color("hsl(0, 100%, 50%)"));  // {r: 1, g: 0, b: 0}
console.log(new THREE.Color( 1, 0, 0 ));            // {r: 1, g: 0, b: 0}

三维物体(Object3D)

  • 组(THREE.Group):方便统一管理物体
var obejct = new THREE.Object3D();
obejct.add();

var group = new THREE.Group();
group.add( cubeA );

var mouse = new THREE.Vector2( 0, 1 );  // {x: 0, y: 1}
var a = new THREE.Vector3( 0, 1, 0 );
var a = new THREE.Vector4( 0, 1, 0, 0 );


/**
 * new THREE.Color( 0xff0000 )
 * new THREE.Color("rgb(255, 0, 0)")
 * new THREE.Color("rgb(100%, 0%, 0%)")
 * new THREE.Color( 'skyblue' )
 * new THREE.Color("hsl(0, 100%, 50%)")
 */
var color = new THREE.Color();  

基础语法

最小可行性:代码示例

可直接运行,查看效果

<body></body>


<script type="importmap">
  {
    "imports": {
      "threejs/": "https://test.ysunlight.com/public/source/threejs/",
      "three": "https://test.ysunlight.com/public/source/three.module.min.js",
      "threejs/jsm/": "https://test.ysunlight.com/public/source/threejs/jsm/",
      "threejs/webgpu": "https://test.ysunlight.com/public/source/three.webgpu.min.js",
      "threejs/tsl": "https://test.ysunlight.com/public/source/three.tsl.min.js",
      "threejs/project/": "https://test.ysunlight.com/visual/"
    }
  }
</script>

<script type="module">
  import * as THREE from "three";


  /**
   * 场景
   */
  export function sceneCore(color = 0x000000) {
    var scene = new THREE.Scene();
    scene.background = new THREE.Color(color);

    return scene;
  }


  /**
   * 摄像机
   */
  export function cameraCore(options = {}) {
    var vector = options.vector || new THREE.Vector4(45, window.innerWidth / window.innerHeight, 0.1, 100000);
    var position = options.position || new THREE.Vector3(1000, 1000, 1000);
    var set = options.set || new THREE.Vector3(0, 1, 0);
    var lookAt = options.lookAt || new THREE.Vector3(0, 0, 0);

    var camera = new THREE.PerspectiveCamera(...vector);

    camera.position.set(...position);
    camera.up.set(...set);
    camera.lookAt(...lookAt);

    return camera;
  }


  /**
   * 渲染器
   */
  export function rendererCore(scene, camera) {
    var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.setPixelRatio(window.devicePixelRatio);

    renderer.shadowMap.enabled = true;
    renderer.toneMapping = THREE.PCFSoftShadowMap;

    document.body.appendChild(renderer.domElement);
    renderer.render(scene, camera);

    return renderer;
  }
</script>

<script type="module">
  import * as THREE from "three";

  import {
    rendererCore,
    sceneCore,
    cameraCore,
  } from "/visual/threejs/core/index.js";
  import { axesHelper, gridHelper } from "/visual/threejs/core/helper.js";
  import { orbitControlCore } from "/visual/threejs/core/control.js";
  import { basicGeometryCore } from "/visual/threejs/core/geometry.js";

  var scene = sceneCore();
  var camera = cameraCore();

  var geometry = basicGeometryCore(scene);

  axesHelper(scene);
  gridHelper(scene);

  var renderer = rendererCore(scene, camera);

  var orbitControl = orbitControlCore(camera, renderer);

  renderer.setAnimationLoop(() => {
    orbitControl.update();
    renderer.render(scene, camera);
  });

  window.addEventListener("resize", () => {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
  });

  console.log("THREE: ", THREE);
  console.log("scene: ", scene);
  console.log("camera: ", camera);
  console.log("renderer: ", renderer);
</script>

渲染器 Renderer

var renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true }); 

renderer.setClearColor(new THREE.Color(0x000000), 1);         // 设置窗口背景颜色为黑
renderer.setSize(window.innerWidth, window.innerHeight);   // 设置窗口尺寸
renderer.setPixelRatio( window.devicePixelRatio );         // 设置设备像素比。通常用于避免HiDPI设备上绘图模糊
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.toneMapping = THREE.ReinhardToneMapping;
renderer.shadowMap
renderer.
renderer.
renderer.

document.body.appendChild(renderer.domElement);
renderer.render(scene, camera);

CSS 2D渲染器(CSS2DRenderer)

CSS 3D渲染器(CSS3DRenderer)

SVG渲染器(SVGRenderer)

自定义挂载canvas

var renderer = new THREE.WebGLRenderer({
  canvas: document.getElementById('mainCanvas')
});

场景 Scene

var scene = new THREE.Scene();



scene.add(object1, object2, ..., objectn);

// 设置背景, Color(颜色) | 一个覆盖canvas的Texture(纹理)
scene.background = new THREE.Color( 0xff0000 );


// 雾(Fog)
scene.fog = new THREE.Fog(0xf7d9aa, 100, 950);


// 雾-指数(FogExp2)
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );


// 默认值为true,若设置了这个值,则渲染器会检查每一帧是否需要更新场景及其中物体的矩阵。 当设为false时,你必须亲自手动维护场景中的矩阵。
scene.autoUpdate = true;


// 添加物体
scene.add(object);
scene.remove(object.uuid);






import { RoomEnvironment } from "threejs/jsm/environments/RoomEnvironment.js";


var pmremGenerator = new THREE.PMREMGenerator(renderer);
scene.background = new THREE.Color(0xeeeeee);
scene.environment = pmremGenerator.fromScene(
  new RoomEnvironment(),
).texture;

背景:图片

// 设置背景纹理
scene.background = new THREE.TextureLoader().load('/Reference/images/141_1443.jpg'); 

RoomEnvironment

import { RoomEnvironment } from "threejs/jsm/environments/RoomEnvironment.js";


var environment = new RoomEnvironment();
var pmremGenerator = new THREE.PMREMGenerator( renderer );
var envMap = pmremGenerator.fromScene( environment ).texture;
scene.environment = envMap;

天空盒:六张图

var loader = new THREE.CubeTextureLoader().setPath(
  "https://test.ysunlight.com/server/static/images/cubemaps/"
);
var cubeTexture = await loader.loadAsync([
  "pos-x.jpg",
  "neg-x.jpg",
  "pos-y.jpg",
  "neg-y.jpg",
  "pos-z.jpg",
  "neg-z.jpg",
]);
scene.background = cubeTexture;

天空盒:一张图

var camera = cameraCore(scene, {
  vector: new THREE.Vector4(
    90,
    window.innerWidth / window.innerHeight,
    0.1,
    100
  ),
});
camera.position.set(0, 0, 0.001);

var axes = axesHelper(scene);
var grid = gridHelper(scene);

var renderer = rendererCore(scene, camera);
var orbitControl = orbitControlCore(camera, renderer);

var textures = getTexturesFromAtlasFile(
  "https://test.ysunlight.com/server/static/images/textures/cute/sun_temple_stripe.jpg",
  6
);

// orbitControl.enablePan = false;
// orbitControl.enableDamping = true;
orbitControl.rotateSpeed = -0.25;

var materials = [];

for (let i = 0; i < 6; i++) {
  materials.push(new THREE.MeshBasicMaterial({ map: textures[i] }));
}

var skyBox = new THREE.Mesh(
  new THREE.BoxGeometry(1, 1, 1),
  materials
);
skyBox.geometry.scale(1, 1, -1);
scene.add(skyBox);

function getTexturesFromAtlasFile(atlasImgUrl, tilesNum) {
  var textures = [];

  for (let i = 0; i < tilesNum; i++) {
    textures[i] = new THREE.Texture();
  }

  new THREE.ImageLoader().load(atlasImgUrl, (image) => {
    let canvas, context;
    var tileWidth = image.height;

    for (let i = 0; i < textures.length; i++) {
      canvas = document.createElement("canvas");
      context = canvas.getContext("2d");
      canvas.height = tileWidth;
      canvas.width = tileWidth;
      context.drawImage(
        image,
        tileWidth * i,
        0,
        tileWidth,
        tileWidth,
        0,
        0,
        tileWidth,
        tileWidth
      );
      textures[i].colorSpace = THREE.SRGBColorSpace;
      textures[i].image = canvas;
      textures[i].needsUpdate = true;
    }
  });

  return textures;
}

摄像机 Camera

⚠️ 摄像机Camera的一系列参数非常重要,直接影响是否正常呈现效果,务必做到非常熟悉具体参数的语法与赋值。

  • 摄像机,即摄像机的空间位置、摄像机对焦的目标对象位置
  • ⚠️ 投影效果:主视图,俯视图,左视图

具体语法

⚠️ 这里的参数非常重要,需要熟悉。

/**
 * 位置:摄像机在场景中的摆放位置,其位置距离场景中的物体越远,物体看起来越小
 * ⚠️ 注意 3D 位置  感知
 * ⚠️ near < z < far
 * 
 * ⚠️ 摄像机的位置不能超出 far 的视野范围,否则无法正常呈现效果
 */
camera.position.set(x, y, z);
camera.position.set(x, 0, 0); // 正前方居中
camera.position.set(0, y, 0); // 正上方居中
camera.position.set(0, 0, z); // 正右方居中





/**
 * 方位:相机相对于坐标系(0, 0, 0)的摆放姿势
 * 
 * 眼睛向上看(0, -1, 0),眼睛向前看(1, 0, 0),眼睛向右看(0, 0, -1),其他
 * ⚠️ camera.up必须放置在camera.lookAt前面
 */
camera.up.set(1, 0, 0); // 从 x 向 -x 方向看
camera.up.set(0, 1, 0); // 从 y 向 -y 方向看
camera.up.set(0, 0, 1); // 从 z 向 -z 方向看





/**
 * 焦点:声明相机拍摄方向(朝向),即相机拍摄时指向的中心点。例如我们看电脑是从屏幕外部向屏幕内部看
 * 
 * 基准点 | 两点决定一条直线 | camera.position与camera.lookAt决定了物体的成像规则 | 大小与形状
 * ⚠️ 可设置小范围THREE.GridHelper网格线看效果
 */
camera.lookAt(0, 0, 0);

透视投影

用来模拟人眼所看到的景象,近大远小

  • 特别注意:在透视投影机制上,camera.position.z值越大,物体成像尺寸越小。
var camera = new THREE.PerspectiveCamera( 
  // 摄像机视锥体垂直视野角度,值越大,物体在场景中的渲染视觉成像越小。默认值 50,值范围0 ~ 180 
  // 类似于人的眼睛睁开视野范围,值越大,物体对象越小。
  fov : Number,  
  // 摄像机视锥体长宽比,实际画布的宽/高比,默认值 1
  aspect : Number,  
  // 摄像机视锥体近端面 | 即摄像机镜头的拍照视野的有效开始点 | 一般就是镜头位置,默认值 0.1
  near : Number,  
  // 摄像机视锥体远端面,该值必须大于near值,可以无限大。
  // 约束镜头能够覆盖的有效范围,小于空间的物体位置时,就会导致物体失去视野呈现,默认值 2000   
  far : Number       
);

正交投影

在这种投影模式下,渲染图像中物体的大小保持不变 无论距离相机多远,都保持恒定。这很有用 用于渲染2D场景和UI元素等。

  • 重点:left、right、top、bottom,构成了一个长方体管子,通过这个管子窥探画布上的成像
new THREE.OrthographicCamera( 
  left : Number,      // 摄像机视锥体左侧面
  right : Number,     // 摄像机视锥体右侧面,不能大于left
  top : Number,       // 摄像机视锥体上侧面
  bottom : Number,    // 摄像机视锥体下侧面,不能大于top
  near : Number,      // 摄像机视锥体近端面
  far : Number        // 摄像机视锥体远端面
);



var camera = new THREE.OrthographicCamera(l, r, t, b, 0.1, 2000);


// 当改变fov、aspect、near、far后,需要重新计算投影矩阵
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();  // 更新摄像机投影矩阵。在任何参数被改变以后必须被调用。

camera.visible = true;
camera.castShadow
camera.receiveShadow

ArrayCamera | 摄像机阵列(ArrayCamera)

功效:ArrayCamera 用于更加高效地使用一组已经预定义的摄像机来渲染一个场景。这将能够更好地提升VR场景的渲染性能。

Camera

CubeCamera

OrthographicCamera

PerspectiveCamera

StereoCamera

控制器 Controls

轨道控制器

import { OrbitControls } from '/public/source/threejs/jsm/controls/OrbitControls.js';


var orbitControl = new OrbitControls(camera, renderer.domElement);

orbitControl.enableZoom = true;             // 是否可以缩放
orbitControl.enablePan = true;              // 是否开启右键拖拽
orbitControl.maxPolarAngle = Math.PI/2;     // 能够垂直旋转的角度的上限,范围是0到Math.PI,其默认值为Math.PI。

orbitControl.target.set( 0, 0.5, 0 );
orbitControl.enableDamping = true;    // 使动画循环使用时阻尼或自转 意思是否有惯性
orbitControl.dampingFactor = 0.25;    // 动态阻尼系数,即鼠标拖拽旋转灵敏度,为0时不能拖曳操作,值越大越灵敏
orbitControl.enableZoom = true;       // 是否可以缩放
orbitControl.autoRotate = false;      // 是否自动旋转,如果它被启用,你必须在你的动画循环里调用.update()。
orbitControl.autoRotateSpeed = 0.2;   // 围绕目标旋转的速度将有多快,默认值为2.0,相当于在60fps时每旋转一周需要30
orbitControl.minDistance = 50;        // 设置相机距离原点的最近距离,约束视野穿透场景中物体,影响体验
orbitControl.maxDistance = 200;       // 设置相机距离原点的最远距离
orbitControl.enablePan = true;        // 是否开启右键拖拽

orbitControl.listenToKeyEvents( window );        // 为指定的DOM元素添加按键监听。推荐将window作为指定的DOM元素。
orbitControl.screenSpacePanning = false;    // 定义当平移的时候摄像机的位置将如何移动。


renderer.setAnimationLoop(() => {
  orbitControl.update();
  renderer.render( scene, camera );
});

拖放控制器

  import { DragControls } from '/public/source/threejs/jsm/controls/DragControls.js';

第一人称控制器

  import { FirstPersonControl } from '/public/source/threejs/jsm/controls/FirstPersonControls.js';

飞行控制器

  import { FlyControls } from '/public/source/threejs/jsm/controls/FlyControls.js';


  var controls = new FlyControls( camera, renderer.domElement );
  controls.autoForward = false;  //  若该值设为true,初始变换后,摄像机将自动向前移动(且不会停止)。默认为false。
  controls.dragToLook = false;  // 若该值设为true,你将只能通过执行拖拽交互来环视四周。默认为false。
  controls.movementSpeed = 1;  // 移动速度,默认为1。
  controls.rollSpeed = 0.005;  // 旋转速度。默认为0.005。

  controls.dispose();  // 若不再需要该控制器,则应当调用此函数。
  controls.update();  // 更新控制器,常被用在动画循环中。

  // 监听变化
  controls.change = function {};


  // 调度机制
  var clock = new THREE.Clock();
  function animate() {
    requestAnimationFrame( animate );
    var delta = clock.getDelta();
    flyControls.update(delta);
    renderer.render( scene, camera );
  }
  animate();

指针锁定控制器

场景:射击游戏

  import { PointerLockControls } from '/public/source/threejs/jsm/controls/PointerLockControls.js';


  var controls = new PointerLockControls(camera, document.body);

轨迹球控制器

  import { TrackballControls } from '/public/source/threejs/jsm/controls/TrackballControls.js';

变换控制器

  import { TransformControls } from '/public/source/threejs/jsm/controls/TransformControls.js';

(ArcballControls)

  import { ArcballControls } from '/public/source/threejs/jsm/controls/ArcballControls.js';

灯光模型Light

  • ⚠️ 光照依赖材质类型(材质的不同颜色设置也存在很大影响),光照只有在特定材质类型的基础上才能产生效果 | 注意THREE.PlaneGeometry问题,[单面] [垂直]等问题

  • ⚠️ 投影很复杂,运用之前请仔细参考 /visual/ThreeJS/Example/Light.html

  • ⚠️ 光源位置必须高于物体位置

  • [投影] 采用[THREE.ShadowMaterial()],除了可以投影,其他的都是透明,体验很好

  • 开启渲染器阴影效果:renderer.shadowMap.enabled = true;

  • 开启物体投影属性:[object].castShadow = true;

  • 开启光源阴影属性:[light].castShadow = true;

  • 开启接收阴影:[name].receiveShadow = true;

  • 注意光源的distance值,该值决定了投影的问题。

  • 前置:阴影呈现载体,一般使用 THREE.PlaneGeometry 绘制

  • 材质要求:必须能够产生光照反应的材质,否则无效。

代码示例

import * as THREE from "three";

import {
  rendererCore,
  sceneCore,
  cameraCore,
} from "/visual/threejs/core/index.js";
import { axesHelper, gridHelper } from "/visual/threejs/core/helper.js";
import { orbitControlCore } from "/visual/threejs/core/control.js";
import {
  directionalLightCore,
  ambientLightCore,
  pointLightCore,
  hemisphereLightCore,
  spotLightCore,
  rectAreaLightCore,
} from "/visual/threejs/core/light.js";
import {
  basicGeometryCore,
  planGeometryCore,
} from "/visual/threejs/core/geometry.js";

var scene = sceneCore({
  background: new THREE.Color(0xffffff),
});
var camera = cameraCore();

var geometry = basicGeometryCore(scene, {
  position: new THREE.Vector3(0, 100, 0),
});

axesHelper(scene);
gridHelper(scene);

var renderer = rendererCore(scene, camera);
renderer.shadowMap.enabled = true;

var orbitControl = orbitControlCore(camera, renderer);

var light = directionalLightCore(scene);
light.target = geometry;

// 投影面板
planGeometryCore(scene, {
  material: new THREE.MeshPhongMaterial(),
});

renderer.setAnimationLoop(() => {
  orbitControl.update();
  renderer.render(scene, camera);
});



// 基础形状
export function basicGeometryCore(scene, options = {}) {
  var content = options.content || new THREE.Vector3(100, 100, 100);
  var position = options.position || new THREE.Vector3(0, 0, 0);
  var geometry = options.geometry || new THREE.BoxGeometry(...content);
  var material = options.material || new THREE.MeshNormalMaterial();

  var mesh = new THREE.Mesh(geometry, material);
  mesh.position.set(...position);
  mesh.castShadow = true;

  scene.add(mesh);

  return mesh;
}

/**
 * 平行光
 *  
 */
export function directionalLightCore(scene, options = {}) {
  var content = options.content || new THREE.Vector2(0xffffff, 1);
  var position = options.position || new THREE.Vector3(0, 500, 0);
  var castShadow = typeof options.castShadow === 'boolean' ? options.castShadow : true;
  var shadow = typeof options.shadow === 'boolean' ? options.shadow : true;
  var camera = options.camera || { top: 100, right: 100, bottom: -100, left: -100, near: 0.1, far: 10000 };
  var helper = typeof options.helper === 'boolean' ? options.helper : true;

  var light = new THREE.DirectionalLight(...content);
  light.position.set(...position);
  light.castShadow = castShadow;

  if (shadow) {
    for (let i in camera) {
      light.shadow.camera[i] = camera[i];
    }
  }
  scene.add(light);

  if (helper) {
    var lightHelper = new THREE.DirectionalLightHelper(light, 10);
    scene.add(lightHelper);
  }

  return light;
}


/**
 * 平面缓冲几何体(PlaneGeometry)
 * 注意:THREE.PlaneGeometry仅仅生成为一个平面,且有一面不可视,默认为与Y轴平行
 */
export function planGeometryCore(scene, options = {}) {
  var content = options.content || new THREE.Vector2(2000, 2000);
  var material = options.material || new THREE.MeshPhongMaterial({ color: 0x000000, depthWrite: false });
  var position = options.position || new THREE.Vector3(0, 0, 0);
  var rotation = options.rotation || new THREE.Vector3(Math.PI * -1 / 2, 0, 0);

  let plane = new THREE.Mesh(
    new THREE.PlaneGeometry(...content),
    material,
  );
  plane.position.set(...position);
  plane.rotation.set(...rotation);
  plane.receiveShadow = true;
  scene.add(plane);

  return plane;
}

基本概念

  • 局部光照:通过计算对象表面和射向它的光线之间的交点来实现表面绘制的模型
  • 全局光照:为了生成更加真实的光照效果,必须考虑从其他对象反射出来的光线,这些反射光线也会照射到物体上并对它们表面的明暗效果产生贡献。

环境光

功效:环境光会均匀的照亮场景中的所有物体。

  • ⚠️ 环境光不能用来投射阴影,因为它没有方向。
new THREE.AmbientLight( 
  color : Integer,   // [可选] 颜色的rgb数值。缺省值为 0xffffff。
  intensity : Float  // [可选] 光照的强度。缺省值为 1。
)


// 环境光(照亮整个场景,无阴影)
var ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);



var ambientLight = new THREE.AmbientLight( 0xffffff );
scene.add( ambientLight );

平行光:太阳光

DirectionalLightHelper | [太阳光] [投影]

  • 材料(Material):[不限]
  • ⚠️ 需要投影则配置shadow参数
  • 功效:平行光是沿着特定方向发射的光。这种光的表现像是无限远,从它发出的光线都是平行的。常常用平行光来模拟太阳光 的效果; 太阳足够远,因此我们可以认为太阳的位置是无限远,所以我们认为从太阳发出的光线也都是平行的。

语法结构

new THREE.DirectionalLight( 
  color : Integer,   // [可选] 16进制表示光的颜色。 缺省值为 0xffffff (白色)。
  intensity : Float  // [可选] 光照的强度。缺省值为1。
)






// 方向光(模拟太阳光,产生阴影)
var directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 100, 50);
directionalLight.castShadow = true;
scene.add(directionalLight);










var directionalLight = new THREE.DirectionalLight( 0xFFFFFF, 1 );
directionalLight.castShadow = true; 
scene.add( directionalLight );

// 追踪到目标对象,即平行光会一直追踪目标对象
var object = new THREE.Object3D();
scene.add(object);
directionalLight.target = object;

// 设置计算阴影的区域
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 300;
directionalLight.shadow.camera.left = -50;
directionalLight.shadow.camera.right = 50;
directionalLight.shadow.camera.top = 200;
directionalLight.shadow.camera.bottom = -100;




// 辅助线
var helper = new THREE.DirectionalLightHelper( directionalLight, 5 );
scene.add( helper );
代码示例
import * as THREE from "three";

import {
  rendererCore,
  sceneCore,
  cameraCore,
} from "/visual/threejs/core/index.js";
import { axesHelper, gridHelper } from "/visual/threejs/core/helper.js";
import { orbitControlCore } from "/visual/threejs/core/control.js";
import {
  basicGeometryCore,
  planGeometryCore,
} from "/visual/threejs/core/geometry.js";

var scene = sceneCore();
var camera = cameraCore(scene);

scene.background = new THREE.Color(0xf0f8ff);

axesHelper(scene);
gridHelper(scene);

var renderer = rendererCore(scene, camera);

var orbitControl = orbitControlCore(camera, renderer);

// 5. 创建核心:平行光(DirectionalLight)
// 第一个参数:光的颜色,第二个参数:光照强度
var directionalLight = new THREE.DirectionalLight(0xffffff, 1.2);
directionalLight.position.set(-2, 150, 2); // 设置平行光位置(平行光沿自身位置指向原点方向发射)
// 开启平行光投射阴影
directionalLight.castShadow = true;
// 调整阴影相机参数(控制阴影渲染范围和精度)
directionalLight.shadow.camera.left = -100;
directionalLight.shadow.camera.right = 100;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.bottom = -100;
directionalLight.shadow.camera.near = 0.1;
directionalLight.shadow.camera.far = 200;
directionalLight.shadow.mapSize.set(2048, 2048); // 阴影贴图分辨率,值越高阴影越清晰
scene.add(directionalLight); // 将平行光添加到场景

// 辅助线
var helper = new THREE.DirectionalLightHelper(directionalLight, 1);
scene.add(helper);

var cube = basicGeometryCore(scene, {
  content: new THREE.Vector3(20, 20, 20),
  material: new THREE.MeshStandardMaterial({
    color: 0xff4500, // 立方体颜色
    metalness: 0.3, // 金属度
    roughness: 0.7, // 粗糙度
  }),
});
cube.position.set(0, 40, 0);

planGeometryCore(scene, {
  material: new THREE.MeshStandardMaterial({
    color: 0x32cd32,
    metalness: 0.1,
    roughness: 0.8,
  }),
});

/**
 * @ 渲染
 */
renderer.setAnimationLoop(() => {
  orbitControl.update();
  renderer.render(scene, camera);

  // 立方体旋转
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
});

window.addEventListener("resize", () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

点光源:光照很显著

PointLightHelper | [灯泡] [投影]

  • 材料(Material):[不限]
  • ⚠️ 需要light.add(target); 添加目标对象才能显著有效
  • ⚠️ 每一个参数至关重要
  • 功效:从一个点向各个方向发射的光源。一个常见的例子是模拟一个灯泡发出的光。
new THREE.PointLight( 
  color : Integer,    // [可选] 十六进制光照颜色。 缺省值 0xffffff (白色)。
  intensity : Float,  // [可选] 光照强度。 缺省值 1,值越大,光照效果越显著。
  distance : Number,  // [可选] 这个距离表示从光源到光照强度为0的位置。 当设置为0时,光永远不会消失(距离无穷大)。缺省值 0.
  decay : Float       // [可选] 沿着光照距离的衰退量。缺省值 1。 在 physically correct 模式中,decay = 2,值越大,光照效果越弱,甚至看不到效果。
)



var pointLight = new THREE.PointLight( 0xffffff, 1 );
pointLight.position.set( 0, 0, 0 );
pointLight.castShadow = true;
scene.add( pointLight );

// 辅助线
var pointLightHelper = new THREE.PointLightHelper( pointLight, 1 );
scene.add( pointLightHelper );






renderer.setAnimationLoop(() => {
  orbitControl.update();
  renderer.render(scene, camera);

  // 可选:让点光源缓慢旋转,展示动态光照效果
  pointLight.position.x = 3 + Math.sin(Date.now() * 0.001) * 2;
  pointLight.position.z = 5 + Math.cos(Date.now() * 0.001) * 2;
  // 同步更新点光源辅助工具(光源位置变化后需更新)
  pointLightHelper.update();
});

代码示例

import * as THREE from "three";

import {
  rendererCore,
  sceneCore,
  cameraCore,
} from "/visual/threejs/core/index.js";
import { axesHelper, gridHelper } from "/visual/threejs/core/helper.js";
import { orbitControlCore } from "/visual/threejs/core/control.js";
import {
  basicGeometryCore,
  planGeometryCore,
} from "/visual/threejs/core/geometry.js";

var scene = sceneCore();
var camera = cameraCore();

camera.position.set(20, 20, 20);
scene.background = new THREE.Color(0xffffff);

axesHelper(scene);
gridHelper(scene);

var renderer = rendererCore(scene, camera);
var orbitControl = orbitControlCore(camera, renderer);

var pointLight = new THREE.PointLight(0xffffff, 100, 0, 3);
pointLight.position.set(3, 8, 5);
scene.add(pointLight);

var pointLightHelper = new THREE.PointLightHelper(
  pointLight,
  1,
  0xff0000
);
scene.add(pointLightHelper);

// 投影面板
planGeometryCore(scene, {
  content: new THREE.Vector2(20, 20),
});

// 多个立方体:展示光照的明暗变化
var cubeGeometry = new THREE.BoxGeometry(1, 1, 1);
var cubePositions = [
  [-2, 0, 0],
  [0, 0, 0],
  [2, 0, 0],
  [-2, 0, -2],
  [0, 0, -2],
  [2, 0, -2],
];

cubePositions.forEach((pos) => {
  var cubeMaterial = new THREE.MeshStandardMaterial({
    color: new THREE.Color(Math.random(), Math.random(), Math.random()), // 随机颜色
    metalness: 0.3,
    roughness: 0.7,
  });
  var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
  cube.position.set(...pos);
  scene.add(cube);
});

// 球体:展示曲面的光照效果
var sphereGeometry = new THREE.SphereGeometry(1, 32, 32);
var sphereMaterial = new THREE.MeshStandardMaterial({
  color: 0x00ffff,
  metalness: 0.5,
  roughness: 0.5,
});
var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(0, 1, 2);
scene.add(sphere);

renderer.setAnimationLoop(() => {
  orbitControl.update();
  renderer.render(scene, camera);

  pointLight.position.x = 3 + Math.sin(Date.now() * 0.001) * 2;
  pointLight.position.z = 5 + Math.cos(Date.now() * 0.001) * 2;
  pointLightHelper.update();
});

聚光灯

SpotLightHelper [投影]

  • 材料(Material):MeshLambertMaterial | MeshPhongMaterial
  • 功效:光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大。

构造函数

new THREE.SpotLight( 
  color : Integer,    // [可选] 十六进制光照颜色。 缺省值 0xffffff (白色)。
  intensity : Float,  // [可选] 光照强度。 缺省值 1。
  distance : Float,   // [可选] 从光源发出光的最大距离,其强度根据光源的距离线性衰减。
  angle : Radians,    // [可选] 光线散射角度,最大为Math.PI/2。
  penumbra : Float,   // [可选] 聚光锥的半影衰减百分比。在0和1之间的值。默认为0。
  decay : Float       // [可选] 沿着光照距离的衰减量。
)




var spotLight = new THREE.SpotLight( 0xffffff );
spotLight.position.set( 100, 1000, 100 );  
spotLight.angle = Math.PI /6
spotLight.castShadow = true;
scene.add( spotLight );

// 阴影区域
spotLight.shadow.camera.near = 1
spotLight.shadow.camera.far = 300
spotLight.shadow.camera.fov = 20

// 辅助线
var lightHelper = new THREE.SpotLightHelper( spotLight );
scene.add( lightHelper );

半球光

HemisphereLightHelper

  • 功效:光源直接放置于场景之上,光照颜色从天空光线颜色渐变到地面光线颜色。
  • ⚠️ 半球光不能投射阴影。
new THREE.HemisphereLight( 
  skyColor : Integer,     // [可选] 天空中发出光线的颜色。 缺省值 0xffffff。
  groundColor : Integer,  // [可选] 地面发出光线的颜色。 缺省值 0xffffff。
  intensity : Float       // [可选] 光照强度。 缺省值 1。
)




var hemisphereLight = new THREE.HemisphereLight( 0xffffff, 0xff0000, 1 );
hemisphereLight.position.set( 100, 1000, 100 );
scene.add( hemisphereLight );

// 辅助线
var lightHelper = new THREE.HemisphereLightHelper( hemisphereLight, 5 );
scene.add( lightHelper );

平面光光源

RectAreaLightHelper

  • [材料(Material)]:支持MeshStandardMaterial | MeshPhysicalMaterial
  • 功效:平面光光源从一个矩形平面上均匀地发射光线。这种光源可以用来模拟像明亮的窗户或者条状灯光光源。
  • ⚠️ 光源有效距离,如果太远则失效。
  • ⚠️ 不支持阴影。
  • 你必须在你的场景中加入 RectAreaLightUniformsLib ,并调用init()。
new THREE.RectAreaLight( 
  color : Integer,    // [可选] 十六进制数字表示的光照颜色。缺省值为 0xffffff (白色)
  intensity : Float,  // [可选] 光源强度/亮度 。缺省值为 1。
  width : Float,      // [可选] 光源宽度。缺省值为 10。
  height : Float      // [可选] 光源高度。缺省值为 10。
)





import { RectAreaLightUniformsLib } from '/public/source/threejs/jsm/lights/RectAreaLightUniformsLib.js';
import { RectAreaLightHelper } from '/public/source/threejs/jsm/helpers/RectAreaLightHelper.js';


RectAreaLightUniformsLib.init();




var rectAreaLight = new THREE.RectAreaLight( 0xffffff, 1,  10, 10 );
rectAreaLight.position.set( 5, 5, 0 );
rectAreaLight.lookAt( 0, 0, 0 );
scene.add( rectAreaLight );

var lightHelper = new THREE.RectAreaLightHelper( rectAreaLight );
scene.add( lightHelper );

环境光探针

new THREE.AmbientLightProbe( 
  color : Color,     // [可选] 一个表示颜色的 Color 的实例、字符串或数字。
  intensity : Float  // [可选] 光照探针强度的数值。默认值为1。
)

半球光探针

new THREE.HemisphereLight( 
  skyColor : Integer,     // [可选] 一个表示颜色的 Color 的实例、字符串或数字。
  groundColor : Integer,  // [可选] 一个表示颜色的 Color 的实例、字符串或数字。
  intensity : Float       // [可选] 光照探针强度的数值。默认值为1。
)

镜头光晕(LensFlare)


基类:Light

API属性与方法

材质 Material

 THREE.DoubleSide

THREE.MeshNormalMaterial

THREE.MeshNormalMaterial

// 有颜色:红、绿、蓝
var geometry = new THREE.BoxGeometry(100, 100, 100, 1, 1, 1);
var material = new THREE.MeshNormalMaterial();
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

THREE.MeshBasicMaterial

THREE.MeshBasicMaterial,一种用于简单阴影(平面或线框)方式绘制几何体的材质。

  • ⚠️ 这种材料不受光线影响。
  • ⚠️ 无光照依赖,性能最高,适合简单纯色 / 线框展示,不支持高光 / 阴影。
// 纯色: 灰
var geometry = new THREE.BoxGeometry(100, 100, 100, 1, 1, 1);
var material = new THREE.MeshBasicMaterial();
var cube = new THREE.Mesh(geometry, material);
scene.add(cube);

立方体六面贴纹理图片

var textureLoader = new THREE.TextureLoader();
var textures = [
  textureLoader.load(
    "https://test.ysunlight.com/server/static/images/001.jpeg"
  ), // 正X面 贴图
  textureLoader.load(
    "https://test.ysunlight.com/server/static/images/002.jpeg"
  ), // 负X面 贴图
  textureLoader.load(
    "https://test.ysunlight.com/server/static/images/003.jpeg"
  ), // 正Y面 贴图
  textureLoader.load(
    "https://test.ysunlight.com/server/static/images/004.jpeg"
  ), // 负Y面 贴图
  textureLoader.load(
    "https://test.ysunlight.com/server/static/images/005.jpeg"
  ), // 正Z面 贴图
  textureLoader.load(
    "https://test.ysunlight.com/server/static/images/006.jpeg"
  ), // 负Z面 贴图
];
var materials = textures.map((texture) => {
  return new THREE.MeshBasicMaterial({
    map: texture, // 绑定当前纹理
    side: THREE.FrontSide, // 只渲染正面,优化性能
  });
});
var geometry = new THREE.BoxGeometry(100, 100, 100, 1, 1, 1);
var cube = new THREE.Mesh(geometry, materials);
scene.add(cube);

标准网格材质

THREE.MeshStandardMaterial

  • ⚠️ 物理基础材质(PBR),视觉效果最真实,支持金属度、粗糙度等物理属性,推荐优先使用。
var material = new THREE.MeshStandardMaterial({
  color: new THREE.Color(
    Math.random(),
    Math.random(),
    Math.random(),
  ), // 随机颜色
  metalness: 1,
  roughness: 0.7,
});

Lambert网格材质

THREE.MeshLambertMaterial

  • ⚠️ 漫反射材质,受光源影响,性能较好,无高光效果。

基础线条材质

THREE.LineBasicMaterial

虚线材质

THREE.LineDashedMaterial

深度网格材质

THREE.MeshDepthMaterial

THREE.MeshDistanceMaterial

THREE.MeshMatcapMaterial

Phong网格材质

THREE.MeshPhongMaterial

物理网格材质

THREE.MeshPhysicalMaterial

THREE.MeshToonMaterial

点材质

THREE.PointsMaterial

原始着色器材质

THREE.RawShaderMaterial

着色器材质

THREE.ShaderMaterial

阴影材质

THREE.ShadowMaterial

点精灵材质

THREE.SpriteMaterial

基类:Material

API属性与方法

点与线Line

点(THREE.Points)

var pointsMaterial = new THREE.PointsMaterial(
  alphaMap : Texture
  color : Color
  map : Texture
  morphTargets : Boolean
  size : Number
  sizeAttenuation : Boolean
);







let geometry = new THREE.BufferGeometry();
let positions = [];
let particles = 5000;
let color = new THREE.Color();
let colors = [];
/* 使粒子在立方体范围内扩散 */
let n = 200, n2 = n / 2;

for (let i = 0; i < particles; i++) {
  // 点
  let x = Math.random() * n - n2;
  let y = Math.random() * n - n2;
  let z = Math.random() * n - n2;
  positions.push(x, y, z);
  // 颜色
  let vx = (x / n) + 0.5;
  let vy = (y / n) + 0.5;
  let vz = (z / n) + 0.5;
  color.setRGB(vx, vy, vz);
  colors.push(color.r, color.g, color.b);
}

geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));

var material = new THREE.PointsMaterial({
  size: 5,
  sizeAttenuation: true,
  vertexColors: THREE.VertexColors,
  transparent: true,
  opacity: 1
});
var points = new THREE.Points(geometry, material);
scene.add(points);

线

// 线段 | gl.LINE_STRIP
new THREE.Line( 
  geometry : BufferGeometry, 
  material : Material 
)

// 线段 | 自动闭合 | gl.LINE_STRIP
new THREE.LineLoop( 
  geometry : BufferGeometry, 
  material : Material 
)

// 线段 | 与 THREE.Line相同 | gl.LINES
new THREE.LineSegments( 
  geometry : BufferGeometry, 
  material : Material 
)






// 代码示例
var points = [];
points.push( new THREE.Vector3( 0, 0, 0 ) );
points.push( new THREE.Vector3( 100, 100, 0 ) );

var geometry = new THREE.BufferGeometry().setFromPoints( points );
var line = new THREE.Line( 
  geometry, 
  new THREE.LineBasicMaterial({
    color: 0xa84f3c
  })
);
scene.add( line );









// 线条件绘制
var points = ...;

var geometry = new THREE.BufferGeometry().setFromPoints( points );
var material = new THREE.LineBasicMaterial( { color: 0xff0000 } );

var line = new THREE.Line( geometry, material );
scene.add( line );

二次贝塞尔曲线

  var path = new THREE.Path();
  path.lineTo( 0, 0 );
  path.quadraticCurveTo( 0, 10, 10, 10 );
  var points = path.getPoints();

三维样条曲线

  export function CatmullRomCurve3() {
    var curve = new THREE.CatmullRomCurve3( [
      new THREE.Vector3( -10, 0, 10 ),
      new THREE.Vector3( -5, 5, 5 ),
      new THREE.Vector3( 0, 0, 0 ),
      new THREE.Vector3( 5, -5, 5 ),
      new THREE.Vector3( 10, 0, 10 )
    ] );

    var points = curve.getPoints( 50 );
    var geometry = new THREE.BufferGeometry().setFromPoints( points );
    var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
    var line = new THREE.Line( geometry, material );  
    scene.add(line); 
  }

椭圆 | 圆 | 弧线

export function EllipseCurve() {
  var curve = new THREE.EllipseCurve(
    0,  0,            // ax, aY
    10, 10,           // xRadius, yRadius
    0,  2 * Math.PI,  // aStartAngle, aEndAngle
    false,            // aClockwise
    0                 // aRotation
  );
  
  var points = curve.getPoints( 50 );
  var geometry = new THREE.BufferGeometry().setFromPoints( points );
  var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
  var line = new THREE.Line( geometry, material );  
  scene.add(line);
}

二维三次贝塞尔曲线

  export function CubicBezierCurve() {
    var curve = new THREE.CubicBezierCurve(
      new THREE.Vector2( -10, 0 ),
      new THREE.Vector2( -5, 15 ),
      new THREE.Vector2( 20, 15 ),
      new THREE.Vector2( 10, 0 )
    );
    
    var points = curve.getPoints( 50 );
    var geometry = new THREE.BufferGeometry().setFromPoints( points );
    var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
    var line = new THREE.Line( geometry, material );  
    scene.add(line);
  }

三维三次贝塞尔曲线

  export function CubicBezierCurve3() {
    var curve = new THREE.CubicBezierCurve3(
      new THREE.Vector3( -10, 0, 0 ),
      new THREE.Vector3( -5, 15, 0 ),
      new THREE.Vector3( 20, 15, 0 ),
      new THREE.Vector3( 10, 0, 0 )
    );
    
    var points = curve.getPoints( 50 );
    var geometry = new THREE.BufferGeometry().setFromPoints( points );
    var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
    var line = new THREE.Line( geometry, material );  
    scene.add(line);
  }

二维二次贝塞尔曲线

  export function QuadraticBezierCurve() {
    var curve = new THREE.QuadraticBezierCurve(
      new THREE.Vector2( -10, 0 ),
      new THREE.Vector2( 20, 15 ),
      new THREE.Vector2( 10, 0 )
    );
    
    var points = curve.getPoints( 50 );
    var geometry = new THREE.BufferGeometry().setFromPoints( points );
    var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
    var line = new THREE.Line( geometry, material );  
    scene.add(line);
  }

三维二次贝塞尔曲线

  export function QuadraticBezierCurve3() {
    var curve = new THREE.QuadraticBezierCurve3(
      new THREE.Vector3( -10, 0, 0 ),
      new THREE.Vector3( 20, 15, 0 ),
      new THREE.Vector3( 10, 0, 0 )
    );
    
    var points = curve.getPoints( 50 );
    var geometry = new THREE.BufferGeometry().setFromPoints( points );
    var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );
    var line = new THREE.Line( geometry, material );  
    scene.add(line);
  }

纹理Texture

代码示例一

var loader = new THREE.TextureLoader();
var texture = await loader.loadAsync(
  "https://test.ysunlight.com/public/source/threejs/textures/hardwood2_diffuse.jpg"
);

// texture.minFilter = THREE.NearestFilter;
// texture.magFilter = THREE.NearestFilter;
texture.anisotropy = 1;
texture.generateMipmaps = false;

// 纹理参数优化(可选,提升显示效果)
texture.wrapS = THREE.RepeatWrapping; // 水平重复
texture.wrapT = THREE.RepeatWrapping; // 垂直重复
texture.repeat.set(200, 200); // 贴图重复次数
texture.magFilter = THREE.LinearFilter; // 放大过滤
texture.minFilter = THREE.LinearMipmapLinearFilter; // 缩小过滤

texture.encoding = THREE.sRGBEncoding; // 颜色空间校正

texture.anisotropy = renderer.capabilities.getMaxAnisotropy(); // 各向异性过滤,提升纹理清晰度

// 如果更改了图像,画布,视频和数据纹理,则需要设置更新纹理,渲染对象就会自动更新。
texture.needsUpdate = true;



// MeshBasicMaterial:无光照依赖的基础材质,side设为FrontSide确保仅正面显示贴图
var material = new THREE.MeshBasicMaterial({
  map: texture,
  // side: THREE.FrontSide, // 显式指定仅正面渲染(默认值,可省略但更清晰)
  side: THREE.DoubleSide,
  transparent: false, // 关闭透明(根据贴图需求调整)
});
// // 可选:开启材质的PBR属性,增强真实感
// material.roughness = 0.8; // 粗糙度
// material.metalness = 0.1; // 金属度
// material.side = THREE.DoubleSide;
// material.needsUpdate = true;


var geometry = new THREE.PlaneGeometry(40000, 30000);
var planeMesh = new THREE.Mesh(geometry, m);
planeMesh.position.set(...new THREE.Vector3(0, 0, 0));
planeMesh.rotation.set(...new THREE.Vector3(Math.PI / 2, 0, 0));

scene.add(planeMesh); 

THREE.ImageUtils.loadTexture已废弃

THREE.ImageUtils.loadTexture has been deprecated. Use THREE.TextureLoader() instead.

代码示例二

var hemiLight = new THREE.HemisphereLight(0xddeeff, 0x0f0e0d, 5);
scene.add(hemiLight);

var loader = new THREE.TextureLoader();
var texture = await loader.loadAsync(
  "https://test.ysunlight.com/public/source/threejs/textures/brick_diffuse.jpg"
);

// texture.minFilter = THREE.NearestFilter;
// texture.magFilter = THREE.NearestFilter;
texture.anisotropy = 1;
texture.generateMipmaps = false;

// 纹理参数优化(可选,提升显示效果)
texture.wrapS = THREE.RepeatWrapping; // 水平重复
texture.wrapT = THREE.RepeatWrapping; // 垂直重复
texture.repeat.set(200, 200); // 贴图重复次数
texture.magFilter = THREE.LinearFilter; // 放大过滤
texture.minFilter = THREE.LinearMipmapLinearFilter; // 缩小过滤

// 如果更改了图像,画布,视频和数据纹理,则需要设置更新纹理,渲染对象就会自动更新。
texture.needsUpdate = true;

texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.anisotropy = 4;
texture.repeat.set(1, 1);
texture.colorSpace = THREE.SRGBColorSpace;

var boxGeometry = new THREE.BoxGeometry(200, 200, 200);
var cubeMat = new THREE.MeshStandardMaterial({
  roughness: 0.7,
  color: 0xffffff,
  bumpScale: 1,
  metalness: 0.2,
});
cubeMat.map = texture;
cubeMat.needsUpdate = true;
var boxMesh = new THREE.Mesh(boxGeometry, cubeMat);
boxMesh.position.set(0, 50, 0);
boxMesh.castShadow = true;
scene.add(boxMesh);

Canvas纹理

CanvasTexture

压缩的纹理

CompressedTexture

立方纹理(CubeTexture)

DataTexture

DataTexture2DArray

DataTexture3D

深度纹理(DepthTexture)

FramebufferTexture

纹理(Texture)

基类:Texture

API属性与方法

图形与几何体

  • BufferGeometry:是面片、线或点几何体的有效表述。包括顶点位置,面片索引、法相量、颜色值、UV 坐标和自定义缓存属性值。使用 BufferGeometry 可以有效减少向 GPU 传输上述数据所需的开销。
  • BufferAttribute:用于存储与BufferGeometry相关联的 attribute(例如顶点位置向量,面片索引,法向量,颜色值,UV坐标以及任何自定义 attribute )。 利用 BufferAttribute,可以更高效的向GPU传递数据。

基类

BufferGeometry

var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array( [
  -1.0, -1.0,  1.0,
  1.0, -1.0,  1.0,
  1.0,  1.0,  1.0,

  1.0,  1.0,  1.0,
  -1.0,  1.0,  1.0,
  -1.0, -1.0,  1.0
] );
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
geometry.applyMatrix4( new THREE.Matrix4().makeTranslation( 0, 0.5, 0 ) );  // 修改图形的基础坐标
geometry.scale(200, 1000, 500);

立方缓冲几何体

可以绘制正方体、长方体等。

new THREE.BoxGeometry(
  width : Float,              // X轴上面的宽度,默认值为1。
  height : Float,             // Y轴上面的高度,默认值为1。
  depth : Float,              // Z轴上面的深度,默认值为1。
  widthSegments : Integer,    // (可选)宽度的分段数,默认值是1。
  heightSegments : Integer,   // (可选)宽度的分段数,默认值是1。
  depthSegments : Integer     // (可选)宽度的分段数,默认值是1。
);


// 示例一
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
cube.position.set(0, 100, 0);
scene.add( cube );


// 示例二
var mesh = new THREE.Mesh( 
  new THREE.BoxGeometry( 100, 100, 100 ),  // new THREE.BoxGeometry( 100, 100, 300 );  // 长方体
  new THREE.MeshNormalMaterial() 
);
mesh.position.set(0, 100, 0);
scene.add( mesh ); 

圆形缓冲几何体

圆形•散形•多边形•正方形•长方形•三角形

new THREE.CircleGeometry(
  radius : Float,      // 圆形的半径,默认值为1
  segments : Integer,  // 分段(三角面)的数量,最小值为3,默认值为 32。
  thetaStart : Float,  // 第一个分段的起始角度,默认为0。
  thetaLength : Float  // 圆形扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆。
)

圆形

var geometry = new THREE.CircleGeometry(500, 1000);
var material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var circle = new THREE.Mesh(geometry, material);
scene.add(circle);

三角形

var geometry = new THREE.CircleGeometry(500, 3);
var material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var circle = new THREE.Mesh(geometry, material);
scene.add(circle);

n边形

设置 n 即可。

var geometry = new THREE.CircleGeometry(500, n);
var material = new THREE.MeshBasicMaterial({ color: 0xff0000 });
var circle = new THREE.Mesh(geometry, material);
scene.add(circle);

圆锥缓冲几何体

圆锥体

构造函数

  new THREE.ConeGeometry(
    radius : Float,             // 圆锥底部的半径,默认值为1。
    height : Float,             // 圆锥的高度,默认值为1。
    radialSegments : Integer,   // 圆锥侧面周围的分段数,默认为8。 
    heightSegments : Integer,   // 圆锥侧面沿着其高度的分段数,默认值为1。
    openEnded : Boolean,        // 一个Boolean值,指明该圆锥的底面是开放的还是封顶的。默认值为false,即其底面默认是封顶的。
    thetaStart : Float,         // 第一个分段的起始角度,默认为0。
    thetaLength : Float         // 圆锥底面圆扇区的中心角,通常被称为“θ”(西塔)。默认值是2*Pi,这使其成为一个完整的圆锥。
  )

圆底 | 多边底

var geometryMesh = new THREE.Mesh( 
  new THREE.ConeGeometry( 100, 200, 10 ), 
  new THREE.MeshNormalMaterial()  
);
geometryMesh.position.set(0, 100, 0);
scene.add( geometryMesh );

圆柱缓冲几何体

CylinderGeometry

构造函数


代码示例

  

十二面缓冲几何体

DodecahedronGeometry

构造函数


代码示例

  

边缘几何体

EdgesGeometry

构造函数


代码示例

  

挤压缓冲几何体

ExtrudeGeometry

构造函数


代码示例

  

二十面缓冲几何体

IcosahedronGeometry

构造函数


代码示例

  

车削缓冲几何体

LatheGeometry

构造函数


代码示例

  

八面缓冲几何体

OctahedronGeometry

构造函数


代码示例

  

平面缓冲几何体

PlaneGeometry

构造函数

new THREE.PlaneGeometry(
  width : Float, 
  height : Float, 
  widthSegments : Integer, 
  heightSegments : Integer
)







/**
 * 平面缓冲几何体(PlaneGeometry)
 * 注意:THREE.PlaneGeometry仅仅生成为一个平面,且有一面不可视,默认为与Y轴平行
 */
export function planGeometryCore(scene, options = {}) {
  var content = options.content || new THREE.Vector2(1000, 1000);
  var material = options.material || new THREE.MeshBasicMaterial({
    color: 0xff0000, depthWrite: false, side: THREE.DoubleSide,
    transparent: false
  });
  var position = options.position || new THREE.Vector3(0, 0, 0);
  var rotation = options.rotation || new THREE.Vector3(Math.PI / 2, 0, 0);

  let plane = new THREE.Mesh(
    new THREE.PlaneGeometry(...content),
    material,
  );
  plane.position.set(...position);
  plane.rotation.set(...rotation);
  plane.receiveShadow = true;
  scene.add(plane);

  return plane;
}


var plan = planGeometryCore(scene);

多面缓冲几何体

多面体在三维空间中具有一些平面的立体图形。这个类将一个顶点数组投射到一个球面上,之后将它们细分为所需的细节级别。

构造函数

new THREE.PolyhedronGeometry(
  vertices : Array,  // 一个顶点Array(数组):[1,1,1, -1,-1,-1, ... ]。
  indices : Array,   // 一个构成面的索引Array(数组), [0,1,2, 2,3,0, ... ]。
  radius : Float,    // 最终形状的半径。
  detail : Integer   // 将对这个几何体细分多少个级别。细节越多,形状就越平滑。
)

代码示例

  

圆环缓冲几何体

RingGeometry

构造函数


代码示例

  

形状缓冲几何体

ShapeGeometry

构造函数


代码示例

  

球缓冲几何体

构造函数

new THREE.SphereGeometry(
  radius : Float,            // 球体半径,默认为1。
  widthSegments : Integer,   // 水平分段数(沿着经线分段),最小值为3,默认值为8。
  heightSegments : Integer,  // 垂直分段数(沿着纬线分段),最小值为2,默认值为6。
  phiStart : Float,          // 指定水平(经线)起始角度,默认值为0。
  phiLength : Float,         // 指定水平(经线)扫描角度的大小,默认值为 Math.PI * 2。
  thetaStart : Float,        // 指定垂直(纬线)起始角度,默认值为0。
  thetaLength : Float        // 指定垂直(纬线)扫描角度大小,默认值为 Math.PI。
)








四面缓冲几何体

构造函数

new THREE.TetrahedronGeometry(
  radius : Float,   // 四面体的半径,默认值为1。
  detail : Integer  // 默认值为0。将这个值设为一个大于0的数将会为它增加一些顶点,使其不再是一个四面体。
)

代码示例

var geometryMesh = new THREE.Mesh( 
  new THREE.TetrahedronGeometry( 50, 0 ), 
  new THREE.MeshNormalMaterial()
);
geometryMesh.position.set(0, 100, 0);
scene.add( geometryMesh );  

圆环缓冲几何体

构造函数

new THREE.TorusGeometry(
  radius : Float,             // 圆环的半径,从圆环的中心到管道(横截面)的中心。默认值是1。
  tube : Float,               // 管道的半径,默认值为0.4。
  radialSegments : Integer,   // 圆环的分段数,默认值为8。
  tubularSegments : Integer,  // 管道的分段数,默认值为6。
  arc : Float                 // 圆环的圆心角(单位是弧度),默认值为Math.PI * 2。
)

代码示例

var torus = new THREE.Mesh( 
  new THREE.TorusGeometry( 50, 15, 16, 100 ), 
  new THREE.MeshNormalMaterial()
);
scene.add( torus );  

圆环缓冲扭结几何体

TorusKnotGeometry

构造函数


代码示例

  

管道缓冲几何体

TubeGeometry

构造函数


代码示例

  

网格几何体

作用:用作一个辅助物体,来对一个geometry以线框的形式进行查看。

构造函数

new THREE.WireframeGeometry( 
  geometry : BufferGeometry  // 几何体对象
);

代码示例

var line = new THREE.LineSegments( 
  new THREE.WireframeGeometry( 
    new THREE.SphereGeometry( 100, 100, 100 )
  )
);
line.material.depthTest = false;
line.material.opacity = 0.25;
line.material.transparent = true;
scene.add( line );  

经典图形示例

创建三角形

var geometry = new THREE.BufferGeometry();
var vertices = new Float32Array( [
  -10, 0, 0,
  10, 0, 0,
  10, 20, 0,
] );
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
var material = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var mesh = new THREE.Mesh( geometry, material );
scene.add(mesh);

凸包几何体(ConvexGeometry)

ConvexGeometry 可被用于为传入的一组点生成凸包。 该任务的平均时间复杂度被认为是O(nlog(n))

代码示例

import { ConvexGeometry } from '/public/source/threejs/jsm/examples/jsm/geometries/ConvexGeometry.js';

var points = [];
points.push(new THREE.Vector3( 0, 0, 0 ));
points.push(new THREE.Vector3( 50, 0, 0 ));
points.push(new THREE.Vector3( 50, 50, 0 ));
points.push(new THREE.Vector3( 0, 50, 0 ));
points.push(new THREE.Vector3( 0, 50, 100 ));

var geometry = new ConvexGeometry( points );
var material = new THREE.MeshNormalMaterial();
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

基类:BufferGeometry

API属性与方法

动画Animation

更新场景与更新相机视野范围目标,利用三轴坐标的变换来实现。

requestAnimationFrame(function animate() {
  .rotation.x += 0.3;
  renderer.render( scene, camera );
  requestAnimationFrame(loop);
});

动画示例

淡入淡出、缩放、位移、旋转等动画

代替requestAnimationFrame

  renderer.setAnimationLoop( () => {
    renderer.render( scene, camera );
  } );

GSAP

相机动画 | 转场动画

import { TWEEN } from '/Animate/tweenJS/dist/tween.module.min.js';


var startState = camera.position || { x: 0, y: 0, z: 1000 };
var endState = { x: startState.x, y: startState.y, z: ~startState.z };
var duration = 5000;
var delay = 2000;
var tween = new TWEEN.Tween(startState)
  .to(endState, duration)
  .delay(delay)
  .easing(TWEEN.Easing.Quadratic.Out) 
  .onUpdate(() => {
    camera.position.z = startState.z;
  })
  .start();


renderer.setAnimationLoop(() => {
  TWEEN.update();
  renderer.render( scene, camera );
});

加载器

提供加载不同格式的3D模型方法,同时3D模型有数百种文件格式,每种都有不同的用途、各种功能和不同的复杂度。

代码示例

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';


var loader = new GLTFLoader();

// 同步导出
loader.load( 'path/to/model.glb', function ( gltf ) {

  scene.add( gltf.scene );

}, undefined, function ( error ) {

  console.error( error );

} );

操作模型

// 异步导出,异步与同步的区别:异步的输出结果直接是同步的 gltf.scene
var model = await loader.loadAsync( 'path/to/model.glb');
scene.add( model );

console.log("model: ", model);

// 位置
model.position.y = 200;

// 旋转
model.rotation.x = -Math.PI / 2;

// 缩放
model.scale.x = 2;
model.scale.y = 4;
model.scale.z = 2;

不同的3D模型接口

import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import {  } from '/public/source/threejs/jsm/loaders/.js';
import { OBJLoader } from '/public/source/threejs/jsm/loaders/OBJLoader.js';
import { PCDLoader } from '/public/source/threejs/jsm/loaders/PCDLoader.js';
import { PDBLoader } from '/public/source/threejs/jsm/loaders/PDBLoader.js';
import { PLYLoader } from '/public/source/threejs/jsm/loaders/PLYLoader.js';
import { PRWMLoader } from '/public/source/threejs/jsm/loaders/PRWMLoader.js';
import { PVRLoader } from '/public/source/threejs/jsm/loaders/PVRLoader.js';
import { RGBELoader } from '/public/source/threejs/jsm/loaders/RGBELoader.js';
import { STLLoader } from '/public/source/threejs/jsm/loaders/STLLoader.js';
import { SVGLoader } from '/public/source/threejs/jsm/loaders/SVGLoader.js';
import { TDSLoader } from '/public/source/threejs/jsm/loaders/TDSLoader.js';
import { TGALoader } from '/public/source/threejs/jsm/loaders/TGALoader.js';
import { TiltLoader } from '/public/source/threejs/jsm/loaders/TiltLoader.js';
import { TTFLoader } from '/public/source/threejs/jsm/loaders/TTFLoader.js';
import { VOXLoader } from '/public/source/threejs/jsm/loaders/VOXLoader.js';
import { VRMLLoader } from '/public/source/threejs/jsm/loaders/VRMLLoader.js';
import { VRMLoader } from '/public/source/threejs/jsm/loaders/VRMLoader.js';
import { VTKLoader } from '/public/source/threejs/jsm/loaders/VTKLoader.js';
import { XYZLoader } from '/public/source/threejs/jsm/loaders/XYZLoader.js';








// OBJLoader
var objLoader = new OBJLoader();
var object = await objLoader.loadAsync( 'models/monster.obj' );
scene.add( object );




// FBXLoader
var fbxLoader = new FBXLoader();
var object = await fbxLoader.loadAsync( 'models/fbx/stanford-bunny.fbx' );
scene.add( object );



// MTLLoader
var loader = new MTLLoader();
var materials = await loader.loadAsync( 'models/obj/male02/male02.mtl' );
var objLoader = new OBJLoader();
objLoader.setMaterials( materials );

多媒体资源

字体(Fonts)

  • threejs中需要引入中文字体,官网给出的字体文件都是英文的,想要显示出中文字体需要做转换。
# 在本地电脑找个小点的中文字体文件(C:\Windows\Fonts),我用的是华文行楷

# 然后用字体提取工具提取出要显示的文字
> 在线转换:https://www.fontke.com/tool/subfont/

# 将上一步导出的.ttf文件转成json格式
> 转换工具:http://gero3.github.io/facetype.js/ 

# 转换生成出json文件用FontLoader导入
import { FontLoader } from '/public/source/threejs/jsm/loaders/FontLoader.js';
import { TextGeometry } from '/public/source/threejs/jsm/geometries/TextGeometry.js';


import * as THREE from "three";


var FONTS = {
  'helvetiker-regular': '/public/source/threejs/fonts/helvetiker_regular.typeface.json',
  'helvetiker-bold': '/public/source/threejs/fonts/helvetiker_bold.typeface.json',
  'optimer-regular': '/public/source/threejs/fonts/optimer_regular.typeface.json',
  'optimer-bold': '/public/source/threejs/fonts/optimer_bold.typeface.json',
  'gentilis-regular': '/public/source/threejs/fonts/gentilis_regular.typeface.json',
  'gentilis-bold': '/public/source/threejs/fonts/gentilis_bold.typeface.json',
  'droid-sans-regular': '/public/source/threejs/fonts/droid/droid_sans_regular.typeface.json',
  'droid-sans-bold': '/public/source/threejs/fonts/droid/droid_sans_bold.typeface.json',
  'droid-serif-regular': '/public/source/threejs/fonts/droid/droid_serif_regular.typeface.json',
  'droid-serif-bold': '/public/source/threejs/fonts/droid/droid_serif_bold.typeface.json',

  // 中文-简体
  'MI_LanTing_Regular': '/public/source/threejs/fonts/china/MI LANTING_Regular.json',
}

图片(Image)

音频(Audio)

export function AudioStructure() {
  var listener = new THREE.AudioListener();
  camera.add( listener );
  
  // create a global audio source
  var sound = new THREE.Audio( listener );
  
  // load a sound and set it as the Audio object's buffer
  var audioLoader = new THREE.AudioLoader();
  audioLoader.load( '/Reference/audio/module.mp3', function( buffer ) {
    sound.setBuffer( buffer );
    sound.setLoop( true );
    sound.setVolume( 0.5 );
  });

  document.addEventListener('click', function() {
    if (sound.isPlaying) {
      sound.pause();
      return;
    }

    sound.play();
  }, false);
} 

视频(Video)

视频纹理

  • 案例:https://github.com/mrdoob/three.js/blob/master/examples/webgl_materials_video.html
  • https://threejs.org/examples/#webgl_materials_video

代码示例

import { ReaderVideoData } from '/public/module/video.js';
import { fetchFile } from '/public/file/file.js';
import { GeometryStructure } from '/visual/ThreeJS/Example/Geometries/index.js';


export async  function VideoStructure() {
  var path = await fetchFile('https://test.ysunlight.com/Reference/video/Format/video.mp4');
  var video = await ReaderVideoData(path, 'video/mp4');
  video.loop = true;
  var texture = new THREE.VideoTexture( video );

  GeometryStructure({
    material: new THREE.MeshLambertMaterial( { color: 0xffffff, map: texture } )
  });

  document.addEventListener('click', function() {
    if (!video.paused) {
      video.pause();
      return;
    }
    video.play();
  }, false);
}

效果合成器(EffectComposer)

滤镜与效果

  • 效果合成器,即类似于Gulp工作流程一样,通过EffectComposer声明一个渲染管道,往管道中添加不同的效果,最后合成绘制到屏幕上。
  • 这些效果包括:增强光效、滤镜等各种变换。

代码示例

var composer = new EffectComposer( renderer );       // 创建合成器
composer.setPixelRatio( window.devicePixelRatio );
composer.setSize( window.innerWidth, window.innerHeight );

/**
 * 配置后期处理过程链
 * 
 */
// 将渲染好的场景作为输入来提供给下一个后期处理步骤
var renderPass = new RenderPass( scene, camera );    // 渲染通道
composer.addPass(renderPass);


// 描边高亮
var outlinePass = new THREE.OutlinePass(new THREE.Vector2(widnow.innerWidth, window.innerHeight), scene, camera);
composer.addPass(outlinePass);

// 故障效果
glitchPass = new THREE.GlitchPass();
glitchPass.renderToScreen = true;
composer.addPass( glitchPass );

// 
var lutPass = new LUTPass();
composer.addPass( lutPass );





/**
 * 自定义后期处理着色器
 * 
 */
var copyPass = new ShaderPass( CopyShader );
copyPass.renderToScreen = true;                        // 输出当前内容
composer.addPass(copyPass);                            // 输出到屏幕





renderer.setAnimationLoop(() => {
  orbitControls.update();
  renderer.render( scene, camera );
});

代码示例

import { EffectComposer } from '/public/source/threejs/jsm/postprocessing/EffectComposer.js';                    // 效果合成器

import { AdaptiveToneMappingPass } from '/public/source/threejs/jsm/postprocessing/AdaptiveToneMappingPass.js';  // 
import { AfterimagePass } from '/public/source/threejs/jsm/postprocessing/AfterimagePass.js';                    // 
import { BloomPass } from '/public/source/threejs/jsm/postprocessing/BloomPass.js';                              // 增强场景中的亮度
import { BokehPass } from '/public/source/threejs/jsm/postprocessing/BokehPass.js';                              // 
import { ClearPass } from '/public/source/threejs/jsm/postprocessing/ClearPass.js';                              // 
import { CubeTexturePass } from '/public/source/threejs/jsm/postprocessing/CubeTexturePass.js';                  // 
import { DotScreenPass } from '/public/source/threejs/jsm/postprocessing/DotScreenPass.js';                      // 将原始图片输出为灰度点集
import { FilmPass } from '/public/source/threejs/jsm/postprocessing/FilmPass.js';                                // 使用扫描线和失真来模拟电视屏幕效果
import { GlitchPass } from '/public/source/threejs/jsm/postprocessing/GlitchPass.js';                            // 随机在屏幕上显示电脉冲
import { HalftonePass } from '/public/source/threejs/jsm/postprocessing/HalftonePass.js';                        // 
import { LUTPass } from '/public/source/threejs/jsm/postprocessing/LUTPass.js';                                  // 
import { MaskPass } from '/public/source/threejs/jsm/postprocessing/MaskPass.js';                                // 在当前图片上添加掩码,后续的通道只会影响掩码区域
import { OutlinePass } from '/public/source/threejs/jsm/postprocessing/OutlinePass.js';                          // 模型边框高亮
import { Pass } from '/public/source/threejs/jsm/postprocessing/Pass.js';                                        // 
import { RenderPass } from '/public/source/threejs/jsm/postprocessing/RenderPass.js';                            // 渲染通道,该通道在指定的场景和相机的基础上渲染出一个新场景
import { SAOPass } from '/public/source/threejs/jsm/postprocessing/SAOPass.js';                                  // 
import { SavePass } from '/public/source/threejs/jsm/postprocessing/SavePass.js';                                // 
import { ShaderPass } from '/public/source/threejs/jsm/postprocessing/ShaderPass.js';                            // 接受自定义的着色器作为参数,以生成一个高级、自定义的后期处理通道, 产生各种模糊效果和高级滤镜
import { SMAAPass } from '/public/source/threejs/jsm/postprocessing/SMAAPass.js';                                // 
import { SSAARenderPass } from '/public/source/threejs/jsm/postprocessing/SSAARenderPass.js';                    // 
import { SSAOPass } from '/public/source/threejs/jsm/postprocessing/SSAOPass.js';                                // 
import { SSRPass } from '/public/source/threejs/jsm/postprocessing/SSRPass.js';                                  // 
import { SSRrPass } from '/public/source/threejs/jsm/postprocessing/SSRrPass.js';                                // 
import { TAARenderPass } from '/public/source/threejs/jsm/postprocessing/TAARenderPass.js';                      // 
import { TexturePass } from '/public/source/threejs/jsm/postprocessing/TexturePass.js';                          // 保存当前通道的输出,作为后续使用
import { UnrealBloomPass } from '/public/source/threejs/jsm/postprocessing/UnrealBloomPass.js';                  // 

import { ACESFilmicToneMappingShader } from '/public/source/threejs/jsm/shaders/ACESFilmicToneMappingShader.js'; // 
import { AfterimageShader } from '/public/source/threejs/jsm/shaders/AfterimageShader.js';                       // 
import { BasicShader } from '/public/source/threejs/jsm/shaders/BasicShader.js';                                 // 
import { BleachBypassShader } from '/public/source/threejs/jsm/shaders/BleachBypassShader.js';                   // 创造一种镀银的效果
import { BlendShader } from '/public/source/threejs/jsm/shaders/BlendShader.js';
import { BokehShader } from '/public/source/threejs/jsm/shaders/BokehShader.js';
import { BokehShader as BokehShader2, BokehDepthShader } from '/public/source/threejs/jsm/shaders/BokehShader2.js';
import { BrightnessContrastShader } from '/public/source/threejs/jsm/shaders/BrightnessContrastShader.js';       // 改变亮度和对比度
import { ColorCorrectionShader } from '/public/source/threejs/jsm/shaders/ColorCorrectionShader.js';             // 调整颜色的分布
import { ColorifyShader } from '/public/source/threejs/jsm/shaders/ColorifyShader.js';                           // 将某种颜色覆盖到整个屏幕
import { ConvolutionShader } from '/public/source/threejs/jsm/shaders/ConvolutionShader.js';
import { CopyShader } from '/public/source/threejs/jsm/shaders/CopyShader.js';
import { DepthLimitedBlurShader } from '/public/source/threejs/jsm/shaders/DepthLimitedBlurShader.js';
import { DigitalGlitch } from '/public/source/threejs/jsm/shaders/DigitalGlitch.js';
import { DOFMipMapShader } from '/public/source/threejs/jsm/shaders/DOFMipMapShader.js';
import { DotScreenShader } from '/public/source/threejs/jsm/shaders/DotScreenShader.js';
import { FilmShader } from '/public/source/threejs/jsm/shaders/FilmShader.js';
import { FocusShader } from '/public/source/threejs/jsm/shaders/FocusShader.js';                               // 创建中间比较尖锐,周围比较模糊的效果。
import { FreiChenShader } from '/public/source/threejs/jsm/shaders/FreiChenShader.js';
import { FXAAShader } from '/public/source/threejs/jsm/shaders/FXAAShader.js';                                 // 添加抗锯齿的效果
import { GammaCorrectionShader } from '/public/source/threejs/jsm/shaders/GammaCorrectionShader.js';
import { GodRaysDepthMaskShader, GodRaysGenerateShader, GodRaysCombineShader, GodRaysFakeSunShader } from '/public/source/threejs/jsm/shaders/GodRaysShader.js';
import { HalftoneShader } from '/public/source/threejs/jsm/shaders/HalftoneShader.js';
import { HorizontalBlurShader } from '/public/source/threejs/jsm/shaders/HorizontalBlurShader.js';             // 可以向水平和垂直方向创建模糊效果
import { VerticalBlurShader } from '/public/source/threejs/jsm/shaders/VerticalBlurShader.js';                 // 可以向水平和垂直方向创建模糊效果
import { HorizontalTiltShiftShader } from '/public/source/threejs/jsm/shaders/HorizontalTiltShiftShader.js';   // 可以在水平和垂直方向创建倾斜平移的效果  
import { VerticalTiltShiftShader } from '/public/source/threejs/jsm/shaders/VerticalTiltShiftShader.js';       // 可以在水平和垂直方向创建倾斜平移的效果
import { HueSaturationShader } from '/public/source/threejs/jsm/shaders/HueSaturationShader.js';               // 改变颜色的色调和饱和度
import { KaleidoShader } from '/public/source/threejs/jsm/shaders/KaleidoShader.js';                           // 类似于万花筒的效果
import { LuminosityHighPassShader } from '/public/source/threejs/jsm/shaders/LuminosityHighPassShader.js';
import { LuminosityShader } from '/public/source/threejs/jsm/shaders/LuminosityShader.js';                     // 提高亮度
import { MirrorShader } from '/public/source/threejs/jsm/shaders/MirrorShader.js';                             // 创建镜面效果
import { MMDToonShader } from '/public/source/threejs/jsm/shaders/MMDToonShader.js';
import { NormalMapShader } from '/public/source/threejs/jsm/shaders/NormalMapShader.js';
import { PixelShader } from '/public/source/threejs/jsm/shaders/PixelShader.js';
import { RGBShiftShader } from '/public/source/threejs/jsm/shaders/RGBShiftShader.js';                        // 将红绿蓝三种颜色分开
import { SAOShader } from '/public/source/threejs/jsm/shaders/SAOShader.js';
import { SepiaShader } from '/public/source/threejs/jsm/shaders/SepiaShader.js';                              // 创建类似于乌贼墨的效果
import { SMAAEdgesShader, SMAAWeightsShader, SMAABlendShader } from '/public/source/threejs/jsm/shaders/SMAAShader.js';
import { SobelOperatorShader } from '/public/source/threejs/jsm/shaders/SobelOperatorShader.js';
import { SSAOShader } from '/public/source/threejs/jsm/shaders/SSAOShader.js';
import { SSRrShader } from '/public/source/threejs/jsm/shaders/SSRrShader.js';
import { SSRShader } from '/public/source/threejs/jsm/shaders/SSRShader.js';
import { SubsurfaceScatteringShader } from '/public/source/threejs/jsm/shaders/SubsurfaceScatteringShader.js'; // 模拟类似老电影里面的两条彩色效果
import { TechnicolorShader } from '/public/source/threejs/jsm/shaders/TechnicolorShader.js';
import { ToneMapShader } from '/public/source/threejs/jsm/shaders/ToneMapShader.js';
import { ToonShader1, ToonShader2, ToonShaderHatching, ToonShaderDotted } from '/public/source/threejs/jsm/shaders/ToonShader.js';
import { TriangleBlurShader } from '/public/source/threejs/jsm/shaders/TriangleBlurShader.js';                // 基于三角形的方法创造一种模糊效果
import { UnpackDepthRGBAShader } from '/public/source/threejs/jsm/shaders/UnpackDepthRGBAShader.js';
import { VignetteShader } from '/public/source/threejs/jsm/shaders/VignetteShader.js';                        // 添加晕映效果
import { VolumeRenderShader1 } from '/public/source/threejs/jsm/shaders/VolumeShader.js';
import { WaterRefractionShader } from '/public/source/threejs/jsm/shaders/WaterRefractionShader.js';

着色器 Shader

碰撞检测

  • https://www.jb51.net/article/182528.htm
  • http://webmaestro.fr/collisions-detection-three-js-raycasting/

Raycaster事件

光线投射Raycaster,对于网格,面(faces)必须朝向射线原点,这样才能被检测到;通过背面的射线的交叉点将不被检测到。 为了光线投射一个对象的正反两面,你得设置 material 的 side 属性为 THREE.DoubleSide

  • https://threejs.org/docs/#api/zh/core/Raycaster

  • mousemove事件

  • click事件

var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();

function onMouseMove( event ) {  // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
  mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
  mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}


window.addEventListener( 'mousemove', onMouseMove, false );

document.addEventListener( 'pointermove', onPointerMove );
document.addEventListener( 'pointerdown', onPointerDown );
document.addEventListener( 'keydown', onDocumentKeyDown );
document.addEventListener( 'keyup', onDocumentKeyUp );


let INTERSECTED = null;

function updateRender() {
  // 通过摄像机和鼠标位置更新射线
  raycaster.setFromCamera( mouse, Camera );

  // 计算物体和射线的焦点 | 检测所有在射线与物体之间,包含或不包含后辈的相交局部。返回后果时,相交局部将按间隔进行排序,最近的位于第一个。
  var intersects = raycaster.intersectObjects( Scene.children );
  var isValid = INTERSECTED && INTERSECTED.material && INTERSECTED.material.emissive;

  if ( intersects.length > 0 ) {
    // intersects[ 0 ]对象的属性与方法

    // scene.remove( intersect[0].object );  // 移除物体


    // 当前焦点目标并非上一个目标
    if ( intersects[ 0 ].object !== INTERSECTED ) {
      if ( isValid ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
      INTERSECTED = intersects[ 0 ].object;

      if (INTERSECTED.material.emissive) {
        INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
        INTERSECTED.material.emissive.setHex( 0xff0000 );	
      }				
    }
  } else {
    if ( isValid ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
    INTERSECTED = null;
  }
}  


function animate() {
  requestAnimationFrame( animate );

  orbitControls.update();
  updateRender();

  renderer.render( scene, camera );
}
animate();

性能优化方案

基础示例

// 计算这个mesh在gpu中所占内存
BufferGeometryUtils.estimateBytesUsed( mesh.geometry ) + " bytes"

// 使用DefaultUVEncoding降低内存数
GeometryCompressionUtils.compressUvs( mesh );

// 使用QuantizePosEncoding降低内存数
GeometryCompressionUtils.compressPositions( mesh );

// 使用NormEncodingMethods降低内存数
// [ "None", "DEFAULT", "OCT1Byte", "OCT2Byte", "ANGLES" ]
GeometryCompressionUtils.compressNormals( mesh, 'None' );

辅助工具库

three.interaction.js

给物体添加事件机制

GitHub:https://github.com/jasonChen1982/three.interaction.js 官网:https://jasonchen1982.github.io/three.interaction.js/docs/ 案例:https://jasonchen1982.github.io/three.interaction.js/examples/interaction/

stats

性能监控

ammo.js

  • GitHub:https://github.com/kripken/ammo.js/

案例:过山车

import {
  RollerCoasterGeometry,
  RollerCoasterShadowGeometry,
  RollerCoasterLiftersGeometry,
  TreesGeometry,
  SkyGeometry
} from '/public/source/threejs/jsm/examples/jsm/misc/RollerCoaster.js';

let mesh, material, geometry;
var PI2 = Math.PI * 2;

var curve = ( function () {
  var vector = new THREE.Vector3();
  var vector2 = new THREE.Vector3();

  return {
    getPointAt: function ( t ) {
      t = t * PI2;
      var x = Math.sin( t * 3 ) * Math.cos( t * 4 ) * 50;
      var y = Math.sin( t * 10 ) * 2 + Math.cos( t * 17 ) * 2 + 5;
      var z = Math.sin( t ) * Math.sin( t * 4 ) * 50;
      return vector.set( x, y, z ).multiplyScalar( 2 );
    },

    getTangentAt: function ( t ) {
      var delta = 0.0001;
      var t1 = Math.max( 0, t - delta );
      var t2 = Math.min( 1, t + delta );

      return vector2.copy( this.getPointAt( t2 ) )
        .sub( this.getPointAt( t1 ) ).normalize();
    }
  };
} )();

geometry = new RollerCoasterGeometry( curve, 1500 );
material = new THREE.MeshPhongMaterial( {
  vertexColors: true
} );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
Last Updated:
Contributors: 709992523