切換語言為:簡體
利用Cesium後處理實現太陽鏡頭光暈效果

利用Cesium後處理實現太陽鏡頭光暈效果

  • 爱糖宝
  • 2024-11-15
  • 2028
  • 0
  • 0

在三維地球視覺化領域,Cesium是一個非常強大的開源JavaScript庫,它能夠幫助開發者輕鬆地建立豐富的3D地理應用。本文將介紹如何使用Cesium結合GLSL(OpenGL Shading Language)著色器來實現一個動態的光影效果,為您的地球模型增添更多視覺上的吸引力。

引入Cesium

首先,確保您的專案中已經引入了Cesium庫。可以透過npm安裝或直接從CDN引入Cesium到您的HTML檔案中。

<script src="https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.120/Build/Cesium/Widgets/widgets.css" rel="stylesheet">

建立基本的Cesium Viewer

接下來,在頁面上建立一個Cesium Viewer例項,這是所有後續操作的基礎。

var viewer = new Cesium.Viewer('cesiumContainer', {
  terrain: Cesium.Terrain.fromWorldTerrain(),
});

編寫GLSL著色器程式碼

爲了實現動態光影效果,我們需要編寫一個GLSL片段著色器。下面的程式碼段定義了一個複雜的光照效果,包括隨機形狀、光環以及太陽光線等元素。

uniform sampler2D colorTexture;
    uniform sampler2D depthTexture;
    in vec2 v_textureCoordinates;

    float rnd(vec2 p) {
        float f = fract(sin(dot(p, vec2(12.1234, 72.8392))) * 45123.2);
        return f;
    }

    float rnd(float w) {
        float f = fract(sin(w) * 1000.);
        return f;
    }

    float regShape(vec2 p, int N) {
        float f;
        float a = atan(p.x, p.y) + 0.2;
        float b = 6.28319 / float(N);
        f = smoothstep(0.5, 0.51, cos(floor(0.5 + a / b) * b - a) * length(p.xy));
        return f;
    }

    vec3 circle(vec2 p, float size, float decay, vec3 color, vec3 color2, float dist, vec2 mouse) {
    // 控制光暈 效果
        float l = length(p + mouse * (dist * 4.)) + size / 2.;
        float l2 = length(p + mouse * (dist * 4.)) + size / 3.;
        float c = max(0.01 - pow(length(p + mouse * dist), size * 1.4), 0.0) * 50.;
        float c1 = max(0.001 - pow(l - 0.3, 1. / 40.) + sin(l * 30.), 0.0) * 3.;
        float c2 = max(0.04 / pow(length(p - mouse * dist / 2. + 0.09) * 1., 1.), 0.0) / 20.;
        float s = max(0.01 - pow(regShape(p * 5. + mouse * dist * 5. + 0.9, 6), 1.), 0.0) * 5.;

        color = 0.5 + 0.5 * sin(color);
        color = cos(vec3(0.44, 0.24, 0.2) * 8. + dist * 4.) * 0.5 + 0.5;
        vec3 f = c * color;
        f += c1 * color;
        f += c2 * color;
        f += s * color;
        return f - 0.01;
    }

    float sun(vec2 p, vec2 mouse) {
        float f;
        vec2 sunp = p + mouse;
        float sun = 1.0 - length(sunp) * 8.;
        return f;
    }

    void main() {
        float iTime = czm_frameNumber / 10000.;
        vec4 sceneColor = texture(colorTexture, v_textureCoordinates);
        vec2 uv = gl_FragCoord.xy / czm_viewport.zw - 0.5;
        uv.x *= czm_viewport.z / czm_viewport.w;
        float depth = czm_unpackDepth(texture(czm_globeDepthTexture, v_textureCoordinates));
        vec4 sunPos = czm_morphTime == 1.0 ? vec4(czm_sunPositionWC, 1.0) : vec4(czm_sunPositionColumbusView.zxy, 1.0);
        vec4 sunPositionEC = czm_view * sunPos;
        vec4 sunPositionWC = czm_eyeToWindowCoordinates(sunPositionEC);
        sunPos = czm_viewportOrthographic * vec4(sunPositionWC.xy, -sunPositionWC.z, 1.0);
        vec2 mm = sunPos.xy ;
        mm.x *= czm_viewport.z / czm_viewport.w;

        vec3 circColor = vec3(0.9, 0.2, 0.1);
        vec3 circColor2 = vec3(0.3, 0.1, 0.9);

        vec3 color = mix(vec3(0.3, 0.2, 0.02) / 0.9, vec3(0.2, 0.5, 0.8), uv.y) * 3. - 0.52 * sin(iTime); // iTime 太陽光環控制大小 時間速率

        for (float i = 0.; i < 10.; i++) {
            color += circle(uv, pow(rnd(i * 2000.) * 1.8, 2.) + 1.41, 0.0, circColor + i, circColor2 + i, rnd(i * 20.) * 3. + 0.2 - 0.5, mm);
        }

        float a = atan(uv.y - mm.y, uv.x - mm.x);
        float l = max(1.0 - length(uv - mm) - 0.84, 0.0);

        float bright = 0.1;
        color += max(0.1 / pow(length(uv - mm) * 5., 5.), 0.0) * abs(sin(a * 5. + cos(a * 9.))) / 20.;
        color += max(0.1 / pow(length(uv - mm) * 10., 1. / 20.), 0.0) + abs(sin(a * 3. + cos(a * 9.))) / 8. * (abs(sin(a * 9.))) / 1.;
        color += (max(bright / pow(length(uv - mm) * 4., 1. / 2.), 0.0) * 4.) * vec3(0.2, 0.21, 0.3) * 4.;

        color *= exp(1.0 - length(uv - mm)) / 5.;
        vec4 sunDepthColor = texture(czm_globeDepthTexture, v_textureCoordinates);
        float sunDepth = sunDepthColor.r;
        if (depth > sunDepth) {
          out_FragColor = sceneColor;
        } else {
          out_FragColor = vec4(color, 1.0) * vec4(vec4(color, 1.0).aaa, 1.0) + sceneColor * (2.001 - vec4(color, 1.0).a);
        }
    }

此著色器利用了隨機函式rnd生成隨機值,並透過circle函式模擬光圈效果。sun函式則用於計算太陽光線的效果。整個著色器的核心在於main函式,它根據不同的引數組合計算出最終的顏色輸出。

新增後處理階段

爲了將上述著色器應用於Cesium場景,我們需要建立一個PostProcessStage物件,並將其新增到viewer.scene.postProcessStages集合中。

最終實現效果

利用Cesium後處理實現太陽鏡頭光暈效果

下節預告 模型積雪

利用Cesium後處理實現太陽鏡頭光暈效果

0則評論

您的電子郵件等資訊不會被公開,以下所有項目均必填

OK! You can skip this field.