当前位置: 首页 > article >正文

【Three.js】实现护罩(防御罩、金钟罩、护盾)效果

在这里插入图片描述


前言:

在这篇博客中,我们将使用 Three.js 从零开始生成一个护罩效果。护罩将使用自定义的 Shader 材质,带有动画效果,最终呈现一个视觉上酷炫的护罩。此篇文章的重点将放在如何生成一个3D护罩,其它功能将在之后的博客中讨论,后续会有很多酷炫的效果~


实现技术简介:

要实现这个护罩,我们需要掌握以下技术:

  • Three.js: 一个强大的 3D 图形库,简化了 WebGL 的使用。
  • Shader: 我们将用到 GLSL(OpenGL 着色语言)编写自定义着色器,为护罩增添独特的效果,比如渐变、透明度和动态变化。
  • WebGLRenderer: Three.js 的核心渲染器,用于将 3D 场景渲染到网页上。

第一步:设置基础的场景

任何使用three.js的项目都会设置基础的场景,且大同小异

在开始生成护罩之前,我们需要一个 Three.js 基础场景。这个场景包括相机、渲染器和一些光源,以便能够观察到护罩的效果。

代码:
// 创建一个场景
const scene = new THREE.Scene();

// 设置透视相机
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 7, 10);

// 创建 WebGL 渲染器并设置其尺寸
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

// 添加环境光和方向光到场景中
const ambientLight = new THREE.AmbientLight(0xcccccc, 0.4); // 柔和的环境光
scene.add(ambientLight);

const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); // 模拟太阳光的方向光
directionalLight.position.set(0, 5, 5);
scene.add(directionalLight);
解释:
  • 场景(Scene):Three.js 中所有对象的容器,所有物体都会添加到场景中。
  • 相机(Camera):用于查看场景。这里我们使用透视相机,能模拟人眼的视角。
  • 渲染器(Renderer):负责将 3D 场景绘制到网页上,我们选择了 WebGLRenderer 并将其尺寸设置为浏览器窗口大小。
  • 光源(Light):环境光均匀地照亮场景,方向光模拟来自一个方向的强光,给物体添加阴影效果。

第二步:创建自定义护罩材质

为了让护罩看起来与众不同,我们需要自定义材质。我们将使用 GLSL 编写顶点着色器和片段着色器,来控制护罩的颜色、透明度和动画效果。

代码:
// 创建护罩的自定义 Shader 材质
class ShieldMaterial extends THREE.ShaderMaterial {
  constructor() {
    super({
      uniforms: {
        time: { value: 0 }, // 动态变化的时间
        color: { value: new THREE.Color(0x00ffff) } // 护罩的颜色
      },
      vertexShader: `
        varying vec3 vNormal;
        void main() {
          vNormal = normalize(normalMatrix * normal);
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        uniform float time;
        uniform vec3 color;
        varying vec3 vNormal;
        void main() {
          float intensity = pow(0.5 - dot(vNormal, vec3(0.0, 0.0, 1.0)), 2.0);
          gl_FragColor = vec4(color * intensity, 1.0);
        }
      `,
      transparent: true, // 设置材质为透明
      blending: THREE.AdditiveBlending, // 使用叠加效果
    });
  }

  update(time) {
    this.uniforms.time.value = time;
  }
}
解释:
  • ShaderMaterial:我们继承了 THREE.ShaderMaterial 类来创建自定义的护罩材质。
  • 顶点着色器(Vertex Shader):用于处理每个顶点的位置。这里我们计算了每个顶点的法线信息。
  • 片段着色器(Fragment Shader):控制护罩的颜色和透明度。我们通过法线计算光照的强度,并将护罩设置为半透明效果。
  • uniforms:用于在 GPU 上保存变量。我们定义了 time 来让护罩产生动态效果,color 控制护罩的颜色。
  • blending:使用叠加混合模式,使护罩看起来有发光效果。

第三步:生成护罩几何体

接下来,我们需要生成一个 3D 几何体来承载我们的材质。我们使用 Three.js 提供的 SphereGeometry 来生成一个球体,代表护罩的形状。

