ThreeJS-3D教学十-有宽度的line

webgl中线是没有宽度的,现实的应用中一般做法都是将线拓宽成面来绘制。默认threejs的线宽是无法调节的,需要用有厚度的线 THREE.Line2。

先看效果图:

看下代码:

csharp 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    body {
      width: 100%;
      height: 100%;
    }
    * {
      margin: 0;
      padding: 0;
    }
    .label {
      font-size: 20px;
      color: #000;
      font-weight: 700;
    }
  </style>
</head>
<body>
<div id="container"></div>
<script type="importmap">
  {
    "imports": {
      "three": "../three-155/build/three.module.js",
      "three/addons/": "../three-155/examples/jsm/"
    }
  }
</script>
<script type="module">
import * as THREE from 'three';
import Stats from 'three/addons/libs/stats.module.js';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GPUStatsPanel } from 'three/addons/utils/GPUStatsPanel.js';
import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
import { Line2 } from 'three/addons/lines/Line2.js';
import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
import { LineGeometry } from 'three/addons/lines/LineGeometry.js';
let stats, labelRenderer, gpuPanel;
let camera, scene, renderer, controls;
const group = new THREE.Group();
const matLines = [];
let once = true;
init();
initHelp();
initLight();
axesHelperWord();
animate();

let data1 = [
  {
    x: -50,
    y: 50,
    z: 0
  },
  {
    x: 50,
    y: 50,
    z: 0
  },
  {
    x: 50,
    y: -50,
    z: 0
  },
  {
    x: -50,
    y: -50,
    z: 0
  },
  {
    x: -50,
    y: 50,
    z: 0
  }
];

let data2 = [
  {
    x: -50,
    y: 0,
    z: 0
  },
  {
    x: 50,
    y: 1,
    z: 0
  }
];

let data3 = [
  {
    x: -25,
    y: 25,
    z: 0
  },
  {
    x: 25,
    y: 25,
    z: 0
  },
  {
    x: 25,
    y: -25,
    z: 0
  },
  {
    x: -25,
    y: -25,
    z: 0
  },
  {
    x: -25,
    y: 25,
    z: 0
  }
];

let positions1 = [];
let positions2 = [];
let positions3 = [];
let colors = [];
let l = data1.length;

let color = new THREE.Color();

data1.map((v, i) => {
  positions1.push(v.x, v.y, v.z);
  color.setHSL(i / l, 1.0, 0.5);
  colors.push(color.r, color.g, color.b);
});

data2.map((v, i) => {
  positions2.push(v.x, v.y, v.z);
  color.setHSL(i / l, 1.0, 0.5);
  colors.push(color.r, color.g, color.b);
});

data3.map((v, i) => {
  positions3.push(v.x, v.y, v.z);
});

draw(positions1, colors);
draw(positions2, colors);
drawSolidLine(positions3, '#f00');

function draw(positions, colors) {
  let geometry = new LineGeometry();
  // 虚线
  const matLine = new LineMaterial({
    // 只有白色 可以显示出渐变色的效果
    color: 0xffffff,
    linewidth: 10,
    vertexColors: THREE.VertexColors,   // 单独设置顶点颜色
    //resolution:  // renderer.render 时加上这个属性
    dashed: true,
    dashSize: 1,
    gapSize: 1,
    defines: {
      USE_DASH: ''
    }
  });
  let line = new Line2(geometry, matLine);
  line.scale.set(1, 1, 1);
  line.visible = true;
  scene.add(line);
  matLines.push(matLine);
  geometry.setPositions(positions);
  geometry.setColors(colors);
  line.computeLineDistances();
}

function drawSolidLine(positions, color) {
  let geometry = new LineGeometry();
  // 虚线
  const matLine = new LineMaterial({
    // 只有白色 可以显示出渐变色的效果
    color: color,
    linewidth: 10,
    // vertexColors: THREE.VertexColors,   // 单独设置顶点颜色
    // resolution:  // renderer.render 时加上这个属性
    dashed: false
  });
  let line = new Line2(geometry, matLine);
  line.scale.set(1, 1, 1);
  line.visible = true;
  line.rotateX(Math.PI / 3);
  scene.add(line);
  matLines.push(matLine);
  geometry.setPositions(positions);
  line.computeLineDistances();
}

