three.js 楼层加载动画

概述

如有不明白的可以加QQ:2354528292;wx: aichitudousien
更多教学视频请访问:https://space.bilibili.com/236087412
源码获取:https://item.taobao.com/item.htm?spm=a21dvs.23580594.0.0.3c3a645eIMTaft&ft=t&id=714326516274

开发了一个Three.js 使用烘培模型的demo,先看视频效果,整体的效果有水面,太阳,倒影,模型动画,围栏特效,相机动画,楼层动画

three.js 云上城市

创建场景

创建渲染器,灯光,相机,控制器和以前的智慧城市项目一样,从那边照抄过来即可,调用方式如下

	  app = new ZThree("screen");
      app.initThree();
      // app.initHelper();
      app.initOrbitControls();
      light = app.initLight();

      // stats = app.initStatus();
      selectObj = app.initRaycaster();
      window.app = app;
      camera = app.camera;
      // bloomComposer = app.bloomComposer();
      camera.position.set(...this.cameraPosition);
      scene = app.scene;
      renderer = app.renderer;
      renderer.logarithmicDepthBuffer = true;
      renderer.autoClear = false;

      controls = app.controls;
      controls.target.set(...this.target);
      controls.maxDistance = 2000;
      controls.maxPolarAngle = Math.PI / 2.2;
      clock = new THREE.Clock();

创建天空

export function loaderSky(app, water) {
  return new Promise(resolve => {
    let sky = new Sky();
    sky.scale.setScalar(10000);
    app.scene.add(sky);
    let skyUniforms = sky.material.uniforms;

    skyUniforms['turbidity'].value = 1;
    skyUniforms['rayleigh'].value = 3;
    skyUniforms['mieCoefficient'].value = 0.005;
    skyUniforms['mieDirectionalG'].value = 0.8;

    let parameters = {
      inclination: 0.49,
      azimuth: 0.205
    };

    let pmremGenerator = new THREE.PMREMGenerator(app.renderer);

    let sun = new THREE.Vector3();

    let theta = Math.PI * (parameters.inclination - 0.5);
    let phi = 2 * Math.PI * (parameters.azimuth - 0.5);

    sun.x = Math.cos(phi);
    sun.y = Math.sin(phi) * Math.sin(theta);
    sun.z = Math.sin(phi) * Math.cos(theta);

    sky.material.uniforms['sunPosition'].value.copy(sun);
    water.material.uniforms['sunDirection'].value.copy(sun).normalize();

    app.scene.environment = pmremGenerator.fromScene(sky).texture;

    resolve(sky);
  })
}

创建水面

创建一个平面然后加上调用水面的材质就好,很简单

export function loaderWater(app) {
  return new Promise(resolve => {
    let waterGeometry = new THREE.PlaneGeometry(10000, 10000);

    let water = new Water(
      waterGeometry, {
        textureWidth: 512,
        textureHeight: 512,
        waterNormals: new THREE.TextureLoader().load('texture/waternormals.jpg', function (texture) {
          texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
        }),
        alpha: 1.0,
        sunDirection: new THREE.Vector3(),
        sunColor: 0xffffff,
        waterColor: 0x001e0f,
        distortionScale: 3.7,
        fog: app.scene.fog !== undefined
      }
    );

    water.rotation.x = -Math.PI / 2;

    app.scene.add(water);

    resolve(water);
  })
}

此时我们看到的效果是

创建模型

export async function loaderShop(app) {
  return new Promise(async resolve => {
    let gltf = await app.loaderGltfDracoModel('model/', 'yun.glb');
    let model = gltf.scene;
    model.getObjectByName('cloud').visible = false;
    const s = 0.1;
    model.scale.set(s, s, s);
    model.position.set(0, -80, 0);
    let allModel = [];
    let mixer = new THREE.AnimationMixer( model );
		mixer.clipAction( gltf.animations[ 0 ] ).play();
    let clickTextObj = [model.getObjectByName('全息店铺标签'), model.getObjectByName('全息城市标签'), model.getObjectByName('云展标签')];
    // loaderRipple();
    app.scene.add(model);
    resolve({
      model,
      allModel,
      clickTextObj,
      mixer
    });
  })
}

