Three.js实现模拟看房效果

258 阅读1分钟

一:threeJs 简介

  1. ThreeJs是采用javaScript编写的类库
  2. 需要理解的核心概念:场景,相机,渲染,几何体
  3. 虚拟场景 + 虚拟相机 -> 渲染器执行渲染操作 = 得到渲染结果
  4. 场景就是一个显示呈现的舞台
  5. 浏览器端呈现的内容都是相机拍摄
  6. 渲染器决定了内容如何呈现至屏幕
  7. 几何体就是我们需要绘制显示的物体
  8. 按照使用预览官网:www.webgl3d.cn/

f98210a0a7bc4295b8e3b1e17e1c6a56~tplv-k3u1fbpfcp-zoom-in-crop-mark_1512_0_0_0.webp

二:threeJs 绘制立方体

<script type="module">
    // 01:场景
    // 02:相机:分类 位置
    // 03:渲染器:大小 颜色
    // 04:几何体

    // 场景
    const scene = new THREE.Scene()

    // three.js里面有几种不同的相机,这里我们使用的是PerspectiveCamera(透视摄像机)。
    // 第一个参数:第一个参数是视野角度(FOV)。视野角度就是无论在什么时候,你所能在显示器上看到的场景的范围,它的单位是角度(与弧度区分开)。
    // 第二个参数:第二个参数是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的值。比如说,当你在一个宽屏电视上播放老电影时,可以看到图像仿佛是被压扁的。
    // 第三个参数:近截面(near)。
    // 第四个参数:远截面(far)。
    const camera = new THREE.PerspectiveCamera(
        45,
        window.innerWidth / window.innerHeight,
        1,
        1000
    )

    // 渲染器,除了WebGLRenderer渲染器之外,three.js还提供了其他几种渲染器,如果浏览器过于老旧,或者不支持WebGL时,可以使用这几种渲染器进行降级。
    const renderer = new THREE.WebGLRenderer({
        antialias: true,
    })

    renderer.setClearColor(0xffffff) // 设置颜色及其透明度
    renderer.setSize(window.innerWidth, window.innerHeight) // 设置渲染器尺寸

    document.body.appendChild(renderer.domElement) // renderer.domElement 是renderer(渲染器)的dom元素,这一步是将渲染器的dom元素添加到HTML文档中

    const geometry = new THREE.BoxGeometry(1, 1, 1) // BoxGeometry(立方体)对象
    const material = new THREE.MeshBasicMaterial({
        color: 0x285b43,
        wireframe: true, // 框线
    }) // Three.js自带了几种材质,在这里我们使用的是MeshBasicMaterial

    // 新建一个网格传入几何体和材质
    const cube = new THREE.Mesh(geometry, material) // 我们需要一个Mesh(网格)。 网格包含一个几何体以及作用在此几何体上的材质,我们可以直接将网格对象放入到我们的场景中,并让它在场景中自由移动
    scene.add(cube) // 将物体添加到场景中,默认情况下,当我们调用scene.add()的时候,物体将会被添加到(0,0,0)坐标。但将使得摄像机和立方体彼此在一起。为了防止这种情况的发生,我们只需要将摄像机稍微向外移动一些即可。
    camera.position.z = 5 // 移动相机

    function animate() {
        requestAnimationFrame(animate) // 调用requestAnimationFrame 执行动画
        cube.rotation.y += 0.01
        cube.rotation.x += 0.01
        renderer.render(scene, camera) // 执行渲染器
    }

    animate() // 执行函数
</script>

三:threeJs 材质与相机控制

