web3D使用three.js+CSG.js做的墙体添加窗户和门功能
因为之前一直做的是后台,最近接触了下3D,所以用前端three.js做一些项目的工作,代码比较简陋,主要是个人对three.js的一些使用
先展示3D效果模型



本文没有用ThreeBSP.js进行处理,因为我看BSP有版本要求,就没使用,我用的csg.js。
一.使用过程中遇到的问题汇总
1.使用csg对模型进行组合,合并后的材质处理,组合的相对位置处理。
2.添加门和门轴,让门围绕门轴旋转问题。
3.透明窗户的处理
二
1.csg的功能描述 见链接 Constructive Solid Geometry - Three.js Tutorials (sbcode.net)
描述
Union
返回由 A 和 B 组成的新 CSG 实体
A.union(B)
+-------+ +-------+
| | | |
| A | | |
| +--+----+ = | +----+
+----+--+ | +----+ |
| B | | |
| | | |
+-------+ +-------+
Subtract
返回一个新的 CSG 实体,其中 B 从 A 中减去
A.subtract(B)
+-------+ +-------+
| | | |
| A | | |
| +--+----+ = | +--+
+----+--+ | +----+
| B |
| |
+-------+
Intersect
返回 A 和 B 重叠的新 CSG 实体
A.intersect(B)
+-------+
| |
| A |
| +--+----+ = +--+
+----+--+ | +--+
| B |
| |
+-------+
csg的三个基本功能如上所示,我主要说一下开发过程中遇到的问题
第一个问题就是组合中各个模型的坐标问题
一开始我发现组合后相对位置终是居中的,就是没法移动一个模型相对于另外一个模型的位置。导致门和窗户一直在墙体正中间。
用mesh1.position.set设置坐标也不好使。
解决办法:
这样就设置的模型相对墙的位置了。
第二个问题就是设置组合的材质,我目前是把开孔的墙作为一个的模型,门和门轴是一个模型,窗户是一个模型。
如果把门和窗户都组合到墙模型的话不容易改变材质,不方便代码的改动,如果你要做多面墙的话也不方便。可以把多面墙组合到一起。
下图的代码就是给组合完成的对象添加一个纹理,然后设置组合对象的坐标
2.墙体填充门和门轴,让门围绕着门轴旋转 链接ThreeJs入门41-物体旋转的方法和技巧2 - 掘金 (juejin.cn)
旋转默认是按照模型中心旋转的,如果仅对门模型进行rotateY旋转的话,就会出现一半门在里面。一半在外面的情况。我们可以把门和门轴组合起来,然后在让门和门轴旋转就会实现门根据门轴旋转了
材质常见属性
| 材质属性 | 简介 |
|---|---|
| color | 材质颜色,比如蓝色0x0000ff |
| wireframe | 将几何图形渲染为线框。 默认值为false |
| opacity | 透明度设置,0表示完全透明,1表示完全不透明 |
| transparent | 是否开启透明,默认false |
three.module.js,OrbitControls.js和CSG.js官网都有对应的文件)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>Three.js - Fundamentals with light</title>
<style>
html,
body {
height: 100%;
margin: 0;
font-family: sans-serif;
}
#c {
width: 100%;
height: 100%;
}
#container {
position: relative;
/* makes this the origin of its children */
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
</head>
<body>
<body>
<div id="container">
<canvas id="c"></canvas>
</div>
</body>
<script type="importmap">
{
"imports": {
"three": "../build/three.module.js",
"three/addons/": "../build/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { CSG } from '../three-csg-ts/lib/esm/CSG.js'
function main() {
const canvas = document.querySelector('#c');
const renderer = new THREE.WebGLRenderer({ canvas });
const loader = new THREE.TextureLoader();
const fov = 45;
const aspect = 2; // the canvas default
const near = 0.1;
const far = 100;
const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
camera.position.set(0, 10, 20);
const controls = new OrbitControls(camera, canvas);
const scene = new THREE.Scene();
scene.background = new THREE.Color('white');
//地板
{
const planeSize = 40;
const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize);
const planeMat = new THREE.MeshPhongMaterial({
side: THREE.DoubleSide,
color: 0xA9A9A9
});
const mesh = new THREE.Mesh(planeGeo, planeMat);
mesh.rotation.x = Math.PI * -.5;
scene.add(mesh);
}
//灯光
{
const color = 0xFFFFFF;
const intensity = 1;
const light = new THREE.DirectionalLight(color, intensity);
light.position.set(0, 10, 0);
light.target.position.set(-5, 0, 0);
scene.add(light);
scene.add(light.target);
}
{
//门
const cubeGeo = new THREE.BoxGeometry(5.5, 9.9, 0.2);
const cubeMat = new THREE.MeshBasicMaterial(
{ map: loader.load('./images/door_right.png') });
const mesh = new THREE.Mesh(cubeGeo, cubeMat);
mesh.position.set(-2.5, 0, 0);
//m门轴
const lineGeo = new THREE.BoxGeometry(0.2, 9.9, 0.2);
const lineMat = new THREE.MeshBasicMaterial(
{ map: loader.load('./images/line.png') });
const linemesh = new THREE.Mesh(lineGeo, lineMat);
linemesh.position.set(17.5, 5, 0);
linemesh.add(mesh);
//旋转角度
//linemesh.rotateY(-Math.PI / 2)
scene.add(linemesh);
//创建透明玻璃
const windowsGeo = new THREE.BoxGeometry(4, 4, 0.4);
const windowsMat = new THREE.MeshStandardMaterial({
map: loader.load('./images/window.png'),
color: 0xffffff,
opacity: 0.2,
transparent: true
});
const windowsmesh = new THREE.Mesh(windowsGeo, windowsMat);
windowsmesh.position.set(-10, 15, 0);
scene.add(windowsmesh);
//门洞
const cubeGeo1 = new THREE.BoxGeometry(5.5, 10, 1);
const cubeMat1 = new THREE.MeshBasicMaterial({ color: '#8AC' });
const mesh1 = new THREE.Mesh(cubeGeo1, cubeMat1);
//墙
const cubeGeo2 = new THREE.BoxGeometry(40, 20, 1);
const cubeMat2 = new THREE.MeshBasicMaterial({ color: 0xFFFFFF });
const mesh2 = new THREE.Mesh(cubeGeo2, cubeMat2);
//窗户
const cubeGeo3 = new THREE.BoxGeometry(4, 4, 1);
const cubeMat3 = new THREE.MeshBasicMaterial({
});
const mesh3 = new THREE.Mesh(cubeGeo3, cubeMat3);
//不写默认就是居中,坐标是相对于组合时的坐标
mesh1.position.add(new THREE.Vector3(15, -5, 0))
mesh3.position.add(new THREE.Vector3(-10, 5, 0))
mesh1.updateMatrix()
mesh2.updateMatrix()
mesh3.updateMatrix()
const g1 = CSG.fromMesh(mesh1)
const g2 = CSG.fromMesh(mesh2)
const g3 = CSG.fromMesh(mesh3)
//减去
const cubeSphereIntersectCSG1 = g2.subtract(g1)
const cubeSphereIntersectCSG = cubeSphereIntersectCSG1.subtract(g3)
//联盟 组合到一起不好改材质
//const cubeSphereIntersectCSG = cubeSphereIntersectCSG2.union(g)
//相交
//const cubeSphereIntersectCSG = cubeSphereIntersectCSG2.intersect(g3)
//给墙配置一个纹理
loader.load('./images/diffuse_2k.jpg', (texture) => {
const material = new THREE.MeshBasicMaterial({
map: texture,
});
const cubeSphereIntersectMesh = CSG.toMesh(cubeSphereIntersectCSG, new THREE.Matrix4())
cubeSphereIntersectMesh.material = new THREE.MeshBasicMaterial({ map: texture })
cubeSphereIntersectMesh.position.set(0, 10, 0)
scene.add(cubeSphereIntersectMesh)
});
}
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
const width = canvas.clientWidth;
const height = canvas.clientHeight;
const needResize = canvas.width !== width || canvas.height !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
function render() {
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
requestAnimationFrame(render);
}
main();
</script>
</body>
</html>

浙公网安备 33010602011771号