Nicolas
Nicolas

Reputation: 2281

pass array data to shader in threeJS

I am trying to pass "volume._data" to my fragment shader in threeJS. It is a Float32Array and it can contain up to: 512x512x512x4 elements.

  var mat = new THREE.ShaderMaterial({
        uniforms: {
            color: {type: 'f', value: 1.0},
            ijk: {type: '??', value: volume._data},
            dimensions: {type: 'vec3', value: volume._dimensions},
            ijktoras: {type: 'mat4', value: volume._RASToIJK}
        },
        vertexShader: document.
                      getElementById('vertShader').text,
        fragmentShader: document.
                      getElementById('fragShader').text
    });

The problem is that I do not know which type to give it. Or is the best option to pass a 2D texture?

My concern with the texture is that it will be too big to be passed to the shader.

Is there any workaround to pass this amount of information to the shader?

My overall goal is, in the fragment shader, to map the screen pixel position to the object world's coordinate. And then map this world coordinate to my volume._data (ijk space) to display on the screen a specific color.

Thank you, Nicolas

Upvotes: 2

Views: 6461

Answers (2)

Brandon.Blanchard
Brandon.Blanchard

Reputation: 404

The answer Flux provided is correct. For those of you here trying to put together a volumetric shader the following may be useful.

You can use a one dimensional array to represent 3d space and retrieve the correct array item via it's x,y,z coords using the following method.

x,y,z coords should be in local space (rather than world) with the origin at 0,0,0. You'll need to know the width and height of each 2d section.

Assuming a voxel volume of 3x3x3

uniform float[] Voxels;
uniform int Width = 3;
uniform int Height = 3;

float getValueAtVoxel(x,y,z) {
    int voxel = (Width * Height * z) + (y * Width) + x;

    if (voxel < 0 || voxel >= Voxels.length) {
        return 0.0;
    }

    return Voxels[voxel];
}

Upvotes: 1

Flux
Flux

Reputation: 666

Check out https://github.com/mrdoob/three.js/wiki/Uniforms-types for arrays, such as:

"uFloatArray3" : { type: "fv",  value: [ 0.1, 0.2, 0.3, 0.4, 0.5, 0.6 ] }, // float array (vec3)

or

"uVec4Array" : { type: "v4v", value: [ new THREE.Vector4( 0.1, 0.2, 0.3, 0.4 ), 
                                   new THREE.Vector4( 0.4, 0.5, 0.6, 0.7 ) ] }, // Vector4 array

You can always just pack that data into a straight array and use a delimiter.

As far as what you are doing, it's best to pass in 3 textures, each 512x512 and access RGBA for your 4 values. It will not be too slow, don't worry too much about optimization until you try it.

This ofcourse depends on how you fill the data in your buffer. The faster way to do it is through a canvas using the pixels array as described here: https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/

Upvotes: 2

Related Questions