<script>
    // 定义全局变量
    let scene, camera, geometry, mesh, renderer, controls

    // 初始化场景
    function initScene() {
        scene = new THREE.Scene() // 新建场景
        const axesHelper = new THREE.AxesHelper(100) // 用于简单模拟3个坐标轴的对象.红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴.
        scene.add(axesHelper) // 往场景里面添加坐标
    }

    // 初始化相机
    function initCamera() {
        // three.js里面有几种不同的相机,这里我们使用的是PerspectiveCamera(透视摄像机)。
        camera = new THREE.PerspectiveCamera(
            45,
            window.innerWidth / window.innerHeight,
            1,
            1000
        )
        camera.position.set(0, 0, 15) // 移动相机z坐标

        // 轨迹球控制器,可以使得相机围绕目标进行轨道运动。
        controls = new THREE.TrackballControls(
            camera,
            renderer.domElement
        )
    }

    // 初始化渲染器
    function initRenderer() {
        // 渲染器,除了WebGLRenderer渲染器之外,three.js还提供了其他几种渲染器,如果浏览器过于老旧,或者不支持WebGL时,可以使用这几种渲染器进行降级。
        renderer = new THREE.WebGLRenderer({ antialias: true })
        // 设置渲染器尺寸
        renderer.setSize(window.innerWidth, window.innerHeight)
        // 设置渲染器设备像素比
        renderer.setPixelRatio(window.devicePixelRatio)
        // renderer.domElement 是renderer(渲染器)的dom元素,这一步是将渲染器的dom元素添加到HTML文档中
        document.body.appendChild(renderer.domElement)
    }

    // 初始化模型
    function initMesh() {
        geometry = new THREE.BoxGeometry(2, 2, 2) // BoxGeometry是四边形的原始几何类
        // material = new THREE.MeshNormalMaterial() // 一种把法向量映射到RGB颜色的材质。
        const texture = new THREE.TextureLoader().load('img/crate.gif') // 加载texture的一个类。 内部使用ImageLoader来加载文件。
        // 一个以简单着色(平面或线框)方式来绘制几何体的材质。
        material = new THREE.MeshBasicMaterial({
            map: texture, // 颜色贴图
            side: THREE.DoubleSide, // 定义将要渲染哪一面 - 正面,背面或两者。 默认为THREE.FrontSide。其他选项有THREE.BackSide, THREE.DoubleSide 和 THREE.TwoPassDoubleSide。
        })
        mesh = new THREE.Mesh(geometry, material) // 新建一个网格传入几何体和材质 构造器 Mesh( geometry : BufferGeometry, material : Material )
        scene.add(mesh) // 往场景中添加构造器
    }

    // 初始化动画
    function animate() {
        requestAnimationFrame(animate)
        controls.update() // 执行轨迹球控制器
        renderer.render(scene, camera) // 执行渲染器,传入场景和相机
    }

    // 定义初始化方法
    function init() {
        initRenderer()
        initScene()
        initCamera()
        initMesh()
        animate()
    }

    init()
</script>

四:threeJs 光源操作

