LOGO OA教程 ERP教程 模切知识交流 PMS教程 CRM教程 开发文档 其他文档  
 
网站管理员

Three.js 全息影像效果(源码)

admin
2025年4月19日 21:45 本文热度 58

使用 Three.js 的 WebGL 小实验。Three.js 全息影像效果,动画化模型进行过渡效果。

实现代码

HTML:

<div id="app"></div>
CSS:
*,*::after,*::before {  margin0;  padding0;}
body {  box-sizing: border-box;}
#app {  position: fixed;  top0;  left0;  z-index: -1;  width100%;  height100vh;}

JAVASCRIPT:

import * as THREE from "https://esm.sh/three";import { OrbitControls } from "https://esm.sh/three/addons/controls/OrbitControls.js";import gsap from "https://esm.sh/gsap";import GUI from "https://esm.sh/lil-gui";
const vertexShader = `uniform float uTime;uniform float uProgress;uniform float uMinY;uniform float uMaxY;
varying vec3 vPosition;varying vec3 vNormal;
// returns 0.0 to 1.0 value.float random(vec2 st) {  return fract(sin(dot(st.xy, vec2(12.989878.233))) * 43758.5453123);}
void main() {  vec4 modelPosition = modelMatrix * vec4(position, 1.0);
  // base glitch  float glitchTime = uTime - modelPosition.y;  float glitchstrength = sin(glitchTime) * sin(glitchTime * 3.45) + sin(glitchTime * 8.76);  glitchstrength /= 3.0;  glitchstrength = smoothstep(0.51.0, glitchstrength);  glitchstrength *= 2.0;  modelPosition.x += (random(modelPosition.xz + uTime) - 0.5) * glitchstrength;  modelPosition.z += (random(modelPosition.xz + uTime) - 0.5) * glitchstrength;
  // glitch by progress  float normalizedY = (modelPosition.y - uMinY) / (uMaxY - uMinY);  float diff = abs(normalizedY - uProgress);  float glitchStrength = smoothstep(0.020.0, diff);  glitchStrength *= 0.3;
  modelPosition.x += (random(modelPosition.xz + uTime) - 0.5) * glitchStrength;  modelPosition.z += (random(modelPosition.xz + uTime) - 0.5) * glitchStrength;
  gl_Position = projectionMatrix * viewMatrix * modelPosition;
  vPosition = modelPosition.xyz;
  vec4 newNormal = modelMatrix * vec4(normal, 0.0);  vNormal = newNormal.xyz;}`;
const fragmentShader = `uniform float uTime;uniform float uIndex;uniform float uCurrentIndex;uniform float uNextIndex;uniform float uProgress;uniform vec3 uColor;uniform float uMinY;uniform float uMaxY;
varying vec3 vPosition;varying vec3 vNormal;
void main() {  if(uIndex != uCurrentIndex && uIndex != uNextIndex) {    discard;  }
  float lines = 20.0;  float offset = vPosition.y - uTime * 0.2;  float density = mod(offset * lines, 1.0);  density = pow(density, 3.0);
  vec3 viewDirection = normalize(vPosition - cameraPosition);  float fresnel = 1.0 - abs(dot(normalize(vNormal), viewDirection));  fresnel = pow(fresnel, 2.0);
  float falloff = smoothstep(0.80.0, fresnel);
  float holographic = density * fresnel;  holographic += fresnel * 1.25;  holographic *= falloff;
  float normalizedY = (vPosition.y - uMinY) / (uMaxY - uMinY);  if(uIndex == uCurrentIndex && normalizedY < uProgress) {    discard;  }  if(uIndex == uNextIndex && normalizedY > uProgress) {    discard;  }
  gl_FragColor = vec4(uColor, holographic);
  #include <tonemapping_fragment>  #include <colorspace_fragment>}`;
