3D Display Demo 1 – Setting up a 3D Display

This demo is where things start getting serious and evolved. We are going to set up our device to display in 3D using ‘blue line’ to synchronise the glasses. The blue line is a single line at the bottom of the display which our device uses to determine if the current frame is the left or right frame and to synchronise our 3D goggles. On a PROPixx projector, there is a polarizing filter for passive 3D and on the VIEWPixx and VIEWPixx /3D, there is an infra-red emitter to synchronise the active 3D glasses.

You will need to open a screen using PTB’s PsychImaging command. To setup the 3D, we call the following functions:

Datapixx('EnableVideoScanningBacklight'); Datapixx('EnableVideoStereoBlueline'); Datapixx('SetVideoStereoVesaWaveform', 2);

If you are using a VIEWPixx /3D, the command Datapixx('EnableVideoLcd3D60Hz'); is required to use the optimized 3D parameters.

Warning

Some systems can move the blue line by themselves, and in these cases we need to modify the blue line parameters (which are handled by PTB):

SetStereoBlueLineSyncParameters(windowPtr, windowRect(4)+10); % on some
SetStereoBlueLineSyncParameters(windowPtr, windowRect(4));  % on others

That parameter might not be needed.

In this demo, we handle the blue lines ourselves so that the above parameter is not required. The blue line is on the last horizontal line and the first quarter of the line represents the left eye, and from the start to 3/4 of the screen represent the right eye. We put the rest of the line black (represented as left and right off).

This 3D stimulus is a square of dots which has a 3D bump moving inside. There is a red border for one eye and green for the other, causing it to appear as yellow. More PTB information on this demo can be found in ImagingStereoDemo.m, that comes with PTB. This demo can also use VPixx devices with a ImagingStereoDemo(1,1) call.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
 function DatapixxImagingStereoDemo()
%
% Taken from the iconic PTB ImagingStereoDemo,
% slightly modified to drive DATAPixx/VIEWPixx/PROPixx.
%
% Press any key to exit demo.
%

% This script calls Psychtoolbox commands available only in OpenGL-based
% versions of the Psychtoolbox. (So far, the OS X Psychtoolbox is the
% only OpenGL-base Psychtoolbox.)  The Psychtoolbox command
% AssertPsychOpenGL will issue
% an error message if someone tries to execute this script on a computer without
% an OpenGL Psychtoolbox
%
% ??            paa     Written
% Oct 29, 2014  dml     Revised

AssertOpenGL;

% Define response key mappings, unify the names of keys across operating
% systems:
KbName('UnifyKeyNames');
space = KbName('space');
escape = KbName('ESCAPE');

% Hardware stereo buffers are a great idea, but they just seem to be broken on so many systems.
% Set to 1 to try it out, or set to 0 to implement software frame-alternate buffers.
useHardwareStereo = 0;

% Get the list of Screens and choose the one with the highest screen number.
% Screen 0 is, by definition, the display with the menu bar. Often when
% two monitors are connected the one without the menu bar is used as
% the stimulus display.  Choosing the display with the highest display number is
% a best guess about where you want the stimulus displayed.
scrnNum = max(Screen('Screens'));

% Increase level of verbosity for debug purposes:
%Screen('Preference', 'Verbosity', 6);
% Screen('Preference', 'SkipSyncTests', 1); % This can be commented out on a well-behaved system.

% Prepare pipeline for configuration. This marks the start of a list of
% requirements/tasks to be met/executed in the pipeline:
PsychImaging('PrepareConfiguration');

% Tell PTB we want to display on a DataPixx device:
PsychImaging('AddTask', 'General', 'UseDataPixx');

% Decrease GPU workload.
% But if we are manually drawing our bluelines, we need to access entire
% display.
if useHardwareStereo
    PsychImaging('AddTask', 'AllViews', 'RestrictProcessing', CenterRect([0 0 512 512], Screen('Rect', scrnNum)));
end

% Enable DATAPixx blueline support, and VIEWPixx scanning backlight for optimal 3D
Datapixx('Open');
if (Datapixx('IsVIEWPixx'))
    Datapixx('EnableVideoScanningBacklight');       % Only required if a VIEWPixx.
