Trees | Indices | Help |
---|
|
1 #!/usr/bin/env python 2 3 #! This file is a literate Python program. You can compile the documentation 4 #! using mylit (http://pypi.python.org/pypi/mylit/). 5 ## title = "glitter Example: Voxelization" 6 ## stylesheet = "pygments_style.css" 7 8 # <h1><i>glitter</i> Example: Voxelization</h1> 9 10 # <h2>Summary</h2> 11 12 # This program will create an invisible OpenGL context to voxelize a mesh given 13 # as an <a href="www.hdfgroup.org/HDF5/">HDF5</a> data file. The resulting 14 # volume is written to a file. 15 16 #! TODO describe voxelization algorithm 17 #! TODO include spreadbits.py so the data can be viewed in the volume viewer 18 19 # <h2>Front matter</h2> 20 21 # <h3>Module docstring</h3> 22 23 # The module docstring is used as a description of this example in the 24 # generated documentation: 25 """Voxelize a mesh. 26 27 @author: Stephan Wenger 28 @date: 2012-02-29 29 """ 30 31 # We can usually import classes and functions contained in <i>glitter</i> 32 # submodules directly from glitter: 33 from glitter import VertexArray, TextureArray2D, uint32, Framebuffer, State, ShaderProgram 34 35 # <h2>Shaders</h2> 36 37 # The vertex shader transforms the position as usual. 38 vertex_code = """ 39 #version 400 core 40 41 layout(location=0) in vec4 in_position; 42 out vec4 ex_position; 43 44 void main() { 45 gl_Position = ex_position = in_position; 46 } 47 """ 48 49 # The fragment code will perform the actual work. It will quantize each 50 # fragment into a depth layer, compute a bitmask corresponding to that depth, 51 # and write the bit mask into the color attachments. The number of color 52 # attachments is only known at runtime, which is why a <code>%d</code> 53 # placeholder will have to be filled in just before the shader is compiled. 54 fragment_code = """ 55 #version 400 core 56 57 in vec4 ex_position; 58 uniform bool solid; 59 layout(location=0) out uvec4 fragmentColor[%d]; 60 61 uvec4 voxelize(uint z, uint offset, uint fill, uint base) { 62 if (z >= (offset + 1u) * 128u) return uvec4(0u); 63 if (z < offset * 128u) return uvec4(fill); 64 z -= offset * 128u; 65 return uvec4( 66 z < 128u ? (z >= 96u ? base << (z - 96u) : fill) : 0u, 67 z < 96u ? (z >= 64u ? base << (z - 64u) : fill) : 0u, 68 z < 64u ? (z >= 32u ? base << (z - 32u) : fill) : 0u, 69 z < 32u ? base << (z - 0u) : 0u 70 ); 71 } 72 73 void main() { // if solid, switch on all bits >= z; else, switch on bit == z 74 uint z = uint(round(128.0 * float(fragmentColor.length()) * 0.5 * (ex_position.z + 1.0))); 75 for (int i = 0; i < fragmentColor.length(); ++i) 76 fragmentColor[i] = voxelize(z, i, solid ? 0xffffffffu : 0u, solid ? 0xffffffffu : 1u); 77 } 78 """ 79 80 # <h2>Voxelization</h2> 81 82 # The function <code>voxelize()</code> performs solid or boundary voxelization 83 # of a mesh into a volume of given size.85 """Voxelize a mesh into a cube of length C{size}. 86 87 @param mesh: The mesh to voxelize. 88 @type mesh: L{VertexArray} 89 @param size: The length of the resulting cube. Should be a multiple of 128. 90 @type size: C{int} 91 @param solid: Whether to perform solid voxelization instead of boundary voxelization. 92 @type solid: C{bool} 93 94 @attention: The result of boundary voxelization is likely to have holes. To 95 obtain a watertight volume, you can voxelize the volume from different 96 directions and combine the results, or use the gradient of a solid volume. 97 """ 98 99 # An array of 2D integer textures will hold the output volume encoded in 100 # its bits: 101 volume = TextureArray2D(shape=(size // 128, size, size, 4), dtype=uint32) 102 103 # We create a framebuffer with all layers of the volume attached and 104 # directly bind it: 105 with Framebuffer(*[volume[i] for i in range(len(volume))]) as fbo: 106 fbo.clear() 107 # Depending on the voxelization mode, we set the logic op mode. We then 108 # create and activate a shader program and draw the mesh. 109 with State(logic_op_mode="XOR" if solid else "OR", color_logic_op=True): 110 with ShaderProgram(vertex=vertex_code, fragment=fragment_code % len(volume), solid=solid): 111 mesh.draw() 112 113 # Finally, the texture array is returned. The client code can decide to 114 # download the data or to process it further on the GPU. 115 return volume116 117 # <h2>Initialization and main loop</h2> 118 119 # Finally, if this program is being run from the command line, we set up all 120 # the previously mentioned objects and start rendering. The program will expect 121 # two command line parameters: the name of an HDF5 input file, and the name of 122 # an HDF5 output file. An optional third parameter specifies the size of the 123 # resulting volume. 124 if __name__ == "__main__": 125 # We need to read a mesh filename from <code>sys.argv</code>, so import 126 # <code>sys</code>. 127 import sys 128 129 # We assume the mesh is stored in a <a 130 # href="http://www.hdfgroup.org/HDF5/">HDF5</a> file, so import <a 131 # href="h5py.alfven.org"><code>h5py</code></a>. 132 import h5py 133 134 # We open the HDF5 file specified on the command line for reading, extract 135 # the vertices and indices, and store them in a vertex array: 136 with h5py.File(sys.argv[1]) as f: 137 mesh = VertexArray(f["vertices"], elements=f["indices"]) 138 139 # Now the voxelization is performed and the result is returned as an array 140 # of 2D integer textures. 141 volume = voxelize(mesh, int(sys.argv[3]) if len(sys.argv) > 3 else 128) 142 143 # Finally, we open the output file, download the data, and store it in a 144 # dataset. The dataset can typically be compressed a lot, which is why we 145 # enable LZF compression. However, this may take a long time, so you can 146 # disable it if you are not short on disk space. 147 with h5py.File(sys.argv[2], "w") as f: 148 f.create_dataset("data", data=volume.data, compression="lzf") 149 150 # Note how we did not need to create an OpenGL context explicitly. The 151 # first object that needs an OpenGL context (in this case the 152 # <code>VertexArray</code>) will create an invisible context as soon as it 153 # is initialized. This is very convenient; on the other hand, it means that 154 # if you create a context manually (e.g., because you need a visible 155 # window), you have to initialize it before any other objects. Otherwise, 156 # these objects may end up in the wrong context, and sharing data across 157 # context is a problem of its own. 158
Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Fri Mar 16 17:56:07 2012 | http://epydoc.sourceforge.net |