React+Three.js 简单入门 创建简单3D场景

Three.js 几个重要概念

首先我们简单地了解一下一个3D场景必不可少的三个元素,在后面的章节再对各个元素中再做比较详细的介绍。
在这里插入图片描述

Scene

场景,可以把scene看作一个沙盘,你所要呈现的物体需要把它摆放在场景中。

Camera

摄像机,最终呈现在屏幕上的影像,实际就是站在摄像机的角度去看场景中的物体,可以把它看作是我们的眼睛。

Renderer

渲染器,我们有了一个场景,并且通过摄像机捕获到了这个画面,如何将这个画面呈现到屏幕,这就是渲染器的工作了。形象一点说,比如我看到一个物体,把它画出来给你看,这个画出来就相当于一个渲染的过程。

坐标系

在three.js 中使用的是右手坐标系,何谓右手坐标系
右手坐标系
在场景中摆放物品的位置、设置摄像机的位置等一切与位置有关的,都要参考这个坐标系。

创建一个简单的3D场景

1 引入Three.js

1.首先在项目中安装依赖

npm install --save three

2.在你需要创建3D场景的文件中引入

import * as THREE from 'three'

这个引入不是一劳永逸的,有一些组件需要特殊引入,我们在用到的时候再说

2.初始化

SimpleScene.js (接下来所有代码都写在这个文件)

import React, { Component } from 'react'
import * as THREE from 'three'

export default class SimpleScene extends Component {

    componentDidMount(){
        this.init()
    }

    init = () => {
        
        const scene = new THREE.Scene()

        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000)
		
        const renderer = new THREE.WebGLRenderer()
        renderer.setSize(window.innerWidth, window.innerHeight)
        renderer.render(scene, camera)
        document.getElementById('stage').appendChild(this.renderer.domElement)

    }

    render() {
        return (
            <div id="stage">
            </div>
        )
    }
}

首先说明一下初始化中我们做了哪些工作:
1.创建一个场景实例 scene,它是我们后面放置物体的空间
2.创建一个透视摄像机实例 camera,不同摄像机特性有所不同,3D场景中比较常用的就是透视摄像机,四个参数分别为:

  • fov(视野角度): 摄像机的视野角度,在相同的距离下,视野角度越大,看见的东西越多,物体在显示器上就越小
  • aspect(宽高比): 渲染窗口的宽高比
  • near(近截面距离): 在摄像机与近截面直接的物体不会被渲染,也就是不可见
  • far(远截面距离): 远截面后的物体不会被渲染

摄像机示意图
3.创建一个渲染器实例,并设置宽高,这里的宽高指的是显示区域的大小,我们这里使它填充整个屏幕,然后设置它要渲染的场景和摄像机,最后将渲染器放入页面的容器中
至此,我们完成了一个3D场景必需三元素的初始化,运行程序
在这里插入图片描述
我们就得到了一片黑暗的空间,接下来我们开始往里面放东西

3.放置网格模型

网格模型,也就前面所说的物体,一个网格模型由几何体和材质组成

 		const geometry = new THREE.BoxGeometry(1,1,1)
        const material = new THREE.MeshBasicMaterial({color:'red'})
        const mesh = new THREE.Mesh(geometry, material)
        scene.add(mesh)

首先我们创建一个正方体的几何体,这里的几何体可以理解为只是一个骨架,接下来我们要为这个骨架贴上皮,即创建一个材质,这里我们创建的是一个红色的基本材质,最后把骨架和皮组合起来,创建了一个正方体模型,然后就可以把这个模型放进场景中。这时候运行程序,会发现还是一片黑暗。这是因为摄像机默认位置是(0,0,0),模型放置的默认中心位置也是(0,0,0),就是说现在摄像机在模型内部,我们现在把摄像机拉出来

        camera.position.z = 3

就可以看到一个红色正方形
在这里插入图片描述
但是这看起来一点都不3D,这是因为我们现在看到的是正方体的正面,调整一下摄像机的位置,并让它看向正方体

        camera.position.set(3,3,3)
        camera.lookAt(mesh.position)

就可以看到
在这里插入图片描述
有点3D的感觉了,但还是像2D的样子,差了阴影,要产生阴影,就必须有光。