end
Datapixx('EnableVideoStereoBlueline');
Datapixx('SetVideoStereoVesaWaveform', 2);      % If driving NVIDIA glasses
% Datapixx('SetVideoStereoVesaWaveform', 0);    % If driving 3rd party emitter

% Liquid crystal displays can exhibit an artifact when presenting 2 static images on alternating video frames, such as with frame-sequencial 3D.
% The origin of this artifact is related to LCD pixel polarity inversion.
% The optical transmission of a liquid crystal cell varies with the magnitude of the voltage applied to the cell.
% Liquid crystal cells are designed to be driven by an AC voltage with little or no DC component.
% As such, the cell drivers alternate the polarity of the cell's driving voltage on alternate video frames.
% The cell will see no net DC driving voltage, as long as the pixel is programmed to the same intensity on even and odd video frames.
% Small differences in a pixel's even and odd frame luminance tend to leave the cell unaffected,
% and large differences in even and odd frame luminance for short periods of time (10-20 frames?) also do not seem to affect the cell;
% however, large differences in luminance for a longer period of time will cause a DC buildup in the pixel's liquid crystal cell.
% This can result in the pixel not showing the programmed luminance correctly,
% and can also cause the pixel to "stick" for several seconds after the image has been removed, causing an after-image on the display.
% VPixx Technologies has developed a strategy for keeping the pixel cells DC balanced.
% Instead of alternating the cell driving voltage on every video frame, we can alternate the voltage only on every second frame.
% This feature is enabled by calling the function EnableVideoLcd3D60Hz.
% Call this routine before presenting static or slowly-moving 3D images, or when presenting 60Hz flickering stimuli.
% Be sure to call DisableVideoLcd3D60Hz afterwards to return to normal pixel driving.
% Note that this feature is only supported on the VIEWPixx/3D when running with a refresh rate of 120Hz.
if Datapixx('IsViewpixx3D')
    Datapixx('EnableVideoLcd3D60Hz');
end

Datapixx('RegWr');

% Consolidate the list of requirements (error checking etc.), open a
% suitable onscreen window and configure the imaging pipeline for that
% window according to our specs. The syntax is the same as for
% Screen('OpenWindow'):
if useHardwareStereo == 1
    [windowPtr, windowRect]=PsychImaging('OpenWindow', scrnNum, 0, [], [], [], 1);
else
    [windowPtr, windowRect]=PsychImaging('OpenWindow', scrnNum, 0);
end

% There seems to be a blueline generation bug on some OpenGL systems.
% SetStereoBlueLineSyncParameters(windowPtr, windowRect(4)) corrects the
% bug on some systems, but breaks on other systems.
% We'll just disable automatic blueline, and manually draw our own bluelines!
if useHardwareStereo == 1
    SetStereoBlueLineSyncParameters(windowPtr, windowRect(4)+10);
end
blueRectLeftOn   = [0,                 windowRect(4)-1, windowRect(3)/4,   windowRect(4)];
blueRectLeftOff  = [windowRect(3)/4,   windowRect(4)-1, windowRect(3),     windowRect(4)];
blueRectRightOn  = [0,                 windowRect(4)-1, windowRect(3)*3/4, windowRect(4)];
blueRectRightOff = [windowRect(3)*3/4, windowRect(4)-1, windowRect(3),     windowRect(4)];

% Stimulus settings:
numDots = 1000;
vel = 1;   % pix/frames
dotSize = 8;
dots = zeros(3, numDots);

xmax = RectWidth(windowRect)/2;
ymax = RectHeight(windowRect)/2;
xmax = min(xmax, ymax) / 2;
ymax = xmax;

f = 4*pi/xmax;
amp = 16;

dots(1, :) = 2*(xmax)*rand(1, numDots) - xmax;
dots(2, :) = 2*(ymax)*rand(1, numDots) - ymax;

