每个实例具有独特纹理的 InstancedMesh

cha*_*gar 7 three.js

我正在寻找一种方法来绘制具有独特纹理的多个对象。我遇到了这个关于instancedMesh的老问题,其中有人获得了具有不同纹理的多个实例,但在桌面上,纹理有奇怪的伪影。最初我认为该演示一定有问题,但对我来说一切似乎都很好,我还尝试使用混合函数代替条件,但纹理仍然有伪影。

我一直在寻找不同的方法来绘制多个独特的几何图形,因此合并几何图形不是一个选项,但我得到的大多数结果都是针对具有合并几何图形的多个对象。如果有人可以提供一些指导,那就太好了。

    var camera, scene, renderer, stats;

    var mesh;
    var amount = parseInt( window.location.search.substr( 1 ) ) || 10;
    var count = Math.pow( amount, 3 );

    var raycaster = new THREE.Raycaster();
    var mouse = new THREE.Vector2( 1, 1 );

    var rotationTheta = 0.1;
    var rotationMatrix = new THREE.Matrix4().makeRotationY( rotationTheta );
    var instanceMatrix = new THREE.Matrix4();
    var matrix = new THREE.Matrix4();

    init();
    animate();

    function init() {

        camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
        camera.position.set( amount, amount, amount );
        camera.lookAt( 0, 0, 0 );

        scene = new THREE.Scene();

        var light = new THREE.HemisphereLight( 0xffffff, 0x000088 );
        light.position.set( - 1, 1.5, 1 );
        scene.add( light );

        var light = new THREE.HemisphereLight( 0xffffff, 0x880000, 0.5 );
        light.position.set( - 1, - 1.5, - 1 );
        scene.add( light );

        var geometry = new THREE.BoxBufferGeometry( .5, .5, .5, 1, 1, 1 );

        var material = [
            new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/square-outline-textured.png' ) } ),
            new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/golfball.jpg' ) } ),
            new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/metal.jpg' ) } ),
            new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/roughness_map.jpg' ) } ),
            new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/tri_pattern.jpg' ) } ),
            new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/water.jpg' ) } ),
        ];

        material.forEach((m,side)=>{
            if ( side!=2 ) return;

            m.onBeforeCompile = ( shader ) => {

                shader.uniforms.textures = { 'type': 'tv', value: [
                    new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/crate.gif' ),
                    new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/equirectangular.png' ),
                    new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/colors.png' )
                 ] };


                shader.vertexShader = shader.vertexShader.replace(
                        '#define STANDARD',
                        `#define STANDARD
                        varying vec3 vTint;
                        varying float vTextureIndex;`
                ).replace(
                    '#include <common>',
                    `#include <common>
                    attribute vec3 tint;
                    attribute float textureIndex;`
                ).replace(
                    '#include <project_vertex>',
                    `#include <project_vertex>
                    vTint = tint;
                    vTextureIndex=textureIndex;`
                );

                shader.fragmentShader = shader.fragmentShader.replace(
                        '#define STANDARD',
                        `#define STANDARD
                        uniform sampler2D textures[3];
                        varying vec3 vTint;
                        varying float vTextureIndex;`
                )
                .replace(
                    '#include <fog_fragment>',
                    `#include <fog_fragment>
                    int texIdx = int(vTextureIndex);
                    vec4 col;
                    if (texIdx == 0) {
                            col = texture2D(textures[0], vUv );
                        } else if ( texIdx==1) {
                            col = texture2D(textures[1], vUv );
                        } else if ( texIdx==2) {
                                col = texture2D(textures[2], vUv );
                            }

                            gl_FragColor = col;
                    //      gl_FragColor.rgb *= vTint;`

                )
                ;
            }
        });

        mesh = new THREE.InstancedMesh( geometry, material, count );

        var i = 0;
        var offset = ( amount - 1 ) / 2;

        var transform = new THREE.Object3D();
        var textures = [];

        for ( var x = 0; x < amount; x ++ ) {

            for ( var y = 0; y < amount; y ++ ) {

                for ( var z = 0; z < amount; z ++ ) {

                    transform.position.set( offset - x, offset - y, offset - z );
                    transform.updateMatrix();

                    mesh.setMatrixAt( i ++, transform.matrix );


                    textures.push(Math.random()<0.3 ? 0 : (Math.random()<0.5 ? 1 : 2));
                }

            }

        }

        geometry.setAttribute( 'textureIndex',
              new THREE.InstancedBufferAttribute( new Float32Array(textures), 1 ) );

        scene.add( mesh );


        renderer = new THREE.WebGLRenderer( { antialias: false } );
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( renderer.domElement );

        new THREE.OrbitControls( camera, renderer.domElement );

        stats = new Stats();
        document.body.appendChild( stats.dom );

        window.addEventListener( 'resize', onWindowResize, false );
        document.addEventListener( 'mousemove', onMouseMove, false );

    }

    function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize( window.innerWidth, window.innerHeight );

    }

    function onMouseMove( event ) {

        event.preventDefault();

        mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
        mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

    }

    function animate() {

        requestAnimationFrame( animate );

        render();

    }

    function render() {

        raycaster.setFromCamera( mouse, camera );

        var intersection = raycaster.intersectObject( mesh );
// console.log('intersection', intersection.length);
        if ( intersection.length > 0 ) {

            mesh.getMatrixAt( intersection[ 0 ].instanceId, instanceMatrix );
            matrix.multiplyMatrices( instanceMatrix, rotationMatrix );

            mesh.setMatrixAt( intersection[ 0 ].instanceId, matrix );
            mesh.instanceMatrix.needsUpdate = true;

        }

        renderer.render( scene, camera );

        stats.update();

    }
