threejs example

382 阅读8分钟

设置线条粗细+绘制虚线:使用Line2

<script src='/js/lines/LineSegmentsGeometry.js'></script>
<script src='/js/lines/LineGeometry.js'></script>
<script src='/js/lines/LineMaterial.js'></script>
<script src='/js/lines/LineSegments2.js'></script>
<script src='/js/lines/Line2.js'></script>
var geometry = new THREE.LineGeometry();  
var pointArr = [-1000,0,0,
                1000,0,0,
                ]
geometry.setPositions( pointArr);// 几何体传入顶点坐标
this.material  = new THREE.LineMaterial( {
  color: 0x33FF00,
  linewidth: 2,// 线宽度
  dashed: false,
  dashScale : 0.5,
  alphaToCoverage: false,
} );
this.material.resolution.set(window.innerWidth,window.innerHeight);// 把渲染窗口尺寸分辨率传值给材质LineMaterial的resolution属性, resolution属性值会在着色器代码中参与计算
var line = new THREE.Line2(geometry, this.material);
line.computeLineDistances();
scene.add(line);

 animate(){// 把渲染窗口尺寸分辨率传值给材质LineMaterial的resolution属性, resolution属性值会在着色器代码中参与计算
    if (this.material) {
        this.material.resolution.set(window.innerWidth,window.innerHeight);
    }
}

后处理:局部泛光,确保只有特定对象受到 Bloom 效果的影响,多个后处理

单个图形的泛光,可以参照three.js官方提供的另一个例子:threejs.org/examples/?q… 实现的原理实际上是将图形拆分成不同的图层,拾取亮度之前将不需要泛光图层纹理置为黑色,需要泛光的图层提取亮度以及后续的处理,渲染时再将置黑的纹理还原,最后将两者混合。

