threejs-场景创建(基于react-hooks)

个基本的threejs程序,主要内容如下:

  • 一个基本的threejs程序,创建场景,一个兰伯特材质地板
  • 创建MeshLambertMaterial材质的球体和立方体并对他们添加阴影
  • 为场景添加鼠标并使得页面大小自适应屏幕
  • 为立方体添加旋转动画效果
  • 为球体添加弹跳动画效果

效果如图
在这里插入图片描述
代码:

import { useRef, useEffect, useCallback } from 'react'
import * as THREE from 'three'
import OrbitControls from 'three-orbitcontrols';
import './index.scss'

const App = () => {
    const page = useRef(); // useRef不会导致重新渲染
    /**
     * 场景、相机、渲染器作为threejs的基本结构,需要在页面进入时渲染完毕
     */
    const scence = useRef(new THREE.Scene()).current; //场景
    const camera = useRef(new THREE.PerspectiveCamera()).current; //摄像机(透视投影)
    const render = useRef(new THREE.WebGLRenderer()).current; //渲染器

    const meshList = useRef([]).current // 模型容器

    const timer = useRef(null) // 定时器

    const controls = new OrbitControls( camera, render.domElement );//创建控件对象

    let step = 0;

    /** 
     * 自适应浏览器窗口大小
    */
    const reszie = () => {
        //innerHeight 返回窗口的文档显示区的高度,如果有垂直滚动条,也包括滚动条高度
        //innerWidth 返回窗口的文档显示区的宽度,如果有水平滚动条,也包括滚动条高度
        camera.aspect = (window.innerWidth / window.innerHeight);
        camera.updateProjectionMatrix();
        render.setSize(window.innerWidth,window.innerHeight);
        
    }
    window.addEventListener('resize', reszie, false)


    useEffect(()=>{
        page.current.appendChild(render.domElement);
        init();
        initLight();
        initMesh();
        renderScene();
        return () => {
            // 销毁定时器
            cancelAnimationFrame(timer.current);
            // 销毁材质、几何体、渲染器、场景
            meshList.forEach((item) => {
                scence.remove(item)
                item.material.dispose();
                item.geometry.dispose();
            });
        }
    },[])

    // 初始化场景
    const init = useCallback(() => {
        render.setSize(page.current.offsetWidth, page.current.offsetHeight); // 渲染器设置尺寸
        // 设置背景颜色
        render.setClearColor(new THREE.Color(0x000000)); // 设置背景颜色和透明度
        render.shadowMap.enabled = true; // 渲染器允许渲染阴影⭐

        // 添加坐标轴
        // 红色代表 X 轴;绿色代表 Y 轴;蓝色代表 Z 轴
        const axes = new THREE.AxesHelper(20);
        scence.add(axes);

        /**
         * 设置摄像机的属性
         */
        camera.aspect = (page.current.offsetWidth / page.current.offsetHeight) // 摄像机设置屏幕宽高比
        camera.fov = 45; // 摄像机的视角
        camera.near = 0.01; // 近面距离
        camera.far = 1001; // 远面距离
        camera.position.set(30, 40, 30) // 设置摄像机在threejs坐标系中的位置
        camera.lookAt(0, 0, 0) // 摄像机的指向
        camera.updateProjectionMatrix(); // 更新摄像机投影矩阵,在任何参数被改变以后必须被调用
    }, [render, scence])

    // 初始化环境光
    const initLight = () => {
        const ambLight = new THREE.AmbientLight('#ffffff', 0.3) // 基本光源

        /**
         * 设置聚光灯相关的的属性
         */
        const spotLight = new THREE.SpotLight(0xFFFFFF); // 聚光灯
        spotLight.position.set(50, 80, 15);
        spotLight.castShadow = true; // 只有该属性为true时,该点光源允许产生阴影,并且下列属性可用
        spotLight.shadow.mapSize = new THREE.Vector2(1024, 1024);
        scence.add(ambLight,spotLight); // 向场景中添加光源
    }

    // 初始化模型
    const initMesh = () => {
        /**
         * 创建地板
         */
        const planeGeometry = new THREE.PlaneGeometry(60, 20); // 创建平面几何体
        const planeMaterial = new THREE.MeshLambertMaterial({ // 一种非光泽表面的材质,没有镜面高光
            color: 0xAAAAAA
        });
        const plane = new THREE.Mesh(planeGeometry, planeMaterial); // 创建地板模型
        plane.rotation.x = -0.5 * Math.PI; // 默认平行于xoy面,沿着X轴旋转-90°至xoz面
        plane.receiveShadow = true;
        scence.add(plane); // 向场景中添加创建的地板

        /**
         * 创建球体
         */
        const sphereGeometry = new THREE.SphereGeometry(4, 20, 20); // 球状几何体
        const sphereMaterial = new THREE.MeshLambertMaterial({ color: 0x7777FF});
        const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial); // 向场景中添加创建的球模型
        sphere.castShadow = true; // 允许接受阴影
        sphere.position.set(20, 4, 2); // 球模型在坐标系中位置
        scence.add(sphere); // 向场景中添加光源

        /**
         * 创建立方体
         */
        const cubeGeometry = new THREE.BoxGeometry(5,5,5);
        const cubeMaterial = new THREE.MeshLambertMaterial({ color:'blue'})
        const cube = new THREE.Mesh(cubeGeometry,cubeMaterial);
        cube.castShadow = true;
        cube.position.set(-20,2.5,0);
        scence.add(cube)

        meshList.push(sphere,cube); // 将球体、立方体加入容器

    }

    const cubrRoatetSpeed = 0.03;
    const sphereRotateSpeed = 0.03

     // 渲染器执行渲染
     const renderScene = useCallback(() => {
        // 球体添加弹跳效果
        step += sphereRotateSpeed
        meshList[0].position.x = 20 +(10 * Math.cos(step)); //水平方向为余弦曲线
        meshList[0].position.y = 4 + (10 * Math.abs(Math.sin(step))); // 竖直方向为正弦曲线

        // 立方体添加旋转效果
        meshList[1].rotation.x += cubrRoatetSpeed;
        meshList[1].rotation.y += cubrRoatetSpeed;
        meshList[1].rotation.z += cubrRoatetSpeed;

        controls.update(); // 鼠标交互更新

        timer.current = window.requestAnimationFrame(() => renderScene()); // 启动动画,见interactive.md
       
        render.render(scence, camera);
    }, [render])

    return (
        <div className='page' ref={page}></div>
    )
};

export default App
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 12
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值