Run Code Online (Sandbox Code Playgroud)
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
Run Code Online (Sandbox Code Playgroud)

Mar*_*zzo 8

我相信您的问题来自于将 a 转换float为 an int,然后使用它来创建分支。此错误仅出现在少数 GPU 中,而不是全部。vTextureIndex我通过保持as来工作float,对所有 3 个纹理进行采样,如果匹配则将每个纹理乘以 1 textureIndex,如果textureIndex不匹配则将每个纹理乘以 0。

我基本上替换了这些行:

int texIdx = int(vTextureIndex);
vec4 col;
if (texIdx == 0) {
    col = texture2D(textures[0], vUv );
} else if ( texIdx==1) {
    col = texture2D(textures[1], vUv );
} else if ( texIdx==2) {
    col = texture2D(textures[2], vUv );
}
Run Code Online (Sandbox Code Playgroud)

用这种方法:

float x = vTextureIndex;
vec4 col;
col = texture2D(textures[0], vUv ) * step(-0.1, x) * step(x, 0.1);
col += texture2D(textures[1], vUv ) * step(0.9, x) * step(x, 1.1);
col += texture2D(textures[2], vUv ) * step(1.9, x) * step(x, 2.1);
Run Code Online (Sandbox Code Playgroud)
  • 如果textureIndex为0,则第一个纹理乘以1,其他纹理乘以0
  • 如果textureIndex为1,则第二个纹理乘以1,其他纹理乘以0
  • 如果textureIndex是2,则第三个纹理乘以1,其他纹理乘以0

在此输入图像描述

