Creating textures with raw data in threejs
Data textures are a way to go about creating textures in threejs than can then be used for one of the various map options for materials. When it comes to using data textures as a way to create textures with javaScrript code in threejs I just need to know how to produce the texture that I want in terms of a Unit8Array with a set of four values, one for each color channel and a single alpha transparency channel. That is that I need to create an array with integer values between and including the range of 0 to 255 for red, green, blue and alpha for each pixel. Once I have that I can just pass that array, along with a width and height value to the THREE.DataTexture constructor function and the returned result will be a texture that I can then use for the various maps of a material such as the standard material that in turn can be used with a geometry to skin a mesh object.
There is then maybe also a bit more to write about with data textures when it comes to how to go about updating them, after that I think that I will end up started to branch off into other closely related subjects.
Data textures in threejs and what to know first
When working with textures there is a lot to be aware of with geometry
The main focus in this post is to just simply create textures using raw color channel data, and then use the resulting texture with say a map property of a material. There is then a lot more to read about when it comes to the various options with material maps, but also just as much if not more when it comes to the buffer geometry class. I will then not be getting into great detail with things like the uv attribute of buffer geometry, as well as how to go about setting up a groups property along with material index values when using an array of materials.
I have wrote a number of posts on the use of canvas elements, and also a post on using canvas elements as a way to create textures for the materials that are used for mesh objects in threejs.
version numbers matter
The version of threejs that I was using when I first wrote this post was r135, and the last time I came around to do some editing I was using r140. Still code breaking changes might be made at some point in the future so one should always be mindful of the version of threejs that they are using when reading about and using threejs source code examples on the open web.
The source code in this post is up on Github
The source code examples that I am writing about in this post can be found in my test threejs repository on Github.
1 - Basic examples of data textures
To start this post off I will want to cover a few basic examples of data textures. I will be trying to keep these examples fairly simple but even then there is a lot to be aware of when it comes to making textures this way.
1.1 - A basic data texture example
For a starting simple example of this data texture thing in threejs I made this quick example that involves starting at a value of 32 for the red channel and adding 128 over the length of the total number of pixels for the image. I am then also doing something similar for the green channel just subtracting rather than adding.
I start out by making my usual scene object along with a camera and web gl renderer just like with any other threejs project. After that I will want to create an instance of a unit8Array where the length is equal to the number of pixels times four. So I just need to figure out that the width and height of the texture should be and then multiply that to get a size in terms of a total number of pixels. I can then use this size value times four to set the length of the typed array, and also use it as a way to know if I should stop looping or not when it comes to setting the values for this array.
I then have a loop in which I am figuring out what the values should be for each red, green, blue, and alpha channel value for each pixel. I can have an index for each pixel and then just figure out what the actual index value in the array is by just multiplying by four and then adding fro there for each channel value. Once I have my array in the state that I want it for the texture the next step is to then pass that array as an argument when calling the THREE.DataTexture constructor function.
1.2 - Distance to method of the Vector2 class
For this example I am doing more or less the same thing as in the basic example, but now I am using the distance to method of the Vector2 class as a way to get a distance value from a current pixel location to that of the center of the texture. I can then use this as a main to come up with different color channel values for each pixel in the texture.
1.3 - Using the math random method
Now for a quick example using the math random method to create color channel values. There are of course a great number of ways that I could go about doing this sort of thing, but for this example I just went with creating a single value that will be used for each color channel. So in other words I am doing a kind of random gray scale color effect here.
1.4 - The seeded random method of the Math Utils object
I just also wrote a new post on the math utils object in threejs, and one interesting method in there is a seeded random method that will work like math random, with one little difference. Each time I reload the page I see the same texture rather than a new one. So then this seeded random method is a way to get an effect like that of what happens when using the math random method, but in a deterministic kind of way which is cool.
2 - Animation examples
Now that I have some basic examples out of the way it is time to go over at least one if not more examples of animated data textures. This is where things can get a little involved and confusing when first starting out. Also I have found that just with buffer geometry it is a good idea to think more in terms of updating a texture rather than creating a new one on each frame update. By reusing the same texture objects over and over agian, but just updating the data from them, this not only results in better performance but I have also run into problems with a loss of context when going in the direction of creating new textures objects over and over again.
2.1 – For Pixel helper function, and getting started with animated data textures
Thus far all of these data texture examples are just blocks of code that create a single texture. When it comes to making a real project with data textures I will likely want to make a number of them, and do so in different ways. To keep myself from repeating the same code over and over again each time I want to make a data texture I can then make some kind of helper function and pass some values that will result in the kind of texture that I want. When doing so the first feature that comes to mind that I would want to have in this kind of helper function would be an option that I can use to define the logic that is used to create the texture. In other words a kind of for pixel function that would have some kind of hard coded value.
So when it comes to the helper functions of this example I have a create data helper function that will just create and return an array that I can in turn use to create a new data texture, or update an existing one. With that said I then have both a create data texture helper as well as an update data texture helper as well. When it comes to this create data texture helper I have a built in for pix method, but I also created a number of additional functions that can be used to create and return such a method.
In my animation loop I am then using the update texture method passing option objects with custom for pixel methods which results in the kind of outcome that I would like so far with this sort of thing. I am sue that I might still need to refine things a bit with what I have here but the core idea is there for sure I mainly just need to expand the collection of methods that are used ti create the texture data.
The use of data textures to create textures for threejs geometries can then prove to be a little useful here and there then. However I am not sure if this is what I will want to always use, even when it comes to this sort of thing. For the most part I do still like to use canvas elements to create textures as there are a lot of useful methods to work with in the 2d drawing context.
When it comes to really working out modules I, and also uv wrapping while doing so I sometimes think the best way to go would be external image files when it comes to working with dae files for a project.