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

GL-related scaffolding for tensors. Rough stab at a GLTensor vertex

shader - could be greatly optimised.
parent f96a0563
No related branches found
No related tags found
No related merge requests found
...@@ -7,15 +7,26 @@ ...@@ -7,15 +7,26 @@
import logging import logging
import volumeopts import props
import vectoropts
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class TensorOpts(volumeopts.ImageOpts): class TensorOpts(vectoropts.VectorOpts):
# Only show 2D ellipses around
# the three primary tensor axes
outline = props.Boolean(default=False)
# Enable/disable lighting effects
lighting = props.Boolean(default=False)
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
volumeopts.ImageOpts.__init__(self, *args, **kwargs) vectoropts.VectorOpts.__init__(self, *args, **kwargs)
...@@ -19,9 +19,9 @@ import volumeopts ...@@ -19,9 +19,9 @@ import volumeopts
class VectorOpts(volumeopts.ImageOpts): class VectorOpts(volumeopts.ImageOpts):
"""The ``VectorOpts`` class is the base class for :class:`LineVectorOpts` and """The ``VectorOpts`` class is the base class for :class:`LineVectorOpts`,
:class:`RGBVectorOpts`. It contains display settings which are common to :class:`RGBVectorOpts`, and :class:`.TensorOpts`. It contains display
both. settings which are common to each of them.
""" """
......
...@@ -263,7 +263,10 @@ def bootstrap(glVersion=None): ...@@ -263,7 +263,10 @@ def bootstrap(glVersion=None):
rendering :class:`.GLModel` instances. rendering :class:`.GLModel` instances.
``gllabel_funcs`` The version-specific module containing functions for ``gllabel_funcs`` The version-specific module containing functions for
rendering :class:`.GLLabel` instances. rendering :class:`.GLLabel` instances.
``gltensor_funcs`` The version-specific module containing functions for
rendering :class:`.GLTensor` instances.
====================== ==================================================== ====================== ====================================================
...@@ -375,6 +378,7 @@ def bootstrap(glVersion=None): ...@@ -375,6 +378,7 @@ def bootstrap(glVersion=None):
thismod.gllinevector_funcs = glpkg.gllinevector_funcs thismod.gllinevector_funcs = glpkg.gllinevector_funcs
thismod.glmodel_funcs = glpkg.glmodel_funcs thismod.glmodel_funcs = glpkg.glmodel_funcs
thismod.gllabel_funcs = glpkg.gllabel_funcs thismod.gllabel_funcs = glpkg.gllabel_funcs
thismod.gltensor_funcs = glpkg.gltensor_funcs
thismod._bootstrapped = True thismod._bootstrapped = True
......
...@@ -15,6 +15,7 @@ exist: ...@@ -15,6 +15,7 @@ exist:
~fsl.fsleyes.gl.gl21.gllinevector_funcs ~fsl.fsleyes.gl.gl21.gllinevector_funcs
~fsl.fsleyes.gl.gl21.glmodel_funcs ~fsl.fsleyes.gl.gl21.glmodel_funcs
~fsl.fsleyes.gl.gl21.gllabel_funcs ~fsl.fsleyes.gl.gl21.gllabel_funcs
~fsl.fsleyes.gl.gl21.gltensor_funcs
""" """
import glvolume_funcs import glvolume_funcs
...@@ -22,3 +23,4 @@ import glrgbvector_funcs ...@@ -22,3 +23,4 @@ import glrgbvector_funcs
import gllinevector_funcs import gllinevector_funcs
import glmodel_funcs import glmodel_funcs
import gllabel_funcs import gllabel_funcs
import gltensor_funcs
#!/usr/bin/env python
#
# gltensor_funcs.py -
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import logging
import OpenGL.GL as gl
import fsl.fsleyes.gl as fslgl
import fsl.fsleyes.gl.resources as glresources
import fsl.fsleyes.gl.textures as textures
import fsl.fsleyes.gl.shaders as shaders
log = logging.getLogger(__name__)
def init(self):
image = self.image
v1 = image.V1()
v2 = image.V2()
v3 = image.V3()
l1 = image.L1()
l2 = image.L2()
l3 = image.L3()
def vPrefilter(d):
return d.transpose((3, 0, 1, 2))
names = ['v1', 'v2', 'v3', 'l1', 'l2', 'l3']
imgs = [ v1, v2, v3, l1, l2, l3]
for name, img in zip(names, imgs):
texName = '{}_{}'.format(type(self).__name__, id(img))
if name[0] == 'v':
prefilter = vPrefilter
nvals = 3
else:
prefilter = None
nvals = 1
tex = glresources.get(
texName,
textures.ImageTexture,
texName,
img,
nvals=nvals,
normalise=True,
prefilter=prefilter)
setattr(self, '{}Texture'.format(name), tex)
self.shaders = None
compileShaders(self)
def destroy(self):
pass
def compileShaders(self):
if self.shaders is not None:
gl.glDeleteProgram(self.shaders)
vertShaderSrc = shaders.getVertexShader( self)
fragShaderSrc = shaders.getFragmentShader(self)
self.shaders = shaders.compileShaders(vertShaderSrc, fragShaderSrc)
def updateShaderState(self):
pass
def preDraw(self):
pass
def draw(self, zpos, xform=None):
pass
def postDraw(self):
pass
/*
* OpenGL vertex shader used for rendering GLTensor instances.
*
* Author: Paul McCarthy <pauldmccarthy@gmail.com>
*/
#version 120
#define PI 3.141592653589793
#define PI_ON_2 1.5707963267948966
/*
Required inputs:
- Textures containing V1, V2, V3 and L1, L2, L3
- Voxel coordinates (these are the vertices)
- Vertex index
- Ellipsoid resolution
**We use (index % resolution) to calculate:**
- U and V angles
-
*/
uniform sampler3D v1Texture;
uniform sampler3D v2Texture;
uniform sampler3D v3Texture;
uniform sampler3D l1Texture;
uniform sampler3D l2Texture;
uniform sampler3D l3Texture;
uniform mat4 v1ValXform;
uniform mat4 v2ValXform;
uniform mat4 v3ValXform;
uniform mat4 l1ValXform;
uniform mat4 l2ValXform;
uniform mat4 l3ValXform;
uniform mat4 voxToDisplayMat;
uniform vec3 imageShape;
uniform float resolution;
attribute float index;
attribute vec3 voxel;
varying vec3 fragVoxCoord;
varying vec3 fragTexCoord;
vec3 ellipsoidVertex(float l1, float l2, float l3, float u, float v) {
float cosu = cos(u);
float cosv = cos(v);
float sinu = sin(u);
float sinv = sin(v);
float spcu = sign(cosu) * pow(abs(cosu), 2);
float spcv = sign(cosv) * pow(abs(cosv), 2);
float spsu = sign(sinu) * pow(abs(sinu), 2);
float spsv = sign(sinv) * pow(abs(sinv), 2);
vec3 x;
x.x = spcu * spcv;
x.y = spcu * spsv;
x.z = spsu;
return x;
}
void main(void) {
float umin = -PI_ON_2;
float umax = PI_ON_2;
float vmin = -PI;
float vmax = PI;
float ustep = (umax - umin) / resolution;
float vstep = (vmax - vmin) / resolution;
// Index of this vertex
// within the ellipsoid
float ellipsoidIndex = mod(index, resolution);
// Ellipsoid angles for this vertex
float u = umin + ustep * ellipsoidIndex;
float v = vmin + vstep * ellipsoidIndex;
// Lookup the tensor parameters from the textures
vec3 texCoord = (voxel + 0.5) / imageShape;
vec3 v1 = texture3D(v1Texture, texCoord).xyz;
vec3 v2 = texture3D(v2Texture, texCoord).xyz;
vec3 v3 = texture3D(v3Texture, texCoord).xyz;
float l1 = texture3D(l1Texture, texCoord).x;
float l2 = texture3D(l2Texture, texCoord).x;
float l3 = texture3D(l3Texture, texCoord).x;
// Transform from normalised
// texture values to real values
v1 = v1 * v1ValXform[0].x + v1ValXform[3].x;
v2 = v2 * v2ValXform[0].x + v2ValXform[3].x;
v3 = v3 * v3ValXform[0].x + v3ValXform[3].x;
l1 = l1 * l1ValXform[0].x + l1ValXform[3].x;
l2 = l2 * l2ValXform[0].x + l2ValXform[3].x;
l3 = l3 * l3ValXform[0].x + l3ValXform[3].x;
// Calculate the position of
// this vertex on the ellipsoid.
// Vertices are grouped into quads -
// figure out what corner we're on
vec3 pos;
float corner = mod(ellipsoidIndex, 4);
if (corner == 0) pos = ellipsoidVertex(l1, l2, l3, u, v);
else if (corner == 1) pos = ellipsoidVertex(l1, l2, l3, u + ustep, v);
else if (corner == 2) pos = ellipsoidVertex(l1, l2, l3, u + ustep, v + vstep);
else if (corner == 3) pos = ellipsoidVertex(l1, l2, l3, u, v + vstep);
// Transform the vertex from
// the fibre coordinate system
// to the voxel coordinate system
mat3 eigvecs = mat3(v1, v2, v3);
pos = pos * eigvecs + voxel;
// Transform from voxels into display
gl_Position = gl_ModelViewProjectionMatrix *
voxToDisplayMat *
vec4(pos, 1);
fragVoxCoord = voxel;
fragTexCoord = texCoord;
}
#!/usr/bin/env python
#
# gltensor.py -
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import logging
import glvector
import fsl.fsleyes.gl as fslgl
log = logging.getLogger(__name__)
class GLTensor(glvector.GLVector):
def __init__(self, image, display):
glvector.GLVector.__init__(self, image, display)
fslgl.gltensor_funcs.init(self)
def destroy(self):
glvector.GLVector.destroy(self)
fslgl.gltensor_funcs.destroy(self)
def getDataResolution(self, xax, yax):
"""Overrides :meth:`.GLVector.getDataResolution`. Returns a pixel
resolution suitable for rendering this ``GLTensor``.
"""
res = list(glvector.GLVector.getDataResolution(self, xax, yax))
res[xax] *= 20
res[yax] *= 20
return res
def compileShaders(self):
fslgl.gltensor_funcs.compileShaders(self)
def updateShaderState(self):
fslgl.gltensor_funcs.updateShaderState(self)
def preDraw(self):
glvector.GLVector.preDraw(self)
fslgl.gltensor_funcs.preDraw(self)
def draw(self, zpos, xform=None):
fslgl.gltensor_funcs.draw(self, zpos, xform)
def postDraw(self):
glvector.GLVector.postDraw(self)
fslgl.gltensor_funcs.postDraw(self)
...@@ -253,6 +253,9 @@ _shaderTypePrefixMap = td.TypeDict({ ...@@ -253,6 +253,9 @@ _shaderTypePrefixMap = td.TypeDict({
('GLModel', 'vert') : 'glmodel', ('GLModel', 'vert') : 'glmodel',
('GLModel', 'frag') : 'glmodel', ('GLModel', 'frag') : 'glmodel',
('GLTensor', 'vert') : 'gltensor',
('GLTensor', 'frag') : 'glvector',
}) })
"""This dictionary provides a mapping between :class:`.GLObject` types, """This dictionary provides a mapping between :class:`.GLObject` types,
and file name prefixes, identifying the shader programs to use. and file name prefixes, identifying the shader programs to use.
......
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