如何修复 THREE.js 上的 GLSS 着色,使其显示地球上准确的昼夜周期?

问题描述 投票:0回答:1

我正在为一个项目制作地球 3D 模型,但遇到了问题。最初,我没有使用任何着色,并将两种材质添加到一个组中,并使用 THREE.AdditiveBlending。这导致了下图所示的情况,其中夜间纹理上显示的灯光在白天也显示出来。

Original model of the earth

接下来,我切换到 GLSL。我是新手,所以如果我的代码很糟糕,我深表歉意。我正在尝试获取它,以便如果 dayTexture 存在,则不会显示任何灯光。在下图中,您可以看到这只是以夜间纹理应该是深蓝色模糊的区域结束。
Current problem with the shading
你可以忽略大气层和云层,这是在上一张截图之后添加的。

到目前为止,我已经从混合切换到 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之前我也尝试过。我认为这与片段着色器中的混合和/或透明度有关,但我不太确定。

任何帮助将不胜感激!

javascript three.js glsl
1个回答
0
投票

如果你的几何体是

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;
}
© www.soinside.com 2019 - 2024. All rights reserved.