/usr/share/psychtoolbox-3/PsychDemos/ProceduralGarboriumDemo.m is in psychtoolbox-3-common 3.0.11.20131230.dfsg1-1build1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | function ProceduralGarboriumDemo(ngabors)
% ProceduralGarboriumDemo([ngabors=200]) -- An aquarium full of cute little procedural gabors!
%
% This demo shows how to use the Screen('DrawTextures') command to draw a
% large number of similar images quickly - in this case, Gabor patches of
% different position, size and orientation. It also shows how to achieve
% fast, mathematically correct, linear superposition of image patches by
% use of alpha blending and the Psychtoolbox imaging pipeline. While you
% could always achieve proper superposition by precomputing large images in
% Matlab, then drawing them as textures, the use of alpha blending allows
% to offload the computations to your graphics hardware - this allows
% speedups by factors of more than 100x in some cases!
%
% Furthermore the demo demonstrates how to use procedural shading to
% compute gabor patches on-the-fly instead of precomputing a prototypical
% gabor patch image within Matlab. The command CreateProceduralGabor()
% allows to generate such a procedural gabor and then draw it with the
% normal 'DrawTexture' or 'DrawTextures' texture drawing commands. However,
% this gabor patch is not a image like a normal texture. Instead it is
% computed during each draw operation by a so called GLSL shader program --
% the gabor formula is evaluated on the fly inside your fast graphics card.
%
% This method has a few advantages over the standard texture based method
% (as demonstrated in GarboriumDemo):
%
% - GPU's are extremely fast at this kind of jobs, so the method is
% significantly faster on modern GPU's, especially for large patches,
% allowing for even higher redraw rates.
%
% - As the formula is evaluated on the fly for each output pixel, there are
% no resampling artifacts, regardless of size of your gabor.
%
% - You can change all interesting stimulus parameters on the fly -- change
% contrast, aspect ratio, spatial constant, frequency, phase, orientation
% etc. for each patch during each redraw cycle without the need to
% recompute any matrices and without any speed penalty.
%
% The downsides of this method are the need for recent graphics hardware
% (GPU's) and the fact that the stimulus definition formula needs to be
% implemented as a shader program in the GLSL language, which is less easy
% to use - and less forgiving of programming errors - than the simple and
% easy Matlab language. This means that you're either restricted to our
% predifined set of primitives, or you'll have some steep learning curve.
% Currently PTB provides gabors and (via CreateProceduralSineGrating())
% sine gratings.
%
% The demo shows "an aquarium" of many cute little gabor patches, each moving
% into a random direction. Sometimes the gabors intersect, in that case
% you'll see a linear superposition of them. The gabors also shift phase
% and pulse (aspect ratio modulation) to make them more life-like -- and to
% demonstrate runtime change of interesting stimulus parameters, of course.
%
% Each frame of the animation contains 'ngabors' patches, ngabors defaults
% to 200. Change the number as first optional parameter 'ngabors' if you want
% to exercise your graphics hardware and cpu.
%
% You can exit the demo by pressing any key on the keyboard.
%
% This demo needs recent graphics hardware with floating point framebuffer
% support: ATI Radeon X1000 and later, NVidia GeForce 6000 and later.
% History:
% 07/08/2007 Written (MK).
% 05/18/2008 Rewritten, beautified, adapted to current PTB (MK).
% PTB-3 correctly installed and functional? Abort otherwise.
AssertOpenGL;
% Set number of gabor patches to 200 if no number provided:
if nargin < 1 || isempty(ngabors)
ngabors = 200;
end
fprintf('Will draw %i gabor patches per frame.\n', ngabors);
% Select screen with maximum id for output window:
screenid = max(Screen('Screens'));
% Open a fullscreen, onscreen window with gray background. Enable 32bpc
% floating point framebuffer via imaging pipeline on it, if this is possible
% on your hardware while alpha-blending is enabled. Otherwise use a 16bpc
% precision framebuffer together with alpha-blending. We need alpha-blending
% here to implement the nice superposition of overlapping gabors. The demo will
% abort if your graphics hardware is not capable of any of this.
PsychImaging('PrepareConfiguration');
PsychImaging('AddTask', 'General', 'FloatingPoint32BitIfPossible');
[win winRect] = PsychImaging('OpenWindow', screenid, 128);
% Retrieve size of window in pixels, need it later to make sure that our
% moving gabors don't move out of the visible screen area:
[w, h] = RectSize(winRect);
% Query frame duration: We use it later on to time 'Flips' properly for an
% animation with constant framerate:
ifi = Screen('GetFlipInterval', win);
% Enable alpha-blending, set it to a blend equation useable for linear
% superposition with alpha-weighted source. This allows to linearly
% superimpose gabor patches in the mathematically correct manner, should
% they overlap. Alpha-weighted source means: The 'globalAlpha' parameter in
% the 'DrawTextures' can be used to modulate the intensity of each pixel of
% the drawn patch before it is superimposed to the framebuffer image, ie.,
% it allows to specify a global per-patch contrast value:
Screen('BlendFunction', win, GL_ONE, GL_ONE);
% Define prototypical gabor patch of 65 x 65 pixels default size: si is
% half the wanted size. Later on, the 'DrawTextures' command will simply
% scale this patch up and down to draw individual patches of the different
% wanted sizes:
si = 32;
% Size of support in pixels, derived from si:
tw = 2*si+1;
th = 2*si+1;
% Initial parameters of gabors:
% Phase of underlying sine grating in degrees:
phase = 0;
% Spatial constant of the exponential "hull"
sc = 10.0;
% Frequency of sine grating:
freq = .05;
% Contrast of grating:
contrast = 10.0;
% Aspect ratio width vs. height:
aspectratio = 1.0;
% Initialize matrix with spec for all 'ngabors' patches to start off
% identically:
mypars = repmat([phase+180, freq, sc, contrast, aspectratio, 0, 0, 0]', 1, ngabors);
% Build a procedural gabor texture for a gabor with a support of tw x th
% pixels and the 'nonsymetric' flag set to 1 == Gabor shall allow runtime
% change of aspect-ratio:
gabortex = CreateProceduralGabor(win, tw, th, 1);
% Draw the gabor once, just to make sure the gfx-hardware is ready for the
% benchmark run below and doesn't do one time setup work inside the
% benchmark loop. The flag 'kPsychDontDoRotation' tells 'DrawTexture' not
% to apply its built-in texture rotation code for rotation, but just pass
% the rotation angle to the 'gabortex' shader -- it will implement its own
% rotation code, optimized for its purpose. Additional stimulus parameters
% like phase, sc, etc. are passed as 'auxParameters' vector to
% 'DrawTexture', this vector is just passed along to the shader. For
% technical reasons this vector must always contain a multiple of 4
% elements, so we pad with three zero elements at the end to get 8
% elements.
Screen('DrawTexture', win, gabortex, [], [], [], [], [], [], [], kPsychDontDoRotation, [phase, freq, sc, contrast, aspectratio, 0, 0, 0]);
% Preallocate array with destination rectangles:
% This also defines initial gabor patch orientations, scales and location
% for the very first drawn stimulus frame:
texrect = Screen('Rect', gabortex);
inrect = repmat(texrect', 1, ngabors);
dstRects = zeros(4, ngabors);
for i=1:ngabors
scale(i) = 1*(0.1 + 0.9 * randn);
dstRects(:, i) = CenterRectOnPoint(texrect * scale(i), rand * w, rand * h)';
end
% Preallocate array with rotation angles:
rotAngles = rand(1, ngabors) * 360;
% Initially sync us to VBL at start of animation loop.
vbl = Screen('Flip', win);
tstart = vbl;
count = 0;
% Animation loop: Run until any keypress:
while ~KbCheck
% Step one: Batch-Draw all gabor patches at the positions and
% orientations and with the stimulus parameters 'mypars',
% computed during last loop iteration:
Screen('DrawTextures', win, gabortex, [], dstRects, rotAngles, [], [], [], [], kPsychDontDoRotation, mypars);
% Mark drawing ops as finished, so the GPU can do its drawing job while
% we can compute updated parameters for next animation frame. This
% command is not strictly needed, but it may give a slight additional
% speedup, because the CPU can compute new stimulus parameters in
% Matlab, while the GPU is drawing the stimuli for this frame.
% Sometimes it helps more, sometimes less, or not at all, depending on
% your system and code, but it only seldomly hurts.
% performance...
Screen('DrawingFinished', win);
% Compute updated positions and orientations for next frame. This code
% is vectorized, but could be probably optimized even more. Indeed,
% these few lines of Matlab code are the main speed-bottleneck for this
% demos animation loop on modern graphics hardware, not the actual drawing
% of the stimulus. The demo as written here is CPU bound - limited in
% speed by the speed of your main processor.
% Compute new random orientation for each patch in next frame:
rotAngles = rotAngles + 1 * randn(1, ngabors);
% Increment phase-shift of each gabor by 10 deg. per redraw:
mypars(1,:) = mypars(1,:) + 10;
% "Pulse" the aspect-ratio of each gabor with a sine-wave timecourse:
mypars(5,:) = 1.0 + 0.25 * sin(count*0.1);
% Compute centers of all patches, then shift them in new direction of
% motion 'rotAngles', use the mod() operator to make sure they don't
% leave the window display area. Its important to use RectCenterd and
% CenterRectOnPointd instead of RectCenter and CenterRectOnPoint,
% because the latter ones would round all results to integral pixel
% locations, which would make for an odd and jerky movement. It is
% also important to feed all matrices and vectors in proper format, as
% these routines are internally vectorized for higher speed.
[x y] = RectCenterd(dstRects);
x = mod(x + 0.33 * cos(rotAngles/360*2*pi), w);
y = mod(y - 0.33 * sin(rotAngles/360*2*pi), h);
% Recompute dstRects destination rectangles for each patch, given the
% 'per gabor' scale and new center location (x,y):
dstRects = CenterRectOnPointd(inrect .* repmat(scale,4,1), x, y);
% Done. Flip one video refresh after the last 'Flip', ie. try to
% update the display every video refresh cycle if you can.
% This is the same as Screen('Flip', win);
% but the provided explicit 'when' deadline allows PTB's internal
% frame-skip detector to work more accurately and give a more
% meaningful report of missed deadlines at the end of the script. Not
% important for this demo, but here just in case you didn't know ;-)
vbl = Screen('Flip', win, vbl + 0.5 * ifi);
% Next loop iteration...
count = count + 1;
end
% Done. Last flip to take end timestamp and for stimulus offset:
vbl = Screen('Flip', win);
% Print the stats:
count
avgfps = count / (vbl - tstart)
% Close onscreen window, release all ressources:
Screen('CloseAll');
% Done.
return;
% Little helper function: Converts degrees to radians, included for
% backward compatibility with old Matlab versions:
function radians = deg2rad(degrees)
radians = degrees * pi / 180;
return
|