InstanceGeometry中的three.js纹理

时间:2018-10-08 19:54:38

标签: three.js

我正在使用InstanceGeometry渲染场景中的数千个基本几何(框)。它是高效的,仅使用一种材质/纹理,但会为每个实例重复图像纹理。

我正在尝试找出如何使纹理分布在x个实例上。假设有8个盒子实例,我希望1/8的纹理出现在每个盒子上。

我认为THREE.Texture上的transformUV函数是我想使用的,但是我不确定如何在这种情况下使用它。或者,纹理映射是否会在Shader本身中发生?

更新

我自己的代码非常复杂,并且使用了适合实例的内置three.js材料,因此让我们仅使用Three.js示例之一作为起点:https://github.com/mrdoob/three.js/blob/master/examples/webgl_buffergeometry_instancing_dynamic.html

也简短地粘贴在下面。

顶点着色器:

precision highp float;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
attribute vec3 position;
attribute vec3 offset;
attribute vec2 uv;
attribute vec4 orientation;
varying vec2 vUv;

// http://www.geeks3d.com/20141201/how-to-rotate-a-vertex-by-a-quaternion-in-glsl/
vec3 applyQuaternionToVector( vec4 q, vec3 v ){
    return v + 2.0 * cross( q.xyz, cross( q.xyz, v ) + q.w * v );
}

void main() {
    vec3 vPosition = applyQuaternionToVector( orientation, position );
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );
}

片段着色器

precision highp float;
uniform sampler2D map;
varying vec2 vUv;

void main() {
    gl_FragColor = texture2D( map, vUv );
}

JS:

var instances = 50;
var bufferGeometry = new THREE.BoxBufferGeometry( 2, 2, 2 );

var geometry = new THREE.InstancedBufferGeometry();
geometry.index = bufferGeometry.index;
geometry.attributes.position = bufferGeometry.attributes.position;
geometry.attributes.uv = bufferGeometry.attributes.uv;

// per instance data
var offsets = [];
var orientations = [];
var vector = new THREE.Vector4();
var x, y, z, w;

for ( var i = 0; i < instances; i ++ ) {

    // offsets
    x = Math.random() * 100 - 50;
    y = Math.random() * 100 - 50;
    z = Math.random() * 100 - 50;

    vector.set( x, y, z, 0 ).normalize();
    vector.multiplyScalar( 5 );

    offsets.push( x + vector.x, y + vector.y, z + vector.z );

    // orientations
    x = Math.random() * 2 - 1;
    y = Math.random() * 2 - 1;
    z = Math.random() * 2 - 1;
    w = Math.random() * 2 - 1;

    vector.set( x, y, z, w ).normalize();
    orientations.push( vector.x, vector.y, vector.z, vector.w );
}

offsetAttribute = new THREE.InstancedBufferAttribute( new Float32Array( offsets ), 3 );
orientationAttribute = new THREE.InstancedBufferAttribute( new Float32Array( orientations ), 4 ).setDynamic( true );

geometry.addAttribute( 'offset', offsetAttribute );
geometry.addAttribute( 'orientation', orientationAttribute );

// material
var material = new THREE.ShaderMaterial( {
    uniforms: {
        map: { value: new THREE.TextureLoader().load( 'textures/crate.gif' ) }          },
    vertexShader: document.getElementById( 'vertexShader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );

mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );

1 个答案:

答案 0 :(得分:2)

您将不得不创建一个附加的自定义属性来保存UV的偏移量,就像您要创建一个包含x, y, z偏移量但带有u, v的属性一样。

首先,您将其添加到JavaScript中:

var uvOffsets = [];
var u, v;
for ( var i = 0; i < instances; i ++ ) {
    //... inside the loop
    u = Math.random(); // I'm assigning random, but you can do the math...
    v = Math.random(); // ... to make it discrete 1/8th amounts
    uvOffsets.push(u, v);
}

// Add new attribute to BufferGeometry
var uvOffsetAttribute = new THREE.InstancedBufferAttribute( new Float32Array( uvOffsets ), 2 );
geometry.addAttribute( 'uvOffset', uvOffsetAttribute );

然后,在您的顶点着色器中:

// [...]
attribute vec2 uv;
attribute vec2 uvOffset;
varying vec2 vUv;

void main() {
    vec3 vPosition = applyQuaternionToVector( orientation, position );

    // Divide uvs by 8, and add assigned offsets
    vUv = (uv / 8.0) + uvOffset;

    gl_Position = projectionMatrix * modelViewMatrix * vec4( offset + vPosition, 1.0 );
}

最后,在您的片段着色器中:

precision highp float;
uniform sampler2D map;
uniform vec2 uvOffset;
varying vec2 vUv; // <- these UVs have been transformed by vertex shader.

void main() {
    gl_FragColor = texture2D( map, vUv ); // <- Transformation is applied to texture
}