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;
*/
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.
......@@ -37,15 +47,25 @@ uniform bool useSpline;
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;
/*
* Clip voxels above this value.
* Clip voxels above this value. This must be specified
* in the image texture data range.
*/
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
* that are inside the clipLow/High bounds.
......@@ -65,7 +85,10 @@ varying vec3 fragTexCoord;
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
......@@ -79,7 +102,6 @@ void main(void) {
/*
* Look up the voxel value
*/
float voxValue;
if (useSpline) voxValue = spline_interp(imageTexture,
fragTexCoord,
imageShape,
......@@ -87,20 +109,35 @@ void main(void) {
else voxValue = texture3D( imageTexture,
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
*/
if ((!invertClip && (voxValue < clipLow || voxValue > clipHigh)) ||
(invertClip && (voxValue >= clipLow && voxValue <= clipHigh))) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
( invertClip && (voxValue >= clipLow && voxValue <= clipHigh))) {
gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
return;
}
/*
* Transform the voxel value to a colour map texture
* coordinate, and look up the colour for the voxel value
*/
vec4 normVoxValue = voxValXform * vec4(voxValue, 0, 0, 1);
gl_FragColor = texture1D(colourTexture, normVoxValue.x);
*/
normVoxValue = voxValXform * vec4(voxValue, 0, 0, 1);
if (useNegCmap) gl_FragColor = texture1D(negColourTexture, normVoxValue.x);
else gl_FragColor = texture1D(colourTexture, normVoxValue.x);
}
......@@ -58,28 +58,34 @@ def compileShaders(self):
self.shaders = shaders.compileShaders(vertShaderSrc, fragShaderSrc)
# indices of all vertex/fragment shader parameters
self.vertexPos = gl.glGetAttribLocation( self.shaders,
'vertex')
self.voxCoordPos = gl.glGetAttribLocation( self.shaders,
'voxCoord')
self.texCoordPos = gl.glGetAttribLocation( self.shaders,
'texCoord')
self.imageTexturePos = gl.glGetUniformLocation(self.shaders,
'imageTexture')
self.colourTexturePos = gl.glGetUniformLocation(self.shaders,
'colourTexture')
self.imageShapePos = gl.glGetUniformLocation(self.shaders,
'imageShape')
self.useSplinePos = gl.glGetUniformLocation(self.shaders,
'useSpline')
self.voxValXformPos = gl.glGetUniformLocation(self.shaders,
'voxValXform')
self.clipLowPos = gl.glGetUniformLocation(self.shaders,
'clipLow')
self.clipHighPos = gl.glGetUniformLocation(self.shaders,
'clipHigh')
self.invertClipPos = gl.glGetUniformLocation(self.shaders,
'invertClip')
self.vertexPos = gl.glGetAttribLocation( self.shaders,
'vertex')
self.voxCoordPos = gl.glGetAttribLocation( self.shaders,
'voxCoord')
self.texCoordPos = gl.glGetAttribLocation( self.shaders,
'texCoord')
self.imageTexturePos = gl.glGetUniformLocation(self.shaders,
'imageTexture')
self.colourTexturePos = gl.glGetUniformLocation(self.shaders,
'colourTexture')
self.negColourTexturePos = gl.glGetUniformLocation(self.shaders,
'negColourTexture')
self.enableNegCmapPos = gl.glGetUniformLocation(self.shaders,
'enableNegCmap')
self.texZeroPos = gl.glGetUniformLocation(self.shaders,
'texZero')
self.imageShapePos = gl.glGetUniformLocation(self.shaders,
'imageShape')
self.useSplinePos = gl.glGetUniformLocation(self.shaders,
'useSpline')
self.voxValXformPos = gl.glGetUniformLocation(self.shaders,
'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):
......@@ -105,10 +111,13 @@ def updateShaderState(self):
xform = self.imageTexture.invVoxValXform
clipLow = opts.clippingRange[0] * 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.clipHighPos, clipHigh)
gl.glUniform1f(self.invertClipPos, opts.invertClipping)
gl.glUniform1f(self.clipLowPos, clipLow)
gl.glUniform1f(self.clipHighPos, clipHigh)
gl.glUniform1f(self.texZeroPos, texZero)
gl.glUniform1f(self.invertClipPos, opts.invertClipping)
gl.glUniform1f(self.enableNegCmapPos, opts.enableNegativeCmap)
# Bind transformation matrix to transform
# from image texture values to voxel values,
......@@ -121,8 +130,9 @@ def updateShaderState(self):
gl.glUniformMatrix4fv(self.voxValXformPos, 1, False, vvx)
# Set up the colour and image textures
gl.glUniform1i(self.imageTexturePos, 0)
gl.glUniform1i(self.colourTexturePos, 1)
gl.glUniform1i(self.imageTexturePos, 0)
gl.glUniform1i(self.colourTexturePos, 1)
gl.glUniform1i(self.negColourTexturePos, 2)
gl.glUseProgram(0)
......
......@@ -81,10 +81,28 @@ class GLVolume(globject.GLImageObject):
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
The current colour maps (defined by the :attr:`.VolumeOpts.cmap` and
: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
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**
......@@ -92,13 +110,17 @@ class GLVolume(globject.GLImageObject):
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``.
================= =======================================================
==================== =================================================
``imageTexture`` The :class:`.ImageTexture` which stores the image
data.
``colourTexture`` The :class:`.ColourMapTexture` used to store the
colour map.
``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):
# Create an image texture and a colour map texture
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.refreshColourTexture()
self.refreshColourTextures()
fslgl.glvolume_funcs.init(self)
......@@ -138,14 +162,16 @@ class GLVolume(globject.GLImageObject):
glresources.delete(self.imageTexture.getTextureName())
self.colourTexture.destroy()
self.colourTexture .destroy()
self.negColourTexture.destroy()
self.imageTexture = None
self.colourTexture = None
self.imageTexture = None
self.colourTexture = None
self.negColourTexture = None
self.removeDisplayListeners()
fslgl.glvolume_funcs.destroy(self)
fslgl.glvolume_funcs .destroy(self)
globject.GLImageObject.destroy(self)
......@@ -168,7 +194,7 @@ class GLVolume(globject.GLImageObject):
self.onUpdate()
def colourUpdate(*a):
self.refreshColourTexture()
self.refreshColourTextures()
fslgl.glvolume_funcs.updateShaderState(self)
self.onUpdate()
......@@ -176,11 +202,6 @@ class GLVolume(globject.GLImageObject):
fslgl.glvolume_funcs.updateShaderState(self)
self.onUpdate()
def shaderCompile(*a):
fslgl.glvolume_funcs.compileShaders( self)
fslgl.glvolume_funcs.updateShaderState(self)
self.onUpdate()
def imageRefresh(*a):
self.refreshImageTexture()
fslgl.glvolume_funcs.updateShaderState(self)
......@@ -205,6 +226,9 @@ class GLVolume(globject.GLImageObject):
opts .addListener('clippingRange', lName, shaderUpdate, weak=False)
opts .addListener('invertClipping', lName, shaderUpdate, 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('volume', lName, imageUpdate, weak=False)
opts .addListener('resolution', lName, imageUpdate, weak=False)
......@@ -239,16 +263,19 @@ class GLVolume(globject.GLImageObject):
lName = self.name
display.removeListener( 'alpha', lName)
opts .removeListener( 'displayRange', lName)
opts .removeListener( 'clippingRange', lName)
opts .removeListener( 'invertClipping', lName)
opts .removeListener( 'cmap', lName)
opts .removeListener( 'invert', lName)
opts .removeListener( 'volume', lName)
opts .removeListener( 'resolution', lName)
opts .removeListener( 'interpolation', lName)
opts .removeListener( 'transform', lName)
display.removeListener( 'alpha', lName)
opts .removeListener( 'displayRange', lName)
opts .removeListener( 'clippingRange', lName)
opts .removeListener( 'invertClipping', lName)
opts .removeListener( 'cmap', lName)
opts .removeListener( 'negativeCmap', lName)
opts .removeListener( 'enableNegativeCmap', lName)
opts .removeListener( 'cmap', lName)
opts .removeListener( 'invert', lName)
opts .removeListener( 'volume', lName)
opts .removeListener( 'resolution', lName)
opts .removeListener( 'interpolation', lName)
opts .removeListener( 'transform', lName)
if self.__syncListenersRegistered:
opts.removeSyncChangeListener('volume', lName)
......@@ -298,15 +325,16 @@ class GLVolume(globject.GLImageObject):
interp=interp)
def refreshColourTexture(self):
"""Refreshes the :class:`.ColourMapTexture` used to colour image
voxels.
def refreshColourTextures(self):
"""Refreshes the :class:`.ColourMapTexture` instances used to colour
image voxels.
"""
display = self.display
opts = self.displayOpts
alpha = display.alpha / 100.0
cmap = opts.cmap
negCmap = opts.negativeCmap
invert = opts.invert
dmin = opts.displayRange[0]
dmax = opts.displayRange[1]
......@@ -316,6 +344,11 @@ class GLVolume(globject.GLImageObject):
alpha=alpha,
displayRange=(dmin, dmax))
self.negColourTexture.set(cmap=negCmap,
invert=invert,
alpha=alpha,
displayRange=(dmin, dmax))
def preDraw(self):
"""Binds the :class:`.ImageTexture` to ``GL_TEXTURE0`` and the
......@@ -324,8 +357,9 @@ class GLVolume(globject.GLImageObject):
"""
# Set up the image and colour textures
self.imageTexture .bindTexture(gl.GL_TEXTURE0)
self.colourTexture.bindTexture(gl.GL_TEXTURE1)
self.imageTexture .bindTexture(gl.GL_TEXTURE0)
self.colourTexture .bindTexture(gl.GL_TEXTURE1)
self.negColourTexture.bindTexture(gl.GL_TEXTURE2)
fslgl.glvolume_funcs.preDraw(self)
......@@ -359,7 +393,8 @@ class GLVolume(globject.GLImageObject):
version-dependent ``postDraw`` function.
"""
self.imageTexture .unbindTexture()
self.colourTexture.unbindTexture()
self.imageTexture .unbindTexture()
self.colourTexture .unbindTexture()
self.negColourTexture.unbindTexture()
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