如何将具有透明点的纹理正确应用于threejs中的多个堆叠平面实例?

时间:2017-11-09 09:58:26

标签: three.js

我正在使用具有透明区域的纹理创建相同1x1平面的512个实例。这些平面随机散布在原点周围,如下图所示。

前面的飞机如何在后面的平面之后绘制,以便前面的飞机的透明度考虑到后面的飞机输出?

(禁用了depthTest) scene problem (deepTest正常) scene problem, variation 1

作为参考,实例化几何体的透明度禁用版本。这证明了飞机的位置正确。

no transparency for position proof

更新

按要求添加代码:

import {


Mesh,
  ShaderMaterial,
  Vector3,
  PlaneBufferGeometry,
  EdgesGeometry,
  LineBasicMaterial,
  LineSegments,
  InstancedBufferAttribute,
  UniformsLib,
  BufferAttribute,
  TextureLoader,
  InstancedBufferGeometry,
  DoubleSide,
} from 'three'

import path from 'path'
import fs from 'fs'

import {
  randomValueBetween,
} from '../../utils'

const vertexShader = fs.readFileSync(path.resolve(__dirname, './assets/vertex.glsl'), 'utf8')
const fragmentShader = fs.readFileSync(path.resolve(__dirname, './assets/fragment.glsl'), 'utf8')

const createInstancedAtrributes = (geometry, instanceCount) => {
  const startseed = new InstancedBufferAttribute(new Float32Array(instanceCount * 1), 1)
  const scale = new InstancedBufferAttribute(new Float32Array(instanceCount * 3), 3)
  const offset = new InstancedBufferAttribute(new Float32Array(instanceCount * 2), 2)
  const orientationY = new InstancedBufferAttribute(new Float32Array(instanceCount), 1)
  const baseScale = 0.5

  for (let i = 0; i < instanceCount; i += 1) {
    scale.setXYZ(i,
      baseScale * randomValueBetween(0.8, 1.3, 1),
      baseScale * randomValueBetween(0.8, 1.3, 1),
      baseScale * randomValueBetween(0.8, 1.3, 1),
    )

    orientationY.setX(i, randomValueBetween(0.0, 1.0, 3))
    startseed.setX(i, randomValueBetween(1, 3, 1))
  }

  for (let i = 0; i < instanceCount / 4; i += 4) {
    const randomX = randomValueBetween(-3.5, 3.5, 1)
    const randomY = randomValueBetween(-3.5, 3.5, 1)
    offset.setXY(i, randomX, randomY)
  }

  geometry.addAttribute('scale', scale)
  geometry.addAttribute('offset', offset)
  geometry.addAttribute('startseed', offset)
  geometry.addAttribute('orientationY', offset)

  return { scale, offset }
}

const createInstancedGeometry = (instancePerUnitCount) => {
  const geometry = new InstancedBufferGeometry()
  geometry.maxInstancedCount = instancePerUnitCount

  const shape = new PlaneBufferGeometry(1, 1, 1, 3)

  const data = shape.attributes

  geometry.addAttribute('position', new BufferAttribute(new Float32Array(data.position.array), 3))
  geometry.addAttribute('uv', new BufferAttribute(new Float32Array(data.uv.array), 2))
  geometry.addAttribute('normal', new BufferAttribute(new Float32Array(data.normal.array), 3))
  geometry.setIndex(new BufferAttribute(new Uint16Array(shape.index.array), 1))
  shape.dispose()

  createInstancedAtrributes(geometry, instancePerUnitCount)

  return geometry
}

