第四篇:材质与纹理:让物体“真实“起来
第四篇:材质与纹理:让物体"真实"起来
引言
材质和纹理是Three.js的灵魂,它们将冰冷的几何体转化为具有视觉质感的物体。从简单的单色表面到复杂的PBR(物理渲染)材质,Three.js提供了完整的材质系统。本文将深入解析6大核心材质类型,并通过Vue3实现实时材质编辑器,带你掌握3D视觉定制的核心技术。
1. 材质基础:定义物体表面
1.1 材质核心属性
1.2 材质类型对比
材质类型 | 光照需求 | 性能 | 真实感 | 适用场景 |
---|---|---|---|---|
MeshBasicMaterial | 无 | ★★★★★ | ★☆☆ | 线框/简单色块 |
MeshLambertMaterial | 需要 | ★★★★☆ | ★★☆ | 漫反射表面 |
MeshPhongMaterial | 需要 | ★★★☆☆ | ★★★ | 塑料/金属 |
MeshStandardMaterial | 需要 | ★★★☆☆ | ★★★★ | PBR基础 |
MeshPhysicalMaterial | 需要 | ★★☆☆☆ | ★★★★★ | 高级PBR |
MeshMatcapMaterial | 无 | ★★★★★ | ★★★☆ | 静态展示 |
2. 六大核心材质详解
2.1 基础材质(MeshBasicMaterial)
<script setup>
import { ref } from 'vue';const material = new THREE.MeshBasicMaterial({color: 0x00ff00, // 十六进制颜色wireframe: true, // 线框模式transparent: true, // 开启透明度opacity: 0.7, // 透明度值side: THREE.DoubleSide // 双面渲染
});
</script>
适用场景:调试几何体、HUD元素、X射线效果
2.2 标准材质(MeshStandardMaterial)
const material = new THREE.MeshStandardMaterial({color: 0xffffff,roughness: 0.5, // 粗糙度 (0:镜面, 1:磨砂)metalness: 0.8, // 金属度 (0:非金属, 1:金属)envMap: cubeTexture, // 环境贴图normalMap: normalTexture, // 法线贴图aoMap: aoTexture, // 环境光遮蔽贴图aoMapIntensity: 1.0 // AO强度
});
2.3 物理材质(MeshPhysicalMaterial)
const material = new THREE.MeshPhysicalMaterial({clearcoat: 1.0, // 清漆层强度clearcoatRoughness: 0.1, // 清漆粗糙度sheen: 0.5, // 织物绒感transmission: 0.9, // 透光率(玻璃效果)ior: 1.5, // 折射率(钻石:2.4,水:1.33)
});
真实感要素:
3. 纹理系统:表面细节之源
3.1 纹理加载与管理
<script setup>
import { ref, onMounted } from 'vue';
import * as THREE from 'three';const textureLoader = new THREE.TextureLoader();
const textures = ref({});// 加载纹理
const loadTexture = (path, name) => {textureLoader.load(path, (texture) => {texture.encoding = THREE.sRGBEncoding; // sRGB色彩空间texture.anisotropy = 16; // 各向异性过滤textures.value[name] = texture;});
};onMounted(() => {loadTexture('textures/diffuse.jpg', 'diffuse');loadTexture('textures/normal.png', 'normal');loadTexture('textures/roughness.png', 'roughness');
});
</script>
3.2 纹理映射类型
贴图类型 | 作用 | 示例 |
---|---|---|
map | 基础颜色 | 木纹/砖墙 |
normalMap | 表面凹凸 | 砖缝/划痕 |
roughnessMap | 粗糙度变化 | 磨损区域 |
metalnessMap | 金属度变化 | 金属氧化 |
aoMap | 环境光遮蔽 | 角落阴影 |
displacementMap | 顶点位移 | 真实凹凸 |
4. UV映射:纹理坐标系统
4.1 UV坐标原理
- UV坐标:2D坐标系(U水平,V垂直),范围[0,1]
- 默认展开:Three.js为内置几何体自动生成UV
4.2 自定义UV映射
const geometry = new THREE.BoxGeometry(2, 2, 2);
const uvs = geometry.attributes.uv.array;// 手动设置六个面的UV
const faceUvs = [// 前面0,0, 1,0, 1,1, 0,1,// 右面0,0, 1,0, 1,1, 0,1,// 后面0,0, 1,0, 1,1, 0,1,// 左面0,0, 1,0, 1,1, 0,1,// 顶面0,0, 1,0, 1,1, 0,1,// 底面0,0, 1,0, 1,1, 0,1
];// 复制到UV数组
for (let i = 0; i < faceUvs.length; i++) {uvs[i] = faceUvs[i];
}geometry.attributes.uv.needsUpdate = true;
4.3 UV变换技巧
// 创建纹理变换对象
const texture = textureLoader.load('brick.jpg');
texture.wrapS = THREE.RepeatWrapping; // 水平重复
texture.wrapT = THREE.RepeatWrapping; // 垂直重复
texture.repeat.set(4, 2); // 重复次数
texture.offset.set(0.5, 0); // 偏移
texture.rotation = Math.PI/4; // 旋转45度
texture.center.set(0.5, 0.5); // 旋转中心
5. Vue3实战:材质实时编辑器
5.1 项目结构
src/├── components/│ ├── MaterialEditor.vue // 材质控制面板│ ├── TexturePicker.vue // 纹理选择组件│ ├── MaterialPreview.vue // 3D预览│ └── ParamRange.vue // 参数滑动条└── App.vue
5.2 核心代码实现
<!-- MaterialEditor.vue -->
<script setup>
import { ref, reactive, watch } from 'vue';// 材质类型选项
const MATERIAL_TYPES = {BASIC: 'MeshBasicMaterial',STANDARD: 'MeshStandardMaterial',PHYSICAL: 'MeshPhysicalMaterial'
};// 材质参数响应式对象
const params = reactive({materialType: MATERIAL_TYPES.STANDARD,color: '#ffffff',opacity: 1,wireframe: false,roughness: 0.5,metalness: 0.5,envMapIntensity: 1,normalScale: 1,textures: {map: null,normalMap: null,roughnessMap: null,metalnessMap: null}
});// 创建材质实例
const createMaterial = () => {const options = {color: new THREE.Color(params.color),opacity: params.opacity,transparent: params.opacity < 1,wireframe: params.wireframe,roughness: params.roughness,metalness: params.metalness,envMapIntensity: params.envMapIntensity};// 添加纹理Object.entries(params.textures).forEach(([key, texture]) => {if (texture) options[key] = texture;});// 根据类型创建材质switch (params.materialType) {case MATERIAL_TYPES.BASIC:return new THREE.MeshBasicMaterial(options);case MATERIAL_TYPES.STANDARD:return new THREE.MeshStandardMaterial(options);case MATERIAL_TYPES.PHYSICAL:return new THREE.MeshPhysicalMaterial({...options,clearcoat: refClearcoat.value,clearcoatRoughness: refClearcoatRoughness.value});}
};// 监听变化更新材质
watch(params, () => {emit('update', createMaterial());
}, { deep: true });
</script><template><div class="editor"><select v-model="params.materialType"><option v-for="(type, key) in MATERIAL_TYPES" :value="type">{{ key }}</option></select><div class="control-group"><label>基础颜色</label><input type="color" v-model="params.color"><ParamRange label="不透明度" v-model="params.opacity" :min="0" :max="1" :step="0.01"/><ParamRange label="粗糙度" v-model="params.roughness" :min="0" :max="1" :step="0.01"/><ParamRange label="金属度" v-model="params.metalness" :min="0" :max="1" :step="0.01"/><TexturePicker v-for="(_, key) in params.textures" :key="key" :type="key" v-model="params.textures[key]"/></div></div>
</template>
5.3 纹理选择组件
<!-- TexturePicker.vue -->
<script setup>
defineProps(['type', 'modelValue']);
const emit = defineEmits(['update:modelValue']);const textureOptions = [{ name: '无', value: null },{ name: '砖墙', value: 'brick' },{ name: '金属', value: 'metal' },{ name: '木纹', value: 'wood' },{ name: '自定义...', value: 'custom' }
];const handleChange = (value) => {if (value === 'custom') {// 触发文件选择const input = document.createElement('input');input.type = 'file';input.accept = 'image/*';input.onchange = (e) => {const file = e.target.files[0];const url = URL.createObjectURL(file);const texture = new THREE.TextureLoader().load(url);emit('update:modelValue', texture);};input.click();} else {// 使用预设纹理const texture = value ? new THREE.TextureLoader().load(`/textures/${value}_${type}.jpg`): null;emit('update:modelValue', texture);}
};
</script><template><div class="texture-picker"><label>{{ type }}贴图</label><select @change="handleChange($event.target.value)"><option v-for="opt in textureOptions" :value="opt.value":selected="modelValue?.name === opt.value">{{ opt.name }}</option></select></div>
</template>
5.4 材质预览组件
<!-- MaterialPreview.vue -->
<script setup>
import { ref, onMounted, watch } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';const props = defineProps(['material']);
const canvasRef = ref(null);// 场景初始化
const initScene = () => {const scene = new THREE.Scene();const camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000);const renderer = new THREE.WebGLRenderer({ canvas: canvasRef.value, antialias: true });// 添加环境光和平行光scene.add(new THREE.AmbientLight(0xffffff, 0.5));const light = new THREE.DirectionalLight(0xffffff, 1);light.position.set(5, 5, 5);scene.add(light);// 创建立方体球体组合const sphere = new THREE.Mesh(new THREE.SphereGeometry(1, 32, 32), props.material);sphere.position.x = -1.5;const cube = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), props.material);cube.position.x = 1.5;const group = new THREE.Group();group.add(sphere, cube);scene.add(group);// 添加控制器const controls = new OrbitControls(camera, renderer.domElement);camera.position.z = 5;return { scene, camera, renderer, controls, group };
};// 响应材质变化
watch(() => props.material, (newMat) => {scene.traverse(obj => {if (obj.isMesh) obj.material = newMat;});
});onMounted(() => {const { scene, camera, renderer, controls, group } = initScene();// 动画循环const animate = () => {requestAnimationFrame(animate);group.rotation.y += 0.01;controls.update();renderer.render(scene, camera);};animate();
});
</script><template><canvas ref="canvasRef" class="preview-canvas"></template>
</template>
6. 高级材质技巧
6.1 环境贴图(EnvMap)
// 创建立方体贴图
const loader = new THREE.CubeTextureLoader();
const envMap = loader.load(['px.jpg', 'nx.jpg', // 正负X轴'py.jpg', 'ny.jpg', // 正负Y轴'pz.jpg', 'nz.jpg' // 正负Z轴
]);scene.background = envMap; // 场景背景
material.envMap = envMap; // 材质反射
6.2 法线贴图增强
// 创建切线空间
geometry.computeTangents();// 应用法线贴图
material.normalMap = normalTexture;
material.normalScale.set(2, 2); // 增强凹凸强度// 添加细节法线贴图
material.normalMap2 = detailNormalTexture;
material.normalScale2 = new THREE.Vector2(0.5, 0.5);
6.3 自定义着色器(ShaderMaterial)
<script setup>
import { ref } from 'vue';
import * as THREE from 'three';// 顶点着色器
const vertexShader = `varying vec2 vUv;void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}
`;// 片元着色器
const fragmentShader = `uniform vec3 color;uniform float time;varying vec2 vUv;void main() {// 创建动态条纹float stripe = sin(vUv.x * 50.0 + time * 2.0) * 0.5 + 0.5;vec3 finalColor = color * stripe;gl_FragColor = vec4(finalColor, 1.0);}
`;const material = new THREE.ShaderMaterial({uniforms: {color: { value: new THREE.Color(0xff0000) },time: { value: 0 }},vertexShader,fragmentShader
});// 动画更新
const clock = new THREE.Clock();
const animate = () => {requestAnimationFrame(animate);material.uniforms.time.value = clock.getElapsedTime();
};
</script>
7. 性能优化指南
7.1 材质复用策略
graph TDA[新对象] --> B{材质相同?}B -->|是| C[复用已有材质]B -->|否| D{参数相似?}D -->|是| E[克隆材质]D -->|否| F[创建新材质]
7.2 纹理优化技巧
-
纹理压缩:使用Basis Universal格式
import { BasisTextureLoader } from 'three/addons/loaders/BasisTextureLoader.js'; const loader = new BasisTextureLoader();
-
纹理共享:相同纹理只加载一次
const textureCache = {}; function getTexture(url) {if (!textureCache[url]) {textureCache[url] = textureLoader.load(url);}return textureCache[url]; }
-
Mipmap生成:
texture.generateMipmaps = true; texture.minFilter = THREE.LinearMipmapLinearFilter;
7.3 按需更新材质
// 避免每帧检查
material.needsUpdate = false;// 需要更新时手动设置
function updateMaterial() {material.needsUpdate = true;
}
8. 常见问题解答
Q1:为什么纹理显示为黑色?
- 检查纹理是否加载完成(使用onLoad回调)
- 确认UV映射正确
- 验证材质是否需要光照(Basic材质不受光)
Q2:如何实现透明效果?
material.transparent = true;
material.opacity = 0.5;
// 设置混合模式
material.blending = THREE.NormalBlending;
// 深度写入关闭(避免透明物体遮挡)
material.depthWrite = false;
Q3:金属材质没有反射怎么办?
- 添加环境贴图
material.envMap = envTexture
- 增加环境光强度
material.envMapIntensity = 2.0
- 添加HDR环境光源
9. 总结
通过本文,你已掌握:
- 六大核心材质的特性与适用场景
- 纹理加载、映射与变换技术
- UV坐标系统原理与自定义方法
- Vue3实现材质实时编辑系统
- 高级技巧:环境贴图、法线增强、自定义着色器
- 材质性能优化策略
核心原理:PBR(物理渲染)材质通过模拟光与表面的物理交互(金属度/粗糙度),结合环境光遮蔽和法线细节,实现了接近真实世界的渲染效果。
下一篇预告
第五篇:灯光系统:光与影的魔法
你将学习:
- 4种核心光源特性(环境光/平行光/点光源/聚光灯)
- 阴影系统深度解析(阴影贴图/PCF优化)
- 光照烘焙与实时光照的混合使用
- HDR环境光照与IBL技术
- Vue3实现动态光源控制系统
准备好点亮你的3D世界了吗?让我们探索光影艺术的科学原理!