Hello everyone,last time I posted a topic about how to obtain high-resolution screenshots,the topic link is: How to implement front-end screenshots? Team suggested that I can use resolutionScale to solve the resolution issue.Indeed,I solved this problem after trying it out in the program.but now a new problem has arisen,Images can indeed achieve high resolution through this method, but the level of model is not the highest.In other words, if the model level loaded in the program is low in the current perspective (such as when the camera is relatively high), even if the image resolution can reach 6500 * 4000, the effect of the model is very blurry when the image is enlarged.
So, my question is, how to make the model load the highest level when taking screenshots, At least enable the visible part of the model to load at the highest level?
One possible steering factor here could be the maximum screen space error. You can pass this in as one option when creating a tileset:
const tileset = viewer.scene.primitives.add(
await Cesium.Cesium3DTileset.fromUrl(
"http://localhost:8003/tileset.json", {
maximumScreenSpaceError: 8
})
);
The default value is 16, so using a value of 8 will cause the refinement to happen more quickly. You can also change this value with the 3D Tiles Inspector, to see which value fits best for your case.
Thank you for your answer, I will try it soon and then give you a reply.
By use maximumScreenSpaceError, I solved this problem. I have a new question, If I set the resolutionScale value to 4 on a screen of 1980 * 1080, and the resulting image resolution is 7680 * 4320, I find that if I continue to increase the value of resolutionScale, such as 4.5 or 5, the resulting image is a pure black image. What is the reason for this?
There certainly are limits for the ~“maximum size of images”. For example, earlier versions of Chrome had a limit of 8192x8129 pixels for certain images. When you mention that it still works for 7680 * 4320, but does not work for larger images, then this (8192) might very well be one limit that you’re hitting.
Where does this limit come from? Well, that probably depends on many factors - e.g. the Browser, the exact code, maybe even the operating system or graphics card. You might want to have a look at https://webglreport.com/ - maybe something stands out? For example (a wild guess): What is the “Framebuffer” → “Max Render Buffer Size” for you?
Thank you very much for answer again. I have tried this program in both Edge and Chrome, and the results are the same. I have also tried different computers (3090ti, 3080ti, and even 960ti), and each different combination cannot break this limit. I suspect that “Viewer.sence.canvas” did not correctly obtain the canvas element. Does Cesium have such a limit? If not, where should I look for a breakthrough next?
I do not know whether there are specific limits that are inherent to CesiumJS itself. There might be such limits (even though it’s unlikely that there is some hard-coded limit like if (size > 8192) renderBlackImage();
…). I’d guess that it’s almost certainly a limitation of the browser, operating system, or graphics card. Without code or further details about the system and its (Web)GL capabilities, it’s hard to say how this could be resolved. Maybe someone else has an idea here…
OK, Thank you very much. So now I assume that this limitation cannot be resolved, I will try to segment the image and improve the resolution by concatenating screenshots of different parts. Thank you.
Are there any error messages in the console when you try to create the image that is “too large”?
What does https://webglreport.com/ say for the maximum frame buffer size?
There is no error message in the console, everything is normal, except that the image is all black when opened. webglreport.com show that Max Render Buffer Size is 16384 …Yesterday I verified that the maximum resolution supported by 3090ti is 8K, 7680 * 4320. Could the problem be here? The resolution of the image has exceeded the maximum resolution supported by the graphics card. But if the problem lies here, why does running the code “resolutionScale = 5” separately not result in an error, and it can improve the resolution in the browser. This looks very unreasonable.
As I said: There are many possible reasons for this limitation, and the limit for the graphics card that you mentioned might be one of them. I’d have to check with the CesiumJS core team and read more code to get a clearer idea how the screenshot generation works internally, and where this limit comes from. You’ll probably have to find a workaround, unless you think that this is an issue in CesiumJS itself.
Yes, you’re right. So now I want to achieve such flexibility. I want to try dividing the entire image into four parts: top left, top right, bottom left, and bottom right. Taking separate screenshots of each part and finally stitching the four parts together in some way can increase the resolution to 15000. Is there any convenient way for me to quickly change my visible area to a certain part?
It could be difficult to create 4 separate screenshots and stitch them together, because you’d have to make sure that the cameras are fitting pixel-perfectly. But maybe there is a tricky solution for that. For some details or possible ideas, someone from the CesiumJS core team might have to chime in here.
Yes, when considering camera movement, the entire problem becomes very complex. You mentioned the Cesium core team, could you please help me ask them for help? This is very important for my project.
Maybe @Gabby_Getz has an idea or can ping someone about …
- what could be limiting factors for the screenshot size
- how multiple screenshots could be created and stitched together to form a larger screenshot
@AMily Would you be able to provide a minimal example of the code you’re using to generate the screenshots?
Currently there’s nothing fancy about our recommended method of screenshot generation. We capture the state of the canvas and save it to an image, so I’d be surprised if there is an additional limitation there.
Sure, here is my key method:
new Promise((resolve, reject) =>{
viewer.resolutionScale = 5
tileset.maximumScreenSpaceError = 1
setTimeout(()=>{
var canvas = viewer.scene.canvas;
resolve(canvas);
},8000);
}).then((canvasCes)=>{
viewer.resolutionScale = 1
tileset.maximumScreenSpaceError = 2
var tempSrc = canvasCes.toDataURL("image/jpeg",1);
var a = document.createElement("a");
a.href = tempSrc;
a.download = "Image"
document.body.appendChild(a);
a.click();
this.chartLoading = false
});
I use “setTimeout” to ensure that the model has been loaded before obtaining Canvas, and “. then” to ensure that the steps of converting images are executed after obtaining Canvas.
This code runs on my platform (Edge browser, 3080ti, i7) and produces a pure black image. Due to the setting of “viewer. resolutionScale=5”, the image will have a resolution of 9600 * 5400. In fact, even if you set “viewer. resolutionScale=4.5” and generate an 8640 * 4860 image, it is still pure black…
I did play around with that a bit. I had the impression that toBlob
worked for larger sizes than toDataURL
, and that larger sizes are possible in FireFox than in Chrome (in FireFox, I created 10000 pixel wide images…). But no conclusion about the reason yet.
The following is a sandcastle that contains three “Capture” buttons, with scaling factors 1.0, 3.0, and 8.0 (and it seems to consistently fail with 8.0, because toBlob
returns “empty” data), just for the case that someone wants to try it out:
Thank you for your help, but I don’t think this is just a problem with toDataURL or toBolb… In fact, I have tried using html2canvas.js to obtain the Canvas when setting “viewer. resolutionScale=4”. This is difficult to describe, at that time, I used a special operation to double the width of the Canvas, let it achieve a width of 15000+(perhaps this description is more appropriate: this is not achieved through rendering, but by dividing a pixel into four, so the actual image quality has not improved, only size become lager). In this case, toDataURL can output an image with a resolution of “15000 * 8400”.
But this is still very helpful. I will try to find out the difference between toDataUrl and toBolb. Thank you.
Some details may be too specific for me to analyze them in all depth (and as I said: there are many “dimensions” along which this problem might have to be investigated).
My gut feeling was that it might be related to the fact that this (large) canvas has a GL context in the background (maybe you saw some debug output like const w = viewer.scene.context._gl.drawingBufferWidth;
in the sandcastle). But just to rule out that it is a fundamental problem with that, I just created this example: It is a standalone HTML file with a script that draws some random WebGL stuff, and has a ‘save’ link at the bottom.
The size of the canvas
can be changed, e.g. from 500x500 to 10000x10000, and it still works. So there might indeed be something unexpected be going on in CesiumJS…
<html>
<body>
<canvas width=500 height=500 id="c"></canvas><br />
<script>
'use strict';
const canvas = document.querySelector('#c')
const width = canvas.width;
const height = canvas.height;
function draw2D(){
const c = canvas.getContext('2d');
c.fillStyle = "red";
c.beginPath();
c.arc( 150, 150, 50, 0, Math.PI*2, true );
c.closePath();
c.fill();
}
function drawRect(gl, x, y, width, height, color) {
gl.scissor(x, y, width, height);
gl.clearColor(...color);
gl.clear(gl.COLOR_BUFFER_BIT);
}
function rand(min, max) {
if (max === undefined) {
max = min;
min = 0;
}
return Math.random() * (max - min) + min;
}
function drawGL() {
const gl = canvas.getContext('webgl', {
preserveDrawingBuffer: true
});
gl.viewport(0,0,width,height);
gl.enable(gl.SCISSOR_TEST);
for (let i = 0; i < 100; ++i) {
const x = rand(0, width);
const y = rand(0, height);
const w = rand(0, width - x);
const h = rand(0, width - y);
drawRect(gl, x, y, w, h, [rand(1), rand(1), rand(1), 1]);
}
}
drawGL();
//draw2D();
function save() {
canvas.toBlob(function(blob) {
const a = document.createElement('a');
a.download = "Image.jpg";
a.href = URL.createObjectURL(blob);
a.click();
a.remove();
URL.revokeObjectURL(blob);
}, "image/jpeg", 0.95);
}
const link = document.createElement('a');
link.innerHTML = 'save';
link.addEventListener('click', save, false);
document.body.appendChild(link);
</script>
</body>
</html>