一、基础场景搭建与光照系统配置
在Three.js中创建自定义材质前,需要先构建完整的3D场景环境。以下代码展示了如何初始化渲染器、相机和光照系统:
// 初始化WebGL渲染器(开启抗锯齿)const renderer = new THREE.WebGLRenderer({ antialias: true });renderer.setSize(window.innerWidth, window.innerHeight);renderer.setClearColor(0xfefefe); // 设置浅灰色背景document.body.appendChild(renderer.domElement);// 创建透视相机(视野角45度,宽高比1:1,近远裁剪面)const camera = new THREE.PerspectiveCamera(45,window.innerWidth / window.innerHeight,0.1,1000);// 添加轨道控制器(需要引入OrbitControls)const orbit = new OrbitControls(camera, renderer.domElement);camera.position.set(0, -2, 300);camera.lookAt(0, 0, 0);// 创建场景并添加环境光const scene = new THREE.Scene();const ambientLight = new THREE.AmbientLight(0xa3a3a3, 0.8);scene.add(ambientLight);// 添加平行光(模拟太阳光)const directionalLight = new THREE.DirectionalLight(0xffffff);directionalLight.position.set(2, 2, 8);scene.add(directionalLight);
这段代码完成了三个关键设置:
- 创建支持抗锯齿的WebGL渲染器
- 配置带轨道控制的透视相机
- 建立包含环境光和平行光的双光源系统
二、几何体生成与基础材质应用
使用二十面体几何体创建500个随机分布的网格:
// 创建二十面体几何体(半径4,细分级别30)const geometry = new THREE.IcosahedronGeometry(4, 30);// 传统物理材质方案(MeshPhysicalMaterial)const material1 = new THREE.MeshPhysicalMaterial();// 批量生成网格for (let i = 0; i < 500; i++) {const mesh = new THREE.Mesh(geometry, material1);// 随机位置(-500到500范围)mesh.position.x = Math.random() * 1000 - 500;mesh.position.y = Math.random() * 1000 - 500;mesh.position.z = Math.random() * 1000 - 500;// 随机缩放(1-4倍)mesh.scale.setScalar(Math.random() * 3 + 1);scene.add(mesh);}
此方案使用物理材质(MeshPhysicalMaterial)虽然能正确响应光照,但存在两个局限:
- 材质属性静态,无法根据空间位置变化
- 无法实现自定义着色逻辑
三、ShaderMaterial核心实现原理
要实现动态着色效果,需要使用ShaderMaterial。其核心包含三个部分:
1. Uniform变量定义
const uniforms = {u_resolution: {value: new THREE.Vector2(window.innerWidth, window.innerHeight)},u_time: { value: 0 }, // 添加时间变量用于动画u_mouse: { value: new THREE.Vector2() } // 可选鼠标交互变量};
2. 着色器代码结构
在HTML中需要定义两个<script type="x-shader/x-vertex">和<script type="x-shader/x-fragment">标签:
<!-- 顶点着色器 --><script id="vertexshader" type="x-shader/x-vertex">varying vec2 vUv;void main() {vUv = uv; // 传递UV坐标到片段着色器gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}</script><!-- 片段着色器 --><script id="fragmentshader" type="x-shader/x-fragment">uniform vec2 u_resolution;varying vec2 vUv;void main() {vec2 st = gl_FragCoord.xy / u_resolution.xy;// 基于UV坐标的动态颜色计算vec3 color = vec3(st.x, st.y, abs(sin(st.x * 10.0 + u_time)));gl_FragColor = vec4(color, 1.0);}</script>
3. 材质创建与网格应用
// 创建ShaderMaterialconst shaderMaterial = new THREE.ShaderMaterial({uniforms: uniforms,vertexShader: document.getElementById('vertexshader').textContent,fragmentShader: document.getElementById('fragmentshader').textContent,side: THREE.DoubleSide // 双面渲染});// 重新生成网格(使用ShaderMaterial)const meshes = [];for (let i = 0; i < 500; i++) {const mesh = new THREE.Mesh(geometry, shaderMaterial);// ...位置和缩放设置同上...scene.add(mesh);meshes.push(mesh);}
四、动态着色效果实现
要实现基于网格位置的动态着色,需要修改片段着色器:
// 修改后的片段着色器核心逻辑varying vec3 vPosition; // 从顶点着色器传递的世界坐标uniform vec3 u_cameraPos;void main() {// 计算网格到相机的距离float dist = distance(vPosition.xyz, u_cameraPos);// 基于距离和UV坐标的动态颜色vec3 baseColor = vec3(0.5 + 0.5 * sin(vPosition.x * 0.1),0.5 + 0.5 * cos(vPosition.y * 0.1),0.5 + 0.5 * sin(vPosition.z * 0.1));// 添加距离衰减效果float intensity = smoothstep(100.0, 300.0, dist);vec3 finalColor = mix(baseColor, vec3(0.2), intensity);gl_FragColor = vec4(finalColor, 1.0);}
实现要点:
- 通过
varying变量传递顶点位置 - 使用
distance()函数计算空间距离 - 应用
smoothstep()实现平滑过渡 - 使用
mix()函数混合基础色和衰减色
五、性能优化与交互增强
1. 动画循环优化
function animate(time) {// 更新时间变量(毫秒转秒)shaderMaterial.uniforms.u_time.value = time * 0.001;// 可选:鼠标位置跟踪// shaderMaterial.uniforms.u_mouse.value.set(mouseX, mouseY);renderer.render(scene, camera);requestAnimationFrame(animate);}requestAnimationFrame(animate);
2. 响应式设计
window.addEventListener('resize', () => {camera.aspect = window.innerWidth / window.innerHeight;camera.updateProjectionMatrix();renderer.setSize(window.innerWidth, window.innerHeight);// 更新分辨率UniformshaderMaterial.uniforms.u_resolution.value.set(window.innerWidth,window.innerHeight);});
3. 调试技巧
- 使用
THREE.ShaderChunk简化着色器开发 - 通过
console.log(gl.getShaderInfoLog(shader))调试编译错误 - 使用
glsl-canvas等工具预览着色器效果
六、典型应用场景
- 数据可视化:将数值映射到颜色空间
- 动态背景:创建响应式环境效果
- 特殊材质:实现卡通渲染、边缘光等效果
- 交互设计:根据用户输入改变材质表现
通过掌握ShaderMaterial的开发技术,开发者可以突破Three.js内置材质的限制,实现高度定制化的3D渲染效果。这种基于GPU的编程方式不仅能提升视觉表现力,还能通过并行计算优化性能表现。