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

2025-04-17 0 642

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

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

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

免责声明 1、百创网作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创网无关;无论卖家以何理由要求线下交易的,请联系管理举报。 3. 百创网网站的资源均由店家上传出售,本站无法判断和识别资源的版权等合法性属性。如果您对本网站上传的信息资源的版权存有异议,请您及时联系 我们。如果需要删除链接,请下载下面的附件,正确填写信息后并发给我们,本站核实信息真实性后,在24小时内对商品进行删除处理。 联系邮箱:baicxx@baicxx.com (相关事务请发函至该邮箱)

百创网-源码交易平台_网站源码_商城源码_小程序源码 行业资讯 Three.js 全息影像效果(源码) https://www.baicxx.com/30203.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、百创会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、百创无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在百创上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于百创介入快速处理。
查看详情
  • 1、百创作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创无关;无论卖家以何理由要求线下交易的,请联系管理举报。
查看详情
  • 免责声明 1、百创网作为第三方中介平台,依据交易合同(商品描述、交易前商定的内容)来保障交易的安全及买卖双方的权益; 2、非平台线上交易的项目,出现任何后果均与百创网无关;无论卖家以何理由要求线下交易的,请联系管理举报。 3. 百创网网站的资源均由店家上传出售,本站无法判断和识别资源的版权等合法性属性。如果您对本网站上传的信息资源的版权存有异议,请您及时联系 我们。如果需要删除链接,请下载下面的附件,正确填写信息后并发给我们,本站核实信息真实性后,在24小时内对商品进行删除处理。 联系邮箱:baicxx@baicxx.com (相关事务请发函至该邮箱)
查看详情

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务

  • 0 +

    访问总数

  • 0 +

    会员总数

  • 0 +

    文章总数

  • 0 +

    今日发布

  • 0 +

    本周发布

  • 0 +

    运行天数

你的前景,远超我们想象