Skip to content

Texture Objects and Texture Intrinsics

Stefan Zellmann edited this page Oct 24, 2016 · 3 revisions

Content of this page: Texture object configuration and texture access via built-in intrinsics.

Note that the current version of Visionaray is an early preview. At this stage, the framework, including the API, are likely to undergo frequent changes.

Visionaray implements texture mapping routines that are accessible from within user written kernels. Visionaray's texturing utilities build upon the framework presented in Zel15. The interface of Visionaray's texturing utilities is inspired by the way the NVIDIA CUDA toolkit provides access to textures. In addition to exposing the hardware texturing capabilities of GPGPUs, textures can also be accessed with texture coordinates stored in SIMD vectors. Visionaray also supports 3rd order texture filtering with B-Splines or Cardinal Splines.

Texture Objects

Texture objects are template classes that provide storage for the texture image data and store information on how textures are accessed and reconstructed. Visionaray supports 1-D, 2-D, and 3-D textures that store image data.

CPU textures

The following listing shows how to allocate storage for a 2-D image texture that is hosted on the CPU and that stores fixed point unsigned normalized float texels (values in the range [0.0..1.0]) with 8-bit precision.

typedef texture<unorm<8>, 2> texture_t;

texture_t tex(256, 256);     // Alloc storage for 256x256 pixels.
tex.reset( some_array );     // Deep copy image data to texture.
tex.set_filter_mode(Linear); // Linear interpolation.
                             // Choose from [Nearest|Linear|BSpline|CSpline].
tex.set_address_mode(Clamp); // Handle out-of-range texture coordinates.

The texture allocated above provides storage for an image with 256 x 256 pixels, where each pixel stores a single color channel (e.g. for luminance or depth) with 8-bit precision. The first template parameter determines the data storage type and the second template parameter determines the dimensions of the texture. By setting up the texture with the special type unorm<8>, reading from the texture will yield values in the interval [0.0..1.0] that are stored with 8-bit precision.

When accessing the texture, reconstruction with bilinear interpolation will be performed. The texture address mode determines how out-of-range texture accesses are handled (i.e. when accessing the texture with a (normalized) texture coordinate that is lower than 0 or greater than 1). In this case, texture coordinates will be clamped to the interval [0.0..1.0]. Alternative texture address modes are Wrap and Mirror, which wrap or mirror the texture, respectively.

Texture references

Texture references provide the same interface that texture objects expose, but only store pointers rather than maintaining actual texture storage. On the CPU, this may come in handy if the texture data is e.g. already present in some array and it is not desired to duplicate the texture image data.

NOTE: When the user maintains the image data and only interfaces it through a texture reference, certain optimizations such as reordering texels for better cache coherence cannot be performed, which may have a negative impact on rendering performance.

The following listing shows how to setup a reference to an array with 3-D (stacked) image data maintained by the user.

typedef vector<4, float> RGBA;
typedef texture_ref<RGBA, 3> texture_t;

RGBA data[64 * 64 * 64];         // Array with texture data.

texture_t volume(64, 64, 64);    // No storage allocated here.
volume.reset( data );            // Now just store a pointer.
volume.set_filter_mode(Nearest); // Just like before.
volume.set_address_mode(Mirror);

CUDA textures

We are still working on this, so please check back soon.

Texture Access

Texture access from within a user written kernel is performed using the texturing intrinsics tex1D(), tex2D(), and tex3D() for 1-D, 2-D, and 3-D texture access, respectively. The intrinsics are called with a texture object or texture reference as first parameter, and with a 1-D, 2-D, or 3-D texture coordinate, depending on the dimensionality of the texture. Texture coordinates range from 0.0 to 1.0 in each dimension. Handling of out-of-range texture coordinates is determined by the value of the tex_address_mode parameter of the texture. It is also possible to access the texture with a SIMD SoA texture coordinate storing N independent coordinates. The texturing intrinsics will then return a SoA vector containing N independent reconstructed values from the texture. The following listing demonstrates those concepts.

// Some typedefs ------------------------------------------

typedef texture<float, 1> texture1D_t;
typedef texture<float, 2> texture2D_t;
typedef texture<float, 3> texture3D_t;


// Setup a 1-D texture ------------------------------------

texture1D_t t1(256);
t1.set_filter_mode(Nearest);
t1.set_address_mode(Wrap);


// access w/ floating point coordinate.
auto val = tex1D(t1, 0.5f);

// access w/ an SSE coordinate yields four values.
using simd::float4;
float4 val4 = tex1D(t1, float4(0.5f, 0.4f, 0.3f, 0.2f));


// Setup a 2-D texture ------------------------------------

texture2D_t t2(32, 32);
...

// access w/ floating point coordinate.
auto val2 = tex2D(t2, vec2(-0.5f, 0.5f));

// acccess w/ an SSE coordinate.
float4 val2_4 = tex2D(
        t2,
        vector<2, float4>(0.5f, -0.5f, 0.0f, 1.0f)
        );