`<template>
    <div class="interference">
        <div class="sim-area">
            <div class="container" ref="mainContainer">
                <div class="three-scene" ref="threeScene" >
                </div>
            </div>
        </div>
    </div>
</template>
<script setup>
import * as THREE from 'three';
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry.js';
import { LineMaterial } from 'three/examples/jsm/lines/LineMaterial.js';
import { Line2 } from 'three/examples/jsm/lines/Line2.js';
import { EffectComposer } from 'three/examples/jsm//postprocessing/EffectComposer.js';
import { ShaderPass } from 'three/examples/jsm//postprocessing/ShaderPass.js';
import { FXAAShader } from 'three/examples/jsm//shaders/FXAAShader.js';

import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
import { ref, reactive, onMounted } from "vue";


const threeScene = ref(null)

let params = reactive({
    input:{
        color: {
            r: 231,
            g: 0,
            b: 0
        },
    },
    modelMaterials:{}
})

// 主程序
onMounted(async ()=>{
        threeSystem.initThreeScene()
})

let threeSystem = {
    initThreeScene(){
        /*** 1. 初始化场景***/
        this.scene = new THREE.Scene(); 
        this.con = threeScene.value
        this.width = this.con.clientWidth
        this.height = this.con.clientHeight
        /*** 2. 初始化相机***/
        this.camera = new THREE.PerspectiveCamera(70, this.width / this.height, 1, 100000); 
        this.camera.position.set(-1200, 1200, 1200); //设置相机位置
        this.camera.lookAt(this.scene.position); //设置相机方向(指向的场景对象)
        this.camera.zoom = 3
        this.camera.updateProjectionMatrix()
        this.scene.add(this.camera);

        /*** 3.初始化渲染器***/
        this.renderer = new THREE.WebGLRenderer( { 
            antialias: true,// 抗锯齿
            alpha: true 
        });
        this.renderer.setPixelRatio(window.devicePixelRatio)//设置渲染器的像素比
        this.renderer.setSize(this.width, this.height);// 设置渲染宽高
        this.renderer.autoClear = false//局部辉光要加这一句
        this.renderer.shadowMap.enabled = true;//开启阴影
        this.renderer.shadowMap.type = THREE.BasicShadowMap;

        this.con.appendChild(this.renderer.domElement);// 将渲染器添加到页面,this.renderer.domElement==canvas

        let pmremGenerator = new THREE.PMREMGenerator(this.renderer); // 设置环境贴图
        pmremGenerator.compileEquirectangularShader(); //阴影
        this.scene.environment = pmremGenerator.fromScene(
            new RoomEnvironment(),
            0.04
        ).texture;
        this.renderer.physicallyCorrectLights = true
        this.animate()
        this.addBottomBoard()// 地板
        this.addLightLine()// 红色光线
        this.postprocessingInit()//后处理光线
    },
    resize(){
        this.camera.aspect = this.width / this.height; // 更新摄像头宽高比
        this.camera.updateProjectionMatrix(); // 更新摄像机投影矩阵
        this.renderer.setSize(this.width, this.height);//更新渲染页面大小
         //后处理
         if (this.FXAAShaderPass) {
                this.FXAAShaderPass.uniforms['resolution'].value.set(1 / this.width, 1 / this.height);
            }
            if (this.bloomComposer) {
                this.bloomComposer.setSize(this.width, this.height);
            }
            if (this.labelRenderer) {
                this.labelRenderer.setSize(this.width, this.height);
            }
    },
    animate(){
        if (this.matLine) {
            this.matLine.resolution.set(this.width, this.height);
        }

        this.scene.traverse(this.darkenNonBloomed);//将不需要 Bloom 效果的对象材质替换为暗色。
        //这里可以把scene的background给变成空,存起来
        if (this.bloomComposer) { this.bloomComposer.render() }//这时只有需要 Bloom 效果的对象会被亮化。因为阈值的原因,黑色的永远不会发光
        this.scene.traverse(this.restoreMaterial);//恢复对象原始材质
        //这里再把background给还原回去,这样就不会把hdr贴图给弄发光了
        if (this.finalComposer) { this.finalComposer.render() }

        this.animationTimer=requestAnimationFrame(this.animate.bind(this));
    },
    //底板
    addBottomBoard() {
        const geometry = new THREE.BoxGeometry(10, 800, 800);
        const material = new THREE.MeshPhysicalMaterial({
            color: 0x444444,
            roughness: 0.1,
        });
        const bottomBoard = new THREE.Mesh(geometry, material);
        this.scene.add(bottomBoard);
        bottomBoard.position.set(0, -1, -100)
        bottomBoard.rotation.set(0, 0, Math.PI / 2)

        const loader = new THREE.TextureLoader();
        loader.load('./static/material/大理石.png', (texture) => {
            texture.wrapS = THREE.RepeatWrapping;
            texture.wrapT = THREE.RepeatWrapping;
            // uv两个方向纹理重复数量
            texture.repeat.set(2, 2);
            material.map = texture
        })
        return bottomBoard
    },
    //添加光线
    addLightLine(){
        let lineColor = new THREE.Color(`rgb(${params.input.color.r}, ${params.input.color.g}, ${params.input.color.b})`)

        this.line1_Points  = [
            -245, 120, -155,
            -260, 70, 103,
            111, 76, -210,
        ]
        const lineGeometry1 = new LineGeometry();
        lineGeometry1.setPositions( this.line1_Points  );

        this.matLine = new LineMaterial( {
            color: lineColor,
            linewidth: 1.5, 
            dashed: false,
            alphaToCoverage: false,
        } );

        let line1 = new Line2( lineGeometry1, this.matLine );
        line1.computeLineDistances();
        line1.name="线段1"
        // 第二条光线
        this.line2_Points = [
            -200, 120, -161,
            -240, 36, 88,
            120, 78, 4,
        ]
        const lineGeometry2 = new LineGeometry();
        lineGeometry2.setPositions( this.line2_Points );
        let line2 = new Line2( lineGeometry2, this.matLine );
        line2.computeLineDistances();
        line2.name="线段2"

        this.lightLineGroup = new THREE.Group()
        this.lightLineGroup.add(line1,line2)

        //同时在图层1可见
        line1.layers.enable(1)
        line2.layers.enable(0)
        this.scene.add(this.lightLineGroup)
    },
    //后处理渲染光线
    postprocessingInit() {
        const renderScene = new RenderPass(this.scene, this.camera);
        const bloomPass = new UnrealBloomPass(new THREE.Vector2(this.width, this.height), 1.5, 0.4, 0.85);//效果的强度1.5,半径0.4,bloom 效果的亮度阈值:0.85。只有超过这个亮度的部分才会产生 Bloom 效果
        bloomPass.threshold = 0;//阈值:决定哪些亮度的部分会触发 Bloom 效果。值为 0 时,所有亮度都参与效果。
        bloomPass.strength = 3;//强度:控制 Bloom 效果的亮度或强度。值越大,效果越明显。
        bloomPass.radius = 0;//半径:影响 Bloom 效果的扩散范围。值为 0 时,效果集中,不会扩散。

        this.bloomComposer = new EffectComposer(this.renderer);
        this.bloomComposer.renderToScreen = false;
        this.bloomComposer.addPass(renderScene);
        this.bloomComposer.addPass(bloomPass);

        //抗锯齿通道
        // ShaderPass创建自定义的后期处理效果
        this.FXAAShaderPass = new ShaderPass(FXAAShader)
        this.FXAAShaderPass.uniforms['resolution'].value.set(1 / this.width, 1 / this.height);//设置分辨率以适应屏幕大小。
        this.FXAAShaderPass.renderToScreen = true;//将渲染结果输出到屏幕。

        const finalPass = new ShaderPass(
            new THREE.ShaderMaterial({
                uniforms: {
                    baseTexture: { value: null },//基础纹理。
                    bloomTexture: { value: this.bloomComposer.renderTarget2.texture }//renderTarget2是 EffectComposer 的(帧缓冲区),用于存储效果处理后的结果,texture表示渲染目标中的纹理,可以在后续的着色器中使用。
                },
                vertexShader: `
                    varying vec2 vUv;
                    void main() {
                        vUv = uv;
                        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
                    }
                `,
                fragmentShader: `
                    uniform sampler2D baseTexture;
                    uniform sampler2D bloomTexture;
                    varying vec2 vUv;
                    void main() {
                        gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
                    }
                `,
                // texture2D( baseTexture, vUv ):从基础纹理采样颜色。
                // texture2D(bloomTexture, vUv):从 Bloom 效果纹理采样颜色。
                // gl_FragColor:将两个纹理颜色相加,输出合成后的颜色。
                defines: {}//设置编译时常量
            }), "baseTexture"//指定基础纹理的名称
        );
        finalPass.needsSwap = true;//true:处理结果会被交换到下一个通道,确保效果链的正确性

        this.finalComposer = new EffectComposer(this.renderer);//效果合成器
        // 添加渲染通道:
        this.finalComposer.addPass(renderScene);//1.基础场景渲染通道。
        this.finalComposer.addPass(this.FXAAShaderPass);//2.用于抗锯齿的通道
        this.finalComposer.addPass(finalPass);//3.用于合成基础纹理和 Bloom 效果的通道。

        this.darkMaterial = new THREE.MeshBasicMaterial({ color: "black" });
    },
     //保存材质,并赋予新材质
     darkenNonBloomed(obj) {
        if ( obj.layers.mask !== 3) {//不需要 Bloom 效果的
            params.modelMaterials[obj.uuid] = obj.material;
            obj.material = threeSystem.darkMaterial;//将不需要 Bloom 效果的对象材质替换为暗色。
        }
    },
     //重新赋予原来的材质
    restoreMaterial(obj) {
        if (params.modelMaterials[obj.uuid]) {
            obj.material = params.modelMaterials[obj.uuid];
            delete params.modelMaterials[obj.uuid];
        }
    },
}


window.addEventListener('resize',()=>{
    threeSystem.resize()
})

</script>

<style lang="less" >
    @import url('./index.less');
</style>
`

