我正在为一个项目制作地球 3D 模型,但遇到了问题。最初,我没有使用任何着色,并将两种材质添加到一个组中,并使用 THREE.AdditiveBlending。这导致了下图所示的情况,其中夜间纹理上显示的灯光在白天也显示出来。
接下来,我切换到 GLSL。我是新手,所以如果我的代码很糟糕,我深表歉意。我正在尝试获取它,以便如果 dayTexture 存在,则不会显示任何灯光。在下图中,您可以看到这只是以夜间纹理应该是深蓝色模糊的区域结束。
你可以忽略大气层和云层,这是在上一张截图之后添加的。
到目前为止,我已经从混合切换到 GLSL,我当前的代码在这里:
<script id="earthVertexShader" type="x-shader/x-vertex">
//varying means the value is passed in the both the vertex and fragment shader, so a vector3 is passed to the fragment shader
varying vec3 varyNormal;
varying vec3 varyPosition;
void main(){
//basically prevents the model from being distorted, when orbitControls are used
varyNormal = normalize(normalMatrix * normal);
//position of the vertex in the world space (where the earth is)
varyPosition = vec3(modelViewMatrix * vec4(position, 1.0));
//gets the final position for the rendering
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
</script>
<!-- fragment shader deals with colors, while vertex is for points on the 3d shape-->
<script id="earthFragmentShader" type="x-shader/x-fragment">
uniform sampler2D dayTexture;
uniform sampler2D nightTexture;
uniform vec3 lightDirection;
uniform vec2 resolution;
varying vec3 varyNormal;
varying vec3 varyPosition;
void main(){
vec3 lightDir = normalize(lightDirection);
float dotData = dot(varyNormal, lightDir);
//takes info abt the light and the normal of the vertex to calculate the brightness of the vertex
float blendFactor = smoothstep(-0.1, 0.1, dotData);
//gets brightness of the current vertex
vec2 uv = gl_FragCoord.xy / resolution;
vec4 dayColor = texture2D(dayTexture, uv);
vec4 nightColor = texture2D(nightTexture, uv);
// Blend day and night textures
vec4 finalColor = mix(nightColor, dayColor, blendFactor);
gl_FragColor = vec4(finalColor.rgb, 1.0);
}
</script>
此外,这里是我向地球添加夜间纹理的地方:
//values needed for the shading: texture and where the light is from
const dayTexture = new THREE.TextureLoader().load(dayTimeTexture);
const nightTexture = new THREE.TextureLoader().load(nightTimeTexture);
const lightDirection = new THREE.Vector3(-3.5, 0.5, 1.5).normalize();
//changed to shaderMaterial for the shading
const nightShader = new THREE.ShaderMaterial({
//uniforms are just the data that the shader uses we defined earlier
uniforms: {
dayTexture: {value: dayTexture},
nightTexture: {value: nightTexture},
lightDirection: {value: lightDirection},
resolution: {value: new THREE.Vector2(window.innerWidth, window.innerHeight)},
},
vertexShader: document.getElementById("earthVertexShader").textContent,
fragmentShader: document.getElementById("earthFragmentShader").textContent,
transparent: true,
blending: THREE.NormalBlending,
});
const lightMesh = new THREE.Mesh(geometry, nightShader)
earthGrouping.add(lightMesh);
添加的daytimeTexture是使用imgmap的meshPhongMaterial,在意识到这不适用于ShaderMaterial之前我也尝试过。我认为这与片段着色器中的混合和/或透明度有关,但我不太确定。
任何帮助将不胜感激!
如果你的几何体是
THREE.SphereGeometry(...)
,那么在这种情况下你已经有了良好的法线和uv,并且uv和法线都可以从vertexShader传递给fragmentShader,如下所示:varying vec2 vUV;
varying vec3 vNormal;
void main() {
vUV = uv;
vNormal = normal;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
片段着色器:
varying vec2 vUV;
varying vec3 vNormal;
uniform vec3 u_direction;
uniform sampler2D u_dayTexture;
uniform sampler2D u_nightTexture;
// in radians [0...pi]
float angleBetweenVec(vec3 a, vec3 b) {
return acos( dot(a,b)/length(a)/length(b) );
}
const float PI = 3.141592653589793;
void main() {
vec4 dayTextureColor = texture2D( u_dayTexture, vUV );
vec4 nightTextureColor = texture2D( u_nightTexture, vUV );
float center = PI/2.0;
float halfDelta = PI/40.0;
float blendFactor = smoothstep( center-halfDelta, center+halfDelta, angleBetweenVec(vNormal, u_direction));
vec4 color = mix( nightTextureColor, dayTextureColor, blendFactor );
gl_FragColor = color;
}