<script>
    // 定义全局变量
    let scene, camera, geometry, mesh, renderer, controls

    // 初始化场景
    function initScene() {
        scene = new THREE.Scene()
        const axesHelper = new THREE.AxesHelper(100)
        scene.add(axesHelper)

        // 平行光是沿着特定方向发射的光。这种光的表现像是无限远,从它发出的光线都是平行的。常常用平行光来模拟太阳光 的效果; 太阳足够远,因此我们可以认为太阳的位置是无限远,所以我们认为从太阳发出的光线也都是平行的。
        // const directionalLight = new THREE.DirectionalLight('red') // 平行光

        // const ambientLight = new THREE.AmbientLight('orange') // 环境光会均匀的照亮场景中的所有物体。

        // const pointLight = new THREE.PointLight('green') //点光源 从一个点向各个方向发射的光源。一个常见的例子是模拟一个灯泡发出的光。

        // const spotLight = new THREE.SpotLight('lightblue') // 聚光灯 光线从一个点沿一个方向射出,随着光线照射的变远,光线圆锥体的尺寸也逐渐增大。

        const hemisphereLight = new THREE.HemisphereLight('red') // 半球光 光源直接放置于场景之上,光照颜色从天空光线颜色渐变到地面光线颜色。

        hemisphereLight.position.set(0, 30, 0)
        scene.add(hemisphereLight)
    }

    // 初始化相机
    function initCamera() {
        camera = new THREE.PerspectiveCamera(
            45,
            window.innerWidth / window.innerHeight,
            1,
            1000
        )
        camera.position.set(0, 0, 15)
        controls = new THREE.TrackballControls(
            camera,
            renderer.domElement
        )
    }

    // 初始化渲染器
    function initRenderer() {
        renderer = new THREE.WebGLRenderer({ antialias: true })
        renderer.setSize(window.innerWidth, window.innerHeight)
        renderer.setPixelRatio(window.devicePixelRatio)
        document.body.appendChild(renderer.domElement)
    }

    // 初始化模型
    function initMesh() {
        geometry = new THREE.SphereGeometry(3, 26, 26) // 球缓冲几何体  一个用于生成球体的类。
        const texture = new THREE.TextureLoader().load('img/crate.gif') // 加载texture的一个类。 内部使用ImageLoader来加载文件。
        // Phong网格材质,更加适合打光效果
        material = new THREE.MeshPhongMaterial({
            map: texture,
            side: THREE.DoubleSide,
        })
        mesh = new THREE.Mesh(geometry, material)
        scene.add(mesh)
    }

    // 初始化动画
    function animate() {
        requestAnimationFrame(animate)
        controls.update()
        renderer.render(scene, camera)
    }

    // 定义初始化方法
    function init() {
        initRenderer()
        initScene()
        initCamera()
        initMesh()
        animate()
    }

    init()
</script>

五:threeJs 精灵材质与交互操作

<script>
    // 定义全局变量
    let scene, camera, geometry, mesh, renderer, controls

    const raycaster = new THREE.Raycaster() // 光线投射Raycaster 这个类用于进行raycasting(光线投射)。 光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。
    const mouse = new THREE.Vector2() //数学库 二维向量

    function onMouseMove(event) {
        // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)

        mouse.x = (event.clientX / window.innerWidth) * 2 - 1
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
    }

    window.addEventListener('mousemove', onMouseMove, false)

    window.addEventListener(
        'click',
        function () {
            // 计算物体和射线的焦点
            const intersects = raycaster.intersectObjects([mesh]) // 检查与射线相交的物体。
            // 碰到了
            if (intersects.length > 0) {
                mesh.rotation.x += 01
            }
        },
        false
    )

    // 初始化场景
    function initScene() {
        scene = new THREE.Scene()
        const axesHelper = new THREE.AxesHelper(100)
        scene.add(axesHelper)
    }

    // 初始化相机
    function initCamera() {
        camera = new THREE.PerspectiveCamera(
            45,
            window.innerWidth / window.innerHeight,
            1,
            1000
        )
        camera.position.set(0, 0, 15)
        controls = new THREE.TrackballControls(
            camera,
            renderer.domElement
        )
    }

    // 初始化渲染器
    function initRenderer() {
        renderer = new THREE.WebGLRenderer({ antialias: true })
        renderer.setSize(window.innerWidth, window.innerHeight)
        renderer.setPixelRatio(window.devicePixelRatio)
        document.body.appendChild(renderer.domElement)
    }

    // 初始化模型
    function initMesh() {
        // const map = new THREE.TextureLoader().load('img/icon.png')
        // const material = new THREE.SpriteMaterial({ map: map, color: 0xffffff })
        // const sprite = new THREE.Sprite(material)
        // scene.add(sprite)

        geometry = new THREE.BoxGeometry(2, 2, 2)
        // material = new THREE.MeshNormalMaterial()
        const texture = new THREE.TextureLoader().load('img/crate.gif')
        material = new THREE.MeshBasicMaterial({
            map: texture,
            side: THREE.DoubleSide,
        })
        mesh = new THREE.Mesh(geometry, material)
        scene.add(mesh)
    }

    // 初始化动画
    function animate() {
        requestAnimationFrame(animate)
        controls.update()
        renderer.render(scene, camera)
    }

    // 定义初始化方法
    function init() {
        initRenderer()
        initScene()
        initCamera()
        initMesh()
        animate()
    }

    init()
