How to make Sky Boxes from A-Frame Scenes

Bookmarklet to produce equirectangular images suitable for sky spheres from A-Frame scenes.

Ada Rose Cannon
Aug 30, 2016 · 3 min read

This has now been rewritten with a new faster method based on cubemaps. The orignal post has been kept for posterity.

UPDATE 2: This can now be done entirely within AFrame

AFRAME.scenes[0].components.screenshot.capture()

I am working on a page to show off my A-Frame scenes and I want to give the users an immersive preview without loading up the whole scene.

I can do this by loading up a spherical image into an <a-sky> or perhaps set it as then environment in the Samsung VR Browser as detailed here.

But producing these images usually requires special render rigs in Maya or Blender so are hard to reproduce the exact look from the web.

This script when run on a web page with a A-Frame scene will render out the scene at many (64800) different angles stitch them together into a single image. It’s a bit brute force but it works pretty well.

The bookmarklet

Create a new bookmark with the following url:

javascript:(function()%7Bvar%20width%20%3D%20window.renderTileWidth%20%7C%7C%2010%3Bvar%20dT%20%3D%20window.renderTileAngle%20%7C%7C%201%3Bvar%20sceneEl%20%3D%20document.querySelector('a-scene')%3Bvar%20scene%20%3D%20sceneEl.object3D%3Bvar%20camera%20%3D%20new%20THREE.PerspectiveCamera(%20dT%2C%201%2C%200.1%2C%2010000%20)%3Bvar%20renderer%20%3D%20new%20THREE.WebGLRenderer()%3Bvar%20CONV%20%3D%20Math.PI%2F180%3Bif%20(window.renderOrigin)%20%7Bcamera.position.copy(window.renderOrigin)%3B%7D%20else%20%7Bcamera.position.copy(sceneEl.camera.getWorldPosition())%3B%7Dcamera.rotation.reorder('YXZ')%3Brenderer.setSize(%20width%2C%20width%20)%3Bvar%20canvas%20%3D%20document.createElement('canvas')%3Bcanvas.width%3Dwidth*(360%2FdT)%3Bcanvas.height%3Dwidth*(180%2FdT)%3Bvar%20ctx%20%3D%20canvas.getContext('2d')%3Bfor%20(var%20i%3D0%3B%20i%3C360%3B%20i%2B%3DdT)%20%7Bfor%20(var%20j%3D0%3B%20j%3C180%3B%20j%2B%3DdT)%20%7Bcamera.rotation.set((-j%2B90)*CONV%2C-i*CONV%2C0%20)%3Brenderer.render(%20scene%2C%20camera%20)%3Bctx.drawImage(renderer.domElement%2C%20width*i%2FdT%2C%20width*j%2FdT)%3B%7D%7Dwindow.open(canvas.toDataURL())%2C%20undefined%7D)()

The method

I couldn’t find a way to render a full 360 degree view of a scene in one go so I decided to make my own. This is probably not the optimal way to do it.

This method rotates the camera by a small degree and takes a tiny render. It renders many vertical stripes then it then stitches them all together in giant canvas and opens the resulting image in a new tab for saving.

It takes a couple of minutes on my netbook but gives a pretty good result.

Captured image using the bookmarklet
var width = window.renderTileWidth || 10;
var dT = window.renderTileAngle || 1;
var sceneEl = document.querySelector('a-scene');
var scene = sceneEl.object3D;
var camera = new THREE.PerspectiveCamera( dT, 1, 0.1, 10000 );
var renderer = new THREE.WebGLRenderer();
var CONV = Math.PI/180;
if (window.renderOrigin) {
camera.position.copy(window.renderOrigin);
} else {
camera.position.copy(sceneEl.camera.getWorldPosition());
}
camera.rotation.reorder('YXZ');
renderer.setSize( width, width );
var canvas = document.createElement('canvas');
canvas.width=width*(360/dT);
canvas.height=width*(180/dT);
var ctx = canvas.getContext('2d');
for (var i=0; i<360; i+=dT) {
for (var j=0; j<180; j+=dT) {
camera.rotation.set((-j+90)*CONV,-i*CONV,0 );
renderer.render( scene, camera );
ctx.drawImage(renderer.domElement, width*i/dT, width*j/dT);
}
}
window.open(canvas.toDataURL());

Using the bookmarklet

The bookmarklet will render from the scene’s current camera’s position by default.

Once it is done it will open the result in a new window.

It will render 360x180 10x10 squares this can be tweaked to increase to decrease quality, this very low quality example gives a good idea of how the bookmarklet works.

renderTileAngle of 30 with a renderTileWidth of 100

One more from: https://samsunginternet.github.io/a-frame-demos/racer/

Drawbacks

It is a brute force method so can take a while to run. It tends to breakdown at the top and bottom of the scene where the greatest stretch happens. If you have a better/faster method please comment!!!

Samsung Internet Developers

Writings from the Samsung Internet Developer Relations…

Thanks to Peter O'Shaughnessy

Ada Rose Cannon

Written by

Co-chair of the W3C Immersive Web Working Group, Developer Advocate for Samsung.

Samsung Internet Developers

Writings from the Samsung Internet Developer Relations Team. For more info see our disclaimer: https://hub.samsunginter.net/about-blog

Ada Rose Cannon

Written by

Co-chair of the W3C Immersive Web Working Group, Developer Advocate for Samsung.

Samsung Internet Developers

Writings from the Samsung Internet Developer Relations Team. For more info see our disclaimer: https://hub.samsunginter.net/about-blog

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store