前面学了 CSS3DRenderer 一直没练习下:

这节我们就来做一个实战:

我们来实现一个这样的电脑,并且是可以操作的,屏幕可以点击、滚动那种。
这种需求很明显要用 CSS3DRenderer 结合网页来实现。
这里网页渲染我们用 React 前端框架。
创建项目:
npx create-vite css3d-computer

选择 react、js 来创建 vite 项目。
进入项目,安装依赖:
pnpm install
pnpm install --save three
pnpm install --save-dev @types/three
去掉 StrictMode 和 index.css

然后改一下 App.jsx
import { useEffect, useRef, useState } from 'react';
import { init } from './3d-init'
import './App.css'
function App() {
useEffect(() => {
const dom = document.getElementById('content');
const { scene } = init(dom);
return () => {
dom.innerHTML = '';
}
}, []);
return <div>
<div id="main">
<div id="content">
</div>
</div>
</div>
}
export default App
在 App.css 写下样式:
body {
margin: 0;
}
然后来初始化 3d 场景:
创建 3d-init.js
import * as THREE from 'three';
import {
OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh';
export function init(dom) {
const scene = new THREE.Scene();
scene.add(mesh);
const axesHelper = new THREE.AxesHelper(500);
scene.add(axesHelper);
const directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(500, 400, 300);
scene.add(directionalLight);
const ambientLight = new THREE.AmbientLight(0xffffff);
scene.add(ambientLight);
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 10000);
camera.position.set(0, 500, 500);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(width, height);
const controls = new OrbitControls(camera, renderer.domElement);
function render(time) {
controls.update(time);
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
dom.append(renderer.domElement);
window.onresize = function () {
const width = window.innerWidth;
const height = window.innerHeight;
renderer.setSize(width,height);
camera.aspect = width / height;
camera.updateProjectionMatrix();
};
return {
scene,
renderer,
controls
}
}
创建 mesh.js
import * as THREE from 'three';
const group = new THREE.Group();
const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshLambertMaterial({
color: 'orange'
});
const mesh = new THREE.Mesh(geometry, material);
group.add(mesh);
export default group;
我们先跑起来看下:
npm run dev


然后从 sketchfab 找个电脑模型:
https://sketchfab.com/3d-models/desktop-monitor-low-poly-a30bb55b7e774b1fa696edbee9588835#download

下载下来放到 public 目录:


改下 mesh.js 加载下模型:
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
const mesh = new THREE.Group();
loader.load("./monitor.glb", function (gltf) {
console.log(gltf);
mesh.add(gltf.scene);
gltf.scene.scale.set(300, 300, 300);
})
export default mesh;

调整下相机的位置和 lookAt:

相机位置是从 x 轴正方向看过去,焦点在电脑的中心,差不多 100 高度的位置。
注意,lookAt 改了要修改 controls 的 target,不然会重置到 0,0,0

camera.position.set(600, 500, 0);
camera.lookAt(0, 100, 0);
controls.target.set(0, 100, 0);

这样就好了。
改一下背景色:

renderer.setClearColor('lightyellow');

电脑后面没有反光,有点假。
我们调一下平行光位置和强度:

const directionalLight = new THREE.DirectionalLight(0xffffff, 10);
directionalLight.position.set(-100, 1000, 0);
directionalLight.lookAt(0, 0, 0);
灯光在电脑上方偏后的位置,强度调为 10

这样就好多了。
然后我们加个办公桌:
https://sketchfab.com/3d-models/computer-desk-05353724b7884bfb81211c7033a57fd4

下载下来放 public 目录下:

加载进来:

loader.load("./desk.glb", function (gltf) {
console.log(gltf);
mesh.add(gltf.scene);
gltf.scene.scale.set(200, 200, 200);
})

调一下旋转角度:
gltf.scene.rotateY(Math.PI / 2);

有点近了,调远点:


背景色还是改为浅蓝色:
renderer.setClearColor('lightblue');
去掉坐标轴:


然后我们加一下阴影:
给电脑开启投射阴影:

gltf.scene.traverse(obj => {
obj.castShadow = true;
});
renderer 开启阴影渲染:

renderer.shadowMap.enabled = true;
然后给灯光开启投射阴影,设置下阴影矩阵:

directionalLight.castShadow = true;
directionalLight.shadow.camera.left = -200;
directionalLight.shadow.camera.right = 200;
directionalLight.shadow.camera.top = 100;
directionalLight.shadow.camera.bottom = -100;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 1000;
const cameraHelper = new THREE.CameraHelper(directionalLight.shadow.camera);
scene.add(cameraHelper);

投影矩阵有点小,调大一点:
directionalLight.castShadow = true;
directionalLight.shadow.camera.left = -800;
directionalLight.shadow.camera.right = 800;
directionalLight.shadow.camera.top = 500;
directionalLight.shadow.camera.bottom = -500;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 2000;

现在投影矩阵大小就正好了。
那为啥没有产生阴影呢?
因为没有物体接收阴影啊。
给桌子开启接收阴影。

gltf.scene.traverse(obj => {
obj.receiveShadow = true;
});

这样就有阴影了。
去掉 cameraHelper 看一下:

场景搭建完成。
案例代码上传了小册仓库
总结
这节我们搭建了电脑、桌面的场景。
从 sketchfab.com 找的电脑和桌子的模型,调节了相机的角度和 lookAt 还有灯光位置,加上了阴影。
下节我们来用 CSS3DRenderer 加上电脑屏幕的内容。