A volumetric display is a graphical display device that creates a visual representation of an object in three physical dimensions. It renders a digital representation of a real object in physical space, the resulting image displays similar characteristics to a real world object, allowing an observer to view it from any direction. The three-dimensional images created by volumetric 3D displays are visible to the naked eye. It's just one type of 3D display, and it has always fascinated me more than other common types of 3D displays, such as stereoscopic displays.
Although often seen in science fiction, volumetric displays are not yet commonplace. There are professional systems on the market, but they are very expensive. The goal of my project was to create a kind of DIY volumetric display that can be built at home with available components for makers. Of course there are significant limitations of my DIY version compared to the professional volumetric systems available on the market. But the result is remarkable despite the limited possibilities.
Several types of volumetric displays can be observed:
- a. self-luminous (e.g. a LED matrix or rotating LED strips)
- b. projection-based (projection of points onto a 2D projection plane that periodically sweeps to form a physical volume)
- c. cross section based (e.g. multiplanar stacked translucent 2D displays)
- d. Holographic (wave optics based systems that reproduce the properties of light waves - amplitude, wavelength, and phase)
My display project belongs to type "b.", it uses a projector and a sweeping volume. The main components for my DIY display are :
- 1) A fast projector capable of displaying many hundreds of frames per second (more is better)
- 2) A sweeping projection volume that can be precisely controlled and is fast enough to provide the necessary persistence of vision effect.
- 3) Electronics for synchronization and control (based on Arduino)
- 4) Software tools for calibration, content creation and visualization (Windows based software)
The basic principle of operation is that the projector shoots a series of images onto the moving projection surface, resulting in a 3D object composed of many layers. For correct operation, the movement of the volume must be synchronized with the projector.
1) A fast projectorTo achieve the necessary persistence of vision effect, we need a projector capable of producing several hundred frames per second. There are professional projectors for scientific purposes that can go up to 1000 frames per second and beyond, but they are very expensive. There are also some projects that use the TI "light Crafter" evaluation kit, but I have not been able to get my hands on one.
So can a cheap projector be "hacked"(modified) for this purpose? Yes - sort of. Here is what I did. First, the only type of projector that can achieve these high frames rates is DLP. LCDs are too slow and LCOS too expensive. DLP projectors can easily output 1000Hz frames because they modulate light intensity (grayscale) by switching on and off at several kHz. But the problem is how to control the DMD chip for our purposes. All the magic is in the ASIC and hidden. So we need to find another way. A standard DLP projector used for home theater or office purposes is a projector that uses a DLP chipset to create the image. The chipset is the micromirror device ("DMD") and an ASIC(s) that controls it. The DMD is a chip that consists millions of tiny mirrors which represents the pixels of the image.
Home DLP projectors use only one DLP chip and process RGB light sequentially using a rotating color wheel. A single image is actually a sequence of subframes of red, green and blue image components. Due to the persistence of vision of our eyes, our brain sees a complete color image. Typically 3 or 4 segment color wheels are used. For better color reproduction, 6-segment RGBRGB color wheels are used as well. Some color wheels contain a yellow, cyan, or magenta segment in addition to the RGB segments. They are not suitable for our purpose. For this project, we need a simple wheel with RGB configuration only.
The simple idea is to use the RGB color subframes as individual monochrome frames. If the color wheel is moved out of the projector's optical path (it can't be removed completey since the projector would no longer work), we get a bunch of additional black and white images that used to be the R, G, B planes. Basically, we sacrifice color and gain frame rate. Which projector to look for ? Here are my search criteria:
- must be a DLP type
- should have a simple color wheel configuration such as RGBRGB or RGBW (no RGBCMY or something like this)
- capable of accepting vertical input rates >60Hz, ideally 120Hz or more
- affordable
After some research, my choice was the Benq W1070. Introduced in 2013, it was (and still is) an excellent 1080p projector for home cinema and office use. It accepts 120Hz input for DLP3D and according to available documentation it has a RGBRGB color wheel. Meanwhile it has been discontinued by BenQ and can be purchased used in the 100-200EUR range. Please have a look at my video for detailed step-by-step instructions on how I did this modification.
Relocation of the color wheel : The best location I found is where the speaker was previously mounted. Of course this is only one possiblity.
Sensor-cable: I simply attached additional wires in parallel to the sensor, so that we can later analyze the sensor signal.
DISCLAIMER: Projectors contain high voltage components. Modification and/or disassembly may result in fire or electric shock! If you are not sure what you are doing, do not attempt to modify or disassemble the projector !
1.2 Frame sequence analysisIn order to reduce the "rainbow effect" of DLP projectors, most color wheels cycle through the RGB planes a number of times per frame. So projectors show repeating RGB planes multiple times per frame quickly to reduce this effect. However, these repeated RGB planes are not suitable for our application. The same content would end up multiple times on different Y-positions. The goal is to find a projection mode with a high input frame rate and as few R, G, B subframe repetitions as possible. A good solution for me was to use the "120Hz frame sequential mode" for DLP3D.
Some numbers:
- This 120Hz mode provides consecutive 120Hz R, G, B images : 120Hz*3 = 360Hz
- To avoid flickering, the total "Volume Refresh Rate" should not be less than 30Hz (but also does not need to be much higher).
- With 360fps and 30Hz volume refresh, we will get 12 vertical planes for our volume : 360fps/30Hz=12
- Not impressive enough? How about this: At Full HD input, we get 24.8 million voxels : 1920x1080x12 = 24.8M (!)
Back to our 120Hz input mode. It is intended for DLP3D and will sequentially generate a high refresh rate. Don't be confused by the term DLP "3D" : We only use the highest possible frame rate of the projector, which happens to be this mode! Later on, the Arduino will control the projector via RS232 and will set this mode. The frame sequence for this mode is shown below.
A remaining problem is that the projector stream contains unnecessary elements, such as a "sync flash" to trigger the 3D viewing system. We need to block these (and other) elements from the projector stream. How to do that ?
1.3 Adding a shutterThis can be done using a LCD shutter (sometimes also called "light valves"). Basically a shutter is "one huge LCD pixel", which will block or unblock the light passing though it. It's a liquid crystal display without a backlight and is used to control the amount of light passing through an optical system. If the shutter is fast enough, we can select each subframe from the projector stream individually.
I tried a couple of commercially available light valves but due to switching speed, the best option was to re-use an old pair of old TV "active shutter glasses" which were used in the passed 3D-TV era. They usually can flip in less than 1ms and can easily achieve 144Hz or more. In order to reproduce the shutter control signal later, the signal was first analyzed in the working 3D glasses (Note : This signal of course depends very much on the model). In my case, I measured the follwing signal on the control wires : The control voltage oscillates around +9V..0..-9V. They become opaque at +9V and -9V and will be transparent when shorted (0V).
The shutter element was carefully removed and 2 wires were attached. They will later be controlled by the electronics (see section below). I mounted the projector on a base plate and used aluminum profiles to create a solid frame around it.
To be able to project upwards, I used an angled mirror and placed the shutter on top. This element is what I call the projector front unit. All front unit parts are 3D printed and a link to the STL repository is included below.
What we have so far: The combination of the above elements is a cheap projector modified to produce black and white frames at 360fps that can be addressed individually.
2) The swept volumeFor the swept volume, many different projection screens can be observed. Mostly rotating screens can be seen and have been implemented in many forms e.g. as a spinning disk, a helix winding around the vertical axis, etc...I also used rotating volumes for first experiements, as it's easy to start with. However I realized that the rotation surface provides a number of disadantages.
There are distortions coming from the projection onto a tilted surface and fast rotations needs to be precisely sync'ed with the projector frames. Also, I wanted something more "linear" and a rear-projection method. Ideally a projection plane that moves up and down. So how to precisely control a 30Hz plane movement?
2.1 Destroying speakersMy inspiration for the linear swept volume technology came from watching my speakers while listening to music... voice coils ! Voice coils are able to produce fast linear movements and are relatively cheap. So I ended up in modifying old (bass) speakers into cheap linear coil actuators. The speakers are cannibalized - so that only the permanent magnet, the voice coil and the "spider" (flexible suspension that forces the voice coil to move axially) remain. Finally the outer basket and the diaphragm are removed. Those speaker don't "speak" anymore, they only vibrate. (Hi-Fi enthusiasts, please don't hate me, these were cheap old speakers... :-)
The voice coils are driven by a cheap 2-channel audio amplifier module, which does not have to be a "class D" type or anything fancy. In the end, a simple sine wave around 30Hz is all we need. The analog sine wave input for the amp is generated by the Arduino in-sync with our projector. To realize the rear projection surface, a rear-projection foil on a thin transparent plastic sheet is used. This surface needs to swing up and down vertically driven by the coils. So we need some flexible levers to hold the surface and at the same time transmit and amplify the movement of the coils.
2.2 Flexible structuresI decided to try "compliant mechanisms" for the first time. Compliant mechanisms are flexible structures that transfer an input displacement to another point though elastic body deformation. The beauty of these structures is that they are easy to 3D print. They are also lighter and do not need springs etc. All joints in my final mechanism are composed by flexures in a parallel structure. Due to the integration of this compliant and parallel mechanism, the motion of the projection surface is guided by mechanical constraints from the parallel structure to move only vertically. In early versions, I used standard 3D printing materials like PLA or PETG for my experiments. But I soon realized that these materials are not ideal. Such standard printing materials are too stiff, and my structure has always been broken after a certain period of use. The reason is because PLA or PETG are not elastic enough, they have a high flexural modulus. So is there a material which we can 3d print which can withstand the necessary elastic deformations for a long time ? Yes : Polypropylene (PP).
Polypropylene is an ideal material for this purpose because it can deform without breaking and it's very flexible. It is tough, light and has a high fatigue resistance. It fully retains its shape after being bent. This makes it suitable for living hinges which are usually not achievable with more familiar 3D printing materials like PLA etc. Many living-hinges are made from PP (e.g. the famous TicTac box cover). However, it is more difficult to print, which is why it is not (yet?) widely used in 3D printing: PP often warps and has poor bed adhesion. After reading about it, I really expected a lot of problems, but to my surprise, printing worked really well with a few tips I found:
- to increase bed adhesion: PP loves to stick to itself, and most packing tapes are made of polypropylene. So I used a clear packing tape made of PP. (it almost stuck too well... it was hard to remove!)
- to avoid warping issues : I added a large brim and fence to the slicer, which provided good stability during the printing process.
- Finally I tuned the default profile slightly towards very slow printing speeds (50mm/s max). The drawback is obviously that the print takes quite long, but thankfully my structures are not too big. Print time was around 3h which was ok for me.
All of the above lead to successful prints - even without an enclosure. The results are excellent. The structures amplify the vibrations perfectly without breaking. And now I can stretch the projection volume up to 15-20mm without risk of damage.
The Arduino + circuit is necessary to "glue" everything together. Here are all the main tasks for the Arduino electronics:
- 3.1 synchronize with the projector (color wheel signal measured by the Ardunio)
- 3.2 drive the voice coils to move the projection surface in sync with the projector (low-pass-filtered PWM with offset compensation)
- 3.2 control the shutter to select only relevant parts from the projector output stream (H-bridge)
Secondary functions:
- Serial remote control for the projector (RS232)
- Front panel controls, such as buttons, a small display, and some LEDs
- Provide a serial control and status interface to a PC
(secondary functions are not explained in detail here)
3.1 analyze the projector timingTo get in sync with the projector, we need to "hack" into the color wheel sync signal. This is usually a simple IR sensor that measures the speed of rotation of the disc. The sensor detects when it receives the echo from the reflective part of the wheel and can sense the starting point of each rotation. I added a parallel output (2 wires) to the IR sensor and fed it to an opamp circuit. It's important that we don't interfere with the original signal and that we can and create a proper 5V pulse signal that an Arduino can safely detect on one of the external interrupt pins. This required some signal conditioning. The IR-sensor provides tiny analog pulses which needs to be amplified and run through a schmitt-trigger stage before being fed onto the Arduino input. The circuit I designed consists of a decoupling-stage, non-inverting amplifyer (gain factor around 6) to bring the signal between the "rails" (approx. 3V), and finally the schmitt trigger with a hysterisis of VL=2V, VH=3V. With this circuit we can now reliably generate an external interrupt on the Arduino when a new color wheel rotation starts.
Note: The circuit shown here does not contain the bug that was in the first version (as explained in my video).
I use a simple wavetable player that starts on every 2nd color wheel pulse. The wave output (a PWM port on the Arduino) has an adjustable frequency (typ. 30Hz) and therefore results in a waveform that is perfectly synchronized with the projector. The waveform is a simple sine wave (I've experimented with other waveforms, but the result is always a sine motion of the physical projection surface... simply because of the inert behavior of the surface due to its mass and friction).
The PWM output is low-pass filtered and passed through a simple op-amp stage to adjust the level and remove the positive DC offset to make it oscillate around GND level. This allows the connected amplifier and coils to reach their maximum potential and the surface to perform smooth movements.
The LCD shutter works like a capacitor. When charged, it becomes opaque - when shorted, it becomes transparent. For simple low frequency applications, applying a digital voltage would be enough - but we need to get the maximum performance (highest switching speed). LCD elements typically perform best when the applied polarity is constantly changing. The signal alternates from high to low in opposite states on the pins. To reproduce the shutter control signal in the shutter glasses, the signal was first analyzed in the working 3D glasses. Of course, this signal depends very much on the model and will be different. I measured the following signal on the control wires:
The control voltage oscillates around +9V..0..-9V. They become opaque at +9V and -9V and will be transparent when shorted (0V). To reproduce this waveform, we need an H-bridge (even the 3D glasses ICs have H-bridges built in). I use the well-known L293, which is very common for motor control, but it does exactly the same job without much effort. All it needs is 2 input pins from the Arduino (1A and 2A), a 9V supply and we are good to go. The shutter is connected to 1Y and 2Y respectively. This is the function table for the L293 connected to the shutter:
- 1A 1Y 2A 2Y Shutter State
- H H H H both sides charged (forbidden)
- H H L L Charge + side -> blocking (shutter opaque)
- L L H H Charge - side -> blocking (shutter opaque)
- L L L L both sides grounded -> open (shutter transparent)
The Arduino is able to switch the state precisely during the output of the wave, so we can choose which part of the projected planes we want to get on our surface.
After extensive testing on a breadboard, all functions were finally consolidated onto a single PCB. The PCB was designed in KiCAD and consists of all the functions described above. The complete schematic is included below.
Each of the 12 frames appears at a unique vertical position of the moving projection plane. Since the plane moves up and down, the frame position has no direct relation (frame number -> Y-position), but they appear somehow mixed up. This depends on the phase of the sine wave. In order to display the correct 3D objects, we first have to find out the correct mapping (subframe sequence -> Y-position). To do this, I created a tool that displays a test pattern with 12 numbered stripes (R, G, B planes), each of which can be rearranged. By arranging all the strips in an ascending Y-order, we can find the assignment. This only needs to be done once. The final configuration is stored in a layer configuration file (VolDisLayerConf.vdl).
To create 3D objects for this machine, we need to slice 3D models into 12 layers and assign them to frames according to their Y-position. I created a simple slicer tool for Windows that reads common 3D file formats like STL, OBJ, etc. and allows to adjust the object position and orientation. It creates 12 slices of this object with their corresponding monochrome images. The tool reads the layer-config file we found earlier and is able to assign the 12 layers correctly (Y-position -> frame-number). The monochrome images are recolored in pure R, G, B to match the correct frame and then saved as final bitmaps. I use 2 bitmaps for an object in the 120Hz "side-by-side format", which squeezes 2 bitmaps into one, allowing the projector input to be driven with only 60Hz instead of 120Hz. Each bitmap contains 2 sides with 3 frames each of R, G and B - a total of 12 frames.
Finally, the player application reads these 2 bitmaps and displays them in strinct vsync with a 60Hz output generated by the GPU. The player is designed to run on the built-in single board computer Lattepanda, as I wanted to have a standalone unit. Alternatively, a connected PC can be used - the external video input is located on the side "droid" panel and the input can be changed from the Arduino using the menu. The player application works with a number of subdirectories, which can contain either a static object (2 bitmaps with 12 layers) or animations - a series of bitmap pairs. In any case, the entire content of a subdirectory is repeated endlessly until the next source directory is selected. All 3 tools (Calibration, Slicer, and Player) are standalone Windows applications (.EXE).
NOTE : The tools created for this project are not included here. But I am happy to make them available if you are interested. Simply drop me a note.Please also be aware that they are not fully tested and probably not perfect. Use them at your own risk. However, they worked well for my project.
4.4 Arduino ScriptThe Arduino script (included below) is divided into several modules for better readability. The module "Volumetric14.ino" contains the 3 main functions :
- Analyze the projector timing : After one full revolution, the color wheel sensor triggers an interrupt on pin D18 (BRGBRG sequence typ each 8.3ms or 120Hz). The assigned interrupt function measures the frequency and restarts the waveform output after 2 interrupts.
- Drive the voice coils : When started, the waveform output is controlled by the TIMER4 interrupt of the MEGA. The interrupt frequency is typically set to 30Hz (256 table entries -> 130us for 30Hz). On each TIMER4 interrupt, the waveform table is incremented (see the sinus.h include file) and the PWM output pin D3 is updated accordingly. The PWM is controlled by TIMER3 on the MEGA.
- Control the shutter : In the same TIMER4 interrupt function, the L294 IC is controlled by 2 pins of the Arduino 1A and 2A. The corresponding IC outputs to the shutter are 1Y and 2Y.
Secondary functions of the Arduino script are: Handling serial communication to PC and projector, controlling OLED display, rotary knob, LEDs and buttons. In order not to make this article too long, they are not explained in detail here. See the module source code and comments for more details.
Why the Mega again...?
First, I needed a lot of memory, pins and available timers. Then, to add a serial interface from the Arduino to the projector for this purpose, the Arduino Mega provides a number of hardware UARTs. Almost no other Arduino has multiple hardware UARTs. This is necessary because the command protocol requires simultaneous sending and receiving. So no software UART can be used. To connect the TTL (Transistor-Transistor Logic) level signals from the Arduino UART to the RS232 interface, I use a cheap converter module.
5) Star Wars inspired housingFrom the first time I saw a working 3D image, there was this association with a Star Wars holoprojector. Those pale, translucent, volumetric appearances of 3D objects floating in mid-air are what we have all seen in the movies. And being a Star Wars fan, I just had to give it the look and feel of a console from a galaxy far, far away... For the front and side covers, I decided to use layered panels with the typical elements like square tiled red and white buttons, lots of LEDs, white pin stripes and the inevitable greeblies used in most other designs. The panels are 3d printed - a link to the STL files is included below.
The USB connector for debugging the Arduino has to be a droid interface, of course - and finally, after a bit of weathering, it almost looks like it has been used on a hidden rebel base for years...
The user interface is divided into several parts: the Arduino controls include buttons to turn the projector on and off, to enable wave output to the coils (to move the projection plane), and a sync button to turn off sync to the projector. This sync button can produce interesting effects: The layers start moving slowly up and down when the 30Hz output frequency is no longer synchronized to the projector. This can lead to cool animations for symmetrical objects like a cone, etc. See the end of my second video for examples. The "Volume Knob" allows you to control the vertical size of the volume (sine wave amplitude for the voice coils). To have a small menu on the machine, I implemented a rotary encoder knob and an OLED display. This allows to change parameters like phase, frequency, shutter on/off positions etc. and can show some status information. Finally, the Lattepanda display shows the player frontend, where the objects from the subdirectories are displayed and can be selected. Some additional options like vsync, auto-play etc. are also available, but the main purpose is to select the desired objects. The player menu itself is also based on the minimalist Star Wars screen design.
ConclusionThis project was a long journey with many dead ends. It took me more than a year to complete this machine, and I encountered completely new areas of technology that I had never touched before (e.g. compliant structures). There are certainly many limitations in this design (such as only 12 layers, only monochrome, etc.). But in the end it is a DIY project and not a professional product.
For the future, there may be a lot of room for improvement in the design - maybe second-hand gaming projectors that accept 240Hz input will be cheaper soon?
And before you ask: No, I never managed to create the iconic Princess Leia hologram scene, but that was more due to a lack of 3D source material.
Anyway, I am really happy with the result and hope that some of you will find it interesting. Thanks for reading. I look forward to your comments.
Mac
Comments