ThreeJS实现:基于位置为地球模型应用日夜纹理并添加柔化过渡
实现地球昼夜纹理柔化过渡的方案
要实现这个效果,核心是用自定义ShaderMaterial控制纹理混合逻辑,ThreeJS默认材质没法直接实现这种基于朝向的动态纹理切换与柔化过渡。下面是具体实现步骤和代码示例:
核心思路
通过片元着色器计算每个表面点与太阳方向的夹角,生成平滑过渡的混合因子,用这个因子混合白天纹理和带灯光的夜间纹理,避免生硬的明暗分界。
具体实现步骤
1. 准备纹理资源
需要至少两张纹理(可选叠加灯光纹理增强效果):
- 地球白天纹理:正常地表颜色图
- 地球夜间纹理:深色地表图
- 城市灯光纹理:高亮城市灯光区域的灰度图(可选,用来模拟夜间灯光)
2. 编写自定义Shader
顶点着色器
传递表面法线和太阳方向到片元着色器:
varying vec3 vNormal; varying vec3 vLightDir; uniform vec3 sunWorldPos; void main() { vNormal = normalize(normalMatrix * normal); // 将太阳世界坐标转换为地球局部坐标系下的方向向量 vec3 sunLocalPos = (modelMatrixInverse * vec4(sunWorldPos, 1.0)).xyz; vLightDir = normalize(sunLocalPos - position); gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); }
片元着色器
计算混合因子,采样纹理并完成柔化混合:
uniform sampler2D dayTexture; uniform sampler2D nightTexture; uniform sampler2D lightMask; varying vec3 vNormal; varying vec3 vLightDir; void main() { // 计算表面法线与太阳方向的点积,朝向太阳的面为正值 float dotProduct = dot(vNormal, vLightDir); // 用smoothstep生成平滑过渡的混合因子,调整参数控制过渡宽度 // 区间越大,过渡带越宽,这里-0.1到0.1是柔和过渡区间 float mixFactor = smoothstep(-0.1, 0.1, dotProduct); // 采样纹理(用uv坐标,适配SphereGeometry的默认UV映射) vec4 dayColor = texture2D(dayTexture, gl_TexCoord[0].xy); vec4 nightColor = texture2D(nightTexture, gl_TexCoord[0].xy); vec4 lightColor = texture2D(lightMask, gl_TexCoord[0].xy); // 给夜间纹理叠加灯光效果,增强真实感 nightColor = nightColor + lightColor * 0.7; // 混合白天与夜间颜色 vec4 finalColor = mix(nightColor, dayColor, mixFactor); gl_FragColor = finalColor; }
3. 初始化地球网格与材质
在JS代码中加载纹理并创建ShaderMaterial:
// 加载纹理(实际项目建议用LoadingManager批量加载) const textureLoader = new THREE.TextureLoader(); const dayTexture = textureLoader.load('path/to/day_texture.jpg'); const nightTexture = textureLoader.load('path/to/night_texture.jpg'); const lightMask = textureLoader.load('path/to/light_mask.png'); // 创建高细分地球几何体,细分度越高过渡越平滑 const earthGeo = new THREE.SphereGeometry(5, 64, 64); // 创建自定义Shader材质 const earthMaterial = new THREE.ShaderMaterial({ uniforms: { dayTexture: { value: dayTexture }, nightTexture: { value: nightTexture }, lightMask: { value: lightMask }, sunWorldPos: { value: new THREE.Vector3(0, 0, 100) } // 初始太阳位置 }, vertexShader: document.getElementById('vertex-shader').textContent, fragmentShader: document.getElementById('fragment-shader').textContent, side: THREE.FrontSide }); // 创建地球网格并加入场景 const earth = new THREE.Mesh(earthGeo, earthMaterial); scene.add(earth);
4. 动态更新太阳方向
如果太阳或地球有动画,在每一帧更新Shader中的太阳位置uniform:
function animate() { requestAnimationFrame(animate); // 假设场景中有sunMesh对象作为太阳 earthMaterial.uniforms.sunWorldPos.value.copy(sunMesh.position); renderer.render(scene, camera); } animate();
关键细节
- 过渡宽度:调整smoothstep的两个参数(比如改成-0.2到0.2)可以加宽过渡带,让昼夜边界更柔和。
- 纹理UV:确保SphereGeometry的UV坐标正确,避免纹理错位。
- 灯光强度:夜间纹理的灯光叠加系数(示例中的0.7)可根据需求调整,避免灯光过亮或过暗。
内容的提问来源于stack exchange,提问作者cphayash
相关产品推荐
相关产品推荐

