1. 程式人生 > >Cesium中級教程9

Cesium中級教程9

Cesium中文網:http://cesiumcn.org/ | 國內快速訪問:http://cesium.coinidea.com/

要了解粒子系統的基礎知識,請參見粒子系統入門教程。

Weather 天氣

image.png

image.png

Setup 設定

若要生成雪效果,請首先為每個粒子新增雪花影象,然後在updateParticle函式中定義粒子的移動行為和其他動態元素。

The images 影象

本教程中使用了以下三個影象。左邊是雨粒子;中間的影象是雪粒子;右邊的影象用於火效果。

image.png

The update function 更新函式

更新函式用於定義粒子的移動、排列和視覺化。修改粒子的color顏色、imageSize影象大小和particleLife

粒子生命週期。我們甚至可以基於到相機距離定義粒子(如下所述)、匯入模型或到地球本身的距離來修改它們。

下面是我們針對雪的更新函式:

// snow
var snowGravityVector = new Cesium.Cartesian3();
var snowUpdate = function(particle, dt) {
    Cesium.Cartesian3.normalize(particle.position, snowGravityVector);
    Cesium.Cartesian3.multiplyByScalar(snowGravityVector,
                                                            Cesium.Math.randomBetween(-30.0, -300.0),
                                                            snowGravityVector);
    particle.velocity = Cesium.Cartesian3.add(particle.velocity, snowGravityVector, particle.velocity);

    var distance = Cesium.Cartesian3.distance(scene.camera.position, particle.position);
    if (distance > (snowRadius)) {
        particle.endColor.alpha = 0.0;
    } else {
        particle.endColor.alpha = snowSystem.endColor.alpha / (distance / snowRadius + 0.1);
    }
};

函式的第一部分使粒子像重力一樣下落。更新函式還包含一個距離檢查,以便粒子在遠離相機時消失。

image.png

額外的天氣效應

使用霧和大氣效果來增強視覺化效果,並匹配我們試圖複製的天氣型別。

hueshift沿著顏色光譜改變顏色,saturationShift改變了視覺實際需要的顏色與黑白的對比程度,brightnessShift改變了顏色的生動程度。

霧密度改變了地球上覆蓋物與霧的顏色之間的不透明程度。霧的minimumBrightness用來使霧變暗。

// snow
scene.skyAtmosphere.hueShift = -0.8;
scene.skyAtmosphere.saturationShift = -0.7;
scene.skyAtmosphere.brightnessShift = -0.33;

scene.fog.density = 0.001;
scene.fog.minimumBrightness = 0.8;

The systems 系統

image.png

Snow 雪

雪花系統使用snowflake_particle影象,並使用minimumImageSizemaximumImageSize,在該範圍內隨機建立雪花。

var snowParticleSize = scene.drawingBufferWidth / 100.0;
var snowRadius = 100000.0;

var snowSystem = new Cesium.ParticleSystem({
    modelMatrix : new Cesium.Matrix4.fromTranslation(scene.camera.position),
    minimumSpeed : -1.0,
    maximumSpeed : 0.0,
    lifetime : 15.0,
    emitter : new Cesium.SphereEmitter(snowRadius),
    startScale : 0.5,
    endScale : 1.0,
    image : "../../SampleData/snowflake_particle.png",
    emissionRate : 7000.0,
    startColor : Cesium.Color.WHITE.withAlpha(0.0),
    endColor : Cesium.Color.WHITE.withAlpha(1.0),
    minimumImageSize : new Cartesian2(snowParticleSize, snowParticleSize),
    maximumImageSize : new Cartesian2(snowParticleSize * 2.0, snowParticleSize * 2.0),
    updateCallback : snowUpdate
});
scene.primitives.add(snowSystem);

Rain 雨

雨滴系統使用circular_particle.png用於雨滴。imageSize用於垂直拉伸影象,使雨水呈現細長的外觀。

rainSystem = new Cesium.ParticleSystem({
    modelMatrix : new Cesium.Matrix4.fromTranslation(scene.camera.position),
    speed : -1.0,
    lifetime : 15.0,
    emitter : new Cesium.SphereEmitter(rainRadius),
    startScale : 1.0,
    endScale : 0.0,
    image : "../../SampleData/circular_particle.png",
    emissionRate : 9000.0,
    startColor :new Cesium.Color(0.27, 0.5, 0.70, 0.0),
    endColor : new Cesium.Color(0.27, 0.5, 0.70, 0.98),
    imageSize : new Cesium.Cartesian2(rainParticleSize, rainParticleSize * 2),
    updateCallback : rainUpdate
});
scene.primitives.add(rainSystem);

