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

Three.js中仅接收光照的透明材质实现及Mapbox自定义图层聚光灯模拟技术咨询

嘿,这个需求我刚好在类似的Mapbox+Three.js项目里折腾过,给你捋捋具体的实现思路:

透明平面接收聚光灯光照的解决方案

一、原生Three.js有没有现成的材质?

原生Three.js里没有直接满足「仅接收光照、其余状态完全透明」的内置材质——ShadowMaterial其实只负责处理阴影接收,不会响应聚光灯的直接漫反射/高光。所以咱们得自己动手写个自定义材质。

二、用自定义ShaderMaterial实现核心效果

核心逻辑很简单:在片元着色器里只计算聚光灯对当前片段的光照贡献,当光照强度大于阈值时才显示颜色,否则直接丢弃像素实现透明。下面是适配Mapbox场景的简化示例:

1. 自定义材质代码

// 顶点着色器:复用Three.js默认的顶点变换与法线传递逻辑
const vertexShader = `
varying vec3 vNormal;
varying vec3 vPosition;

void main() {
    vNormal = normalize(normalMatrix * normal);
    vPosition = vec3(modelViewMatrix * vec4(position, 1.0));
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;

// 片元着色器:只响应聚光灯光照,无光照时透明
const fragmentShader = `
uniform vec3 lightColor;
uniform vec3 lightPosition;
uniform float lightAngle;
uniform float lightPenumbra;
uniform float lightIntensity;

varying vec3 vNormal;
varying vec3 vPosition;

void main() {
    // 计算聚光灯方向与顶点法线的夹角
    vec3 lightDir = normalize(lightPosition - vPosition);
    float angle = acos(dot(-lightDir, normalize(vNormal)));
    
    // 计算聚光灯的衰减(包含角度范围和半影过渡)
    float spotAttenuation = smoothstep(lightAngle + lightPenumbra, lightAngle, angle);
    // 计算漫反射光照强度
    float diffuse = max(dot(vNormal, lightDir), 0.0) * spotAttenuation * lightIntensity;
    
    // 光照强度过低时直接丢弃像素,实现透明
    if (diffuse < 0.01) {
        discard;
    }
    
    // 输出光照颜色,透明度随光照强度变化
    gl_FragColor = vec4(lightColor * diffuse, diffuse);
}
`;

// 创建自定义材质
const spotlightReceiverMaterial = new THREE.ShaderMaterial({
    vertexShader,
    fragmentShader,
    uniforms: {
        lightColor: { value: new THREE.Color(0xffffbb) }, // 聚光灯颜色
        lightPosition: { value: new THREE.Vector3(0, 0, 10) }, // 聚光灯位置
        lightAngle: { value: THREE.MathUtils.degToRad(30) }, // 聚光灯照射角度
        lightPenumbra: { value: THREE.MathUtils.degToRad(10) }, // 半影过渡角度
        lightIntensity: { value: 1.5 } // 光照强度
    },
    transparent: true,
    depthWrite: false, // 避免遮挡地图图层
    side: THREE.DoubleSide // 平面双面都能接收光照,按需调整
});

2. Mapbox自定义图层的适配要点

在Mapbox GL的Three.js自定义图层里使用时,还要注意这些细节:

  • 确保透明平面(比如THREE.PlaneGeometry)的坐标和Mapbox的地理投影坐标系对齐,需要把Three.js的局部坐标转换为Mapbox的经纬度投影坐标。
  • 如果聚光灯需要跟随地图视角/交互移动,要在图层的render回调里实时更新lightPosition这个uniform的值。
  • 调整图层的zIndex,确保聚光效果显示在地图之上但不遮挡其他交互元素。

三、备选方案:静态光照贴图

如果你的聚光灯位置固定,也可以提前渲染一张光照贴图,用MeshBasicMaterial结合透明贴图实现——不过这种方法灵活性差,只适合静态场景,不如自定义ShaderMaterial适配动态聚光灯需求。


内容的提问来源于stack exchange,提问作者daumann

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 14:47:29