497 lines
18 KiB
HTML
497 lines
18 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="Create 3D models using glTF." />
|
|
<meta name="cesium-sandcastle-labels" content="3D Tiles" />
|
|
<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);
|
|
#toolbar {
|
|
background: rgba(42, 42, 42, 0.8);
|
|
padding: 4px;
|
|
border-radius: 4px;
|
|
}
|
|
#toolbar input {
|
|
vertical-align: middle;
|
|
padding-top: 2px;
|
|
padding-bottom: 2px;
|
|
}
|
|
</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
|
|
const viewer = new Cesium.Viewer("cesiumContainer", {
|
|
orderIndependentTranslucency: false,
|
|
});
|
|
|
|
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601(
|
|
"2021-11-09T20:27:37.016064475348684937Z"
|
|
);
|
|
|
|
// Model positioning ===============================================
|
|
|
|
const position = Cesium.Cartesian3.fromDegrees(
|
|
-123.0744619,
|
|
44.0503706,
|
|
0
|
|
);
|
|
const hpr = new Cesium.HeadingPitchRoll(0, 0, 0);
|
|
const fixedFrameTransform = Cesium.Transforms.localFrameToFixedFrameGenerator(
|
|
"north",
|
|
"west"
|
|
);
|
|
|
|
// Custom Shader Definitions ========================================
|
|
|
|
// Dragging the mouse will expand/shrink the model.
|
|
const expandModelShader = new Cesium.CustomShader({
|
|
uniforms: {
|
|
// Vector from latest drag center to the mouse
|
|
u_drag: {
|
|
type: Cesium.UniformType.VEC2,
|
|
value: new Cesium.Cartesian2(0.0, 0.0),
|
|
},
|
|
},
|
|
vertexShaderText: `
|
|
// If the mouse is dragged to the right, the model grows
|
|
// If the mouse is dragged to the left, the model shrinks
|
|
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput)
|
|
{
|
|
vsOutput.positionMC += 0.01 * u_drag.x * vsInput.attributes.normalMC;
|
|
}
|
|
`,
|
|
});
|
|
|
|
const textureUniformShader = new Cesium.CustomShader({
|
|
uniforms: {
|
|
// elapsed time in seconds for animation
|
|
u_time: {
|
|
type: Cesium.UniformType.FLOAT,
|
|
value: 0,
|
|
},
|
|
// user-defined texture
|
|
u_stripes: {
|
|
type: Cesium.UniformType.SAMPLER_2D,
|
|
value: new Cesium.TextureUniform({
|
|
url: "../../SampleData/cesium_stripes.png",
|
|
}),
|
|
},
|
|
},
|
|
// Apply the texture to the model, but move the texture coordinates
|
|
// a bit over time so it's animated.
|
|
fragmentShaderText: `
|
|
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
|
|
{
|
|
vec2 texCoord = fsInput.attributes.texCoord_0 + 0.1 * vec2(u_time, 0.0);
|
|
material.diffuse = texture(u_stripes, texCoord).rgb;
|
|
}
|
|
`,
|
|
});
|
|
|
|
// make a checkerboard texture with an alpha that increases with the
|
|
// diagonal number
|
|
function makeCheckerboardTexture(textureSize) {
|
|
const checkerboard = new Uint8Array(4 * textureSize * textureSize);
|
|
|
|
const maxDiagonal = 2 * (textureSize - 1);
|
|
for (let i = 0; i < textureSize; i++) {
|
|
for (let j = 0; j < textureSize; j++) {
|
|
const index = i * textureSize + j;
|
|
// Checking the parity of the diagonal number gives a checkerboard
|
|
// pattern.
|
|
const diagonal = i + j;
|
|
if (diagonal % 2 === 0) {
|
|
// set the square red. We only need to set the red channel!
|
|
checkerboard[4 * index] = 255;
|
|
}
|
|
// otherwise we'd set the square to black. But arrays are already
|
|
// initialized to 0s so nothing needed here.
|
|
|
|
// for the alpha channel, map the diagonal number to [0, 255]
|
|
checkerboard[4 * index + 3] = (255 * diagonal) / maxDiagonal;
|
|
}
|
|
}
|
|
return new Cesium.TextureUniform({
|
|
typedArray: checkerboard,
|
|
width: textureSize,
|
|
height: textureSize,
|
|
// Don't interpolate, we want crisp checkerboard edges
|
|
minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
|
|
magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
|
|
});
|
|
}
|
|
const checkerboardTexture = makeCheckerboardTexture(8);
|
|
|
|
// Use the checkerboard red channel as a mask
|
|
const checkerboardMaskShader = new Cesium.CustomShader({
|
|
uniforms: {
|
|
u_checkerboard: {
|
|
type: Cesium.UniformType.SAMPLER_2D,
|
|
value: checkerboardTexture,
|
|
},
|
|
},
|
|
fragmentShaderText: `
|
|
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
|
|
{
|
|
vec2 texCoord = fsInput.attributes.texCoord_0;
|
|
vec4 checkerboard = texture(u_checkerboard, texCoord);
|
|
material.diffuse = mix(material.diffuse, vec3(0.0), checkerboard.r);
|
|
}
|
|
`,
|
|
});
|
|
|
|
// Color like a checkerboard but make the transparency vary with
|
|
// the diagonal
|
|
const checkerboardAlphaShader = new Cesium.CustomShader({
|
|
uniforms: {
|
|
u_checkerboard: {
|
|
type: Cesium.UniformType.SAMPLER_2D,
|
|
value: checkerboardTexture,
|
|
},
|
|
},
|
|
// This model normally renders opaque so material.alpha would be ignored.
|
|
// this setting forces the shader to render in the translucent pass.
|
|
translucencyMode: Cesium.CustomShaderTranslucencyMode.TRANSLUCENT,
|
|
fragmentShaderText: `
|
|
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
|
|
{
|
|
vec2 texCoord = fsInput.attributes.texCoord_0;
|
|
vec4 checkerboard = texture(u_checkerboard, texCoord);
|
|
material.diffuse = checkerboard.rgb;
|
|
material.alpha = checkerboard.a;
|
|
}
|
|
`,
|
|
});
|
|
|
|
// Use the checkerboard to cut holes in the model
|
|
const checkerboardHolesShader = new Cesium.CustomShader({
|
|
uniforms: {
|
|
u_checkerboard: {
|
|
type: Cesium.UniformType.SAMPLER_2D,
|
|
value: checkerboardTexture,
|
|
},
|
|
},
|
|
fragmentShaderText: `
|
|
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
|
|
{
|
|
vec2 texCoord = fsInput.attributes.texCoord_0;
|
|
vec4 checkerboard = texture(u_checkerboard, texCoord);
|
|
if (checkerboard.r > 0.0) {
|
|
discard;
|
|
}
|
|
}
|
|
`,
|
|
});
|
|
|
|
// This example is to demonstrate the conventions used for orienting
|
|
// the texture. +x is to the right and +y is from **bottom to top**.
|
|
// This is to be consistent with WebGL conventions.
|
|
//
|
|
// This example also demonstrates how to use a different pixel format,
|
|
// in this case, RGB.
|
|
function makeGradientTexture() {
|
|
const size = 256;
|
|
const typedArray = new Uint8Array(3 * size * size);
|
|
for (let i = 0; i < size; i++) {
|
|
for (let j = 0; j < size; j++) {
|
|
const index = i * size + j;
|
|
// red increases in the +x direction (to the right)
|
|
typedArray[3 * index + 0] = j;
|
|
// Green increases in the +y direction (from bottom to top)
|
|
typedArray[3 * index + 1] = i;
|
|
// blue is 0 so it is omitted.
|
|
}
|
|
}
|
|
|
|
return new Cesium.TextureUniform({
|
|
typedArray: typedArray,
|
|
width: size,
|
|
height: size,
|
|
pixelFormat: Cesium.PixelFormat.RGB,
|
|
});
|
|
}
|
|
const gradientTexture = makeGradientTexture();
|
|
|
|
// Color the texture along its UV coordinates.
|
|
const gradientShader = new Cesium.CustomShader({
|
|
uniforms: {
|
|
u_gradient: {
|
|
type: Cesium.UniformType.SAMPLER_2D,
|
|
value: gradientTexture,
|
|
},
|
|
},
|
|
fragmentShaderText: `
|
|
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
|
|
{
|
|
material.diffuse = texture(u_gradient, fsInput.attributes.texCoord_0).rgb;
|
|
}
|
|
`,
|
|
});
|
|
|
|
// Dragging the mouse will modify the PBR values
|
|
const modifyPbrShader = new Cesium.CustomShader({
|
|
uniforms: {
|
|
// Vector from latest drag center to the mouse
|
|
u_drag: {
|
|
type: Cesium.UniformType.VEC2,
|
|
value: new Cesium.Cartesian2(0.0, 0.0),
|
|
},
|
|
},
|
|
fragmentShaderText: `
|
|
// Click and drag to vary the PBR values
|
|
void fragmentMain(FragmentInput vsInput, inout czm_modelMaterial material)
|
|
{
|
|
float dragDistance = length(u_drag);
|
|
float variation = smoothstep(0.0, 300.0, dragDistance);
|
|
// variation adds an golden tint to the specular highlights
|
|
material.specular = mix(material.specular, vec3(0.8, 0.5, 0.1), variation);
|
|
// variation makes the material glossier
|
|
material.roughness = clamp(1.0 - variation, 0.01, 1.0);
|
|
// variation mixes some red into the diffuse color
|
|
material.diffuse += vec3(0.5, 0.0, 0.0) * variation;
|
|
}
|
|
`,
|
|
});
|
|
|
|
const pointCloudWaveShader = new Cesium.CustomShader({
|
|
uniforms: {
|
|
// elapsed time in seconds for animation
|
|
u_time: {
|
|
type: Cesium.UniformType.FLOAT,
|
|
value: 0,
|
|
},
|
|
},
|
|
vertexShaderText: `
|
|
void vertexMain(VertexInput vsInput, inout czm_modelVertexOutput vsOutput)
|
|
{
|
|
// This model's x and y coordinates are in the range [0, 1], which
|
|
// conveniently doubles as UV coordinates.
|
|
vec2 uv = vsInput.attributes.positionMC.xy;
|
|
// Make the point cloud undulate in a complex wave that varies in
|
|
// both space and time. The amplitude is based on the original shape
|
|
// of the point cloud (which already is a wavy surface). The wave
|
|
// is computed relative to the center of the model, hence the
|
|
// transformations from [0, 1] -> [-1, 1] -> [0, 1]
|
|
float amplitude = 2.0 * vsInput.attributes.positionMC.z - 1.0;
|
|
float wave = amplitude * sin(2.0 * czm_pi * uv.x - 2.0 * u_time) * sin(u_time);
|
|
vsOutput.positionMC.z = 0.5 + 0.5 * wave;
|
|
// Make the points pulse in and out by changing their size
|
|
vsOutput.pointSize = 10.0 + 5.0 * sin(u_time);
|
|
}
|
|
`,
|
|
fragmentShaderText: `
|
|
void fragmentMain(FragmentInput fsInput, inout czm_modelMaterial material)
|
|
{
|
|
// Make the points circular instead of square
|
|
float distance = length(gl_PointCoord - 0.5);
|
|
if (distance > 0.5) {
|
|
discard;
|
|
}
|
|
// Make a sinusoid color palette that moves in the general direction
|
|
// of the wave, but at a different speed.
|
|
// Coefficients were chosen arbitrarily
|
|
vec2 uv = fsInput.attributes.positionMC.xy;
|
|
material.diffuse = 0.2 * fsInput.attributes.color_0.rgb;
|
|
material.diffuse += vec3(0.2, 0.3, 0.4) + vec3(0.2, 0.3, 0.4) * sin(2.0 * czm_pi * vec3(3.0, 2.0, 1.0) * uv.x - 3.0 * u_time);
|
|
}
|
|
`,
|
|
});
|
|
|
|
// Demos ==============================================================
|
|
|
|
const models = {
|
|
balloon: "../../SampleData/models/CesiumBalloon/CesiumBalloon.glb",
|
|
drone: "../../SampleData/models/CesiumDrone/CesiumDrone.glb",
|
|
pawns: "../../SampleData/models/CesiumDrone/Pawns.glb",
|
|
milkTruck:
|
|
"../../SampleData/models/CesiumMilkTruck/CesiumMilkTruck.glb",
|
|
groundVehicle:
|
|
"../../SampleData/models/GroundVehicle/GroundVehicle.glb",
|
|
pointCloudWave:
|
|
"../../SampleData/models/PointCloudWave/PointCloudWave.glb",
|
|
};
|
|
|
|
let needsDrag = false;
|
|
const demos = [
|
|
{
|
|
text: "Custom Texture",
|
|
onselect: function () {
|
|
selectModel(models.groundVehicle, textureUniformShader);
|
|
needsDrag = false;
|
|
},
|
|
},
|
|
{
|
|
text: "Procedural Texture",
|
|
onselect: function () {
|
|
selectModel(models.balloon, checkerboardMaskShader);
|
|
needsDrag = false;
|
|
},
|
|
},
|
|
{
|
|
text: "Translucent materials",
|
|
onselect: function () {
|
|
selectModel(models.balloon, checkerboardAlphaShader);
|
|
needsDrag = false;
|
|
},
|
|
},
|
|
{
|
|
text: "Use Texture as Mask",
|
|
onselect: function () {
|
|
selectModel(models.balloon, checkerboardHolesShader);
|
|
needsDrag = false;
|
|
},
|
|
},
|
|
{
|
|
text: "Procedural Gradient Texture",
|
|
onselect: function () {
|
|
selectModel(models.balloon, gradientShader);
|
|
needsDrag = false;
|
|
},
|
|
},
|
|
{
|
|
text: "Modify PBR values via Mouse Drag",
|
|
onselect: function () {
|
|
selectModel(models.groundVehicle, modifyPbrShader);
|
|
needsDrag = true;
|
|
},
|
|
},
|
|
{
|
|
text: "Expand Model via Mouse Drag",
|
|
onselect: function () {
|
|
selectModel(models.milkTruck, expandModelShader);
|
|
needsDrag = true;
|
|
},
|
|
},
|
|
{
|
|
text: "Animated Point Cloud",
|
|
onselect: function () {
|
|
selectModel(models.pointCloudWave, pointCloudWaveShader);
|
|
needsDrag = false;
|
|
},
|
|
},
|
|
];
|
|
|
|
async function selectModel(url, customShader) {
|
|
viewer.scene.primitives.removeAll();
|
|
try {
|
|
const model = viewer.scene.primitives.add(
|
|
await Cesium.Model.fromGltfAsync({
|
|
url: url,
|
|
customShader: customShader,
|
|
modelMatrix: Cesium.Transforms.headingPitchRollToFixedFrame(
|
|
position,
|
|
hpr,
|
|
Cesium.Ellipsoid.WGS84,
|
|
fixedFrameTransform
|
|
),
|
|
})
|
|
);
|
|
|
|
const removeListener = model.readyEvent.addEventListener(() => {
|
|
viewer.camera.flyToBoundingSphere(model.boundingSphere, {
|
|
duration: 0.0,
|
|
});
|
|
|
|
removeListener();
|
|
});
|
|
} catch (error) {
|
|
console.log(`Error loading model: ${error}`);
|
|
}
|
|
}
|
|
Sandcastle.addToolbarMenu(demos);
|
|
|
|
// Event handlers =====================================================
|
|
|
|
const startTime = performance.now();
|
|
viewer.scene.postUpdate.addEventListener(function () {
|
|
const elapsedTimeSeconds = (performance.now() - startTime) / 1000;
|
|
textureUniformShader.setUniform("u_time", elapsedTimeSeconds);
|
|
pointCloudWaveShader.setUniform("u_time", elapsedTimeSeconds);
|
|
});
|
|
|
|
let dragActive = false;
|
|
const dragCenter = new Cesium.Cartesian2();
|
|
|
|
viewer.screenSpaceEventHandler.setInputAction(function (movement) {
|
|
if (!needsDrag) {
|
|
return;
|
|
}
|
|
|
|
const pickedFeature = viewer.scene.pick(movement.position);
|
|
if (!Cesium.defined(pickedFeature)) {
|
|
return;
|
|
}
|
|
|
|
viewer.scene.screenSpaceCameraController.enableInputs = false;
|
|
|
|
// set the new drag center
|
|
dragActive = true;
|
|
movement.position.clone(dragCenter);
|
|
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
|
|
|
|
const scratchDrag = new Cesium.Cartesian2();
|
|
viewer.screenSpaceEventHandler.setInputAction(function (movement) {
|
|
if (!needsDrag) {
|
|
return;
|
|
}
|
|
|
|
if (dragActive) {
|
|
// get the mouse position relative to the center of the screen
|
|
const drag = Cesium.Cartesian3.subtract(
|
|
movement.endPosition,
|
|
dragCenter,
|
|
scratchDrag
|
|
);
|
|
|
|
// Update uniforms
|
|
expandModelShader.setUniform("u_drag", drag);
|
|
modifyPbrShader.setUniform("u_drag", drag);
|
|
}
|
|
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
|
|
|
viewer.screenSpaceEventHandler.setInputAction(function (movement) {
|
|
if (!needsDrag) {
|
|
return;
|
|
}
|
|
|
|
viewer.scene.screenSpaceCameraController.enableInputs = true;
|
|
|
|
dragActive = false;
|
|
}, Cesium.ScreenSpaceEventType.LEFT_UP);
|
|
|
|
//Sandcastle_End
|
|
};
|
|
if (typeof Cesium !== "undefined") {
|
|
window.startupCalled = true;
|
|
window.startup(Cesium).catch((error) => {
|
|
"use strict";
|
|
console.error(error);
|
|
});
|
|
Sandcastle.finishedLoading();
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|