diff --git a/fsl/fsleyes/gl/gl14/glvolume_frag.prog b/fsl/fsleyes/gl/gl14/glvolume_frag.prog index 99d0e41ad6cca5a289173141308dc9771ba86885..7fe020ebf69b7e459ee7b13ac22187c7ae572836 100644 --- a/fsl/fsleyes/gl/gl14/glvolume_frag.prog +++ b/fsl/fsleyes/gl/gl14/glvolume_frag.prog @@ -38,7 +38,6 @@ # the range). Clipping values are assumed to be # normalised to the image texture value range. # -# # Outputs: # # result.color - The fragment colour diff --git a/fsl/fsleyes/gl/gl14/glvolume_funcs.py b/fsl/fsleyes/gl/gl14/glvolume_funcs.py index 4ffbe4aa8a3e3ab1ea5996b4ad5a0e8cd3659a41..4546faaab674409e895b412190cd47068d7f73ab 100644 --- a/fsl/fsleyes/gl/gl14/glvolume_funcs.py +++ b/fsl/fsleyes/gl/gl14/glvolume_funcs.py @@ -1,24 +1,14 @@ #!/usr/bin/env python # -# glvolume_funcs.py - Functions used by the GLVolume class to render 3D -# images in an OpenGL 1.4 compatible manner. +# glvolume_funcs.py - OpenGL 1.4 functions used by the GLVolume class. # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -"""Provides functions which are used by the :class:`.GLVolume` class to render -3D images in an OpenGL 1.4 compatible manner. - -This module depends upon two OpenGL ARB extensions, ARB_vertex_program and -ARB_fragment_program which, being ancient (2002) technology, should be -available on pretty much any graphics card in the wild today. - -See the :mod:`.gl21.glvolume_funcs` module for more details. - -This PDF is quite useful: - - http://www.renderguild.com/gpuguide.pdf - +"""This module provides functions which are used by the :class:`.GLVolume` +class to render :class:`.Image` overlays in an OpenGL 1.4 compatible manner. """ + import logging import numpy as np @@ -34,7 +24,30 @@ import fsl.fsleyes.gl.shaders as shaders log = logging.getLogger(__name__) +def init(self): + """Calls :func:`compleShaders` and :func:`updateShaderState`.""" + + self.vertexProgram = None + self.fragmentProgram = None + + compileShaders( self) + updateShaderState(self) + + +def destroy(self): + """Deletes handles to the vertex/fragment programs.""" + + arbvp.glDeleteProgramsARB(1, gltypes.GLuint(self.vertexProgram)) + arbfp.glDeleteProgramsARB(1, gltypes.GLuint(self.fragmentProgram)) + + def compileShaders(self): + """Compiles the vertex and fragment programs used for rendering + :class:`.GLVolume` instances. This is performed using the :mod:`.shaders` + module. The compiled vertex and shader programs are stored as attributes + on the ``GLVolume`` instance, called ``vertexProgram`` and + ``framgentProgram`` respectively. + """ if self.vertexProgram is not None: arbvp.glDeleteProgramsARB(1, gltypes.GLuint(self.vertexProgram)) @@ -53,25 +66,9 @@ def compileShaders(self): self.vertexProgram = vertexProgram self.fragmentProgram = fragmentProgram - -def init(self): - """Compiles the vertex and fragment programs used for rendering.""" - - self.vertexProgram = None - self.fragmentProgram = None - - compileShaders( self) - updateShaderState(self) - -def destroy(self): - """Deletes handles to the vertex/fragment programs.""" - - arbvp.glDeleteProgramsARB(1, gltypes.GLuint(self.vertexProgram)) - arbfp.glDeleteProgramsARB(1, gltypes.GLuint(self.fragmentProgram)) - - def updateShaderState(self): + """Sets all variables required by the vertex and fragment programs. """ opts = self.displayOpts # enable the vertex and fragment programs @@ -117,8 +114,7 @@ def updateShaderState(self): def preDraw(self): - """Prepares to draw a slice from the given :class:`.GLVolume` instance. - """ + """Prepares to draw a slice from the given :class:`.GLVolume` instance. """ # enable drawing from a vertex array gl.glEnableClientState(gl.GL_VERTEX_ARRAY) @@ -138,7 +134,6 @@ def preDraw(self): def draw(self, zpos, xform=None): """Draws a slice of the image at the given Z location. """ - vertices, voxCoords, texCoords = self.generateVertices(zpos, xform) diff --git a/fsl/fsleyes/gl/gl14/glvolume_sw_frag.prog b/fsl/fsleyes/gl/gl14/glvolume_sw_frag.prog index f7c28841ec5b2f153793d6d775b05c311a3a0d63..9276d56d820ab2891c4da2abb171cc8ac36bec53 100644 --- a/fsl/fsleyes/gl/gl14/glvolume_sw_frag.prog +++ b/fsl/fsleyes/gl/gl14/glvolume_sw_frag.prog @@ -1,20 +1,10 @@ !!ARBfp1.0 # -# Fragment program used for rendering GLVolume instances. -# -# This fragment program does the following: -# -# - Retrieves the display space/voxel coordinates corresponding to the -# fragment -# -# - Uses those voxel coordinates to look up the corresponding voxel -# value in the 3D image texture. -# -# - Uses that voxel value to look up the corresponding colour in the -# 1D colour map texture. -# -# - Sets the fragment colour. -# +# Fragment program used for rendering GLVolume instances. This is a faster +# (and more limited) version of the standard glvolume_frag.prog shader, which +# does not perform bounds checking, and does not allow the clipping range to +# be inverted. + # Required inputs: # # fragment.texcoord[0] - Fragment texture coordinates @@ -30,7 +20,6 @@ # high threshold (y) will not be shown. Assumed to # be normalised to the image texture value range. # -# # Outputs: # # result.color - The fragment colour diff --git a/fsl/fsleyes/gl/gl14/glvolume_sw_vert.prog b/fsl/fsleyes/gl/gl14/glvolume_sw_vert.prog index c5040438129255d30ff815c9d0d98b565eab04c9..5e01a841b0c49cde5ed00bcd8bbfb054eef5863a 100644 --- a/fsl/fsleyes/gl/gl14/glvolume_sw_vert.prog +++ b/fsl/fsleyes/gl/gl14/glvolume_sw_vert.prog @@ -2,14 +2,16 @@ # # Vertex program for rendering GLVolume instances. # +# This version is slightly faster than the standard glvolume_vert.prog shader +# The only difference is that this version only copies texture coordinates +# through to the fragment program, not voxel coordinates, +# # Inputs: -# state.matrix.mvp -# vertex.position -# vertex.texcoord[0] +# vertex.texcoord[0] - Texture coordinates. # # # Outputs: -# result.position +# result.texcoord[0] - Texture coordinates. # # Transform the vertex position into display coordinates diff --git a/fsl/fsleyes/gl/gl14/glvolume_vert.prog b/fsl/fsleyes/gl/gl14/glvolume_vert.prog index 91fa583fc3174431174b13ffd8cd345c3fd924cb..f03921e2a95c29e08270d0721010120d10754c64 100644 --- a/fsl/fsleyes/gl/gl14/glvolume_vert.prog +++ b/fsl/fsleyes/gl/gl14/glvolume_vert.prog @@ -1,18 +1,19 @@ !!ARBvp1.0 # -# Vertex program for rendering GLVolume instances. +# Vertex program for rendering GLVolume instances. +# +# Performs a standard transformation of the vertex coordinates, and +# passes the corresponding voxel and texture coordinates through to the +# fragment program. # # Inputs: -# state.matrix.mvp -# vertex.position -# vertex.texcoord[0] -# program.local[0] - image shape +# vertex.texcoord[0] - Texture coordinates +# program.local[0] - image shape # # # Outputs: -# result.position -# result.texcoord[0] -# result.texcoord[1] +# result.texcoord[0] - Texture coordinates +# result.texcoord[1] - Voxel coordinates # PARAM imageShape = program.local[0]; diff --git a/fsl/fsleyes/gl/gl14/test_in_bounds.prog b/fsl/fsleyes/gl/gl14/test_in_bounds.prog index b334d4af96a2cb03bd89ad3b71062b5e29605014..4e19852bde2d907349e37deb7b56c72f9f61a602 100644 --- a/fsl/fsleyes/gl/gl14/test_in_bounds.prog +++ b/fsl/fsleyes/gl/gl14/test_in_bounds.prog @@ -6,7 +6,7 @@ # voxCoord - 3D voxel coordinates # imageShape - Shape of the image # -# Author: Paul McCarthy<pauldmccarthy@gmail.com> +# Author: Paul McCarthy <pauldmccarthy@gmail.com> # TEMP workspace; diff --git a/fsl/fsleyes/gl/gl21/glvolume_frag.glsl b/fsl/fsleyes/gl/gl21/glvolume_frag.glsl index 98e952ca940d7374fb5cf819e6d656e344d07791..006509b51051ee001a4e2a870901d3dca23c6066 100644 --- a/fsl/fsleyes/gl/gl21/glvolume_frag.glsl +++ b/fsl/fsleyes/gl/gl21/glvolume_frag.glsl @@ -1,5 +1,5 @@ /* - * OpenGL fragment shader used by fsl/fslview/gl/gl21/glvolume_funcs.py. + * OpenGL fragment shader used for rendering GLVolume instances. * * Author: Paul McCarthy <pauldmccarthy@gmail.com> */ @@ -57,9 +57,8 @@ uniform bool invertClip; */ varying vec3 fragVoxCoord; - /* - * Corresponding texture coordinates. + * Corresponding image texture coordinates. */ varying vec3 fragTexCoord; diff --git a/fsl/fsleyes/gl/gl21/glvolume_funcs.py b/fsl/fsleyes/gl/gl21/glvolume_funcs.py index 588140f5ac3c0af3e43c21273f5baaed9e1a7154..cb59ceee93cd122962e38ad65985215329438a54 100644 --- a/fsl/fsleyes/gl/gl21/glvolume_funcs.py +++ b/fsl/fsleyes/gl/gl21/glvolume_funcs.py @@ -1,36 +1,14 @@ #!/usr/bin/env python # -# glvolume_funcs.py - Functions used by the GLVolume class to render 3D images -# in an OpenGL 2.1 compatible manner. +# glvolume_funcs.py - OpenGL 2.1 functions used by the GLVolume class. # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -"""A GLVolume object encapsulates the OpenGL information necessary -to render 2D slices of a 3D image, in an OpenGL 2.1 compatible manner. - -This module is extremely tightly coupled to the fragment shader -program ``glvolume_frag.glsl``. - -This module provides the following functions: - - - :func:`init`: Initialises GL objects and memory buffers. - - - :func:`compileShaders`: Compiles vertex/fragment shaders. - - - :func:`updateShaderState`: Refreshes the parameters used by the shader - programs, controlling clipping and interpolation. - - - :func:`preDraw`: Prepares the GL state for drawing. - - - :func:`draw`: Draws the scene. - - - :func:`drawAll`: Draws multiple scenes. - - - :func:`postDraw`: Resets the GL state after drawing. - - - :func:`destroy`: Deletes the vertex and texture coordinate VBOs. +"""This module provides functions which are used by the :class:`.GLVolume` +class to render :class:`.Image` overlays in an OpenGL 2.1 compatible manner. """ + import logging import ctypes @@ -45,10 +23,31 @@ import fsl.utils.transform as transform log = logging.getLogger(__name__) +def init(self): + """Calls :func:`compleShaders` and :func:`updateShaderState`, + and creates a GL buffer which will be used to store vertex data. + """ + + self.shaders = None + + compileShaders( self) + updateShaderState(self) + + self.vertexAttrBuffer = gl.glGenBuffers(1) + + +def destroy(self): + """Cleans up the vertex buffer handle and shader programs.""" + + gl.glDeleteBuffers(1, gltypes.GLuint(self.vertexAttrBuffer)) + gl.glDeleteProgram(self.shaders) + + def compileShaders(self): """Compiles and links the OpenGL GLSL vertex and fragment shader programs, and attaches a reference to the resulting program, and - all GLSL variables, to the given :class:`.GLVolume` object. + all GLSL variables, as attributes on the given :class:`.GLVolume` + object. """ if self.shaders is not None: @@ -85,25 +84,6 @@ def compileShaders(self): 'invertClip') -def init(self): - """Compiles the vertex and fragment shaders used to render image slices. - """ - - self.shaders = None - - compileShaders( self) - updateShaderState(self) - - self.vertexAttrBuffer = gl.glGenBuffers(1) - - -def destroy(self): - """Cleans up the vertex buffer handle and shader programs.""" - - gl.glDeleteBuffers(1, gltypes.GLuint(self.vertexAttrBuffer)) - gl.glDeleteProgram(self.shaders) - - def updateShaderState(self): """Updates the parameters used by the shader programs, reflecting the current display properties. @@ -218,8 +198,7 @@ def draw(self, zpos, xform=None): def drawAll(self, zposes, xforms): - """Delegates to the default implementation in :meth:`.GLObject.drawAll`. - """ + """Draws all of the specified slices. """ nslices = len(zposes) vertices = np.zeros((nslices * 6, 3), dtype=np.float32) diff --git a/fsl/fsleyes/gl/gl21/glvolume_sw_frag.glsl b/fsl/fsleyes/gl/gl21/glvolume_sw_frag.glsl index 441aa999b3272a22eadd334c1cac812c0836aedc..22c2da2839f19d2107c88a09a55e7b400bf0d2e6 100644 --- a/fsl/fsleyes/gl/gl21/glvolume_sw_frag.glsl +++ b/fsl/fsleyes/gl/gl21/glvolume_sw_frag.glsl @@ -1,14 +1,11 @@ /* - * Fast but limited OpenGL fragment shader used by - * fsl/fslview/gl/gl21/glvolume_funcs.py. + * Fast but limited OpenGL fragment shader used for rendering GLVolume + * instances. * * Author: Paul McCarthy <pauldmccarthy@gmail.com> */ #version 120 -#pragma include spline_interp.glsl -#pragma include test_in_bounds.glsl - /* * image data texture. */ @@ -19,7 +16,6 @@ uniform sampler3D imageTexture; */ uniform sampler1D colourTexture; - /* * Shape of the imageTexture. */ @@ -42,9 +38,8 @@ uniform float clipLow; */ uniform float clipHigh; - /* - * Corresponding texture coordinates. + * Image texture coordinates. */ varying vec3 fragTexCoord; diff --git a/fsl/fsleyes/gl/glmask.py b/fsl/fsleyes/gl/glmask.py index 5dfa7a7b290a4563d6588b7243dc456216f2f4c6..6674298351897a9109dd12a64715b71c747ba4d9 100644 --- a/fsl/fsleyes/gl/glmask.py +++ b/fsl/fsleyes/gl/glmask.py @@ -1,19 +1,11 @@ #!/usr/bin/env python # -# glmask.py - OpenGL rendering of a binary mask image. +# glmask.py - The GLMask class. # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -"""This module defines the :class:`GLMask` class, which provides functionality -for OpenGL rendering of a 3D volume as a binary mask. - -When created, a :class:`GLMask` instance assumes that the provided -:class:`.Image` instance has an ``overlayType`` of ``mask``, and that its -associated :class:`.Display` instance contains a :class:`.MaskOpts` instance, -containing mask-specific display properties. - -The :class:`GLMask` class uses the functionality of the :class:`.GLVolume` -class through inheritance. +"""This module provides the :class:`GLMask` class, which implements +functionality for rendering an :class:`.Image` overlay as a binary mask. """ import logging @@ -29,12 +21,17 @@ log = logging.getLogger(__name__) class GLMask(glvolume.GLVolume): - """The :class:`GLMask` class encapsulates logic to render 2D slices of a + """The ``GLMask`` class encapsulates logic to render 2D slices of a :class:`.Image` instance as a binary mask in OpenGL. - ``GLMask`` is a subclass of the :class:`.GLVolume class. It overrides a - few key methods of ``GLVolume``, but most of the logic is provided by - ``GLVolume``. + When created, a ``GLMask`` instance assumes that the provided + :class:`.Image` instance has a :attr:`.Display.overlayType` of ``mask``, + and that its associated :class:`.Display` instance contains a + :class:`.MaskOpts` instance, containing mask-specific display properties. + + The ``GLMask`` class derives from the :class:`.GLVolume` class. It + overrides a few key methods of ``GLVolume``, but most of the logic is + provided by ``GLVolume``. """ diff --git a/fsl/fsleyes/gl/glvolume.py b/fsl/fsleyes/gl/glvolume.py index f1470633e78e19bc3f2016c846280a6eca09d998..461bc098a4063f2310f678e9ad9e7c9f199b4d49 100644 --- a/fsl/fsleyes/gl/glvolume.py +++ b/fsl/fsleyes/gl/glvolume.py @@ -1,76 +1,109 @@ #!/usr/bin/env python # -# glvolume.py - OpenGL vertex/texture creation for 2D slice rendering of a 3D -# image. +# glvolume.py - The GLVolume class. # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -"""Defines the :class:`GLVolume` class, which creates and encapsulates the -data and logic required to render 2D slice of a 3D image. The -:class:`GLVolume` class provides the interface defined in the -:class:`.GLObject` class. +"""This module provides the :class:`GLVolume` class, which creates and +encapsulates the data and logic required to render 2D slice of an +:class:`.Image` instance. +""" -A :class:`GLVolume` instance may be used to render an :class:`.Image` instance -which has an ``overlayType`` of ``volume``. It is assumed that this ``Image`` -instance is associated with a :class:`.Display` instance which, in turn, -contains a :class:`.VolumeOpts` instance, containing display options specific -to volume rendering. -The :class:`GLVolume` class makes use of the functions defined in the -:mod:`.gl14.glvolume_funcs` or the :mod:`.gl21.glvolume_funcs` modules, which -provide OpenGL version specific details for creation/storage of vertex data, -and for rendering. +import logging -These version dependent modules must provide the following functions: +import OpenGL.GL as gl - - ``init(GLVolume)``: Perform any necessary initialisation. +import fsl.fsleyes.gl as fslgl +import textures +import globject +import resources as glresources - - ``destroy(GLVolume)``: Perform any necessary clean up. - - ``compileShaders(GLVolume)``: (Re-)Compile the shader programs. +log = logging.getLogger(__name__) - - ``updateShaderState(GLVolume)``: Updates the shader program states - when display parameters are changed. - - ``preDraw(GLVolume)``: Initialise the GL state, ready for drawing. +class GLVolume(globject.GLImageObject): + """The ``GLVolume`` class is a :class:`.GLImageObject` which encapsulates + the data and logic required to render 2D slices of an :class:`.Image` + overlay. - - ``draw(GLVolume, zpos, xform=None)``: Draw a slice of the image at the - given Z position. If xform is not None, it must be applied as a - transformation on the vertex coordinates. + + A ``GLVolume`` instance may be used to render an :class:`.Image` instance + which has a :attr:`.Display.overlayType` equal to ``volume``. It is + assumed that this ``Image`` instance is associated with a + :class:`.Display` instance which, in turn, contains a :class:`.VolumeOpts` + instance, containing display options specific to volume rendering. - - ``drawAll(Glvolume, zposes, xforms)`` - Draws slices at each of the - specified ``zposes``, applying the corresponding ``xforms`` to each. - - ``postDraw(GLVolume)``: Clear the GL state after drawing. + **Version dependent modules** -Images are rendered in essentially the same way, regardless of which OpenGL -version-specific module is used. The image data itself is stored on the GPU -as a 3D texture, and the current colour map as a 1D texture. A slice through -the texture is rendered using six vertices, located at the respective corners -of the image bounds. -""" + + The ``GLVolume`` class makes use of the functions defined in the + :mod:`.gl14.glvolume_funcs` or the :mod:`.gl21.glvolume_funcs` modules, + which provide OpenGL version specific details for creation/storage of + vertex data, and for rendering. -import logging -log = logging.getLogger(__name__) -import OpenGL.GL as gl + These version dependent modules must provide the following functions: -import fsl.fsleyes.gl as fslgl -import textures -import globject -import resources as glresources + ===================================== ===================================== + ``init(GLVolume)`` Perform any necessary initialisation. + ``destroy(GLVolume)`` Perform any necessary clean up. -class GLVolume(globject.GLImageObject): - """The :class:`GLVolume` class encapsulates the data and logic required to - render 2D slices of a 3D image. - """ + ``compileShaders(GLVolume)`` (Re-)Compile the shader programs. + + ``updateShaderState(GLVolume)`` Updates the shader program states + when display parameters are changed. + + ``preDraw(GLVolume)`` Initialise the GL state, ready for + drawing. + + ``draw(GLVolume, zpos, xform=None)`` Draw a slice of the image at the + given ``zpos``. If ``xform`` is not + ``None``, it must be applied as a + transformation on the vertex + coordinates. - def __init__(self, image, display): - """Creates a GLVolume object bound to the given image, and associated - image display. + ``drawAll(Glvolume, zposes, xforms)`` Draws slices at each of the + specified ``zposes``, applying the + corresponding ``xforms`` to each. + + ``postDraw(GLVolume)`` Clear the GL state after drawing. + ===================================== ===================================== + - Initialises the OpenGL data required to render the given image. + **Rendering** + + + Images are rendered in essentially the same way, regardless of which + OpenGL version-specific module is used. The image data itself is stored + as an :class:`.ImageTexture`. This ``ImageTexture`` is managed by the + :mod:`.resources` module, so may be shared by many ``GLVolume`` instances. + The current colour map (defined by the :class:`.VolumeOpts.cmap` property) + is stored as a :class:`.ColourMapTexture`. A slice through the texture is + rendered using six vertices, located at the respective corners of the + image bounds. + + + **Attributes** + + + The following attributes are available on a ``GLVolume`` instance: + + ================= ======================================================= + ``imageTexture`` The :class:`.ImageTexture` which stores the image data. + ``colourTexture`` The :class:`.ColourMapTexture` used to store the + colour map. + ``texName`` A name used for the ``imageTexture`` and + ``colourTexture``. + ================= ======================================================= + """ + + + def __init__(self, image, display): + """Create a ``GLVolume`` object. :arg image: An :class:`.Image` object. @@ -87,8 +120,8 @@ class GLVolume(globject.GLImageObject): # Create an image texture and a colour map texture self.texName = '{}_{}'.format(type(self).__name__, id(self.image)) - self.imageTexture = None self.colourTexture = textures.ColourMapTexture(self.texName) + self.imageTexture = None self.refreshImageTexture() self.refreshColourTexture() @@ -97,9 +130,10 @@ class GLVolume(globject.GLImageObject): def destroy(self): - """This should be called when this :class:`GLVolume` object is no + """This must be called when this :class:`GLVolume` object is no longer needed. It performs any needed clean up of OpenGL data (e.g. - deleting texture handles). + deleting texture handles), calls :meth:`removeDisplayListeners`, + and calls :meth:`.GLImageObject.destroy`. """ glresources.delete(self.imageTexture.getTextureName()) @@ -115,66 +149,8 @@ class GLVolume(globject.GLImageObject): globject.GLImageObject.destroy(self) - def testUnsynced(self): - """Used by the :meth:`refreshImageTexture` method. - - Returns ``True`` if certain critical :class:`VolumeOpts` properties - have been unsynced from the parent instance, meaning that this - :class:`GLVolume` instance needs to create its own image texture; - returns ``False`` otherwise. - """ - return (self.displayOpts.getParent() is None or - not self.displayOpts.isSyncedToParent('volume') or - not self.displayOpts.isSyncedToParent('resolution') or - not self.displayOpts.isSyncedToParent('interpolation')) - - - def refreshImageTexture(self): - - opts = self.displayOpts - texName = self.texName - unsynced = self.testUnsynced() - - if unsynced: - texName = '{}_unsync_{}'.format(texName, id(opts)) - - if self.imageTexture is not None: - glresources.delete(self.imageTexture.getTextureName()) - - if opts.interpolation == 'none': interp = gl.GL_NEAREST - else: interp = gl.GL_LINEAR - - # The image texture may be used elsewhere, - # so we'll use the resource management - # module rather than creating one directly - self.imageTexture = glresources.get( - texName, - textures.ImageTexture, - texName, - self.image, - interp=interp) - - - def refreshColourTexture(self): - """Configures the colour texture used to colour image voxels.""" - - display = self.display - opts = self.displayOpts - - alpha = display.alpha / 100.0 - cmap = opts.cmap - invert = opts.invert - dmin = opts.displayRange[0] - dmax = opts.displayRange[1] - - self.colourTexture.set(cmap=cmap, - invert=invert, - alpha=alpha, - displayRange=(dmin, dmax)) - - def addDisplayListeners(self): - """Called by :meth:`init`. + """Called by :meth:`__init__`. Adds a bunch of listeners to the :class:`.Display` object, and the associated :class:`.VolumeOpts` instance, which define how the image @@ -267,9 +243,71 @@ class GLVolume(globject.GLImageObject): opts.removeSyncChangeListener('interpolation', lName) + def testUnsynced(self): + """Used by the :meth:`refreshImageTexture` method. + + Returns ``True`` if certain critical :class:`VolumeOpts` properties + have been unsynced from the parent instance, meaning that this + ``GLVolume`` instance needs to create its own image texture; + returns ``False`` otherwise. + """ + return (self.displayOpts.getParent() is None or + not self.displayOpts.isSyncedToParent('volume') or + not self.displayOpts.isSyncedToParent('resolution') or + not self.displayOpts.isSyncedToParent('interpolation')) + + + def refreshImageTexture(self): + """Refreshes the :class:`.ImageTexture` used to store the + :class:`.Image` data. This is performed through the :mod:`.resources` + module, so the image texture can be shared between multiple + ``GLVolume`` instances. + """ + + opts = self.displayOpts + texName = self.texName + unsynced = self.testUnsynced() + + if unsynced: + texName = '{}_unsync_{}'.format(texName, id(opts)) + + if self.imageTexture is not None: + glresources.delete(self.imageTexture.getTextureName()) + + if opts.interpolation == 'none': interp = gl.GL_NEAREST + else: interp = gl.GL_LINEAR + + self.imageTexture = glresources.get( + texName, + textures.ImageTexture, + texName, + self.image, + interp=interp) + + + def refreshColourTexture(self): + """Refreshes the :class:`.ColourMapTexture` used to colour image + voxels. + """ + + display = self.display + opts = self.displayOpts + alpha = display.alpha / 100.0 + cmap = opts.cmap + invert = opts.invert + dmin = opts.displayRange[0] + dmax = opts.displayRange[1] + + self.colourTexture.set(cmap=cmap, + invert=invert, + alpha=alpha, + displayRange=(dmin, dmax)) + + def preDraw(self): - """Sets up the GL state to draw a slice from this :class:`GLVolume` - instance. + """Binds the :class:`.ImageTexture` to ``GL_TEXTURE0`` and the + :class:`.ColourMapTexture` to ``GL_TEXTURE1, and calls the + version-dependent ``preDraw`` function. """ # Set up the image and colour textures @@ -280,30 +318,32 @@ class GLVolume(globject.GLImageObject): def draw(self, zpos, xform=None): - """Draws a 2D slice of the image at the given real world Z location. - This is performed via a call to the OpenGL version-dependent `draw` - function, contained in one of the :mod:`.gl14` or :mod:`.gl21` - packages. + """Draws a 2D slice of the image at the given Z location in the + display coordinate system. + + . This is performed via a call to the OpenGL version-dependent ``draw`` + function, contained in one of the :mod:`.gl14.glvolume_funcs` or + :mod:`.gl21.glvolume_funcs` modules. - If `xform` is not None, it is applied as an affine transformation to - the vertex coordinates of the rendered image data. + If ``xform`` is not ``None``, it is applied as an affine + transformation to the vertex coordinates of the rendered image data. - Note: Calls to this method must be preceded by a call to - :meth:`preDraw`, and followed by a call to :meth:`postDraw`. + .. note:: Calls to this method must be preceded by a call to + :meth:`preDraw`, and followed by a call to :meth:`postDraw`. """ fslgl.glvolume_funcs.draw(self, zpos, xform) def drawAll(self, zposes, xforms): - """Calls the module-specific ``drawAll`` function. """ + """Calls the version dependent ``drawAll`` function. """ fslgl.glvolume_funcs.drawAll(self, zposes, xforms) def postDraw(self): - """Clears the GL state after drawing from this :class:`GLVolume` - instance. + """Unbinds the ``ImageTexture`` and ``ColourMapTexture``, and calls the + version-dependent ``postDraw`` function. """ self.imageTexture .unbindTexture() diff --git a/fsl/fsleyes/gl/shaders.py b/fsl/fsleyes/gl/shaders.py index 256f795b84da97e175d940f01328d57875895f71..9bf0c72356496c5323bb3c383e6a7d93d10b63fc 100644 --- a/fsl/fsleyes/gl/shaders.py +++ b/fsl/fsleyes/gl/shaders.py @@ -50,6 +50,15 @@ the OpenGL version specific packages, i.e. :mod:`.gl14` When a shader file is loaded, a simple preprocessor is applied to the source - any lines of the form '#pragma include filename', will be replaced with the contents of the specified file. + + +**Resources** + + + - http://oss.sgi.com/projects/ogl-sample/registry/ARB/vertex_program.txt + - http://oss.sgi.com/projects/ogl-sample/registry/ARB/fragment_program.txt + - http://www.renderguild.com/gpuguide.pdf + - https://www.opengl.org/registry/doc/GLSLangSpec.Full.1.20.8.pdf """