Three.js基础光源、摄像机等快速入门(附VR看车代码)

  • 场景(Scene):场景是 Three.js 中所有对象的容器,包括灯光、相机和几何体等。

  • 相机(Camera):Three.js 提供了几种类型的相机,包括透视相机(PerspectiveCamera)和正交相机(OrthographicCamera)。相机决定了观察场景的视角。

  • 几何体(Geometry):几何体定义了 3D 对象的形状,可以是简单的形状如立方体、球体或更复杂的自定义形状。

  • 网格(Mesh):网格是几何体和材质的组合,用于在场景中显示 3D 对象。

  • 材质(Material):材质定义了网格的外观,可以是基本的材质如基本材质(MeshBasicMaterial)或更高级的材质如着色器材质(ShaderMaterial)。

  • 灯光(Light):Three.js 支持多种类型的灯光,包括环境光(AmbientLight)、点光源(PointLight)、聚光灯(SpotLight)和定向光(DirectionalLight)。

  • 渲染器(Renderer):Three.js 使用 WebGLRenderer 来渲染场景。渲染器负责将场景中的所有对象转换为浏览器可以理解的格式。

  • 动画(Animation):Three.js 允许你通过 JavaScript 来控制对象的位置、旋转和缩放,从而创建动画效果。

  • 纹理(Texture):纹理可以应用于材质上,给 3D 对象添加更丰富的视觉效果。

  • 控制(Controls):Three.js 提供了多种控制类,如 OrbitControls,允许用户通过鼠标或触摸操作来浏览场景。

  • 物理引擎(Physics):虽然 Three.js 本身不包含物理引擎,但可以与像 Cannon.js 或 Ammo.js 这样的物理引擎库结合使用,来模拟真实世界的物理效果。

  • 扩展和插件:Three.js 有一个活跃的社区,提供了许多扩展和插件,用于添加额外的功能,如粒子系统、动画库等。

import * as Three from 'three';
import { TrackballControls } from 'three/addons/controls/TrackballControls.js';
import './main.css';
let scene,camera,render,cube,geometry,material,controls;
// 创建射线
const raycaster = new Three.Raycaster();
// 创建二维平面
const pointer = new Three.Vector2();
// 鼠标移动
function onPointerMove(event){
    pointer.x = (event.clientX/window.innerWidth)*2-1;
    pointer.y = -(event.clientY/window.innerHeight)*2+1;
    raycaster.setFromCamera(pointer,camera);
    const intersects = raycaster.intersectObjects(scene.children);
    if(intersects.length>0){
        intersects[0].object.material.color.set(0xff0000);
    }
}
window.addEventListener("pointermove",onPointerMove);// 鼠标移动事件
// 点击屏幕时判断是否光线命中物体 点击几何体使其旋转
window.addEventListener("click",function(){
    const intersects = raycaster.intersectObjects([cube]);
    if(intersects.length>0){
        cube.rotation.x += 0.1;
        cube.rotation.y += 0.1;
        cube.rotation.z += 0.1;
    }
})
// 初始化渲染器
function initRenderer(){
    render = new Three.WebGLRenderer();
    render.setSize(window.innerWidth,window.innerHeight);
    document.body.appendChild(render.domElement);
}
// 初始化场景
function initScene(){
    scene = new Three.Scene();
    // 设置背景色
    render.setClearColor("#ccc");
    // 设置坐标轴
    const axesHelper = new Three.AxesHelper(100);
    scene.add(axesHelper);
    // 添加光源 平行光
    const directionalLight = new Three.DirectionalLight("lightblue");
    // 设置光源位置
    directionalLight.position.set(0,30,0);
    scene.add(directionalLight);
    // 添加光源 环境光
    // const ambientLight = new Three.AmbientLight("lightblue");
    // ambientLight.position.set(40,0,0);
    // scene.add(ambientLight);
    // 添加光源 点光源
    // const pointLight = new Three.PointLight("lightblue");
    // pointLight.position.set(0,30,0);
    // scene.add(pointLight);
    // 添加光源 聚光灯
    // const spotLight = new Three.SpotLight("red");
    // spotLight.position.set(30,0,0);
    // scene.add(spotLight);
}
// 初始化相机
function initCamera(){
    camera = new Three.PerspectiveCamera(45,window.innerWidth/window.innerHeight,1,1000);
    camera.position.z = 10;
    // 加入滚轮控制器
    //controls = new Three.OrbitControls(camera,render.domElement);
    controls = new TrackballControls(camera,render.domElement);
}
// 初始化几何体
function initGeometry(){
    geometry = new Three.BoxGeometry(2,2,2);
    // 创建材质
    // 使用指定材质才能看见光源效果
    // material = new Three.MeshBasicMaterial({color:(125, 182, 191)});
    material = new Three.MeshBasicMaterial({color:(125, 182, 191)});
    // 创建纹理
    const texture = new Three.TextureLoader().load('https://threejs.org/examples/textures/crate.gif');
    // 纹理和材质结合
    material = new Three.MeshPhongMaterial({map:texture,side:Three.DoubleSide});
    // 材质和几何体结合
    cube = new Three.Mesh(geometry,material);
    scene.add(cube);
}
// 创建动画旋转立方体
function animate(){
    cube.rotation.x += 0.01;
    cube.rotation.y += 0.01;
    cube.rotation.z += 0.01;
    render.render(scene,camera);
    controls.update();
    render.render(scene,camera);
    requestAnimationFrame(animate);
}
function init(){
    initRenderer();
    initScene();
    initCamera();
    initGeometry();
    animate();
}
init();
// 车模型:灯光一切调整好终极版本
import * as THREE from 'three'
import { BoxGeometry, DoubleSide } from 'three';
import { Mesh } from 'three';
import { AxesHelper } from 'three';
import { TextureLoader } from 'three';
import { GridHelper } from 'three';
import { MeshBasicMaterial } from 'three';
import { WebGLRenderer } from 'three';
import { PerspectiveCamera } from 'three';
import { Scene } from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'

