problem making circular aperture across any monitor

Hi,

I am trying to present anaglyph gratings through a circular apperture subtending 3 degrees of the visual angle on any conventional monitor. The general idea is that no matter which monitor I use, the apperture will always subtend ~3 degree of visual angle both veritcally and horizontally (from 3m).

I've tried manipulating the Driftdemos with much success. However no matter what I do the gratings are presented in a oval apperture, in a way that matches the dimensions of the screen (horizontally stretched).

Could someone guide me in the right direction? I'm sort of at a loss here. Does it have to do with the aspect ratio or am I totally off track?

My code is split into 2 m-files: visualangle.m and high_strength_AOC_anaglyphs_v4.m (run this file):

visualangle.m
function [pixelWidth pixelHeight spatialFrequencyPixelWidth spatialFrequencyPixelHeight] = visualangle (visualAngleWidth, visualAngleHeight, spatialFrequency, resolutionWidth, resolutionHeight, screenWidth, screenHeight, observerdistance)
% visualAngleWidth: horizontal visual angle of grating
% visualAngleHeight: vertical visual angle of grating
% spatialFrequency: spatial frequency
% observerdistance: distance between eyes and monitor
% resolutionWidth: horizontal resolution
% resolutionHeight: vertical resolution
% screenWidth: width of screen (needs to be adjusted for different screens)
% screenHeight: height of screen (needs to be adjusted for different screens)

% type monitor we are using
% 1 AOC:              1920x1080     509.2x286.4mm
% 2 True3Di 19 inch:  1280x1024     376.32x301.056mm
% 3 True3Di 22 inch:  1280x1024     473.76x296.1mm
monitorType = 1;

if nargin > 8 || nargin < 8
    visualAngleWidth = 1.5;
    visualAngleHeight = 1.5;
    spatialFrequency = 8;
    observerdistance = 3000;
    if monitorType == 1
        resolutionWidth = 1920;
        resolutionHeight = 1080;
        screenWidth = 509.2;
        screenHeight = 286.4;
    elseif monitorType == 2
        resolutionWidth = 1280;
        resolutionHeight = 1024;
        screenWidth = 376.32;
        screenHeight = 301.056;
    elseif monitorType == 3
        resolutionWidth = 1280;
        resolutionHeight = 1024;
        screenWidth = 473.76;
        screenHeight = 296.1;
    end
end

pixelWidth = round(tan(degtorad(visualAngleWidth/2))*2*observerdistance*resolutionWidth/screenWidth);
pixelHeight = round(tan(degtorad(visualAngleHeight/2))*2*observerdistance*resolutionHeight/screenHeight);

spatialFrequencyPixelWidth = spatialFrequency/(pixelWidth*2);
spatialFrequencyPixelHeight = spatialFrequency/(pixelHeight*2);


high_strength_AOC_anaglyphs_v4.m
function high_strength_AOC_anaglyphs_v4

%%%%%%%%%%%%%%%%%%%
% Observer parameters
% inches from screen = 118.11
%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%
% Screen parameters

