function [blobid, blobrect] = CreateProceduralGaussBlob(windowPtr, width, height, backgroundColorOffset, disableNorm, contrastPreMultiplicator) % [blobid, blobrect] = CreateProceduralGaussBlob(windowPtr, width, height [, backgroundColorOffset =(0,0,0,0)][, disableNorm=0][, contrastPreMultiplicator=1]) % % Creates a procedural texture that allows to draw "Gaussian blob" stimulus % patches in a very fast and efficient manner on modern graphics hardware. % % This works on GPU's with support for the OpenGL shading language and % vertex- and fragment shaders. See ProceduralGarboriumDemo for examples % on how to use this function. % % Parameters and their meaning: % % 'windowPtr' A handle to the onscreen window. % 'width' x 'height' The maximum size (in pixels) of the blob. More % precise, the size of the mathematical support of the blob. Providing too % small values here would 'cut off' peripheral parts or your blob. Too big % values don't hurt wrt. correctness or accuracy, they just hurt % performance, ie. drawing speed. Use a reasonable size for your purpose. % % 'backgroundColorOffset' Optional, defaults to [0 0 0 0]. A RGBA offset % color to add to the final RGBA colors of the drawn blob, prior to % drawing it. % % 'disableNorm' Optional, defaults to 0. If set to a value of 1, the % special multiplicative normalization term normf = 1/(sqrt(2*pi) * sc) % will not be applied to the computed blob. By default (setting 0), it % will be applied. This term seems to be a reasonable normalization of the % total amplitude of the blob, but it is not part of the standard % definition of a blob. Therefore we allow to disable this normalization. % % 'contrastPreMultiplicator' Optional, defaults to 1. This value is % multiplied as a scaling factor to the requested contrast value. If you % set the 'disableNorm' parameter to 1 to disable the builtin normf % normalization and then specify contrastPreMultiplicator = 0.5 then the % per blob 'contrast' value will correspond to what practitioners of the % field usually understand to be the contrast value of a blob. % % % The function returns a procedural texture handle 'blobid' that you can % pass to the Screen('DrawTexture(s)', windowPtr, blobid, ...) functions % like any other texture handle. The 'blobrect' is a rectangle which % describes the size of the blob support. % % A typical invocation to draw a single blob patch may look like this: % % Screen('DrawTexture', windowPtr, blobid, [], dstRect, Angle, [], [], % modulateColor, [], kPsychDontDoRotation, [contrast, sc, aspectratio, 0]); % % Draws the blob 'blobid' into window 'windowPtr', at position 'dstRect', % or in the center if 'dstRect' is set to []. Make sure 'dstRect' has the % size of 'blobrect' to avoid spatial distortions! You could do, e.g., % dstRect = OffsetRect(blobrect, xc, yc) to place the blob centered at % screen position (xc,yc). % % The definition of the blob mostly follows the definition of Wikipedia for a % gabor patch, just that there isn't a sin() term to define a grating. Instead % there's assumed to be a constant 1.0, so you only get the shape of the % exponential function - a gaussian blob. % % Wikipedia's definition (better readable): http://en.wikipedia.org/wiki/Gabor_filter % See http://tech.groups.yahoo.com/group/psychtoolbox/message/9174 for % Psychtoolbox forum message 9174 with an in-depth discussion of this % function. % % 'Angle' is the optional orientation angle in degrees (0-360), default is zero degrees. % % 'modulateColor' is the base color of the blob patch - it defaults to % white, ie. the blob has only luminance, but no color. If you'd set it to % [255 0 0] you'd get a reddish blob. % % 'sc' is the spatial constant of the gaussian hull function of the blob, ie. % the "sigma" value in the exponential function. % % 'contrast' is the amplitude of your blob in intensity units - A factor % that is multiplied to the evaluated blob equation before converting the % value into a color value. 'contrast' may be a bit of a misleading term % here... % % 'aspectratio' Defines the aspect ratio of the hull of the blob. % % Make sure to use the Screen('DrawTextures', ...); function properly, % instead of the Screen('DrawTexture', ...); function, if you want to draw % many different blobs simultaneously - this is much faster! % % History: % 24-Nov-2014 Written. (MK) debuglevel = 1; % Global GL struct: Will be initialized in the LoadGLSLProgramFromFiles % below: global GL; if nargin < 3 || isempty(windowPtr) || isempty(width) || isempty(height) error('You must provide "windowPtr", "width" and "height"!'); end if nargin < 4 || isempty(backgroundColorOffset) backgroundColorOffset = [0 0 0 0]; else if length(backgroundColorOffset) < 4 error('The "backgroundColorOffset" must be a 4 component RGBA vector [red green blue alpha]!'); end end if nargin < 5 || isempty(disableNorm) disableNorm = 0; end if ~isscalar(disableNorm) || ~ismember(disableNorm, [0, 1]) error('The "disableNorm" flag must be 0 or 1. Invalid flag passed!'); end if nargin < 6 || isempty(contrastPreMultiplicator) contrastPreMultiplicator = 1.0; end % Switch to windowPtr OpenGL context: Screen('GetWindowInfo', windowPtr); % Make sure we have support for shaders, abort otherwise: AssertGLSL; % Load gaussian blob shader: blobShader = LoadGLSLProgramFromFiles('BasicGaussBlobShader', debuglevel); % Setup shader: glUseProgram(blobShader); % Set the 'Center' parameter to the center position of the patch [tw/2, th/2]: glUniform2f(glGetUniformLocation(blobShader, 'Center'), width/2, height/2); glUniform4f(glGetUniformLocation(blobShader, 'Offset'), backgroundColorOffset(1),backgroundColorOffset(2),backgroundColorOffset(3),backgroundColorOffset(4)); % Assign disable flag for normalization: glUniform1f(glGetUniformLocation(blobShader, 'disableNorm'), disableNorm); % Apply contrast premultiplier: glUniform1f(glGetUniformLocation(blobShader, 'contrastPreMultiplicator'), contrastPreMultiplicator); % Setup done: glUseProgram(0); % Create a purely virtual procedural texture 'blobid' of size width x height virtual pixels. % Attach the blobShader to it to define its appearance: blobid = Screen('SetOpenGLTexture', windowPtr, [], 0, GL.TEXTURE_RECTANGLE_EXT, width, height, 1, blobShader); % Query and return its bounding rectangle: blobrect = Screen('Rect', blobid); % Ready! return;