• Three.js-CSS渲染器(CSS2DRenderer)


    在Three中提供2D和3Dcss渲染器供用户使用,如果你希望将二维或三维物体和基于HTML的标签相结合,则这一渲染器将十分有用。在这里,各个DOM元素也被包含到一个CSS2DObject或CSS3DObject实例中,并被添加到场景图中。

     

    目录

    1. CSS2DRenderer

    1.1 构造函数

    1.2 方法 

    2. CSS3DRenderer

    2.1 构造函数

    2.2 方法

    3. CSS2DRenderer实现地月旋转


    1. CSS2DRenderer

    CSS2DRenderer是CSS3DRenderer(CSS 3D渲染器)的简化版本,唯一支持的变换是位移。
    如果你希望将三维物体和基于HTML的标签相结合,则这一渲染器将十分有用。在这里,各个DOM元素也被包含到一个CSS2DObject实例中,并被添加到场景图中。

     例如官方他提供的标签案例(label):

    通过HTML及CSS设置标签样式,并绑定至Three创建的物体中:

     同理还有molecules分子案例:

    1.1 构造函数

    CSS2DRenderer()

    1.2 方法 

    .getSize () : Object

    返回一个包含有渲染器宽和高的对象。

    .render ( scene : Scene, camera : Camera ) : undefined

    使用camera渲染scene。

    .setSize (width : Number, height : Number) : undefined

    将渲染器的尺寸调整为(width, height).

    2. CSS3DRenderer

    CSS3DRenderer用于通过CSS3的transform属性, 将层级的3D变换应用到DOM元素上。 如果你希望不借助基于canvas的渲染来在你的网站上应用3D变换,那么这一渲染器十分有趣。 同时,它也可以将DOM元素与WebGL的内容相结合。
    然而,这一渲染器也有一些十分重要的限制:

    • 它不可能使用three.js中的材质系统。
    • 同时也不可能使用几何体。

    因此,CSS3DRenderer仅仅关注普通的DOM元素,这些元素被包含到了特殊的对象中(CSS3DObject或者CSS3DSprite),然后被加入到场景图中。

    例如Three官方提供的元素周期表案例:

    2.1 构造函数

    CSS3DRenderer()

    2.2 方法

    .getSize () : Object

    返回一个包含有渲染器宽和高的对象。

    .render ( scene : Scene, camera : PerspectiveCamera ) : undefined

    使用perspective camera渲染scene。

    .setSize (width : Number, height : Number) : undefined

    将渲染器尺寸重新调整为(width, height)。

    3. CSS2DRenderer实现地月旋转

    通过使用二维css渲染器实现官方地月围绕旋转及label标签的显示:

    实现原理:

    1)导入CSS2DRenderer, CSS2DObject;

    2)添加提示标签;

    3)实例化标签为CSS2DObject;

    4)设置标签Object的位置;

    5)将标签添加至物体中;

    6)实例化css2d渲染器。

    main.js:

    1. import * as THREE from "three";
    2. import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
    3. import {
    4. CSS2DRenderer,
    5. CSS2DObject,
    6. } from "three/examples/jsm/renderers/CSS2DRenderer.js";
    7. let camera, scene, renderer, labelRenderer;
    8. const clock = new THREE.Clock();
    9. const textureLoader = new THREE.TextureLoader();
    10. let moon;
    11. let chinaLabel;
    12. const raycaster = new THREE.Raycaster();
    13. init();
    14. animate();
    15. // 创建射线
    16. function init() {
    17. const EARTH_RADIUS = 1;
    18. const MOON_RADIUS = 0.27;
    19. camera = new THREE.PerspectiveCamera(
    20. 45,
    21. window.innerWidth / window.innerHeight,
    22. 0.1,
    23. 200
    24. );
    25. camera.position.set(0, 5, -10);
    26. scene = new THREE.Scene();
    27. // 设置直线光源
    28. const dirLight = new THREE.DirectionalLight(0xffffff);
    29. dirLight.position.set(0, 0, 1);
    30. scene.add(dirLight);
    31. const light = new THREE.AmbientLight(0xffffff, 0.5); // soft white light
    32. scene.add(light);
    33. // 设置地球实体
    34. const earthGeometry = new THREE.SphereGeometry(EARTH_RADIUS, 16, 16);
    35. const earthMaterial = new THREE.MeshPhongMaterial({
    36. specular: 0x333333,
    37. shininess: 5,
    38. map: textureLoader.load("textures/planets/earth_atmos_2048.jpg"),
    39. specularMap: textureLoader.load("textures/planets/earth_specular_2048.jpg"),
    40. normalMap: textureLoader.load("textures/planets/earth_normal_2048.jpg"),
    41. normalScale: new THREE.Vector2(0.85, 0.85),
    42. });
    43. const earth = new THREE.Mesh(earthGeometry, earthMaterial);
    44. // earth.rotation.y = Math.PI;
    45. scene.add(earth);
    46. //设置月球实体
    47. const moonGeometry = new THREE.SphereGeometry(MOON_RADIUS, 16, 16);
    48. const moonMaterial = new THREE.MeshPhongMaterial({
    49. shininess: 5,
    50. map: textureLoader.load("textures/planets/moon_1024.jpg"),
    51. });
    52. moon = new THREE.Mesh(moonGeometry, moonMaterial);
    53. scene.add(moon);
    54. // 添加提示标签
    55. const earthDiv = document.createElement('div');
    56. earthDiv.className = "label";
    57. earthDiv.innerHTML = "地球";
    58. const earthLabel = new CSS2DObject(earthDiv);
    59. earthLabel.position.set(0,1,0);//相对于父级元素的位置
    60. earth.add(earthLabel);
    61. // 中国
    62. const chinaDiv = document.createElement('div');
    63. chinaDiv.className = "label1";
    64. chinaDiv.innerHTML = "中国";
    65. chinaLabel = new CSS2DObject(chinaDiv);
    66. chinaLabel.position.set(-0.3,0.5,-0.9);
    67. earth.add(chinaLabel);
    68. console.log(chinaLabel)
    69. // 月球
    70. const moonDiv = document.createElement('div');
    71. moonDiv.className = "label";
    72. moonDiv.innerHTML = "月球";
    73. const moonLabel = new CSS2DObject(moonDiv);
    74. moonLabel.position.set(0,0.3,0);
    75. moon.add(moonLabel);
    76. // 实例化css2d的渲染器
    77. labelRenderer = new CSS2DRenderer();
    78. labelRenderer.setSize(window.innerWidth, window.innerHeight);
    79. document.body.appendChild(labelRenderer.domElement)
    80. //设置样式
    81. labelRenderer.domElement.style.position = 'fixed';
    82. labelRenderer.domElement.style.top = '0px';
    83. labelRenderer.domElement.style.left = '0px';
    84. labelRenderer.domElement.style.zIndex = '10';//设置层级
    85. renderer = new THREE.WebGLRenderer();
    86. renderer.setPixelRatio(window.devicePixelRatio);
    87. renderer.setSize(window.innerWidth, window.innerHeight);
    88. document.body.appendChild(renderer.domElement);
    89. const controls = new OrbitControls(camera, labelRenderer.domElement);
    90. controls.minDistance = 5;
    91. controls.maxDistance = 100;
    92. //
    93. window.addEventListener("resize", onWindowResize);
    94. }
    95. function onWindowResize() {
    96. camera.aspect = window.innerWidth / window.innerHeight;
    97. camera.updateProjectionMatrix();
    98. renderer.setSize(window.innerWidth, window.innerHeight);
    99. labelRenderer.setSize(window.innerWidth, window.innerHeight);
    100. }
    101. function animate() {
    102. requestAnimationFrame(animate);
    103. const elapsed = clock.getElapsedTime();
    104. moon.position.set(Math.sin(elapsed) * 5, 0, Math.cos(elapsed) * 5);
    105. // 通过射线设置标签隐藏
    106. const chinaPosition = chinaLabel.position.clone();
    107. // 计算出标签跟摄像机的距离
    108. const labelDistance = chinaPosition.distanceTo(camera.position);
    109. // 检测射线的碰撞
    110. // chinaLabel.position
    111. // 向量(坐标)从世界空间投影到相机的标准化设备坐标 (NDC) 空间。
    112. chinaPosition.project(camera);
    113. raycaster.setFromCamera(chinaPosition,camera);
    114. const intersects = raycaster.intersectObjects(scene.children,true)
    115. // console.log(intersects)
    116. // 如果没有碰撞到任何物体,那么让标签显示
    117. if(intersects.length == 0){
    118. chinaLabel.element.classList.add('visible');
    119. }else{
    120. // if(labelDistance)
    121. const minDistance = intersects[0].distance;
    122. console.log(minDistance,labelDistance)
    123. if(minDistance
    124. chinaLabel.element.classList.remove('visible');
    125. }else{
    126. chinaLabel.element.classList.add('visible');
    127. }
    128. }
    129. // 标签渲染器渲染
    130. labelRenderer.render(scene,camera);
    131. renderer.render(scene, camera);
    132. }

    实现效果:

     

  • 相关阅读:
    【持续更新】Spark Submit命令 配置参数详解
    HarmonyOS/OpenHarmony原生应用开发-华为Serverless服务支持情况(四)
    【pytest】conftest.py使用
    分享一个JS对象隐式转换的问题
    数学基础之曲线拟合
    关键字查询方法
    5个WebGIS功能小技巧
    PE结构学习(6)_重定位表
    GPU训练yolov5问题
    深潜Kotlin协程(二十一):Flow 生命周期函数
  • 原文地址:https://blog.csdn.net/damadashen/article/details/126055360