Babylon.js-6 材质

参考链接:https://doc.babylonjs.com/babylon101/materials

我们可以在将材质附着在网格上实现渲染,可以设置颜色以及纹理。一个材质对象可以附着在多个网格上。

对光的反应

一个材质对象对光的反应取决于以下几项:

  1. 漫反射 散射(Diffuse)在光的照射下的材质的基本颜色或纹理
  2. 镜面反射(Specular) 高亮照射下材质的颜色或纹理
  3. 放射(Emissive)材质自身发射的颜色或纹理
  4. 环境(Ambient)由环境照亮时材质的颜色或纹理

漫反射和镜面反射需要创建一个光源。环境颜色需要场景的环境颜色设置,给出一个环境背景光。

1
scene.ambientColor = new BABYLON.Color3(1, 1, 1)

透明度 - 对于具有透明部分的图像,可以使用它以使材料的适当部分不可见。 这需要设置alpha属性。

颜色 (Color)

使用以下代码创建一个标准材质StandardMaterial

1
const myMaterial = new BABYLON.StandardMaterial("myMaterial", scene)

我们可以设置myMaterial的属性,对于颜色有以下属性

  • diffuseColor
  • specularColor
  • emissiveColor
  • ambientColor
对于环境光颜色ambientColor,我们需要设置场景的环境光颜色才能够生效。

使用方法如下:

1
2
3
4
5
6
7
8
var myMaterial = new BABYLON.StandardMaterial("myMaterial", scene)

myMaterial.diffuseColor = new BABYLON.Color3(1, 0, 1)
myMaterial.specularColor = new BABYLON.Color3(0.5, 0.6, 0.87)
myMaterial.emissiveColor = new BABYLON.Color3(1, 1, 1)
myMaterial.ambientColor = new BABYLON.Color3(0.23, 0.98, 0.53)

mesh.material = myMaterial

我们创建一个场景来说明材质的颜色,我们使用BABYLON提供的创建场景方法创建了一个场景,同时对其进行修改。我们使用一个平行光HemisphericLight来观察确认材质的颜色。我们调整平行光的强度,但不超过最强强度的一半(防止看不到聚光灯照射的效果),观察光在材质上的渲染效果(这里使用了Babylon的GUI):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
var createSceneColor = function (engine,canvas) {
var scene = new BABYLON.Scene(engine)
var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 3, 10, new BABYLON.Vector3(-1,0,0), scene)
camera.attachControl(canvas, false)
var mats = [
new BABYLON.Color3(1, 1, 0),
new BABYLON.Color3(1, 0, 1),
new BABYLON.Color3(0, 1, 1),
new BABYLON.Color3(1, 1, 1)
]
var redMat = new BABYLON.StandardMaterial("redMat", scene)
redMat.emissiveColor = new BABYLON.Color3(1, 0, 0)
var greenMat = new BABYLON.StandardMaterial("greenMat", scene)
greenMat.emissiveColor = new BABYLON.Color3(0, 1, 0)
var blueMat = new BABYLON.StandardMaterial("blueMat", scene)
blueMat.emissiveColor = new BABYLON.Color3(0, 0, 1)
var whiteMat = new BABYLON.StandardMaterial("whiteMat", scene)
whiteMat.emissiveColor = new BABYLON.Color3(1, 1, 1)
var hemisphericLight = new BABYLON.HemisphericLight('hemisphericLight',new BABYLON.Vector3(0,1,0),scene)
hemisphericLight.intensity=0
var direction = 1
const anime=()=>{
hemisphericLight.intensity+=direction*0.005
if(hemisphericLight.intensity>=0.5)
direction =-1
else if(hemisphericLight.intensity<=0)
direction = 1
requestAnimationFrame(anime)
}
anime()
//red light
var lightRed = new BABYLON.SpotLight("spotLight", new BABYLON.Vector3(-0.9, 1 , -1.8), new BABYLON.Vector3(0, -1, 0), Math.PI / 2, 1.5, scene)
lightRed.diffuse = new BABYLON.Color3(1, 0, 0)
lightRed.specular = new BABYLON.Color3(0, 0, 0)
//green light
var lightGreen = new BABYLON.SpotLight("spotLight1", new BABYLON.Vector3(0, 1, -0.5), new BABYLON.Vector3(0, -1, 0), Math.PI / 2, 1.5, scene)
lightGreen.diffuse = new BABYLON.Color3(0, 1, 0)
lightGreen.specular = new BABYLON.Color3(0, 0, 0)
//blue light
var lightBlue = new BABYLON.SpotLight("spotLight2", new BABYLON.Vector3(0.9, 1, -1.8), new BABYLON.Vector3(0, -1, 0), Math.PI / 2, 1.5, scene)
lightBlue.diffuse = new BABYLON.Color3(0, 0, 1)
lightBlue.specular = new BABYLON.Color3(0, 0, 0)
//white light
var lightWhite = new BABYLON.SpotLight("spotLight3", new BABYLON.Vector3(0, 1, 1), new BABYLON.Vector3(0, -1, 0), Math.PI / 2, 1.5, scene)
lightWhite.diffuse = new BABYLON.Color3(1, 1, 1)
lightWhite.specular = new BABYLON.Color3(0, 0, 0)
var redSphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 0.25}, scene)
redSphere.material = redMat
redSphere.position = lightRed.position
var greenSphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 0.25}, scene)
greenSphere.material = greenMat
greenSphere.position = lightGreen.position
var blueSphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 0.25}, scene)
blueSphere.material = blueMat
blueSphere.position = lightBlue.position
var whiteSphere = BABYLON.MeshBuilder.CreateSphere("sphere", {diameter: 0.25}, scene)
whiteSphere.material = whiteMat
whiteSphere.position = lightWhite.position
var groundMat = new BABYLON.StandardMaterial("groundMat", scene)
groundMat.diffuseColor = mats[0]
var ground = BABYLON.MeshBuilder.CreateGround("ground", {width: 4, height: 6}, scene)
ground.material = groundMat
/*******************GUI***********************/
var makeYellow = function() {
groundMat.diffuseColor = mats[0]
}
var makePurple = function() {
groundMat.diffuseColor = mats[1]
}
var makeCyan = function() {
groundMat.diffuseColor = mats[2]
}
var makeWhite = function() {
groundMat.diffuseColor = mats[3]
}
var matGroup = new BABYLON.GUI.RadioGroup("Material Color", "radio")
matGroup.addRadio("Yellow", makeYellow, true)
matGroup.addRadio("Purple", makePurple)
matGroup.addRadio("Cyan", makeCyan)
matGroup.addRadio("White", makeWhite)
var advancedTexture = BABYLON.GUI.AdvancedDynamicTexture.CreateFullscreenUI("UI")
var selectBox = new BABYLON.GUI.SelectionPanel("sp", [matGroup])
selectBox.width = 0.4
selectBox.height = "50%"
selectBox.top = "4px"
selectBox.left = "4px"
selectBox.background = "white"
selectBox.horizontalAlignment = BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT
selectBox.verticalAlignment = BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP
advancedTexture.addControl(selectBox)
return scene
}
const canvasColor = document.getElementById('renderCanvas-6-color')
const engineColor = new BABYLON.Engine(canvasColor,true)
const sceneColor=createSceneColor(engineColor,canvasColor)
engineColor.runRenderLoop(function(){sceneColor.render()})

