最佳ThreeJS实践 · 实现赛博朋克风格的三维图像气泡效果

在现代计算机图形学和游戏开发中,创建引人入胜且逼真的三维场景是至关重要的。赛博朋克风格,以其鲜艳的色彩、充满未来感的细节以及复杂的光影效果,成为了许多开发者和艺术家的热门选择。在本文中,我们将深入探讨如何利用 Three.js 创建一个高质量的赛博朋克风格三维场景,特别是如何优化纹理的清晰度和材质设置,以实现最佳的视觉效果。

Three.js 简介

Three.js 是一个开源的 JavaScript 库,用于在网页上创建和显示三维图形。它提供了强大的工具和功能,使得开发者可以轻松地创建复杂的三维场景。Three.js 支持多种材质和纹理配置,使得开发者能够对每个细节进行精确控制。

整体思路

赛博朋克风格

赛博朋克风格源于对未来科技与破碎社会之间关系的描绘。这种风格通常包括霓虹灯光、复杂的建筑结构和阴暗的背景。为了捕捉这种独特的美学,我们需要精心设计场景的每一个细节,从背景色彩到纹理质量,每一部分都需要与赛博朋克风格的主题相辅相成。

创建赛博朋克风格场景

在创建赛博朋克风格场景时,我们首先需要搭建基本的 Three.js 环境。

初始化项目

🌶️ 创建一个文件夹叫ThreeJsDemo,然后在当前的这个文件夹下面执行如下命令:

javascript 复制代码
npm init -y

🌶️ 安装Three.js

javascript 复制代码
npm install three

将Vite安装成开发时依赖,使用vite启动开发服务

javascript 复制代码
npm i vite -D

纹理优化

为了在赛博朋克风格场景中实现最佳的视觉效果,我们需要关注纹理的清晰度。以下几个参数对纹理的显示效果有显著影响:

过滤方式: minFilter 和 magFilter 控制纹理的缩小和放大效果。我们选择了 THREE.LinearMipMapLinearFilter 和 THREE.LinearFilter,这能在不同的缩放比例下提供更平滑的效果,避免了锯齿状的边缘。

各向异性过滤: anisotropy 设置了纹理的各向异性过滤等级。通过 renderer.capabilities.getMaxAnisotropy() 自动获取最大等级,确保在远处或倾斜视角下纹理仍然清晰。

编码方式: encoding 参数设置了纹理的颜色编码方式。THREE.sRGBEncoding 用于处理 gamma 校正,使得纹理颜色更加准确和生动。

调整图片大小和气泡感效果

为了增强赛博朋克风格的视觉效果,我们在代码中实现了动态调整图片大小和发光效果。adjustImageSizes 函数用于根据摄像机与图片之间的距离调整图片的缩放和发光强度。最近的图片会被放大并增强发光效果,而较远的图片则会缩小并减弱发光效果,这种效果能够增强场景的深度感和立体感。

构建一个赛博朋克风格的三维场景

使用 Three.js 构建一个赛博朋克风格的三维场景,并且在该场景中创建一种"气泡感"的动态效果。该效果能够根据相机的位置动态调整图片的大小和发光强度,给用户带来沉浸式的视觉体验。具体实现过程如下:

一、场景搭建

首先,我们利用 Three.js 构建基本的三维场景。在这个场景中,添加了相机、渲染器、光照以及一个赛博朋克风格的背景渐变。该背景渐变使用 CanvasTexture 创建,颜色范围从深紫色渐变到亮粉色,营造出一种赛博朋克特有的霓虹灯氛围。

javascript 复制代码
// 创建赛博朋克风格的背景渐变
const gradientTexture = new THREE.CanvasTexture(createGradientCanvas()); // 创建渐变纹理
scene.background = gradientTexture; // 将背景设置为渐变纹理

函数 createGradientCanvas 用于创建渐变背景:

javascript 复制代码
// 创建渐变背景
function createGradientCanvas() {
  const canvas = document.createElement('canvas'); // 创建一个 canvas 元素
  canvas.width = 512; // 设置 canvas 的宽度
  canvas.height = 512; // 设置 canvas 的高度
  const context = canvas.getContext('2d'); // 获取 2D 上下文
  const gradient = context.createLinearGradient(0, 0, 512, 512); // 创建线性渐变
  gradient.addColorStop(0, '#2c003e'); // 渐变开始颜色(深紫色)
  gradient.addColorStop(1, '#ff007d'); // 渐变结束颜色(亮粉色)
  // gradient.addColorStop(0, '#FFFFFF'); // 渐变开始颜色(深紫色)
  // gradient.addColorStop(1, '#EEE000'); // 渐变结束颜色(亮粉色)
  context.fillStyle = gradient; // 设置填充样式为渐变
  context.fillRect(0, 0, 512, 512); // 填充整个 canvas
  return canvas; // 返回创建的 canvas
}

二、添加光照

为了增强赛博朋克风格的灯光效果,场景中加入了环境光和方向光。环境光用来提供基础的整体照明,方向光则用来模拟从某个方向照射的光源,增加物体的立体感。

javascript 复制代码
// 添加环境光
// const ambientLight = new THREE.AmbientLight(0x444444); // 创建环境光,颜色较暗
const ambientLight = new THREE.AmbientLight(0xffffff); // 创建环境光,颜色较暗
scene.add(ambientLight); // 将环境光添加到场景中

// 添加方向光
// const directionalLight = new THREE.DirectionalLight(0x00ffff, 1); // 创建方向光,颜色为青色
const directionalLight = new THREE.DirectionalLight(0xffffff, 1); // 创建方向光,颜色为青色
directionalLight.position.set(10, 10, 10).normalize(); // 设置光源的位置并归一化
scene.add(directionalLight); // 将方向光添加到场景中

