
文章目录
1. 引言:材质与纹理的重要性
1.1 材质与纹理的核心作用
2. 基础材质:StandardMaterial
2.1 材质属性详解
2.2 实战:创建金属材质
3. 纹理贴图:从基础到高级
3.1 基础纹理映射
3.2 多纹理混合技术
4. 高级材质:PBRMaterial
4.1 PBR 材质基础
4.2 PBR 材质实战:生锈金属
5. 动态材质与自定义着色器
5.1 动态修改材质属性
5.2 自定义 GLSL 着色器
6. 实战任务
任务 1:创建雪地下雪效果
任务 2:创建下雨天气
任务 3:创建全球多云雷电效果
7. 性能优化与常见问题
7.1 纹理优化技巧
7.2 常见问题解答
8. 总结与下一章预告
8.1 关键知识点回顾
8.2 下一章预告
1. 引言:材质与纹理的重要性
上一章详解灯光与阴影,点光源、方向光、聚光灯的设置与阴影优化技巧。
这一章详细介绍一下Babylon中,材质与纹理的相关知识,包含基础材质、PBR材质、着色器等。
材质与纹理的好坏,决定着场景的显示效果,更决定着使用者的体验。
1.1 材质与纹理的核心作用
核心作用:材质定义物体的视觉属性(颜色、反光、粗糙度),纹理提供表面细节(图案、凹凸、磨损)。
案例对比:
无材质:物体呈现单一颜色,缺乏真实感。
基础材质:通过颜色和反光增强立体感。
纹理+材质:表面细节丰富(如木纹、锈迹、布料纤维)。
2. 基础材质:StandardMaterial
2.1 材质属性详解
如图,是一个没有材质的地面,下面为这个地形添加材质

