Skip to main content
Skip table of contents

3D Projection with the PROPixx and 3D Polarizer

In this guide, we will cover how to present stereoscopic stimuli using the PROPixx projector and DepthQ 3D Polarizer. These two devices can be used together for full-colour stereoscopic presentation at up to 480 Hz (240 Hz/eye). In addition to high-speed presentation, this system has several other advantages, including:

  • Passive circular filters are plastic and thus MRI, MEG and OPM safe

  • Custom filter sheets can be purchased to create custom viewing apparati (e.g., for non-human primates)

  • No specialized graphics card or native 3D support required

  • Optional custom high-speed sequencer designed in partnership with the NIH to actively manage crosstalk levels, and reduce Pulfrich effects

First, we will consider some general hardware and screen requirements, then discuss our recommended method of preparing stimuli for 3D stimulus presentation. Code examples will be provided where appropriate.

Getting Started: Equipment and Materials

To effectively present 3D stimuli, the DepthQ polarizer must be connected to the VESA 3D port on the back of the PROPixx (pictured below). The polarizer should be placed directly in front of the lens with the arrow pointing away from the projector. Wingnuts on the sizes of the polarizer frame can be used to adjust the height of the polarizing filter so that it aligns with the lens.

A.png

A. VESA 3D port on the back of the PROPixx. B. Example placement of DepthQ polarizer in front of the projection lens. C. Example 3D rear-projection layout, note the polarizer in front of the PROPixx on the left.

The 3D glasses provided along with the polarizer are standard circular polarizing filters. These are the same glasses used for 3D movies at a cinema. If you need a custom lens layout, you can purchase right- and left-handed circular filter material directly from certain manufacturers (for example, here) and cut it to size. Note that when buying filter material, HE indicates left-handed and HER indicates right-handed polarization.

It is normal for the polarizer and glasses to filter some visible light. Average spectral transmission of 30-35% is not uncommon. During luminance tests, VPixx staff scientists noted that inexpensive, commercial 3D filter glasses can vary in transmission rates across units. Users requiring an exact measure of light transmission should take direct measurements using a colorimeter or spectrophotometer, and should not assume measurements generalize across individual pairs of glasses.

Projection screen materials

Typical projection screens are designed for Lambertian light distribution. In other words, they aim to diffuse or reflect light in every direction, thus ensuring 1) equal luminance output across the display surface and 2) wide viewing angles. Unfortunately, this same property severely degrades 3D polarization. In practice, projection screens optimized for 2D use make for poor-quality 3D displays.

Fortunately, specialty ‘silvered’ screen materials exist. These materials prioritize direct reflectance or transmission, preserving polarized light. Silvered screens are often recognizable by their shiny surface and grey coloration (as opposed to the bright white of 2D screens). Below is a side-by-side example:

3D.png

Left: a silvered 3D screen, with a visibly darker material. Right: a standard 2D screen.

VPixx Technologies manufactures several screen shapes and sizes for standalone, tabletop and in-bore projection systems. All of our screen models can be fitted with a 3D-optimized screen material.

Due to the non-Lambertian light distribution, 3D screens are more prone to hotspotting (bright spots and uneven luminance), particularly at short projection distances. VPixx Software Tools include a PROPixx calibration routine to correct for hotspotting in these cases; see the section below for details.

Hotspot correction with the PROPixx projector

Due to their polarization preservation qualities, 3D-optimized screens can sometimes display hotspotting or uneven luminance, particularly at short projection distances. To perform a hotspot correction, you will need:

  • A device which can report the luminance value for specific screen points in cd/m^2, such as a spectrophotometer or colorimeter, and

  • A Windows computer with LabMaestro 1.7 or later installed   

First, ensure your firmware is on the latest version. To do this, open LabMaestro with your device connected, Select the PROPixx device, right-click and select Firmware Update from the side menu. Follow the steps in the widget to complete the update.

After the update, set the PROPixx to the correct projection mode for your layout (e.g., rear projection, ceiling mount projection, desired resolution).