const params = {  color: "#00d5ff",  stageColor: "#d4d4d4",  ambientLight: "#ffffff",  directionalLight: "#a4d5f4",  currentIndex: 0,  nextIndex: 1,};
const uniforms = {  uColor: new THREE.Uniform(new THREE.Color(params.color)),  uTime: new THREE.Uniform(0),  uProgress: new THREE.Uniform(0),  uIndex: new THREE.Uniform(0),  uCurrentIndex: new THREE.Uniform(params.currentIndex),  uNextIndex: new THREE.Uniform(params.nextIndex),  uMinY: new THREE.Uniform(0),  uMaxY: new THREE.Uniform(0),};
const initThree = (app) => {  const size = {    width: window.innerWidth,    height: window.innerHeight,    dpr: window.devicePixelRatio,  };
  // ---------------------------------------  // Scene  const scene new THREE.Scene();  // Camera  const camera new THREE.PerspectiveCamera(    35,    size.width / size.height,    0.1,    100  );  camera.position.set(04, -10);  scene.add(camera);  // Renderer  const renderer new THREE.WebGLRenderer({ antialiastrue });  renderer.shadowMap.enabled = true;  renderer.shadowMap.type = THREE.PCFSoftShadowMap;  renderer.setSize(size.width, size.height);  renderer.setPixelRatio(size.dpr);  app.appendChild(renderer.domElement);  // Controls  const controls new OrbitControls(camera, renderer.domElement);
  // ---------------------------------------  // Lights  const ambientLight new THREE.AmbientLight(    new THREE.Color(params.ambientLight),    0.5  );  scene.add(ambientLight);
  const dLight new THREE.DirectionalLight(    new THREE.Color(params.directionalLight),    1.0  );  dLight.position.set(031);  scene.add(dLight);
  const pLight new THREE.PointLight(new THREE.Color(params.color), 110);  pLight.position.set(0, -1.30);  scene.add(pLight);
  // ---------------------------------------  const baseMaterial new THREE.ShaderMaterial({    vertexShader: vertexShader,    fragmentShader: fragmentShader,    uniforms,    transparenttrue,    blending: THREE.AdditiveBlending,  });  baseMaterial.depthWrite = false;
  // Stage  const cylinder new THREE.Mesh(    new THREE.CylinderGeometry(220.5128),    new THREE.MeshStandardMaterial({      colornew THREE.Color(params.stageColor),    })  );  cylinder.position.set(0, -20);  scene.add(cylinder);
  // Geoms  const torusknotGeometry new THREE.TorusKnotGeometry(10.512832);  torusknotGeometry.computeBoundingBox();  const icosahedronGeometry new THREE.IcosahedronGeometry(224);  icosahedronGeometry.computeBoundingBox();  const torusGeometry new THREE.TorusGeometry(1.40.512832);  torusGeometry.computeBoundingBox();
  // compute minY and maxY  const minY = Math.min(    torusknotGeometry.boundingBox.min.y,    icosahedronGeometry.boundingBox.min.y,    torusGeometry.boundingBox.min.y  );  const maxY = Math.max(    torusknotGeometry.boundingBox.max.y,    icosahedronGeometry.boundingBox.max.y,    torusGeometry.boundingBox.max.y  );  const margin 0.1;  const posY 0.5;  uniforms.uMinY.value = minY + posY - margin;  uniforms.uMaxY.value = maxY + posY + margin;
  // Torus knot  const torusKnotMaterial = baseMaterial.clone();  torusKnotMaterial.uniforms.uIndex.value = 0;  const torusKnot new THREE.Mesh(torusknotGeometry, torusKnotMaterial);  torusKnot.position.y = posY;  scene.add(torusKnot);
  // Icosahedron  const icosahedronMaterial = baseMaterial.clone();  icosahedronMaterial.uniforms.uIndex.value = 1;  const icosahedron new THREE.Mesh(icosahedronGeometry, icosahedronMaterial);  icosahedron.position.y = posY;  scene.add(icosahedron);
  // Torus  const torusMaterial = baseMaterial.clone();  torusMaterial.uniforms.uIndex.value = 2;  const torus new THREE.Mesh(torusGeometry, torusMaterial);  torus.position.y = posY;  scene.add(torus);
  const materials = [torusKnotMaterial, icosahedronMaterial, torusMaterial];
  // ---------------------------------------  const clock new THREE.Clock();  const animate = () => {    requestAnimationFrame(animate);    const delta = clock.getDelta();    const elapsedTime = clock.getElapsedTime();
    // update index and animate progress    const newIndex = Math.floor((elapsedTime * 0.25) % 3);    if (newIndex !== params.currentIndex) {      params.currentIndex = newIndex;      params.nextIndex = newIndex === 2 ? 0 : newIndex + 1;      materials.forEach((material) => {        material.uniforms.uCurrentIndex.value = params.currentIndex;        material.uniforms.uNextIndex.value = params.nextIndex;        gsap.fromTo(          material.uniforms.uProgress,          { value0 },          { value1, duration1.5, ease"linear" }        );      });    }
    // Update models    torusKnot.rotation.y += delta * 0.5;    torusKnot.rotation.x += delta * 0.5;    torus.rotation.y += delta * 0.5;    torus.rotation.x += delta * 0.5;    // Update uniforms    torusMaterial.uniforms.uTime.value = elapsedTime;    icosahedronMaterial.uniforms.uTime.value = elapsedTime;
    renderer.render(scene, camera);  };  animate();  materials.forEach((material) => {    material.uniforms.uCurrentIndex.value = params.currentIndex;    material.uniforms.uNextIndex.value = params.nextIndex;    gsap.fromTo(      material.uniforms.uProgress,      { value0 },      { value1, duration1.5, ease"linear" }    );  });
  // ---------------------------------------  // Resize  window.addEventListener("resize", () => {    size.width = window.innerWidth;    size.height = window.innerHeight;    size.dpr = window.devicePixelRatio;    camera.aspect = size.width / size.height;    camera.updateProjectionMatrix();    renderer.setSize(size.width, size.height);    renderer.setPixelRatio(size.dpr);  });
  // ----------------------------------------  // GUI  const gui new GUI();  gui    .addColor(params, "color")    .name("hologram color")    .onChange((val) => {      materials.forEach((material) => {        material.uniforms.uColor.value = new THREE.Color(val);      });      pLight.color = new THREE.Color(val);    });  gui    .addColor(params, "stageColor")    .name("stage color")    .onChange((val) => (cylinder.material.color = new THREE.Color(val)));  gui    .addColor(params, "ambientLight")    .name("ambient light")    .onChange((val) => (ambientLight.color = new THREE.Color(val)));  gui    .addColor(params, "directionalLight")    .name("directional light")    .onChange((val) => (dLight.color = new THREE.Color(val)));};
