不同分辨率加载不同js_Vue+Three.js,新手demo

694e5b63458facbbd1d7871e33f6302d.png

准备

我们需要建立一个vue项目,这里我直接用vue-cli脚手架了。

Part 1:引入three.js

项目文件夹里打开终端窗口,并运行:

npm install --save three

在需要使用three.js的组件内引入

import * as THREE from 'three'

Part 2:创建容器

创建canvas标签,为3D渲染建立容器。

<template>
  <div>
    <canvas id="three"></canvas>
  </div>
</template>

Part 3:创建场景

Three.js依赖一些要素,第一是scene,第二是render,第三是carmea。

我们首先为Three.js创建一个scene:

const scene = new THREE.Scene()

scene可以理解为我们将要渲染的环境、背景。我们可以给它换个颜色:

scene.background = new THREE.Color('#eee')

也可以通过自定义的 纹理(Texture) 来为场景设置背景贴图。

接下来我们拿到canvas:

const canvas = document.querySelector('#three')

创建一个WebGLRenderer,将canvas和配置参数传入:

const renderer = new THREE.WebGLRenderer({ canvas, antialias: true })

最后我们来创建一个camera用来观看场景里的内容,Three.js提供多种相机,比较常用的是PerspectiveCamera(透视摄像机)以及OrthographicCamera (正交投影摄像机)。

透视相机用来模拟人眼所看到的景象,物体的大小会受远近距离的影响,它是3D场景的渲染中使用得最普遍的投影模式。

而正交投影摄像机,不具有透视效果,即物体的大小不受远近距离的影响。

这里我们使用PerspectiveCamera(透视摄像机):

const camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.1, 1000)

PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )具有四个参数:

  • fov — 摄像机视锥体垂直视野角度。可以理解为人类的视野广度。
  • aspect — 摄像机视锥体横纵比。渲染结果的横向尺寸和纵向尺寸的比值,这里我们使用的是 浏览器窗口的宽高比。
  • near — 摄像机视锥体近端面。一切比近面更近的事物将不被渲染。
  • far — 摄像机视锥体远端面。一切比远面更远的事物将不被渲染,但是设置过大可能会影响性能。
    这些参数一起定义了摄像机的视锥体。

6c994e8b784d9caa4380b633828d9545.png
PerspectiveCamera(透视摄像机)

生成的camera默认是放在中心点(0,0,0)的,但这是待会模型要放的位置,因此,我们把摄像机挪个位置:

camera.position.z = 10

Three.js 需要一个动画循环函数,Three.js 的每一帧都会执行这个函数。

function animate() {
  renderer.render(scene, camera)
  requestAnimationFrame(animate)
}
animate()

我们的场景的准备工作已经做好了,现在是一片灰色,没有物体。vue组件代码是这样的:

<template>
  <div>
    <canvas id="three"></canvas>
  </div>
</template>

<script>
import * as THREE from 'three'
export default {
  mounted() {
    this.initThree()
  },
  methods: {
    initThree() {
      const scene = new THREE.Scene()
      scene.background = new THREE.Color('#eee')
      const canvas = document.querySelector('#three')
      const renderer = new THREE.WebGLRenderer({ canvas, antialias: true })
      const camera = new THREE.PerspectiveCamera(
        50,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      )
      camera.position.z = 10

      function animate() {
        renderer.render(scene, camera)
        requestAnimationFrame(animate)
      }
      animate()
    },
  },
}
</script>

<style scoped>
#three {
  width: 100%;
  height: 100%;
  position: fixed;
  left: 0;
  top: 0;
}
</style>

Part 4 引入3D模型

官网介绍说three.js的核心专注于3D引擎最重要的组件。其它很多有用的组件 - 如控制器(control)、加载器(loader)以及后期处理效果(post-processing effect)这些就需要我们单独去引入。

如果我们想要加载外部3D模型,那么就需要用到加载器(loader),而Three.js提供的加载器又有好多种类型,分别可以加载不同的文件格式,其中官方比较推荐的是glTF格式,那么我们这里就使用glTF加载器:

import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'

当然你也可以选择FBX、OBJ或者COLLADA等其他加载器,如果你是npm安装的three.js,可以去node_modules/three/examples/jsm/loaders 目录下查看更多支持的加载器。

加载器有了,现在我们还没有模型。可以去搜索一些免费的3D模型素材下载,当然你也可以自己做,我是去 Sketchfab 下载的,如果对3D模型的制作感兴趣也可以学习一下,推荐一个免费的软件Blender 。

df7781c18658599abb1deaf40567c06f.png

萨勒芬妮。下载好后,解压,放进项目文件的public目录。

声明一个加载器,加载我们下载的模型,并把它添加到场景中,在animate函数上面添加代码:

    const gltfLoader = new GLTFLoader()
      gltfLoader.load('/seraphine/scene.gltf', (gltf) => {
        var model = gltf.scene
        scene.add(model)
      })

刷新页面,场景里有了模糊的黑色的小人,这是因为我们还没有给她添加纹理。

5fd4237f56f57fa0fcc93cd8d7e415ba.png