开启模型动画

loaderModel.js 文件代码:

let mixer = new THREE.AnimationMixer( model );
mixer.clipAction( gltf.animations[ 0 ] ).play();

渲染模块代码:

const delta = clock.getDelta();
if (model.mixer) {
          model.mixer.update( delta );
 }

此时我们就可以看到模型的动画了。

栅栏动画

生成栅栏的mesh

let ripple;
export function loaderRipple(pos) {
  if (ripple) {
    app.scene.remove(ripple);
    ripple.geometry.dispose();
    ripple.material.dispose();
    ripple = null;
  }
  let vector3s = [];
  for (let i = 0; i < pos.length; i++) {
    vector3s.push(new THREE.Vector3(...pos[i]));
  }
  // 围栏
  let rippleGeometry = getRippleGeometry(vector3s, 60);

  let rippleMaterial = new THREE.ShaderMaterial({
    vertexShader: rippleShader.vs,
    fragmentShader: rippleShader.fs,
    uniforms: rippleShader.uniform,
    side: THREE.DoubleSide,
    transparent: true,
    depthWrite: false
  })

  ripple = new THREE.Mesh(rippleGeometry, rippleMaterial);
  app.scene.add(ripple);
}

生成栅栏的geometry

function getRippleGeometry(points = [], height = 10) {
  let positions = []
  let uvs = []
  for (let i = 0, j = positions.length, t = uvs.length; i < points.length - 1; i++) {
    let vUvyMax = 1
    let left = points[i]
    let right = points[i + 1]
    positions[j++] = left.x
    positions[j++] = 0
    positions[j++] = left.y
    uvs[t++] = 0
    uvs[t++] = 0

    positions[j++] = right.x
    positions[j++] = 0
    positions[j++] = right.y
    uvs[t++] = 1
    uvs[t++] = 0

    positions[j++] = left.x
    positions[j++] = height
    positions[j++] = left.y
    uvs[t++] = 0
    uvs[t++] = vUvyMax

    positions[j++] = left.x
    positions[j++] = height
    positions[j++] = left.y
    uvs[t++] = 0
    uvs[t++] = vUvyMax

    positions[j++] = right.x
    positions[j++] = 0
    positions[j++] = right.y
    uvs[t++] = 1
    uvs[t++] = 0

    positions[j++] = right.x
    positions[j++] = height
    positions[j++] = right.y
    uvs[t++] = 1
    uvs[t++] = vUvyMax
  }
  let geometry = new THREE.BufferGeometry()
  geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positions), 3))
  geometry.addAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2))
  return geometry;
}

使用的shader:

export const rippleShader = {
  vs:"\n  precision lowp float;\n  precision lowp int;\n  ".concat(THREE.ShaderChunk.fog_pars_vertex, "\n  varying vec2 vUv;\n  void main() {\n    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n    vUv = uv;\n    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    ").concat(THREE.ShaderChunk.fog_vertex, "\n  }\n"),
  fs: "\n  precision lowp float;\n  precision lowp int;\n  uniform float time;\n  uniform float opacity;\n  uniform vec3 color;\n  uniform float num;\n  uniform float hiz;\n\n  varying vec2 vUv;\n\n  void main() {\n    vec4 fragColor = vec4(0.);\n    float sin = sin((vUv.y - time * hiz) * 10. * num);\n    float high = 0.92;\n    float medium = 0.4;\n    if (sin > high) {\n      fragColor = vec4(mix(vec3(.8, 1., 1.), color, (1. - sin) / (1. - high)), 1.);\n    } else if(sin > medium) {\n      fragColor = vec4(color, mix(1., 0., 1.-(sin - medium) / (high - medium)));\n    } else {\n      fragColor = vec4(color, 0.);\n    }\n\n    vec3 fade = mix(color, vec3(0., 0., 0.), vUv.y);\n    fragColor = mix(fragColor, vec4(fade, 1.), 0.85);\n    gl_FragColor = vec4(fragColor.rgb, fragColor.a * opacity * (1. - vUv.y));\n  }\n",
  uniform:{
    time: {
      type: "pv2",
      value: 0
  },
  color: {
      type: "uvs",
      value: new THREE.Color('#1E90FF')
  },
  opacity: {
      type: "pv2",
      value: 0.8
  },
  num: {
      type: "pv2",
      value: 8
  },
  hiz: {
      type: "pv2",
      value: 0.15
  }
  }
}