加载blender模型时,glb模型材质近似黑色

因为这类模型默认材质是pbr材质,这种材质需要设置material.env贴图,也可以给scene设置环境贴图

    import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';

    let pmremGenerator = new THREE.PMREMGenerator(this.renderer); // 设置环境贴图,有些外部导入的模型是pbr材质所以需要envMap贴图,不然就是一片黑
    pmremGenerator.compileEquirectangularShader(); //阴影
    this.scene.environment = pmremGenerator.fromScene(
        new RoomEnvironment(),
        0.04
    ).texture;
    this.renderer.physicallyCorrectLights = true;//启动物理颜色校正,防止环境贴图过于发白
    const light1 = new THREE.AmbientLight(0xFFFFFF, 0.3);
    this.camera.add(light1);

    const light2 = new THREE.DirectionalLight(0xFFFFFF, 0.8 * Math.PI);
    light2.position.set(0.5, 0, 0.866); // ~60º
    this.camera.add(light2);//防止环境贴图过于黑暗

镜面效果

`<template>
    <div ref="containerRef" class="container"></div>
  </template>

  <script setup>
  import * as THREE from 'three'
  import { onMounted, ref, onUnmounted } from 'vue'
  import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
  import { Reflector  } from 'three/examples/jsm/objects/Reflector.js'

  const containerRef = ref(null)

  let camera, scene, renderer

  let cameraControls

  let sphereGroup, smallSphere

  let groundMirror, verticalMirror

  // renderer
  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.setSize(window.innerWidth, window.innerHeight)

  // scene
  scene = new THREE.Scene()

  // camera
  camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 500)
  camera.position.set(0, 75, 160)
  cameraControls = new OrbitControls(camera, renderer.domElement)
  cameraControls.target.set(0, 40, 0)
  cameraControls.maxDistance = 400
  cameraControls.minDistance = 10
  cameraControls.update()

  const planeGeo = new THREE.PlaneGeometry(100.1, 100.1)

  // reflectors/mirrors
  let geometry, material

  geometry = new THREE.CircleGeometry(40, 64)

  groundMirror = new Reflector(geometry, {
      clipBias: 0.003,
      textureWidth: window.innerWidth * window.devicePixelRatio,
      textureHeight: window.innerHeight * window.devicePixelRatio,
      color: '#b5b5b5'
  })
  groundMirror.position.y = 0.5
  groundMirror.rotateX(- Math.PI / 2)
  scene.add(groundMirror)

  geometry = new THREE.PlaneGeometry(100, 100)
  verticalMirror = new Reflector(geometry, {
      clipBias: 0.003,
      textureWidth: window.innerWidth * window.devicePixelRatio,
      textureHeight: window.innerHeight * window.devicePixelRatio,
      color: '#c1cbcb'
  })
  verticalMirror.position.y = 50
  verticalMirror.position.z = - 50
  scene.add(verticalMirror)

  sphereGroup = new THREE.Object3D()
  scene.add(sphereGroup)

  geometry = new THREE.CylinderGeometry(0.1, 15 * Math.cos(Math.PI / 180 * 30), 0.1, 24, 1)
  material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: '#8d8d8d' })
  const sphereCap = new THREE.Mesh(geometry, material)
  sphereCap.position.y = - 15 * Math.sin(Math.PI / 180 * 30) - 0.05
  sphereCap.rotateX(- Math.PI)

  geometry = new THREE.SphereGeometry(15, 24, 24, Math.PI / 2, Math.PI * 2, 0, Math.PI / 180 * 120)
  const halfSphere = new THREE.Mesh(geometry, material)
  halfSphere.add(sphereCap)
  halfSphere.rotateX(- Math.PI / 180 * 135)
  halfSphere.rotateZ(- Math.PI / 180 * 20)
  halfSphere.position.y = 7.5 + 15 * Math.sin(Math.PI / 180 * 30)

  sphereGroup.add(halfSphere)

  geometry = new THREE.IcosahedronGeometry(5, 0)
  material = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: '#7b7b7b', flatShading: true })
  smallSphere = new THREE.Mesh(geometry, material)
  scene.add(smallSphere)

  // walls
  const planeTop = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff }))
  planeTop.position.y = 100
  planeTop.rotateX(Math.PI / 2)
  scene.add(planeTop)

  const planeBottom = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xffffff }))
  planeBottom.rotateX(- Math.PI / 2)
  scene.add(planeBottom)

  const planeFront = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xbbbbfe }))
  planeFront.position.z = 50
  planeFront.position.y = 50
  planeFront.rotateY(Math.PI)
  scene.add(planeFront)

  const planeRight = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0x00ff00 }))
  planeRight.position.x = 50
  planeRight.position.y = 50
  planeRight.rotateY(- Math.PI / 2)
  scene.add(planeRight)

  const planeLeft = new THREE.Mesh(planeGeo, new THREE.MeshPhongMaterial({ color: 0xff0000 }))
  planeLeft.position.x = - 50
  planeLeft.position.y = 50
  planeLeft.rotateY(Math.PI / 2)
  scene.add(planeLeft)

  // lights
  const mainLight = new THREE.PointLight('#e7e7e7', 1.5, 250)
  mainLight.position.y = 60
  scene.add(mainLight)

  const greenLight = new THREE.PointLight('#00ff00', 0.25, 1000)
  greenLight.position.set(550, 50, 0)
  scene.add(greenLight)

  const redLight = new THREE.PointLight('#ff0000', 0.25, 1000)
  redLight.position.set(- 550, 50, 0)
  scene.add(redLight)

  const blueLight = new THREE.PointLight('#bbbbfe', 0.25, 1000)
  blueLight.position.set(0, 50, 550)
  scene.add(blueLight)

  // 后期模糊处理
  /* const composer = new EffectComposer(renderer)

  const renderPass = new RenderPass(scene, camera)
  composer.addPass(renderPass)

  const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 0.5, 0.4, 0.85)
  composer.addPass(bloomPass) */

  function animate() {

      requestAnimationFrame(animate)

      const timer = Date.now() * 0.01

      sphereGroup.rotation.y -= 0.002

      smallSphere.position.set(
          Math.cos(timer * 0.1) * 30,
          Math.abs(Math.cos(timer * 0.2)) * 20 + 5,
          Math.sin(timer * 0.1) * 30
      )
      smallSphere.rotation.y = (Math.PI / 2) - timer * 0.1
      smallSphere.rotation.z = timer * 0.8

      renderer.render(scene, camera)
      // composer.render()

  }

  function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight
      camera.updateProjectionMatrix()

      renderer.setSize(window.innerWidth, window.innerHeight)

      groundMirror.getRenderTarget().setSize(
          window.innerWidth * window.devicePixelRatio,
          window.innerHeight * window.devicePixelRatio
      )
      verticalMirror.getRenderTarget().setSize(
          window.innerWidth * window.devicePixelRatio,
          window.innerHeight * window.devicePixelRatio
      )
  }

  onMounted(() => {
      containerRef.value.appendChild(renderer.domElement)
      animate()
      window.addEventListener('resize', onWindowResize)
  })

  onUnmounted(() => {
      window.removeEventListener('resize', onWindowResize)
  })
  </script>
`