三、加载图片并设置材质

场景的核心元素是均匀分布在球体表面的图片。这些图片作为网格(Mesh)添加到场景中,并且使用 CircleGeometry 创建圆形几何体来显示图片。

javascript 复制代码
  const geometry = new THREE.CircleGeometry(circleRadius, 32); // 创建圆形几何体
  const texture = textureLoader.load(url); // 加载纹理

  // 设置纹理的过滤方式
  texture.minFilter = THREE.LinearMipMapLinearFilter;
  texture.magFilter = THREE.LinearFilter;

  // 设置各向异性过滤
  texture.anisotropy = renderer.capabilities.getMaxAnisotropy();

  // 设置纹理的颜色编码方式
  texture.encoding = THREE.sRGBEncoding;

  const material = new THREE.MeshStandardMaterial({
    map: texture,
    emissive: new THREE.Color(0x000000), // 默认不发光
    emissiveIntensity: 0, // 默认发光强度为0
    side: THREE.DoubleSide, // 双面渲染
    roughness: 0.2, // 低粗糙度
    metalness: 0.5, // 适度金属光泽
  });

为了保持视觉效果的一致性,配置了纹理的过滤方式(minFiltermagFilter),并设置了最大各向异性过滤(anisotropy),确保在不同视角和距离下纹理显示清晰。

四、相机控制与自适应窗口大小

使用 OrbitControls 实现相机的平滑控制,用户可以自由旋转、缩放和移动场景。同时,通过监听窗口的大小变化,自适应调整相机和渲染器的尺寸,确保场景始终保持最佳比例。

javascript 复制代码
// 创建 OrbitControls 实例,用于相机的平滑控制
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true; // 启用平滑阻尼效果
controls.dampingFactor = 0.25; // 设置阻尼因子
controls.enableZoom = true; // 启用缩放
controls.enableRotate = true; // 启用旋转


// 自适应窗口大小
window.addEventListener('resize', () => {
  const width = window.innerWidth;
  const height = window.innerHeight;
  renderer.setSize(width, height); // 调整渲染器大小
  camera.aspect = width / height; // 调整相机的宽高比
  camera.updateProjectionMatrix(); // 更新相机投影矩阵
});

五、动态调整图片大小和发光效果

场景的核心是"气泡感"效果,它通过根据相机与图片之间的距离动态调整图片的大小和发光强度。最近的图片会逐渐变大并增强发光效果,而较远的图片会缩小,营造出一种动态的深度感。

实现该效果的关键是相机视锥体(Frustum)的使用。首先计算相机的视锥体,然后检查每个图片网格是否在视锥体内,最后根据距离调整图片的缩放和发光效果:

javascript 复制代码
// 调整图片大小和气泡感效果
function adjustImageSizes() {
  let closestCircle = null; // 存储距离相机最近的图片
  let minDistance = Infinity; // 初始设置为无穷大

  // 找到距离相机最近且在视锥体内的图片
  circles.forEach((circle) => {
    const distance = camera.position.distanceTo(circle.position); // 计算相机到图片的距离

    // 检查图片是否在相机视锥体内
    const circleBoundingBox = new THREE.Box3().setFromObject(circle); // 获取图片的包围盒
    if (frustum.intersectsBox(circleBoundingBox)) { // 检查包围盒是否与视锥体相交
      if (distance < minDistance) { // 如果距离更近,更新最近的图片
        minDistance = distance;
        closestCircle = circle;
      }
    }
  });

  // 为每个图片设置缩放效果
  circles.forEach((circle) => {
    if (circle === closestCircle) {
      // 最近的图片放大到 1.8 倍,使用缓动效果
      circle.scale.lerp(new THREE.Vector3(1.8, 1.8, 1.8), 0.1);
      // circle.material.emissive = new THREE.Color('#ff007d'); // 设置发光颜色
      circle.material.emissiveIntensity = 0.5; // 设置发光强度
    } else {
      // 其他图片缩小到 0.5 倍,模拟气泡挤压感
      circle.scale.lerp(new THREE.Vector3(0.5, 0.5, 0.5), 0.1);
      circle.material.emissive = new THREE.Color('#000000'); // 无发光效果
      circle.material.emissiveIntensity = 0; // 发光强度为0
    }
  });
}

该函数通过缓动(lerp)效果平滑地调整图片大小,保证视觉效果的连贯性。最靠近相机的图片会被放大到 1.8 倍,并且增强其发光效果,而其他图片则逐渐缩小,模拟出类似气泡挤压的效果。

总结

通过利用 Three.js 的丰富功能,我们能够轻松实现一个具有赛博朋克风格的动态三维场景。本文重点介绍了如何通过材质、纹理和光照的优化,来提升场景的视觉效果。同时,基于相机位置的动态调整图片大小和发光效果,为场景添加了更具沉浸感的气泡效果。

无论是用于游戏开发、虚拟现实项目,还是网页三维可视化,Three.js 都是一个强大且灵活的工具。

相关推荐
学不会•1 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
EasyNTS2 小时前
H.264/H.265播放器EasyPlayer.js视频流媒体播放器关于websocket1006的异常断连
javascript·h.265·h.264
活宝小娜3 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点3 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow3 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o3 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
开心工作室_kaic4 小时前
ssm161基于web的资源共享平台的共享与开发+jsp(论文+源码)_kaic
java·开发语言·前端
刚刚好ā4 小时前
js作用域超全介绍--全局作用域、局部作用、块级作用域
前端·javascript·vue.js·vue
沉默璇年6 小时前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder6 小时前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript