【问题标题】:buffergeometry groups with individual shaders具有单独着色器的缓冲几何组
【发布时间】:2020-11-23 22:42:18
【问题描述】:

我认为显示代码示例是最好的。因此,我创建了一个显示问题的小示例。 我想创建一个缓冲区几何,其中每个组都有自己的着色器。虽然我总是在材质数组中创建一个新实例,但我不能独立使用各个着色器的制服。我在阵列中一个着色器的统一中调整的内容总是对材质阵列中的所有其他着色器具有相同的效果。 在我问之前,我尝试通过研究来推进,但在这里我已经到了无法再进一步的地步。有谁知道为什么材质数组中的各个着色器相互依赖以及如何避免这种情况?

var camera, controls, scene, renderer, container;

var PI = Math.PI;
var clock = new THREE.Clock(); 

var plane;
var MAX_Planes = 100;
var velocity = [];
var geometry;

var test1, test2, test3;


function init() {

    renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true} );
    renderer.setPixelRatio( window.devicePixelRatio ); 
    renderer.shadowMap.enabled = true; 
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    //renderer.sortObjects = true;
             
    container = document.getElementById('container');
    renderer.setSize(container.clientWidth, container.clientHeight);
    container.appendChild( renderer.domElement );

    var aspect = container.clientWidth / container.clientHeight; 
    scene = new THREE.Scene();
    scene.background = new THREE.Color( 0x000000 );
    
    camera = new THREE.PerspectiveCamera( 45, container.clientWidth / container.clientHeight, 1, 100000 );
    camera.position.set(0, 0, 4000);

    controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.enableZoom = true;
    controls.enabled = true;
    controls.target.set(0, 0, 0);
    
//---------------shaders--------------- 

    var BasicVertexShader = `
        void main() {
        gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
    }`;

    var BasicFragmentShader = `
        void main() {
        gl_FragColor = vec4(vec3(1.0, 1.0, 0.8), 1.0); 
    }`;


    var VertexShader = `
    varying vec3 sPos;
    uniform vec3 pos;
    uniform float stretch;
        void main() {

        float rotation = 0.0; 
        sPos = position; 

        vec3 scale;
        scale.x = 1.0*stretch;
        scale.y = 1.0*stretch;
        scale.z = 1.0*stretch;

        vec3 alignedPosition = vec3(position.x * scale.x, position.y * scale.y, 0.0);

        vec3 rotatedPosition;
        rotatedPosition.x = cos(rotation) * alignedPosition.x - sin(rotation) * alignedPosition.y;
        rotatedPosition.y = sin(rotation) * alignedPosition.x + cos(rotation) * alignedPosition.y;
        rotatedPosition.z = alignedPosition.z;

        vec4 finalPosition;

        finalPosition = modelViewMatrix * vec4( 0, 0, 0, 1.0 );
        finalPosition.xyz += rotatedPosition;
        finalPosition = projectionMatrix * finalPosition;
        gl_Position = finalPosition;

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

    }`;

    var FragmentShader = `
    varying vec3 sPos;
        void main() {

        vec3 nDistVec = normalize(sPos); 
        float dist = pow(sPos.x, 2.0) + pow(sPos.y, 2.0); 
        float magnitude = 1.0/dist * pow(4.0, 2.0);

    //  gl_FragColor = vec4(vec3(1.0, 1.0, 0.8), 1.0) * magnitude; 
        gl_FragColor = vec4(vec3(1.0, 1.0, 0.8), 1.0); 
    }`;

    var uniform = { 
        stretch: {type: 'f', value: 1.0},
        pos: { value: new THREE.Vector3(0,0,0) },
    } 

        Shader = new THREE.ShaderMaterial( {                    
        uniforms: uniform,                  
        vertexShader: VertexShader,
        fragmentShader: FragmentShader, 
        transparent: true,
        depthTest: false,   
        depthWrite: false
    }); 
    
    //just for tests
    var Shade = new THREE.ShaderMaterial( {                                 
        vertexShader: BasicVertexShader,
        fragmentShader: BasicFragmentShader, 
        side:THREE.DoubleSide
    }); 

