使用贴纸贴花

更新时间: 2025-04-08 10:47:20

本节学习如何给模型贴上额外的纹理,也叫做贴花

# Decal

场景中有一个球体,我们想使用光线投射在上面贴上一些贴纸

我们给场景绑定点击事件,点击到小球的时候创建一个Decal

scene.onPointerDown = function castRay() {
    const hit = scene.pick(scene.pointerX, scene.pointerY)
    if(hit.pickedMesh && hit.pickedMesh.name === 'sphere') {
      const decal = BABYLON.MeshBuilder.CreateDecal('decal',sphere, {
        position: hit.pickedPoint
      })
    }
  }
1
2
3
4
5
6
7
8

不过现在看起来什么变化都没有,因为贴画只是创建了并没有材质,我们赋予一个材质

 
 







 



  const mat = new BABYLON.StandardMaterial()
  mat.diffuseColor = BABYLON.Color3.Red()  

  scene.onPointerDown = function castRay() {
    const hit = scene.pick(scene.pointerX, scene.pointerY)
    if(hit.pickedMesh && hit.pickedMesh.name === 'sphere') {
      const decal = BABYLON.MeshBuilder.CreateDecal('decal',sphere, {
        position: hit.pickedPoint
      })
      decal.material = mat
    }
  }
1
2
3
4
5
6
7
8
9
10
11
12


现在可以显示了,但是和小球发生了z-fighting,因为decal和小球表面在相同的Z位置进行渲染

为了解决这个问题,我将在贴画材质上添加一个z偏移

mat.zOffset = -1
1

现在我们将材质的diffuseTexture设置一下

  const mat = new BABYLON.StandardMaterial()
  const texture = new BABYLON.Texture('/vite.svg')
  mat.diffuseTexture = texture
  mat.zOffset = -1
1
2
3
4

现在有图案了但是背景是黑色的,我们的背景明明是透明的

我们只需要将贴花材质的alpha属性设置一下

mat.diffuseTexture.hasAlpha = true
1

现在又发现了新的问题,纹理有奇怪的缩放扭曲

为了解决这个问题,我们要将小球法线传递给贴花



 


const decal = BABYLON.MeshBuilder.CreateDecal('decal',sphere, {
    position: hit.pickedPoint,
    normal: hit.getNormal(true)
})
1
2
3
4

现在贴花就正确了,我们可以分别使用size和angle属性来更改贴花尺寸并旋转它




 
 


const decal = BABYLON.MeshBuilder.CreateDecal('decal',sphere, {
    position: hit.pickedPoint,
    normal: hit.getNormal(true),
    size: new BABYLON.Vector3(0.5, 0.5, 0.5),
    angle: Math.PI / 2
})
1
2
3
4
5
6

贴花应用于模型会比这个复杂,因为模型并不是一个单一的网格,它是一组网格

我们移除小球,并导入一个模型

 BABYLON.ImportMeshAsync('/skateboard_mesh.glb', scene).then(res => {

  })
1
2
3

为了知道模型的名字,我们将Inspector打开

Inspector.Show(scene,{})
1

 BABYLON.ImportMeshAsync('/skateboard_mesh.glb', scene).then(res => {
    const mesh = scene.getMeshByName('skateboard_low')  

    scene.onPointerDown = function castRay() {
      const hit = scene.pick(scene.pointerX, scene.pointerY)
      if(hit.pickedMesh && hit.pickedMesh.name === 'skateboard_low') {
        const decal = BABYLON.MeshBuilder.CreateDecal('decal',mesh, {
          position: hit.pickedPoint,
          normal: hit.getNormal(true),
          size: new BABYLON.Vector3(0.1, 0.1, 0.1),
          angle: Math.PI / 2
        })
        decal.material = mat
      }
    }
  })

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

现在可以了,最后再解决一个问题,让默认的环境天空不被点击事件捕获

scene.getMeshByName('BackgroundSkyBox').isPickable = false 
1