function retval = SetAnaglyphStereoParameters(cmd, win, rgb, aux1, aux2) % retval = SetAnaglyphStereoParameters(subcommand, windowPtr, [red green blue]); % % Change parameters of built-in anaglyph stereo display function at % runtime. Allows to change all relevant settings of the algorithm at any % time in your script. You need to open your onscreen window with the % 'stereomode' parameter set to one of the anaglyph modes (i.e. one of % 6=Red-Green anaglyph, 7=Green-Red, 8=Red-Blue, 9=Blue-Red, where % Red-Green means "Left eye displayed in red channel, right eye displayed % in green channel"). % % Anaglyph stereo also works in stereo modes 2, 3, 4, 5 and 10 if you used % the ... % PsychImaging('AddTask', 'LeftView', 'DisplayColorCorrection', 'AnaglyphStereo') % ... and ... % PsychImaging('AddTask', 'RightView', 'DisplayColorCorrection', 'AnaglyphStereo') % ... setup sequence to add an anaglyph shader to the end of each view % channel. However, in those "non-pure" exotic anaglyph modes only a subset % of the commands mentioned here work, specifically the ones for advanced % anaglyph display, ie., subfunctions 'ColorAnaglyphMode' and friends. Inverted mode % or simple mode or changing NTSC weights or simple gains does not work in % this configuration. % % Additionally you also need to enable the Psychtoolbox image processing % pipeline by using the PsychImaging() command for configuring and opening % onscreen windows. % % Example: % PsychImaging('PrepareConfiguration'); % stereomode = 6; % windowPtr = PsychImaging('OpenWindow', screenid, bgcolor, [], [], [], stereomode); % % See 'help PsychImaging' and 'help PsychGLImageProcessing' for an overview % of the imaging pipeline. % % Parameters and their meaning: % ----------------------------- % % 'windowPtr' is the handle to a anaglyph stereo onscreen window. % % 'subcommand' can be one of: % % 'LeftGains' - Provide (in optional vector [red green blue]) per-channel % color gains for computation of the left-eye channel. This allows to % adjust the output colors to optimally match the color emission / filter % profile of your anaglyph glasses and monitor - Reduce ghosting / visual % crosstalk. E.g. % % SetAnaglyphStereoParameters('LeftGains', win, [1.0 0.0 0.0]); sets the % red-gain for the left eye image to 1.0 and disables output of the left % eye image into the green or blue channels. This is the default setting % for Red-Green or Red-Blue anaglyph, left eye view only goes into red % channel. % % The command always returns the old gains as a 3 component vector. % % 'RightGains' - See left gains, but this time for right image: % SetAnaglyphStereoParameters('RightGains', win, [0.0 0.7 0.0]); % would set the green channel to 70% output intensity and disable the red- % and blue channels. This is a typical setting for Red-Green anaglyph % stereo: Right eye only written to green color channel, but with reduced % output intensity to compensate for the stronger green-sensitivity of the % human eye. % % 'ColorToLuminanceWeights' - Set the color to luminance conversion % weights: PTB converts all color images into pure luminance (greyscale) % images before distributing them into the color channels via the formula: % Luminance = redvalue * redweight + bluevalue * blueweight + greenvalue * % greenweight. % % The default red- green- and blue-weights are (0.3, 0.59, 0.11), so: % L = 0.3*red + 0.59*green + 0.11*blue. This is according to NTSC standard % (if i remember correctly!). You can change the weights via: % % SetAnaglyphStereoParameters('ColorToLuminanceWeights', win, [redweight greenweight blueweight]); % % The command always returns the old weights as a 3 component vector. % % 'BackgroundColorBias' - Set the "background color" for inverted anaglyph % mode. This color value is added to each pixel. It defaults to zero - bias % free display. For inverted mode, one usually sets it to maximum output % intensity, so that the default background is a bright output. % % 'InvertedMode' - Switch to inverted display mode, i.e., set the % 'BackgroundColorBias' and color gains properly. The image is displayed in % an inverted fashion, like a photo-negative. This allows for less ghosting % artifacts if your stimulus is sparse, e.g., dots and lines. This trick % was proposed by Massimiliano di Luca. % % 'NormalMode' - Switch from inverted mode to normal anaglyph mode. % Background color bias is set to zero, gains a positive. % % Color reproduction in color images with Anaglyphs: % % SetAnaglyphStereoParameters('ColorAnaglyphMode', win [,LeftMatrix] [,RightMatrix] [, Gamma]); % % This switches to a more flexible anaglyph implementation, where the % output color is calculated as: % % [redout, greenout, blueout]' = LeftMatrix * [leftred, leftgreen, leftblue]' + RightMatrix * [rightred, rightgreen, rightblue]' % % where [redout, greenout, blueout] == Final anaglyph color pixel. % [leftred, leftgreen, leftblue] == Input left eye pixel color. % [rightred, rightgreen, rightblue] == Input right eye pixel color. % % and LeftMatrix and RightMatrix are 3 by 3 color weight matrices. % % If 'Gamma' is provided, then the 'redout' color is gamma corrected with % the given 'Gamma' value, ie. redout = redout ^ Gamma. % % Multiple sets of possible matrices and gamma weights are already defined % and selectable with the following names: % % SetAnaglyphStereoParameters('OptimizedColorAnaglyphMode', win); % SetAnaglyphStereoParameters('FullColorAnaglyphMode', win); % SetAnaglyphStereoParameters('HalfColorAnaglyphMode', win); % SetAnaglyphStereoParameters('GrayAnaglyphMode', win); % % The idea and matrices/gammas for these four selectable presets are taken % from the following website: % % http://mitglied.lycos.de/stereo3d/anaglyphcomparison.htm % % % Overlay support: % % If you want to have an additional monoscopic overlay window, e.g., for % text instructions to the subject, fixation dots etc., you can get one via % a call to: % % overlaywindow = SetAnaglyphStereoParameters('CreateMonoOverlay', win); % % This will create a fullscreen 'overlaywindow'. You can draw to that % window like to any other window. It's content will be overlaid to the % final anaglyph image, but without applying anaglyph conversion to it. % Wherever this overlay has an alpha color value of zero, the stimulus will % be shown. Wherever its alpha value is greater than zero, the overlays % image will be shown. % % overlaywindow = SetAnaglyphStereoParameters('CreateGreenOverlay', win); % ... and ... % overlaywindow = SetAnaglyphStereoParameters('CreateBlueOverlay', win); % ... will allow to only place the overlay info into the green color channel % or blue color channel, ie., the channel not used by Red-Blue or Red-Green % color anaglyph stereo. % % 'GetHandle' - Return GLSL handle to anaglyph shader. Allows to modify the % shader itself, e.g., replace it by your own customized shader. Only for % OpenGL experts! % % History: % 5.1.2007 Wrote it (MK). % 17.2.2007 Implemented setup code for Max di Lucas inversion trick (MK). % 11.11.2007 Implemented setup code for extended/exotic anaglyph shading % and for color overlay support. (MK) % 14.08.2012 Implement setup code for anaglyph stereo shaders inside per-view % channels, to handle separate display outputs, e.g., as on MPI % Kuka projection setup. % 08.07.2014 Implement 'CreateGreenOverlay' and 'CreateBlueOverlay'. (MK) persistent inverted; % Only works if GL shading language is supported. if isempty(inverted) AssertGLSL; inverted = []; end if nargin < 2 error('Insufficient number of input arguments.'); end if strcmpi(cmd, 'CreateMonoOverlay') || ... strcmpi(cmd, 'CreateGreenOverlay') || ... strcmpi(cmd, 'CreateBlueOverlay') winfo = Screen('GetWindowInfo', win); if winfo.StereoMode < 4 || winfo.StereoMode > 9 error('SetAnaglyphStereoParameters(''%s'') is not supported in current stereomode %i!', cmd, winfo.StereoMode); end if (strcmpi(cmd, 'CreateGreenOverlay') && ~ismember(winfo.StereoMode, [8, 9])) || ... (strcmpi(cmd, 'CreateBlueOverlay') && ~ismember(winfo.StereoMode, [6, 7])) error('SetAnaglyphStereoParameters(''%s'') is not supported in current stereomode %i!', cmd, winfo.StereoMode); end % Create a monoscopic overlay window which is not affected by % anaglyph conversion, but just overlaid to the final rendering. glUseProgram(0); % Create Offscreen window for the overlay. It has the same size as % the onscreen window, the same pixeldepth, but a completely black % background with alpha value zero -- fully transparent by default. % The specialflags 32 setting protects the overlay offscreen % window from accidental batch-deletion by usercode calls to % Screen('Close'): overlaywin = Screen('OpenOffscreenWindow', win, [0 0 0 0], [], [], 32); % Retrieve low-level OpenGl texture handle to the window: overlaytex = Screen('GetOpenGLTexture', win, overlaywin); if strcmpi(cmd, 'CreateMonoOverlay') % Append blitter command for a one-to-one blit of the overlay % texture to the target buffer. We need to disable the 2nd texture % unit and setup proper alpha testing, so a) it works at all and b) % the overly only occludes the main image where the overly has a % non-zero alpha: Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup1 Alphatest for Overlay', 'glAlphaFunc(516, 0.0);'); Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup2 Alphatest for Overlay', 'glEnable(3008);'); Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup3 Texunit1 off for Overlay', 'glActiveTexture(33985);'); Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup4 Texunit1 off for Overlay', 'glDisable(34037);'); Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup5 Texunit1 off for Overlay', 'glActiveTexture(33984);'); Screen('Hookfunction', win, 'AppendBuiltin', 'StereoCompositingBlit', 'Builtin:IdentityBlit', sprintf('TEXTURERECT2D(0)=%i', overlaytex)); Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Teardown Alphatest for Overlay', 'glDisable(3008);'); end if strcmpi(cmd, 'CreateGreenOverlay') || strcmpi(cmd, 'CreateBlueOverlay') % Append blitter command for a one-to-one blit of the overlay % texture to the target buffer. We need to disable the 2nd texture % unit. Then we setup the color write mask to only write to the green % or blue channel. Red and blue or green channel will only be set by % anaglyph code, but the green or blue channel will only be set by the % overlay window: if strcmpi(cmd, 'CreateGreenOverlay') Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup1 Green only mask for Overlay', 'glColorMask(0, 1, 0, 0);'); else Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup1 Blue only mask for Overlay', 'glColorMask(0, 0, 1, 0);'); end Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup2 Texunit1 off for Overlay', 'glActiveTexture(33985);'); Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup3 Texunit1 off for Overlay', 'glDisable(34037);'); Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Setup4 Texunit1 off for Overlay', 'glActiveTexture(33984);'); Screen('Hookfunction', win, 'AppendBuiltin', 'StereoCompositingBlit', 'Builtin:IdentityBlit', sprintf('TEXTURERECT2D(0)=%i', overlaytex)); Screen('Hookfunction', win, 'AppendMFunction', 'StereoCompositingBlit', 'Reset colormask after Overlay blit', 'glColorMask(1, 1, 1, 1);'); end % Return handle to overlay window: retval = overlaywin; % Done. return; end try % Assume combined shader in 'StereoCompositingBlit' chain: combinedAnaglyph = 1; % Query GLSL shader handle for anaglyph shader: [slot shaderid blittercfg voidptr glsl] = Screen('HookFunction', win, 'Query', 'StereoCompositingBlit', 'StereoCompositingShaderAnaglyph'); %#ok<*ASGLU> % Shader found? if slot == -1 % Nope. Retry with per-view channels. combinedAnaglyph = 0; [slot shaderid blittercfg voidptr glsl(1)] = Screen('HookFunction', win, 'Query', 'StereoLeftCompositingBlit', 'ICM:AnaglyphStereo'); if slot ~= -1 [slot shaderid blittercfg voidptr glsl(2)] = Screen('HookFunction', win, 'Query', 'StereoRightCompositingBlit', 'ICM:AnaglyphStereo'); end end % Still no shader? if slot == -1 % Game over: error('Either the imaging pipeline is not enabled for given onscreen window, or it is not switched to Anaglyph stereo mode.'); end if glsl == 0 error('Anaglyph shader is not operational for unknown reason. Sorry...'); end % Bind it: glUseProgram(glsl(1)); catch %#ok<*CTCH> % If anything failed. Unbind: glUseProgram(0); psychrethrow(psychlasterror); end % Subcommand dispatch: if strcmpi(cmd, 'GetHandle') retval = glsl; end if strcmpi(cmd, 'InvertedMode') if ~combinedAnaglyph warning('PTB:SetAnaglyphStereoParametersUNSUPPORTED', 'SetAnaglyphStereoParameters(''InvertedMode'') is not supported for separate-viewchannel anaglyph mode. Ignored!'); retval = 0; return; end % Switch to inverted display (Max di Lucas trick): if isempty(find(inverted == win)) %#ok<*EFIND> % Switch needed: Add windowhandle to our list of inverted % windows: inverted = [inverted win]; % Retrieve background color bias, check if its non-zero: uniloc = glGetUniformLocation(glsl, 'ChannelBias'); retval = glGetUniformfv(glsl, uniloc) * WhiteIndex(win); if max(abs(retval)) <= 0 % Bias is zero, that's not suitable for inverted mode. Set % it to maximum output color, except for the components % where both gains are zero, i.e., unused channels: lg=SetAnaglyphStereoParameters('LeftGains', win); rg=SetAnaglyphStereoParameters('RightGains', win); glUseProgram(glsl); glUniform3fv(uniloc, 1, sign(lg + rg)); end % Query and set color gains - This will switch gains properly: SetAnaglyphStereoParameters('LeftGains', win, SetAnaglyphStereoParameters('LeftGains', win)); SetAnaglyphStereoParameters('RightGains', win, SetAnaglyphStereoParameters('RightGains', win)); retval = 1; end end if strcmpi(cmd, 'StandardMode') if ~combinedAnaglyph warning('PTB:SetAnaglyphStereoParametersUNSUPPORTED', 'SetAnaglyphStereoParameters(''StandardMode'') is not supported for separate-viewchannel anaglyph mode. Ignored!'); retval = 0; return; end % Switch to standard, non-inverted display: winidx = find(inverted == win); if ~isempty(winidx) % Switch needed: Remove windowhandle from our list of inverted % windows, actually null it out, don't remove: inverted(winidx) = 0; % Retrieve background color bias, check if its non-zero: uniloc = glGetUniformLocation(glsl, 'ChannelBias'); retval = glGetUniformfv(glsl, uniloc) * WhiteIndex(win); if max(abs(retval)) > 0 % Bias is non-zero, that's not suitable for inverted mode. Set % it to black output color: glUniform3fv(uniloc, 1, [0 0 0]); end % Query and set color gains - This will switch gains properly: SetAnaglyphStereoParameters('LeftGains', win, SetAnaglyphStereoParameters('LeftGains', win)); SetAnaglyphStereoParameters('RightGains', win, SetAnaglyphStereoParameters('RightGains', win)); retval = 0; end end if strcmpi(cmd, 'BackgroundColorBias') if ~combinedAnaglyph warning('PTB:SetAnaglyphStereoParametersUNSUPPORTED', 'SetAnaglyphStereoParameters(''BackgroundColorBias'') is not supported for separate-viewchannel anaglyph mode. Ignored!'); retval = [0, 0, 0]; return; end uniloc = glGetUniformLocation(glsl, 'ChannelBias'); retval = glGetUniformfv(glsl, uniloc) * WhiteIndex(win); if nargin>=3 if size(rgb)~=3 error('Provided call parameter must be a 3 component vector with (R,G,B) bias color values.'); end % Normalize from color range of window to GL's 0-1 range: rgb = rgb / WhiteIndex(win); glUniform3fv(uniloc, 1, rgb); end end if strcmpi(cmd, 'ColorToLuminanceWeights') if ~combinedAnaglyph warning('PTB:SetAnaglyphStereoParametersUNSUPPORTED', 'SetAnaglyphStereoParameters(''ColorToLuminanceWeights'') is not supported for separate-viewchannel anaglyph mode. Ignored!'); retval = [0, 0, 0]; return; end uniloc = glGetUniformLocation(glsl, 'ColorToGrayWeights'); retval = glGetUniformfv(glsl, uniloc); if nargin>=3 if size(rgb)~=3 error('Provided call parameter must be a 3 component vector with color weights or gains.'); end glUniform3fv(uniloc, 1, rgb); end end if strcmpi(cmd, 'LeftGains') if ~combinedAnaglyph warning('PTB:SetAnaglyphStereoParametersUNSUPPORTED', 'SetAnaglyphStereoParameters(''LeftGains'') is not supported for separate-viewchannel anaglyph mode. Ignored!'); retval = [0, 0, 0]; return; end uniloc = glGetUniformLocation(glsl, 'Gains1'); retval = abs(glGetUniformfv(glsl, uniloc)); if find(inverted == win) inverter = -1; else inverter = 1; end if nargin>=3 if size(rgb)~=3 error('Provided call parameter must be a 3 component vector with color weights or gains.'); end glUniform3fv(uniloc, 1, inverter * rgb); end end if strcmpi(cmd, 'RightGains') if ~combinedAnaglyph warning('PTB:SetAnaglyphStereoParametersUNSUPPORTED', 'SetAnaglyphStereoParameters(''RightGains'') is not supported for separate-viewchannel anaglyph mode. Ignored!'); retval = [0, 0, 0]; return; end uniloc = glGetUniformLocation(glsl, 'Gains2'); retval = abs(glGetUniformfv(glsl, uniloc)); if find(inverted == win) inverter = -1; else inverter = 1; end if nargin>=3 if size(rgb)~=3 error('Provided call parameter must be a 3 component vector with color weights or gains.'); end glUniform3fv(uniloc, 1, inverter * rgb); end end if strcmpi(cmd, 'FullColorAnaglyphMode') % Implement colored anaglyph rendering for full color anaglyphs: % Store left-gain matrix in 'rgb': rgb = [[1 0 0]; [0 0 0]; [0 0 0]]; % Store right-gain matrix in 'aux1': aux1 = [[0 0 0]; [0 1 0]; [0 0 1]]; % Rewrite cmd, so the generic setup code for color anaglyphs gets % called to do the actual setup work: cmd = 'ColorAnaglyphMode'; end if strcmpi(cmd, 'GrayAnaglyphMode') % Implement colored anaglyph rendering for gray anaglyphs: % Store left-gain matrix in 'rgb': rgb = [[0.299 0.587 0.114]; [0 0 0]; [0 0 0]]; % Store right-gain matrix in 'aux1': aux1 = [[0 0 0]; [0.299 0.587 0.114]; [0.299 0.587 0.114]]; % Rewrite cmd, so the generic setup code for color anaglyphs gets % called to do the actual setup work: cmd = 'ColorAnaglyphMode'; end if strcmpi(cmd, 'HalfColorAnaglyphMode') % Implement colored anaglyph rendering for half color anaglyphs: % Store left-gain matrix in 'rgb': rgb = [[0.299 0.587 0.114]; [0 0 0]; [0 0 0]]; % Store right-gain matrix in 'aux1': aux1 = [[0 0 0]; [0 1 0]; [0 0 1]]; % Rewrite cmd, so the generic setup code for color anaglyphs gets % called to do the actual setup work: cmd = 'ColorAnaglyphMode'; end if strcmpi(cmd, 'OptimizedColorAnaglyphMode') % Implement colored anaglyph rendering for optimized color anaglyphs: % Store left-gain matrix in 'rgb': rgb = [[0 0.7 0.3]; [0 0 0]; [0 0 0]]; % Store right-gain matrix in 'aux1': aux1 = [[0 0 0]; [0 1 0]; [0 0 1]]; % Setup gamma correction for red channel with a gamma of 1.5: aux2 = 1.5; % Rewrite cmd, so the generic setup code for color anaglyphs gets % called to do the actual setup work: cmd = 'ColorAnaglyphMode'; end if strcmpi(cmd, 'ColorAnaglyphMode') % Switch from standard luminance monochrome anaglyph shader (as % created by Screen('OpenWindow', ...) by default) to a more % complex and flexible shader. Either that, or reparameterize that % shader... % Color anaglyph shader already bound to imaging pipeline? Only for % regular combined case, as we know the proper shader is bound % otherwise, because it was already created by % PsychColorCorrection() during pipeline setup in PsychImaging(): if combinedAnaglyph && ~strcmpi(shaderid, 'StereoCompositingShaderAnaglyphExtended') % Nope. Destroy old default shader and replace it by our % shader: Screen('HookFunction', win, 'Remove', 'StereoCompositingBlit', slot); % Load shader from file: colorshader = LoadGLSLProgramFromFiles('ColoredAnaglyphsShader', 1); glsl = colorshader; % Setup stereo image to texture unit mappings: glUseProgram(colorshader); glUniform1i(glGetUniformLocation(colorshader, 'Image1'), 0); glUniform1i(glGetUniformLocation(colorshader, 'Image2'), 1); % Setup default gamma correction for red channel to zero, ie, % no gamma correction at all! glUniform1f(glGetUniformLocation(colorshader, 'RedGamma'), 0); % Insert it at former position of the old shader: posstring = sprintf('InsertAt%iShader', slot); Screen('Hookfunction', win, posstring, 'StereoCompositingBlit', 'StereoCompositingShaderAnaglyphExtended', colorshader); % Ok, shader ready. Now for the setup of conversion % parameters... end if combinedAnaglyph % Regular case, one shader to rule them all: unilocleft = glGetUniformLocation(glsl, 'GainsLeft'); unilocright = glGetUniformLocation(glsl, 'GainsRight'); gammaloc = glGetUniformLocation(glsl, 'RedGamma'); if exist('rgb', 'var') && ~isempty(rgb) if size(rgb,1)~=3 || size(rgb,2)~=3 error('Provided left-gain matrix parameter must be a 3x3 component matrix.'); end glUniformMatrix3fv( unilocleft, 1, 0, rgb); end if exist('aux1', 'var') && ~isempty(aux1) if size(aux1,1)~=3 || size(aux1,2)~=3 error('Provided right-gain matrix parameter must be a 3x3 component matrix.'); end glUniformMatrix3fv( unilocright, 1, 0, aux1); end if exist('aux2', 'var') && ~isempty(aux2) if size(aux2,1)~=1 || size(aux2,2)~=1 error('Provided gamma parameter must be a scalar.'); end glUniform1f( gammaloc, aux2); end else % Separate shaders for left- and right view channel: % Handle left channel shader first: unilocleft = glGetUniformLocation(glsl(1), 'GainsLeft'); gammaloc = glGetUniformLocation(glsl(1), 'RedGamma'); glUseProgram(glsl(1)); if exist('rgb', 'var') && ~isempty(rgb) if size(rgb,1)~=3 || size(rgb,2)~=3 error('Provided left-gain matrix parameter must be a 3x3 component matrix.'); end glUniformMatrix3fv( unilocleft, 1, 0, rgb); end if exist('aux2', 'var') && ~isempty(aux2) if size(aux2,1)~=1 || size(aux2,2)~=1 error('Provided gamma parameter must be a scalar.'); end glUniform1f( gammaloc, aux2); end % Handle right channel shader similarly: glUseProgram(glsl(2)); % Even the "right gain matrix" is named "GainsLeft" in this case: unilocright = glGetUniformLocation(glsl(2), 'GainsLeft'); gammaloc = glGetUniformLocation(glsl(2), 'RedGamma'); if exist('aux1', 'var') && ~isempty(aux1) if size(aux1,1)~=3 || size(aux1,2)~=3 error('Provided right-gain matrix parameter must be a 3x3 component matrix.'); end glUniformMatrix3fv( unilocright, 1, 0, aux1); end if exist('aux2', 'var') && ~isempty(aux2) if size(aux2,1)~=1 || size(aux2,2)~=1 error('Provided gamma parameter must be a scalar.'); end glUniform1f( gammaloc, aux2); end end end try % Unbind shader again... glUseProgram(0); catch psychrethrow(psychlasterror); end % Done. return;