function CylinderAnnulusOpenGLDemo(patternType, multiSample) % CylinderAnnulusOpenGLDemo([patternType=0][, multiSample=0]) % % This demo demonstrates use of OpenGL commands in a Matlab script to % map a 2D image onto a 3D cylindrical surface. % % It loads a JPEG image of the earths surface from the filesystem, using % Matlabs imread() function, then converts the image into a Psychtoolbox % texture using Screen('MakeTexture'), then provides this texture as a % standard OpenGL compatible texture using Screen('GetOpenGLTexture'). % This standard texture is applied to a cylinder using standard OpenGL commands % and finally the cylinder is drawn as a rotating object in a simple animation % loop. --> You'll see a rotating cylinder. % % Stop the demo by pressing any key and it will finish. % % The optional parameter 'multiSample' allows to enable anti-aliased % drawing with 'multiSample' samples per pixel on hardware that supports % this. % % The optional parameter 'patternType' allows (if set to non-zero % value) to apply a specific pattern to the spinning cylinder, instead of % a "earth surface texture image". This demonstrates algorithmic texture % generation and the use of trilinear mipmap filtering to improve image % quality for high frequency edges and such... % % 0 = Jpeg image of earth surface. % 1 = Checkerboard pattern. % 2 = Simple vertical annulus. % % The OpenGL Red Book is a great introduction and reference for OpenGL % programming. Release 1.0 is available online, later releases can be % purchased in any good book store: % % http://www.opengl.org/documentation/red_book_1.0/ % % For more infos, code samples, tutorials, online documentation, go to: % % http://www.opengl.org % % The earth surface JPEG-image is taken from the Linux/KDE application % kdeworldclock. kdeworldclock and its components are licensed under % GPL. % History: % 09-Aug-2009 -- Derived from MinimalisticOpenGLDemo (MK) if nargin < 2 multiSample = []; end if nargin < 1 patternType = []; end if isempty(patternType) patternType = 0; end % Is the script running in OpenGL Psychtoolbox? Abort, if not. AssertOpenGL; % Find the screen to use for display: screenid=max(Screen('Screens')); % Setup Psychtoolbox for OpenGL 3D rendering support and initialize the % mogl OpenGL for Matlab wrapper: InitializeMatlabOpenGL; % Open a double-buffered full-screen window on the main displays screen. PsychImaging('PrepareConfiguration'); PsychImaging('AddTask', 'General', 'UseVirtualFramebuffer'); [win, winRect] = PsychImaging('OpenWindow', screenid, 0, [], [], [], [], multiSample); % Setup the OpenGL rendering context of the onscreen window for use by % OpenGL wrapper. After this command, all following OpenGL commands will % draw into the onscreen window 'win': Screen('BeginOpenGL', win); % Get the aspect ratio of the screen: ar=winRect(4)/winRect(3); % Setup default drawing color to white: This is the base color of the % cylinders projection, which is modulated in intensity or color by the % texture pattern: glColor3f(1,1,1); % Enable proper (self-)occlusion handling via depth tests: glEnable(GL.DEPTH_TEST); % Set projection matrix: This defines a perspective projection, % corresponding to the model of a pin-hole camera - which is a good % approximation of the human eye and of standard real world cameras -- % well, the best aproximation one can do with 3 lines of code ;-) glMatrixMode(GL.PROJECTION); glLoadIdentity; % Field of view is 25 degrees from line of sight. Objects closer than % 0.1 distance units or farther away than 100 distance units get clipped % away and not drawn. Aspect ratio is adapted to the monitors aspect ratio: gluPerspective(25, 1/ar, 0.1, 100); % Setup modelview matrix: This defines the position, orientation and % looking direction of the virtual camera or virtual projector: glMatrixMode(GL.MODELVIEW); glLoadIdentity; % Cam/Projector is located at 3D position (0,0,15), points upright (0,1,0) and fixates % at the origin (0,0,0) of the worlds coordinate system: % % The OpenGL coordinate system is a right-handed system as follows: % Default origin is in the center of the display. % Positive x-Axis points horizontally to the right. % Positive y-Axis points vertically upwards. % Positive z-Axis points to the observer, perpendicular to the display % screens surface. gluLookAt(0, 0, 15, 0, 0, 0, 0, 1, 0); % Set background clear color to 'black' (R,G,B,A)=(0,0,0,0): glClearColor(0,0,0,0); % Clear out the backbuffer: This also cleans the depth-buffer for % proper occlusion handling: You need to glClear the depth buffer whenever % you redraw your scene, e.g., in an animation loop. Otherwise occlusion % handling will screw up in funny ways... glClear; % Finish OpenGL rendering into PTB window. This will switch back to the % standard 2D drawing functions of Screen and will check for OpenGL errors. Screen('EndOpenGL', win); switch (patternType) case 0, % Prepare texture to by applied to the sphere: Load & create it from an image file: myimg = imread([PsychtoolboxRoot 'PsychDemos/OpenGL4MatlabDemos/earth_512by256.jpg']); case 1, % Apply regular checkerboard pattern as texture: bv = zeros(16); wv = ones(16); myimg = double(repmat([bv wv; wv bv],32,32) > 0.5) * 255; case 2, % Apply an annulus pattern as texture: % Everything white... myimg = ones(1024, 1) * 255; % ...except for a vertical stripe along the cylinder of width 100 % texels, which we color in a nice 50% intensity of 128 instead % 255. myimg(1:100, :) = 128; otherwise sca; error('Unknown pattern type!'); end % Make a special power-of-two texture from the image by setting the enforcepot - flag to 1 % when calling 'MakeTexture'. GL_TEXTURE_2D textures (==power of two textures) are % especially easy to handle in OpenGL. If you use the enforcepot flag, it is important % that the texture image 'myimg' has a width and a height that is exactly a power of two, % otherwise this command will fail: Allowed values for image width and height are, e.g., % 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048 and on some high-end gfx cards % 4096 pixels. Our example image has a size of 512 by 256 pixels... % Psychtoolbox also supports rectangular textures of arbitrary size, so called % GL_TEXTURE_RECTANGLE_2D textures. These are normally used for Screen's drawing % commands, but they are more difficult to handle in standard OpenGL code... mytex = Screen('MakeTexture', win, myimg, [], 1); % Retrieve OpenGL handles to the PTB texture. These are needed to use the texture % from "normal" OpenGL code: [gltex, gltextarget] = Screen('GetOpenGLTexture', win, mytex); % Begin OpenGL rendering into onscreen window again: Screen('BeginOpenGL', win); % Enable texture mapping for this type of textures... glEnable(gltextarget); % Bind our texture, so it gets applied to all following objects: glBindTexture(gltextarget, gltex); % Textures color texel values shall modulate the color computed by lighting model: glTexEnvfv(GL.TEXTURE_ENV, GL.TEXTURE_ENV_MODE, GL.MODULATE); % Clamping behaviour shall be a cyclic repeat: glTexParameteri(gltextarget, GL.TEXTURE_WRAP_S, GL.REPEAT); glTexParameteri(gltextarget, GL.TEXTURE_WRAP_T, GL.REPEAT); % Set up minification and magnification filters. This is crucial for the thing to work! % We use trilinear mipmap filtering for the minification filter, so we % don't get too many aliasing artifacts for patterns with high-frequency edges. glTexParameteri(gltextarget, GL.TEXTURE_MIN_FILTER, GL.LINEAR_MIPMAP_LINEAR); % Need mipmapping for trilinear filtering --> Create mipmaps: if ~isempty(findstr(glGetString(GL.EXTENSIONS), 'GL_EXT_framebuffer_object')) % Ask the hardware to generate all depth levels automatically: glGenerateMipmapEXT(GL.TEXTURE_2D); else % No hardware support for fast auto-mipmap-generation. Do it "manually": % Use GLU to compute the image resolution mipmap pyramid and create % OpenGL textures ouf of it: This is slow, compared to glGenerateMipmapEXT: r = gluBuild2DMipmaps(gltextarget, GL.LUMINANCE, size(myimg,1), size(myimg,2), GL.LUMINANCE, GL.UNSIGNED_BYTE, uint8(myimg)); if r>0 sca; error('gluBuild2DMipmaps failed for some reason.'); end end % Use bilinear filtering for magnification filter: glTexParameteri(gltextarget, GL.TEXTURE_MAG_FILTER, GL.LINEAR); % Create the cylinder as a quadric object. 'mycylinder' is a handle that you % need to pass to all quadric functions: mycylinder = gluNewQuadric; % Enable automatic generation of texture coordinates for our quadric object: gluQuadricTexture(mycylinder, GL.TRUE); % Apply some static rotation to the object to have a nice view onto it: glRotatef(0.0, 1,0,0); % Rotate around x axis. glRotatef(0.0, 0,1,0); % Rotate around y axis. % Now for our little animation loop. This loop will run until a key is % pressed. It rotates the object by a few degrees (actually: Applies a % rotation transformation to all objects to be drawn) and then redraws it % at its new orientation: while ~KbCheck % Clear out backbuffer and depth buffer: glClear; % Increment rotation angle around z-Axis (0,0,1) by 0.1 degrees: glRotatef(0.1, 0, 0, 1); % Draw a textured cylinder with a radius of 1.0 units at base and top, % height of 10.0 units. Divide the cylinder into 360 slices around % z-axis, ie., 1 degree resolution, and 1 stack along the z-axis. Because % graphics hardware can only render flat surfaces, the curved cylinder is % approximated by a mesh of 360 rectangular panels, arranged to form a % cylinder. Increasing the subdivision beyound 360 may create a better % approximation at the cost of slower drawing speeds: gluCylinder(mycylinder, 1.0, 1.0, 10.0, 360, 1); % Finish OpenGL rendering into PTB window. This will switch back to the % standard 2D drawing functions of Screen and will check for OpenGL errors. Screen('EndOpenGL', win); % Show new image at next retrace: The special dontclear = 2 flag % prevents 'Flip' from clearing the backbuffer, as this is done at the % top of the while-loop with glClear anyway: Screen('Flip', win, 0, 2); % Start OpenGL rendering again after flip for drawing of next frame... Screen('BeginOpenGL', win); % Ready for next draw loop iteration... end; % Done with the drawing loop: Shutdown stuff... % Delete our cylinder object: gluDeleteQuadric(mycylinder); % Unselect our texture... glBindTexture(gltextarget, 0); % ... and disable texture mapping: glDisable(gltextarget); % End of OpenGL rendering... Screen('EndOpenGL', win); % Close onscreen window and release all other ressources: sca; % Well done! return;