import pkq from './public/pkq1.jpg'

import Lamborghini from './public/Lamborghini.glb'
import { AmbientLight } from 'three';
import { PlaneGeometry } from 'three';
import { MeshPhysicalMaterial } from 'three';
import { SpotLight } from 'three';
import { CylinderGeometry } from 'three';
import GUI from 'lil-gui';
import { Vector2 } from 'three';
import { Raycaster } from 'three';
import messi from './public/messi.JPG'
import { RectAreaLightHelper } from 'three/examples/jsm/helpers/RectAreaLightHelper'
import { RectAreaLightUniformsLib } from 'three/examples/jsm/lights/RectAreaLightUniformsLib.js';


const TWEEN = require('@tweenjs/tween.js')


let scene, camera, renderer, controls, mesh;
let doors = []

let carStatus;
// 车身材质
let bodyMaterial = new THREE.MeshPhysicalMaterial({
    color: "#6e2121",
    metalness: 1,
    roughness: 0.5,
    clearcoat: 1.0,
    clearcoatRoughness: 0.03,
});


// 玻璃材质
let glassMaterial = new THREE.MeshPhysicalMaterial({
    color: "#793e3e",
    metalness: 0.25,
    roughness: 0,
    transmission: 1.0 //透光性.transmission属性可以让一些很薄的透明表面,例如玻璃,变得更真实一些。
});

// 初始化场景
function initScene() {
    scene = new Scene()
    RectAreaLightUniformsLib.init();
    // scene.add(new AxesHelper(3))
}

function initCamera() {
    camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)

    camera.position.set(4.25, 1.4, -4.5);

}

function initRenderer() {
    renderer = new WebGLRenderer({
        antialias: true
    })
    renderer.setSize(window.innerWidth, window.innerHeight)
    // 支持阴影
    renderer.shadowMap.enabled = true
    renderer.outputEncoding = THREE.sRGBEncoding;
    renderer.toneMapping = THREE.ACESFilmicToneMapping;


    document.body.appendChild(renderer.domElement)
}

function loadCarModal() {
    new GLTFLoader().load(Lamborghini, function (gltf) {
        console.log(gltf)
        const carModel = gltf.scene

        carModel.rotation.y = Math.PI

        carModel.traverse(obj => {
            if (obj.name === 'Object_103' || obj.name == 'Object_64' || obj.name == 'Object_77') {
                // 车身
                obj.material = bodyMaterial

            } else if (obj.name === 'Object_90') {
                // 玻璃
                obj.material = glassMaterial
            } else if (obj.name === 'Empty001_16' || obj.name === 'Empty002_20') {
                // 门
                doors.push(obj)
            } else {


            }
            obj.castShadow = true;
        })


        scene.add(carModel)
    })
}


function initAmbientLight() {
    var ambientLight = new AmbientLight('#fff', 0.5)
    scene.add(ambientLight)
}

function initGripHelper() {
    let grid = new GridHelper(20, 40, 'red', 0xffffff)
    grid.material.opacity = 0.2
    grid.material.transparent = true
    scene.add(grid)
}