const init = () => {  const app = document.querySelector("#app");  if (app) {    initThree(app);  }};
document.addEventListener("DOMContentLoaded", init);

源码:

https://codepen.io/kyoko-nr/pen/bNGJrQe

体验:

https://codepen.io/kyoko-nr/full/bNGJrQe


阅读原文:原文链接


该文章在 2025/4/21 10:31:37 编辑过
关键字查询
相关文章
正在查询...
点晴ERP是一款针对中小制造业的专业生产管理软件系统,系统成熟度和易用性得到了国内大量中小企业的青睐。
点晴PMS码头管理系统主要针对港口码头集装箱与散货日常运作、调度、堆场、车队、财务费用、相关报表等业务管理,结合码头的业务特点,围绕调度、堆场作业而开发的。集技术的先进性、管理的有效性于一体,是物流码头及其他港口类企业的高效ERP管理信息系统。
点晴WMS仓储管理系统提供了货物产品管理,销售管理,采购管理,仓储管理,仓库管理,保质期管理,货位管理,库位管理,生产管理,WMS管理系统,标签打印,条形码,二维码管理,批号管理软件。
点晴免费OA是一款软件和通用服务都免费,不限功能、不限时间、不限用户的免费OA协同办公管理系统。
Copyright 2010-2025 ClickSun All Rights Reserved