% Initially fill left- and right-eye image buffer with black background
% color:
if useHardwareStereo == 1
    Screen('SelectStereoDrawBuffer', windowPtr, 0);
    Screen('FillRect', windowPtr, BlackIndex(scrnNum));
    Screen('SelectStereoDrawBuffer', windowPtr, 1);
    Screen('FillRect', windowPtr, BlackIndex(scrnNum));
    Screen('Flip', windowPtr);
else
    Screen('FillRect', windowPtr, BlackIndex(scrnNum));
    Screen('Flip', windowPtr);
end

% Set up alpha-blending for smooth (anti-aliased) drawing of dots:
Screen('BlendFunction', windowPtr, 'GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA');

col1 = WhiteIndex(scrnNum);
col2 = col1;
i = 1;
keyIsDown = 0;
center = [0 0];
sigma = 50;
xvel = 2*vel*rand(1,1)-vel;
yvel = 2*vel*rand(1,1)-vel;

Screen('Flip', windowPtr);

% Maximum number of animation frames to show:
nmax = 100000;

% Perform a flip to sync us to vbl and take start-timestamp in t:
t = Screen('Flip', windowPtr);

% Run until a key is pressed:
while length(t) < nmax

        % Select left-eye image buffer for drawing:
    if useHardwareStereo == 1
        Screen('SelectStereoDrawBuffer', windowPtr, 0);
    end

    % Draw left stim:
    Screen('DrawDots', windowPtr, dots(1:2, :) + [dots(3, :)/2; zeros(1, numDots)], dotSize, col1, [windowRect(3:4)/2], 1);
    Screen('FrameRect', windowPtr, [255 0 0], [], 5);
    Screen('FillRect', windowPtr, [0, 0, 255], blueRectLeftOn);
    Screen('FillRect', windowPtr, [0, 0, 0], blueRectLeftOff);

        % Select right-eye image buffer for drawing:
    if useHardwareStereo == 1
        Screen('SelectStereoDrawBuffer', windowPtr, 1);
    else
        Screen('DrawingFinished', windowPtr);
        onset = Screen('Flip', windowPtr);
        t = [t onset];
    end

    % Draw right stim:
    Screen('DrawDots', windowPtr, dots(1:2, :) - [dots(3, :)/2; zeros(1, numDots)], dotSize, col2, [windowRect(3:4)/2], 1);
    Screen('FrameRect', windowPtr, [0 255 0], [], 5);
    Screen('FillRect', windowPtr, [0, 0, 255], blueRectRightOn);
    Screen('FillRect', windowPtr, [0, 0, 0], blueRectRightOff);

    % Flip stim to display and take timestamp of stimulus-onset after
    % displaying the new stimulus and record it in vector t:
    Screen('DrawingFinished', windowPtr);
    onset = Screen('Flip', windowPtr);
    t = [t onset];

    % Now all non-drawing tasks:

    % Compute dot positions and offsets for next frame:
    center = center + [xvel yvel];
    if ((center(1) > xmax) | (center(1) < -xmax))
        xvel = -xvel;
    end

    if ((center(2) > ymax) | (center(2) < -ymax))
        yvel = -yvel;
    end
    amp = amp - 0.05;
    dots(3, :) = -amp.*exp(-(dots(1, :) - center(1)).^2 / (2*sigma*sigma)).*exp(-(dots(2, :) - center(2)).^2 / (2*sigma*sigma));

    % Keypress ends demo
    [pressed dummy keycode] = KbCheck;
    if pressed
        break;
    end
end 

% Last Flip:
Screen('Flip', windowPtr);

if Datapixx('IsViewpixx3D')
    Datapixx('DisableVideoLcd3D60Hz');
    Datapixx('RegWr');
end

% Done. Close the onscreen window:
Screen('CloseAll')
Datapixx('Close');

% Compute and show timing statistics:
dt = t(2:end) - t(1:end-1);
disp(sprintf('N.Dots\tMean (s)\tMax (s)\t%%>20ms\t%%>30ms\n'));
disp(sprintf('%d\t%5.3f\t%5.3f\t%5.2f\t%5.2f\n', numDots, mean(dt), max(dt), sum(dt > 0.020)/length(dt), sum(dt > 0.030)/length(dt)));

% We're done.
return;