关键属性:
// 地面
var ground = BABYLON.Mesh.CreateGroundFromHeightMap(
"ground",
"/textures/heightMap.png",
250,
250,
200,
0,
50, scene, false
);
// 地面材质
var groundMaterial = new BABYLON.StandardMaterial("ground", scene);
// 地面材质贴图
groundMaterial.diffuseTexture = new BABYLON.Texture("/textures/ground.jpg", scene);
// 横向纵向重复次数
groundMaterial.diffuseTexture.uScale = 6;
groundMaterial.diffuseTexture.vScale = 6;
// 漫反射颜色(基础色)
groundMaterial.diffuseColor = new BABYLON.Color3(1, 1, 1);
// 高光颜色
groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
// 自发光颜色(如霓虹灯)
groundMaterial.emissiveColor = new BABYLON.Color3(0, 0, 0);
// 环境光影响颜色
groundMaterial.ambientColor = new BABYLON.Color3(1, 1, 1);
// 透明度(0 ~ 1)
groundMaterial.alpha = 1;
ground.position.y = -2.05;
ground.material = groundMaterial;
光泽与粗糙度:
groundMaterial.specularPower = 64; // 高光聚焦程度(值越大,光斑越小)
groundMaterial.roughness = 0.3; // 表面粗糙度(0光滑,1粗糙)
![图片[1] - Babylon.js学习之路《六、材质与纹理:为模型赋予真实的表面效果》 - 宋马](https://pic.songma.com/blogimg/20250529/d40cc9ace5914e16a049d7a2d0f3dfb4.png)
经过几行代码的处理,一个沙丘形状的地形就展现出来。
2.2 实战:创建金属材质

目标:模拟抛光金属表面(高反光、锐利高光)。
代码示例:
const metalMat = new BABYLON.StandardMaterial("metalMat", scene);
metalMat.diffuseColor = new BABYLON.Color3(0.8, 0.8, 0.8); // 银灰色
metalMat.specularColor = new BABYLON.Color3(1, 1, 1); // 白色高光
metalMat.specularPower = 256; // 高光聚焦
metalMat.emissiveColor = new BABYLON.Color3(0.1, 0.1, 0.1); // 轻微自发光
sphere.material = metalMat;
3. 纹理贴图:从基础到高级
3.1 基础纹理映射

加载纹理贴图:
const diffuseTexture = new BABYLON.Texture(
"textures/wood.jpg", // 纹理路径
scene
);
material.diffuseTexture = diffuseTexture; // 应用漫反射贴图
纹理参数调整:
diffuseTexture.uScale = 2; // 横向重复次数
diffuseTexture.vScale = 2; // 纵向重复次数
diffuseTexture.wrapU = BABYLON.Texture.WRAP_REPEAT; // 横向重复模式
diffuseTexture.wrapV = BABYLON.Texture.WRAP_MIRROR; // 纵向镜像重复
3.2 多纹理混合技术
法线贴图(Normal Map):模拟表面凹凸细节。
const normalTexture = new BABYLON.Texture("textures/brick_normal.jpg", scene);
material.bumpTexture = normalTexture;
material.bumpTexture.level = 0.5; // 凹凸强度
高光贴图(Specular Map):控制表面反光区域。
const specularTexture = new BABYLON.Texture("textures/brick_specular.jpg", scene);
material.specularTexture = specularTexture;
环境光遮蔽贴图(AO Map):增强阴影细节。
const aoTexture = new BABYLON.Texture("textures/brick_ao.jpg", scene);
material.ambientTexture = aoTexture;
4. 高级材质:PBRMaterial
4.1 PBR 材质基础

PBR 核心原理:基于物理的渲染(金属度、粗糙度、环境反射)。
代码示例:
const pbrMat = new BABYLON.PBRMaterial("pbrMat", scene);
pbrMat.albedoTexture = new BABYLON.Texture("textures/metal_albedo.png", scene);
pbrMat.metallic = 0.9; // 金属度(0非金属,1金属)
pbrMat.roughness = 0.2; // 粗糙度(0光滑,1粗糙)
pbrMat.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(
"textures/env.dds", scene
); // 环境反射贴图
4.2 PBR 材质实战:生锈金属

纹理组合:
pbrMat.albedoTexture = new BABYLON.Texture("textures/rusted_iron_albedo.png", scene);
pbrMat.metallicTexture = new BABYLON.Texture("textures/rusted_iron_metallic.png", scene);
pbrMat.roughnessTexture = new BABYLON.Texture("textures/rusted_iron_roughness.png", scene);
pbrMat.normalTexture = new BABYLON.Texture("textures/rusted_iron_normal.png", scene);
pbrMat.ambientTexture = new BABYLON.Texture("textures/rusted_iron_ao.png", scene);
5. 动态材质与自定义着色器
5.1 动态修改材质属性

颜色渐变效果:
let time = 0;
scene.registerBeforeRender(() => {
material.diffuseColor = new BABYLON.Color3(
Math.sin(time) * 0.5 + 0.5, // R通道
Math.cos(time) * 0.5 + 0.5, // G通道
0.5 // B通道
);
time += 0.02;
});
5.2 自定义 GLSL 着色器

着色器材质(ShaderMaterial):
// 创建基于着色器中的材质效果【名称、场景、着色器代码的路径、创建着色器的选项】
const customShader = new BABYLON.ShaderMaterial(
"waveEffect",
scene,
{
vertex: "waveEffect",
fragment: "waveEffect",
},
{
attributes: ["position", "uv"],
uniforms: [
"worldViewProjection",
"time",
"effectIntensity",
"effectMultiplier"
],
samplers: ["map", "noiseMap"],
defines: ["#define PI 3.1415", "#define TAU 6.2832"]
}
);
// 实例化新纹理【加载为纹理的图片】
const noiseTexture = new BABYLON.Texture("textures/fur.jpg", scene);
// 在着色器中设置纹理
customShader.setTexture("noiseMap", noiseTexture);
// 动画循环
scene.registerBeforeRender(() => {
// 在着色器中设置浮动
customShader.setFloat("time", performance.now() / 1000);
customShader.setFloat("effectIntensity", 1.0);
customShader.setFloat("effectMultiplier", 1.0);
});
groundMesh.material = customShader;
6. 实战任务
任务 1:创建雪地下雪效果

目标:雪地山谷间的雪人。
实现步骤:
引入山地模型
引入雪人模型
创建粒子系统
代码示例:
// 引入山地
BABYLON.SceneLoader.ImportMesh("", "https://raw.githubusercontent.com/Ethan-J13/Promp_Models/main/Weather/", "snow_mountain.glb", scene, function(newMeshes, particleSystems, skeletons) {
var Mountains = newMeshes[0]
Mountains.scaling.x = 4
Mountains.scaling.y = 2
Mountains.scaling.z = 2
Mountains.addRotation(-0.2, 0.15, -0.1);
Mountains.position = new BABYLON.Vector3(-120, -22, 100);
})
// 引入雪人模型
let [snowMan] = await Promise.all([
BABYLON.SceneLoader.ImportMeshAsync("", "https://assets.babylonjs.com/meshes/Demos/Snow_Man_Scene/", "snowMan.glb", scene)
])
snowMan = snowMan.meshes[0];
snowMan.name = "snowMan";
snowMan.scaling = new BABYLON.Vector3(12, 12, 12); // set the scaling to 20 times bigger
snowMan.position.y += 28;
snowMan.position.z += -85;
// 创建粒子系统【代码段,场景,GPU开关,URL,定义系统容量】
BABYLON.ParticleHelper.CreateFromSnippetAsync("VK7R6H#269", scene, false);
任务 2:创建下雨天气

目标:通过粒子系统创建下雨天气。
引入天空盒
创建地面
创建粒子系统
代码示例:
// 创建不同粒子系统
BABYLON.ParticleHelper.CreateAsync("rain", scene, false).then((set) => {
set.start();
});
// 天空盒
var skybox = BABYLON.Mesh.CreateBox("skyBox", 100.0, scene);
var skyboxMaterial = new BABYLON.StandardMaterial("skyBox", scene);
skyboxMaterial.backFaceCulling = false;
skyboxMaterial.reflectionTexture = new BABYLON.CubeTexture("textures/skybox", scene);
skyboxMaterial.reflectionTexture.coordinatesMode = BABYLON.Texture.SKYBOX_MODE;
skyboxMaterial.diffuseColor = new BABYLON.Color3(0, 0, 0);
skyboxMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
skyboxMaterial.disableLighting = true;
skybox.material = skyboxMaterial;
// 创建地面
var ground = BABYLON.Mesh.CreateGroundFromHeightMap("ground", "textures/worldHeightMap.jpg", 200, 200, 250, 0, 10, scene, false);
var groundMaterial = new BABYLON.StandardMaterial("ground", scene);
groundMaterial.diffuseTexture = new BABYLON.Texture("textures/ground.jpg", scene);
groundMaterial.diffuseTexture.uScale = 6;
groundMaterial.diffuseTexture.vScale = 6;
groundMaterial.specularColor = new BABYLON.Color3(0, 0, 0);
ground.material = groundMaterial;
任务 3:创建全球多云雷电效果

目标:通过着色器自定义的材质,应用到地球表面,模拟地球自转和雷电。
代码示例:
const createScene = async function () {
const scene = new BABYLON.Scene(engine);
const light = new BABYLON.PointLight("Omni", new BABYLON.Vector3(20, 20, 100), scene);
const camera = new BABYLON.ArcRotateCamera("Camera", 0, 0.8, 200, BABYLON.Vector3.Zero(), scene);
camera.attachControl(canvas, false);
let time = 0;
const rate = 0.01;
scene.registerBeforeRender(function () {
camera.alpha = time;
light.position = camera.position;
time += scene.getAnimationRatio() * rate;
});
// PostProcess可用于在渲染纹理后将着色器应用于纹理【片段着色器的url】
const postEffect = new BABYLON.PostProcess("customEffect", "customEffect", ["iTime", "iResolution"], [], 1, camera);
// 添加到onApplyObservable的函数
postEffect.onApply = function (effect) {
effect.setVector2('iResolution', new BABYLON.Vector2(postEffect.width, postEffect.height));
effect.setFloat('iTime', time);
};
return scene;
}
BABYLON.Effect.ShadersStore['customEffectFragmentShader'] = `
uniform float iTime;
uniform vec2 iResolution;
uniform sampler2D textureSampler;
varying vec3 vPosition;
varying vec2 vUV;
const mat2 m = mat2(1.616, 1.212, -1.212, 1.616);
float hash12(vec2 p) {
p = fract(p * vec2(5.3983, 5.4427));
p += dot(p.yx, p.xy + vec2(21.5351, 14.3137));
return fract(p.x * p.y * 95.4337);
}
vec2 hash21(float p) {
vec2 p2 = fract(p * vec2(5.3983, 5.4427));
p2 += dot(p2.yx, p2.xy + vec2(21.5351, 14.3137));
return fract(vec2(p2.x * p2.y * 95.4337, p2.x * p2.y * 97.597));
}
float noise(in vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(mix(hash12(i + vec2(0.0, 0.0)),
hash12(i + vec2(1.0, 0.0)), u.x),
mix(hash12(i + vec2(0.0, 1.0)),
hash12(i + vec2(1.0, 1.0)), u.x), u.y);
}
float hash12_3(vec2 p) {
float f = hash12(p);
return f * f * f;
}
float noise_3(in vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(mix(hash12_3(i + vec2(0.0, 0.0)),
hash12_3(i + vec2(1.0, 0.0)), u.x),
mix(hash12_3(i + vec2(0.0, 1.0)),
hash12_3(i + vec2(1.0, 1.0)), u.x), u.y);
}
float fbm(vec2 p) {
float f = 0.0;
f += 0.5 * noise(p); p = m * p;
f += 0.25 * noise(p); p = m * p;
f += 0.125 * noise(p); p = m * p;
f += 0.0625 * noise(p); p = m * p;
f += 0.03125 * noise(p); p = m * p;
f += 0.015625 * noise(p);
return f / 0.984375;
}
vec3 getDir(vec2 screenPos) {
screenPos -= 0.5;
screenPos.x *= iResolution.x / iResolution.y;
return normalize(vec3(0.0, -1.0, -3.0)
+ screenPos.x * vec3(1.0, 0.0, 0.0)
- screenPos.y * vec3(0.0, -0.948683298, 0.316227766));
}
bool getPosition(in vec3 camera, in vec3 dir, out vec2 pos) {
bool valid = false;
float b = dot(camera, dir);
float c = dot(camera, camera) - 1.0;
float h = b * b - c;
if (h > 0.0) {
valid = true;
vec3 p = camera + (-b - sqrt(h)) * dir;
pos = p.xz + iTime * vec2(0.005, 0.02);
}
return valid;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 screen = fragCoord.xy / iResolution.xy;
vec3 camera = vec3(0.0, 1.2, 0.7);
vec3 dir = getDir(screen);
vec3 earth = vec3(0.0, 0.0, 0.0);
vec2 position;
if (getPosition(camera, dir, position)) {
float geography = fbm(6.0 * position);
float coast = 0.2 * pow(geography + 0.5, 50.0);
float population = smoothstep(0.2, 0.6, fbm(2.0 * position) + coast);
vec2 p = 40.0 * position;
population *= (noise_3(p) + coast); p = m * p;
population *= (noise_3(p) + coast); p = m * p;
population *= (noise_3(p) + coast); p = m * p;
population *= (noise_3(p) + coast); p = m * p;
population *= (noise_3(p) + coast);
population = smoothstep(0.0, 0.02, population);
vec3 land = vec3(0.1 + 2.0 * population, 0.07 + 1.3 * population, population);
vec3 water = vec3(0.0, 0.05, 0.1);
vec3 ground = mix(land, water, smoothstep(0.49, 0.5, geography));
vec2 wind = vec2(fbm(30.0 * position), fbm(60.0 * position));
float weather = fbm(20.0 * (position + 0.03 * wind)) * (0.6 + 0.4 * noise(10.0 * position));
float clouds = 0.8 * smoothstep(0.35, 0.45, weather) * smoothstep(-0.25, 1.0, fbm(wind));
earth = mix(ground, vec3(0.5, 0.5, 0.5), clouds);
float lightning = 0.0;
vec2 strike;
// 模拟闪电
if (getPosition(camera, getDir(hash21(iTime)), strike)) {
vec2 diff = position - strike;
lightning += clamp(1.0 - 1500.0 * dot(diff, diff), 0.0, 1.0);
}
lightning *= smoothstep(0.65, 0.75, weather);
earth += lightning * vec3(1.0, 1.0, 1.0);
}
vec3 altitude = camera - dir * dot(camera, dir);
float horizon = sqrt(dot(altitude, altitude));
vec3 atmosphere = vec3(0.2, 0.25, 0.3);
atmosphere = mix(atmosphere, vec3(0.05, 0.1, 0.3), smoothstep(0.992, 1.004, horizon));
atmosphere = mix(atmosphere, vec3(0.1, 0.0, 0.0), smoothstep(1.0, 1.004, horizon));
atmosphere = mix(atmosphere, vec3(0.2, 0.17, 0.1), smoothstep(1.008, 1.015, horizon));
atmosphere = mix(atmosphere, vec3(0.0, 0.0, 0.0), smoothstep(1.015, 1.02, horizon));
horizon = clamp(pow(horizon, 20.0), 0.0, 1.0);
fragColor = vec4(mix(earth, atmosphere, horizon), 1.0);
}
void main() {
mainImage(gl_FragColor, vUV * iResolution.xy);
}
`;
7. 性能优化与常见问题
7.1 纹理优化技巧
纹理压缩:使用 .ktx 或 .basis 格式减少加载时间。
Mipmap 配置:
const texture = new BABYLON.Texture("texture.jpg", scene, {
generateMipMaps: true, // 启用 Mipmap
samplingMode: BABYLON.Texture.TRILINEAR_SAMPLINGMODE // 三线性过滤
});
7.2 常见问题解答
问题 1:纹理拉伸或错位
解决方案:调整模型的 UV 映射,或在代码中设置 uScale/vScale。
问题 2:透明材质显示异常
解决方案:启用透明度混合并设置渲染顺序:
material.alpha = 0.5;
material.backFaceCulling = false; // 禁用背面剔除
material.needDepthPrePass = true; // 深度预渲染
问题 3:PBR 材质过暗
解决方案:添加环境光或 HDR 天空盒:
scene.environmentTexture = BABYLON.CubeTexture.CreateFromPrefilteredData(
"textures/environment.dds", scene
);
8. 总结与下一章预告
8.1 关键知识点回顾
标准材质与 PBR 材质配置、多纹理混合、动态材质控制。
扩展方向:
程序化纹理生成:通过代码动态生成纹理图案。
后处理链:结合泛光(Bloom)、景深(Depth of Field)提升画面效果。
8.2 下一章预告
《用户交互:鼠标点击、拖拽与射线检测》:实现物体点击事件、拖拽操作与射线碰撞检测。



















暂无评论内容