% resolution (22') = 1680 x 1024
% active display size (22') = 473.76(h) x 296.1(w)

% resolution (19') = 1280 x 1024
% active display size (19') = 376.32(h) x 301.056(w)
%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%
% Stimuli paramters

% aperture size (visual angle) = 1.5 deg
% grating pixel size (22') = 278.526 (width) & 271.629 (height)
% grating pixel size (19') = 267.158 (width & height)
%%%%%%%%%%%%%%%%%%%

% high strength: s.f. = 8 cycles/deg; drift speed = 4 cycles/sec
% low strength: s.f. = 4 cycles/deg; drift speed = 0 cycles/sec

Screen('Preference', 'SkipSyncTests', 1);

try
    % 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
    AssertOpenGL;
   
    % 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.  Chosing the display with the highest dislay number is
    % a best guess about where you want the stimulus displayed. 
    screens=Screen('Screens');
    screenNumber=0;%max(screens);

    % Below values are the predefined parameters of the stimulus
    visualAngleWidth = 1.5;
    visualAngleHeight = 1.5;
    spatialFrequency = 8;
    observerdistance = 3000;

    [pixelWidth pixelHeight spatialFrequencyPixelWidth spatialFrequencyPixelHeight] = visualangle;

    gratingsize = 534; %534 is double of 267.158 roundedup, 534.316 is double of 267.158. By default the visible grating is 400 pixels by 400 pixels in size:

    texsize=gratingsize / 2;% Define Half-Size of the grating image (i.e. radius).
    texsize_horiz = pixelWidth;
    texsize_vert = pixelHeight;

    f=8/gratingsize; % Set at 0.015. Grating frequency in cycles/pixel: By default 0.05 cycles per pixel.
    f_horiz=spatialFrequencyPixelWidth; % red
    f_vert=spatialFrequencyPixelHeight; % blue

    cyclespersecond = 4;% Speed of grating in cycles per second: 1 cycle per second by default.

   
    % Find the color values which correspond to white and black: Usually
    % black is always 0 and white 255, but this rule is not true if one of
    % the high precision framebuffer modes is enabled via the
    % PsychImaging() commmand, so we query the true values via the
    % functions WhiteIndex and BlackIndex:
    white=WhiteIndex(screenNumber);
    black=BlackIndex(screenNumber);
   
    % Round gray to integral number, to avoid roundoff artifacts with some
    % graphics cards:
    gray=round((white+black)/2);

    % This makes sure that on floating point framebuffers we still get a
    % well defined gray. It isn't strictly neccessary in this demo:
    if gray == white
        gray=white / 2;
    end
   
    % Contrast 'inc'rement range for given white and gray values:
    inc=white-gray;

    % Open a double buffered fullscreen window and set default background
    % color to black:
    %[w screenRect]=Screen('OpenWindow',screenNumber, black);
    PsychImaging('PrepareConfiguration');
    [w screenRect]=PsychImaging('OpenWindow', screenNumber, 0, [], [], [], 8);
   
    %Obtain width & height of screen
    [width, height] = WindowSize(w);
   
        SetAnaglyphStereoParameters('LeftGains', w, [0.4 0.0 0.0]);
        SetAnaglyphStereoParameters('RightGains', w, [0.0 0.2 0.7]);

        % Initially fill left- and right-eye image buffer with black background
        % color:
        Screen('SelectStereoDrawBuffer', w, 0);
        Screen('FillRect', w, BlackIndex(screenNumber));
        Screen('SelectStereoDrawBuffer', w, 1);
        Screen('FillRect', w, BlackIndex(screenNumber));
       
    % Enable alpha blending for proper combination of the gaussian aperture
    % with the drifting sine grating:
    Screen('BlendFunction', w, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   
    % Calculate parameters of the grating:
    %
    % First we compute pixels per cycle, rounded up to full pixels, as we
    % need this to create a grating of proper size below:
    %p=ceil(1/f)
    p_vert=ceil(1/f_vert)
    p_horiz=ceil(1/f_horiz) 
      
    % Also need frequency in radians:
    %fr=f*2*pi;
    fr_horiz=f_horiz*2*pi;
    fr_vert=f_vert*2*pi;
   
    % This is the visible size of the grating. It is twice the half-width
    % of the texture plus one pixel to make sure it has an odd number of
    % pixels and is therefore symmetric around the center of the texture:
    %visiblesize=2*texsize+1
    visiblesize_horiz=2*texsize_horiz+1
    visiblesize_vert=2*texsize_vert+1
   
    % Create one single static grating image:
    %
    % We only need a texture with a single row of pixels(i.e. 1 pixel in height) to
    % define the whole grating! If the 'srcRect' in the 'Drawtexture' call
    % below is "higher" than that (i.e. visibleSize >> 1), the GPU will
    % automatically replicate pixel rows. This 1 pixel height saves memory
    % and memory bandwith, ie. it is potentially faster on some GPUs.
    %
    % However it does need 2 * texsize + p columns, i.e. the visible size
    % of the grating extended by the length of 1 period (repetition) of the
    % sine-wave in pixels 'p':
    %x = meshgrid(-texsize:texsize + p, 1);
    x_horiz = meshgrid(-texsize_horiz:texsize_horiz + p_horiz, 1);
    x_vert = meshgrid(-texsize_vert:texsize_vert + p_vert, 1);
   
    % Compute actual cosine grating:
    %grating=gray + inc*cos(fr*x);
   
    % Compute actual square-wave grating:
    %grating=gray + inc*floor(cos(fr*x+1)); %gray + inc*cos(fr*x);
    grating_horiz=gray + inc*floor(cos(fr_horiz*x_horiz+1)); %gray + inc*cos(fr*x);
    grating_vert=gray + inc*floor(cos(fr_vert*x_vert+1)); %gray + inc*cos(fr*x);

    % Store 1-D single row grating in texture:
    %gratingtex=Screen('MakeTexture', w, grating);
    gratingtex_horiz=Screen('MakeTexture', w, grating_horiz);
    gratingtex_vert=Screen('MakeTexture', w, grating_vert);

    % Create a single gaussian transparency mask and store it to a texture:
    % The mask must have the same size as the visible size of the grating
    % to fully cover it. Here we must define it in 2 dimensions and can't
    % get easily away with one single row of pixels.
    %
    % We create a  two-layer texture: One unused luminance channel which we
    % just fill with the same color as the background color of the screen
    % 'gray'. The transparency (aka alpha) channel is filled with a
    % gaussian (exp()) aperture mask:
    %mask=ones(2*texsize+1, 2*texsize+1, 2) * gray;
    %[x,y]=meshgrid(-1*texsize:1*texsize,-1*texsize:1*texsize);
    %mask(:, :, 2)=0;%white * (1 - exp(-((x/90).^2)-((y/90).^2)));
    %masktex=Screen('MakeTexture', w, mask);
        
    mask_horiz=ones(2*texsize_horiz+1, 2*texsize_horiz+1, 2) * gray;
    [x,y]=meshgrid(-1*texsize_horiz:1*texsize_horiz,-1*texsize_horiz:1*texsize_horiz);
    mask_horiz(:, :, 2)=0;
    masktex_horiz=Screen('MakeTexture', w, mask_horiz);
   
    mask_vert=ones(2*texsize_vert+1, 2*texsize_vert+1, 2) * gray;
    [x,y]=meshgrid(-1*texsize_vert:1*texsize_vert,-1*texsize_vert:1*texsize_vert);
    mask_vert(:, :, 2)=0;
    masktex_vert=Screen('MakeTexture', w, mask_vert);
   
    % Query maximum useable priorityLevel on this system:
    priorityLevel=MaxPriority(w) %priority set to 1

    % We don't use Priority() in order to not accidentally overload older
    % machines that can't handle a redraw every 40 ms. If your machine is
    % fast enough, uncomment this to get more accurate timing.
    Priority(priorityLevel);
   
    % Definition of the drawn rectangle on the screen:
    % Compute it to  be the visible size of the grating, centered on the
    % screen:
    dstRect=[0 0 visiblesize_vert visiblesize_horiz];
    dstRect=CenterRect(dstRect, screenRect);

    % Query duration of one monitor refresh interval:
    ifi=Screen('GetFlipInterval', w);
   
    % Translate that into the amount of seconds to wait between screen
    % redraws/updates:
   
    % waitframes = 1 means: Redraw every monitor refresh. If your GPU is
    % not fast enough to do this, you can increment this to only redraw
    % every n'th refresh. All animation paramters will adapt to still
    % provide the proper grating. However, if you have a fine grating
    % drifting at a high speed, the refresh rate must exceed that
    % "effective" grating speed to avoid aliasing artifacts in time, i.e.,
    % to make sure to satisfy the constraints of the sampling theorem
    % (See Wikipedia: "Nyquist?Shannon sampling theorem" for a starter, if
    % you don't know what this means):
    waitframes = 1;
   
    % Translate frames into seconds for screen update interval:
    waitduration = waitframes * ifi;
   
    % Recompute p, this time without the ceil() operation from above.
    % Otherwise we will get wrong drift speed due to rounding errors!
    p=1/f;  % pixels/cycle   
    p_horiz=1/f_horiz;
    p_vert=1/f_vert;
   
    % Translate requested speed of the grating (in cycles per second) into
    % a shift value in "pixels per frame", for given waitduration: This is
    % the amount of pixels to shift our srcRect "aperture" in horizontal
    % directionat each redraw:
    %shiftperframe= cyclespersecond * p * waitduration;
    shiftperframe_horiz= cyclespersecond * p_horiz * waitduration;
    shiftperframe_vert= cyclespersecond * p_vert * waitduration;
   
    % Perform initial Flip to sync us to the VBL and for getting an initial
    % VBL-Timestamp as timing baseline for our redraw loop:
    vbl=Screen('Flip', w);

   
    %destinationRect for the 2 gratings
    %dst1Rect = [(width/2) - (texsize/2), (height/2) - (texsize/2), (width/2) + (texsize/2), (height/2) + (texsize/2)];
    dstRect = [(width/2) - (texsize_horiz/2), (height/2) - (texsize_vert/2), (width/2) + (texsize_horiz/2), (height/2) + (texsize_vert/2)];
    dstRect_rect = [(width/2) - (texsize/2)-(width/2), (height/2) - (texsize/2)-(width/2), (width/2) + (texsize/2)+(width/2), (height/2) + (texsize/2)+(width/2)];

    %Set index for drifting
    i=0;
   
    % Our framecounter, we love stats ;-)
    fcount = 0;
   
    hidecursor;
   
    % Animationloop:
    while ~KbCheck %(vbl < vblendtime)
        Screen('SelectStereoDrawBuffer', w, 0);
               
        % Shift the grating by "shiftperframe" pixels per frame:
        % the mod'ulo operation makes sure that our "aperture" will snap
        % back to the beginning of the grating, once the border is reached.
        % Fractional values of 'xoffset' are fine here. The GPU will
        % perform proper interpolation of color values in the grating
        % texture image to draw a grating that corresponds as closely as
        % technical possible to that fractional 'xoffset'. GPU's use
        % bilinear interpolation whose accuracy depends on the GPU at hand.
        % Consumer ATI hardware usually resolves 1/64 of a pixel, whereas
        % consumer NVidia hardware usually resolves 1/256 of a pixel. You
        % can run the script "DriftTexturePrecisionTest" to test your
        % hardware...
        %xoffset = mod(i*shiftperframe,p);
        xoffset_horiz = mod(i*shiftperframe_horiz,p_horiz);
        xoffset_vert = mod(i*shiftperframe_vert,p_vert);

        i=i+1;
       
        % Define shifted srcRect that cuts out the properly shifted rectangular
        % area from the texture: We cut out the range 0 to visiblesize in
        % the vertical direction although the texture is only 1 pixel in
        % height! This works because the hardware will automatically
        % replicate pixels in one dimension if we exceed the real borders
        % of the stored texture. This allows us to save storage space here,
        % as our 2-D grating is essentially only defined in 1-D:
        %srcRect=[xoffset 0 xoffset + visiblesize_vert visiblesize_horiz];
        srcRect_horiz=[xoffset_horiz 0 xoffset_horiz + visiblesize_vert visiblesize_horiz];
        srcRect_vert=[xoffset_vert 0 xoffset_vert + visiblesize_vert visiblesize_horiz];
       
        % Fill circular 'dstRect' region with an alpha value of 255:
        %Screen('FillOval', w, [0 0 0 255], dstRect);
       
        Screen('SelectStereoDrawBuffer', w, 0);
        Screen('Blendfunction', w, GL_ONE, GL_ZERO, [0 0 0 1]);
        Screen('FillRect', w, [0 0 0 0], dstRect_rect);
        Screen('FillOval', w, [0 0 0 255], dstRect);
        Screen('Blendfunction', w, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, [1 1 1 1]);
        %Screen('DrawTexture', w, gratingtex, srcRect, dst1Rect, 180, [], [], [255 255 255]); %draw left vertical grating
        Screen('DrawTexture', w, gratingtex_horiz, srcRect_horiz, dstRect, 180, [], [], [255 255 255]); %draw left vertical grating
           
        Screen('SelectStereoDrawBuffer', w, 1);
        Screen('Blendfunction', w, GL_ONE, GL_ZERO, [0 0 0 1]);
        Screen('FillRect', w, [0 0 0 0], dstRect_rect);
        Screen('FillOval', w, [0 0 0 255], dstRect);
        Screen('Blendfunction', w, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, [1 1 1 1]);
        %Screen('DrawTexture', w, gratingtex, srcRect, dst1Rect, 270, [], [], [255 255 255]); %draw right horizontal grating
        Screen('DrawTexture', w, gratingtex_vert, srcRect_vert, dstRect, 270, [], [], [255 255 255]); %draw right horizontal grating
               
        %if drawmask==1
            % Draw gaussian mask over grating:
            %Screen('DrawTexture', w, masktex, [0 0 visiblesize visiblesize], dstRect, angle);
        %end;

        % Restore alpha blending mode for next draw iteration:
        Screen('Blendfunction', w, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       
        % Flip 'waitframes' monitor refresh intervals after last redraw.
        % Providing this 'when' timestamp allows for optimal timing
        % precision in stimulus onset, a stable animation framerate and at
        % the same time allows the built-in "skipped frames" detector to
        % work optimally and report skipped frames due to hardware
        % overload:
        vbl = Screen('Flip', w, vbl + (waitframes - 0.5) * ifi);
       
        fcount = fcount + 1;
       
        % Abort demo if any key is pressed:
        %if KbCheck
            %break;
        %end;
    end;
   
    % Restore normal priority scheduling in case something else was set
    % before:
    Priority(0);
   
    showcursor;
   
    %The same commands wich close onscreen and offscreen windows also close
    %textures.
    Screen('CloseAll');

catch
    %this "catch" section executes in case of an error in the "try" section
    %above.  Importantly, it closes the onscreen window if its open.
    %Screen('CloseAll');
    Priority(0);
    psychrethrow(psychlasterror);
end %try..catch..

% We're done!
return;

I haven't looked through all your code, but this should be simple to do.

Assuming you are working on a spatially calibrated CRT of a LCD, you would simply need to work out how many pixels there are in a degree and how many degrees you want your aperture to be. So it would just be a multiplication of those two numbers.

Have you checked what the numbers are you are feeding into your stimulus immediately prior to drawing? Are the numbers correct and the stimulus wrong?

Are you using a single monitor setup or dual monitors?

Maybe try drawing something very simple, e.g. a filled square, with the same numbers. See if that works.

Also, if you are using textures and srcRects and dstRects make sure your srcRects and dstRects are the same size, if they are not PTB might stretch your texture to fit the dstRect, which may result in distortion.




--- In psychtoolbox@yahoogroups.com, "philliplaw22" <philliplaw22@...> wrote:
>
> Hi,
>
> I am trying to present anaglyph gratings through a circular apperture
> subtending 3 degrees of the visual angle on any conventional monitor.
> The general idea is that no matter which monitor I use, the apperture
> will always subtend ~3 degree of visual angle both veritcally and
> horizontally (from 3m).
>
> I've tried manipulating the Driftdemos with much success. However no
> matter what I do the gratings are presented in a oval apperture, in a
> way that matches the dimensions of the screen (horizontally stretched).
>
> Could someone guide me in the right direction? I'm sort of at a loss
> here. Does it have to do with the aspect ratio or am I totally off
> track?
>
> My code is split into 2 m-files: visualangle.m and
> high_strength_AOC_anaglyphs_v4.m (run this file):
>
> visualangle.m
> function [pixelWidth pixelHeight spatialFrequencyPixelWidth
> spatialFrequencyPixelHeight] = visualangle (visualAngleWidth,
> visualAngleHeight, spatialFrequency, resolutionWidth, resolutionHeight,
> screenWidth, screenHeight, observerdistance)
> % visualAngleWidth: horizontal visual angle of grating
> % visualAngleHeight: vertical visual angle of grating
> % spatialFrequency: spatial frequency
> % observerdistance: distance between eyes and monitor
> % resolutionWidth: horizontal resolution
> % resolutionHeight: vertical resolution
> % screenWidth: width of screen (needs to be adjusted for different
> screens)
> % screenHeight: height of screen (needs to be adjusted for different
> screens)
>
> % type monitor we are using
> % 1 AOC: 1920x1080 509.2x286.4mm
> % 2 True3Di 19 inch: 1280x1024 376.32x301.056mm
> % 3 True3Di 22 inch: 1280x1024 473.76x296.1mm
> monitorType = 1;
>
> if nargin > 8 || nargin < 8
> visualAngleWidth = 1.5;
> visualAngleHeight = 1.5;
> spatialFrequency = 8;
> observerdistance = 3000;
> if monitorType == 1
> resolutionWidth = 1920;
> resolutionHeight = 1080;
> screenWidth = 509.2;
> screenHeight = 286.4;
> elseif monitorType == 2
> resolutionWidth = 1280;
> resolutionHeight = 1024;
> screenWidth = 376.32;
> screenHeight = 301.056;
> elseif monitorType == 3
> resolutionWidth = 1280;
> resolutionHeight = 1024;
> screenWidth = 473.76;
> screenHeight = 296.1;
> end
> end
>
> pixelWidth =
> round(tan(degtorad(visualAngleWidth/2))*2*observerdistance*resolutionWid\
> th/screenWidth);
> pixelHeight =
> round(tan(degtorad(visualAngleHeight/2))*2*observerdistance*resolutionHe\
> ight/screenHeight);
>
> spatialFrequencyPixelWidth = spatialFrequency/(pixelWidth*2);
> spatialFrequencyPixelHeight = spatialFrequency/(pixelHeight*2);
>
> high_strength_AOC_anaglyphs_v4.m
> function high_strength_AOC_anaglyphs_v4
>
> %%%%%%%%%%%%%%%%%%%
> % Observer parameters
> % inches from screen = 118.11
> %%%%%%%%%%%%%%%%%%%
>
> %%%%%%%%%%%%%%%%%%%
> % Screen parameters
>
> % resolution (22') = 1680 x 1024
> % active display size (22') = 473.76(h) x 296.1(w)
>
> % resolution (19') = 1280 x 1024
> % active display size (19') = 376.32(h) x 301.056(w)
> %%%%%%%%%%%%%%%%%%%
>
> %%%%%%%%%%%%%%%%%%%
> % Stimuli paramters
>
> % aperture size (visual angle) = 1.5 deg
> % grating pixel size (22') = 278.526 (width) & 271.629 (height)
> % grating pixel size (19') = 267.158 (width & height)
> %%%%%%%%%%%%%%%%%%%
>
> % high strength: s.f. = 8 cycles/deg; drift speed = 4 cycles/sec
> % low strength: s.f. = 4 cycles/deg; drift speed = 0 cycles/sec
>
> Screen('Preference', 'SkipSyncTests', 1);
>
> try
> % 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
> AssertOpenGL;
>
> % 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. Chosing the display with the highest dislay
> number is
> % a best guess about where you want the stimulus displayed.
> screens=Screen('Screens');
> screenNumber=0;%max(screens);
>
> % Below values are the predefined parameters of the stimulus
> visualAngleWidth = 1.5;
> visualAngleHeight = 1.5;
> spatialFrequency = 8;
> observerdistance = 3000;
>
> [pixelWidth pixelHeight spatialFrequencyPixelWidth
> spatialFrequencyPixelHeight] = visualangle;
>
> gratingsize = 534; %534 is double of 267.158 roundedup, 534.316 is
> double of 267.158. By default the visible grating is 400 pixels by 400
> pixels in size:
>
> texsize=gratingsize / 2;% Define Half-Size of the grating image
> (i.e. radius).
> texsize_horiz = pixelWidth;
> texsize_vert = pixelHeight;
>
> f=8/gratingsize; % Set at 0.015. Grating frequency in cycles/pixel:
> By default 0.05 cycles per pixel.
> f_horiz=spatialFrequencyPixelWidth; % red
> f_vert=spatialFrequencyPixelHeight; % blue
>
> cyclespersecond = 4;% Speed of grating in cycles per second: 1 cycle
> per second by default.
>
>
> % Find the color values which correspond to white and black: Usually
> % black is always 0 and white 255, but this rule is not true if one
> of
> % the high precision framebuffer modes is enabled via the
> % PsychImaging() commmand, so we query the true values via the
> % functions WhiteIndex and BlackIndex:
> white=WhiteIndex(screenNumber);
> black=BlackIndex(screenNumber);
>
> % Round gray to integral number, to avoid roundoff artifacts with
> some
> % graphics cards:
> gray=round((white+black)/2);
>
> % This makes sure that on floating point framebuffers we still get a
> % well defined gray. It isn't strictly neccessary in this demo:
> if gray == white
> gray=white / 2;
> end
>
> % Contrast 'inc'rement range for given white and gray values:
> inc=white-gray;
>
> % Open a double buffered fullscreen window and set default
> background
> % color to black:
> %[w screenRect]=Screen('OpenWindow',screenNumber, black);
> PsychImaging('PrepareConfiguration');
> [w screenRect]=PsychImaging('OpenWindow', screenNumber, 0, [], [],
> [], 8);
>
> %Obtain width & height of screen
> [width, height] = WindowSize(w);
>
> SetAnaglyphStereoParameters('LeftGains', w, [0.4 0.0 0.0]);
> SetAnaglyphStereoParameters('RightGains', w, [0.0 0.2 0.7]);
>
> % Initially fill left- and right-eye image buffer with black
> background
> % color:
> Screen('SelectStereoDrawBuffer', w, 0);
> Screen('FillRect', w, BlackIndex(screenNumber));
> Screen('SelectStereoDrawBuffer', w, 1);
> Screen('FillRect', w, BlackIndex(screenNumber));
>
> % Enable alpha blending for proper combination of the gaussian
> aperture
> % with the drifting sine grating:
> Screen('BlendFunction', w, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
>
> % Calculate parameters of the grating:
> %
> % First we compute pixels per cycle, rounded up to full pixels, as
> we
> % need this to create a grating of proper size below:
> %p=ceil(1/f)
> p_vert=ceil(1/f_vert)
> p_horiz=ceil(1/f_horiz)
>
> % Also need frequency in radians:
> %fr=f*2*pi;
> fr_horiz=f_horiz*2*pi;
> fr_vert=f_vert*2*pi;
>
> % This is the visible size of the grating. It is twice the
> half-width
> % of the texture plus one pixel to make sure it has an odd number of
> % pixels and is therefore symmetric around the center of the
> texture:
> %visiblesize=2*texsize+1
> visiblesize_horiz=2*texsize_horiz+1
> visiblesize_vert=2*texsize_vert+1
>
> % Create one single static grating image:
> %
> % We only need a texture with a single row of pixels(i.e. 1 pixel in
> height) to
> % define the whole grating! If the 'srcRect' in the 'Drawtexture'
> call
> % below is "higher" than that (i.e. visibleSize >> 1), the GPU will
> % automatically replicate pixel rows. This 1 pixel height saves
> memory
> % and memory bandwith, ie. it is potentially faster on some GPUs.
> %
> % However it does need 2 * texsize + p columns, i.e. the visible
> size
> % of the grating extended by the length of 1 period (repetition) of
> the
> % sine-wave in pixels 'p':
> %x = meshgrid(-texsize:texsize + p, 1);
> x_horiz = meshgrid(-texsize_horiz:texsize_horiz + p_horiz, 1);
> x_vert = meshgrid(-texsize_vert:texsize_vert + p_vert, 1);
>
> % Compute actual cosine grating:
> %grating=gray + inc*cos(fr*x);
>
> % Compute actual square-wave grating:
> %grating=gray + inc*floor(cos(fr*x+1)); %gray + inc*cos(fr*x);
> grating_horiz=gray + inc*floor(cos(fr_horiz*x_horiz+1)); %gray +
> inc*cos(fr*x);
> grating_vert=gray + inc*floor(cos(fr_vert*x_vert+1)); %gray +
> inc*cos(fr*x);
>
> % Store 1-D single row grating in texture:
> %gratingtex=Screen('MakeTexture', w, grating);
> gratingtex_horiz=Screen('MakeTexture', w, grating_horiz);
> gratingtex_vert=Screen('MakeTexture', w, grating_vert);
>
> % Create a single gaussian transparency mask and store it to a
> texture:
> % The mask must have the same size as the visible size of the
> grating
> % to fully cover it. Here we must define it in 2 dimensions and
> can't
> % get easily away with one single row of pixels.
> %
> % We create a two-layer texture: One unused luminance channel which
> we
> % just fill with the same color as the background color of the
> screen
> % 'gray'. The transparency (aka alpha) channel is filled with a
> % gaussian (exp()) aperture mask:
> %mask=ones(2*texsize+1, 2*texsize+1, 2) * gray;
> %[x,y]=meshgrid(-1*texsize:1*texsize,-1*texsize:1*texsize);
> %mask(:, :, 2)=0;%white * (1 - exp(-((x/90).^2)-((y/90).^2)));
> %masktex=Screen('MakeTexture', w, mask);
>
> mask_horiz=ones(2*texsize_horiz+1, 2*texsize_horiz+1, 2) * gray;
>
> [x,y]=meshgrid(-1*texsize_horiz:1*texsize_horiz,-1*texsize_horiz:1*texsi\
> ze_horiz);
> mask_horiz(:, :, 2)=0;
> masktex_horiz=Screen('MakeTexture', w, mask_horiz);
>
> mask_vert=ones(2*texsize_vert+1, 2*texsize_vert+1, 2) * gray;
>
> [x,y]=meshgrid(-1*texsize_vert:1*texsize_vert,-1*texsize_vert:1*texsize_\
> vert);
> mask_vert(:, :, 2)=0;
> masktex_vert=Screen('MakeTexture', w, mask_vert);
>
> % Query maximum useable priorityLevel on this system:
> priorityLevel=MaxPriority(w) %priority set to 1
>
> % We don't use Priority() in order to not accidentally overload
> older
> % machines that can't handle a redraw every 40 ms. If your machine
> is
> % fast enough, uncomment this to get more accurate timing.
> Priority(priorityLevel);
>
> % Definition of the drawn rectangle on the screen:
> % Compute it to be the visible size of the grating, centered on the
> % screen:
> dstRect=[0 0 visiblesize_vert visiblesize_horiz];
> dstRect=CenterRect(dstRect, screenRect);
>
> % Query duration of one monitor refresh interval:
> ifi=Screen('GetFlipInterval', w);
>
> % Translate that into the amount of seconds to wait between screen
> % redraws/updates:
>
> % waitframes = 1 means: Redraw every monitor refresh. If your GPU is
> % not fast enough to do this, you can increment this to only redraw
> % every n'th refresh. All animation paramters will adapt to still
> % provide the proper grating. However, if you have a fine grating
> % drifting at a high speed, the refresh rate must exceed that
> % "effective" grating speed to avoid aliasing artifacts in time,
> i.e.,
> % to make sure to satisfy the constraints of the sampling theorem
> % (See Wikipedia: "Nyquist?Shannon sampling theorem" for a starter,
> if
> % you don't know what this means):
> waitframes = 1;
>
> % Translate frames into seconds for screen update interval:
> waitduration = waitframes * ifi;
>
> % Recompute p, this time without the ceil() operation from above.
> % Otherwise we will get wrong drift speed due to rounding errors!
> p=1/f; % pixels/cycle
> p_horiz=1/f_horiz;
> p_vert=1/f_vert;
>
> % Translate requested speed of the grating (in cycles per second)
> into
> % a shift value in "pixels per frame", for given waitduration: This
> is
> % the amount of pixels to shift our srcRect "aperture" in horizontal
> % directionat each redraw:
> %shiftperframe= cyclespersecond * p * waitduration;
> shiftperframe_horiz= cyclespersecond * p_horiz * waitduration;
> shiftperframe_vert= cyclespersecond * p_vert * waitduration;
>
> % Perform initial Flip to sync us to the VBL and for getting an
> initial
> % VBL-Timestamp as timing baseline for our redraw loop:
> vbl=Screen('Flip', w);
>
>
> %destinationRect for the 2 gratings
> %dst1Rect = [(width/2) - (texsize/2), (height/2) - (texsize/2),
> (width/2) + (texsize/2), (height/2) + (texsize/2)];
> dstRect = [(width/2) - (texsize_horiz/2), (height/2) -
> (texsize_vert/2), (width/2) + (texsize_horiz/2), (height/2) +
> (texsize_vert/2)];
> dstRect_rect = [(width/2) - (texsize/2)-(width/2), (height/2) -
> (texsize/2)-(width/2), (width/2) + (texsize/2)+(width/2), (height/2) +
> (texsize/2)+(width/2)];
>
> %Set index for drifting
> i=0;
>
> % Our framecounter, we love stats ;-)
> fcount = 0;
>
> hidecursor;
>
> % Animationloop:
> while ~KbCheck %(vbl < vblendtime)
> Screen('SelectStereoDrawBuffer', w, 0);
>
> % Shift the grating by "shiftperframe" pixels per frame:
> % the mod'ulo operation makes sure that our "aperture" will snap
> % back to the beginning of the grating, once the border is
> reached.
> % Fractional values of 'xoffset' are fine here. The GPU will
> % perform proper interpolation of color values in the grating
> % texture image to draw a grating that corresponds as closely as
> % technical possible to that fractional 'xoffset'. GPU's use
> % bilinear interpolation whose accuracy depends on the GPU at
> hand.
> % Consumer ATI hardware usually resolves 1/64 of a pixel,
> whereas
> % consumer NVidia hardware usually resolves 1/256 of a pixel.
> You
> % can run the script "DriftTexturePrecisionTest" to test your
> % hardware...
> %xoffset = mod(i*shiftperframe,p);
> xoffset_horiz = mod(i*shiftperframe_horiz,p_horiz);
> xoffset_vert = mod(i*shiftperframe_vert,p_vert);
>
> i=i+1;
>
> % Define shifted srcRect that cuts out the properly shifted
> rectangular
> % area from the texture: We cut out the range 0 to visiblesize
> in
> % the vertical direction although the texture is only 1 pixel in
> % height! This works because the hardware will automatically
> % replicate pixels in one dimension if we exceed the real
> borders
> % of the stored texture. This allows us to save storage space
> here,
> % as our 2-D grating is essentially only defined in 1-D:
> %srcRect=[xoffset 0 xoffset + visiblesize_vert
> visiblesize_horiz];
> srcRect_horiz=[xoffset_horiz 0 xoffset_horiz + visiblesize_vert
> visiblesize_horiz];
> srcRect_vert=[xoffset_vert 0 xoffset_vert + visiblesize_vert
> visiblesize_horiz];
>
> % Fill circular 'dstRect' region with an alpha value of 255:
> %Screen('FillOval', w, [0 0 0 255], dstRect);
>
> Screen('SelectStereoDrawBuffer', w, 0);
> Screen('Blendfunction', w, GL_ONE, GL_ZERO, [0 0 0 1]);
> Screen('FillRect', w, [0 0 0 0], dstRect_rect);
> Screen('FillOval', w, [0 0 0 255], dstRect);
> Screen('Blendfunction', w, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA,
> [1 1 1 1]);
> %Screen('DrawTexture', w, gratingtex, srcRect, dst1Rect, 180,
> [], [], [255 255 255]); %draw left vertical grating
> Screen('DrawTexture', w, gratingtex_horiz, srcRect_horiz,
> dstRect, 180, [], [], [255 255 255]); %draw left vertical grating
>
> Screen('SelectStereoDrawBuffer', w, 1);
> Screen('Blendfunction', w, GL_ONE, GL_ZERO, [0 0 0 1]);
> Screen('FillRect', w, [0 0 0 0], dstRect_rect);
> Screen('FillOval', w, [0 0 0 255], dstRect);
> Screen('Blendfunction', w, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA,
> [1 1 1 1]);
> %Screen('DrawTexture', w, gratingtex, srcRect, dst1Rect, 270,
> [], [], [255 255 255]); %draw right horizontal grating
> Screen('DrawTexture', w, gratingtex_vert, srcRect_vert, dstRect,
> 270, [], [], [255 255 255]); %draw right horizontal grating
>
> %if drawmask==1
> % Draw gaussian mask over grating:
> %Screen('DrawTexture', w, masktex, [0 0 visiblesize
> visiblesize], dstRect, angle);
> %end;
>
> % Restore alpha blending mode for next draw iteration:
> Screen('Blendfunction', w, GL_SRC_ALPHA,
> GL_ONE_MINUS_SRC_ALPHA);
>
> % Flip 'waitframes' monitor refresh intervals after last redraw.
> % Providing this 'when' timestamp allows for optimal timing
> % precision in stimulus onset, a stable animation framerate and
> at
> % the same time allows the built-in "skipped frames" detector to
> % work optimally and report skipped frames due to hardware
> % overload:
> vbl = Screen('Flip', w, vbl + (waitframes - 0.5) * ifi);
>
> fcount = fcount + 1;
>
> % Abort demo if any key is pressed:
> %if KbCheck
> %break;
> %end;
> end;
>
> % Restore normal priority scheduling in case something else was set
> % before:
> Priority(0);
>
> showcursor;
>
> %The same commands wich close onscreen and offscreen windows also
> close
> %textures.
> Screen('CloseAll');
>
> catch
> %this "catch" section executes in case of an error in the "try"
> section
> %above. Importantly, it closes the onscreen window if its open.
> %Screen('CloseAll');
> Priority(0);
> psychrethrow(psychlasterror);
> end %try..catch..
>
> % We're done!
> return;
>
Hello Peter,

[Have you checked what the numbers are you are feeding into your stimulus immediately prior to drawing? Are the numbers correct and the stimulus wrong?]
The numbers are correct. For example when I try to draw a square (based on the monitor resolution and my desired visual angle it is 296 (vertical) x 296 (horizontal) pixels. However when I draw the square ([492 364 788 660]) it is a rectangle. The aspect ratio is 1.778.

[Are you using a single monitor setup or dual monitors?]
A single screen monitor.

Phillip

--- In psychtoolbox@yahoogroups.com, "peter.scarfe" <peterscarfe@...> wrote:
>
>
>
>
>
>
> I haven't looked through all your code, but this should be simple to do.
>
> Assuming you are working on a spatially calibrated CRT of a LCD, you would simply need to work out how many pixels there are in a degree and how many degrees you want your aperture to be. So it would just be a multiplication of those two numbers.
>
> Have you checked what the numbers are you are feeding into your stimulus immediately prior to drawing? Are the numbers correct and the stimulus wrong?
>
> Are you using a single monitor setup or dual monitors?
>
> Maybe try drawing something very simple, e.g. a filled square, with the same numbers. See if that works.
>
> Also, if you are using textures and srcRects and dstRects make sure your srcRects and dstRects are the same size, if they are not PTB might stretch your texture to fit the dstRect, which may result in distortion.
>
>
>
>
> --- In psychtoolbox@yahoogroups.com, "philliplaw22" <philliplaw22@> wrote:
> >
> > Hi,
> >
> > I am trying to present anaglyph gratings through a circular apperture
> > subtending 3 degrees of the visual angle on any conventional monitor.
> > The general idea is that no matter which monitor I use, the apperture
> > will always subtend ~3 degree of visual angle both veritcally and
> > horizontally (from 3m).
> >
> > I've tried manipulating the Driftdemos with much success. However no
> > matter what I do the gratings are presented in a oval apperture, in a
> > way that matches the dimensions of the screen (horizontally stretched).
> >
> > Could someone guide me in the right direction? I'm sort of at a loss
> > here. Does it have to do with the aspect ratio or am I totally off
> > track?
> >
> > My code is split into 2 m-files: visualangle.m and
> > high_strength_AOC_anaglyphs_v4.m (run this file):
> >
> > visualangle.m
> > function [pixelWidth pixelHeight spatialFrequencyPixelWidth
> > spatialFrequencyPixelHeight] = visualangle (visualAngleWidth,
> > visualAngleHeight, spatialFrequency, resolutionWidth, resolutionHeight,
> > screenWidth, screenHeight, observerdistance)
> > % visualAngleWidth: horizontal visual angle of grating
> > % visualAngleHeight: vertical visual angle of grating
> > % spatialFrequency: spatial frequency
> > % observerdistance: distance between eyes and monitor
> > % resolutionWidth: horizontal resolution
> > % resolutionHeight: vertical resolution
> > % screenWidth: width of screen (needs to be adjusted for different
> > screens)
> > % screenHeight: height of screen (needs to be adjusted for different
> > screens)
> >
> > % type monitor we are using
> > % 1 AOC: 1920x1080 509.2x286.4mm
> > % 2 True3Di 19 inch: 1280x1024 376.32x301.056mm
> > % 3 True3Di 22 inch: 1280x1024 473.76x296.1mm
> > monitorType = 1;
> >
> > if nargin > 8 || nargin < 8
> > visualAngleWidth = 1.5;
> > visualAngleHeight = 1.5;
> > spatialFrequency = 8;
> > observerdistance = 3000;
> > if monitorType == 1
> > resolutionWidth = 1920;
> > resolutionHeight = 1080;
> > screenWidth = 509.2;
> > screenHeight = 286.4;
> > elseif monitorType == 2
> > resolutionWidth = 1280;
> > resolutionHeight = 1024;
> > screenWidth = 376.32;
> > screenHeight = 301.056;
> > elseif monitorType == 3
> > resolutionWidth = 1280;
> > resolutionHeight = 1024;
> > screenWidth = 473.76;
> > screenHeight = 296.1;
> > end
> > end
> >
> > pixelWidth =
> > round(tan(degtorad(visualAngleWidth/2))*2*observerdistance*resolutionWid\
> > th/screenWidth);
> > pixelHeight =
> > round(tan(degtorad(visualAngleHeight/2))*2*observerdistance*resolutionHe\
> > ight/screenHeight);
> >
> > spatialFrequencyPixelWidth = spatialFrequency/(pixelWidth*2);
> > spatialFrequencyPixelHeight = spatialFrequency/(pixelHeight*2);
> >
> > high_strength_AOC_anaglyphs_v4.m
> > function high_strength_AOC_anaglyphs_v4
> >
> > %%%%%%%%%%%%%%%%%%%
> > % Observer parameters
> > % inches from screen = 118.11
> > %%%%%%%%%%%%%%%%%%%
> >
> > %%%%%%%%%%%%%%%%%%%
> > % Screen parameters
> >
> > % resolution (22') = 1680 x 1024
> > % active display size (22') = 473.76(h) x 296.1(w)
> >
> > % resolution (19') = 1280 x 1024
> > % active display size (19') = 376.32(h) x 301.056(w)
> > %%%%%%%%%%%%%%%%%%%
> >
> > %%%%%%%%%%%%%%%%%%%
> > % Stimuli paramters
> >
> > % aperture size (visual angle) = 1.5 deg
> > % grating pixel size (22') = 278.526 (width) & 271.629 (height)
> > % grating pixel size (19') = 267.158 (width & height)
> > %%%%%%%%%%%%%%%%%%%
> >
> > % high strength: s.f. = 8 cycles/deg; drift speed = 4 cycles/sec
> > % low strength: s.f. = 4 cycles/deg; drift speed = 0 cycles/sec
> >
> > Screen('Preference', 'SkipSyncTests', 1);
> >
> > try
> > % 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
> > AssertOpenGL;
> >
> > % 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. Chosing the display with the highest dislay
> > number is
> > % a best guess about where you want the stimulus displayed.
> > screens=Screen('Screens');
> > screenNumber=0;%max(screens);
> >
> > % Below values are the predefined parameters of the stimulus
> > visualAngleWidth = 1.5;
> > visualAngleHeight = 1.5;
> > spatialFrequency = 8;
> > observerdistance = 3000;
> >
> > [pixelWidth pixelHeight spatialFrequencyPixelWidth
> > spatialFrequencyPixelHeight] = visualangle;
> >
> > gratingsize = 534; %534 is double of 267.158 roundedup, 534.316 is
> > double of 267.158. By default the visible grating is 400 pixels by 400
> > pixels in size:
> >
> > texsize=gratingsize / 2;% Define Half-Size of the grating image
> > (i.e. radius).
> > texsize_horiz = pixelWidth;
> > texsize_vert = pixelHeight;
> >
> > f=8/gratingsize; % Set at 0.015. Grating frequency in cycles/pixel:
> > By default 0.05 cycles per pixel.
> > f_horiz=spatialFrequencyPixelWidth; % red
> > f_vert=spatialFrequencyPixelHeight; % blue
> >
> > cyclespersecond = 4;% Speed of grating in cycles per second: 1 cycle
> > per second by default.
> >
> >
> > % Find the color values which correspond to white and black: Usually
> > % black is always 0 and white 255, but this rule is not true if one
> > of
> > % the high precision framebuffer modes is enabled via the
> > % PsychImaging() commmand, so we query the true values via the
> > % functions WhiteIndex and BlackIndex:
> > white=WhiteIndex(screenNumber);
> > black=BlackIndex(screenNumber);
> >
> > % Round gray to integral number, to avoid roundoff artifacts with
> > some
> > % graphics cards:
> > gray=round((white+black)/2);
> >
> > % This makes sure that on floating point framebuffers we still get a
> > % well defined gray. It isn't strictly neccessary in this demo:
> > if gray == white
> > gray=white / 2;
> > end
> >
> > % Contrast 'inc'rement range for given white and gray values:
> > inc=white-gray;
> >
> > % Open a double buffered fullscreen window and set default
> > background
> > % color to black:
> > %[w screenRect]=Screen('OpenWindow',screenNumber, black);
> > PsychImaging('PrepareConfiguration');
> > [w screenRect]=PsychImaging('OpenWindow', screenNumber, 0, [], [],
> > [], 8);
> >
> > %Obtain width & height of screen
> > [width, height] = WindowSize(w);
> >
> > SetAnaglyphStereoParameters('LeftGains', w, [0.4 0.0 0.0]);
> > SetAnaglyphStereoParameters('RightGains', w, [0.0 0.2 0.7]);
> >
> > % Initially fill left- and right-eye image buffer with black
> > background
> > % color:
> > Screen('SelectStereoDrawBuffer', w, 0);
> > Screen('FillRect', w, BlackIndex(screenNumber));
> > Screen('SelectStereoDrawBuffer', w, 1);
> > Screen('FillRect', w, BlackIndex(screenNumber));
> >
> > % Enable alpha blending for proper combination of the gaussian
> > aperture
> > % with the drifting sine grating:
> > Screen('BlendFunction', w, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
> >
> > % Calculate parameters of the grating:
> > %
> > % First we compute pixels per cycle, rounded up to full pixels, as
> > we
> > % need this to create a grating of proper size below:
> > %p=ceil(1/f)
> > p_vert=ceil(1/f_vert)
> > p_horiz=ceil(1/f_horiz)
> >
> > % Also need frequency in radians:
> > %fr=f*2*pi;
> > fr_horiz=f_horiz*2*pi;
> > fr_vert=f_vert*2*pi;
> >
> > % This is the visible size of the grating. It is twice the
> > half-width
> > % of the texture plus one pixel to make sure it has an odd number of
> > % pixels and is therefore symmetric around the center of the
> > texture:
> > %visiblesize=2*texsize+1
> > visiblesize_horiz=2*texsize_horiz+1
> > visiblesize_vert=2*texsize_vert+1
> >
> > % Create one single static grating image:
> > %
> > % We only need a texture with a single row of pixels(i.e. 1 pixel in
> > height) to
> > % define the whole grating! If the 'srcRect' in the 'Drawtexture'
> > call
> > % below is "higher" than that (i.e. visibleSize >> 1), the GPU will
> > % automatically replicate pixel rows. This 1 pixel height saves
> > memory
> > % and memory bandwith, ie. it is potentially faster on some GPUs.
> > %
> > % However it does need 2 * texsize + p columns, i.e. the visible
> > size
> > % of the grating extended by the length of 1 period (repetition) of
> > the
> > % sine-wave in pixels 'p':
> > %x = meshgrid(-texsize:texsize + p, 1);
> > x_horiz = meshgrid(-texsize_horiz:texsize_horiz + p_horiz, 1);
> > x_vert = meshgrid(-texsize_vert:texsize_vert + p_vert, 1);
> >
> > % Compute actual cosine grating:
> > %grating=gray + inc*cos(fr*x);
> >
> > % Compute actual square-wave grating:
> > %grating=gray + inc*floor(cos(fr*x+1)); %gray + inc*cos(fr*x);
> > grating_horiz=gray + inc*floor(cos(fr_horiz*x_horiz+1)); %gray +
> > inc*cos(fr*x);
> > grating_vert=gray + inc*floor(cos(fr_vert*x_vert+1)); %gray +
> > inc*cos(fr*x);
> >
> > % Store 1-D single row grating in texture:
> > %gratingtex=Screen('MakeTexture', w, grating);
> > gratingtex_horiz=Screen('MakeTexture', w, grating_horiz);
> > gratingtex_vert=Screen('MakeTexture', w, grating_vert);
> >
> > % Create a single gaussian transparency mask and store it to a
> > texture:
> > % The mask must have the same size as the visible size of the
> > grating
> > % to fully cover it. Here we must define it in 2 dimensions and
> > can't
> > % get easily away with one single row of pixels.
> > %
> > % We create a two-layer texture: One unused luminance channel which
> > we
> > % just fill with the same color as the background color of the
> > screen
> > % 'gray'. The transparency (aka alpha) channel is filled with a
> > % gaussian (exp()) aperture mask:
> > %mask=ones(2*texsize+1, 2*texsize+1, 2) * gray;
> > %[x,y]=meshgrid(-1*texsize:1*texsize,-1*texsize:1*texsize);
> > %mask(:, :, 2)=0;%white * (1 - exp(-((x/90).^2)-((y/90).^2)));
> > %masktex=Screen('MakeTexture', w, mask);
> >
> > mask_horiz=ones(2*texsize_horiz+1, 2*texsize_horiz+1, 2) * gray;
> >
> > [x,y]=meshgrid(-1*texsize_horiz:1*texsize_horiz,-1*texsize_horiz:1*texsi\
> > ze_horiz);
> > mask_horiz(:, :, 2)=0;
> > masktex_horiz=Screen('MakeTexture', w, mask_horiz);
> >
> > mask_vert=ones(2*texsize_vert+1, 2*texsize_vert+1, 2) * gray;
> >
> > [x,y]=meshgrid(-1*texsize_vert:1*texsize_vert,-1*texsize_vert:1*texsize_\
> > vert);
> > mask_vert(:, :, 2)=0;
> > masktex_vert=Screen('MakeTexture', w, mask_vert);
> >
> > % Query maximum useable priorityLevel on this system:
> > priorityLevel=MaxPriority(w) %priority set to 1
> >
> > % We don't use Priority() in order to not accidentally overload
> > older
> > % machines that can't handle a redraw every 40 ms. If your machine
> > is
> > % fast enough, uncomment this to get more accurate timing.
> > Priority(priorityLevel);
> >
> > % Definition of the drawn rectangle on the screen:
> > % Compute it to be the visible size of the grating, centered on the
> > % screen:
> > dstRect=[0 0 visiblesize_vert visiblesize_horiz];
> > dstRect=CenterRect(dstRect, screenRect);
> >
> > % Query duration of one monitor refresh interval:
> > ifi=Screen('GetFlipInterval', w);
> >
> > % Translate that into the amount of seconds to wait between screen
> > % redraws/updates:
> >
> > % waitframes = 1 means: Redraw every monitor refresh. If your GPU is
> > % not fast enough to do this, you can increment this to only redraw
> > % every n'th refresh. All animation paramters will adapt to still
> > % provide the proper grating. However, if you have a fine grating
> > % drifting at a high speed, the refresh rate must exceed that
> > % "effective" grating speed to avoid aliasing artifacts in time,
> > i.e.,
> > % to make sure to satisfy the constraints of the sampling theorem
> > % (See Wikipedia: "Nyquist?Shannon sampling theorem" for a starter,
> > if
> > % you don't know what this means):
> > waitframes = 1;
> >
> > % Translate frames into seconds for screen update interval:
> > waitduration = waitframes * ifi;
> >
> > % Recompute p, this time without the ceil() operation from above.
> > % Otherwise we will get wrong drift speed due to rounding errors!
> > p=1/f; % pixels/cycle
> > p_horiz=1/f_horiz;
> > p_vert=1/f_vert;
> >
> > % Translate requested speed of the grating (in cycles per second)
> > into
> > % a shift value in "pixels per frame", for given waitduration: This
> > is
> > % the amount of pixels to shift our srcRect "aperture" in horizontal
> > % directionat each redraw:
> > %shiftperframe= cyclespersecond * p * waitduration;
> > shiftperframe_horiz= cyclespersecond * p_horiz * waitduration;
> > shiftperframe_vert= cyclespersecond * p_vert * waitduration;
> >
> > % Perform initial Flip to sync us to the VBL and for getting an
> > initial
> > % VBL-Timestamp as timing baseline for our redraw loop:
> > vbl=Screen('Flip', w);
> >
> >
> > %destinationRect for the 2 gratings
> > %dst1Rect = [(width/2) - (texsize/2), (height/2) - (texsize/2),
> > (width/2) + (texsize/2), (height/2) + (texsize/2)];
> > dstRect = [(width/2) - (texsize_horiz/2), (height/2) -
> > (texsize_vert/2), (width/2) + (texsize_horiz/2), (height/2) +
> > (texsize_vert/2)];
> > dstRect_rect = [(width/2) - (texsize/2)-(width/2), (height/2) -
> > (texsize/2)-(width/2), (width/2) + (texsize/2)+(width/2), (height/2) +
> > (texsize/2)+(width/2)];
> >
> > %Set index for drifting
> > i=0;
> >
> > % Our framecounter, we love stats ;-)
> > fcount = 0;
> >
> > hidecursor;
> >
> > % Animationloop:
> > while ~KbCheck %(vbl < vblendtime)
> > Screen('SelectStereoDrawBuffer', w, 0);
> >
> > % Shift the grating by "shiftperframe" pixels per frame:
> > % the mod'ulo operation makes sure that our "aperture" will snap
> > % back to the beginning of the grating, once the border is
> > reached.
> > % Fractional values of 'xoffset' are fine here. The GPU will
> > % perform proper interpolation of color values in the grating
> > % texture image to draw a grating that corresponds as closely as
> > % technical possible to that fractional 'xoffset'. GPU's use
> > % bilinear interpolation whose accuracy depends on the GPU at
> > hand.
> > % Consumer ATI hardware usually resolves 1/64 of a pixel,
> > whereas
> > % consumer NVidia hardware usually resolves 1/256 of a pixel.
> > You
> > % can run the script "DriftTexturePrecisionTest" to test your
> > % hardware...
> > %xoffset = mod(i*shiftperframe,p);
> > xoffset_horiz = mod(i*shiftperframe_horiz,p_horiz);
> > xoffset_vert = mod(i*shiftperframe_vert,p_vert);
> >
> > i=i+1;
> >
> > % Define shifted srcRect that cuts out the properly shifted
> > rectangular
> > % area from the texture: We cut out the range 0 to visiblesize
> > in
> > % the vertical direction although the texture is only 1 pixel in
> > % height! This works because the hardware will automatically
> > % replicate pixels in one dimension if we exceed the real
> > borders
> > % of the stored texture. This allows us to save storage space
> > here,
> > % as our 2-D grating is essentially only defined in 1-D:
> > %srcRect=[xoffset 0 xoffset + visiblesize_vert
> > visiblesize_horiz];
> > srcRect_horiz=[xoffset_horiz 0 xoffset_horiz + visiblesize_vert
> > visiblesize_horiz];
> > srcRect_vert=[xoffset_vert 0 xoffset_vert + visiblesize_vert
> > visiblesize_horiz];
> >
> > % Fill circular 'dstRect' region with an alpha value of 255:
> > %Screen('FillOval', w, [0 0 0 255], dstRect);
> >
> > Screen('SelectStereoDrawBuffer', w, 0);
> > Screen('Blendfunction', w, GL_ONE, GL_ZERO, [0 0 0 1]);
> > Screen('FillRect', w, [0 0 0 0], dstRect_rect);
> > Screen('FillOval', w, [0 0 0 255], dstRect);
> > Screen('Blendfunction', w, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA,
> > [1 1 1 1]);
> > %Screen('DrawTexture', w, gratingtex, srcRect, dst1Rect, 180,
> > [], [], [255 255 255]); %draw left vertical grating
> > Screen('DrawTexture', w, gratingtex_horiz, srcRect_horiz,
> > dstRect, 180, [], [], [255 255 255]); %draw left vertical grating
> >
> > Screen('SelectStereoDrawBuffer', w, 1);
> > Screen('Blendfunction', w, GL_ONE, GL_ZERO, [0 0 0 1]);
> > Screen('FillRect', w, [0 0 0 0], dstRect_rect);
> > Screen('FillOval', w, [0 0 0 255], dstRect);
> > Screen('Blendfunction', w, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA,
> > [1 1 1 1]);
> > %Screen('DrawTexture', w, gratingtex, srcRect, dst1Rect, 270,
> > [], [], [255 255 255]); %draw right horizontal grating
> > Screen('DrawTexture', w, gratingtex_vert, srcRect_vert, dstRect,
> > 270, [], [], [255 255 255]); %draw right horizontal grating
> >
> > %if drawmask==1
> > % Draw gaussian mask over grating:
> > %Screen('DrawTexture', w, masktex, [0 0 visiblesize
> > visiblesize], dstRect, angle);
> > %end;
> >
> > % Restore alpha blending mode for next draw iteration:
> > Screen('Blendfunction', w, GL_SRC_ALPHA,
> > GL_ONE_MINUS_SRC_ALPHA);
> >
> > % Flip 'waitframes' monitor refresh intervals after last redraw.
> > % Providing this 'when' timestamp allows for optimal timing
> > % precision in stimulus onset, a stable animation framerate and
> > at
> > % the same time allows the built-in "skipped frames" detector to
> > % work optimally and report skipped frames due to hardware
> > % overload:
> > vbl = Screen('Flip', w, vbl + (waitframes - 0.5) * ifi);
> >
> > fcount = fcount + 1;
> >
> > % Abort demo if any key is pressed:
> > %if KbCheck
> > %break;
> > %end;
> > end;
> >
> > % Restore normal priority scheduling in case something else was set
> > % before:
> > Priority(0);
> >
> > showcursor;
> >
> > %The same commands wich close onscreen and offscreen windows also
> > close
> > %textures.
> > Screen('CloseAll');
> >
> > catch
> > %this "catch" section executes in case of an error in the "try"
> > section
> > %above. Importantly, it closes the onscreen window if its open.
> > %Screen('CloseAll');
> > Priority(0);
> > psychrethrow(psychlasterror);
> > end %try..catch..
> >
> > % We're done!
> > return;
> >
>
Please provide some minimal code the demonstrates the error i.e. code that someone can run on their machine to see the problem.

I think many people would have found this problem if it were a PTB issue. So I imagine it is just something incorrectly calculated in your code.



--- In psychtoolbox@yahoogroups.com, "philliplaw22" <philliplaw22@...> wrote:
>
> To expand on my previous post, to start things off simple...I now got a square to display on monitor A (AOC e2353Phz; 1920x1080; aspect ratio = 1.78; active display area = 509.76 x 286.74 mm). However the size of the square is smaller on a different monitor B (Dell E2311H; 1920x1080; aspect ratio = 1.78; active display area = 509.76 x 286.74 mm). Yes both monitors have the same specs, and I know it is smaller because I used a ruler to physically measure them on both screens.
>
> Monitor A is the AOC e2353Phz (http://www.aocmonitorap.com/root/anz/product_display.php?id=10)
> Monitor B is the Dell E2311H (http://shop.it.ee/Specid/Monitorid/E2311H_E2211H.pdf)
>
> What else could account for this size difference for my square? I'm at a loss.
>
> Phillip
>
> --- In psychtoolbox@yahoogroups.com, "philliplaw22" <philliplaw22@> wrote:
> >
> > Hello Peter,
> >
> > [Have you checked what the numbers are you are feeding into your stimulus immediately prior to drawing? Are the numbers correct and the stimulus wrong?]
> > The numbers are correct. For example when I try to draw a square (based on the monitor resolution and my desired visual angle it is 296 (vertical) x 296 (horizontal) pixels. However when I draw the square ([492 364 788 660]) it is a rectangle. The aspect ratio is 1.778.
> >
> > [Are you using a single monitor setup or dual monitors?]
> > A single screen monitor.
> >
> > Phillip
> >
> > --- In psychtoolbox@yahoogroups.com, "peter.scarfe" <peterscarfe@> wrote:
> > >
> > >
> > >
> > >
> > >
> > >
> > > I haven't looked through all your code, but this should be simple to do.
> > >
> > > Assuming you are working on a spatially calibrated CRT of a LCD, you would simply need to work out how many pixels there are in a degree and how many degrees you want your aperture to be. So it would just be a multiplication of those two numbers.
> > >
> > > Have you checked what the numbers are you are feeding into your stimulus immediately prior to drawing? Are the numbers correct and the stimulus wrong?
> > >
> > > Are you using a single monitor setup or dual monitors?
> > >
> > > Maybe try drawing something very simple, e.g. a filled square, with the same numbers. See if that works.
> > >
> > > Also, if you are using textures and srcRects and dstRects make sure your srcRects and dstRects are the same size, if they are not PTB might stretch your texture to fit the dstRect, which may result in distortion.
> > >
> > >
> > >
> > >
> > > --- In psychtoolbox@yahoogroups.com, "philliplaw22" <philliplaw22@> wrote:
> > > >
> > > > Hi,
> > > >
> > > > I am trying to present anaglyph gratings through a circular apperture
> > > > subtending 3 degrees of the visual angle on any conventional monitor.
> > > > The general idea is that no matter which monitor I use, the apperture
> > > > will always subtend ~3 degree of visual angle both veritcally and
> > > > horizontally (from 3m).
> > > >
> > > > I've tried manipulating the Driftdemos with much success. However no
> > > > matter what I do the gratings are presented in a oval apperture, in a
> > > > way that matches the dimensions of the screen (horizontally stretched).
> > > >
> > > > Could someone guide me in the right direction? I'm sort of at a loss
> > > > here. Does it have to do with the aspect ratio or am I totally off
> > > > track?
> > > >
> > > > My code is split into 2 m-files: visualangle.m and
> > > > high_strength_AOC_anaglyphs_v4.m (run this file):
> > > >
> > > > visualangle.m
> > > > function [pixelWidth pixelHeight spatialFrequencyPixelWidth
> > > > spatialFrequencyPixelHeight] = visualangle (visualAngleWidth,
> > > > visualAngleHeight, spatialFrequency, resolutionWidth, resolutionHeight,
> > > > screenWidth, screenHeight, observerdistance)
> > > > % visualAngleWidth: horizontal visual angle of grating
> > > > % visualAngleHeight: vertical visual angle of grating
> > > > % spatialFrequency: spatial frequency
> > > > % observerdistance: distance between eyes and monitor
> > > > % resolutionWidth: horizontal resolution
> > > > % resolutionHeight: vertical resolution
> > > > % screenWidth: width of screen (needs to be adjusted for different
> > > > screens)
> > > > % screenHeight: height of screen (needs to be adjusted for different
> > > > screens)
> > > >
> > > > % type monitor we are using
> > > > % 1 AOC: 1920x1080 509.2x286.4mm
> > > > % 2 True3Di 19 inch: 1280x1024 376.32x301.056mm
> > > > % 3 True3Di 22 inch: 1280x1024 473.76x296.1mm
> > > > monitorType = 1;
> > > >
> > > > if nargin > 8 || nargin < 8
> > > > visualAngleWidth = 1.5;
> > > > visualAngleHeight = 1.5;
> > > > spatialFrequency = 8;
> > > > observerdistance = 3000;
> > > > if monitorType == 1
> > > > resolutionWidth = 1920;
> > > > resolutionHeight = 1080;
> > > > screenWidth = 509.2;
> > > > screenHeight = 286.4;
> > > > elseif monitorType == 2
> > > > resolutionWidth = 1280;
> > > > resolutionHeight = 1024;
> > > > screenWidth = 376.32;
> > > > screenHeight = 301.056;
> > > > elseif monitorType == 3
> > > > resolutionWidth = 1280;
> > > > resolutionHeight = 1024;
> > > > screenWidth = 473.76;
> > > > screenHeight = 296.1;
> > > > end
> > > > end
> > > >
> > > > pixelWidth =
> > > > round(tan(degtorad(visualAngleWidth/2))*2*observerdistance*resolutionWid\
> > > > th/screenWidth);
> > > > pixelHeight =
> > > > round(tan(degtorad(visualAngleHeight/2))*2*observerdistance*resolutionHe\
> > > > ight/screenHeight);
> > > >
> > > > spatialFrequencyPixelWidth = spatialFrequency/(pixelWidth*2);
> > > > spatialFrequencyPixelHeight = spatialFrequency/(pixelHeight*2);
> > > >
> > > > high_strength_AOC_anaglyphs_v4.m
> > > > function high_strength_AOC_anaglyphs_v4
> > > >
> > > > %%%%%%%%%%%%%%%%%%%
> > > > % Observer parameters
> > > > % inches from screen = 118.11
> > > > %%%%%%%%%%%%%%%%%%%
> > > >
> > > > %%%%%%%%%%%%%%%%%%%
> > > > % Screen parameters
> > > >
> > > > % resolution (22') = 1680 x 1024
> > > > % active display size (22') = 473.76(h) x 296.1(w)
> > > >
> > > > % resolution (19') = 1280 x 1024
> > > > % active display size (19') = 376.32(h) x 301.056(w)
> > > > %%%%%%%%%%%%%%%%%%%
> > > >
> > > > %%%%%%%%%%%%%%%%%%%
> > > > % Stimuli paramters
> > > >
> > > > % aperture size (visual angle) = 1.5 deg
> > > > % grating pixel size (22') = 278.526 (width) & 271.629 (height)
> > > > % grating pixel size (19') = 267.158 (width & height)
> > > > %%%%%%%%%%%%%%%%%%%
> > > >
> > > > % high strength: s.f. = 8 cycles/deg; drift speed = 4 cycles/sec
> > > > % low strength: s.f. = 4 cycles/deg; drift speed = 0 cycles/sec
> > > >
> > > > Screen('Preference', 'SkipSyncTests', 1);
> > > >
> > > > try
> > > > % 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
> > > > AssertOpenGL;
> > > >
> > > > % 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. Chosing the display with the highest dislay
> > > > number is
> > > > % a best guess about where you want the stimulus displayed.
> > > > screens=Screen('Screens');
> > > > screenNumber=0;%max(screens);
> > > >
> > > > % Below values are the predefined parameters of the stimulus
> > > > visualAngleWidth = 1.5;
> > > > visualAngleHeight = 1.5;
> > > > spatialFrequency = 8;
> > > > observerdistance = 3000;
> > > >
> > > > [pixelWidth pixelHeight spatialFrequencyPixelWidth
> > > > spatialFrequencyPixelHeight] = visualangle;
> > > >
> > > > gratingsize = 534; %534 is double of 267.158 roundedup, 534.316 is
> > > > double of 267.158. By default the visible grating is 400 pixels by 400
> > > > pixels in size:
> > > >
> > > > texsize=gratingsize / 2;% Define Half-Size of the grating image
> > > > (i.e. radius).
> > > > texsize_horiz = pixelWidth;
> > > > texsize_vert = pixelHeight;
> > > >
> > > > f=8/gratingsize; % Set at 0.015. Grating frequency in cycles/pixel:
> > > > By default 0.05 cycles per pixel.
> > > > f_horiz=spatialFrequencyPixelWidth; % red
> > > > f_vert=spatialFrequencyPixelHeight; % blue
> > > >
> > > > cyclespersecond = 4;% Speed of grating in cycles per second: 1 cycle
> > > > per second by default.
> > > >
> > > >
> > > > % Find the color values which correspond to white and black: Usually
> > > > % black is always 0 and white 255, but this rule is not true if one
> > > > of
> > > > % the high precision framebuffer modes is enabled via the
> > > > % PsychImaging() commmand, so we query the true values via the
> > > > % functions WhiteIndex and BlackIndex:
> > > > white=WhiteIndex(screenNumber);
> > > > black=BlackIndex(screenNumber);
> > > >
> > > > % Round gray to integral number, to avoid roundoff artifacts with
> > > > some
> > > > % graphics cards:
> > > > gray=round((white+black)/2);
> > > >
> > > > % This makes sure that on floating point framebuffers we still get a
> > > > % well defined gray. It isn't strictly neccessary in this demo:
> > > > if gray == white
> > > > gray=white / 2;
> > > > end
> > > >
> > > > % Contrast 'inc'rement range for given white and gray values:
> > > > inc=white-gray;
> > > >
> > > > % Open a double buffered fullscreen window and set default
> > > > background
> > > > % color to black:
> > > > %[w screenRect]=Screen('OpenWindow',screenNumber, black);
> > > > PsychImaging('PrepareConfiguration');
> > > > [w screenRect]=PsychImaging('OpenWindow', screenNumber, 0, [], [],
> > > > [], 8);
> > > >
> > > > %Obtain width & height of screen
> > > > [width, height] = WindowSize(w);
> > > >
> > > > SetAnaglyphStereoParameters('LeftGains', w, [0.4 0.0 0.0]);
> > > > SetAnaglyphStereoParameters('RightGains', w, [0.0 0.2 0.7]);
> > > >
> > > > % Initially fill left- and right-eye image buffer with black
> > > > background
> > > > % color:
> > > > Screen('SelectStereoDrawBuffer', w, 0);
> > > > Screen('FillRect', w, BlackIndex(screenNumber));
> > > > Screen('SelectStereoDrawBuffer', w, 1);
> > > > Screen('FillRect', w, BlackIndex(screenNumber));
> > > >
> > > > % Enable alpha blending for proper combination of the gaussian
> > > > aperture
> > > > % with the drifting sine grating:
> > > > Screen('BlendFunction', w, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
> > > >
> > > > % Calculate parameters of the grating:
> > > > %
> > > > % First we compute pixels per cycle, rounded up to full pixels, as
> > > > we
> > > > % need this to create a grating of proper size below:
> > > > %p=ceil(1/f)
> > > > p_vert=ceil(1/f_vert)
> > > > p_horiz=ceil(1/f_horiz)
> > > >
> > > > % Also need frequency in radians:
> > > > %fr=f*2*pi;
> > > > fr_horiz=f_horiz*2*pi;
> > > > fr_vert=f_vert*2*pi;
> > > >
> > > > % This is the visible size of the grating. It is twice the
> > > > half-width
> > > > % of the texture plus one pixel to make sure it has an odd number of
> > > > % pixels and is therefore symmetric around the center of the
> > > > texture:
> > > > %visiblesize=2*texsize+1
> > > > visiblesize_horiz=2*texsize_horiz+1
> > > > visiblesize_vert=2*texsize_vert+1
> > > >
> > > > % Create one single static grating image:
> > > > %
> > > > % We only need a texture with a single row of pixels(i.e. 1 pixel in
> > > > height) to
> > > > % define the whole grating! If the 'srcRect' in the 'Drawtexture'
> > > > call
> > > > % below is "higher" than that (i.e. visibleSize >> 1), the GPU will
> > > > % automatically replicate pixel rows. This 1 pixel height saves
> > > > memory
> > > > % and memory bandwith, ie. it is potentially faster on some GPUs.
> > > > %
> > > > % However it does need 2 * texsize + p columns, i.e. the visible
> > > > size
> > > > % of the grating extended by the length of 1 period (repetition) of
> > > > the
> > > > % sine-wave in pixels 'p':
> > > > %x = meshgrid(-texsize:texsize + p, 1);
> > > > x_horiz = meshgrid(-texsize_horiz:texsize_horiz + p_horiz, 1);
> > > > x_vert = meshgrid(-texsize_vert:texsize_vert + p_vert, 1);
> > > >
> > > > % Compute actual cosine grating:
> > > > %grating=gray + inc*cos(fr*x);
> > > >
> > > > % Compute actual square-wave grating:
> > > > %grating=gray + inc*floor(cos(fr*x+1)); %gray + inc*cos(fr*x);
> > > > grating_horiz=gray + inc*floor(cos(fr_horiz*x_horiz+1)); %gray +
> > > > inc*cos(fr*x);
> > > > grating_vert=gray + inc*floor(cos(fr_vert*x_vert+1)); %gray +
> > > > inc*cos(fr*x);
> > > >
> > > > % Store 1-D single row grating in texture:
> > > > %gratingtex=Screen('MakeTexture', w, grating);
> > > > gratingtex_horiz=Screen('MakeTexture', w, grating_horiz);
> > > > gratingtex_vert=Screen('MakeTexture', w, grating_vert);
> > > >
> > > > % Create a single gaussian transparency mask and store it to a
> > > > texture:
> > > > % The mask must have the same size as the visible size of the
> > > > grating
> > > > % to fully cover it. Here we must define it in 2 dimensions and
> > > > can't
> > > > % get easily away with one single row of pixels.
> > > > %
> > > > % We create a two-layer texture: One unused luminance channel which
> > > > we
> > > > % just fill with the same color as the background color of the
> > > > screen
> > > > % 'gray'. The transparency (aka alpha) channel is filled with a
> > > > % gaussian (exp()) aperture mask:
> > > > %mask=ones(2*texsize+1, 2*texsize+1, 2) * gray;
> > > > %[x,y]=meshgrid(-1*texsize:1*texsize,-1*texsize:1*texsize);
> > > > %mask(:, :, 2)=0;%white * (1 - exp(-((x/90).^2)-((y/90).^2)));
> > > > %masktex=Screen('MakeTexture', w, mask);
> > > >
> > > > mask_horiz=ones(2*texsize_horiz+1, 2*texsize_horiz+1, 2) * gray;
> > > >
> > > > [x,y]=meshgrid(-1*texsize_horiz:1*texsize_horiz,-1*texsize_horiz:1*texsi\
> > > > ze_horiz);
> > > > mask_horiz(:, :, 2)=0;
> > > > masktex_horiz=Screen('MakeTexture', w, mask_horiz);
> > > >
> > > > mask_vert=ones(2*texsize_vert+1, 2*texsize_vert+1, 2) * gray;
> > > >
> > > > [x,y]=meshgrid(-1*texsize_vert:1*texsize_vert,-1*texsize_vert:1*texsize_\
> > > > vert);
> > > > mask_vert(:, :, 2)=0;
> > > > masktex_vert=Screen('MakeTexture', w, mask_vert);
> > > >
> > > > % Query maximum useable priorityLevel on this system:
> > > > priorityLevel=MaxPriority(w) %priority set to 1
> > > >
> > > > % We don't use Priority() in order to not accidentally overload
> > > > older
> > > > % machines that can't handle a redraw every 40 ms. If your machine
> > > > is
> > > > % fast enough, uncomment this to get more accurate timing.
> > > > Priority(priorityLevel);
> > > >
> > > > % Definition of the drawn rectangle on the screen:
> > > > % Compute it to be the visible size of the grating, centered on the
> > > > % screen:
> > > > dstRect=[0 0 visiblesize_vert visiblesize_horiz];
> > > > dstRect=CenterRect(dstRect, screenRect);
> > > >
> > > > % Query duration of one monitor refresh interval:
> > > > ifi=Screen('GetFlipInterval', w);
> > > >
> > > > % Translate that into the amount of seconds to wait between screen
> > > > % redraws/updates:
> > > >
> > > > % waitframes = 1 means: Redraw every monitor refresh. If your GPU is
> > > > % not fast enough to do this, you can increment this to only redraw
> > > > % every n'th refresh. All animation paramters will adapt to still
> > > > % provide the proper grating. However, if you have a fine grating
> > > > % drifting at a high speed, the refresh rate must exceed that
> > > > % "effective" grating speed to avoid aliasing artifacts in time,
> > > > i.e.,
> > > > % to make sure to satisfy the constraints of the sampling theorem
> > > > % (See Wikipedia: "Nyquist?Shannon sampling theorem" for a starter,
> > > > if
> > > > % you don't know what this means):
> > > > waitframes = 1;
> > > >
> > > > % Translate frames into seconds for screen update interval:
> > > > waitduration = waitframes * ifi;
> > > >
> > > > % Recompute p, this time without the ceil() operation from above.
> > > > % Otherwise we will get wrong drift speed due to rounding errors!
> > > > p=1/f; % pixels/cycle
> > > > p_horiz=1/f_horiz;
> > > > p_vert=1/f_vert;
> > > >
> > > > % Translate requested speed of the grating (in cycles per second)
> > > > into
> > > > % a shift value in "pixels per frame", for given waitduration: This
> > > > is
> > > > % the amount of pixels to shift our srcRect "aperture" in horizontal
> > > > % directionat each redraw:
> > > > %shiftperframe= cyclespersecond * p * waitduration;
> > > > shiftperframe_horiz= cyclespersecond * p_horiz * waitduration;
> > > > shiftperframe_vert= cyclespersecond * p_vert * waitduration;
> > > >
> > > > % Perform initial Flip to sync us to the VBL and for getting an
> > > > initial
> > > > % VBL-Timestamp as timing baseline for our redraw loop:
> > > > vbl=Screen('Flip', w);
> > > >
> > > >
> > > > %destinationRect for the 2 gratings
> > > > %dst1Rect = [(width/2) - (texsize/2), (height/2) - (texsize/2),
> > > > (width/2) + (texsize/2), (height/2) + (texsize/2)];
> > > > dstRect = [(width/2) - (texsize_horiz/2), (height/2) -
> > > > (texsize_vert/2), (width/2) + (texsize_horiz/2), (height/2) +
> > > > (texsize_vert/2)];
> > > > dstRect_rect = [(width/2) - (texsize/2)-(width/2), (height/2) -
> > > > (texsize/2)-(width/2), (width/2) + (texsize/2)+(width/2), (height/2) +
> > > > (texsize/2)+(width/2)];
> > > >
> > > > %Set index for drifting
> > > > i=0;
> > > >
> > > > % Our framecounter, we love stats ;-)
> > > > fcount = 0;
> > > >
> > > > hidecursor;
> > > >
> > > > % Animationloop:
> > > > while ~KbCheck %(vbl < vblendtime)
> > > > Screen('SelectStereoDrawBuffer', w, 0);
> > > >
> > > > % Shift the grating by "shiftperframe" pixels per frame:
> > > > % the mod'ulo operation makes sure that our "aperture" will snap
> > > > % back to the beginning of the grating, once the border is
> > > > reached.
> > > > % Fractional values of 'xoffset' are fine here. The GPU will
> > > > % perform proper interpolation of color values in the grating
> > > > % texture image to draw a grating that corresponds as closely as
> > > > % technical possible to that fractional 'xoffset'. GPU's use
> > > > % bilinear interpolation whose accuracy depends on the GPU at
> > > > hand.
> > > > % Consumer ATI hardware usually resolves 1/64 of a pixel,
> > > > whereas
> > > > % consumer NVidia hardware usually resolves 1/256 of a pixel.
> > > > You
> > > > % can run the script "DriftTexturePrecisionTest" to test your
> > > > % hardware...
> > > > %xoffset = mod(i*shiftperframe,p);
> > > > xoffset_horiz = mod(i*shiftperframe_horiz,p_horiz);
> > > > xoffset_vert = mod(i*shiftperframe_vert,p_vert);
> > > >
> > > > i=i+1;
> > > >
> > > > % Define shifted srcRect that cuts out the properly shifted
> > > > rectangular
> > > > % area from the texture: We cut out the range 0 to visiblesize
> > > > in
> > > > % the vertical direction although the texture is only 1 pixel in
> > > > % height! This works because the hardware will automatically
> > > > % replicate pixels in one dimension if we exceed the real
> > > > borders
> > > > % of the stored texture. This allows us to save storage space
> > > > here,
> > > > % as our 2-D grating is essentially only defined in 1-D:
> > > > %srcRect=[xoffset 0 xoffset + visiblesize_vert
> > > > visiblesize_horiz];
> > > > srcRect_horiz=[xoffset_horiz 0 xoffset_horiz + visiblesize_vert
> > > > visiblesize_horiz];
> > > > srcRect_vert=[xoffset_vert 0 xoffset_vert + visiblesize_vert
> > > > visiblesize_horiz];
> > > >
> > > > % Fill circular 'dstRect' region with an alpha value of 255:
> > > > %Screen('FillOval', w, [0 0 0 255], dstRect);
> > > >
> > > > Screen('SelectStereoDrawBuffer', w, 0);
> > > > Screen('Blendfunction', w, GL_ONE, GL_ZERO, [0 0 0 1]);
> > > > Screen('FillRect', w, [0 0 0 0], dstRect_rect);
> > > > Screen('FillOval', w, [0 0 0 255], dstRect);
> > > > Screen('Blendfunction', w, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA,
> > > > [1 1 1 1]);
> > > > %Screen('DrawTexture', w, gratingtex, srcRect, dst1Rect, 180,
> > > > [], [], [255 255 255]); %draw left vertical grating
> > > > Screen('DrawTexture', w, gratingtex_horiz, srcRect_horiz,
> > > > dstRect, 180, [], [], [255 255 255]); %draw left vertical grating
> > > >
> > > > Screen('SelectStereoDrawBuffer', w, 1);
> > > > Screen('Blendfunction', w, GL_ONE, GL_ZERO, [0 0 0 1]);
> > > > Screen('FillRect', w, [0 0 0 0], dstRect_rect);
> > > > Screen('FillOval', w, [0 0 0 255], dstRect);
> > > > Screen('Blendfunction', w, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA,
> > > > [1 1 1 1]);
> > > > %Screen('DrawTexture', w, gratingtex, srcRect, dst1Rect, 270,
> > > > [], [], [255 255 255]); %draw right horizontal grating
> > > > Screen('DrawTexture', w, gratingtex_vert, srcRect_vert, dstRect,
> > > > 270, [], [], [255 255 255]); %draw right horizontal grating
> > > >
> > > > %if drawmask==1
> > > > % Draw gaussian mask over grating:
> > > > %Screen('DrawTexture', w, masktex, [0 0 visiblesize
> > > > visiblesize], dstRect, angle);
> > > > %end;
> > > >
> > > > % Restore alpha blending mode for next draw iteration:
> > > > Screen('Blendfunction', w, GL_SRC_ALPHA,
> > > > GL_ONE_MINUS_SRC_ALPHA);
> > > >
> > > > % Flip 'waitframes' monitor refresh intervals after last redraw.
> > > > % Providing this 'when' timestamp allows for optimal timing
> > > > % precision in stimulus onset, a stable animation framerate and
> > > > at
> > > > % the same time allows the built-in "skipped frames" detector to
> > > > % work optimally and report skipped frames due to hardware
> > > > % overload:
> > > > vbl = Screen('Flip', w, vbl + (waitframes - 0.5) * ifi);
> > > >
> > > > fcount = fcount + 1;
> > > >
> > > > % Abort demo if any key is pressed:
> > > > %if KbCheck
> > > > %break;
> > > > %end;
> > > > end;
> > > >
> > > > % Restore normal priority scheduling in case something else was set
> > > > % before:
> > > > Priority(0);
> > > >
> > > > showcursor;
> > > >
> > > > %The same commands wich close onscreen and offscreen windows also
> > > > close
> > > > %textures.
> > > > Screen('CloseAll');
> > > >
> > > > catch
> > > > %this "catch" section executes in case of an error in the "try"
> > > > section
> > > > %above. Importantly, it closes the onscreen window if its open.
> > > > %Screen('CloseAll');
> > > > Priority(0);
> > > > psychrethrow(psychlasterror);
> > > > end %try..catch..
> > > >
> > > > % We're done!
> > > > return;
> > > >
> > >
> >
>