css2drender+css3d

`<template>
    <div class="interference">
        <div class="sim-area">

            <div class="container" ref="mainContainer">
                <div class="three-scene1" ref="threeContainer">

                </div>
            </div>
        </div>
    </div>
</template>


<script setup>

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js';
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
import { ref, onMounted} from "vue";
import { gsap } from "gsap";
import MotionPathPlugin from 'gsap/dist/MotionPathPlugin.js'
gsap.registerPlugin(MotionPathPlugin);

const threeContainer = ref(null)

// 主程序
onMounted(async ()=>{;
    threeSystem.init()
})

let threeSystem = {
    init(){
        this.container = threeContainer.value
        this.scene = new THREE.Scene();
        // 添加环境光
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // 柔和的白光
        this.scene.add(ambientLight);
        this.scene.background = new THREE.Color( 0x252525 );//浅色背景
        const mainLight = new THREE.PointLight('#e7e7e7', 1.5, 250)
        mainLight.position.y = 60
        this.scene.add(mainLight)

        const greenLight = new THREE.PointLight('#00ff00', 0.25, 1000)
        greenLight.position.set(550, 50, 0)
        this.scene.add(greenLight)

        const redLight = new THREE.PointLight('#ff0000', 0.25, 1000)
        redLight.position.set(- 550, 50, 0)
        this.scene.add(redLight)

        const blueLight = new THREE.PointLight('#bbbbfe', 0.25, 1000)
        blueLight.position.set(0, 50, 550)
        this.scene.add(blueLight)

        this.width = this.container.clientWidth
        this.height = this.container.clientHeight
        this.camera = new THREE.PerspectiveCamera(75,  this.width /  this.height, 0.1, 10000 );
        this.camera.position.z = 5

        this.create2dLabel(); //初始化css2d渲染器
        this.create3dLabel(); //初始化css2d渲染器
        this.createTextGeo()//为css2d文字创建位置球体
        this.createCss2dText()//创建文字标识
        this.renderer = new THREE.WebGLRenderer({});
        this.renderer.setSize(this.width , this.height);
        // 创建坐标轴辅助
        const axesHelper = new THREE.AxesHelper(5);
        this.scene.add(axesHelper);
        this.container.appendChild(this.renderer.domElement); 
        this.animate()

        this.orbitControls = new OrbitControls(this.camera,this.renderer.domElement);
        this.orbitControls.update();
    },
     //初始化css2d渲染器
     create2dLabel() {
        this.labelRenderer = new CSS2DRenderer();
        this.labelRenderer.setSize(this.width, this.height);
        this.labelRenderer.domElement.style.position = "absolute";
        this.labelRenderer.domElement.style.top = "-2.1rem";
        this.labelRenderer.domElement.style.pointerEvents = "none"; //外面div父元素遮挡了Canvas画布鼠标事件,所以要设置鼠标穿透
        this.labelRenderer.domElement.style.backfacevisibility = "hidden"; // 禁止CSS3DObject标签对应HTMl元素背面显示
        this.container.appendChild(this.labelRenderer.domElement);
    },
    //初始化css3d渲染器
    create3dLabel() {
        this.labelRenderer2 = new CSS3DRenderer();
        this.labelRenderer2.setSize(this.width, this.height);
        this.labelRenderer2.domElement.style.position = "absolute";
        this.labelRenderer2.domElement.style.top = "-2.1rem";
        this.labelRenderer2.domElement.style.pointerEvents = "none"; //外面div父元素遮挡了Canvas画布鼠标事件,所以要设置鼠标穿透
        this.labelRenderer2.domElement.style.backfacevisibility = "hidden"; // 禁止CSS3DObject标签对应HTMl元素背面显示

        this.container.appendChild(this.labelRenderer2.domElement);
    },
     // 创建css2d标签div
     createDiv(context,color,backgroundColor,fs){
        const div = document.createElement("div");
        div.className = "label";
        div.textContent = context;
        div.style.color = color;//白色
        div.style.fontFamily = "sans-serif";
        div.style.padding = "0.2rem";
        div.style.fontSize = fs;
        div.style.backgroundColor = backgroundColor;
        div.style.borderRadius = "0.3rem";
        div.style.fontWeight = "600";
        div.style.cursor = "pointer";
        const label = new CSS2DObject(div);
        console.log("2d",label);

        return label;
    },
     // 创建css3d标签div
     createDiv2(context,color,backgroundColor,fs){
        const div = document.createElement("div");
        div.className = "label";
        div.textContent = context;
        div.style.color = color;//白色
        div.style.fontFamily = "sans-serif";
        div.style.padding = "0.2rem";
        div.style.fontSize = fs;
        div.style.backgroundColor = backgroundColor;
        div.style.borderRadius = "0.3rem";
        div.style.fontWeight = "600";
        div.style.cursor = "pointer";
        const label = new CSS2DObject(div);
        console.log("2d",label);

        return label;
    },
    //为css2d文字创建位置球体
    createTextGeo(){
        const geometry = new THREE.SphereGeometry( 0.1, 32, 16 );
                const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );

        this.text_A  = new THREE.Mesh( geometry, material ); 
        this.text_Z  = new THREE.Mesh( geometry, material ); 

        this.text_A.position.set(2, 0, 0)
        this.text_Z.position.set(-2,0, 0)

        this.scene.add( this.text_A );
        this.scene.add( this.text_Z );
    },
    // 创建文字标识
    createCss2dText(){
        this.text_A.add(this.createDiv('css2d','#00CED1',"rgba(255,255,255, 0.1)","1.6rem"));
        // 显示信息
        this.text_Z.add(this.createDiv2('css3d','#00CED1',"rgba(255,255,255, 0.1)","1.6rem"));
    },
    animate(){  
        this.renderer.render( this.scene, this.camera );
        this.labelRenderer.render(this.scene, this.camera);
        this.labelRenderer2.render(this.scene, this.camera);
        this.animationTimer = requestAnimationFrame(this.animate.bind(this));
    },
    resize(){
        this.width = this.container.clientWidth
        this.height = this.container.clientHeight
        this.camera.aspect = this.width / this.height;
        this.camera.updateProjectionMatrix();
        this.renderer.setSize(  this.width , this.height );

        if (this.labelRenderer) {
            this.labelRenderer.setSize(this.width, this.height);
        }
        if (this.labelRenderer2) {
            this.labelRenderer2.setSize(this.width, this.height);
        }
    },
}

window.addEventListener("resize", () => {
    threeSystem.resize()
});
</script>

<style lang="less" >
    @import url('./index.less');

</style>`