PhongMaterial for InstancedBufferGeometry

时间:2018-05-06 08:55:58

标签: javascript three.js

我需要创建一个包含数千个简单网格的场景,所以我决定使用InstancedBufferGeometry。我从此示例中复制了大部分代码:https://threejs.org/examples/#webgl_buffergeometry_instancing_dynamic

实例化有效,但仅适用于示例中的THREE.RawShaderMaterial

instancedMaterial = new THREE.RawShaderMaterial( {
    uniforms: {
        map: { value: new THREE.TextureLoader().load( 'textures/grassAlpha.png' ) },
        time: { value: 0 }
    },
    vertexShader: document.getElementById( 'vertexShader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );

着色器:

<script id="vertexShader" type="x-shader/x-vertex">
    precision highp float;
    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;
    uniform float time;
    attribute vec3 position;
    attribute vec3 offset;
    attribute vec2 uv;
    attribute vec4 orientation;
    varying vec2 vUv;
    void main() {
        vec3 vPosition = position;
        vec3 vcV = cross( orientation.xyz, vPosition );
        vPosition = vcV * ( 2.0 * orientation.w ) + ( cross( orientation.xyz, vcV ) * 2.0 + vPosition );
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );
    }
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
    precision highp float;
    uniform sampler2D map;
    varying vec2 vUv;
    void main() {
        vec4 texelColor = texture2D( map, vUv );
        if ( texelColor.a < 0.4 ) discard;
        gl_FragColor = texelColor;
    }
</script>

但是如何使用THREE.MeshPhongMaterial代替这个简单的着色器呢?如果我使用其他材质(例如basic或phong)创建网格,它就会消失(或者可能变得完全透明,但控制台中没有错误)。

2 个答案:

答案 0 :(得分:1)

您可以使用https://blender.stackexchange.com/a/18428。代码看起来像这样,只有一个实例属性offset

material.onBeforeCompile = function ( shader ) {
    shader.vertexShader = 'attribute vec3 offset;\n' + shader.vertexShader;
    shader.vertexShader = shader.vertexShader.replace( '#include <begin_vertex>',
       [
         'vec3 transformed = vec3( position + offset );',
       ].join( '\n' )
     );
     materialShader = shader;
};

演示Material.onBeforeCompile

答案 1 :(得分:0)

存在一个npm包,旨在解决这个问题,并为此问题提供用户友好的抽象。

https://www.npmjs.com/package/three-instanced-mesh

It seems to be working with shadows fine.

注意:这是一个副作用,它使它可以与三个所提供的一切一起工作,它不是专门设计用阴影渲染,而是用深度效果正确渲染,所有材料和相机等。

onBeforeCompile可能会破坏某些内容,具体取决于它的使用方式。它似乎更多的是设计而不是错误,只是要注意它:

我认为它是非确定性的 - 如果你将if else放在那里,你可能会根据场景中网格的顺序编制一个不同的着色器。

material.clone()不会克隆整个对象等。在接受的答案中发布的演示实际上就是这种行为的一个很好的例子。 (see this issue on github)。

另一个问题可能是你发现很难&#34;链&#34;需要发生的逻辑。如果您继承了某人已使用onBeforeCompile

的项目
decorate_material_written_by_another_dev( material ){
  ...
  material.onBeforeCompile = shader => {} //oops, what if there is one in there already
  ...
}

接受的答案所指的链接中没有记录这种行为。

重申一下,问题就出现了:

  

我需要创建一个包含数千个简单网格的场景,

如果你想优化数以千计的简单网格,你指的是某种场景图

Scene
|-Mesh0000
|-Mesh0001
...
|-Mesh1000

理想情况下,您不希望与Mesh.positionMesh.scaleMesh.rotation / Mesh.quaternion建立类似的界面,无论优化结果如何?< / p>

换句话说,你不应该使用字符串,着色器,甚至可能不是材料。

想象一下,如果你能做到这一点:

import { InstanceMesh, InstanceMaterial } from 'examples/instancing' //or npm module

const myInstanceMesh = new InstanceMesh(
  new CubeGeometry(), 
  new InstanceMaterial(new MeshStandardMaterial())
)

myInstanceMesh.material.onBeforeCompile = onBeforeCompile_unrelated_to_instancing

如果实施该示例的人使用了onBeforeCompile,则会失败。这就是为什么你没有看到这样的例子。