буферные геометрические группы с отдельными шейдерами - PullRequest
1 голос
/ 03 августа 2020

Думаю, лучше всего показать пример кода. Поэтому я создал небольшой пример, показывающий проблему. Я хочу создать буферную геометрию, в которой каждая группа имеет свой шейдер. хотя я всегда создаю новый экземпляр в массиве материалов, я не могу использовать униформы отдельных шейдеров независимо. то, что я настраиваю в униформе одного шейдера в массиве, всегда одинаково влияет на все остальные шейдеры в массиве материалов. Прежде чем я спрошу, я пытаюсь продвинуться в исследовании, но здесь я достиг точки, когда не могу продвинуться дальше. Кто-нибудь знает, почему отдельные шейдеры в массиве материалов зависят друг от друга и как этого избежать?

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----------

Ответы [ 2 ]

2 голосов
/ 03 августа 2020

Вы вставляете ссылки на один 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     
}
0 голосов
/ 04 августа 2020

это не оставило меня в покое, и мне пришло в голову, что если я предварительно выделю геометрию буфера, мне придется сделать это с шейдерами для каждой группы. Я бы сейчас сделал это намного сложнее, чем с ".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

без вашего совета я бы предварительно инициализировал все шейдеры в a для l oop. Такие как vertexshader [i], fragmentshader [i], uniforms [i], Shader [i]. Использование ".clone ()", конечно, намного лучше ?

...