Skip to content

Commit

Permalink
Merge branch 'main' of github.com:rikkeaas/strangevis-moviemaker into…
Browse files Browse the repository at this point in the history
… main
  • Loading branch information
rikke.aas committed May 9, 2021
2 parents 6c0df96 + 6ee46ce commit a06d1b3
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 11 deletions.
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,18 @@ Secondary requirements from Dr. Strangevis which our program fulfills:
- T23: I want to be able to generate realistic images with shadows.
- T25: I want to see an overview of the whole dataset.

Extra features not specified by Dr. Strangevis which our program includes:

- Saving snapshots : users have the ability to save snapshots of the program. These will be saved across program instances letting the user "pick up where they left of" the next time they run the program.
- Animation : users can play an animation that loops through all saved snapshots. This allows the user to explore different parts of the volume without having to explicitly set the different transformations and transfer function mappings. It also allows for nice presentations of the volume models.
- Saving high resolution screenshots : users can choose to save high resolution screen shots of the rendered volume.
- Asynchronous loading : volumetric datasets can be very large and thus take many tens of seconds to load. Therefore we have implemented asynchronous loading of the volumes, allowing the user to continue to interact with the program while the new volume is being loaded in the background.

### 3D Volume Rendering

The main task of our program was to have the ability to load and render a volumetric model.

Our program uses volumetric data from a data file chosen by the user. We assume the file the user chooses follows the specifications in `fileformat.txt`, and that the file `<volumedata>.dat` is accompanied by a metadata file called `<volumedata>.ini` containing information about the spacing of the regular grid of voxels.
Our program uses volumetric data from a data file chosen by the user. We assume the file the user chooses follows the specifications in [`fileformat.txt`](fileformat.txt), and that the file `<volumedata>.dat` is accompanied by a metadata file called `<volumedata>.ini` containing information about the spacing of the regular grid of voxels.

This volume is rendered in the main window of our application by direct volume rendering. This is achieved by rendering a screen filling quad in order to shoot a ray through each rendered pixel. The rays are checked for intersection with the bounding box of the volume, if there is no intersection the pixel is set to the color of the background. If there is intersection, the shader steps though the volume along the ray while sampling the volume to check the density at each point. The density is then used to sample the user defined `transfer function`, which maps density values to a RGBA-color vector. If this color vector does not have a very small alpha value (opacity), we estimate the gradient at the point in the volume by central differences. The gradient is used as the normal at the point when calculating the final color contribution of the point to the pixel. We are using the diffuse component of `Phong shading` to achieve a more realistic final result.

Expand Down Expand Up @@ -75,9 +82,9 @@ The `Movie Maker` part of our program hints about presenting the data in a way w

#### States

A state can be saved in two different ways; either using the <kbd>K</kbd> key, or using the large <kbd>+</kbd> button in the `Keyframe Handler` in the lower left corner of the screen. A state is then saved to a local folder, and contains information about the `projection`, `rotation`, `scaling`, and `translation` matrices used with the current volume in view, in addition to the `background color`, `transfer function` data and each `layer` used. A low-resolution snapshot is also saved with each state, which together creates the Keyframes the Keyframe Handler is presenting.
A state can be saved in two different ways; either using the <kbd>K</kbd> key, or using the large <kbd>+</kbd> button in the `Keyframe Handler` in the lower left corner of the screen. A state is then saved to a local folder, and contains information about the `projection`, `rotation`, `scaling`, and `translation` matrices used with the current volume in view, in addition to the `background color`, `transfer function` data and each `layer` used. A low-resolution snapshot is also saved with each state, which together creates the `Keyframes` the `Keyframe Handler` is presenting.

A state is loaded on demand, and can be triggered by clicking on a saved Keyframe. This initializes a function which updates the matrices to the corresponding ones included in the state file, and the same goes for the other contents saved in the state. The results of a state read is that the volume looks exactly like the corresponding low-resolution snapshot which was clicked to trigger the state read.
A state is loaded on demand, and can be triggered by clicking on a saved `Keyframe`. This initializes a function which updates the matrices to the corresponding ones included in the state file, and the same goes for the other contents saved in the state. The results of a state read is that the volume looks exactly like the corresponding low-resolution snapshot which was clicked to trigger the state read.

#### Interpolation