代码:
// 创建护罩
const createShield = () => {
  // 创建球体几何体,表示护罩
  const geometry = new THREE.SphereGeometry(1.5, 64, 64); // 半径为1.5的球体,使用64段分割
  const material = new ShieldMaterial(); // 使用自定义的护罩材质

  // 创建网格并将几何体和材质绑定
  const shield = new THREE.Mesh(geometry, material);
  
  // 将护罩添加到场景中
  scene.add(shield);

  return shield;
};

const shield = createShield();
解释:
  • SphereGeometry:生成一个球体,半径为 1.5,几何体细分为 64 段,使球体看起来更加平滑。
  • Mesh:将几何体与材质结合生成一个网格,并将这个护罩网格添加到场景中。

第四步:渲染和动画

为了让护罩动画起来,我们可以通过不断更新时间 time 变量,使其动态变化。在动画循环中,更新 ShieldMaterial 的时间。

代码:
// 创建动画循环
const clock = new THREE.Clock(); // 用于追踪时间

const animate = () => {
  requestAnimationFrame(animate); // 不断调用 animate 函数
  const elapsedTime = clock.getElapsedTime(); // 获取流逝的时间

  // 更新护罩的时间,使其产生动态效果
  shield.material.update(elapsedTime);

  // 渲染场景
  renderer.render(scene, camera);
};

animate(); // 开始动画循环
解释:
  • Clock:Three.js 中的 Clock 对象用于获取经过的时间,使我们可以根据时间变化来创建动画效果。
  • requestAnimationFrame:浏览器内置的动画函数,确保动画以最佳帧率运行。
  • update:我们将流逝的时间传递给护罩材质的 time 参数,让护罩随时间变化而动态变动。

总结:

通过以上步骤,我们使用 Three.js 和自定义 Shader 成功生成了一个带有动画效果的护罩。你可以根据需求自定义护罩的颜色、动画速度和透明度。

在未来的博客中,我们将深入探讨如何扩展这个护罩效果,比如添加交互、生成多个护罩或是为护罩之间添加飞线特效。


ps:最近的项目使用three.js的地方很多,且拓展了很多功能,我会将一个个的功能点拆分出来。因为之前查找相关博客发现很多都是整个一起的,对于只想要其中一部分而言会增加很多不必要的时间成本,所以我会将功能点拆解出来。


http://www.kler.cn/a/302386.html

相关文章:

  • 3D编辑器教程:如何实现3D模型多材质定制效果?
  • SQL集合运算
  • 速盾:高防 CDN 和 CDN 的缓存机制都一样吗?
  • 【操作系统】守护进程
  • PostgreSQL分区表:基础语法与运维实践
  • java八股-jvm入门-程序计数器,堆,元空间,虚拟机栈,本地方法栈,类加载器,双亲委派,类加载执行过程
  • 【PGCCC】PostgreSQL重做日志内幕!如何掌握事务日志记录的“黑魔法”
  • 9月13日星期五今日早报简报微语报早读
  • ssm“健康早知道”微信小程序 LW PPT源码调试讲解
  • P1544 三倍经验 (记忆化搜索)
  • SpringBoot 整合 Guava Cache 实现本地缓存
  • 算法day23| 93.复原IP地址、78.子集、90.子集II
  • 数据库安全性控制
  • 深入MySQL的索引实践及优化
  • 【开源风云】从若依系列脚手架汲取编程之道(四)
  • 【自然语言处理】实验一:基于NLP工具的中文分词
  • yolov5实战全部流程
  • 【Hot100】LeetCode—64. 最小路径和
  • GaN挑战Si价格底线?英飞凌推出全球首个12英寸GaN晶圆技术
  • 高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!
  • golang面试
  • Windows Python 指令补全方法
  • 【C\C++】Eigen初体验(VS Code编译)
  • 正则表达式 - 运算符优先级
  • Vue3 父组件向子组件传值:异步数据处理的显示问题
  • 多维度智能体验:引领未来的RAG型知识图谱数字