3
% Written by Peter Scarfe
8
screenNumber = max(Screen('Screens'));
10
% Define black, white and grey
11
white = WhiteIndex(screenNumber);
12
grey = GrayIndex(screenNumber);
13
black = BlackIndex(screenNumber);
16
PsychImaging('PrepareConfiguration');
17
PsychImaging('AddTask', 'General', 'FloatingPoint32Bit');
18
[window, windowRect] = PsychImaging('OpenWindow', screenNumber, grey);
20
% Query the frame duration
21
ifi = Screen('GetFlipInterval', window);
23
% Maximum priority level
24
topPriorityLevel = MaxPriority(window);
26
% Get the centre coordinate of the window
27
[xCenter, yCenter] = RectCenter(windowRect);
37
sigma = gaborDimPix / 6;
44
% Spatial Frequency (Cycles Per Pixel)
45
% One Cycle = Grey-Black-Grey-White-Grey i.e. One Black and One White Lobe
47
freq = numCycles / gaborDimPix;
49
% Build a procedural gabor texture
50
gabortex = CreateProceduralGabor(window, gaborDimPix, gaborDimPix, [], [0.5 0.5 0.5 0.0], 1, 0.5);
52
% Positions of the Gabors
54
[x, y] = meshgrid(-dim:dim, -dim:dim);
56
% Calculate the distance in "Gabor numbers" of each gabor from the center
58
dist = sqrt(x.^2 + y.^2);
60
% Cut out an inner annulus
62
x(dist <= innerDist) = nan;
63
y(dist <= innerDist) = nan;
65
% Cut out an outer annulus
67
x(dist >= outerDist) = nan;
68
y(dist >= outerDist) = nan;
70
% Select only the finite values
74
% Center the annulus coordinates in the centre of the screen
75
xPos = x .* gaborDimPix + xCenter;
76
yPos = y .* gaborDimPix + yCenter;
78
% Count how many Gabors there are
79
nGabors = numel(xPos);
81
% Make the destination rectangles for all the Gabors in the array
82
baseRect = [0 0 gaborDimPix gaborDimPix];
83
allRects = nan(4, nGabors);
85
allRects(:, i) = CenterRectOnPointd(baseRect, xPos(i), yPos(i));
88
% Drift speed for the 2D global motion
90
degPerFrame = degPerSec * ifi;
92
% Randomise the Gabor orientations and determine the drift speeds of each gabor.
93
% This is given by multiplying the global motion speed by the cosine
94
% difference between the global motion direction and the global motion.
95
% Here the global motion direction is 0. So it is just the cosine of the
96
% angle we use. We re-orientate the array when drawing
97
gaborAngles = rand(1, nGabors) .* 180 - 90;
98
degPerFrameGabors = cosd(gaborAngles) .* degPerFrame;
100
% Randomise the phase of the Gabors and make a properties matrix. We could
101
% if we want have each Gabor with different properties in all dimensions.
102
% Not just orientation and drift rate as we are doing here.
103
% This is the power of using procedural textures
104
phaseLine = rand(1, nGabors) .* 360;
105
propertiesMat = repmat([NaN, freq, sigma, contrast, aspectRatio, 0, 0, 0], nGabors, 1);
106
propertiesMat(:, 1) = phaseLine';
108
% Perform initial flip to gray background and sync us to the retrace:
109
vbl = Screen('Flip', window);
111
% Numer of frames to wait before re-drawing
117
% Set the right blend function for drawing the gabors
118
Screen('BlendFunction', window, 'GL_ONE', 'GL_ZERO');
120
% Batch draw all of the Gabors to screen
121
Screen('DrawTextures', window, gabortex, [], allRects, gaborAngles - 90, [], [], [], [],...
122
kPsychDontDoRotation, propertiesMat');
124
% Change the blend function to draw an antialiased fixation point
125
% in the centre of the array
126
Screen('BlendFunction', window, 'GL_SRC_ALPHA', 'GL_ONE_MINUS_SRC_ALPHA');
128
% Draw the fixation point
129
Screen('DrawDots', window, [xCenter; yCenter], 10, black, [], 2);
132
% Flip our drawing to the screen
133
vbl = Screen('Flip', window, vbl + (waitframes - 0.5) * ifi);
135
% Increment the phase of our Gabors
136
phaseLine = phaseLine + degPerFrameGabors;
137
propertiesMat(:, 1) = phaseLine';
141
% Wait for keyboard press