Expand Down Expand Up @@ -125,14 +132,16 @@ Before we began implementing the program, we followed the [Five Design Sheet Met

# Installation

TODO: Write about how to install the project
To install the project, simply download the [latest release](https://github.com/rikkeaas/strangevis-moviemaker/releases) which contains the complete source code for the project, in addition to all prebuilt dependencies and binaries for Windows.

# Running

To run the project, un-zip the downloaded folder from the previous step. After this, a folder named `Release` will be created, and this contains the executable file `strangevis-moviemaker.exe`. <kbd>Double Click</kbd> this executable, and the project will start running.

# Usage

`Strangevis Movie Maker` is highly interactive with loads of different features. Here is a list of how to use every single one of them.

TODO: Write about how to run the project

## Keyboard Shortcuts and Mouse Actions

- <kbd>A</kbd>: Interpolate through all saved states
Expand Down Expand Up @@ -181,7 +190,13 @@ Items in the <kbd>Advanced</kbd> menu:

# Feature Preview

![Linear Interpolation Animation Preview](assets/linear-interpolation.gif)
![Linear Interpolation Preview](assets/improved_linear_interpolation.gif)

> Linear Interpolation between saved states
![File Open Preview](assets/file_open.gif)

> Loading a new model with a cut enabled
# Screenshots

Expand All @@ -191,10 +206,6 @@ Items in the <kbd>Advanced</kbd> menu:

![Skeleton](assets/app_preview_2.png)

# Ackowledgements

TODO: Might have this field in the README

# License

Copyright © 2021, [Rikke Aas](https://github.com/rikkeaas) and [Christian Hein](https://github.com/chrhein). Written as a part of the [INF252](https://vis.uib.no/courses/inf252/) course at the [University of Bergen](https://github.com/uib). Released under the GPLv3 License.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/improved_linear_interpolation.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions fileformat.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
== DAT file format ==

All data sets are stored in a simple binary format. It consits of a 6 byte header which contains the data set dimensions (one unsigned 2 byte value per dimension) followed by the voxel data (one unsigned 2 byte value per voxel). After the 6 byte header, the voxels are stored in slice-row-column order, i.e., the elements of each slice are contiguous and, within a slice, the elements of each row (scanline) are contiguous. While each voxel is stored in 2 bytes, only 12 of the 16 bits are actually used. Hence, the effective data range is [0,4095].

The format uses little-endian byte order (https://en.wikipedia.org/wiki/Endianness). Note that in C/C++, the endianness is determined by the underlying processor architecture (for x86 processors, this is little endian which matches the file format). However, the Java Virtual Machine uses big-endian byte order, so you will normally need to swap bytes when reading the file (or use a method that does this during reading).

In addition to the actual data file, an additional metadata file with the extension ".ini" (and the same file name) is normally present. This file is a simple text file which contains the voxel spacing in each dimension in the case of anisotropic volumes (regular grids instead of cartesian grids). The content of this file looks like this:

[DatFile]
oldDat Spacing X=1.0
oldDat Spacing Y=1.0
oldDat Spacing Z=2.0

The spacing values can be interpreted as the size of a cell in the grid in world units (e.g. millimeters). In this example, a single cell in the volume would correspond to a 1x1x2 region in space. Hence, if we have a volume grid with dimensions 256x256x128 with this voxel size, the whole volume would correspond to a cube of 256x256x256 units in world coordinates - each cell in this cube has the same width and height, but double the depth. On windows, you can read such files with the deprecated Windows API function GetPrivateProfileInt, but a better option is probably to use one of the many parser implementations for such files (e.g., [1,2,3]).

Here is a simple C/C++ code snippet to read a DAT file into an array:

FILE *fp = fopen("filename.dat","rb");
unsigned int uWidth, uHeight, uDepth;

fread((void*)&uWidth,1,sizeof(unsigned short),fp);
fread((void*)&uHeight,1,sizeof(unsigned short),fp);
fread((void*)&uDepth,1,sizeof(unsigned short),fp);

unsigned short *pData = new unsigned short[uWidth*uHeight*uDepth];
fread((void*)pData,uWidth*uHeight*uDepth,sizeof(unsigned short),fp);
fclose(fp);

For a position (x,y,z) in the volume, the corresponding value can be retrieved in the following manner:

unsigned short uValue = pData[x + y*uWidth + z*uWidth*uHeight];


== References ==

[1] http://www.compuphase.com/minini.htm
[2] https://github.com/benhoyt/inih
[3] https://github.com/brofield/simpleini

0 comments on commit a06d1b3

Please sign in to comment.