function init() {

  camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 10, 2000 );
  camera.up.set(0, 1, 0);
  camera.position.set(60, 40, 60);
  camera.lookAt(0, 0, 0);

  scene = new THREE.Scene();
  scene.background = new THREE.Color( '#ccc' );

  renderer = new THREE.WebGLRenderer( { antialias: true } );
  renderer.setPixelRatio( window.devicePixelRatio );
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );

  labelRenderer = new CSS2DRenderer();
  labelRenderer.setSize( window.innerWidth, window.innerHeight );
  labelRenderer.domElement.style.position = 'absolute';
  labelRenderer.domElement.style.top = '0px';
  labelRenderer.domElement.style.pointerEvents = 'none';
  document.getElementById( 'container' ).appendChild( labelRenderer.domElement );

  controls = new OrbitControls( camera, renderer.domElement );
  // 设置最大最小视距
  controls.minDistance = 20;
  controls.maxDistance = 1000;

  window.addEventListener( 'resize', onWindowResize );

  stats = new Stats();
  stats.setMode(1); // 0: fps, 1: ms
  document.body.appendChild( stats.dom );

  gpuPanel = new GPUStatsPanel( renderer.getContext() );
  stats.addPanel( gpuPanel );
  stats.showPanel( 0 );

  scene.add( group );
}

function initLight() {
  const AmbientLight = new THREE.AmbientLight(new THREE.Color('rgb(255, 255, 255)'));
  scene.add( AmbientLight );
}

function initHelp() {
  // const size = 100;
  // const divisions = 5;
  // const gridHelper = new THREE.GridHelper( size, divisions );
  // scene.add( gridHelper );

  // The X axis is red. The Y axis is green. The Z axis is blue.
  const axesHelper = new THREE.AxesHelper( 100 );
  scene.add( axesHelper );
}

function axesHelperWord() {
  let xP = addWord('X轴');
  let yP = addWord('Y轴');
  let zP = addWord('Z轴');
  xP.position.set(50, 0, 0);
  yP.position.set(0, 50, 0);
  zP.position.set(0, 0, 50);
}

function addWord(word) {
  let name = `<span>${word}</span>`;
  let moonDiv = document.createElement( 'div' );
  moonDiv.className = 'label';
  // moonDiv.textContent = 'Moon';
  // moonDiv.style.marginTop = '-1em';
  moonDiv.innerHTML = name;
  const label = new CSS2DObject( moonDiv );
  group.add( label );
  return label;
}

function onWindowResize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
}

function animate() {
  requestAnimationFrame( animate );
  // 这里请注意	
  // 把渲染窗口尺寸分辨率传值给材质LineMaterial的resolution属性
  // resolution属性值会在着色器代码中参与计算
  if (matLines.length && once) {
    matLines.forEach(matLine => {
      matLine.resolution.set(window.innerWidth, window.innerHeight); // resolution of the viewport
    });
    once = false;
  }

  stats.update();
  controls.update();
  labelRenderer.render( scene, camera );
  renderer.render( scene, camera );
}
</script>
</body>
</html>
相关推荐
fthux1 小时前
RenoPit 能为普通业主做什么?看懂图纸、审查合同,提前发现装修坑
javascript·人工智能·ai·开源·github·chrome扩展·open source·edge扩展·firefox扩展
仿生狮子2 小时前
✂️ Nuxt 最简单的字体裁剪工具:Fontize
javascript·vue.js·nuxt.js
cd_949217214 小时前
哪些AI工具适合生成游戏开发用的3D道具模型?从道具草稿到引擎测试的选择方法
人工智能·3d
石小石Orz5 小时前
我发现了开发者AI产品营收的新方向
前端·虚拟现实
老马识途2.05 小时前
关于跨域问题的总结
java·前端
魔力女仆5 小时前
分享一个 JS 鼠标跟随贪吃蛇背景库
开发语言·javascript·计算机外设
别惊醒渔人7 小时前
Vue3 Diff 优化:最长递增子序列 LIS
前端·javascript·vue.js
颜酱7 小时前
07 | 把字段与指标同步到 Qdrant(生成阶段)
前端·人工智能·后端
用户059540174467 小时前
把AI长期记忆测试从手动验证换成pytest,2天揪出11个隐藏Bug
前端·css
Larcher7 小时前
从“加载模型”界面到端侧推理:拆解一个 React + WebGPU 大模型 Demo
javascript·后端