4.设置灯光

        const pointLight = new THREE.PointLight(0xffffff)
        pointLight.position.set(3,2,1)
        scene.add(pointLight)

为场景添加一个点光源,光的颜色是白色,刷新一下,发现还是这样,没有阴影
在这里插入图片描述
这是材质的问题,MeshBasicMaterial这种材质对光无感,这也是为啥刚才没有设置灯光的情况下,我们仍可以看到这个正方体,现在我们给正方体换一种皮

        const material = new THREE.MeshPhongMaterial({color:'red'})

在这里插入图片描述
现在终于有3D的感觉了,画面偏暗,我们可以增加灯光的强度,或者增加一个环境光

        const ambientLight = new THREE.AmbientLight(0xcccccc, 0.5)
        scene.add(ambientLight)

在这里插入图片描述

5.让正方体动起来

光看个图片也不够3D,下一步我们让正方体转起来

    animate = () => {
        requestAnimationFrame(this.animate)
        this.mesh.rotation.y += 0.02
        this.renderer.render(this.scene, this.camera)
    }

然后在初始化后调用,就可以看到正方体以y轴为轴心转起来了
​​​​​​

requestAnimationFrame(this.animate)

这一行代码的作用是使屏幕刷新时调用括号中的函数,这里的刷新指的是指屏幕刷新率的那个刷新,所以每次执行animate,正方体就会沿y轴旋转0.02个单位,如果电脑的刷新频率是60hz的话,一秒内这个函数执行60次,看起来正方体就转了起来,记得在旋转后调用

this.renderer.render(this.scene, this.camera)

不然只是修改了属性,没有渲染。

6.控制摄像机

光看着小正方体旋转还是不够3D,接下来我们将控制摄像机,全方位观察小正方体

首先需要引入轨道控制器,有了它我们就可以控制摄像机

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

在初始化的代码中,加上

        const controls = new OrbitControls(this.camera, this.renderer.domElement)
        controls.addEventListener('change', this.handleControl)
        this.controls = controls

第一行,声明这个控制器是控制哪个摄像机,在哪个渲染器节点控制摄像机,第二行给控制器添加监听事件,每当我们使用鼠标拖拽或者滚轮时都会触发回调函数,回调函数相当简单:

    handleControl = () => {
        this.renderer.render(this.scene, this.camera)
    }

这样我们就可以控制摄像机了,来看看效果,已经非常3D了,动画中移近摄像头的时候,正方体好像盖子要打开,这其实是因为正方体触碰到了近截面,前面说过,摄像机与近截面之间的不会被渲染,所以才会出现这种情况,可以把近截面距离给调小。

至此,我们的一个非常简单的3D场景就搭建完毕了。

全部代码

import React, { Component } from 'react'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'

export default class SimpleScene extends Component {

    componentDidMount(){
        this.init()
        this.animate()
    }

    
    init = () => {
        
        const scene = new THREE.Scene()
        this.scene = scene

        const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1000)
        this.camera = camera

        const geometry = new THREE.BoxGeometry(1,1,1)
        const material = new THREE.MeshPhongMaterial({color:'red'})
        const mesh = new THREE.Mesh(geometry, material)
        this.mesh = mesh
        scene.add(mesh)

        camera.position.set(3,3,3)
        camera.lookAt(mesh.position)

        const pointLight = new THREE.PointLight(0xffffff,1)
        pointLight.position.set(3,2,1)
        scene.add(pointLight)

        const ambientLight = new THREE.AmbientLight(0xcccccc, 0.5)
        scene.add(ambientLight)

        const renderer = new THREE.WebGLRenderer()
        renderer.setSize(window.innerWidth, window.innerHeight)
        renderer.render(scene, camera)
        this.renderer = renderer
        document.getElementById('stage').appendChild(renderer.domElement)

        const controls = new OrbitControls(this.camera, this.renderer.domElement)
        controls.addEventListener('change', this.handleControl)
        this.controls = controls
    }

    handleControl = () => {
        this.renderer.render(this.scene, this.camera)
    }

    animate = () => {
        requestAnimationFrame(this.animate)
        this.mesh.rotation.y += 0.02
        this.renderer.render(this.scene, this.camera)
    }

    render() {
        return (
            <div id="stage">
            </div>
        )
    }
}

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值