Three.js:如何将场景的 2D 快照制作为 JPG 图像?

Posted

技术标签:

【中文标题】Three.js:如何将场景的 2D 快照制作为 JPG 图像?【英文标题】:Three.js: How can I make a 2D SnapShot of a Scene as a JPG Image? 【发布时间】:2014-11-29 09:11:28 【问题描述】:

我有一个像下面这样的 three.js 场景:

            var scene = new THREE.Scene();
            var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);

            var renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            var geometry = new THREE.BoxGeometry(1,1,1);
            var material = new THREE.MeshBasicMaterial(color: 0x00ff00);
            var cube = new THREE.Mesh(geometry, material);
            scene.add(cube);

            camera.position.z = 5;

            var render = function () 
                requestAnimationFrame(render);

                cube.rotation.x += 0.1;
                cube.rotation.y += 0.1;

                renderer.render(scene, camera);
            ;

            render();

是否可以从场景中制作 2D 快照或屏幕截图并将其导出为 JPG 图像?

【问题讨论】:

一些资源可以帮助您:***.com/questions/16431318/… 和 ***.com/questions/15558418/… 初始化 webgl 上下文,将 preserveDrawingBuffer 标志设置为 true 并使用 yourCanvas.toDataURL() @LJ_1102 你能发个例子吗? 骗子包括***.com/questions/34847293/…、***.com/questions/15558418/…、***.com/questions/26431862/… 【参考方案1】:

您需要做几件事,将框架保存为 jpg 图像。

首先像这样初始化webgl上下文

 renderer = new THREE.WebGLRenderer(
                    preserveDrawingBuffer: true
                );

preserveDrawingBuffer 标志会帮你获取当前帧的base64编码 代码是这样的

var strMime = "image/jpeg";
imgData = renderer.domElement.toDataURL(strMime);

其次,您可能希望使用.jpg 扩展名保存文件,但并非所有浏览器都允许您指定文件名。 我找到的最佳解决方案是this SO thread。

所以我们的脚本将检查浏览器是否允许它会创建一个新的anchor 元素并设置它的download 并单击它(这会将文件保存在指定的文件名中)否则它只会下载文件但用户必须使用 .jpg 扩展名重命名它才能打开它。

Codepen Link

 var camera, scene, renderer;
    var mesh;
    var strDownloadMime = "image/octet-stream";

    init();
    animate();

    function init() 

        var saveLink = document.createElement('div');
        saveLink.style.position = 'absolute';
        saveLink.style.top = '10px';
        saveLink.style.width = '100%';
        saveLink.style.background = '#FFFFFF';
        saveLink.style.textAlign = 'center';
        saveLink.innerhtml =
            '<a href="#" id="saveLink">Save Frame</a>';
        document.body.appendChild(saveLink);
        document.getElementById("saveLink").addEventListener('click', saveAsImage);
        renderer = new THREE.WebGLRenderer(
            preserveDrawingBuffer: true
        );
        renderer.setSize(window.innerWidth, window.innerHeight);
        document.body.appendChild(renderer.domElement);

        //

        camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
        camera.position.z = 400;

        scene = new THREE.Scene();

        var geometry = new THREE.BoxGeometry(200, 200, 200);



        var material = new THREE.MeshBasicMaterial(
            color: 0x00ff00
        );

        mesh = new THREE.Mesh(geometry, material);
        scene.add(mesh);

        //

        window.addEventListener('resize', onWindowResize, false);

    

    function onWindowResize() 

        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();

        renderer.setSize(window.innerWidth, window.innerHeight);

    

    function animate() 

        requestAnimationFrame(animate);

        mesh.rotation.x += 0.005;
        mesh.rotation.y += 0.01;

        renderer.render(scene, camera);

    

    function saveAsImage() 
        var imgData, imgNode;

        try 
            var strMime = "image/jpeg";
            imgData = renderer.domElement.toDataURL(strMime);

            saveFile(imgData.replace(strMime, strDownloadMime), "test.jpg");

         catch (e) 
            console.log(e);
            return;
        

    

    var saveFile = function (strData, filename) 
        var link = document.createElement('a');
        if (typeof link.download === 'string') 
            document.body.appendChild(link); //Firefox requires the link to be in the body
            link.download = filename;
            link.href = strData;
            link.click();
            document.body.removeChild(link); //remove the link when done
         else 
            location.replace(uri);
        
    
html, body 
    padding:0px;
    margin:0px;

canvas 
    width: 100%;
    height: 100%
<script src="http://cdnjs.cloudflare.com/ajax/libs/three.js/r69/three.min.js"></script>
<script src="http://threejs.org/examples/js/libs/stats.min.js"></script>

【讨论】:

如果我没听错的话,即使在不支持它的浏览器中,文件也总是保存为 .jpg?您知道如何在不降低浏览器质量的情况下优化导出的图像吗?在桌面上,我使用 imageoptim imageoptim.com,它在 MAC 上效果很好。导出的图像可以在不损失质量的情况下缩小 35%。您知道如何在浏览器中执行此操作吗? @confile: 如果浏览器支持,那么文件将保存在给定的文件名中,如example.jpg,但如果浏览器不支持它,文件将保存为一些随机自动生成的文件名,如ac2wz43 ,没有 .jpg 扩展名,因此用户必须手动将其重命名为带有 .jpg 扩展名的内容才能打开它。您可以在此处检查下载属性的兼容性caniuse.com/#feat=download @confile:我不确定我们如何在客户端完全优化图像,但您可以搜索其他可能能够做到这一点的 javascript 库。 @shiva: JS Fiddle Link 无法正常工作,请您更正。我需要看一下工作示例。谢谢。 @Shiva - 你的回答很好,非常感谢!希望我能给 +5 而不是 +1!

以上是关于Three.js:如何将场景的 2D 快照制作为 JPG 图像?的主要内容,如果未能解决你的问题,请参考以下文章

将 three.js 模型转换为 svg

Three.js/WebGL 和 2D Canvas -- 将 getImageData() 数组传递给 Three.DataTexture()

three.js 2D 鼠标点击到 3d 坐标使用 raycaster

如何在three.js场景中使对象仅对一台摄像机可见

three.js 3d 空间中的 .svg 资产

如何将fbxloader加载的摄像头添加到three.js中的场景?