var camera, scene, renderer, stats;

    var mesh;
    var amount = parseInt( window.location.search.substr( 1 ) ) || 10;
    var count = Math.pow( amount, 3 );

    var raycaster = new THREE.Raycaster();
    var mouse = new THREE.Vector2( 1, 1 );

    var rotationTheta = 0.1;
    var rotationMatrix = new THREE.Matrix4().makeRotationY( rotationTheta );
    var instanceMatrix = new THREE.Matrix4();
    var matrix = new THREE.Matrix4();

    init();
    animate();

    function init() {

        camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
        camera.position.set( amount, amount, amount );
        camera.lookAt( 0, 0, 0 );

        scene = new THREE.Scene();

        var light = new THREE.HemisphereLight( 0xffffff, 0x666666 );
        light.position.set( - 1, 1.5, 1 );
        scene.add( light );

        var light = new THREE.HemisphereLight( 0xffffff, 0x666666, 0.5 );
        light.position.set( - 1, - 1.5, - 1 );
        scene.add( light );

        var geometry = new THREE.BoxBufferGeometry( .5, .5, .5, 1, 1, 1 );

        var material = [
            new THREE.MeshStandardMaterial({color: 0xff9900}),
            new THREE.MeshStandardMaterial({color: 0xff0099}),
            new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/metal.jpg' ) } ),
            new THREE.MeshStandardMaterial({color: 0x9900ff}),
            new THREE.MeshStandardMaterial({color: 0x0099ff}),
            new THREE.MeshStandardMaterial({color: 0x99ff00}),
        ];

        material.forEach((m,side)=>{
            if ( side!=2 ) return;

            m.onBeforeCompile = ( shader ) => {

                shader.uniforms.textures = { 'type': 'tv', value: [
                    new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/crate.gif' ),
                    new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/sprite0.png' ),
                    new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/sprite.png' )
                 ] };


                shader.vertexShader = shader.vertexShader.replace(
                        '#define STANDARD',
                        `#define STANDARD
                        varying vec3 vTint;
                        varying float vTextureIndex;`
                ).replace(
                    '#include <common>',
                    `#include <common>
                    attribute vec3 tint;
                    attribute float textureIndex;`
                ).replace(
                    '#include <project_vertex>',
                    `#include <project_vertex>
                    vTint = tint;
                    vTextureIndex=textureIndex;`
                );

                shader.fragmentShader = shader.fragmentShader.replace(
                        '#define STANDARD',
                        `#define STANDARD
                        uniform sampler2D textures[3];
                        varying vec3 vTint;
                        varying float vTextureIndex;`
                )
                .replace(
                    '#include <fog_fragment>',
                    `#include <fog_fragment>
                    float x = vTextureIndex;
                    vec4 col;
                    col = texture2D(textures[0], vUv ) * step(-0.1, x) * step(x, 0.1);
                    col += texture2D(textures[1], vUv ) * step(0.9, x) * step(x, 1.1);
                    col += texture2D(textures[2], vUv ) * step(1.9, x) * step(x, 2.1);

                    gl_FragColor = col;
                    `

                )
                ;
            }
        });

        mesh = new THREE.InstancedMesh( geometry, material, count );

        var i = 0;
        var offset = ( amount - 1 ) / 2;

        var transform = new THREE.Object3D();
        var textures = [];

        for ( var x = 0; x < amount; x ++ ) {

            for ( var y = 0; y < amount; y ++ ) {

                for ( var z = 0; z < amount; z ++ ) {

                    transform.position.set( offset - x, offset - y, offset - z );
                    transform.updateMatrix();

                    mesh.setMatrixAt( i ++, transform.matrix );


                    textures.push(Math.random()<0.3 ? 0 : (Math.random()<0.5 ? 1 : 2));
                }

            }

        }

        geometry.setAttribute( 'textureIndex',
              new THREE.InstancedBufferAttribute( new Float32Array(textures), 1 ) );

        scene.add( mesh );


        renderer = new THREE.WebGLRenderer( { antialias: false } );
        renderer.setPixelRatio( window.devicePixelRatio );
        renderer.setSize( window.innerWidth, window.innerHeight );
        document.body.appendChild( renderer.domElement );

        new THREE.OrbitControls( camera, renderer.domElement );

        stats = new Stats();
        document.body.appendChild( stats.dom );

        window.addEventListener( 'resize', onWindowResize, false );
        document.addEventListener( 'mousemove', onMouseMove, false );

    }

    function onWindowResize() {

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize( window.innerWidth, window.innerHeight );

    }

    function onMouseMove( event ) {

        event.preventDefault();

        mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
        mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;

    }

    function animate() {

        requestAnimationFrame( animate );

        render();

    }

    function render() {

        raycaster.setFromCamera( mouse, camera );

        var intersection = raycaster.intersectObject( mesh );
// console.log('intersection', intersection.length);
        if ( intersection.length > 0 ) {

            mesh.getMatrixAt( intersection[ 0 ].instanceId, instanceMatrix );
            matrix.multiplyMatrices( instanceMatrix, rotationMatrix );

            mesh.setMatrixAt( intersection[ 0 ].instanceId, matrix );
            mesh.instanceMatrix.needsUpdate = true;

        }

        renderer.render( scene, camera );

        stats.update();

    }
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdn.jsdelivr.net/npm/three@0.140.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.140.0/examples/js/libs/stats.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.140.0/examples/js/controls/OrbitControls.js"></script>
Run Code Online (Sandbox Code Playgroud)