3d-force-graph и Three. js - Добавление геометрии c свечение / атмосфера c материал / простая текстура к отдельным узлам и группам узлов - PullRequest
1 голос
/ 05 февраля 2020

Я работаю над 3D-визуализацией форс-графа и использую замечательную библиотеку:

https://github.com/vasturiano/3d-force-graph

Я хотел бы попытаться получить какой-то светящийся эффект на узлах силового графа, и я смотрел на что-то вроде этого, чтобы выполнить sh, но это доказывает проблему:

https://github.com/jeromeetienne/threex.geometricglow

Пока у меня есть возможность только добавлять новые фигуры и текстурировать их. Я также добавил текстовые спрайты, которые следуют за узлами, поэтому я подозреваю, что возможен подход, который объединяет эти два. См. Ниже код.

Добавление эффекта свечения или текстуры к этому базовому коду также было бы очень полезно:

https://github.com/vasturiano/3d-force-graph/blob/master/example/async-load/index.html

<head>
<style>
    body {
        margin: 0;
    }

</style>

<script src="//unpkg.com/three"></script>
<script src="//unpkg.com/three-spritetext"></script>

<script src="//unpkg.com/3d-force-graph"></script>
<!--<script src="../../dist/3d-force-graph.js"></script>-->

<script>
    const loader = new THREE.TextureLoader();

    const Graph = ForceGraph3D()
        (document.getElementById('3d-graph'))
        .jsonUrl('../datasets/testdata.json')
        .nodeLabel('id')
        .backgroundColor('#F7F8FA')
        .nodeAutoColorBy('group')
        .nodeThreeObjectExtend(true)
        .nodeThreeObject(node => {
            // extend link with text sprite
            const sprite = new SpriteText(`${node.id}`);
            sprite.color = 'lightgrey';
            sprite.textHeight = 4
            sprite.fontFace = "Comic Sans MS"
            sprite.position.set(5, 5, 5)
            return sprite;

        })
        .nodeVal('size')
        .linkWidth(2)


    const distance = 600;



    //
    const sphereGeometry = new THREE.SphereGeometry(18);
    const sphereMaterial = new THREE.MeshBasicMaterial({
        map: loader.load('../datasets/texture.jpg')
    });
    const mesh = new THREE.Mesh(sphereGeometry, sphereMaterial);
    mesh.position.set(9, 17, 22);
    Graph.scene().add(mesh);
    //


    // camera orbit
    let angle = 0;
    setInterval(() => {
        Graph.cameraPosition({
            x: distance * Math.sin(angle),
            z: distance * Math.cos(angle)
        });
        angle += Math.PI / 1000;

    }, 10); //


    let materialArray = [];
    let texture_ft = new THREE.TextureLoader().load('../datasets/penguins/kenon_star_ft.jpg');
    let texture_bk = new THREE.TextureLoader().load('../datasets/penguins/kenon_star_bk.jpg');
    let texture_up = new THREE.TextureLoader().load('../datasets/penguins/kenon_star_up.jpg');
    let texture_dn = new THREE.TextureLoader().load('../datasets/penguins/kenon_star_dn.jpg');
    let texture_rt = new THREE.TextureLoader().load('../datasets/penguins/kenon_star_rt.jpg');
    let texture_lf = new THREE.TextureLoader().load('../datasets/penguins/kenon_star_lf.jpg');

    materialArray.push(new THREE.MeshBasicMaterial({
        map: texture_ft
    }));
    materialArray.push(new THREE.MeshBasicMaterial({
        map: texture_bk
    }));
    materialArray.push(new THREE.MeshBasicMaterial({
        map: texture_up
    }));
    materialArray.push(new THREE.MeshBasicMaterial({
        map: texture_dn
    }));
    materialArray.push(new THREE.MeshBasicMaterial({
        map: texture_rt
    }));
    materialArray.push(new THREE.MeshBasicMaterial({
        map: texture_lf
    }));

    for (let i = 0; i < 6; i++)
        materialArray[i].side = THREE.BackSide;
    let skyboxGeo = new THREE.BoxGeometry(10000, 10000, 10000);
    let skybox = new THREE.Mesh(skyboxGeo, materialArray);
    Graph.scene().add(skybox);

    /* loader.load('https://images.pexels.com/photos/1205301/pexels-photo-1205301.jpeg', function(texture) {
         scene.background = texture;
     });
     */

</script>

Спасибо

1 Ответ

0 голосов
/ 11 февраля 2020

Меня также заинтересовало, чтобы это работало.

Я использовал этот трих пример и увидел, что эти две строки были необходимы:

var glowMesh    = new THREEx.GeometricGlowMesh(mesh)
mesh.add(glowMesh.object3d)

Объедините это с этим примером 3d-графика силы для пользовательской геометрии узла.

Вся базовая c рабочая вещь приведена ниже:

// Random tree data
var generateData = function() {
  const N = 50;
  const gData = {
    nodes: [...Array(N).keys()].map(i => ({ id: i })),
    links: [...Array(N).keys()]
  .filter(id => id)
    .map(id => ({
      source: id,
      target: Math.round(Math.random() * (id-1))
    }))
  };
  
  return gData;
}

// Create a three.js sphere mesh.
var sphereMesh = function(id) {
  var mesh =  new THREE.Mesh(
    [
      new THREE.SphereGeometry(10, 32, 32)
    ][id%1],
    new THREE.MeshLambertMaterial({
      color: '#277ec9',
      transparent: true,
      opacity: 1.0
    })
  )

  // Make it glow.
  var glowMesh = new THREEx.GeometricGlowMesh(mesh);
  mesh.add(glowMesh.object3d);

  var insideUniforms  = glowMesh.insideMesh.material.uniforms;
  insideUniforms.glowColor.value.set('yellow');

  var outsideUniforms = glowMesh.outsideMesh.material.uniforms;
  outsideUniforms.glowColor.value.set('yellow');
  
  return mesh
}