</script>

六:threeJsVr全景拼接

<script>
	// 定义全局变量
	let scene, camera, geometry, mesh, renderer, controls

	// 初始化渲染器
	function initRenderer() {
		renderer = new THREE.WebGLRenderer({ antialias: true })
		renderer.setSize(window.innerWidth, window.innerHeight)
		renderer.setPixelRatio(window.devicePixelRatio)
		document.body.appendChild(renderer.domElement)
	}

	// 初始化场景
	function initScene() {
		scene = new THREE.Scene()
		const axesHelper = new THREE.AxesHelper(100)
		scene.add(axesHelper)
	}

	// 初始化相机
	function initCamera() {
		camera = new THREE.PerspectiveCamera(
			45,
			window.innerWidth / window.innerHeight,
			1,
			1000
		)
		camera.position.set(0, 0, 15)
		controls = new THREE.TrackballControls(camera, renderer.domElement)
	}

	// 初始化模型
	function initMesh() {
		// 前面
		const geometryF = new THREE.PlaneGeometry(4, 4) // 一个用于生成平面几何体的类。
		const materialF = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_f.jpg'),
			side: THREE.DoubleSide,
		})
		const meshF = new THREE.Mesh(geometryF, materialF)
		meshF.rotation.y = (180 * Math.PI) / 180 // 内部旋转
		meshF.position.z = 2
		scene.add(meshF)

		// 后面
		const geometryB = new THREE.PlaneGeometry(4, 4) // 一个用于生成平面几何体的类。
		const materialB = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_b.jpg'),
			side: THREE.DoubleSide,
		})
		const meshB = new THREE.Mesh(geometryB, materialB)
		meshB.position.z = -2
		scene.add(meshB)

		// 左侧
		const geometryL = new THREE.PlaneGeometry(4, 4) // 一个用于生成平面几何体的类。
		const materialL = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_l.jpg'),
			side: THREE.DoubleSide,
		})
		const meshL = new THREE.Mesh(geometryL, materialL)
		meshL.rotation.y = (-90 * Math.PI) / 180
		meshL.position.x = 2
		scene.add(meshL)

		// 右侧
		const geometryR = new THREE.PlaneGeometry(4, 4) // 一个用于生成平面几何体的类。
		const materialR = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_r.jpg'),
			side: THREE.DoubleSide,
		})
		const meshR = new THREE.Mesh(geometryR, materialR)
		meshR.rotation.y = (90 * Math.PI) / 180
		meshR.position.x = -2
		scene.add(meshR)

		// 上面
		const geometryU = new THREE.PlaneGeometry(4, 4) // 一个用于生成平面几何体的类。
		const materialU = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_u.jpg'),
			side: THREE.DoubleSide,
		})
		const meshU = new THREE.Mesh(geometryU, materialU)
		meshU.rotation.x = (90 * Math.PI) / 180
		meshU.rotation.z = (180 * Math.PI) / 180
		meshU.position.y = 2
		scene.add(meshU)

		// 下面
		const geometryD = new THREE.PlaneGeometry(4, 4) // 一个用于生成平面几何体的类。
		const materialD = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_d.jpg'),
			side: THREE.DoubleSide,
		})
		const meshD = new THREE.Mesh(geometryD, materialD)
		meshD.rotation.x = (-90 * Math.PI) / 180
		meshD.rotation.z = (180 * Math.PI) / 180
		meshD.position.y = -2
		scene.add(meshD)
	}

	// 初始化动画
	function animate() {
		requestAnimationFrame(animate)
		controls.update()
		renderer.render(scene, camera)
	}

	// 定义初始化方法
	function init() {
		initRenderer()
		initScene()
		initCamera()
		initMesh()
		animate()
	}

	init()
