Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CreateScreenshotUsingRenderTarget: Render to texture only when texture and camera are ready #14463

Merged
merged 2 commits into from Oct 25, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
70 changes: 46 additions & 24 deletions packages/dev/core/src/Misc/screenshotTools.ts
Expand Up @@ -203,6 +203,7 @@ export function CreateScreenshotWithResizeAsync(engine: Engine, camera: Camera,
* @param enableStencilBuffer Whether the stencil buffer should be enabled or not (default: false)
* @param useLayerMask if the camera's layer mask should be used to filter what should be rendered (default: true)
* @param quality The quality of the image if lossy mimeType is used (e.g. image/jpeg, image/webp). See {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob | HTMLCanvasElement.toBlob()}'s `quality` parameter.
* @param customizeTexture An optional callback that can be used to modify the render target texture before taking the screenshot. This can be used, for instance, to enable camera post-processes before taking the screenshot.
*/
export function CreateScreenshotUsingRenderTarget(
engine: Engine,
Expand All @@ -216,7 +217,8 @@ export function CreateScreenshotUsingRenderTarget(
renderSprites = false,
enableStencilBuffer = false,
useLayerMask = true,
quality?: number
quality?: number,
customizeTexture?: (texture: RenderTargetTexture) => void
): void {
const { height, width, finalWidth, finalHeight } = _GetScreenshotSize(engine, camera, size);
const targetTextureSize = { width, height };
Expand Down Expand Up @@ -253,36 +255,56 @@ export function CreateScreenshotUsingRenderTarget(
texture.renderSprites = renderSprites;
texture.activeCamera = camera;
texture.forceLayerMaskCheck = useLayerMask;

const renderToTexture = () => {
engine.onEndFrameObservable.addOnce(() => {
if (finalWidth === width && finalHeight === height) {
texture.readPixels(undefined, undefined, undefined, false)!.then((data) => {
DumpTools.DumpData(width, height, data, successCallback as (data: string | ArrayBuffer) => void, mimeType, fileName, true, undefined, quality);
texture.dispose();
});
} else {
ApplyPostProcess("pass", texture.getInternalTexture()!, scene, undefined, undefined, undefined, finalWidth, finalHeight).then((texture) => {
engine._readTexturePixels(texture, finalWidth, finalHeight, -1, 0, null, true, false, 0, 0).then((data) => {
DumpTools.DumpData(finalWidth, finalHeight, data, successCallback as (data: string | ArrayBuffer) => void, mimeType, fileName, true, undefined, quality);
customizeTexture?.(texture);

const renderWhenReady = () => {
if (texture.isReadyForRendering() && camera.isReady(true)) {
engine.onEndFrameObservable.addOnce(() => {
if (finalWidth === width && finalHeight === height) {
texture.readPixels(undefined, undefined, undefined, false)!.then((data) => {
DumpTools.DumpData(width, height, data, successCallback as (data: string | ArrayBuffer) => void, mimeType, fileName, true, undefined, quality);
texture.dispose();
});
});
}
});
} else {
ApplyPostProcess("pass", texture.getInternalTexture()!, scene, undefined, undefined, undefined, finalWidth, finalHeight).then((texture) => {
engine._readTexturePixels(texture, finalWidth, finalHeight, -1, 0, null, true, false, 0, 0).then((data) => {
DumpTools.DumpData(
finalWidth,
finalHeight,
data,
successCallback as (data: string | ArrayBuffer) => void,
mimeType,
fileName,
true,
undefined,
quality
);
texture.dispose();
});
});
}
});

texture.render(true);

// re-render the scene after the camera has been reset to the original camera to avoid a flicker that could occur
// if the camera used for the RTT rendering stays in effect for the next frame (and if that camera was different from the original camera)
scene.incrementRenderId();
scene.resetCachedMaterial();
engine.setSize(originalSize.width, originalSize.height);
camera.getProjectionMatrix(true); // Force cache refresh;
scene.render();
} else {
setTimeout(renderWhenReady, 16);
sebavan marked this conversation as resolved.
Show resolved Hide resolved
}
};

const renderToTexture = () => {
// render the RTT
scene.incrementRenderId();
scene.resetCachedMaterial();
texture.render(true);

// re-render the scene after the camera has been reset to the original camera to avoid a flicker that could occur
// if the camera used for the RTT rendering stays in effect for the next frame (and if that camera was different from the original camera)
scene.incrementRenderId();
scene.resetCachedMaterial();
engine.setSize(originalSize.width, originalSize.height);
camera.getProjectionMatrix(true); // Force cache refresh;
scene.render();
renderWhenReady();
};

if (antialiasing) {
Expand Down