Smoke2EXR
The Smoke2EXR add-on provides a new button at the bottom of the Smoke Domain Cache properties panel. Clicking this button on a previously Baked smoke simulation allows you to convert a frame of the smoke into an EXR image representing the properties of the smoke at each point in the domain. The 3D domain is mapped into the 2D image by splitting into a number of 'slices' and some (relatively) simple maths can be used to convert from the 3D to 2D coordinates.
​
Note that once the smoke is captured to an image you can apply distortions that are not (currently) possible with the data when stored as smoke (since the smoke simulation only allows access to the current 'point' in the domain, without displacement).
​
Note also, that there are whispers of potential changes to volumetrics to allow the Displacement socket to be used to displace the coordinates of volumetrics - I look forward to such changes as it will remove the need to capture the details from the domain to be able to distort the volumetric data.
What does it do?
The add-on adds functionality to Blender to allow you to capture the underlying data from a frame of a smoke simulation and make it available in a way that can be accessed and manipulated within your shader material.
​
How does it work?
Once your smoke has been 'baked' into a cache file, pressing the new 'Convert Smoke to EXR' button will prompt you for details of what to convert (the name of the cache and the chosen frame).
The add-on will then examine the smoke cache, extract the information regarding the selected frame and generate a number of images containing the smoke domain data - this can include one or more of smoke 'density'/'colors', 'flame/heat/fuel' (if a Flow object includes flame), velocity.
​
NOTE: Prior to verson 1.12 of the add-on, the output was always produced as a single row of images. However, at 1.12 the 'Multi-Row option was introduced which will result in the add-on splitting the row of 'slices' over multiple lines to result in a better proportioned image (so it's more square (rather than a long, thin image that can cause rounding discrepancies).
Why would I want to do that?
When rendering a smoke domain, the smoke information is accessed by was of an Attribute node - for example, to get the 'density' or 'flame' at the current render point in the domain. The standard method of access the smoke domain data does not allow other points in the domain to be sampled - only the simulation result at that one particular point in the volume can be accessed. By extracting the entire frame of smoke into an image, the material shader can access any other point within that frame of the simulation (in fact, by extracting to a sequence of images it would be possible to also access the data for other frames of the simulation!) and this allows the smoke to be manipulated in various ways (such as add additional distortion).
Here a smoke simulation was set up to emit smoke from the entire volume of Suzanne - to capture the volume of the undistorted mesh. At render time, the coordinates are distorted so as to draw out tendrils and wisps from the back of the skull, using Noise textures to warp them into interesting shapes. As the wisps are generated procedurally rather - than via a simulation - we have freedom to manipulate the volume in ways not possible using a smoke simulation alone.
What are the limitations?
Normal smoke simulations and the Image Texture node perform interpolation for sample points between two voxels/pixels of the data. With 'slices', the standard interpolation works fine "within" the slice but there is currently no interpolation between the slices of the simulation. This can result in artifacts where the domain appears "layered" (zoom into the above image of Suzanne and you should be able to make out the horizontal "steps" above the eye).
This could be overcome by implementing interpolation between the layers (by taking two sample points (one from each adjacent layer) and interpolating between them to smooth out the layers via a second Image Texture and some additional maths (a future development).
In addition, Linear interpolation does not seem to be able to cope with the multi-slice format of the image when using Eevee - therefore, it is recommended to use a different interpolation mode (closest, quadratic, etc.).
The add-on cannot currently capture details from a 'high resolution' simulation, it should be a non-Adaptive domain (otherwise you'll get variable number of slices and frame detail per frame), must be in Point Cache format.
How to install it
Download the ZIP file and use the 'Install from Zip' button (renamed to just 'Install...' at Blender version 2.8) within the Add-Ons section of the preferences to install it. Once installed, don't forget to tick the Enable checkbox alongside the add-on.
How to use it
Once enabled, you should see the new Convert Smoke to EXR button in the Cache section of the Smoke Domain properties. You *must* give the smoke cache a name before you can convert it to EXR as the name is used to identify those cache files on disk alongside the .blend file and the smoke must then be 'baked' (you must have saved the .blend file before you can bake the simulation and the simulation files will be placed in the same location as the .blend.
​
Once the conversion is complete you should have a number of new images visible in the image editor.
​
The 3D domain coordinates can be converted into 2D image coordinates using the following mathematical calculation :
# Expression to convert Generated coordinates into 'sliced' coordinates for image generated from Smoke2EXR
# Use the Node Expressions add-on to generate the node group from this text
_x = Input[x]
_y = Input[y]
_z = Input[z]
_slice = min(1,max(_z,0)) * ZSlices{128}
_sliceNo1 = floor(_slice)
_sliceNo2 = _sliceNo1 + 1
#...calculate tileX and tileY. Note '0.001' added in to avoid rounding crossover issues
_tilePosX1 = mod(_sliceNo1, TileColumns)
_tilePosY1 = floor(_sliceNo1 / TileColumns+0.001)
_newx1 = (clip(_x) + _tilePosX1)/ TileColumns
_newy1 = (clip(_y) + _tilePosY1)/ NumRows
_tilePosX2 = mod(_sliceNo2, TileColumns)
_tilePosY2 = floor(_sliceNo2 / TileColumns+0.001)
_newx2 = (clip(_x) + _tilePosX2)/ TileColumns
_newy2 = (clip(_y) + _tilePosY2)/ NumRows
Output1[] = combine(_newx1, _newy1, 0)
Output2[] = combine(_newx2, _newy2,0)
InterpolationMix = _slice - _sliceNo1
Use the Node Expressions add-on to convert the above text into a Node Group which supports interpolation between image 'slices'.
​
Alternatively, if the smoke has been converted to a single row of tiles (ie, you disabled the Multi-Row option when capturing the smoke to an image) the Node Expressions (Lite) version of the add-on can be used with the following expressions to create a basic (non-interpolated) note group :
Output[] = combine((min(1,max(Input[x],0)) + floor(min(1,max(Input[z],0)) * ZSlices{128}))/ZSlices, 1-min(1,max(Input[y],0)), 0)
Set the parameters (ZSlices, TileColumns, NumRows) appropriate to the generated EXR image - the parameters are encodedi in the generated filename (ie, '<slices>_<columns>x<rows>_name...'). In the case of a 'Multi-Row' image you should specify the number of ZSlices as well as the TileColumns and NumRows. In the case of 'Single-Row' images you only need specify the number of ZSlices.
An Example
Finally, here's an example captured from a smoke simulation generated from errupting particles. Note the coloring representing the velocity of the smoke at each point.