Commit 50edd483 authored by Paul McCarthy's avatar Paul McCarthy 🚵
Browse files

Merge branch 'enh/volume-lighting' into 'master'

Enh/volume lighting

See merge request fsl/fsleyes/fsleyes!222
parents 8697a284 7c23a6af
......@@ -3,13 +3,14 @@
set -e
apt-get install -y bc
# Temporary: this should be done
# in docker image definition
# in docker image definitions
apt install -y locales
locale-gen en_US.UTF-8
locale-gen en_GB.UTF-8
update-locale
export LANG=en_GB.UTF-8
# If running on a fork repository, we merge in the
# upstream/master branch. This is done so that merge
......@@ -72,27 +73,29 @@ if [ "$TEST_STYLE"x != "x" ]; then flake8 fsleyes || t
if [ "$TEST_STYLE"x != "x" ]; then pylint --output-format=colorized fsleyes || true; fi;
if [ "$TEST_STYLE"x != "x" ]; then exit 0; fi
# Run the tests
export MPLBACKEND=wxagg
# Run the tests. First batch requires
# a GUI, so we run via xvfb-run
export FSLEYES_TEST_GL=2.1
((xvfb-run -a -s "-screen 0 1920x1200x24" pytest --cov-report= --cov-append -m "not (clitest or overlayclitest)" && echo "0" > status) || echo "1" > status) || true
status=`cat status`
failed=$status
sleep 5
((xvfb-run -a -s "-screen 0 1920x1200x24" pytest --cov-report= --cov-append -m "clitest" && echo "0" > status) || echo "1" > status) || true
# Remaining tests are all off-screen,
# so we can use osmesa
((pytest --cov-report= --cov-append -m "clitest" && echo "0" > status) || echo "1" > status) || true
status=`cat status`
failed=`echo "$status + $failed" | bc`
sleep 5
((xvfb-run -a -s "-screen 0 1920x1200x24" pytest --cov-report= --cov-append -m "overlayclitest" && echo "0" > status) || echo "1" > status) || true
((pytest --cov-report= --cov-append -m "overlayclitest" && echo "0" > status) || echo "1" > status) || true
status=`cat status`
failed=`echo "$status + $failed" | bc`
sleep 5
# test overlay types for GL14 as well
export FSLEYES_TEST_GL=1.4
((xvfb-run -a -s "-screen 0 1920x1200x24" pytest --cov-report= --cov-append -m "overlayclitest" && echo "0" > status) || echo "1" > status) || true
((pytest --cov-report= --cov-append -m "overlayclitest" && echo "0" > status) || echo "1" > status) || true
status=`cat status`
failed=`echo "$status + $failed" | bc`
......
......@@ -6,7 +6,7 @@ ENV PY_PACKAGES "python3-pip python3.7-venv"
ENV PY_VENV "python3.7 -m venv"
ENV WXPYTHON_VERSION "wxPython-4.1.1"
ENV DEBIAN_FRONTEND "noninteractive"
ENV LANG "C.UTF-8"
ENV LANG "en_GB.UTF-8"
ADD scripts/install_system_deps.sh /scripts/install_system_deps.sh
ADD scripts/install_python.sh /scripts/install_python.sh
......
......@@ -6,7 +6,7 @@ ENV PY_PACKAGES "python3-pip python3.8-venv"
ENV PY_VENV "python3.8 -m venv"
ENV WXPYTHON_VERSION "wxPython-4.1.1"
ENV DEBIAN_FRONTEND "noninteractive"
ENV LANG "C.UTF-8"
ENV LANG "en_GB.UTF-8"
ADD scripts/install_system_deps.sh /scripts/install_system_deps.sh
ADD scripts/install_python.sh /scripts/install_python.sh
......
......@@ -6,7 +6,7 @@ ENV PY_PACKAGES "python3-pip python3.9-venv"
ENV PY_VENV "python3.9 -m venv"
ENV WXPYTHON_VERSION "wxPython-4.1.1"
ENV DEBIAN_FRONTEND "noninteractive"
ENV LANG "C.UTF-8"
ENV LANG "en_GB.UTF-8"
ADD scripts/install_system_deps.sh /scripts/install_system_deps.sh
ADD scripts/install_python.sh /scripts/install_python.sh
......
......@@ -21,6 +21,11 @@ apt-get install -y --ignore-missing \
libhdf5-dev \
openssh-client
apt install -y locales
locale-gen en_US.UTF-8
locale-gen en_GB.UTF-8
update-locale
cat /etc/lsb-release | grep "14.04" && apt-get install -y libspatialindex-c3 || true
cat /etc/lsb-release | grep "16.04" && apt-get install -y libspatialindex-c4v5 || true
cat /etc/lsb-release | grep "18.04" && apt-get install -y libspatialindex-c4v5 || true
......
......@@ -208,6 +208,12 @@ test:3.8:
<<: *test_template
test:3.9:
stage: test
image: pauldmccarthy/fsleyes-py39-wxpy4-gtk3
<<: *test_template
test:build-pypi-dist:
stage: test
image: pauldmccarthy/fsleyes-py36-wxpy4-gtk3
......
......@@ -13,6 +13,18 @@ chronological order.
--------------------------
Added
^^^^^
* The lighting effect in the 3D view is now applied to ``volume`` overlays
(OpenGL 2.1 or newer only).
* New ``--lightDistance`` option (for 3D view), allowing the distance of
the light source from the centre of the display bounding box to be set.
* New ``--noBlendByIntensity`` option, for ``volume`` overlays in the 3D view,
allowing the modulation of samples by voxel intensity to be disabled.
Changed
^^^^^^^
......@@ -22,12 +34,21 @@ Changed
* Removed dependence on [Free]GLUT - this means that ``fsleyes render`` can
now be used on headless systems without using ``xvfb-run``, as long as
`OSMesa <https://docs.mesa3d.org/osmesa.html>`_ is installed.
* The ``--lightPos`` command-line option (for the 3D view) has been changed to
expect three rotation values (in degrees), which specify the position of the
light source with respect to the centre of the display bounding box. This
can be combined with the new ``--lightDistance`` option to specify the
position of the light source.
* FSLeyes no longer ignores the ``LIBGL_ALWAYS_INDIRECT`` environment
variable.
Fixed
^^^^^
* Various fixes and improvements to the lighting effect on ``mesh`` overlays
in the 3D view.
* When opening a ``melodic_IC.nii.gz`` file with the
``--autoDisplay'`/``-ad``, option, the ``melodic_IC`` file is now selected
by default, instead of the ``mean`` underlay.
......
......@@ -12,11 +12,10 @@
# {{ param_modulate }}
# {{ param_flatColour }}
# {{ param4_cmapXform }}
#
# {{ param_lighting }} - The xyz components contain the light position
# in display coordinates. The w component contains a
# 1 if lighting should be applied, or a -1 if
# lighting should not be applied.
# {{ param_lighting }} - The xyz components contain the light position
# in display coordinates. The w component contains
# a 1 if lighting should be applied, or a -1 if
# lighting should not be applied.
#
# Input textures:
# - {{ texture_cmap }}
......@@ -27,7 +26,6 @@
# - {{ varying_modulateData }} - Vertex alpha modulation value
# - {{ varying_vertex }} - Vertex position in display coordinates
# - {{ varying_normal }} - Vertex normal
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
......
......@@ -5,7 +5,7 @@
#
# Input parameters:
#
# {{ param4_normalMatrix }} - Matrix to transform normal vectors into
# {{ param3_normalMatrix }} - Matrix to transform normal vectors into
# display coordinates.
#
# Input attributes:
......@@ -15,13 +15,15 @@
# Outputs:
# {{ varying_vertexData }} - As above, passed through to fragment shader.
# {{ varying_modulateData }} - As above, passed through to fragment shader.
# {{ varying_vertex }} - The vertex position in display coordinates.
# {{ varying_vertex }} - The vertex position in view coordinates.
# {{ varying_normal }} - As above, passed through to fragment shader.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
PARAM normalMatrix[4] = {{ param4_normalMatrix }};
PARAM normalMatrix[3] = {{ param3_normalMatrix }};
TEMP normal;
# Transform the vertex position into clip coordinates
DP4 result.position.x, state.matrix.mvp.row[0], vertex.position;
......@@ -36,10 +38,17 @@ DP4 {{ varying_vertex }}.z, state.matrix.modelview[0].row[2], vertex.position;
DP4 {{ varying_vertex }}.w, state.matrix.modelview[0].row[3], vertex.position;
# Transform the normal vector
DP4 {{ varying_normal }}.x, normalMatrix[0], {{ attr_normal }};
DP4 {{ varying_normal }}.y, normalMatrix[1], {{ attr_normal }};
DP4 {{ varying_normal }}.z, normalMatrix[2], {{ attr_normal }};
DP4 {{ varying_normal }}.w, normalMatrix[3], {{ attr_normal }};
DP3 normal.x, normalMatrix[0], {{ attr_normal }};
DP3 normal.y, normalMatrix[1], {{ attr_normal }};
DP3 normal.z, normalMatrix[2], {{ attr_normal }};
# Normalise to unit length
DP3 normal.w, normal, normal;
RSQ normal.w, normal.w;
MUL normal, normal, normal.w;
MOV normal.w, 0;
MOV {{ varying_normal }}, normal;
# Copy the vertex/modulate data
MOV {{ varying_vertexData }}, {{ attr_vertexData }};
......
......@@ -5,16 +5,15 @@
# Input parameters:
#
# {{ param_colour }} - The mesh colour
#
# {{ param_lighting }} - The xyz components contain the light position
# in display coordinates. The w component contains a
# 1 if lighting should be applied, or a -1 if
# in display coordinates. The w component contains
# a 1 if lighting should be applied, or a -1 if
# lighting should not be applied.
#
#
# Input varyings:
# - {{ varying_vertex }} - Vertex position in display coordinates
# - {{ varying_normal }} - Vertex normal
# - {{ varying_vertex }} - Vertex position in display coordinates
# - {{ varying_normal }} - Vertex normal
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
......
......@@ -5,7 +5,7 @@
#
# Input parameters:
#
# {{ param4_normalMatrix }} - Matrix to transform normal vectors into
# {{ param3_normalMatrix }} - Matrix to transform normal vectors into
# display coordinates.
#
# Input attributes:
......@@ -18,7 +18,9 @@
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
PARAM normalMatrix[4] = {{ param4_normalMatrix }};
PARAM normalMatrix[3] = {{ param3_normalMatrix }};
TEMP normal;
# Transform the vertex position into clip coordinates
DP4 result.position.x, state.matrix.mvp.row[0], vertex.position;
......@@ -33,9 +35,16 @@ DP4 {{ varying_vertex }}.z, state.matrix.modelview[0].row[2], vertex.position;
DP4 {{ varying_vertex }}.w, state.matrix.modelview[0].row[3], vertex.position;
# Transform the normal vector
DP4 {{ varying_normal }}.x, normalMatrix[0], {{ attr_normal }};
DP4 {{ varying_normal }}.y, normalMatrix[1], {{ attr_normal }};
DP4 {{ varying_normal }}.z, normalMatrix[2], {{ attr_normal }};
DP4 {{ varying_normal }}.w, normalMatrix[3], {{ attr_normal }};
DP3 normal.x, normalMatrix[0], {{ attr_normal }};
DP3 normal.y, normalMatrix[1], {{ attr_normal }};
DP3 normal.z, normalMatrix[2], {{ attr_normal }};
# Normalise to unit length
DP3 normal.w, normal, normal;
RSQ normal.w, normal.w;
MUL normal, normal, normal.w;
MOV normal.w, 0;
MOV {{ varying_normal }}, normal;
END
......@@ -21,21 +21,29 @@ TEMP specular;
# Calculate the direction towards the
# light and normalise it to unit length
#
# lightDir = normalize(lightPos - vertex);
#
SUB lightDir.xyz, {{ lightPos }}, {{ vertex }};
DP3 lightDir.w, lightDir, lightDir;
RSQ lightDir.w, lightDir.w;
MUL lightDir.xyz, lightDir, lightDir.w;
# Normalised camera direction
#
# viewDir = normalize(-vertex)
#
MUL viewDir, {{ vertex }}, -1;
DP3 viewDir.w, viewDir, viewDir;
RSQ viewDir.w, viewDir.w;
MUL viewDir.xyz, viewDir, viewDir.w;
# Normalised reflection angle from
# the light off the surface
#
# reflect(I, N) = I – 2 * dot(N, I) * N
# angle = normalize(reflect(lightDir, normal));
#
DP3 angle.xyz, lightDir, {{ normal }};
MUL angle.xyz, angle, {{ normal }};
MUL angle.xyz, angle, 2;
......@@ -48,9 +56,15 @@ MUL angle.xyz, angle, angle.w;
MOV ambient.x, 0.5;
# Diffuse component, clamped to [0, 1]
#
# diffuse = clamp(dot(normal, lightDir), 0.0, 1.0);
#
DP3_SAT diffuse.x, {{ normal }}, lightDir;
# Specular component, clamped to [0, 1]
#
# spec = clamp(pow(max(dot(angle, viewDir), 0.0), 64), 0.0, 1.0);
#
DP3 specular.x, angle, viewDir;
MAX specular.x, specular.x, 0.0;
......@@ -61,6 +75,9 @@ MOV specular.y, 64.0;
POW_SAT specular.x, specular.x, specular.y;
# Combine the final colour
#
# result = colour * vec3(amb + diff + spec);
#
ADD {{ out_colour }}.rgb, ambient.x, diffuse.x;
ADD {{ out_colour }}.rgb, {{ out_colour }}, specular.x;
MUL {{ out_colour }}.rgb, {{ out_colour }}, {{ colour }};
......
......@@ -15,7 +15,9 @@ OPTION ARB_precision_hint_nicest;
# {{ param_negCmap }}
#
# {{ param_screenSize }} - First two components contain the screen width
# and height in pixels.
# and height in pixels. Third component determines whether to modulate
# samples by voxel intensity (blendByIntensity, +1), or whether to
# only use the blend factor (-1).
#
# {{ param_rayStep }} - xyz is a vector defining how far to move through
# the volume texture space on each ray-casting iteration.
......@@ -213,18 +215,33 @@ MAD tempVar.x, tempVar.x, 2.0, -1;
MIN skipTest.x, skipTest.x, tempVar.x;
# Adjust the sample opacity - it is
# a function of the (normalised)
# voxel intensity and the blend factor.
# Adjust the sample opacity -
# if blendByIntesnity (screenSize.z),
# it is a function of the (normalised)
# voxel intensity and the blend factor
# (stored in tempVar.y):
#
# a = 1 - pow(1 - clamp(voxValue, 0, 1), 1 - blendFactor);
#
# Otherwise it is just a function of
# the blend factor (stored in tempVar.x):
#
# a = 1 - blendFactor;
SUB tempVar.x, 1, {{ param_settings }}.x;
# Clamp voxValue to [0, 1]
# (MOV_SAT does not work for me)
MIN colour.a, voxValue.x, 1;
MAX colour.a, colour.a, 0;
MIN tempVar.y, voxValue.x, 1;
MAX tempVar.y, tempVar.y, 0;
SUB tempVar.y, 1, tempVar.y;
SUB tempVar.z, 1, {{ param_settings }}.x;
POW tempVar.y, tempVar.y, tempVar.z;
SUB tempVar.y, 1, tempVar.y;
# blend by intensity or just blend by blend factor
CMP colour.a, {{ param_screenSize }}.z, tempVar.x, tempVar.y;
SUB colour.a, 1, colour.a;
POW colour.a, colour.a, {{ param_settings }}.x;
SUB colour.a, 1, colour.a;
MUL colour.rgb, colour, colour.a;
# Blend the sample into the
......
......@@ -6,10 +6,11 @@
#version 120
#pragma include glmesh_data_common.glsl
#pragma include glmesh_3d_lighting.glsl
#pragma include phong_lighting.glsl
uniform bool lighting;
uniform vec3 lightPos;
varying vec3 fragVertex;
varying vec3 fragNormal;
......@@ -19,8 +20,10 @@ void main(void) {
vec4 colour = glmesh_data_colour();
if (lighting) {
colour.rgb = mesh_lighting(fragVertex, fragNormal, lightPos, colour.rgb);
colour.rgb = phong_lighting(fragVertex,
fragNormal,
lightPos,
colour.rgb);
}
gl_FragColor = colour;
......
......@@ -4,23 +4,18 @@
* Author: Paul McCarthy <pauldmccarthy@gmail.com>
*/
#version 120
#pragma include glmesh_3d_lighting.glsl
#pragma include phong_lighting.glsl
/* Colour to use. */
uniform vec4 colour;
/* Toggle lighting */
uniform bool lighting;
/* Toggle lighting */
uniform vec3 lightPos;
/* Vertex */
varying vec3 fragVertex;
/* Vertex normal */
varying vec3 fragNormal;
......@@ -30,11 +25,10 @@ void main(void) {
vec4 finalColour = colour;
if (lighting) {
finalColour.rgb = mesh_lighting(fragVertex,
fragNormal,
lightPos,
finalColour.rgb);
finalColour.rgb = phong_lighting(fragVertex,
fragNormal,
lightPos,
finalColour.rgb);
}
gl_FragColor = finalColour;
......
......@@ -5,14 +5,13 @@
*/
#version 120
attribute vec3 vertex;
attribute vec3 normal;
varying vec3 fragVertex;
varying vec3 fragNormal;
attribute vec3 vertex;
attribute vec3 normal;
varying vec3 fragVertex;
varying vec3 fragNormal;
void main(void) {
fragVertex = (gl_ModelViewMatrix * vec4(vertex, 1)).xyz;
fragNormal = normalize(gl_NormalMatrix * normal);
gl_Position = gl_ModelViewProjectionMatrix * vec4(vertex, 1);
fragVertex = (gl_ModelViewMatrix * vec4(vertex, 1)).xyz;
fragNormal = normalize(gl_NormalMatrix * normal);
gl_Position = gl_ModelViewProjectionMatrix * vec4(vertex, 1);
}
......@@ -7,6 +7,7 @@
#pragma include spline_interp.glsl
#pragma include test_in_bounds.glsl
#pragma include phong_lighting.glsl
#pragma include rand.glsl
/*
......@@ -33,14 +34,11 @@ uniform float modOffset;
/*
* Texture containing the colour map.
* Texture containing the colour maps.
*/
uniform sampler1D colourTexture;
/*
* Texture containing the negative colour map.
*/
uniform sampler1D negColourTexture;
uniform bool useNegCmap;
/*
* Matrix which can be used to transform a texture value
......@@ -60,28 +58,20 @@ uniform vec3 imageShape;
*/
uniform vec3 texShape;
/*
* Flag which determines whether to
* use the negative colour map.
*/
uniform bool useNegCmap;
/*
* Use spline interpolation?
*/
uniform bool useSpline;
/*
* Clip voxels below this value. This must be specified
* in the image texture data range.
* Clip voxels below/above these value. This must be specified
* in the image texture data range. invertClip inverts the logic,
* i.e. clip voxels that are inside the clipLow/High bounds.
*/
uniform float clipLow;
/*
* Clip voxels above this value. This must be specified
* in the image texture data range.
*/
uniform float clipHigh;
uniform bool invertClip;
/*
* Value in the image texture data range which corresponds
......@@ -92,31 +82,11 @@ uniform float clipHigh;
uniform float texZero;
/*
* Invert clipping behaviour - clip voxels
* that are inside the clipLow/High bounds.
*/
uniform bool invertClip;
/*
* Number of active clip planes. Regions which are clipped
* by *all* active clip planes are not drawn.
*/
uniform int numClipPlanes;
/*
* The clip planes, specified as plane equations in the image
* texture coordinate system.
* Clipping planes - see the is_clipped function below.
*/
uniform int numClipPlanes;
uniform vec4 clipPlanes[5];
/*
* How the clipping planes are applied:
* - 1 clips the intersection of all planes
* - 2 clips the union of all planes
* - 3 clips the complement of all planes
*/
uniform int clipMode;
uniform int clipMode;
/*
* A vector which defines how far to move in one iteration
......@@ -145,6 +115,15 @@ uniform float stepLength;
uniform float blendFactor;
/*
* If true, colours from samples along the ray are modulated
* according to the voxel intensity before being blended.
* Otherwise colours are simply blended according to the
* blendFactor.
*/
uniform bool blendByIntensity;
/*
* Final transparency that the fragment should have.
*/
......@@ -158,23 +137,215 @@ uniform float alpha;
*/
uniform mat4 tex2ScreenXform;
/*
* Apply a simple lighting model to the rendered volume.
* Light position is specified in *image texture*
* coordinates.
*/
uniform bool lighting;
uniform vec3 lightPos;
/*
* Corresponding image texture coordinates.
* Image texture coordinates.
*/
varying vec3 fragTexCoord;
/*
* Fragment location in display coordinate system.
*/
varying vec3 fragVertex;
#pragma include glvolume_common.glsl
/*
* Test the given texture coordinate to see if it is within a
* region clipped by the clipping plane(s).
*
* - numClipPlanes: Number of active clip planes. Regions which are
* clipped by *all* active clip planes are not drawn.
*
* - clipPlanes[5]: The clip planes, specified as plane equations in the
* image texture coordinate system.
*
* - clipMode: How the clipping planes are applied:
* - 1 clips the intersection of all planes
* - 2 clips the union of all planes
* - 3 clips the complement of all planes