Select the PROPixx from the device menu, right-click and select “Hotspot Correction.” Follow the steps in the widget to complete the calibration.

After the correction is done once, it can be enabled or disabled on any computer via the following methods:

  • In LabMaestro via device settings (right click PROPixx and select ‘Configuration’)

  • In MATLAB via the following commands (followed by a register write):

    • Datapixx('EnableHotspotCorrection');

    • Datapixx('DisableHotspotCorrection');

  • In Python via the PROPixx class (followed by a register write):

    • setHotSpotCorrection(enable) #set to true or false

If you intend to build your own screen for stereoscopic presentation, we recommend the following flexible screen materials from Stuart FilmScreen, which can be purchased directly from the supplier:

Rear-projection: Stuart FilmScreen FilmScreen 150
Front-projection: Stuart FilmScreen Silver 5D

These are the same materials we use in our screens.


Mirrors in the light path

Some projection systems use one or more 45-degree mirrors to redirect the beam of light to the screen. This is common in MEG and MRI facilities, where the projection waveguide may be offset from the screen location. MRI systems also use mirrors mounted on the head coil to allow the participant to view an image on a screen at the head of the bore.

For 3D applications, we recommend all mirrors in the light path be first-surface for the best possible 3D image. That is, the reflective layer of the mirror should be in front of the glass layer, not behind it. This ensures internal reflections within the glass layer do not compromise the polarization.

mirror1.png

Types of mirrors. Source: https://www.dynasil.com/optical-coatings/first-surface-vs-second-surface-mirror/

An easy way to tell if a mirror is first- or second-surface is to place an item against it. If it connects with its reflection, it is a first-surface mirror.

mirror2.png

Source: Wikipedia

Finally, some eye-tracking systems utilize “hot” mirrors that have an infrared reflective coating. It is strongly advised to avoid using hot mirrors during 3D stimulus presentation, as these are known to disrupt polarization.

Enabling 3D Mode and Formatting 3D Stimuli

There are many ways to format 3D stimuli for presentation. Here we discuss several strategies and when it is best to use them. All of the strategies discussed are supported by the PROPixx; some are more robust than others. At the end of this section, we provide a summary of the different options, their supported refresh rates and their best use cases.

Frame-sequential stereo

In frame-sequential stereo, images are interleaved temporally. Even and odd video frames are presented to the left and right eye, respectively.

Time (4).png

Frame sequential stereo

Image alternation is controlled manually (e.g., drawing left and right eye images and flipping them one after another) or via dedicated stereo draw buffers alternately sampled during stimulus presentation.

This mode is the default behaviour for a connected 3D Polarizer. It will alternate polarization on subsequent video frames until instructed otherwise, independent of video content.

This method’s biggest advantage is simplicity and ease of implementation; however, it is vulnerable to frame dropping and 3D de-synchronization. As such we recommend a small modification to this method, described in the next section.

Stereo Blue Lines

Stereo Blue Lines are a popular method of explicitly encoding each video frame to target the correct eye. This preserves stereo synchronization even if your system is dropping frames.

Stereo Blue Line is easy to implement:

  1. Organize your stimuli as you would for frame-sequential stereo.

  2. To assign a frame to the right eye: Set the bottom row of pixels in the frame to an intensity greater than mid-grey [128, 128, 128]

  3. To assign a frame to the left eye: Set the bottom row of pixels in the frame to an intensity less than mid-grey [128, 128, 128]

Note images must be full-screen for Blue Lines to work.

Drawings (7).png

Stereo Blue Lines added to the bottom of each image to ensure they are shown to the correct eye. The lines are exaggerated for visibility.

Some software will automate Blue Line generation for you. We generally recommend drawing the line yourself. It is simple and ensures you are formatting your video frames correctly.

MATLAB Example
MATLAB
function helloWorld3D()

% Initialize PROPixx
initializePROPixx();

