Three.js - 如何在一个 PointCloud 中使用多个纹理

9
我将使用ShaderMaterial以单个PointCloud中使用多个纹理。我会向着色器传递一个纹理数组以及纹理索引属性,并在片段着色器中选择要使用的适当纹理。
相关设置代码:
var particleCount = 100;

var uniforms = {
    textures: {
        type: 'tv',
        value: this.getTextures()
    }
};

var attributes = {
    texIndex: {
        type: 'f',
        value: []
    },
    color: {
        type: 'c',
        value: []
    },
};

var material = new THREE.ShaderMaterial({
    uniforms: uniforms,
    attributes: attributes,
    vertexShader: document.getElementById('vertexShader').textContent,
    fragmentShader: document.getElementById('fragmentShader').textContent,
    transparent: true
});

var geometry = new THREE.Geometry();

for (var i = 0; i < particleCount; i++) {
    geometry.vertices.push(new THREE.Vector3(
    (Math.random() - 0.5) * 50, (Math.random() - 0.5) * 50, (Math.random() - 0.5) * 50));
    attributes.texIndex.value.push(Math.random() * 3 | 0);
    attributes.color.value.push(new THREE.Color(0xffffff));
}

var particles = new THREE.PointCloud(geometry, material);
particles.sortParticles = true;
this.container.add(particles);

顶点着色器:

attribute vec3 color;
attribute float texIndex;

varying vec3 vColor;
varying float vTexIndex;

void main() {
    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);

    vColor = color;
    vTexIndex = texIndex;

    gl_PointSize = 50.0;
    gl_Position = projectionMatrix * mvPosition;
}

片元着色器:

uniform sampler2D textures[3];

varying vec3 vColor;
varying float vTexIndex;

void main() {
    vec4 startColor = vec4(vColor, 1.0);
    vec4 finalColor;

    if (vTexIndex == 0.0) {
        finalColor = texture2D(textures[0], gl_PointCoord);
    } else if (vTexIndex == 1.0) {
        finalColor = texture2D(textures[1], gl_PointCoord);
    } else if (vTexIndex == 2.0) {
        finalColor = texture2D(textures[2], gl_PointCoord);
    }

    gl_FragColor = startColor * finalColor;
}

问题是一些点(那些使用纹理索引高于0的点)闪烁,原因不明。其他尝试看起来也会在不透明度和纹理之间闪烁。
可以在http://jsfiddle.net/6qrubbk6/4/上看到这个问题的示例。
我在多个项目中都放弃了解决此问题,但我很想找到一个彻底的解决方案。非常感谢任何帮助。
编辑:检查vTexIndex是否小于n,而不是等于n,可以解决这个问题。
if (vTexIndex < 0.5) {
    finalColor = texture2D(textures[0], gl_PointCoord);
} else if (vTexIndex < 1.5) {
    finalColor = texture2D(textures[1], gl_PointCoord);
} else if (vTexIndex < 2.5) {
    finalColor = texture2D(textures[2], gl_PointCoord);
}

As seen here: http://jsfiddle.net/6qrubbk6/5/

3个回答

2

感谢您回答自己的问题。您帮助我启动了一个类似的功能。

我认为这可能对其他人有所帮助,所以我在这里回复。

我创建了一个fiddle,可以实现您正在进行的操作,但是是动态的。您可以将尽可能多的纹理添加到纹理数组中,并且它们将被动态添加到节点中。这在glsl中很棘手,需要一些hacky javascript模板。

为此,我只需创建两个方法,返回顶点和片段着色器到着色器材质:

片段着色器方法:

World.prototype.getFragmentShader = function(numTextures){
var fragShader =  `uniform sampler2D textures[${numTextures}];

varying vec3 vColor;
varying float vTexIndex;

void main() {
    vec4 startColor = vec4(vColor, 1.0);
    vec4 finalColor;

    `;
  for(var i = 0; i < numTextures; i++){
    if(i == 0){ 
      fragShader += `if (vTexIndex < ${i}.5) {
        finalColor = texture2D(textures[${i}], gl_PointCoord);
        }
      `
    }
    else{
      fragShader += `else if (vTexIndex < ${i}.5) {
        finalColor = texture2D(textures[${i}], gl_PointCoord);
        }
      `
    }
  }
fragShader += `gl_FragColor = startColor * finalColor;
}`;

console.log('frag shader: ', fragShader)
return fragShader;
}

顶点着色器:

World.prototype.getVertexShader = function(){

let vertexShader = `attribute vec3 color;
attribute float texIndex;

varying vec3 vColor;
varying float vTexIndex;

void main() {
    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);

    vColor = color;
    vTexIndex = texIndex;

    gl_PointSize = 50.0;
    gl_Position = projectionMatrix * mvPosition;
}`;

return vertexShader;
}

你��以在这里查看实时演示:http://jsfiddle.net/jigglebilly/drmvz5co/

2

你也可以将vTexIndex转换为整数类型。

int textureIndex = int(vTexIndex + 0.5);

if (textureIndex == 0) {
    finalColor = texture2D(textures[0], gl_PointCoord);
} else if (textureIndex == 1) {
    finalColor = texture2D(textures[1], gl_PointCoord);
} else if (textureIndex == 2) {
    finalColor = texture2D(textures[2], gl_PointCoord);
}

0

Three.js的新版本不支持ShaderMaterial中的attributes。我们需要在new THREE.ShaderMaterial中删除attributes: attributes,并改用geometry.addAttribute。以下是定义texIndex的代码:

var vIndex = new Float32Array( vertices.length );
for ( var i = 0, l = vertices.length; i < l; i ++ ) {
        vIndex[i] = Math.random()*getTextures().length;
}
geometry.addAttribute( 'texIndex', new THREE.BufferAttribute( vIndex, 1 ) );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接