function initFloor() {
    const floorGeometry = new PlaneGeometry(20, 20)
    const material = new MeshPhysicalMaterial({
        side: DoubleSide,
        color: 0x808080,
        metalness: 0,
        roughness: 0.1
    })

    const floorMesh = new Mesh(floorGeometry, material)
    floorMesh.rotation.x = Math.PI / 2
    floorMesh.receiveShadow = true;
    scene.add(floorMesh)
}

function initSpotLight() {
    // 添加头顶聚光灯
    const bigSpotLight = new SpotLight("#ffffff", 0.5);

    bigSpotLight.angle = Math.PI / 8; //散射角度,跟水平线的家教
    bigSpotLight.penumbra = 0.2;  // 聚光锥的半影衰减百分比
    bigSpotLight.decay = 2; // 纵向:沿着光照距离的衰减量。
    bigSpotLight.distance = 30;
    bigSpotLight.shadow.radius = 10;
    // 阴影映射宽度,阴影映射高度
    bigSpotLight.shadow.mapSize.set(4096, 4096);

    bigSpotLight.position.set(-5, 10, 1);
    // 光照射的方向
    bigSpotLight.target.position.set(0, 0, 0);
    bigSpotLight.castShadow = true;
    // bigSpotLight.map = bigTexture
    scene.add(bigSpotLight);
}

function initCylinder() {
    const geometry = new CylinderGeometry(10, 10, 20, 20)
    const material = new MeshPhysicalMaterial({
        color: 0x6c6c6c,
        side: DoubleSide
    })

    const cylinder = new Mesh(geometry, material)
    scene.add(cylinder)
}

function initController() {
    controls = new OrbitControls(camera, renderer.domElement)

    controls.enableDamping = true

    controls.maxDistance = 9
    controls.minDistance = 1

    controls.minPolarAngle = 0
    controls.maxPolarAngle = 80 / 360 * 2 * Math.PI

    // controls.target.set(0, 0.5, 0)

}

function initGUI() {
    var obj = {
        bodyColor: '#6e2121',
        glassColor: '#aaaaaa',
        carOpen,
        carClose,
        carIn,
        carOut
    };

    const gui = new GUI();
    gui.addColor(obj, "bodyColor").name('车身颜色').onChange((value) => {
        bodyMaterial.color.set(value)
    })

    gui.addColor(obj, "glassColor").name('玻璃颜色').onChange((value) => {
        glassMaterial.color.set(value)
    })

    gui.add(obj, "carOpen").name('打开车门')
    gui.add(obj, "carClose").name('关门车门')

    gui.add(obj, "carIn").name('车内视角')
    gui.add(obj, "carOut").name('车外视角')
}

function carOpen() {
    carStatus = 'open'
    for (let i = 0; i < doors.length; i++) {
        setAnimationDoor({ x: 0 }, { x: Math.PI / 3 }, doors[i])
    }
}

function carClose() {
    carStatus = 'close'
    for (let i = 0; i < doors.length; i++) {
        setAnimationDoor({ x: Math.PI / 3 }, { x: 0 }, doors[i])
    }
}

function carIn() {
    setAnimationCamera({ cx: 4.25, cy: 1.4, cz: -4.5, ox: 0, oy: 0.5, oz: 0 }, { cx: -0.27, cy: 0.83, cz: 0.60, ox: 0, oy: 0.5, oz: -3 });
}

function carOut() {
    setAnimationCamera({ cx: -0.27, cy: 0.83, cz: 0.6, ox: 0, oy: 0.5, oz: -3 }, { cx: 4.25, cy: 1.4, cz: -4.5, ox: 0, oy: 0.5, oz: 0 });
}

function setAnimationDoor(start, end, mesh) {
    const tween = new TWEEN.Tween(start).to(end, 1000).easing(TWEEN.Easing.Quadratic.Out)
    tween.onUpdate((that) => {
        mesh.rotation.x = that.x
    })
    tween.start()
}

function setAnimationCamera(start, end) {
    const tween = new TWEEN.Tween(start).to(end, 3000).easing(TWEEN.Easing.Quadratic.Out)
    tween.onUpdate((that) => {
        //  camera.postition  和 controls.target 一起使用
        camera.position.set(that.cx, that.cy, that.cz)
        controls.target.set(that.ox, that.oy, that.oz)
    })
    tween.start()
}

function createSpotlight(color) {
    const newObj = new THREE.SpotLight(color, 2);
    newObj.castShadow = true;
    newObj.angle = Math.PI / 6;;
    newObj.penumbra = 0.2;
    newObj.decay = 2;
    newObj.distance = 50;
    return newObj;
}