雨水更新函式略有不同,因為降雨比降雪快得多。

// rain
rainGravityScratch = Cesium.Cartesian3.normalize(particle.position, rainGravityScratch);
rainGravityScratch = Cesium.Cartesian3.multiplyByScalar(rainGravityScratch,
                                                        -1050.0,
                                                        rainGravityScratch);

particle.position = Cesium.Cartesian3.add(particle.position, rainGravityScratch, particle.position);

要使環境與場景的氣氛匹配,請修改大氣和霧以匹配雨水。下面的程式碼使一個深藍色的天空被薄霧覆蓋。

// rain
scene.skyAtmosphere.hueShift = -0.97;
scene.skyAtmosphere.saturationShift = 0.25;
scene.skyAtmosphere.brightnessShift = -0.4;

scene.fog.density = 0.00025;
scene.fog.minimumBrightness = 0.01;

獲取額外的幫助,請訪問Sandcastle example for both snow and rain

Comet and rocket tails 彗星和火箭尾部

image.png

image.png

Using multiple particle systems 使用多重粒子系統

要建立彗星和火箭尾部,我們需要多個粒子系統。該示例建立的粒子環上的每個位置都是完全獨立的粒子系統。這使我們能夠更均勻地控制系統的運動方向。視覺化此效果的一個簡單方法是將cometOptions.numberOfSystems限制為2,且cometOptions.colorOptions僅包括兩種顏色,如下圖所示。

image.png

為了簡化不同的系統集合,建立陣列來攜帶與彗星相關聯的獨立系統和與火箭例子相關聯的獨立系統。

var rocketSystems = [];
var cometSystems = [];

為物件建立兩個不同的選項:一個用於彗星Comet版本,另一個用於火箭Rocket版本。這使得兩個系統的初始系統數、偏移值等不同,看起來也不同。

var cometOptions = {
    numberOfSystems : 100.0,
    iterationOffset : 0.003,
    cartographicStep : 0.0000001,
    baseRadius : 0.0005,

    colorOptions : [{
        red : 0.6,
        green : 0.6,
        blue : 0.6,
        alpha : 1.0
    }, {
        red : 0.6,
        green : 0.6,
        blue : 0.9,
        alpha : 0.9
    }, {
        red : 0.5,
        green : 0.5,
        blue : 0.7,
        alpha : 0.5
    }]
};

var rocketOptions = {
    numberOfSystems : 50.0,
    iterationOffset :  0.1,
    cartographicStep : 0.000001,
    baseRadius : 0.0005,

    colorOptions : [{
        minimumRed : 1.0,
        green : 0.5,
        minimumBlue : 0.05,
        alpha : 1.0
    }, {
        red : 0.9,
        minimumGreen : 0.6,
        minimumBlue : 0.01,
        alpha : 1.0
    }, {
        red : 0.8,
        green : 0.05,
        minimumBlue : 0.09,
        alpha : 1.0
    }, {
        minimumRed : 1,
        minimumGreen : 0.05,
        blue : 0.09,
        alpha : 1.0
    }]
};

colorOptions是用於隨機視覺的顏色陣列。每個系統從一個特定的顏色開始,而不是有一組顏色幾何,這取決於當前正在建立的系統。在下面的示例中,i表示當前迭代輪數。

var color = Cesium.Color.fromRandom(options.colorOptions[i % options.colorOptions.length]);

Setup 設定

使用下面的函式作為每個系統的初始化

function createParticleSystems(options, systemsArray) {
    var length = options.numberOfSystems;
    for (var i = 0; i < length; ++i) {
        scratchAngleForOffset = Math.PI * 2.0 * i / options.numberOfSystems;
        scratchOffset.x += options.baseRadius * Math.cos(scratchAngleForOffset);
        scratchOffset.y += options.baseRadius * Math.sin(scratchAngleForOffset);

        var emitterModelMatrix = Cesium.Matrix4.fromTranslation(scratchOffset, matrix4Scratch);
        var color = Cesium.Color.fromRandom(options.colorOptions[i % options.colorOptions.length]);
        var force = forceFunction(options, i);

        var item = viewer.scene.primitives.add(new Cesium.ParticleSystem({
            image : getImage(),
            startColor : color,
            endColor : color.withAlpha(0.0),
            particleLife : 3.5,
            speed : 0.00005,
            imageSize : new Cesium.Cartesian2(15.0, 15.0),
            emissionRate : 30.0,
            emitter : new Cesium.CircleEmitter(0.1),
            bursts : [ ],
            lifetime : 0.1,
            forces : force,
            modelMatrix : particlesModelMatrix,
            emitterModelMatrix : emitterModelMatrix
        }));
        systemsArray.push(item);
    }
}

由於兩個尾部版本都相似,因此可以使用相同的createParticleSystems函式來建立其中一個。傳入CometOptionsRocketOptions選項引數以建立不同的效果。

Create the particle image from scratch 從頭開始建立粒子影象

不是從URL載入影象,getImage函式是使用HTML畫布建立影象。這使得影象建立更加靈活。

var particleCanvas;
function getImage() {
    if (!Cesium.defined(particleCanvas)) {
        particleCanvas = document.createElement('canvas');
        particleCanvas.width = 20;
        particleCanvas.height = 20;
        var context2D = particleCanvas.getContext('2d');
        context2D.beginPath();
        context2D.arc(8, 8, 8, 0, Cesium.Math.TWO_PI, true);
        context2D.closePath();
        context2D.fillStyle = 'rgb(255, 255, 255)';
        context2D.fill();
    }
    return particleCanvas;
}

The force function 強制函式

下面使我們的updateCallback函式:

var scratchCartesian3 = new Cesium.Cartesian3();
var scratchCartographic = new Cesium.Cartographic();
var forceFunction = function(options, iteration) {
    var iterationOffset = iteration;
    var func = function(particle) {
        scratchCartesian3 = Cesium.Cartesian3.normalize(particle.position, new Cesium.Cartesian3());
        scratchCartesian3 = Cesium.Cartesian3.multiplyByScalar(scratchCartesian3, -1.0, scratchCartesian3);

        particle.position = Cesium.Cartesian3.add(particle.position, scratchCartesian3, particle.position);

        scratchCartographic = Cesium.Cartographic.fromCartesian(particle.position,
                                                                Cesium.Ellipsoid.WGS84,
                                                                scratchCartographic);

        var angle = Cesium.Math.PI * 2.0 * iterationOffset / options.numberOfSystems;
        iterationOffset += options.iterationOffset;
        scratchCartographic.longitude += Math.cos(angle) * options.cartographicStep;
        scratchCartographic.latitude += Math.sin(angle) * options.cartographicStep;

        particle.position = Cesium.Cartographic.toCartesian(scratchCartographic);
    };
    return func;
};

注意forceFunction正在返回函式。返回的func是實際的updateCallback函式。對於每次迭代,update函式根據angleiterationOffset建立不同的旋轉偏移量。較小的迭代偏移只會稍微調整角度,允許半徑隨著系統的繼續穩步增大,如彗星示例所示。較大的迭代偏移將更快地改變角度;這將使更緊密、更不穩定的圓柱形輸出,如火箭例子中所示。

本教程使用正弦和餘弦函式進行迴圈效果。對於其他的效果,試著做一些形狀,比如Lissajous curveGibbs phenomenon,或者square wave

Relative position 相對位置

image.png

使用modelMatrix將粒子系統定位在平面後面的適當位置。因為這些系統是垂直的,所以我們需要使用particleOffset值進行輕微的偏移。如createParticleSystems函式所示,根據迭代,計算每個系統的emitterModelMatrix偏移量。

// positioning the plane
var planePosition = Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883, 800.0);
var particlesOffset = new Cesium.Cartesian3(-8.950115473940969, 34.852766731753945, -30.235411095432937);

// creating the particles model matrix
var transl = Cesium.Matrix4.fromTranslation(particlesOffset, new Cesium.Matrix4());
var translPosition = Cesium.Matrix4.fromTranslation(planePosition, new Cesium.Matrix4());
var particlesModelMatrix = Cesium.Matrix4.multiplyTransformation(translPosition, transl, new Cesium.Matrix4());

Resources 資源

額外的幫助請訪問Sandcastle example for both tails examples 更多示例程式碼:

本文由 admin 創作,採用 知識共享署名 3.0 中國大陸許可協議 進行許可。 可自由轉載、引用,但需署名作者且註明文章出處。

Cesium中文網交流QQ群:807482793

Cesium中文網:http://cesiumcn.org/ | 國內快速訪問:http://ces