feature/threejs

This commit is contained in:
lys 2025-04-24 11:13:19 +08:00
parent c215fbcefb
commit a83128db05
3 changed files with 168 additions and 8 deletions

View File

@ -32,7 +32,8 @@ export default class AmapViewer {
}
private initAmapFile() {
if ((window as any).AMap && (window as any).Loca) {
// && (window as any).Loca
if ((window as any).AMap) {
this.initMap();
} else {
const url = `https://webapi.amap.com/maps?v=2.0&key=${AMAP_KEY}`;
@ -53,7 +54,7 @@ export default class AmapViewer {
// 获取画布dom
this.amapDom = document.getElementById(this.id) as HTMLElement;
this.map = new AMap.Map(this.amapDom, {
zoom: 18,
zoom: 14,
center: this.center,
resizeEnable: true,
viewMode: "3D",

View File

@ -1,4 +1,4 @@
import React, { useEffect, useRef } from "react";
import React, { useEffect, useRef, useState } from "react";
import AmapViewer from "@/lib/AmapViewer";
import ShadowMaterialAmap from "./modules/shadow";
import styles from "./index.less";
@ -10,15 +10,29 @@ type Props = OwnProps;
const PAGE_ID = "DEMO_THREEAMAP",
CENTER = [118.794694, 32.434165];
const ThreeAmap: React.FC<Props> = (props) => {
const [isAMapLoaded, setIsAMapLoaded] = useState(false); //判断是否加载完成AMap对象
const amapRef = useRef<AmapViewer>();
useEffect(() => {
init();
return () => amapRef.current?.destoryMap();
}, []);
useEffect(() => {
const checkAMapLoaded = () => {
if (window.AMap && !isAMapLoaded) {
setIsAMapLoaded(true);
initLayer();
}
};
checkAMapLoaded();
if (!isAMapLoaded) window.addEventListener("load", checkAMapLoaded);
return () => window.removeEventListener("load", checkAMapLoaded);
}, [isAMapLoaded]);
const init = () => {
amapRef.current = new AmapViewer(PAGE_ID, CENTER);
};
const initLayer = () => {
const amap = amapRef.current;
const shadowMaterialAmap = new ShadowMaterialAmap(amap);
const shadowMaterialAmap = new ShadowMaterialAmap(amapRef.current as AmapViewer);
shadowMaterialAmap.CreateGLlayer();
};
return (

View File

@ -1,24 +1,169 @@
import type AmapViewer from "src/lib/AmapViewer";
import * as THREE from "three";
import { Camera, MeshPhongMaterial, PerspectiveCamera, Scene, WebGLRenderer } from "three";
const guiCtrl = {
lightPositionX: 10000,
lightPositionY: 5000,
lightPositionZ: 5000,
cameraNear: 0,
caremaFar: 20000,
cameraLeft: -5000,
cameraRight: 5000,
cameraTop: 5000,
cameraBottom: -5000,
mapSize: 512,
planeMaterialOpacity: 0.5
};
export default class ShadowMaterialAmap {
protected Amap!: any;
public locationDataSource!: any;
public meshes!: any[];
public plane!: any;
public camera!: PerspectiveCamera;
public renderer!: WebGLRenderer;
public scene!: Scene;
public mat!: MeshPhongMaterial;
public isDestroy = false; // 是否销毁
constructor(map: AmapViewer) {
this.Amap = map;
this.meshes = [];
}
// 创建 GL 图层
public CreateGLlayer() {
console.log("gllayer", this.Amap);
return;
this.locationDataSource = this.Amap.map.customCoords.lngLatsToCoords([
[118.794, 32.43] // [118.795, 32.43],
// [118.796, 32.43]
]);
const gllayer = new AMap.GLCustomLayer({
zIndex: 10, // 初始化的操作
init: (gl: any) => {
console.log("gl", gl);
this.initRender(gl);
this.initScene();
this.initCamera();
this.initMat();
this.addBoxGeometry();
// const animate = () => {
// if (this.isDestroy) return;
// requestAnimationFrame(animate);
//
// // 全局的公共动画函数,添加函数可同步执行
// this.animateEventList.forEach((event) => {
// if (event.fun && event.content) event.fun(event.content);
// });
// };
// animate();
// const aLight = new THREE.AmbientLight(0xffffff, 0.3);
// this.scene.add(aLight);
},
render: () => {
console.log(123);
this.renderer.resetState();
const { near, far, fov, up, lookAt, position } = this.Amap.map.customCoords.getCameraParams();
// 设置相机参数
this.camera.near = near;
this.camera.far = far;
this.camera.fov = fov;
// @ts-ignore
this.camera.position.set(...position);
// @ts-ignore
this.camera.up.set(...up);
// @ts-ignore
this.camera.lookAt(...lookAt);
this.camera.updateProjectionMatrix();
this.updateDom();
this.renderDom();
this.renderer.resetState();
}
});
this.Amap.map.add(gllayer);
}
private initRender(gl: any) {
// 获取画布dom
this.renderer = new THREE.WebGLRenderer({ antialias: true, context: gl });
}
// 创建场景
private initScene() {
this.scene = new THREE.Scene();
// 环境光
const aLight = new THREE.AmbientLight(0xffffff, 0.3);
this.scene.add(aLight);
// 平行光
const { lightPositionX, lightPositionY, lightPositionZ, cameraNear, caremaFar, cameraLeft, cameraRight, cameraTop, cameraBottom, mapSize, planeMaterialOpacity } = guiCtrl;
const dLight = new THREE.DirectionalLight(0xff0000, 3);
dLight.position.set(lightPositionX, lightPositionY, lightPositionZ);
dLight.castShadow = true; // 开启阴影投射
dLight.shadow.mapSize.width = mapSize; // 增加阴影分辨率
dLight.shadow.mapSize.height = mapSize;
dLight.shadow.camera.near = cameraNear;
dLight.shadow.camera.far = caremaFar;
dLight.shadow.camera.left = cameraLeft;
dLight.shadow.camera.right = cameraRight;
dLight.shadow.camera.top = cameraTop;
dLight.shadow.camera.bottom = cameraBottom;
this.scene.add(dLight);
// 创建接收阴影的平面
const planeGeo = new THREE.PlaneGeometry(50000, 50000);
const shadowMat = new THREE.ShadowMaterial({
opacity: planeMaterialOpacity
});
this.plane = new THREE.Mesh(planeGeo, shadowMat);
this.plane.receiveShadow = true; // 接收阴影
this.scene.add(this.plane);
}
// 初始化相机
public initCamera() {
this.camera = new THREE.PerspectiveCamera(60, this.Amap.amapDom.clientWidth / this.Amap.amapDom.clientHeight, 100, 1 << 30);
}
// 初始化背景(盒模型背景,视角在盒子里面,看到的是盒子内部)
private initMat() {
// 加载纹理
const texture = new THREE.TextureLoader().load("https://a.amap.com/jsapi_demos/static/demo-center-v2/three.jpeg");
texture.minFilter = THREE.LinearFilter;
// 创建材质
this.mat = new THREE.MeshPhongMaterial({
color: 0xfff0f0,
depthTest: true,
transparent: true,
map: texture
});
}
private addBoxGeometry() {
// 创建几何体
const geo = new THREE.BoxGeometry(1000, 1000, 1000);
for (let i = 0; i < this.locationDataSource.length; i++) {
const d = this.locationDataSource[i];
const mesh = new THREE.Mesh(geo, this.mat);
mesh.position.set(d[0], d[1], 500);
mesh.castShadow = true; // 启用阴影投射
mesh.receiveShadow = true; // 接收阴影
this.meshes.push({ mesh, count: i });
this.scene.add(mesh);
}
}
// 更新参数
public updateDom() {
this.camera.aspect = this.Amap.amapDom.clientWidth / this.Amap.amapDom.clientHeight; // 摄像机视锥体的长宽比,通常是使用画布的宽/画布的高
this.camera.updateProjectionMatrix(); // 更新摄像机投影矩阵。在任何参数被改变以后必须被调用,来使得这些改变生效
// 禁用自动清理,以保持地图底图可见
this.renderer.autoClear = false;
this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
this.renderer.setSize(this.Amap.amapDom.clientWidth, this.Amap.amapDom.clientHeight);
this.renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比
}
// 渲染dom
public renderDom() {
this.renderer?.render(this.scene as Scene, this.camera as Camera);
}
}