function initMessiLight() {
    const spotLight1 = createSpotlight('#ffffff');
    const texture = new TextureLoader().load(messi)

    spotLight1.position.set(0, 3, 0);
    spotLight1.target.position.set(-10, 3, 10)

    spotLight1.map = texture
    lightHelper1 = new THREE.SpotLightHelper(spotLight1);
    scene.add(spotLight1);
}

function initMutilColor() {
    //创建三色光源
    rectLight1 = new THREE.RectAreaLight(0xff0000, 50, 1, 10);
    rectLight1.position.set(15, 10, 15);
    rectLight1.rotation.x = -Math.PI / 2
    rectLight1.rotation.z = -Math.PI / 4
    scene.add(rectLight1);


    rectLight2 = new THREE.RectAreaLight(0x00ff00, 50, 1, 10);
    rectLight2.position.set(13, 10, 13);
    rectLight2.rotation.x = -Math.PI / 2
    rectLight2.rotation.z = -Math.PI / 4
    scene.add(rectLight2);


    rectLight3 = new THREE.RectAreaLight(0x0000ff, 50, 1, 10);
    rectLight3.position.set(11, 10, 11);
    rectLight3.rotation.x = -Math.PI / 2
    rectLight3.rotation.z = -Math.PI / 4
    scene.add(rectLight3);

    scene.add(new RectAreaLightHelper(rectLight1));
    scene.add(new RectAreaLightHelper(rectLight2));
    scene.add(new RectAreaLightHelper(rectLight3));


    startColorAnim()
}



function startColorAnim() {
    const carTween = new TWEEN.Tween({ x: -5 }).to({ x: 25 }, 2000).easing(TWEEN.Easing.Quadratic.Out);
    carTween.onUpdate(function (that) {
        rectLight1.position.set(15 - that.x, 10, 15 - that.x)
        rectLight2.position.set(13 - that.x, 10, 13 - that.x)
        rectLight3.position.set(11 - that.x, 10, 11 - that.x)
    });
    carTween.onComplete(function (that) {
        rectLight1.position.set(-15, 10, 15);
        rectLight2.position.set(-13, 10, 13);
        rectLight3.position.set(-11, 10, 11);

        rectLight1.rotation.z = Math.PI / 4
        rectLight2.rotation.z = Math.PI / 4
        rectLight3.rotation.z = Math.PI / 4
    })
    carTween.repeat(10)


    const carTween2 = new TWEEN.Tween({ x: -5 }).to({ x: 25 }, 2000).easing(TWEEN.Easing.Quadratic.Out);
    carTween2.onUpdate(function (that) {
        rectLight1.position.set(-15 + that.x, 10, 15 - that.x)
        rectLight2.position.set(-13 + that.x, 10, 13 - that.x)
        rectLight3.position.set(-11 + that.x, 10, 11 - that.x)
    });
    carTween2.onComplete(function (that) {
        rectLight1.position.set(15, 10, 15);
        rectLight2.position.set(13, 10, 13);
        rectLight3.position.set(11, 10, 11);
        rectLight1.rotation.z = - Math.PI / 4
        rectLight2.rotation.z = - Math.PI / 4
        rectLight3.rotation.z = - Math.PI / 4
    })

    carTween.start();
}

function init() {
    initScene()
    initCamera()
    initRenderer()
    loadCarModal()
    initAmbientLight()
    initFloor()
    initSpotLight()

    initMessiLight()
    initCylinder()
    initController()
    initGUI()

    initMutilColor()
}

init()


function render(time) {

    // if (mesh.position.x > 3) {

    // } else {
    //     mesh.position.x += 0.01
    // }

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

    TWEEN.update(time)
    controls.update()
}

render()

window.addEventListener('resize', function () {
    // camera
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()

    // renderer
    renderer.setSize(window.innerWidth, window.innerHeight)
})


window.addEventListener('click', onPointClick);
function onPointClick(event) {
    let pointer = {}
    pointer.x = (event.clientX / window.innerWidth) * 2 - 1;
    pointer.y = - (event.clientY / window.innerHeight) * 2 + 1;


    var vector = new Vector2(pointer.x, pointer.y)
    var raycaster = new Raycaster()
    raycaster.setFromCamera(vector, camera)
    let intersects = raycaster.intersectObjects(scene.children);


    intersects.forEach((item) => {
        if (item.object.name === 'Object_64' || item.object.name === 'Object_77') {
            if (!carStatus || carStatus === 'close') {
                carOpen()
            } else {
                carClose()
            }
            console.log(intersects)
        }
    })


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值