如何为Three.js的ShaderMaterial添加清晰阴影以凸显模型边缘
自定义渐变着色器实现清晰边缘与阴影的方案
问题描述
我使用自定义着色器为模型添加渐变颜色,但该着色器无法清晰呈现阴影,导致模型边缘模糊,呈现出类似“发光”的效果。希望自定义着色器能像THREE.MeshStandardMaterial那样清晰展示模型边缘,该如何实现?
原自定义渐变着色器代码:
const mat: THREE.Material = new THREE.ShaderMaterial({ uniforms: { color1: { value: new THREE.Color("#01e2f6") }, color2: { value: new THREE.Color("#FF6969") }, color3: { value: new THREE.Color("#FFF9DE") }, bMin: { value: this.mesh.geometry.boundingBox.min }, bMax: { value: this.mesh.geometry.boundingBox.max } }, vertexShader: ` uniform vec3 bMin; uniform vec3 bMax; varying vec2 vUv; void main() { vUv.y = (position.y - bMin.y) / (bMax.y - bMin.y); gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0); } `, fragmentShader: ` uniform vec3 color1; uniform vec3 color2; uniform vec3 color3; varying vec2 vUv; void main() { gl_FragColor = vec4(mix(color1, color2, vUv.y), 1.0); gl_FragColor = mix(gl_FragColor, vec4(mix(color2, color3, vUv.y), 1.0), vUv.y); } ` }); this.mesh.material = mat;
解决方案
核心问题是当前着色器完全没做光照计算,MeshStandardMaterial靠PBR光照逻辑处理法线、光影来体现立体感和边缘。要实现类似效果,需要给着色器加入光照相关的计算:
1. 传递法线信息到片元着色器
修改顶点着色器,计算并传递视图空间下的法线,用于后续光照计算:
uniform vec3 bMin; uniform vec3 bMax; varying vec2 vUv; varying vec3 vNormal; // 新增:传递法线到片元 void main() { vUv.y = (position.y - bMin.y) / (bMax.y - bMin.y); // 计算视图空间的法线(normalMatrix是模型视图矩阵的逆转置,用于变换法线) vNormal = normalize(normalMatrix * normal); gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }
2. 添加基础漫反射光照计算
在片元着色器中加入光照逻辑,模拟光影效果,让边缘清晰:
uniform vec3 color1; uniform vec3 color2; uniform vec3 color3; uniform vec3 lightDirection; // 方向光的视图空间方向 uniform vec3 lightColor; // 光源颜色 varying vec2 vUv; varying vec3 vNormal; void main() { // 渐变计算(保留原逻辑) vec3 gradientColor = mix(color1, color2, vUv.y); gradientColor = mix(gradientColor, mix(color2, color3, vUv.y), vUv.y); // 计算漫反射:法线与光线方向的点积,取非负值 float diffuseStrength = max(dot(vNormal, normalize(lightDirection)), 0.0); vec3 diffuseLight = diffuseStrength * lightColor; // 加入环境光避免暗部全黑,强度可自行调整 vec3 finalColor = gradientColor * (diffuseLight + vec3(0.2)); gl_FragColor = vec4(finalColor, 1.0); }
3. 更新ShaderMaterial的Uniforms
给材质补充光照相关的参数:
const mat: THREE.ShaderMaterial = new THREE.ShaderMaterial({ uniforms: { color1: { value: new THREE.Color("#01e2f6") }, color2: { value: new THREE.Color("#FF6969") }, color3: { value: new THREE.Color("#FFF9DE") }, bMin: { value: this.mesh.geometry.boundingBox.min }, bMax: { value: this.mesh.geometry.boundingBox.max }, // 添加光照参数,可根据场景实际光源调整 lightDirection: { value: new THREE.Vector3(-1, 1, 1).normalize() }, lightColor: { value: new THREE.Color(0xffffff) } }, vertexShader: `/* 上面修改后的顶点着色器代码 */`, fragmentShader: `/* 上面修改后的片元着色器代码 */`, // 可选:开启阴影支持 castShadow: true, receiveShadow: true });
4. 开启场景阴影(可选)
如果需要模型投射/接收阴影,还要给场景中的光源开启阴影:
// 以方向光为例 const directionalLight = new THREE.DirectionalLight(0xffffff, 1); directionalLight.castShadow = true; // 调整阴影参数优化效果(可选) directionalLight.shadow.mapSize.width = 2048; directionalLight.shadow.mapSize.height = 2048; scene.add(directionalLight);
内容的提问来源于stack exchange,提问作者Jnr
相关产品推荐
相关产品推荐

