使用贴纸贴花
更新时间: 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
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
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
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
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
2
3
4
5
6
贴花应用于模型会比这个复杂,因为模型并不是一个单一的网格,它是一组网格
我们移除小球,并导入一个模型
BABYLON.ImportMeshAsync('/skateboard_mesh.glb', scene).then(res => {
})
1
2
3
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
现在可以了,最后再解决一个问题,让默认的环境天空不被点击事件捕获
scene.getMeshByName('BackgroundSkyBox').isPickable = false
1