Skip to content
Snippets Groups Projects
Commit cdbc68e2 authored by Paul McCarthy's avatar Paul McCarthy
Browse files

Negative colour map implemented in GL21. Wasn't as hard as I had expected.

parent 4a835d45
No related branches found
No related tags found
No related merge requests found
...@@ -18,6 +18,16 @@ uniform sampler3D imageTexture; ...@@ -18,6 +18,16 @@ uniform sampler3D imageTexture;
*/ */
uniform sampler1D colourTexture; uniform sampler1D colourTexture;
/*
* Texture containing the negative colour map.
*/
uniform sampler1D negColourTexture;
/*
* Flag which determines whether to
* use the negative colour map.
*/
uniform bool enableNegCmap;
/* /*
* Shape of the imageTexture. * Shape of the imageTexture.
...@@ -37,15 +47,25 @@ uniform bool useSpline; ...@@ -37,15 +47,25 @@ uniform bool useSpline;
uniform mat4 voxValXform; uniform mat4 voxValXform;
/* /*
* Clip voxels below this value. * Clip voxels below this value. This must be specified
* in the image texture data range.
*/ */
uniform float clipLow; uniform float clipLow;
/* /*
* Clip voxels above this value. * Clip voxels above this value. This must be specified
* in the image texture data range.
*/ */
uniform float clipHigh; uniform float clipHigh;
/*
* Value in the image texture data range which corresponds
* to zero - this is used to determine whether to use the
* regular, or the negative colour texture (if enableNegCmap
* is true).
*/
uniform float texZero;
/* /*
* Invert clipping behaviour - clip voxels * Invert clipping behaviour - clip voxels
* that are inside the clipLow/High bounds. * that are inside the clipLow/High bounds.
...@@ -65,7 +85,10 @@ varying vec3 fragTexCoord; ...@@ -65,7 +85,10 @@ varying vec3 fragTexCoord;
void main(void) { void main(void) {
vec3 voxCoord = fragVoxCoord; float voxValue;
vec4 normVoxValue;
bool useNegCmap = false;
vec3 voxCoord = fragVoxCoord;
/* /*
* Skip voxels that are out of the image bounds * Skip voxels that are out of the image bounds
...@@ -79,7 +102,6 @@ void main(void) { ...@@ -79,7 +102,6 @@ void main(void) {
/* /*
* Look up the voxel value * Look up the voxel value
*/ */
float voxValue;
if (useSpline) voxValue = spline_interp(imageTexture, if (useSpline) voxValue = spline_interp(imageTexture,
fragTexCoord, fragTexCoord,
imageShape, imageShape,
...@@ -87,20 +109,35 @@ void main(void) { ...@@ -87,20 +109,35 @@ void main(void) {
else voxValue = texture3D( imageTexture, else voxValue = texture3D( imageTexture,
fragTexCoord).r; fragTexCoord).r;
/*
* If we are using a negative colour map,
* and the voxel value is below the negative
* threshold (texZero) invert the voxel
* value, and set a flag telling the code
* below to use the neagtive colour map.
*/
if (enableNegCmap && voxValue <= texZero) {
useNegCmap = true;
voxValue = texZero + (texZero - voxValue);
}
/* /*
* Clip out of range voxel values * Clip out of range voxel values
*/ */
if ((!invertClip && (voxValue < clipLow || voxValue > clipHigh)) || if ((!invertClip && (voxValue < clipLow || voxValue > clipHigh)) ||
(invertClip && (voxValue >= clipLow && voxValue <= clipHigh))) { ( invertClip && (voxValue >= clipLow && voxValue <= clipHigh))) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return; gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
} }
/* /*
* Transform the voxel value to a colour map texture * Transform the voxel value to a colour map texture
* coordinate, and look up the colour for the voxel value * coordinate, and look up the colour for the voxel value
*/ */
vec4 normVoxValue = voxValXform * vec4(voxValue, 0, 0, 1); normVoxValue = voxValXform * vec4(voxValue, 0, 0, 1);
gl_FragColor = texture1D(colourTexture, normVoxValue.x);
if (useNegCmap) gl_FragColor = texture1D(negColourTexture, normVoxValue.x);
else gl_FragColor = texture1D(colourTexture, normVoxValue.x);
} }
...@@ -58,28 +58,34 @@ def compileShaders(self): ...@@ -58,28 +58,34 @@ def compileShaders(self):
self.shaders = shaders.compileShaders(vertShaderSrc, fragShaderSrc) self.shaders = shaders.compileShaders(vertShaderSrc, fragShaderSrc)
# indices of all vertex/fragment shader parameters # indices of all vertex/fragment shader parameters
self.vertexPos = gl.glGetAttribLocation( self.shaders, self.vertexPos = gl.glGetAttribLocation( self.shaders,
'vertex') 'vertex')
self.voxCoordPos = gl.glGetAttribLocation( self.shaders, self.voxCoordPos = gl.glGetAttribLocation( self.shaders,
'voxCoord') 'voxCoord')
self.texCoordPos = gl.glGetAttribLocation( self.shaders, self.texCoordPos = gl.glGetAttribLocation( self.shaders,
'texCoord') 'texCoord')
self.imageTexturePos = gl.glGetUniformLocation(self.shaders, self.imageTexturePos = gl.glGetUniformLocation(self.shaders,
'imageTexture') 'imageTexture')
self.colourTexturePos = gl.glGetUniformLocation(self.shaders, self.colourTexturePos = gl.glGetUniformLocation(self.shaders,
'colourTexture') 'colourTexture')
self.imageShapePos = gl.glGetUniformLocation(self.shaders, self.negColourTexturePos = gl.glGetUniformLocation(self.shaders,
'imageShape') 'negColourTexture')
self.useSplinePos = gl.glGetUniformLocation(self.shaders, self.enableNegCmapPos = gl.glGetUniformLocation(self.shaders,
'useSpline') 'enableNegCmap')
self.voxValXformPos = gl.glGetUniformLocation(self.shaders, self.texZeroPos = gl.glGetUniformLocation(self.shaders,
'voxValXform') 'texZero')
self.clipLowPos = gl.glGetUniformLocation(self.shaders, self.imageShapePos = gl.glGetUniformLocation(self.shaders,
'clipLow') 'imageShape')
self.clipHighPos = gl.glGetUniformLocation(self.shaders, self.useSplinePos = gl.glGetUniformLocation(self.shaders,
'clipHigh') 'useSpline')
self.invertClipPos = gl.glGetUniformLocation(self.shaders, self.voxValXformPos = gl.glGetUniformLocation(self.shaders,
'invertClip') 'voxValXform')
self.clipLowPos = gl.glGetUniformLocation(self.shaders,
'clipLow')
self.clipHighPos = gl.glGetUniformLocation(self.shaders,
'clipHigh')
self.invertClipPos = gl.glGetUniformLocation(self.shaders,
'invertClip')
def updateShaderState(self): def updateShaderState(self):
...@@ -105,10 +111,13 @@ def updateShaderState(self): ...@@ -105,10 +111,13 @@ def updateShaderState(self):
xform = self.imageTexture.invVoxValXform xform = self.imageTexture.invVoxValXform
clipLow = opts.clippingRange[0] * xform[0, 0] + xform[3, 0] clipLow = opts.clippingRange[0] * xform[0, 0] + xform[3, 0]
clipHigh = opts.clippingRange[1] * xform[0, 0] + xform[3, 0] clipHigh = opts.clippingRange[1] * xform[0, 0] + xform[3, 0]
texZero = 0.0 * xform[0, 0] + xform[3, 0]
gl.glUniform1f(self.clipLowPos, clipLow) gl.glUniform1f(self.clipLowPos, clipLow)
gl.glUniform1f(self.clipHighPos, clipHigh) gl.glUniform1f(self.clipHighPos, clipHigh)
gl.glUniform1f(self.invertClipPos, opts.invertClipping) gl.glUniform1f(self.texZeroPos, texZero)
gl.glUniform1f(self.invertClipPos, opts.invertClipping)
gl.glUniform1f(self.enableNegCmapPos, opts.enableNegativeCmap)
# Bind transformation matrix to transform # Bind transformation matrix to transform
# from image texture values to voxel values, # from image texture values to voxel values,
...@@ -121,8 +130,9 @@ def updateShaderState(self): ...@@ -121,8 +130,9 @@ def updateShaderState(self):
gl.glUniformMatrix4fv(self.voxValXformPos, 1, False, vvx) gl.glUniformMatrix4fv(self.voxValXformPos, 1, False, vvx)
# Set up the colour and image textures # Set up the colour and image textures
gl.glUniform1i(self.imageTexturePos, 0) gl.glUniform1i(self.imageTexturePos, 0)
gl.glUniform1i(self.colourTexturePos, 1) gl.glUniform1i(self.colourTexturePos, 1)
gl.glUniform1i(self.negColourTexturePos, 2)
gl.glUseProgram(0) gl.glUseProgram(0)
......
...@@ -81,10 +81,28 @@ class GLVolume(globject.GLImageObject): ...@@ -81,10 +81,28 @@ class GLVolume(globject.GLImageObject):
OpenGL version-specific module is used. The image data itself is stored OpenGL version-specific module is used. The image data itself is stored
as an :class:`.ImageTexture`. This ``ImageTexture`` is managed by the as an :class:`.ImageTexture`. This ``ImageTexture`` is managed by the
:mod:`.resources` module, so may be shared by many ``GLVolume`` instances. :mod:`.resources` module, so may be shared by many ``GLVolume`` instances.
The current colour map (defined by the :class:`.VolumeOpts.cmap` property) The current colour maps (defined by the :attr:`.VolumeOpts.cmap` and
is stored as a :class:`.ColourMapTexture`. A slice through the texture is :attr:`.VolumeOpts.negativeCmap` properties) are stored as
:class:`.ColourMapTexture` instances. A slice through the texture is
rendered using six vertices, located at the respective corners of the rendered using six vertices, located at the respective corners of the
image bounds. image bounds.
**Textures**
The ``GLVolume`` class uses three textures:
- An :class:`.ImageTexture`, a 3D texture which contains image data.
This is bound to texture unit 0.
- A :class:`.ColourMapTexture`, a 1D texture which contains the
colour map defined by the :attr:`.VolumeOpts.cmap` property.
This is bound to texture unit 1.
- A :class:`.ColourMapTexture`, a 1D texture which contains the
colour map defined by the :attr:`.VolumeOpts.negativeCmap` property.
This is bound to texture unit 2.
**Attributes** **Attributes**
...@@ -92,13 +110,17 @@ class GLVolume(globject.GLImageObject): ...@@ -92,13 +110,17 @@ class GLVolume(globject.GLImageObject):
The following attributes are available on a ``GLVolume`` instance: The following attributes are available on a ``GLVolume`` instance:
================= ======================================================= ==================== =================================================
``imageTexture`` The :class:`.ImageTexture` which stores the image data. ``imageTexture`` The :class:`.ImageTexture` which stores the image
``colourTexture`` The :class:`.ColourMapTexture` used to store the data.
colour map. ``colourTexture`` The :class:`.ColourMapTexture` used to store the
``texName`` A name used for the ``imageTexture`` and colour map.
``colourTexture``. ``negColourTexture`` The :class:`.ColourMapTexture` used to store the
================= ======================================================= negative colour map.
``texName`` A name used for the ``imageTexture``,
``colourTexture``, and ``negColourTexture`. The
name for the latter is suffixed with ``'_neg'``.
==================== =================================================
""" """
...@@ -120,11 +142,13 @@ class GLVolume(globject.GLImageObject): ...@@ -120,11 +142,13 @@ class GLVolume(globject.GLImageObject):
# Create an image texture and a colour map texture # Create an image texture and a colour map texture
self.texName = '{}_{}'.format(type(self).__name__, id(self.image)) self.texName = '{}_{}'.format(type(self).__name__, id(self.image))
self.colourTexture = textures.ColourMapTexture(self.texName) self.imageTexture = None
self.imageTexture = None self.colourTexture = textures.ColourMapTexture(self.texName)
self.negColourTexture = textures.ColourMapTexture(
'{}_neg'.format(self.texName))
self.refreshImageTexture() self.refreshImageTexture()
self.refreshColourTexture() self.refreshColourTextures()
fslgl.glvolume_funcs.init(self) fslgl.glvolume_funcs.init(self)
...@@ -138,14 +162,16 @@ class GLVolume(globject.GLImageObject): ...@@ -138,14 +162,16 @@ class GLVolume(globject.GLImageObject):
glresources.delete(self.imageTexture.getTextureName()) glresources.delete(self.imageTexture.getTextureName())
self.colourTexture.destroy() self.colourTexture .destroy()
self.negColourTexture.destroy()
self.imageTexture = None self.imageTexture = None
self.colourTexture = None self.colourTexture = None
self.negColourTexture = None
self.removeDisplayListeners() self.removeDisplayListeners()
fslgl.glvolume_funcs.destroy(self)
fslgl.glvolume_funcs .destroy(self)
globject.GLImageObject.destroy(self) globject.GLImageObject.destroy(self)
...@@ -168,7 +194,7 @@ class GLVolume(globject.GLImageObject): ...@@ -168,7 +194,7 @@ class GLVolume(globject.GLImageObject):
self.onUpdate() self.onUpdate()
def colourUpdate(*a): def colourUpdate(*a):
self.refreshColourTexture() self.refreshColourTextures()
fslgl.glvolume_funcs.updateShaderState(self) fslgl.glvolume_funcs.updateShaderState(self)
self.onUpdate() self.onUpdate()
...@@ -176,11 +202,6 @@ class GLVolume(globject.GLImageObject): ...@@ -176,11 +202,6 @@ class GLVolume(globject.GLImageObject):
fslgl.glvolume_funcs.updateShaderState(self) fslgl.glvolume_funcs.updateShaderState(self)
self.onUpdate() self.onUpdate()
def shaderCompile(*a):
fslgl.glvolume_funcs.compileShaders( self)
fslgl.glvolume_funcs.updateShaderState(self)
self.onUpdate()
def imageRefresh(*a): def imageRefresh(*a):
self.refreshImageTexture() self.refreshImageTexture()
fslgl.glvolume_funcs.updateShaderState(self) fslgl.glvolume_funcs.updateShaderState(self)
...@@ -205,6 +226,9 @@ class GLVolume(globject.GLImageObject): ...@@ -205,6 +226,9 @@ class GLVolume(globject.GLImageObject):
opts .addListener('clippingRange', lName, shaderUpdate, weak=False) opts .addListener('clippingRange', lName, shaderUpdate, weak=False)
opts .addListener('invertClipping', lName, shaderUpdate, weak=False) opts .addListener('invertClipping', lName, shaderUpdate, weak=False)
opts .addListener('cmap', lName, colourUpdate, weak=False) opts .addListener('cmap', lName, colourUpdate, weak=False)
opts .addListener('negativeCmap', lName, colourUpdate, weak=False)
opts .addListener('enableNegativeCmap',
lName, colourUpdate, weak=False)
opts .addListener('invert', lName, colourUpdate, weak=False) opts .addListener('invert', lName, colourUpdate, weak=False)
opts .addListener('volume', lName, imageUpdate, weak=False) opts .addListener('volume', lName, imageUpdate, weak=False)
opts .addListener('resolution', lName, imageUpdate, weak=False) opts .addListener('resolution', lName, imageUpdate, weak=False)
...@@ -239,16 +263,19 @@ class GLVolume(globject.GLImageObject): ...@@ -239,16 +263,19 @@ class GLVolume(globject.GLImageObject):
lName = self.name lName = self.name
display.removeListener( 'alpha', lName) display.removeListener( 'alpha', lName)
opts .removeListener( 'displayRange', lName) opts .removeListener( 'displayRange', lName)
opts .removeListener( 'clippingRange', lName) opts .removeListener( 'clippingRange', lName)
opts .removeListener( 'invertClipping', lName) opts .removeListener( 'invertClipping', lName)
opts .removeListener( 'cmap', lName) opts .removeListener( 'cmap', lName)
opts .removeListener( 'invert', lName) opts .removeListener( 'negativeCmap', lName)
opts .removeListener( 'volume', lName) opts .removeListener( 'enableNegativeCmap', lName)
opts .removeListener( 'resolution', lName) opts .removeListener( 'cmap', lName)
opts .removeListener( 'interpolation', lName) opts .removeListener( 'invert', lName)
opts .removeListener( 'transform', lName) opts .removeListener( 'volume', lName)
opts .removeListener( 'resolution', lName)
opts .removeListener( 'interpolation', lName)
opts .removeListener( 'transform', lName)
if self.__syncListenersRegistered: if self.__syncListenersRegistered:
opts.removeSyncChangeListener('volume', lName) opts.removeSyncChangeListener('volume', lName)
...@@ -298,15 +325,16 @@ class GLVolume(globject.GLImageObject): ...@@ -298,15 +325,16 @@ class GLVolume(globject.GLImageObject):
interp=interp) interp=interp)
def refreshColourTexture(self): def refreshColourTextures(self):
"""Refreshes the :class:`.ColourMapTexture` used to colour image """Refreshes the :class:`.ColourMapTexture` instances used to colour
voxels. image voxels.
""" """
display = self.display display = self.display
opts = self.displayOpts opts = self.displayOpts
alpha = display.alpha / 100.0 alpha = display.alpha / 100.0
cmap = opts.cmap cmap = opts.cmap
negCmap = opts.negativeCmap
invert = opts.invert invert = opts.invert
dmin = opts.displayRange[0] dmin = opts.displayRange[0]
dmax = opts.displayRange[1] dmax = opts.displayRange[1]
...@@ -316,6 +344,11 @@ class GLVolume(globject.GLImageObject): ...@@ -316,6 +344,11 @@ class GLVolume(globject.GLImageObject):
alpha=alpha, alpha=alpha,
displayRange=(dmin, dmax)) displayRange=(dmin, dmax))
self.negColourTexture.set(cmap=negCmap,
invert=invert,
alpha=alpha,
displayRange=(dmin, dmax))
def preDraw(self): def preDraw(self):
"""Binds the :class:`.ImageTexture` to ``GL_TEXTURE0`` and the """Binds the :class:`.ImageTexture` to ``GL_TEXTURE0`` and the
...@@ -324,8 +357,9 @@ class GLVolume(globject.GLImageObject): ...@@ -324,8 +357,9 @@ class GLVolume(globject.GLImageObject):
""" """
# Set up the image and colour textures # Set up the image and colour textures
self.imageTexture .bindTexture(gl.GL_TEXTURE0) self.imageTexture .bindTexture(gl.GL_TEXTURE0)
self.colourTexture.bindTexture(gl.GL_TEXTURE1) self.colourTexture .bindTexture(gl.GL_TEXTURE1)
self.negColourTexture.bindTexture(gl.GL_TEXTURE2)
fslgl.glvolume_funcs.preDraw(self) fslgl.glvolume_funcs.preDraw(self)
...@@ -359,7 +393,8 @@ class GLVolume(globject.GLImageObject): ...@@ -359,7 +393,8 @@ class GLVolume(globject.GLImageObject):
version-dependent ``postDraw`` function. version-dependent ``postDraw`` function.
""" """
self.imageTexture .unbindTexture() self.imageTexture .unbindTexture()
self.colourTexture.unbindTexture() self.colourTexture .unbindTexture()
self.negColourTexture.unbindTexture()
fslgl.glvolume_funcs.postDraw(self) fslgl.glvolume_funcs.postDraw(self)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment