mirror of https://github.com/CesiumGS/cesium.git
659 lines
20 KiB
HTML
659 lines
20 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
<meta
|
|
name="viewport"
|
|
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
|
|
/>
|
|
<meta
|
|
name="description"
|
|
content="Rendering Volume Cloud with Texture3D and Custom GLSL. Transplanted from Three.js"
|
|
/>
|
|
<meta name="cesium-sandcastle-labels" content="Tutorials,Showcases" />
|
|
<title>Cesium Demo</title>
|
|
<script type="text/javascript" src="../Sandcastle-header.js"></script>
|
|
<script
|
|
type="text/javascript"
|
|
src="../../../Build/CesiumUnminified/Cesium.js"
|
|
nomodule
|
|
></script>
|
|
<script type="module" src="../load-cesium-es6.js"></script>
|
|
</head>
|
|
|
|
<body class="sandcastle-loading" data-sandcastle-bucket="bucket-requirejs.html">
|
|
<style>
|
|
@import url(../templates/bucket.css);
|
|
</style>
|
|
<div id="cesiumContainer" class="fullSize"></div>
|
|
<div id="loadingOverlay">
|
|
<h1>Loading...</h1>
|
|
</div>
|
|
<div id="toolbar"></div>
|
|
<script id="cesium_sandcastle_script">
|
|
window.startup = async function (Cesium) {
|
|
"use strict";
|
|
//Sandcastle_Begin
|
|
|
|
// ImprovedNoise from Three.js
|
|
// https://github.com/mrdoob/three.js/blob/dev/examples/jsm/math/ImprovedNoise.js
|
|
|
|
const lerp = Cesium.Math.lerp;
|
|
|
|
const _p = [
|
|
151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36,
|
|
103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0,
|
|
26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56,
|
|
87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134, 139, 48, 27, 166,
|
|
77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55,
|
|
46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132,
|
|
187, 208, 89, 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109,
|
|
198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126,
|
|
255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183,
|
|
170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172,
|
|
9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104,
|
|
218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81,
|
|
51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84,
|
|
204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67,
|
|
29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180,
|
|
];
|
|
|
|
for (let i = 0; i < 256; i++) {
|
|
_p[256 + i] = _p[i];
|
|
}
|
|
|
|
function fade(t) {
|
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
}
|
|
|
|
function grad(hash, x, y, z) {
|
|
const h = hash & 15;
|
|
const u = h < 8 ? x : y,
|
|
v = h < 4 ? y : h === 12 || h === 14 ? x : z;
|
|
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
|
|
}
|
|
|
|
/**
|
|
* A utility class providing a 3D noise function.
|
|
*
|
|
* The code is based on [IMPROVED NOISE]{@link https://cs.nyu.edu/~perlin/noise/}
|
|
* by Ken Perlin, 2002.
|
|
*
|
|
* @three_import import { ImprovedNoise } from 'three/addons/math/ImprovedNoise.js';
|
|
*/
|
|
class ImprovedNoise {
|
|
/**
|
|
* Returns a noise value for the given parameters.
|
|
*
|
|
* @param {number} x - The x coordinate.
|
|
* @param {number} y - The y coordinate.
|
|
* @param {number} z - The z coordinate.
|
|
* @return {number} The noise value.
|
|
*/
|
|
noise(x, y, z) {
|
|
const floorX = Math.floor(x),
|
|
floorY = Math.floor(y),
|
|
floorZ = Math.floor(z);
|
|
|
|
const X = floorX & 255,
|
|
Y = floorY & 255,
|
|
Z = floorZ & 255;
|
|
|
|
x -= floorX;
|
|
y -= floorY;
|
|
z -= floorZ;
|
|
|
|
const xMinus1 = x - 1,
|
|
yMinus1 = y - 1,
|
|
zMinus1 = z - 1;
|
|
|
|
const u = fade(x),
|
|
v = fade(y),
|
|
w = fade(z);
|
|
|
|
const A = _p[X] + Y,
|
|
AA = _p[A] + Z,
|
|
AB = _p[A + 1] + Z,
|
|
B = _p[X + 1] + Y,
|
|
BA = _p[B] + Z,
|
|
BB = _p[B + 1] + Z;
|
|
|
|
return lerp(
|
|
lerp(
|
|
lerp(grad(_p[AA], x, y, z), grad(_p[BA], xMinus1, y, z), u),
|
|
lerp(grad(_p[AB], x, yMinus1, z), grad(_p[BB], xMinus1, yMinus1, z), u),
|
|
v,
|
|
),
|
|
lerp(
|
|
lerp(
|
|
grad(_p[AA + 1], x, y, zMinus1),
|
|
grad(_p[BA + 1], xMinus1, y, zMinus1),
|
|
u,
|
|
),
|
|
lerp(
|
|
grad(_p[AB + 1], x, yMinus1, zMinus1),
|
|
grad(_p[BB + 1], xMinus1, yMinus1, zMinus1),
|
|
u,
|
|
),
|
|
v,
|
|
),
|
|
w,
|
|
);
|
|
}
|
|
}
|
|
|
|
// End ImprovedNoise from Three.js
|
|
|
|
// GeometryPrimitive
|
|
const {
|
|
Cartesian3,
|
|
destroyObject,
|
|
DrawCommand,
|
|
VertexArray,
|
|
GeometryPipeline,
|
|
Matrix4,
|
|
} = Cesium;
|
|
|
|
/**
|
|
* Custom Primitive for Geometry
|
|
*/
|
|
class GeometryPrimitive {
|
|
/**
|
|
*
|
|
* @param {*} options
|
|
* @param {*} options.modelMatrix
|
|
* @param {*} options.vertexShaderSource
|
|
* @param {*} options.fragmentShaderSource
|
|
* @param {*} options.uniformMap
|
|
* @param {*} options.renderState
|
|
* @param {*} options.pass
|
|
*/
|
|
constructor(geometry, options) {
|
|
this.options = options;
|
|
this.geometry = geometry;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {*} frameState
|
|
*/
|
|
update(frameState) {
|
|
if (Cesium.defined(this._drawCommand)) {
|
|
frameState.commandList.push(this._drawCommand);
|
|
return;
|
|
}
|
|
if (this.geometry.constructor.createGeometry) {
|
|
this.geometry = this.geometry.constructor.createGeometry(this.geometry);
|
|
}
|
|
|
|
const context = frameState.context;
|
|
|
|
const attributeLocations = GeometryPipeline.createAttributeLocations(
|
|
this.geometry,
|
|
);
|
|
const vertexArray = VertexArray.fromGeometry({
|
|
context: context,
|
|
geometry: this.geometry,
|
|
attributeLocations,
|
|
});
|
|
|
|
// calculate boundingSphere
|
|
const boundingSphere = this.geometry.boundingSphere;
|
|
boundingSphere.center = Matrix4.multiplyByPoint(
|
|
this.options.modelMatrix,
|
|
boundingSphere.center,
|
|
new Cartesian3(),
|
|
);
|
|
boundingSphere.radius = 1000000;
|
|
|
|
this._boundingSphereWC = [boundingSphere];
|
|
|
|
const shaderProgram = Cesium.ShaderProgram.fromCache({
|
|
context: context,
|
|
attributeLocations,
|
|
vertexShaderSource: this.options.vertexShaderSource,
|
|
fragmentShaderSource: this.options.fragmentShaderSource,
|
|
});
|
|
|
|
this._drawCommand = new DrawCommand({
|
|
owner: this,
|
|
boundingVolume: boundingSphere,
|
|
primitiveType: this.geometry.primitiveType,
|
|
vertexArray: vertexArray,
|
|
shaderProgram,
|
|
...this.options,
|
|
});
|
|
}
|
|
|
|
destroy() {
|
|
return destroyObject(this);
|
|
}
|
|
|
|
isDestroyed() {
|
|
return false;
|
|
}
|
|
}
|
|
// GeometryPrimitive end
|
|
|
|
function makeTexture3D(context) {
|
|
const size = 100;
|
|
const dataLength = size * size * size;
|
|
const data = new Uint8Array(dataLength);
|
|
let i = 0;
|
|
const scale = 0.05;
|
|
const perlin = new ImprovedNoise();
|
|
let vector = new Cesium.Cartesian3();
|
|
const halfSize = Cesium.Cartesian3.fromElements(
|
|
size / 2,
|
|
size / 2,
|
|
size / 2,
|
|
new Cesium.Cartesian3(),
|
|
);
|
|
|
|
for (let z = 0; z < size; z++) {
|
|
for (let y = 0; y < size; y++) {
|
|
for (let x = 0; x < size; x++) {
|
|
vector = Cesium.Cartesian3.fromElements(x, y, z, vector);
|
|
vector = Cesium.Cartesian3.subtract(vector, halfSize, vector);
|
|
vector = Cesium.Cartesian3.divideByScalar(vector, size, vector);
|
|
|
|
const d = 1.0 - Cesium.Cartesian3.magnitude(vector);
|
|
const tv =
|
|
(128 +
|
|
128 * perlin.noise((x * scale) / 1.5, y * scale, (z * scale) / 1.5)) *
|
|
d *
|
|
d;
|
|
|
|
data[i] = tv;
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return new Cesium.Texture3D({
|
|
context: context,
|
|
width: size,
|
|
height: size,
|
|
depth: size,
|
|
flipY: false,
|
|
pixelFormat: Cesium.PixelFormat.RED,
|
|
pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE,
|
|
source: {
|
|
arrayBufferView: data,
|
|
width: size,
|
|
height: size,
|
|
depth: size,
|
|
},
|
|
sampler: new Cesium.Sampler({
|
|
minificationFilter: Cesium.TextureMinificationFilter.LINEAR,
|
|
magnificationFilter: Cesium.TextureMagnificationFilter.LINEAR,
|
|
}),
|
|
});
|
|
}
|
|
|
|
const vertexShader = /* glsl */ `
|
|
in vec3 position3DHigh;
|
|
in vec3 position3DLow;
|
|
in vec3 normal;
|
|
in vec2 st;
|
|
in float batchId;
|
|
|
|
out vec3 vOrigin;
|
|
out vec3 vDirection;
|
|
out vec3 vPosition;
|
|
|
|
vec4 translateRelativeToEye(vec3 high, vec3 low) {
|
|
vec3 highDifference = high - czm_encodedCameraPositionMCHigh;
|
|
if(length(highDifference) == 0.0f) {
|
|
highDifference = vec3(0);
|
|
}
|
|
vec3 lowDifference = low - czm_encodedCameraPositionMCLow;
|
|
return vec4(highDifference + lowDifference, 1.0f);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
vec4 p = translateRelativeToEye(position3DHigh, position3DLow);
|
|
|
|
vOrigin = czm_encodedCameraPositionMCHigh + czm_encodedCameraPositionMCLow;
|
|
|
|
vec3 modelPosition = position3DHigh + position3DLow;
|
|
vPosition = modelPosition;
|
|
|
|
vDirection = modelPosition - vOrigin;
|
|
|
|
gl_Position = czm_modelViewProjectionRelativeToEye * p;
|
|
}`;
|
|
|
|
const fragmentShader = /* glsl */ `
|
|
precision highp float;
|
|
precision highp sampler3D;
|
|
|
|
in vec3 vOrigin;
|
|
in vec3 vDirection;
|
|
|
|
// https://github.com/mrdoob/three.js/blob/dev/examples/webgl_volume_cloud.html
|
|
uniform vec3 base;
|
|
uniform sampler3D map;
|
|
uniform float threshold;
|
|
uniform float range;
|
|
uniform float opacity;
|
|
uniform float steps;
|
|
uniform float frame;
|
|
|
|
uint wang_hash(uint seed)
|
|
{
|
|
seed = (seed ^ 61u) ^ (seed >> 16u);
|
|
seed *= 9u;
|
|
seed = seed ^ (seed >> 4u);
|
|
seed *= 0x27d4eb2du;
|
|
seed = seed ^ (seed >> 15u);
|
|
return seed;
|
|
}
|
|
|
|
float randomFloat(inout uint seed)
|
|
{
|
|
return float(wang_hash(seed)) / 4294967296.;
|
|
}
|
|
|
|
vec2 hitBox( vec3 orig, vec3 dir ) {
|
|
const vec3 box_min = vec3( - 0.5 );
|
|
const vec3 box_max = vec3( 0.5 );
|
|
vec3 inv_dir = 1.0 / dir;
|
|
vec3 tmin_tmp = ( box_min - orig ) * inv_dir;
|
|
vec3 tmax_tmp = ( box_max - orig ) * inv_dir;
|
|
vec3 tmin = min( tmin_tmp, tmax_tmp );
|
|
vec3 tmax = max( tmin_tmp, tmax_tmp );
|
|
float t0 = max( tmin.x, max( tmin.y, tmin.z ) );
|
|
float t1 = min( tmax.x, min( tmax.y, tmax.z ) );
|
|
return vec2( t0, t1 );
|
|
}
|
|
|
|
float sample1( vec3 p ) {
|
|
return texture( map, p ).r;
|
|
}
|
|
|
|
float shading( vec3 coord ) {
|
|
float step = 0.01;
|
|
return sample1( coord + vec3( - step ) ) - sample1( coord + vec3( step ) );
|
|
}
|
|
|
|
vec4 linearToSRGB( in vec4 value ) {
|
|
return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );
|
|
}
|
|
|
|
void main() {
|
|
vec3 rayDir = normalize( vDirection );
|
|
vec2 bounds = hitBox( vOrigin, rayDir );
|
|
if ( bounds.x > bounds.y ) discard;
|
|
bounds.x = max( bounds.x, 0.0 );
|
|
vec3 p = vOrigin + bounds.x * rayDir;
|
|
vec3 inc = 1.0 / abs( rayDir );
|
|
float delta = min( inc.x, min( inc.y, inc.z ) );
|
|
delta /= steps;
|
|
|
|
// Nice little seed from
|
|
// https://blog.demofox.org/2020/05/25/casual-shadertoy-path-tracing-1-basic-camera-diffuse-emissive/
|
|
uint seed = uint( gl_FragCoord.x ) * uint( 1973 ) + uint( gl_FragCoord.y ) * uint( 9277 ) + uint( frame ) * uint( 26699 );
|
|
vec3 size = vec3( textureSize( map, 0 ) );
|
|
float randNum = randomFloat( seed ) * 2.0 - 1.0;
|
|
p += rayDir * randNum * ( 1.0 / size );
|
|
|
|
vec4 ac = vec4( base, 0.0 );
|
|
for ( float t = bounds.x; t < bounds.y; t += delta ) {
|
|
float d = sample1( p + 0.5 );
|
|
d = smoothstep( threshold - range, threshold + range, d ) * opacity;
|
|
float col = shading( p + 0.5 ) * 3.0 + ( ( p.x + p.y ) * 0.25 ) + 0.2;
|
|
ac.rgb += ( 1.0 - ac.a ) * d * col;
|
|
ac.a += ( 1.0 - ac.a ) * d;
|
|
if ( ac.a >= 0.95 ) break;
|
|
p += rayDir * delta;
|
|
}
|
|
vec4 color = linearToSRGB( ac );
|
|
color = czm_gammaCorrect( color );
|
|
if ( color.a == 0.0 ) discard;
|
|
out_FragColor = color;
|
|
}
|
|
`;
|
|
|
|
const viewer = new Cesium.Viewer("cesiumContainer", {
|
|
orderIndependentTranslucency: true,
|
|
});
|
|
const texture3D = makeTexture3D(viewer.scene.context);
|
|
texture3D.generateMipmap();
|
|
|
|
const boxSideLength = 1.0;
|
|
const zoomScale = 10000;
|
|
const centerPoint = Cesium.Cartesian3.fromDegrees(
|
|
113,
|
|
33,
|
|
(boxSideLength / 0.5) * zoomScale,
|
|
);
|
|
|
|
const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(centerPoint);
|
|
|
|
const boxSize = new Cesium.Cartesian3(
|
|
boxSideLength,
|
|
boxSideLength,
|
|
boxSideLength,
|
|
);
|
|
|
|
const renderState = Cesium.RenderState.fromCache({
|
|
depthMask: false,
|
|
blending: {
|
|
enabled: true,
|
|
color: {
|
|
red: 0.0,
|
|
green: 0.0,
|
|
blue: 0.0,
|
|
alpha: 0.0,
|
|
},
|
|
},
|
|
depthTest: {
|
|
enabled: true,
|
|
func: Cesium.DepthFunction.LESS_OR_EQUAL,
|
|
},
|
|
cull: {
|
|
enabled: true,
|
|
face: Cesium.CullFace.FRONT,
|
|
},
|
|
});
|
|
|
|
const zoomMat = Cesium.Matrix4.fromScale(
|
|
new Cesium.Cartesian3(zoomScale, zoomScale, zoomScale),
|
|
new Cesium.Matrix4(),
|
|
);
|
|
|
|
Cesium.Matrix4.multiply(modelMatrix, zoomMat, modelMatrix);
|
|
|
|
const halfBox = Cesium.Cartesian3.multiplyByScalar(
|
|
boxSize,
|
|
0.5,
|
|
new Cesium.Cartesian3(),
|
|
);
|
|
const negHalfBox = Cesium.Cartesian3.negate(halfBox, new Cesium.Cartesian3());
|
|
|
|
const boxGeometry = new Cesium.BoxGeometry({
|
|
minimum: negHalfBox,
|
|
maximum: halfBox,
|
|
});
|
|
|
|
const uniforms = {
|
|
base: new Cesium.Color(0.1912, 0.2542, 0.3515, 0),
|
|
map: texture3D,
|
|
opacity: 0.25,
|
|
range: 0.1,
|
|
steps: 100,
|
|
frame: 0,
|
|
threshold: 0.25,
|
|
};
|
|
|
|
window.uniforms = uniforms;
|
|
|
|
const cmdUniforms = {};
|
|
for (const key in uniforms) {
|
|
if (key) {
|
|
cmdUniforms[key] = function () {
|
|
return uniforms[key];
|
|
};
|
|
}
|
|
}
|
|
|
|
const primitive = new GeometryPrimitive(boxGeometry, {
|
|
uniformMap: cmdUniforms,
|
|
vertexShaderSource: vertexShader,
|
|
fragmentShaderSource: fragmentShader,
|
|
renderState: renderState,
|
|
modelMatrix,
|
|
pass: Cesium.Pass.TRANSLUCENT,
|
|
});
|
|
|
|
viewer.scene.primitives.add(primitive);
|
|
|
|
const cameraState = {
|
|
destination: centerPoint,
|
|
orientation: {
|
|
heading: 4.159717744111784,
|
|
pitch: -0.4648127266675117,
|
|
roll: boxSideLength * zoomScale * 2,
|
|
},
|
|
duration: 0,
|
|
};
|
|
// viewer.camera.flyTo(camState);
|
|
const hpr = new Cesium.HeadingPitchRange(
|
|
cameraState.orientation.heading,
|
|
cameraState.orientation.pitch,
|
|
boxSideLength * zoomScale * 2,
|
|
);
|
|
viewer.camera.lookAt(cameraState.destination, hpr);
|
|
|
|
// create params control UI
|
|
function createSlider(
|
|
toolbar,
|
|
{ labelText, min, max, step, defaultValue, callback },
|
|
) {
|
|
const container = document.createElement("div");
|
|
container.style.cssText = `
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
margin-bottom: 8px;
|
|
`;
|
|
|
|
const label = document.createElement("label");
|
|
label.textContent = labelText;
|
|
label.style.cssText = `
|
|
color: #ffffff;
|
|
font-family: Arial, sans-serif;
|
|
min-width: 60px;
|
|
font-size: 14px;
|
|
`;
|
|
|
|
const slider = document.createElement("input");
|
|
slider.type = "range";
|
|
slider.min = min;
|
|
slider.max = max;
|
|
slider.step = step;
|
|
slider.value = defaultValue;
|
|
slider.style.cssText = `
|
|
width: 100px;
|
|
cursor: pointer;
|
|
accent-color: #4CAF50;
|
|
`;
|
|
slider.addEventListener("input", (e) => callback(e.target.value));
|
|
|
|
const valueDisplay = document.createElement("span");
|
|
valueDisplay.textContent = defaultValue;
|
|
valueDisplay.style.cssText = `
|
|
color: #ffffff;
|
|
font-family: monospace;
|
|
width: 30px;
|
|
text-align: right;
|
|
`;
|
|
slider.addEventListener("input", (e) => {
|
|
valueDisplay.textContent = parseFloat(e.target.value).toFixed(2);
|
|
});
|
|
|
|
container.appendChild(label);
|
|
container.appendChild(slider);
|
|
container.appendChild(valueDisplay);
|
|
toolbar.appendChild(container);
|
|
|
|
return slider;
|
|
}
|
|
|
|
function createUi() {
|
|
const toolbar = document.getElementById("toolbar");
|
|
|
|
toolbar.style.cssText = `
|
|
position: fixed;
|
|
top: 10px;
|
|
left: 10px;
|
|
padding: 15px 20px 5px 20px;
|
|
background: rgba(40, 40, 40, 0.85);
|
|
border-radius: 12px;
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
|
backdrop-filter: blur(5px);
|
|
z-index: 1000;
|
|
`;
|
|
|
|
createSlider(toolbar, {
|
|
labelText: "opacity",
|
|
min: 0,
|
|
max: 1,
|
|
step: 0.01,
|
|
defaultValue: uniforms.opacity,
|
|
callback: (val) => {
|
|
uniforms.opacity = parseFloat(val);
|
|
},
|
|
});
|
|
|
|
createSlider(toolbar, {
|
|
labelText: "range",
|
|
min: 0,
|
|
max: 1,
|
|
step: 0.01,
|
|
defaultValue: uniforms.range,
|
|
callback: (val) => {
|
|
uniforms.range = parseFloat(val);
|
|
},
|
|
});
|
|
createSlider(toolbar, {
|
|
labelText: "steps",
|
|
min: 20,
|
|
max: 200,
|
|
step: 1,
|
|
defaultValue: uniforms.steps,
|
|
callback: (val) => {
|
|
uniforms.steps = parseFloat(val);
|
|
},
|
|
});
|
|
createSlider(toolbar, {
|
|
labelText: "threshold",
|
|
min: 0,
|
|
max: 1,
|
|
step: 0.01,
|
|
defaultValue: uniforms.threshold,
|
|
callback: (val) => {
|
|
uniforms.threshold = parseFloat(val);
|
|
},
|
|
});
|
|
}
|
|
|
|
createUi();
|
|
|
|
//Sandcastle_End
|
|
};
|
|
if (typeof Cesium !== "undefined") {
|
|
window.startupCalled = true;
|
|
window.startup(Cesium).catch((error) => {
|
|
"use strict";
|
|
console.error(error);
|
|
});
|
|
Sandcastle.finishedLoading();
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|