const Graph = ForceGraph3D()
  (document.getElementById('3d-graph'))
    .graphData(generateData())
    .nodeThreeObject(({ id }) => sphereMesh(id))
    .nodeLabel('id');
 
 /*
 The following three snippets of code are the minimum required from the THREEx library.
 */
 
 // ========== threex.dilategeometry.js =============
 
 /**
 * @namespace
 */
var THREEx  = THREEx || {}

/**
 * dilate a geometry inplace
 * @param  {THREE.Geometry} geometry geometry to dilate
 * @param  {Number} length   percent to dilate, use negative value to erode
 */
THREEx.dilateGeometry = function(geometry, length){
  // gather vertexNormals from geometry.faces
  var vertexNormals = new Array(geometry.vertices.length);
  geometry.faces.forEach(function(face){
    if( face instanceof THREE.Face4 ){
      vertexNormals[face.a] = face.vertexNormals[0];
      vertexNormals[face.b] = face.vertexNormals[1];
      vertexNormals[face.c] = face.vertexNormals[2];
      vertexNormals[face.d] = face.vertexNormals[3];    
    }else if( face instanceof THREE.Face3 ){
      vertexNormals[face.a] = face.vertexNormals[0];
      vertexNormals[face.b] = face.vertexNormals[1];
      vertexNormals[face.c] = face.vertexNormals[2];
    }else console.assert(false);
  });
  // modify the vertices according to vertextNormal
  geometry.vertices.forEach(function(vertex, idx){
    var vertexNormal = vertexNormals[idx];
    vertex.x  += vertexNormal.x * length;
    vertex.y  += vertexNormal.y * length;
    vertex.z  += vertexNormal.z * length;
  });   
};


// ========== threex.atmospherematerial.js =============
 
var THREEx = THREEx || {}

/**
 * from http://stemkoski.blogspot.fr/2013/07/shaders-in-threejs-glow-and-halo.html
 * @return {[type]} [description]
 */
THREEx.createAtmosphereMaterial = function(){
  var vertexShader  = [
    'varying vec3 vVertexWorldPosition;',
    'varying vec3 vVertexNormal;',

    'varying vec4 vFragColor;',

    'void main(){',
    ' vVertexNormal = normalize(normalMatrix * normal);',

    ' vVertexWorldPosition  = (modelMatrix * vec4(position, 1.0)).xyz;',

    ' // set gl_Position',
    ' gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);',
    '}',

    ].join('\n')
  var fragmentShader  = [
    'uniform vec3 glowColor;',
    'uniform float  coeficient;',
    'uniform float  power;',

    'varying vec3 vVertexNormal;',
    'varying vec3 vVertexWorldPosition;',

    'varying vec4 vFragColor;',

    'void main(){',
    ' vec3 worldCameraToVertex= vVertexWorldPosition - cameraPosition;',
    ' vec3 viewCameraToVertex = (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz;',
    ' viewCameraToVertex  = normalize(viewCameraToVertex);',
    ' float intensity   = pow(coeficient + dot(vVertexNormal, viewCameraToVertex), power);',
    ' gl_FragColor    = vec4(glowColor, intensity);',
    '}',
  ].join('\n')

  // create custom material from the shader code above
  //   that is within specially labeled script tags
  var material  = new THREE.ShaderMaterial({
    uniforms: { 
      coeficient  : {
        type  : "f", 
        value : 1.0
      },
      power   : {
        type  : "f",
        value : 2
      },
      glowColor : {
        type  : "c",
        value : new THREE.Color('pink')
      },
    },
    vertexShader  : vertexShader,
    fragmentShader  : fragmentShader,
    //blending  : THREE.AdditiveBlending,
    transparent : true,
    depthWrite  : false,
  });
  return material
}


// ========== threex.geometricglowmesh.js =============
 
var THREEx  = THREEx || {}

THREEx.GeometricGlowMesh  = function(mesh){
  var object3d  = new THREE.Object3D

  var geometry  = mesh.geometry.clone()
  THREEx.dilateGeometry(geometry, 0.01)
  var material  = THREEx.createAtmosphereMaterial()
  material.uniforms.glowColor.value = new THREE.Color('cyan')
  material.uniforms.coeficient.value  = 1.1
  material.uniforms.power.value   = 1.4
  var insideMesh  = new THREE.Mesh(geometry, material );
  object3d.add( insideMesh );


  var geometry  = mesh.geometry.clone()
  THREEx.dilateGeometry(geometry, 0.1)
  var material  = THREEx.createAtmosphereMaterial()
  material.uniforms.glowColor.value = new THREE.Color('cyan')
  material.uniforms.coeficient.value  = 0.1
  material.uniforms.power.value   = 1.2
  material.side = THREE.BackSide
  var outsideMesh = new THREE.Mesh( geometry, material );
  object3d.add( outsideMesh );

  // expose a few variable
  this.object3d = object3d
  this.insideMesh = insideMesh
  this.outsideMesh= outsideMesh
}
<script src="https://threejs.org/build/three.js"></script>

<head>
  <style> body { margin: 0; } </style>
  <script src="//unpkg.com/3d-force-graph"></script>
</head>

<body>
  <div id="3d-graph"></div>
</body>

То же самое относится к jsfiddle . Вы можете увидеть результаты лучше здесь.

...