此时在调用相机的飞行函数,此函数在智慧城市项目中也有详细介绍,在调用相机的飞行函数后执行生成栅栏的函数即可

app.initRaycaster('click', (selectObj) => {
        if (selectObj) {
          console.log(selectObj);
          // return;
          let object = selectObj.object;
          app.flyTo({
            position: textPos[object.name].position
          })
          loaderRipple(textPos[object.name].rippleVec)
        }
      }, model.clickTextObj);

最后我们就可以看到,在点击了店铺标签后生成一个个栅栏了
![在这里插入图片描述](https://img-blog.csdnimg.cn/27de9280c80143ba8c56845516cd8d04.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5oiR5oOz5b2T5Zad5rC05Lq6,size_20,color_FFFFFF,t_70,g_se,x_16

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
对整threeJS体系进行全面剖析。整理出全面的教学大纲,涵盖内容面非常广。此教学版本为threeJS107版本。关于版本不建议大家使用低于90的版本学习。以下是课程目录1-ThreeJS概览(基本图形简介,什么是点线面如何绘制点线面,什么是材质,什么是几何体,什么是相机,什么是渲染器,什么是场景)2-相机和渲染器(详解相机类型,渲染器如何使用,针对不同场景怎么用,怎么调效果,怎么渲染,怎么输出画布,如何解决透明问题等等)3-创建平面几何(常见的几何体如何使用,如何使用简单的几何体绘制出自定义自己想要的几何体,关于几何体的性能剖析,如何解决性能,几何体的渲染原理)4-高级图形算法常见库(求直线的斜率  计算线段与圆的交点 计算线段的长度 判断折线是否在多边形内 等等)5-sprite精灵(怎么让一个图标永远朝向屏幕,精灵的属性,精灵材质原理等,广告提示框必用)6-骨骼游戏动画(什么是模型动画,常见游戏案例,如何让人头进行各种攻击动作)7-3d模型加载(常见模型格式,如何渲染不同格式,不同格式的特点,什么格式性能优越,模型渲染异常,贴图不显示等问题详解)8-高阶动态纹理(你所不知道的纹理用法,我说你不知道,你肯定不知道)9-漫游轨迹以及其动画路径(怎么绘制贝塞尔曲线,如何使用曲线上的路径,跟随路径移动的原理,相机如何运动,物体如何运动)10-着色器(什么是着色器。初识着色器基础,着色器材质怎么用,怎么使用着色器库)11-常见渲染以及透明度问题12-对象拾取以及拖拽(3d世界里面如何拖拽物体,拖拽的原理,mousemove mouseon等的事件效果)13-世界坐标以及组的问题(什么是相对坐标,什么是世界坐标,什么是当前坐标,怎么转化父子坐标系,组的优化,为什么用组,组的优势)14-指定对象旋转中心(什么是物体的几何体中心,如何改变中心,如何绕轴转动)15-层级对象渲染(多个场景一键切换切换的优势,针对大项目的用法)16-拓展了解系列(不定期不断更新案例,各种酷炫效果bloom,halo等,以及各种3d图表,粒子案例等,不断构建你的3d实践能力)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃土豆丝嗯z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值