来给她上个色:

      gltfLoader.load('/seraphine/scene.gltf', (gltf) => {
        let model = gltf.scene

        //添加这段代码
        //遍历模型每部分
        model.traverse((o) => {
          //将图片作为纹理加载
          let explosionTexture = new THREE.TextureLoader().load(
            '/seraphine/textures/Mat_cwfyfr1_userboy17.bmp_diffuse.png'
          )
          //调整纹理图的方向
          explosionTexture.flipY = false
          //将纹理图生成基础网格材质(MeshBasicMaterial)
          const material = new THREE.MeshBasicMaterial({
            map: explosionTexture,
          })
          //给模型每部分上材质
          o.material = material
        })

        scene.add(model)
      })

好了,现在刷新下,她变成这样了,可以看到已经有颜色了,但还是很糊:

904ca0734a5dd7c4cc4fa381778a7ed9.png

原因是设备的物理像素分辨率与CSS像素分辨率的比值的问题,我们的canvas绘制出来后图片因为高清屏设备的影响,导致图片变大,然而我们在浏览器的渲染窗口并没有变大,因此图片会挤压缩放使得canvas画布会变得模糊。

修改它我们要用到devicePixelRatio这个属性,MDN解释:

此属性返回当前显示设备的物理像素分辨率与CSS像素分辨率的比值。该值也可以被解释为像素大小的比例:即一个CSS像素的大小相对于一个物理像素的大小的比值。

添加函数:

     function resizeRendererToDisplaySize(renderer) {
        const canvas = renderer.domElement
        var width = window.innerWidth
        var height = window.innerHeight
        var canvasPixelWidth = canvas.width / window.devicePixelRatio
        var canvasPixelHeight = canvas.height / window.devicePixelRatio

        const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height
        if (needResize) {
          renderer.setSize(width, height, false)
        }
        return needResize
      }

在animate函数内调用它:

      function animate() {
        controls.update()
        renderer.render(scene, camera)
        requestAnimationFrame(animate)

        //添加下面代码
        if (resizeRendererToDisplaySize(renderer)) {
          const canvas = renderer.domElement
          camera.aspect = canvas.clientWidth / canvas.clientHeight
          camera.updateProjectionMatrix()
        }

      }

5c5287054a571b5f4a193db124e60897.png

模型看起来好多了。

Part 5 添加轨道控制器

现在嘛,就只能看到萨勒芬妮的侧脸,我想康康正脸怎么办。

引入轨道控制器:

import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

创建它,可以写在animate函数上面:

const controls = new OrbitControls(camera, renderer.domElement)

给它加点阻尼感,更真实点,泥可以对比下加不加的区别:

controls.enableDamping = true

最后在animate函数里调用它,要写在 renderer.render(scene, camera)前面啊:

controls.update()

现在就可以拉近点看脸了:

b66de2d2edd8b5640e27e7334caba378.png

Part 6 添加光与影

还是先加个地板叭,不然影子没地方投。Three.js里物体(一般叫网格Mesh)由两部分构成,一是它的形状,二是它的材质,我们给地板创建它们:

let floorGeometry = new THREE.PlaneGeometry(3000, 3000)
let floorMaterial = new THREE.MeshPhongMaterial({color: 0xff0000})

平面几何体,PlaneGeometry(width : Float, height : Float, widthSegments : Integer, heightSegments : Integer)

width — 平面沿着X轴的宽度。默认值是1。
height — 平面沿着Y轴的高度。默认值是1。
widthSegments — (可选)平面的宽度分段数,默认值是1。
heightSegments — (可选)平面的高度分段数,默认值是1。

Phong网格材质(MeshPhongMaterial)

是一种用于具有镜面高光的光泽表面的材质。

生成Mesh,并添加到场景中:

let floor = new THREE.Mesh(floorGeometry, floorMaterial)
floor.rotation.x = -0.5 * Math.PI
floor.receiveShadow = true
floor.position.y = -0.001
scene.add(floor)

再给他调整下位置,让他水平放置在萨勒芬妮的脚下,并让地板可以接收投影。

3c6dc99545dba193e02392f9bb743033.png

可是现在是黑的,跟我们加的颜色不一样,那是因为没有光,Three.js提供的光源有很多种,有的可以产生阴影(平行光,点光源等),有的不行(半球光等):

先添加个平行光:

      const dirLight = new THREE.DirectionalLight(0xffffff, 0.6)
      //光源等位置
      dirLight.position.set(-10, 8, -5)
      //可以产生阴影
      dirLight.castShadow = true
      dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024)
      scene.add(dirLight)

平行光一般用来模拟太阳光,DirectionalLight( color : Integer, intensity : Float )

color - (可选参数) 16进制表示光的颜色。 缺省值为 0xffffff (白色)。
intensity - (可选参数) 光照的强度。缺省值为1。

现在地板有颜色了,还可以添加多个光源,让场景看起来更真实:

      const hemLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6)
      hemLight.position.set(0, 48, 0)
      scene.add(hemLight)