效果如下:

环境光是在材质周围均匀照射的光,当材质设置环境光颜色,并且场景也设置环境颜色时,材质会产生均匀的颜色分布。我们可以这样理解,场景中的环境颜色scene.ambientColor是材质环境光颜色对应RGB通道的上限。

以下效果来自Babylon101 Material的环境光部分,可以修改代码来观察环境光的影响。

我们还可以通过设置材质的alpha通道来改变材质透明度,例如:

1
myMaterial.alpha = 0.5

官网示例

纹理 (Texture)

使用以下代码创建一个标准材质StandardMaterial

1
const myMaterial = new BABYLON.StandardMaterial("myMaterial", scene) // 与上面颜色部分相同

我们可以设置myMaterial的属性,对于纹理有以下属性

  • diffuseTexture
  • specularTexture
  • emissiveTexture
  • ambientTexture

使用方法如下:

1
2
3
4
5
6
7
8
var myMaterial = new BABYLON.StandardMaterial("myMaterial", scene)

myMaterial.diffuseTexture = new BABYLON.Texture("PATH TO IMAGE", scene)
myMaterial.specularTexture = new BABYLON.Texture("PATH TO IMAGE", scene)
myMaterial.emissiveTexture = new BABYLON.Texture("PATH TO IMAGE", scene)
myMaterial.ambientTexture = new BABYLON.Texture("PATH TO IMAGE", scene)

mesh.material = myMaterial

其中"PATH TO IMAGE"是纹理的路径。

你可能注意到了,这个博客的横幅banner部分就是在一个平面plane上贴了一张约会大作战的图片纹理,你可以拖拽查看一下。

