首页 > 解决方案 > THREE.js - 将渐变颜色应用于导入的 GLTF 模型

问题描述

我一直在尝试将渐变颜色应用于此官方 THREE.js 教程中使用的 Flower 模型:

https://threejs.org/examples/webgl_instancing_scatter.html

花模型如下所示:

在此处输入图像描述

它可以从这里下载:https ://github.com/mrdoob/three.js/tree/master/examples/models/gltf/Flower )

到目前为止,我已经能够成功地将模型加载到我的项目中,并且我还能够将不同的颜色应用到它的“开花”网格上——但只有纯色。

我想应用渐变色。
为了说明我的想法,我在 Photoshop 中制作了一张非常快速和肮脏的图像,它可能看起来像:

在此处输入图像描述

我尝试使用该vertexColors技术来做到这一点 - 这是我知道的唯一技术(我对 THREE.js 很陌生) - 到目前为止没有运气(下面的代码。)

在这一点上,我的一部分想知道这是否可能与导入的 GLTF 模型有关 - 或者它是否是一个失败的原因。

希望得到一些输入/帮助。

这是我的代码 - 分为两部分:第一部分是纯色 - 有效,第二部分是我尝试应用渐变色 - 无效:

  // NOTE: I stored the already imported GLTF model in a new Mesh called “blossomMesh”

  // 1. Create a new Group to which I’ll add the Stem and Blossom:
  var newFlower = new THREE.Group();

  // 2. CLONE the MESH of the imported 3D model:
   let newBlossomMesh = blossomMesh.clone();

  // 3. CLONE it's GEOMETRY:
   var newBlossomGeometry = blossomMesh.geometry.clone();

  // 4. CLONE it's MATERIAL:
   var newBlossomMaterial = blossomMesh.material.clone();
                
  // 5. MAKE a NEW COLOR:
  let newBlossomColor = new THREE.Color(generateRandomColor());
  newBlossomMaterial.color = newBlossomColor;
                
  newBlossomMesh.material = newBlossomMaterial;

  newFlower.add(newBlossomMesh);
  newFlower.add(newStemMesh);

  scene.add(newFlower);

所以以上适用于纯色。

这是我试图让渐变色变得更好的方法:

  // THE BLOSSOM:
  // 1. CLONE the MESH of the imported 3D model:
  let newBlossomMesh = blossomMesh.clone();

  // 2. This time use a BufferGeometry to CLONE the GEOMETRY:
  var newBlossomGeometry = new THREE.BufferGeometry();
  newBlossomGeometry = blossomMesh.geometry.clone();

  var blossomVertexPositionsArray = newBlossomGeometry.attributes.position;
  newBlossomGeometry = newBlossomGeometry.toNonIndexed(); 
  blossomVertexPositionsArray = newBlossomGeometry.attributes.position;
                
  // Make a Colors Array:
  var blossomColorsArray = [];
  const newBlossomColor = new THREE.Color();
  for(var i = 0, l = blossomVertexPositionsArray.count; i < l; i ++) {
     newBlossomColor.setHSL(Math.random() * 0.2 + 0.05, 0.95, 0.799);
     blossomColorsArray.push(newBlossomColor.r, newBlossomColor.g, newBlossomColor.b);
  }

  // Now “splash” the "blossomColorsArray" all over the Blossom:
  newBlossomGeometry.setAttribute("color", new     THREE.Float32BufferAttribute(blossomColorsArray, 3));

  newBlossomMesh.material = newBlossomMaterial;

  newFlower.add(newBlossomMesh);
  newFlower.add(newStemMesh);

  scene.add(newFlower);

我从中得到的是黑色的花朵。它基本上看起来只是没有任何颜色,所以我看到的黑色更像是没有颜色,而不是实际的颜色“黑色”。</p>

=================================================

更新:

好的,所以它正在工作 - 但只是“单一地”,而且只有“本地”。<br/>