</script>

七:threeJS 全景看房地标添加

<script>
	// 定义全局变量
	let scene, camera, geometry, mesh, renderer, controls

	// 初始化渲染器
	function initRenderer() {
		renderer = new THREE.WebGLRenderer({ antialias: true })
		renderer.setSize(window.innerWidth, window.innerHeight)
		renderer.setPixelRatio(window.devicePixelRatio)
		document.body.appendChild(renderer.domElement)
	}

	// 初始化场景
	function initScene() {
		scene = new THREE.Scene()
		const axesHelper = new THREE.AxesHelper(100)
		scene.add(axesHelper)
	}

	// 初始化相机
	function initCamera() {
		camera = new THREE.PerspectiveCamera(
			45,
			window.innerWidth / window.innerHeight,
			1,
			1000
		)
		camera.position.set(0, 0, 15)
		controls = new THREE.TrackballControls(camera, renderer.domElement)
	}

	// 初始化模型
	function initMesh() {
		// 前面
		const geometryF = new THREE.PlaneGeometry(4, 4)
		const materialF = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_f.jpg'),
			side: THREE.DoubleSide,
		})
		const meshF = new THREE.Mesh(geometryF, materialF)
		meshF.rotation.y = (180 * Math.PI) / 180
		meshF.position.z = 2
		scene.add(meshF)

		// 后面
		const geometryB = new THREE.PlaneGeometry(4, 4)
		const materialB = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_b.jpg'),
			side: THREE.DoubleSide,
		})
		const meshB = new THREE.Mesh(geometryB, materialB)
		// meshB.rotation.y = 180 * Math.PI / 180
		meshB.position.z = -2
		scene.add(meshB)

		// 左侧
		const geometryL = new THREE.PlaneGeometry(4, 4)
		const materialL = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_l.jpg'),
			side: THREE.DoubleSide,
		})
		const meshL = new THREE.Mesh(geometryL, materialL)
		meshL.rotation.y = (-90 * Math.PI) / 180
		meshL.position.x = 2
		scene.add(meshL)

		// 右侧
		const geometryR = new THREE.PlaneGeometry(4, 4)
		const materialR = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_r.jpg'),
			side: THREE.DoubleSide,
		})
		const meshR = new THREE.Mesh(geometryR, materialR)
		meshR.rotation.y = (90 * Math.PI) / 180
		meshR.position.x = -2
		scene.add(meshR)

		// 上面
		const geometryU = new THREE.PlaneGeometry(4, 4)
		const materialU = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_u.jpg'),
			side: THREE.DoubleSide,
		})
		const meshU = new THREE.Mesh(geometryU, materialU)
		meshU.rotation.x = (90 * Math.PI) / 180
		meshU.rotation.z = (180 * Math.PI) / 180
		meshU.position.y = 2
		scene.add(meshU)

		// 下面
		const geometryD = new THREE.PlaneGeometry(4, 4)
		const materialD = new THREE.MeshBasicMaterial({
			map: new THREE.TextureLoader().load('img/0_d.jpg'),
			side: THREE.DoubleSide,
		})
		const meshD = new THREE.Mesh(geometryD, materialD)
		meshD.rotation.x = (-90 * Math.PI) / 180
		meshD.rotation.z = (180 * Math.PI) / 180
		meshD.position.y = -2
		scene.add(meshD)
	}

	// 初始化动画
	function animate() {
		requestAnimationFrame(animate)
		controls.update()
		renderer.render(scene, camera)
	}

	// 定义初始化方法
	function init() {
		initRenderer()
		initScene()
		initCamera()
		initMesh()
		animate()
	}

	init()
</script>