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

如何为ShaderMaterial应用HemisphereLight实现光照效果?

解决ShaderMaterial物体不受HemisphereLight影响的问题

你的问题核心在于:自定义ShaderMaterial不会像Three.js内置材质那样自动处理光照逻辑,必须手动在着色器中实现光照计算,同时开启材质的光照支持开关。以下是具体修改步骤和完整代码:

关键修改点

  • 开启ShaderMaterial的lights: true选项,让Three.js自动注入光照相关的uniforms
  • 在顶点着色器中计算并传递法向量、视图空间位置给片元着色器(光照计算必需数据)
  • 在片元着色器中实现HemisphereLight的光照计算逻辑

修改后的完整代码

<script src="https://threejs.org/build/three.js"></script>

<script type="module">
function main() {
    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera( 200, window.innerWidth/window.innerHeight, 0.1, 1000 );

    var renderer = new THREE.WebGLRenderer();
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );

    // 修改后的顶点着色器:添加光照所需的变量和计算
    var vShader = `
    precision mediump float;
    uniform float uTime;
    // Three.js自动提供的内置uniforms
    uniform mat4 modelMatrix;
    uniform mat4 modelViewMatrix;
    uniform mat3 normalMatrix;
    // 传递给片元着色器的变量
    varying vec3 vNormal;
    varying vec3 vViewPosition;

    void main() {
      // 保留你的顶点位移逻辑
      float z = 0.1*cos(uTime) * cos(position.y*40. - uTime) * position.y*sin(log(position.y))*0.5 + 0.5;
      vec3 modifiedPosition = vec3(position.x, position.y, z);
      
      // 计算光照所需的法向量和视图空间位置
      vNormal = normalize(normalMatrix * normal);
      vec4 modelViewPos = modelViewMatrix * vec4(modifiedPosition, 1.0);
      vViewPosition = -modelViewPos.xyz;
      
      gl_Position = projectionMatrix * modelViewPos;
    }
  `;

  // 修改后的片元着色器:实现HemisphereLight光照计算
  var fShader = `
  precision mediump float;
    uniform vec2 uRes;
    // Three.js自动注入的HemisphereLight相关uniforms
    uniform vec3 hemisphereLightSkyColor;
    uniform vec3 hemisphereLightGroundColor;
    uniform float hemisphereLightIntensity;
    // 从顶点着色器传递过来的变量
    varying vec3 vNormal;
    varying vec3 vViewPosition;

    void main() {
      // 基础UV颜色
      vec2 uv = 1.5*gl_FragCoord.xy/uRes;
      vec3 baseColor = vec3(uv.x, uv.y, 0.0);
      
      // HemisphereLight光照计算
      vec3 normal = normalize(vNormal);
      float dotNL = dot(normal, vec3(0.0, 1.0, 0.0)); // 半球光的方向是向上的
      vec3 lightColor = mix(hemisphereLightGroundColor, hemisphereLightSkyColor, 0.5 * dotNL + 0.5);
      vec3 finalColor = baseColor * lightColor * hemisphereLightIntensity;
      
      gl_FragColor = vec4(finalColor, 1.0);
    }
  `;

  var planeG = new THREE.PlaneGeometry(25, 60, 20, 20);

  var planeM = new THREE.ShaderMaterial({
      uniforms: {
        uTime: { type:"f", value:0.0 },
        uRes: { type:"v2", value:new THREE.Vector2(window.innerWidth, window.innerHeight) }
      },
      lights: true, // 开启光照支持,必须打开!
      vertexShader: vShader,
      fragmentShader: fShader,
      side: THREE.DoubleSide,
  });

  var plane = new THREE.Mesh( planeG, planeM );
  var light = new THREE.HemisphereLight(0xffffff, 0x000000, 2);
  
  scene.add(light);
  scene.add(plane);

  plane.rotateZ(Math.PI/2);
  camera.position.z = 4;

  scene.background = new THREE.Color(0xffffff);
  
  var render = function (time) {
    requestAnimationFrame( render );
    plane.material.uniforms.uTime.value = time/100;
    renderer.render(scene, camera);
  };

  render();
};

main();
</script>

额外说明

  • 如果你使用其他光源(比如DirectionalLight、PointLight),需要在片元着色器中对应实现该光源的光照计算逻辑
  • Three.js的内置uniforms和变量可参考官方文档,比如normalMatrix用于将法向量转换到视图空间,modelViewMatrix用于将顶点转换到视图空间
  • 已修正你代码中uRes变量的类型错误,从f改为v2以匹配Vector2类型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 02:15:34