export default class GrassDeform extends Mesh {
  constructor() {
    const geometry = createInstancedGeometry(8 * 256)

    const uniforms = {
      uTime: {
        type: 'f',
        value: 0,
      },
      uMap: {
        type: 't',
        value: null,
      },
    }

    const textureLoader = new TextureLoader()
    textureLoader.load(path.resolve(__dirname, './assets/grass-texture-01.png'), (t) => {
      uniforms.uMap.value = t
    })

    const material = new ShaderMaterial({
      uniforms: Object.assign({},
        UniformsLib.ambient,
        UniformsLib.lights,
        uniforms,
      ),
      vertexShader,
      fragmentShader,
      lights: true,
      transparent: true,
      side: DoubleSide,
    })


    super(geometry, material)

    this.geometry = geometry
    this.material = material
    this.up = new Vector3(0, 0, 1)

    const lineGeo = new EdgesGeometry(geometry) // or WireframeGeometry
    const mat = new LineBasicMaterial({ color: 0xffffff, linewidth: 2 })
    const wireframe = new LineSegments(lineGeo, mat)
    this.add(wireframe)

    this.frustumCulled = false
  }

  update({ ellapsedTime }) {
    this.material.uniforms.uTime.value = ellapsedTime
  }
}

然后将对象添加到场景中:

const grass2 = new GrassDeform2()
grass2.position.set(-1, 0, 0.50)
grass2.rotateX(Math.PI / 2)
scene.add(grass2)
dirLight.target = grass2


const animate = (ellapsedTime = 0) => {
  stats.begin()
  grass2.update({ ellapsedTime })
  /// other scene stuff
  renderer.render(scene, playerController.camera)
  requestAnimationFrame(animate)
}

animate()

顶点着色器:

#if NUM_DIR_LIGHTS > 0
struct DirectionalLight {
  vec3 direction;
  vec3 color;
  int shadow;
  float shadowBias;
  float shadowRadius;
  vec2 shadowMapSize;
};
uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ];
#endif

uniform float uTime;

attribute vec2 offset;
attribute vec3 scale;
attribute float startseed;

varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vDirectionalLightDirection;
varying vec3 vDirectionalLightColor;
varying vec3 uNormal;

void main() {
  vec3 pos = position * scale;

  pos.x += offset.x;
  pos.z += offset.y;
  pos.y += (scale.y - 1.0) * 0.5;

  pos.y = orientationY

  vPosition = pos;
  uNormal = normal;


  vUv = uv;
  uNormal = normal;
  vDirectionalLightDirection = directionalLights[0].direction;
  vDirectionalLightColor = directionalLights[0].color;


  float variation = startseed + uTime * 0.002;
  float pass = (0.5 + pos.y) * 0.05;

  pos.x += sin(pass + variation) * pass;
  pos.z += cos(pass + variation + 0.01) * pass;
  pos.y += sin(pass + variation - 0.01) * pass;

  gl_Position = projectionMatrix * modelViewMatrix * vec4(pos,1.0);
}

片段着色器(有一些额外的光源,暂时没有添加):

uniform sampler2D uMap;

varying vec2 vUv;
varying vec3 vPosition;
varying vec3 vDirectionalLightDirection;
varying vec3 vDirectionalLightColor;
varying vec3 uNormal;

void main() {
  vec4 map = texture2D(uMap, vUv);

  vec3 lightVector = normalize((vDirectionalLightDirection) - vPosition);
  float dotNL = dot( uNormal, lightVector );

  vec3 baseColor = map.rgb;

  vec3 lightedColor = vDirectionalLightColor * 0.6 * dotNL;

  if ( map.a < 0.5 ) discard; //!!! THIS WAS THE LINE NEEDED TO SOLVE THE ISSUE

  gl_FragColor = vec4( map.rgb , 1 );
}

在应用最终结果的更改后,场景看起来正确!

working version

1 个答案:

答案 0 :(得分:3)

您可以使用Alpha测试解决您的问题。在片段着色器中使用如下图案:

vec4 texelColor = texture2D( map, vUv );

if ( texelColor.a < 0.5 ) discard;

您的素材不再需要transparent = true,因为您似乎使用了纹理alpha为0或1的剪切。

three.js r.88