You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:44:57