//-------------------------------------------------
//create a plane: points, normals, uv
    const vertices = [
        { pos: [-10, -10,  0], norm: [ 0,  0,  1], uv: [0, 1], },
        { pos: [ 10, -10,  0], norm: [ 0,  0,  1], uv: [1, 1], },
        { pos: [-10,  10,  0], norm: [ 0,  0,  1], uv: [0, 0], },
        { pos: [ 10,  10,  0], norm: [ 0,  0,  1], uv: [1, 0], },
    ];

    const numVertices = vertices.length;
    const positionNumComponents = 3;
    const normalNumComponents = 3;
    const uvNumComponents = 2;
    //arrays for buffergeometry
    const positions = new Float32Array(numVertices * positionNumComponents * MAX_Planes);
    const normals = new Float32Array(numVertices * normalNumComponents * MAX_Planes);
    const uvs = new Float32Array(numVertices * uvNumComponents * MAX_Planes);
  
  //fill arrays with vertices
    var posPointer = 0;
    var nrmPointer = 0;
    var uvPointer = 0;
  
    for(var i = 0; i <= MAX_Planes; i++) {
        var posNdx = 0;
        var nrmNdx = 0;
        var uvNdx = 0;
            for (const vertex of vertices) {
                positions.set(vertex.pos, posNdx + posPointer);
                normals.set(vertex.norm, nrmNdx + nrmPointer);
                uvs.set(vertex.uv, uvNdx + uvPointer);
                posNdx += positionNumComponents;
                nrmNdx += normalNumComponents;
                uvNdx += uvNumComponents;
            }
        posPointer = i * posNdx;
        nrmPointer = i * nrmNdx;
        uvPointer = i * uvNdx;
    }

    //create buffergeometry and assign the attribut arrays
    geometry = new THREE.BufferGeometry();
    geometry.setAttribute('position', new THREE.BufferAttribute(positions, positionNumComponents));
    geometry.setAttribute('normal', new THREE.BufferAttribute(normals, normalNumComponents));
    geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, uvNumComponents));

    var ndx = 0;
    var indices = [];
    //instead 6 vertices for the both triangles of a plane i used 4, so reindication is neccessary
    for(var i = 0; i < MAX_Planes; i++){
        indices.push(ndx, ndx + 1, ndx + 2, ndx + 2, ndx + 1, ndx + 3);
        ndx += 4;
    }
    geometry.setIndex(indices);


    var materials = [];
    geometry.clearGroups(); 

    for(var i = 0; i < MAX_Planes; i++){
        geometry.addGroup( 6*i, 6, i );
        materials.push(Shader);     
    }
    plane = new THREE.Mesh(geometry, materials);
    scene.add(plane); 


    plane.material[0].uniforms.stretch.value = 2;
    plane.material[1].uniforms.stretch.value = 3;
    test1 = Object.keys(plane.material);
    test2 = plane.material[0].uniforms.stretch.value; //why this returns 3 and not 2?
    test3 = plane.material[1].uniforms.stretch.value;
    //the goal is that each group has its own Shader without effecting the other ones
    

//----------------------velocity---------------------------

    for(var i = 0; i < MAX_Planes; i++){
        velocity[i] = new THREE.Vector3(
        Math.random()*2-1,
        Math.random()*2-1,
        Math.random()*2-1);
    }
    
}//-------End init----------


function animate() {

    requestAnimationFrame( animate );  
    render();
    
}//-------End animate----------


function render() {


    document.getElementById("demo1").innerHTML = test1;
    document.getElementById("demo2").innerHTML = test2;
    document.getElementById("demo3").innerHTML = test3;

    for(var i = 0; i < MAX_Planes; i++){
        for(var j = 0; j < 4; j++){
        
            plane.geometry.attributes.position.array[i*12+3*j] = plane.geometry.attributes.position.array[i*12+3*j] + velocity[i].x;
            plane.geometry.attributes.position.array[i*12+3*j+1] =    plane.geometry.attributes.position.array[i*12+3*j+1] + velocity[i].y;
            plane.geometry.attributes.position.array[i*12+3*j+2] =    plane.geometry.attributes.position.array[i*12+3*j+2] + velocity[i].z;
        }
    }
    plane.geometry.attributes.position.needsUpdate = true;

    camera.updateMatrixWorld();
    camera.updateProjectionMatrix(); 
    renderer.render(scene, camera); 
    
}//-------End render----------

