生成建筑,重点在于,什么?
答案当然是数量,生成的建筑过多,那么一定会卡顿模糊,所以。生成建筑的难点而是在于对性能的优化。优化的解决方案就是:BufferGeometryUtils
BufferGeometryUtils的用法就点击上面的连接,去看就好了,这里就直接展示案例:
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import * as BufferGeometryUtils from 'three/examples/jsm/utils/BufferGeometryUtils.js';
export default (domId) => {
/* ------------------------------初始化三件套--------------------------------- */
const dom = document.getElementById(domId);
const { innerHeight, innerWidth } = window
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 2000);
camera.position.set(100, 100, 100);
camera.lookAt(scene.position);
const renderer = new THREE.WebGLRenderer({
antialias: true,// 抗锯齿
alpha: false,// 透明度
powerPreference: 'high-performance',// 性能
logarithmicDepthBuffer: true,// 深度缓冲
})
// renderer.setClearColor(0x000000, 0);// 设置背景色
// renderer.clear();// 清除渲染器
renderer.shadowMap.enabled = true;// 开启阴影
renderer.shadowMap.type = THREE.PCFSoftShadowMap;// 阴影类型
renderer.outputEncoding = THREE.sRGBEncoding;// 输出编码
renderer.toneMapping = THREE.ACESFilmicToneMapping;// 色调映射
renderer.toneMappingExposure = 1;// 色调映射曝光
renderer.physicallyCorrectLights = true;// 物理正确灯光
renderer.setPixelRatio(devicePixelRatio);// 设置像素比
renderer.setSize(innerWidth, innerHeight);// 设置渲染器大小
dom.appendChild(renderer.domElement);
// 重置大小
window.addEventListener('resize', () => {
const { innerHeight, innerWidth } = window
camera.aspect = innerWidth / innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(innerWidth, innerHeight);
})
/* ------------------------------初始化工具--------------------------------- */
const controls = new OrbitControls(camera, renderer.domElement) // 相机轨道控制器
controls.enableDamping = true // 是否开启阻尼
controls.dampingFactor = 0.05// 阻尼系数
controls.panSpeed = -1// 平移速度
const axesHelper = new THREE.AxesHelper(10);
scene.add(axesHelper);
/* ------------------------------正题--------------------------------- */
// 设置黄光 (点光源)
const yellowLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(yellowLight);
// 设置蓝光 (点光源)
const blueLight = new THREE.DirectionalLight(0x0000ff, 3);
blueLight.position.set(50, 50, 50);
scene.add(blueLight);
blueLight.castShadow = true; // 开启阴影
// 设置相机上、下、左、右、近和远裁剪面
blueLight.shadow.camera.top = 100;
blueLight.shadow.camera.bottom = -100;
blueLight.shadow.camera.left = -100;
blueLight.shadow.camera.right = 100;
blueLight.shadow.camera.near = 1;
blueLight.shadow.camera.far = 200;
blueLight.shadow.mapSize.set(1024, 1024);// 阴影贴图大小
// 创建一个平面
const planeGeometry = new THREE.PlaneGeometry(100, 100);
// 创建一个物理材质
const planeMaterial = new THREE.MeshStandardMaterial({
color: new THREE.Color('gray'),
side: THREE.FrontSide,
transparent: true
});
const plane = new THREE.Mesh(planeGeometry, planeMaterial);
plane.rotateX(-Math.PI / 2);
plane.receiveShadow = true; // 开启阴影
scene.add(plane);
const geometries = [];// 存放建筑
// 随机生成建筑
const randomBuilding = () => {
const helper = new THREE.Object3D();
for (let i = 0; i < 100; i++) {
const h = Math.round(Math.random() * 15) + 5;// 高度
const x = Math.round(Math.random() * 50);// x坐标
const y = Math.round(Math.random() * 50);// y坐标
helper.position.set((x % 2 ? -1 : 1) * x, h * 0.5, (y % 2 ? -1 : 1) * y);// 设置位置
const geometry = new THREE.BoxGeometry(5, h, 5);
helper.updateWorldMatrix(true, false);// 更新世界矩阵
geometry.applyMatrix4(helper.matrixWorld);// 应用世界矩阵
geometries.push(geometry);
}
// 合并几何体(几何体数组,是否在合并的几何体中保留原始几何体的分组信息)
const geometry = BufferGeometryUtils.mergeGeometries(geometries, false);
const img = new URL('../assets/images/house.jpg', import.meta.url).href;
const texture = new THREE.TextureLoader().load(img);
// 纹理将以镜像重复的方式填充到物体的表面,即纹理会以镜像的形式重复,直到覆盖整个物体。
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
const material = new THREE.MeshStandardMaterial({
map: texture
})
const mesh = new THREE.Mesh(geometry, material);
mesh.castShadow = true;// 开启阴影投射
mesh.receiveShadow = true;// 开启阴影接收
scene.add(mesh);
const shaderList = []
material.onBeforeCompile = (shader, renderer) => {
console.log(shader.vertexShader)
shaderList.push(shader)
shader.uniforms.uSize = { value: 50 };
shader.vertexShader = shader.vertexShader.replace(
'void main() {',
`uniform float uSize;
varying vec2 vUv;
void main() {`
);
shader.vertexShader = shader.vertexShader.replace(
'#include <fog_vertex>',
`#include <fog_vertex>
vUv=position.xz/uSize;`
);
shader.fragmentShader = shader.fragmentShader.replace(
'void main() {',
`varying vec2 vUv;
uniform float uTime;
void main() {`
);
shader.fragmentShader = shader.fragmentShader.replace(
'#include <dithering_fragment>',
`#include <dithering_fragment>
gl_FragColor.rgb=gl_FragColor.rgb+mix(vec3(0,0.5,0.5),vec3(1,1,0),vUv.y);`
);
}
planeMaterial.onBeforeCompile = (shader, renderer) => {
shaderList.push(shader)
shader.uniforms.uSize = { value: 50 };
shader.vertexShader = shader.vertexShader.replace(
'void main() {',
`uniform float uSize;
varying vec2 vUv;
void main() {`
);
shader.vertexShader = shader.vertexShader.replace(
'#include <fog_vertex>',
`#include <fog_vertex>
vUv=position.xz/uSize;`
);
shader.fragmentShader = shader.fragmentShader.replace(
'void main() {',
`varying vec2 vUv;
uniform float uTime;
void main() {`
);
shader.fragmentShader = shader.fragmentShader.replace(
'#include <dithering_fragment>',
`#include <dithering_fragment>
gl_FragColor.rgb=gl_FragColor.rgb+mix(vec3(0,0.5,0.5),vec3(1,1,0),vUv.y);`
);
}
}
randomBuilding()
/* ------------------------------动画函数--------------------------------- */
const animation = () => {
controls.update();// 如果不调用,就会很卡
renderer.render(scene, camera);
requestAnimationFrame(animation);
}
animation();
}