% Initialize Psychtoolbox window
PsychDefaultSetup(2);
screenNumber = max(Screen('Screens'));
[windowPtr, rect] = PsychImaging('OpenWindow', screenNumber, 0);
Screen('BlendFunction', windowPtr, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
HideCursor;

% Initialize stimuli
helloText = 'Hello';
worldText = 'World';

% Start loop drawing the message
while true
    DrawFormattedText(windowPtr, helloText, 'center', 'center', 255);
    drawBlueLine(windowPtr, rect, 1);
    Screen('Flip', windowPtr);

    DrawFormattedText(windowPtr, worldText, 'center', 'center', 255);
    drawBlueLine(windowPtr, rect, 0);
    Screen('Flip', windowPtr);
    
    [~, ~, keyCode] = KbCheck;
    if keyCode(KbName('Escape'))
        break;
    end
end

% Close the Psychtoolbox window
sca;
Datapixx('Close');
end

function initializePROPixx()
% All VPixx hardware commands to set the right mode for the PROPixx
Datapixx('Open');
Datapixx('EnableVideoStereoBlueline');
Datapixx('RegWr');
end

function drawBlueLine(windowPtr, rect, frame)
% Helper function to draw lines on the appropriate image
% if Frame = 0 draw right eye blueline
% if Frame = 1, draw left eye no blueline
% Call immediately before flip
if frame == 0
    frameColor = [255, 255, 255];
else
    frameColor = [0, 0, 0];
end
rowStart = [rect(3)/2-16, rect(4)-1];
rowEnd = [rect(3)/2+16, rect(4)-1];
Screen('DrawLine', windowPtr, frameColor, rowStart(1), rowStart(2), rowEnd(1), rowEnd(2),2);
end
Python Example
PY
from psychopy import core, visual
from psychopy.hardware import keyboard
from pypixxlib._libdpx import DPxOpen, DPxClose, DPxWriteRegCache, DPxEnableVidVesaBlueline, DPxSetVidMode

def initializePROPixx():
    #All VPixx hardware commands to set the right mode for the PROPixx
    DPxOpen()
    DPxSetVidMode('C24')
    DPxEnableVidVesaBlueline()
    DPxWriteRegCache()
    
def drawBlueLine(win, frame=0):
    #Helper function to draw lines on the appropriate image
    # if Frame = 0 draw right eye blueline
    # if Frame = 1, draw left eye no blueline
    #Call immediately before flip
    if frame==0: 
        frameColor = [255,255,255]
    else:
        frameColor = [0,0,0]
    rowStart= [-win.size[0]/2, -win.size[1]/2]
    rowEnd =[win.size[0]/2, -win.size[1]/2]
    line = visual.Line(
            win=win,
            units = 'pix',
            start=rowStart,
            end=rowEnd,
            interpolate = False, #MUST be set to false
            colorSpace = 'rgb255',
            lineColor = frameColor,
            lineWidth=2)
    line.draw()
    
#Begin Experiment
kb = keyboard.Keyboard()
initializePROPixx()
win = visual.Window(
        screen = 1,
        monitor =None, 
        fullscr=True,
        color='black',
        units = "pix"
        )

#Initialize our stimuli
hello = visual.TextStim(win=win, text='Hello')
world = visual.TextStim(win=win, text='World')

#Start a loop drawing our message
while True:
    hello.draw()
    drawBlueLine(win, frame=1)
    win.flip()
    world.draw()
    drawBlueLine(win, frame=0)
    win.flip()
    keys = kb.getKeys(keyList=['escape'])
    if keys:
        break
    
win.close()
core.quit()
DPxClose()

‘Blue line’ is the name for historical reasons, but it’s a misnomer. The colour of the line is not important. As long as your pixel values are brighter than mid-grey ([128, 128, 128] in RGB255) it will work as intended.

PsychoPy users: Blue Lines must be drawn with interpolation turned off. Otherwise, the pixel values will blend with the background colour and may not trigger proper polarization.

Quad4x3D Mode

If you are using the Quad4x sequencer, a special command will immediately assign each quadrant to a specific eye. Simply enable the mode and ensure you assign the correct stimuli to each quadrant. This method is robust against de-synchronization by frame dropping, because if a frame is dropped all four quadrants are lost, and the next frame will show content in the correct order.

Time (6).png

In Quad4x3D, quadrants are assigned automatically to each eye.

For more details on Quad4x, see our VOCAL on high-speed projection modes.

MATLAB Example
MATLAB
function helloWorld3DQuad4x()

% Initialize PROPixx
initializePROPixx();

% Initialize Psychtoolbox window
PsychDefaultSetup(2);
screenNumber = max(Screen('Screens'));
[windowPtr] = PsychImaging('OpenWindow', screenNumber, 0);
Screen('BlendFunction', windowPtr, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
HideCursor;

%Initialize quadrants for QUAD4x
quadrants = initializeQuadrants();

% Start drawing loop
while 1
    for quadrantID = fieldnames(quadrants)'
        quadrantID = quadrantID{1};

        % Create text stimulus according to quadrant
        if strcmp(quadrantID, 'Q1') || strcmp(quadrantID, 'Q3')
            text = 'Hello';
        else
            text = 'World';
        end
        
        oldPos = [1920/2, 1080/2]; %center of full screen display
        newPos = assignToQuadrant(oldPos, quadrants.(quadrantID));

        % Draw text & blueline
        Screen('DrawText', windowPtr, text, newPos(1), newPos(2),[255, 255, 255]);
    end
    
    % Flip screen after all four quadrants drawn
    Screen('Flip', windowPtr);

    %Check for escape
    [~, ~, keyCode] = KbCheck;
    if keyCode(KbName('Escape'))
        break;
    end

end

% Close window and cleanup
sca;
closePROPixx();
end

%---- HELPER FUNCTIONS
function quadrants = initializeQuadrants()
  % Define quadrant parameters
  quadrants = struct(...
      'Q1', [0, 0], ...       % Top left
      'Q2', [960, 0], ...   % Top right
      'Q3', [0, 540], ...       % Bottom left
      'Q4', [960, 540] );    % Bottom right
end

function newpos = assignToQuadrant(oldpos, quadrantOffset)
    %Helper function to adjust position to quadrant. 
    newpos = [oldpos(1)/2+quadrantOffset(1), oldpos(2)/2+quadrantOffset(2)];
end

function initializePROPixx()
  % All VPixx hardware commands to set the right mode for the PROPixx
  Datapixx('Open');
  Datapixx('SetPropixxDlpSequenceProgram', 2); 
  Datapixx('RegWr');
  Datapixx('EnablePropixxQuad4x3d');
  Datapixx('RegWr');
end

function closePROPixx()
  % Return to defaults and shut down
  Datapixx('SetPropixxDlpSequenceProgram', 0); 
  Datapixx('DisablePropixxQuad4x3d');
  Datapixx('RegWr');
  Datapixx('Close')
end
Python Example
PY
Coming soon!

Top-Bottom Mode

Top-Bottom mode was introduced in software revision 3.9 (July 2021). In this mode, left and right images are stacked on top of one another in a single double-height image (1920 x 2160 @ 60 Hz), which the system deconstructs and presents to each eye sequentially (1920 x 1080 @ 120 Hz).

Time (7).png

Top-bottom mode uses a double-height image and shows the left and right eye content sequentially

This mode was originally designed for use with 3D stimuli designed in Unity and Unreal game engines. However, it may be useful for other high-level stimulus-generation software such as PsychoPy Builder. Because this mode receives left and right eye images on the same video frame from the GPU, there is no risk of de-synchronization of 3D. Occasionally frame drops are not likely to be visible.

To use Top-Bottom mode, you must configure the monitor to accept the 1920 x 2160 resolution using our vputil command-line utility and your display settings.

Configuring your PROPixx for Top-Bottom Mode

First, change the display resolution in our command-line utility, vputil.

  1. Connect the PROPixx to the stimulus computer via USB and video cables. Power on the projector.

  2. In vputil, enter the command edid and hit enter. DATAPixx3 users can enter d3ew instead.

  3. Several resolution and refresh rate options will be presented to you. Select [18] 1920 x 2160 @ 60 Hz and press enter. Repeat this step for the second edid slot. Answer “N” to the NVIDIA question.

  4. Restart your PROPixx.

  5. Verify the correct resolution in your operating system display settings, and ensure scaling is set to 100%.

  6. Restart your experiment software. Some software like MATLAB only check for resolution on startup, and need to be restarted to register a new display resolution.

Note you must repeat these steps with a resolution of 1920 x 1080 @ 120 Hz to return to standard operating mode.

MATLAB example
MATLAB
function PROPixx_TopBottom()
%This demo draws a red and green square in the left and right eye views,
%respectively. 

% Initialize Psychtoolbox window
screenNumber = max(Screen('Screens'));
[windowPtr, ~] = PsychImaging('OpenWindow', screenNumber, 0);
KbName('UnifyKeyNames');

% Initialize stimuli - arbitrarily picked a 120 pixel square in center of
% display. Left eye sees red (top image) and right eye sees green (bottom
% image)
leftEyeCenter = [1920/2,1080/2];
leftEyeBox = [leftEyeCenter(1)-60, leftEyeCenter(2)-60,leftEyeCenter(1)+60, leftEyeCenter(2)+60];
leftEyeCol = [255, 0, 0];

% Right eye is shifted down by 1080 plus blanking interval
rightEyeCenter = [1920/2,1080+1080/2];
rightEyeBox = [rightEyeCenter(1)-60, rightEyeCenter(2)-60, rightEyeCenter(1)+60, rightEyeCenter(2)+60];
rightEyeCol = [0, 255, 0];

% Start presentation loop 
while true
    Screen('FillRect', windowPtr, leftEyeCol, leftEyeBox);
    Screen('FillRect', windowPtr, rightEyeCol, rightEyeBox);
    Screen('Flip', windowPtr);
    
    %Check for escape
    [~, ~, keyCode] = KbCheck;
    if keyCode(KbName('Escape'))
        break;
    end
end

% Close the Psychtoolbox window
sca;
end
PsychoPy example
PY
#This demo draws a red and green square in the left and right eye views,
#respectively. 

from psychopy import core, visual
from psychopy.hardware import keyboard
    
#Begin Experiment
kb = keyboard.Keyboard()
win = visual.Window(
        screen = 1,
        monitor =None, 
        fullscr=True,
        color='black',
        )

# Initialize stimuli in top and bottom halves of display
# Remember in PsychoPy, down/left is negative 

#Center of left eye image
left_eye_center = [0, 0.5]

# Center of right eye image
right_eye_center = [0, -0.5]

# Create squares for both eyes
left_eye_square = visual.Rect(
    win=win,
    size=0.25,
    pos=left_eye_center,
    fillColor='red',
    lineColor='red'
)

right_eye_square = visual.Rect(
    win=win,
    size=0.25,
    pos=right_eye_center,
    fillColor='green',
    lineColor='green'
)

# Main loop to draw the squares
while True:
    left_eye_square.draw()
    right_eye_square.draw()
    win.flip()

    # Check for escape key press
    keys = kb.getKeys()
    if 'escape' in keys:
        break

# Close the PsychoPy window
win.close()
core.quit()
PsychoPy Builder example

Implementing Top-Bottom mode in PsychoPy Builder is straightforward. We recommend creating a new monitor in the monitor center, to ensure the proper resolution is used. Open the Monitor center, and create a new display with the correct 1920 x 2160 resolution:

PsychoPy Monitor Center.png

Monitor center

Next, when creating your stimuli, draw images and text as a single double-height image. Images and text directed at the left eye should be placed in the top half of the image, and those directed at the right eye should be placed in the bottom half.

This is a great opportunity to take advantage of PsychoPy’s normalized spatial units. For example, here is the positioning for text presented in the middle of the left eye image:

image-20240515-201603.png

Text properties

The same text, presented to the right eye, would be positioned at [0, -0.5].

Unity Example

Coming soon!

RB3D Mode

RB3D Mode is a custom PROPixx sequencer that VPixx designed in partnership with the NIH. This mode uses the red and blue colour channels to specify right and left eye images, respectively; the system converts these to greyscale images presented to the right and left eye in a single video frame via a custom subframe DLP sequencer. Each frame will show the left image at half-intensity for 1.2 ms, full intensity right image for 2.4 ms, and then the left image for another 1.2 ms at half-intensity. The rest of the frame is blank. This results in the same exposure time for both eyes, and prevents bias of which image is projected first or last.

Time (10).png

Left and right eye information is drawn in blue and red channels on a single frame. The sequencer shows the images in greyscale according to the timeline above.

RB3D mode enables active management of crosstalk levels and actively eliminates Pulfrich effects. Crosstalk is defined by the following formula

Where the default value is 0.

MATLAB Example
MATLAB
function helloWorldRB3D()

% Initialize PROPixx
initializePROPixx();

% Initialize Psychtoolbox window
PsychDefaultSetup(2);
screenNumber = max(Screen('Screens'));
[windowPtr, rect] = PsychImaging('OpenWindow', screenNumber, 0);

% Initialize stimuli
helloText = 'Hello';
worldText = 'World';

% You can modify the per eye crosstalk here.
Datapixx('SetPropixx3DCrosstalkLR', 0);
Datapixx('SetPropixx3DCrosstalkRL', 0);
Datapixx('RegWr');

% Start loop drawing the message
while true

    %Select red channel while locking others (prevents overwriting colours)
    Screen('BlendFunction', windowPtr, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, [1 0 0 0]);
    DrawFormattedText(windowPtr, helloText, 'center', 'center', [255,0,0]);

    %Select blue channel while locking others (prevents overwriting colours)
    Screen('BlendFunction', windowPtr, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, [0 0 1 0]);
    DrawFormattedText(windowPtr, worldText, 'center', 'center', [0,0,255]);
    Screen('Flip', windowPtr);
    
    [~, ~, keyCode] = KbCheck;
    if keyCode(KbName('Escape'))
        break;
    end
end

% Close the Psychtoolbox window
sca;
closePROPixx();
end

function initializePROPixx()
  % All VPixx hardware commands to set the right mode for the PROPixx
  Datapixx('Open');
  Datapixx('SetPropixxDlpSequenceProgram', 1); 
  Datapixx('RegWr');
end

function closePROPixx()
  % Return to defaults and shut down
  Datapixx('SetPropixxDlpSequenceProgram', 0); 
  Datapixx('RegWr');
  Datapixx('Close');
end
Python Example
PY
Coming soon!

Summary of Different 3D Modes Available with the PROPixx

Description

Maximum Refresh

Best Used For:

Frame-sequential stereo

Temporally interleave left/right eye images

Up to 60 Hz/eye

Simple 3D applications where no frame drops are present

Frame-sequential stereo with Blue Lines

Temporally interleave left/right eye images, blue line used to encode polarization assignment

Up to 240 Hz/eye (in 480 Hz real-time mode)

Any 3D application where video must be robust against frame dropping

Quad4x3D

Four quadrants of display are shown sequentially

240 Hz/eye

High speed displays, robust against frame dropping

Top-Bottom Mode

Left and right eye images stacked in a double-height window; shown sequentially on display

Up to 60 Hz/eye

3D applications where stimuli are created in Unreal or Unity game engines

RB3D Mode

Custom sequencer that presents left and right eye images as greyscale subframes

Up to 120 Hz/eye

Specialized applications where greyscale is appropriate and crosstalk levels are actively controlled

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.