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

A-Frame中如何为受光照物体的不同区域设置特殊纹理?

当然可以实现这个效果!核心思路是通过自定义GLSL shader,根据物体表面的光照强度来混合日间和夜间两张纹理,而且完全可以做到适配不同光源类型和非完美几何体。我给你详细拆解一下实现方案:

核心思路

我们需要在fragment shader里完成两个关键步骤:

  • 计算物体每个表面点的光照强度(0到1之间,0代表完全没被照亮,1代表完全照亮)
  • 用这个光照强度作为混合因子,在日间纹理和夜间纹理之间做平滑过渡

这个方法不依赖物体是完美球体,只要模型的法线数据正确,就能适配任何几何体;同时通过传递光源的参数(方向、位置、类型),可以支持平行光、点光源等多种光源类型。

自定义Shader实现步骤

在A-Frame中,我们可以通过shader: custom来使用自定义GLSL shader,需要分别编写vertex shader和fragment shader:

1. Vertex Shader

顶点着色器的作用是把模型空间的法线、顶点位置转换到世界空间,传递给片元着色器,这样才能正确计算光照:

varying vec3 vNormal;
varying vec2 vUv;

void main() {
  vNormal = normalize(mat3(modelMatrix) * normal);
  vUv = uv;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

2. Fragment Shader(平行光版本)

片元着色器里计算光照强度,然后混合纹理。这里先以模拟太阳光的平行光为例:

uniform sampler2D dayMap;
uniform sampler2D nightMap;
uniform vec3 lightDirection; // 平行光方向(世界空间)
uniform float ambientIntensity; // 环境光强度,避免完全变黑

varying vec3 vNormal;
varying vec2 vUv;

void main() {
  // 计算漫反射强度
  float diffuse = max(dot(normalize(vNormal), normalize(-lightDirection)), 0.0);
  // 结合环境光,得到最终光照强度
  float lightIntensity = ambientIntensity + (1.0 - ambientIntensity) * diffuse;
  
  // 采样两张纹理
  vec4 dayColor = texture2D(dayMap, vUv);
  vec4 nightColor = texture2D(nightMap, vUv);
  
  // 混合纹理:光照强的地方显示日间纹理,弱的地方显示夜间纹理
  gl_FragColor = mix(nightColor, dayColor, lightIntensity);
}

3. 适配点光源

如果要支持点光源,只需要修改光照强度的计算逻辑,加入距离衰减:

// 新增uniform参数
uniform vec3 lightPosition; // 点光源位置(世界空间)
uniform float lightRadius; // 点光源影响半径

// 替换光照强度计算部分
vec3 lightVec = lightPosition - vWorldPosition;
float distance = length(lightVec);
float attenuation = 1.0 - clamp(distance / lightRadius, 0.0, 1.0);
float diffuse = max(dot(normalize(vNormal), normalize(lightVec)), 0.0);
float lightIntensity = ambientIntensity + (1.0 - ambientIntensity) * diffuse * attenuation;

你甚至可以加入一个uniform变量来指定光源类型,在shader里做分支判断,实现同时支持多种光源的灵活效果。

完整A-Frame示例代码

下面是一个可直接运行的地球昼夜效果示例:

<!DOCTYPE html>
<html>
<head>
  <script src="https://aframe.io/releases/1.4.2/aframe.min.js"></script>
  <script>
    AFRAME.registerShader('dayNight', {
      schema: {
        dayMap: { type: 'map' },
        nightMap: { type: 'map' },
        lightDirection: { type: 'vec3', default: [1, 0, 0] },
        ambientIntensity: { type: 'float', default: 0.1 }
      },
      vertexShader: `
        varying vec3 vNormal;
        varying vec2 vUv;

        void main() {
          vNormal = normalize(mat3(modelMatrix) * normal);
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
      `,
      fragmentShader: `
        uniform sampler2D dayMap;
        uniform sampler2D nightMap;
        uniform vec3 lightDirection;
        uniform float ambientIntensity;

        varying vec3 vNormal;
        varying vec2 vUv;

        void main() {
          float diffuse = max(dot(normalize(vNormal), normalize(-lightDirection)), 0.0);
          float lightIntensity = ambientIntensity + (1.0 - ambientIntensity) * diffuse;
          
          vec4 dayColor = texture2D(dayMap, vUv);
          vec4 nightColor = texture2D(nightMap, vUv);
          
          gl_FragColor = mix(nightColor, dayColor, lightIntensity);
        }
      `
    });
  </script>
</head>
<body>
  <a-scene>
    <!-- 平行光模拟太阳光 -->
    <a-light type="directional" position="5 0 0" intensity="1"></a-light>
    
    <!-- 地球模型,使用自定义昼夜shader -->
    <a-entity 
      geometry="primitive: sphere; radius: 2"
      material="shader: dayNight; 
                dayMap: url(https://example.com/earth-day.jpg); 
                nightMap: url(https://example.com/earth-night.jpg);
                lightDirection: 5 0 0;"
      animation="property: rotation; to: 0 360 0; dur: 100000; loop: true">
    </a-entity>
    
    <a-camera position="0 0 5"></a-camera>
  </a-scene>
</body>
</html>
灵活适配的扩展技巧
  • 非完美几何体适配:只要你的模型导出时包含正确的法线数据,这个shader就能正常工作——因为我们用的是模型空间法线转换到世界空间后的结果,和几何体形状无关。
  • 多光源支持:如果场景里有多个光源,可以在shader里计算每个光源的光照强度,然后取最大值或者累加(注意不要超过1.0)。
  • 平滑过渡调整:可以给lightIntensity加入smoothstep函数,让昼夜过渡更自然:lightIntensity = smoothstep(0.2, 0.8, lightIntensity);
  • 动态光源:如果光源位置/方向会变化,只需要在JavaScript里实时更新material的lightDirection或lightPosition属性即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:58:18