【问题讨论】:

    标签: three.js shader buffer-geometry


    【解决方案1】:

    您将对单个 ShaderMaterial 的引用推送到您的材料数组的每个索引中。

    materials.push(Shader);
    

    因为每个索引都是一个引用,所以更改一个索引中对象的属性自然会更改所有其他索引中的对象。

    如果您希望每个组都有自己的属性,那么您需要为每个索引提供唯一的材料。您仍然可以通过仅创建一个原始定义,然后使用Material.clone 创建副本来做到这一点。

    for(var i = 0; i < MAX_Planes; i++){
      geometry.addGroup( 6*i, 6, i );
      materials.push( Shader.clone() ); // creates a unique copy for each index     
    }
    

    【讨论】:

      【解决方案2】:

      这并没有让我平静下来,我突然想到,如果我预先分配缓冲区几何图形,我将不得不对每个组的着色器执行此操作。我现在会让这比使用“.clone ()”复杂得多。很好,我再次检查。你的建议非常有效。我已经更正了定位更新,这正是我想到的结果。这是任何有兴趣的人的定制代码。我现在将它与我的粒子发射器结合起来。

      var camera, controls, scene, renderer, container;
      
      var PI = Math.PI;
      var clock = new THREE.Clock(); 
      
      var plane;
      var MAX_Planes = 2000;
      var velocity = [];
      var geometry;
      
      
      function init() {
      
          renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true} );
          renderer.setPixelRatio( window.devicePixelRatio ); 
          renderer.shadowMap.enabled = true; 
          renderer.shadowMap.type = THREE.PCFSoftShadowMap;
          //renderer.sortObjects = true;
                   
          container = document.getElementById('container');
          renderer.setSize(container.clientWidth, container.clientHeight);
          container.appendChild( renderer.domElement );
      
          var aspect = container.clientWidth / container.clientHeight; 
          scene = new THREE.Scene();
          scene.background = new THREE.Color( 0x000000 );
          
          camera = new THREE.PerspectiveCamera( 45, container.clientWidth / container.clientHeight, 1, 100000 );
          camera.position.set(0, 0, 4000);
      
          controls = new THREE.OrbitControls( camera, renderer.domElement );
          controls.enableZoom = true;
          controls.enabled = true;
          controls.target.set(0, 0, 0);
          
      //---------------shader---------------  
      
          var VertexShader = `
          varying vec3 sPos;
          uniform vec3 pos;
          uniform float stretch;
              void main() {
      
              float rotation = 0.0; 
              sPos = position; 
      
              vec3 scale;
              scale.x = 1.0*stretch;
              scale.y = 1.0*stretch;
              scale.z = 1.0*stretch;
      
              vec3 alignedPosition = vec3(position.x * scale.x, position.y * scale.y, 0.0);
      
              vec3 rotatedPosition;
              rotatedPosition.x = cos(rotation) * alignedPosition.x - sin(rotation) * alignedPosition.y;
              rotatedPosition.y = sin(rotation) * alignedPosition.x + cos(rotation) * alignedPosition.y;
              rotatedPosition.z = alignedPosition.z;
      
              vec4 finalPosition;
      
              finalPosition = modelViewMatrix * vec4( pos, 1.0 );
              finalPosition.xyz += rotatedPosition;
              finalPosition = projectionMatrix * finalPosition;
              gl_Position = finalPosition;
      
          }`;
      
          var FragmentShader = `
          varying vec3 sPos;
              void main() {
      
              vec3 nDistVec = normalize(sPos); 
              float dist = pow(sPos.x, 2.0) + pow(sPos.y, 2.0); 
              float magnitude = 1.0/dist * pow(3.0, 2.0);
              
              float alpha = 1.0;
              
              if(magnitude  < 0.01){
              alpha = 0.0;
              }
              
              gl_FragColor = vec4(vec3(1.0, 1.0, 0.8), alpha) * magnitude; 
          //  gl_FragColor = vec4(vec3(1.0, 1.0, 0.8), 1.0); 
          }`;
      
          var uniform = { 
              stretch: {type: 'f', value: 1.0},
              pos: { value: new THREE.Vector3(0,0,0) },
          } 
      
              var Shader = new THREE.ShaderMaterial( {                    
              uniforms: uniform,                  
              vertexShader: VertexShader,
              fragmentShader: FragmentShader, 
              transparent: true,
              depthTest: false,   
              depthWrite: false
          }); 
          
      
      //-------------------------------------------------
      //create a plane: points, normals, uv
          const vertices = [
              { pos: [-20, -20,  0], norm: [ 0,  0,  1], uv: [0, 1], },
              { pos: [ 20, -20,  0], norm: [ 0,  0,  1], uv: [1, 1], },
              { pos: [-20,  20,  0], norm: [ 0,  0,  1], uv: [0, 0], },
              { pos: [ 20,  20,  0], norm: [ 0,  0,  1], uv: [1, 0], },
          ];
      
          const numVertices = vertices.length;
          const positionNumComponents = 3;
          const normalNumComponents = 3;
          const uvNumComponents = 2;
          //arrays for buffergeometry
          const positions = new Float32Array(numVertices * positionNumComponents * MAX_Planes);
          const normals = new Float32Array(numVertices * normalNumComponents * MAX_Planes);
          const uvs = new Float32Array(numVertices * uvNumComponents * MAX_Planes);
        
        //fill arrays with vertices
          var posPointer = 0;
          var nrmPointer = 0;
          var uvPointer = 0;
        
          for(var i = 0; i <= MAX_Planes; i++) {
              var posNdx = 0;
              var nrmNdx = 0;
              var uvNdx = 0;
                  for (const vertex of vertices) {
                      positions.set(vertex.pos, posNdx + posPointer);
                      normals.set(vertex.norm, nrmNdx + nrmPointer);
                      uvs.set(vertex.uv, uvNdx + uvPointer);
                      posNdx += positionNumComponents;
                      nrmNdx += normalNumComponents;
                      uvNdx += uvNumComponents;
                  }
              posPointer = i * posNdx;
              nrmPointer = i * nrmNdx;
              uvPointer = i * uvNdx;
          }
      
          //create buffergeometry and assign the attribut arrays
          geometry = new THREE.BufferGeometry();
          geometry.setAttribute('position', new THREE.BufferAttribute(positions, positionNumComponents));
          geometry.setAttribute('normal', new THREE.BufferAttribute(normals, normalNumComponents));
          geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, uvNumComponents));
      
          var ndx = 0;
          var indices = [];
          //instead 6 vertices for the both triangles of a plane i used 4, so reindication is neccessary
          for(var i = 0; i < MAX_Planes; i++){
              indices.push(ndx, ndx + 1, ndx + 2, ndx + 2, ndx + 1, ndx + 3);
              ndx += 4;
          }
          geometry.setIndex(indices);
      
      
          var materials = [];
          geometry.clearGroups(); 
      
          for(var i = 0; i < MAX_Planes; i++){
              geometry.addGroup( 6*i, 6, i );
              materials.push(Shader.clone()); 
          }
          plane = new THREE.Mesh(geometry, materials);
          scene.add(plane); 
      
      
      //----------------------velocity---------------------------
          
          for(var i = 0; i < MAX_Planes; i++){
              velocity[i] = new THREE.Vector3(
              Math.random()*2-1,
              Math.random()*2-1,
              Math.random()*2-1);
          }   
          
      }//-------End init----------
      
      
      function animate() {
      
          requestAnimationFrame( animate );  
          render();
          
      }//-------End animate----------
      
      var loop = 0;
      function render() {
      
          loop = loop + 0.5;
      
          for(var i = 0; i < MAX_Planes; i++){
          
          var pos = new THREE.Vector3(0, 0, 0);
              
              pos.x += velocity[i].x*loop;
              pos.y += velocity[i].y*loop;
              pos.z += velocity[i].z*loop;
              
              plane.material[i].uniforms.pos.value = pos;
      
          }
          plane.geometry.attributes.position.needsUpdate = true;
      
          camera.updateMatrixWorld();
          camera.updateProjectionMatrix(); 
          renderer.render(scene, camera); 
          
      }//-------End render----------
      

      我在这里设置了 2000 个矩形,并且对代码运行的流畅程度感到惊喜。即使有 5000 个矩形,一切都很顺利,即使每个矩形都有自己的着色器。缓冲几何真的很酷。

      谢谢TheJim01

      如果没有您的建议,我会在 for 循环中预初始化所有着色器。如vertexshader[i]、fragmentshader[i]、uniforms[i]、Shader[i]。使用 “.clone()”当然要好得多?

      【讨论】:

      • Spiri,感谢您分享您的结果、代码和更新的见解。如果我的回答解决了您的问题,请将其标记为已接受,以便其他人可以看到此帖子已解决。
      猜你喜欢
      • 1970-01-01
      • 2018-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-04
      • 1970-01-01
      相关资源
      最近更新 更多