PROPixx Demo 3 – Drawing dots at 1440 Hz with blending

This demo is a simple demonstration of the PROPixx’s 1440 Hz sequencer. We display a single white dot in the center of the screen, refreshing at 1440 Hz. The demo ends when a key is pressed.

The PROPixx uses a “sequencer” to break up a single 1920 x 1080, 120 Hz RGB video signal into 12 multiple frames displayed in sequence. The 1920 x 1080 image is first divided into four 960 x 540 images, or “quadrants”, which are magnified to full screen. Each 8-bit RGB color channel is then converted to grayscale and displayed separately. The order of appearance is Q1 red, Q2 red, Q3 red, Q4 red, and then this order is repeated for G and B channels.

_images/Quad12xStill.png

To create our stimuli, we determine target locations as if they were full resolution, full screen. The helper script convertToQuadrant reassigns and rescales the target positon to the correct quadrant. Depending on the frame, we draw our targets as either full red, full green or full blue.

Because our targets overlap in position, we use blending to ensure that red, green and blue colors are correctly added to the image. Without blending, the image will only contain the last color drawn in that position (blue) and our dot will only appear on 4/12 frames. This is often visually apparent in 1440 Hz as a drop in luminance.

To blend different color textures, we use Psychtoolbox’s Screen 'BlendFunction' and specify which channel [R, G, B, alpha] we want to draw to. Channels set to 0 will be preserved/blended instead of overwritten.

A screen running in 120 Hz mode will display our four blended dots on the same frame and in the correct quadrants.

_images/1440HzDemo2FullHD.gif

The PROPixx in 1440 Hz mode will display red, green and blue, quadrant by quadrant. The image will be full screen, grayscale:

_images/1440HzDemo2.gif

Drawing twelve full R, G, B dots and blending them is functionally equivalent to drawing four white dots. That is, if you have overlapping stimuli with the same characteristics, you can opt to draw them as a single stimulus and populate more than one color channel when defining the texture. The sequencer will behave the same way in either case, drawing the different color channels in different frames. In this demo, we draw the colored dots individually in order to demonstrate blending.

  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
function PPxDraw1440HzDots2
%This function draws a central dot refreshing at 1440Hz. It demonstrates how to
%use blending to ensure textures drawn to different colour channels are
%integrated properly in the final 1440Hz display

% First, we construct a single 1920 x 1080 RGB image, which is passed to
% the PROPixx. The sequencer breaks the image down and shows the quadrants
% and colour channels as 12 individual frames. These frames are 960 x 540
% resolution, shown full screen:
%_______________________________
%|             |                |           
%|     Q1      |       Q2       | 
%|             |                | 
%|_____________|________________|   
%|             |                |         
%|     Q3      |       Q4       |          
%|             |                |          
%|_____________|________________|          

%Quadrants are shown FULL SCREEN, GREYSCALE in the order: 
% Quadrant 1 red channel
% Quadrant 2 red channel
% Quadrant 3 red channel
% Quadrant 4 red channel
% Quadrant 1 green channel
% Quadrant 2 green channel
% Quadrant 3 green channel
% Quadrant 4 green channel
% Quadrant 1 blue channel
% Quadrant 2 blue channel
% Quadrant 3 blue channel
% Quadrant 4 blue channel

%In this demo we draw dots as normal. The helper function 'convertToQuadrant'
%reassigns the positon of the dot (based on full display) to the correct
%quadrant.

%The dot is always in the center of the display. To prevent colours from
%overwriting each other, we use blending to add the colour maps together as
%they are drawn. Drawing three dots (1=R, 2=G and 3=B) in a quadrant and
%blending is functionally equivalent to drawing a single dot with colour =
%[R, G, B] in each quadrant. For example, if we draw a [255, 255, 255] dot 
%in the top left quadrant, this would produce white dots on frame 1, 5 and
%10. Here, we draw the dots separately in each colour channel to make the
%process a bit more transparent and demonstrate how to use blending.

% 2020 Mar 27       lef     written

%Check connection and open Datapixx if it's not open yet
isConnected = Datapixx('isReady');
if ~isConnected
    Datapixx('Open');
end

Datapixx('SetPropixxDlpSequenceProgram', 5);        %Set Propixx to 1440Hz refresh (also known as Quad12x)
Datapixx('RegWrRd');                                %Push command to device register

%Open a display on the Propixx
AssertOpenGL;
KbName('UnifyKeyNames')
Screen('Preference', 'SkipSyncTests', 1);
screenID = 2;                                        %Change this value to change display
[windowPtr,rect] = Screen('OpenWindow', screenID, 0);

%Set up some stimulus characteristics
dotRadius = 15;
center = [rect(3)/2, rect(4)/2];
   
%Start drawing dots 
while 1 
    
    %DRAW RED
    Screen('BlendFunction', windowPtr, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, [1 0 0 0]);
    colour = [255,0,0];
    for k=1:4
        quadrant = k;
        [x,y] = convertToQuadrant(center, rect, quadrant); 
        Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    end
    

    %DRAW GREEN
    Screen('BlendFunction', windowPtr, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, [0 1 0 0]);
    colour = [0,255,0];
    for k=1:4
        quadrant = k;
        [x,y] = convertToQuadrant(center, rect, quadrant); 
        Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    end
    
    
    %DRAW BLUE
    Screen('BlendFunction', windowPtr, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, [0 0 1 0]);
    colour = [0,0,255];
    for k=1:4
        quadrant = k;
        [x,y] = convertToQuadrant(center, rect, quadrant); 
        Screen('FillOval', windowPtr, colour, [x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius]);
    end

    
    %Now that we have drawn content to all 12 frames, it is time to flip.
    %The graphics card sends this as a single 1920 x 1080 image at 120 Hz,
    %which the sequencer breaks down into the 12 frames, presented in the
    %order they were drawn.
    Screen('Flip',windowPtr);

    
    %Keypress to exit
    [keyIsDown, ~, ~, ~] = KbCheck;
    if keyIsDown
        break
    end 
end
        
Screen('Closeall');
Datapixx('SetPropixxDlpSequenceProgram', 0);         %Revert to standard 120Hz refresh rate
Datapixx('RegWrRd');
Datapixx('Close');

end

function [x,y] = convertToQuadrant(position, displaySize, quad)
%This scales an x, y position into a specific quadrant of the screen    
scale = 0.5;

switch quad
    case 1; xOffset = 0; yOffset = 0;
    case 2; xOffset = displaySize(3)/2; yOffset = 0; 
    case 3; xOffset = 0; yOffset = displaySize(4)/2;
    case 4; xOffset = displaySize(3)/2; yOffset = displaySize(4)/2;
end
    
x = (position(1)*scale)+xOffset;
y = (position(2)*scale)+yOffset;

end