这就是我的意思:我想制作的不仅仅是一个花卉对象,而是100个。而且我不想在回调函数中这样做,GLTFLoader因为我需要将各种其他逻辑应用于我的 100 个花对象(比如给它们 (x, y, z) 坐标,给它们userData值,将它们添加到因此,我创建了一个单独的函数来处理所有这些工作,我的策略是:

  1. 像我们一直在做的那样加载Flower.glb模型GLTFLoader,一旦加载成功……<br/>
  2. 调用我的另一个函数,它会在循环中创建 500 个此 Flower 的新实例,每个实例都有自己独特的花朵渐变颜色、x、y、z、坐标等。

您的代码工作得很好,但我试图让它在多个 Blossom 对象上工作,并在GLTFLoader函数的回调之外进行。正是这后半部分让我抓狂。

这是我的代码:(
目前的结果是,我的 500 朵花中的每一朵都得到完全相同的灰色——而且没有渐变;它们只是不工作。)

var blossomMesh;
var stemMesh;

function load3DFlowerModel() {
   loader.load("./Flower.glb", function(theFlower) {
      flowerScene = theFlower.scene;

      // Assign values to my global "blossomMesh" and "stemMesh" variables:
      blossomMesh = flowerScene.children[0]; // Blossom 
      stemMesh = flowerScene.children[1];  // Stem

      // Now call my external function:
      makeFlowers();
   });
}




 function makeFlowers() {
    for(var flowerCount = 0; flowerCount < TOTAL_FLOWERS; flowerCount ++) {
                
       var newFlowerGroup = new THREE.Group();

       // I. THE BLOSSOM:
       // 1. First, make a new copy of the incoming global "blossomMesh" object:
       var newBlossomMesh = blossomMesh.clone();
                
       // 2. Next, make a new copy of the MATERIAL of the incoming global "blossomMesh" object:
       var newBlossomMaterial = blossomMesh.material.clone(); 

       // 3. Now make a new copy of the GEOMETRY of the incoming global "blossomMesh" object:
       var newBlossomGeometry = blossomMesh.geometry.clone();

       // 4. Get its vertices:
       let blossomVertexPositionsArray = newBlossomGeometry.attributes.position;
       // 5. Make Colors for it:
       var blossomColorsArray = [];
       var newBlossomColor = new THREE.Color();
       for(var i = 0; i < blossomVertexPositionsArray.count; i ++) {
           newBlossomColor.setHSL(Math.random() * 0.2 + 0.05, 0.95, 0.799);
           blossomColorsArray.push(newBlossomColor.r, newBlossomColor.g, newBlossomColor.b);
                }
       // 6. Finally SPLASH the "blossomColorsArray" all over the Blossom's Geometry:
       newBlossomGeometry.setAttribute("color", new THREE.Float32BufferAttribute(blossomColorsArray, 3));   
       // And set "vertexColors" to true:               
       newBlossomMaterial.vertexColors = true;

       // II. THE STEM
       let newStemMesh = stemMesh.clone();
       newStemMesh.castShadow = true;


       newTulipGroup.add(newStemMesh);
       newTulipGroup.add(newBlossomMesh);
       newTulipGroup.name = "Tulip#" + tulipCount;


       // etc.

所以我得到了一整束花,但它们都有相同的灰色花朵。总是灰色的...

标签: three.jsmeshbuffer-geometry

解决方案


按预期工作。为您的场景添加灯光,vertexColors: true为材质设置。

body{
  overflow: hidden;
  margin: 0;
}
<script type="module">
  import * as THREE from "https://threejs.org/build/three.module.js";
  import {OrbitControls} from "https://threejs.org/examples/jsm/controls/OrbitControls.js";
  import {GLTFLoader} from "https://threejs.org/examples/jsm/loaders/GLTFLoader.js";
  
  let scene = new THREE.Scene();
  let camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 10);
  camera.position.set(0, 5, 5);
  let renderer = new THREE.WebGLRenderer();
  renderer.setSize(innerWidth, innerHeight);
  document.body.appendChild(renderer.domElement);
  
  let controls = new OrbitControls(camera, renderer.domElement);
  
  let light = new THREE.DirectionalLight(0xffffff, 1);
  light.position.setScalar(10);
  scene.add(light);
  scene.add(new THREE.AmbientLight(0xffffff, 0.5));
  
  scene.add(new THREE.GridHelper());
  
  let loader = new GLTFLoader();
  loader.load("https://threejs.org/examples/models/gltf/Flower/Flower.glb", gltf => {
    let model = gltf.scene;
    model.scale.setScalar(10);
    //console.log(model);
    let g = model.children[0].geometry;
    let m = model.children[0].material;
    let p = g.attributes.position;
    let c = new THREE.Color();
    let colors = [];
    for(let i = 0; i < p.count; i++){
      c.set(Math.random() < 0.5 ? 0xff00ff : 0xffff00);
      colors.push(c.r, c.g, c.b);
    }
    g.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
    m.vertexColors = true;
    scene.add(model);
  });
  
  renderer.setAnimationLoop( _ => {
    renderer.render(scene, camera);
  });
</script>


推荐阅读