提问人:wandugu 提问时间:10/15/2023 最后编辑:LJᛃwandugu 更新时间:10/16/2023 访问量:41
使用 Primitive 而不是 CustomShader 来达到所需的效果
Using Primitive instead of CustomShader to achieve the desired effect
问:
该效果是官方铯论坛的一个例子。我希望在原始示例中使用 Cesium.Primitive 类而不是 Cesium.CustomShader 类来达到相同的效果。可能吗?https://sandcastle.cesium.com/?src=Custom%20Shaders%20Models.html&label=All
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);
Primitive 类需要两个最重要的属性:geometry 和 Appearance。由于 Appearance 类不附带制服,因此最终目标是使用可以传递统一变量的类。在Cesium论坛上看到官方示例后,我想问一下是否可以使用Cesium.CustomShader而不是Cesium.Primitive。
答: 暂无答案
评论