半球光光源直接放置于场景之上,光照颜色从天空光线颜色渐变到地面光线颜色。HemisphereLight( skyColor : Integer, groundColor : Integer, intensity : Float )

skyColor - (可选参数) 天空中发出光线的颜色。 缺省值 0xffffff。
groundColor - (可选参数) 地面发出光线的颜色。 缺省值 0xffffff。
intensity - (可选参数) 光照强度。 缺省值 1。

想要产生影子,还需要在renderer下添加:

      const renderer = new THREE.WebGLRenderer({ canvas, antialias: true })
      //加这句
      renderer.shadowMap.enabled = true;

以及:

        model.traverse((o) => {
          //将图片作为纹理加载
          let explosionTexture = new THREE.TextureLoader().load(
            '/seraphine/textures/Mat_cwfyfr1_userboy17.bmp_diffuse.png'
          )
          //调整纹理图的方向
          explosionTexture.flipY = false
          //将纹理图生成基础网格材质(MeshBasicMaterial)
          const material = new THREE.MeshBasicMaterial({
            map: explosionTexture,
          })
          //给模型每部分上材质
          o.material = material

          //加这句,让模型等每个部分都能产生阴影
          if (o.isMesh) {
            o.castShadow = true
            o.receiveShadow = true
          }

        })

现在就有影子啦!

4578d767b4c16ee77ca176032b254aa4.png

最后最后最后,给场景加个雾叭:

const scene = new THREE.Scene()
scene.background = new THREE.Color('#eee')
//在代码上面声明场景等下面加这句:
scene.fog = new THREE.Fog('#eee', 20, 100)

6a26ee44c0dbe1d2d9a04cb1357da457.png
知乎视频​www.zhihu.com

OK,完事。

喔喔代码忘放了:

<template>
  <div>
    <canvas id="three"></canvas>
  </div>
</template>

<script>
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'

export default {
  mounted() {
    this.initThree()
  },
  methods: {
    initThree() {
      const scene = new THREE.Scene()
      scene.background = new THREE.Color('#eee')
      scene.fog = new THREE.Fog('#eee', 20, 100)

      const canvas = document.querySelector('#three')
      const renderer = new THREE.WebGLRenderer({ canvas, antialias: true })
      renderer.shadowMap.enabled = true

      const camera = new THREE.PerspectiveCamera(
        50,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      )
      camera.position.z = 10

      const gltfLoader = new GLTFLoader()
      gltfLoader.load('/seraphine/scene.gltf', (gltf) => {
        let model = gltf.scene
        //遍历模型每部分
        model.traverse((o) => {
          //将图片作为纹理加载
          let explosionTexture = new THREE.TextureLoader().load(
            '/seraphine/textures/Mat_cwfyfr1_userboy17.bmp_diffuse.png'
          )
          //调整纹理图的方向
          explosionTexture.flipY = false
          //将纹理图生成基础网格材质(MeshBasicMaterial)
          const material = new THREE.MeshBasicMaterial({
            map: explosionTexture,
          })
          //给模型每部分上材质
          o.material = material
          if (o.isMesh) {
            o.castShadow = true
            o.receiveShadow = true
          }
        })
        scene.add(model)
      })

      const hemLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6)
      hemLight.position.set(0, 48, 0)
      scene.add(hemLight)

      const dirLight = new THREE.DirectionalLight(0xffffff, 0.6)
      //光源等位置
      dirLight.position.set(-10, 8, -5)
      //可以产生阴影
      dirLight.castShadow = true
      dirLight.shadow.mapSize = new THREE.Vector2(1024, 1024)
      scene.add(dirLight)

      let floorGeometry = new THREE.PlaneGeometry(8000, 8000)
      let floorMaterial = new THREE.MeshPhongMaterial({
        color: 0x857ebb,
        shininess: 0,
      })

      let floor = new THREE.Mesh(floorGeometry, floorMaterial)
      floor.rotation.x = -0.5 * Math.PI
      floor.receiveShadow = true
      floor.position.y = -0.001
      scene.add(floor)

      const controls = new OrbitControls(camera, renderer.domElement)
      controls.enableDamping = true

      function animate() {
        controls.update()
        renderer.render(scene, camera)
        requestAnimationFrame(animate)

        if (resizeRendererToDisplaySize(renderer)) {
          const canvas = renderer.domElement
          camera.aspect = canvas.clientWidth / canvas.clientHeight
          camera.updateProjectionMatrix()
        }
      }
      animate()

      function resizeRendererToDisplaySize(renderer) {
        const canvas = renderer.domElement
        var width = window.innerWidth
        var height = window.innerHeight
        var canvasPixelWidth = canvas.width / window.devicePixelRatio
        var canvasPixelHeight = canvas.height / window.devicePixelRatio

        const needResize =
          canvasPixelWidth !== width || canvasPixelHeight !== height
        if (needResize) {
          renderer.setSize(width, height, false)
        }
        return needResize
      }
    },
  },
}
</script>

<style scoped>
#three {
  width: 100%;
  height: 100%;
  position: fixed;
  left: 0;
  top: 0;
}
</style>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值