Three.js - 将热图纹理应用于点云

Three.js - Apply heatmap texture to pointcloud

我想做的是将热图纹理应用于点云,这样当点云旋转时,纹理不会旋转。例如,红色需要始终处于 Z 向上,蓝色需要始终处于 Z 向下。

是否可以将纹理添加到 Points 对象并使其“锁定”到位,使其不随几何体旋转?或者我是否需要创建另一个对象,例如具有纹理的立方体,并以某种方式将其与点云混合?

创建着色器或将颜色属性添加到顶点是唯一的选择吗?

非常感谢所有评论和建议!

一个非常粗略的选项,使用修改后的PointsMaterialBox3:

body{
  overflow: hidden;
  margin: 0;
}
<script type="module">
console.clear();
import * as THREE from 'https://threejs.org/build/three.module.js';

import { OrbitControls } from 'https://threejs.org/examples/jsm/controls/OrbitControls.js';
import { PCDLoader } from 'https://threejs.org/examples/jsm/loaders/PCDLoader.js';

let camera, scene, renderer, clock, pcd;
let box = new THREE.Box3();
let size = new THREE.Vector3();
let uniforms = {
  yMin: {value: 0},
  yMax: {value: 0}
}

init();

function init() {

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

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.01, 40 );
  camera.position.set( 0, 0, 1 );
  scene.add( camera );

  const controls = new OrbitControls( camera, renderer.domElement );
  //controls.addEventListener( 'change', render ); // use if there is no animation loop
  controls.minDistance = 0.5;
  controls.maxDistance = 10;

  //scene.add( new THREE.AxesHelper( 1 ) );

  const loader = new PCDLoader();
  loader.load( 'https://threejs.org/examples/models/pcd/binary/Zaghetto.pcd', function ( points ) {

    points.geometry.center();
    points.geometry.rotateX( Math.PI );
    
    points.material.onBeforeCompile = shader => {
      shader.uniforms.yMin = uniforms.yMin;
      shader.uniforms.yMax = uniforms.yMax;
      shader.vertexShader = `
        varying vec4 worldPosition;
        ${shader.vertexShader}
      `.replace(
        `#include <worldpos_vertex>`,
        `
          worldPosition = vec4( transformed, 1.0 );
          worldPosition = modelMatrix * worldPosition;
        `
      );
      console.log(shader.vertexShader);
      shader.fragmentShader = `
        uniform float yMin;
        uniform float yMax;
        
        varying vec4 worldPosition;
        
        // https://www.shadertoy.com/view/4dsSzr
        vec3 ansiGradient(float t) {
          return mod(floor(t * vec3(8.0, 4.0, 2.0)), 2.0);
        }
        ${shader.fragmentShader}
      `.replace(
        `vec4 diffuseColor = vec4( diffuse, opacity );`,
        `
          float a = (worldPosition.y - yMin) / (yMax - yMin);
          vec3 col = ansiGradient(a);
          vec4 diffuseColor = vec4( col, opacity );
        `
      );
      console.log(shader.fragmentShader);
    
    }
    pcd = points;
    console.log(pcd);
    scene.add( points );
    
    const helper = new THREE.Box3Helper( box, 0xffff00 );
    scene.add( helper );

  } );
  
  clock = new THREE.Clock();

  window.addEventListener( 'resize', onWindowResize );

}

function onWindowResize() {

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

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

}

renderer.setAnimationLoop( _ => {
  let t = clock.getElapsedTime();
  if (pcd) {
    pcd.rotation.x = (Math.sin(t) * 0.5 + 0.5) * THREE.MathUtils.degToRad(-60);
    
    box.setFromObject(pcd);
    let yMin = box.min.y;
    let yMax = box.max.y;

    uniforms.yMin.value = yMin;
    uniforms.yMax.value = yMax;
  }

  renderer.render( scene, camera );

});
</script>