我们将这张图片贴到一个盒子box上,使用如下代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var createSceneTexture = function (engine,canvas) {
var scene = new BABYLON.Scene(engine)
var camera = new BABYLON.ArcRotateCamera("Camera", -Math.PI / 2, Math.PI / 4, 4, BABYLON.Vector3.Zero(), scene)
camera.attachControl(canvas, false)

// 打一个默认平行光(白色)
var light = new BABYLON.HemisphericLight("hemiLight", new BABYLON.Vector3(-1, 1, 0), scene)
// 创建材质
var material = new BABYLON.StandardMaterial("material",scene)
//创建并选择纹理
material.diffuseTexture = new BABYLON.Texture("https://zzqizqute.github.io/css/images/banner.jpg",scene)
//创建盒子
var box = BABYLON.MeshBuilder.CreateBox("sphere",{size:2},scene)
//将材质应用于盒子
box.material=material

return scene
}
const canvasTexture = document.getElementById('renderCanvas-6-texture')
const engineTexture = new BABYLON.Engine(canvasTexture,true)
const sceneTexture = createSceneTexture(engineTexture,canvasTexture)
engineTexture.runRenderLoop(function(){sceneTexture.render()})

效果如下:

同样的,我们可以设置材质的alpha通道使其透明,但是对于纹理,需要设置纹理TexturehasAlpha属性为true

1
2
myMaterial.alpha = 0.5
myMaterial.diffuseTexture.hasAlpha = true
如果材质是png格式的图片,并且包含alpha通道的时候,将会根据alpha通道的值进行透明处理。

背景剔除 (Back Face Culling)

默认情况下3d渲染时为提高性能,不渲染背面。对于透明材质,默认情况下其backFaceCulling属性为true,如果在前面观察,将看不到透过透明区域的背面形状。此时将backFaceCulling设置为false,就可以看到了。下面是Babylon提供的代码,我们对其进行修改,使其一直旋转,并定时切换backFaceCulling属性,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
    var createSceneBackFaceCulling = function (engine,canvas) {
var scene = new BABYLON.Scene(engine)
var camera = new BABYLON.ArcRotateCamera("Camera", 13 * Math.PI / 8, Math.PI / 4, 3, BABYLON.Vector3.Zero(), scene)
camera.attachControl(canvas, false)

var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene)
light.intensity = 0.7

var pl = new BABYLON.PointLight("pl", BABYLON.Vector3.Zero(), scene)
pl.diffuse = new BABYLON.Color3(1, 1, 1)
pl.specular = new BABYLON.Color3(1, 1, 1)
pl.intensity = 0.8

var mat = new BABYLON.StandardMaterial("dog", scene)
mat.diffuseTexture = new BABYLON.Texture("https://upload.wikimedia.org/wikipedia/commons/8/87/Alaskan_Malamute%2BBlank.png", scene)
mat.diffuseTexture.hasAlpha = true
mat.backFaceCulling = true
var box = BABYLON.MeshBuilder.CreateBox("box", {}, scene)
box.material = mat
var backfacecullingHint=document.querySelector('#backfaceculling-hint')
backfacecullingHint.innerText="背景剔除"
const anime = ()=> {
box.rotation.y+=0.04
if(box.rotation.y>=Math.PI*2){
box.rotation.y=0
mat.backFaceCulling=!mat.backFaceCulling
if(mat.backFaceCulling){
backfacecullingHint.style.textDecoration=""
}else{
backfacecullingHint.style.textDecoration="line-through"
}
}
requestAnimationFrame(anime)

}
anime()
return scene
}
const canvasBackFaceCulling = document.getElementById('renderCanvas-6-backfaceculling')
const engineBackFaceCulling = new BABYLON.Engine(canvasBackFaceCulling,true)
const sceneBackFaceCulling = createSceneBackFaceCulling(engineBackFaceCulling,canvasBackFaceCulling)
engineBackFaceCulling.runRenderLoop(function(){sceneBackFaceCulling.render()})

效果如下:

网格图

可以显示mesh的网格骨架。

1
myMaterial.wireframe = true

示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var createSceneWireFrame = function (engine,canvas) {
var scene = new BABYLON.Scene(engine)
var camera = new BABYLON.ArcRotateCamera("Camera", 13 * Math.PI / 8, Math.PI / 4, 2, BABYLON.Vector3.Zero(), scene)
camera.attachControl(canvas, false)

var light = new BABYLON.HemisphericLight("light1", new BABYLON.Vector3(0, 1, 0), scene)
light.intensity = 0.7
var sphere = BABYLON.MeshBuilder.CreateSphere("sphere",{},scene)
var material = new BABYLON.StandardMaterial("material",scene)
material.diffuseColor=new BABYLON.Color3(1,1,1)
material.wireframe=true
sphere.material=material
return scene
}
const canvasWireFrame = document.getElementById('renderCanvas-6-wireframe')
const engineWireFrame = new BABYLON.Engine(canvasWireFrame,true)
const sceneWireFrame = createSceneWireFrame(engineWireFrame,canvasWireFrame)
engineWireFrame.runRenderLoop(function(){sceneWireFrame.render()})

效果如下: