Specifying Texture Coordinates
Normally you read the pixel value of your first texture by simply referencing env.CHANNEL_0
, and you are done.
In Indigo proper, CHANNEL_0
is carefully constructed to ensure that the texture coordinate read adheres to the fill type specified in Indigo, based on the UV coordinates passed to the fragment shader from the vertex shader.
There are times, though, when you want to be able to control the texture coordinates directly. Maybe you only want to render a sub region of a texture, for example. Let's explore an example.
Example Links
Stretching a texture to fill the entity.
In this example we're going to mimic Indigo's 'stretch' FillType
, to tell the shader to
stretch the texture to fill the entity (or in our case, the available space), by telling the
shader how to map the entities UV's onto the texture.
Fundamentally, we're just going to look at using the texture2D
function in a shader, and if
you are using Ultraviolet outside of Indigo, that's all you really need.
Since we are in Indigo though, things are slightly more complex because we are required to know a bit about how Indigo works with textures.
How Indigo stores texture information
Briefly: Indigo stores textures on a texture atlas. Which means that many texture are all stored in the same image / texture buffer, which can then be accessed repeatedly without requiring a context change by the graphics card.
Indigo can have many atlases, which are limited to 4096x4096 pixels in size, but in pixel art terms that's a lot of space! It's common for whole games to easily fit in one atlas.
What does that mean in practice? Well it means that when you want to make use of a texture in
the SRC_CHANNEL
- the enourmous texture atlas - you need to know where in that atlas your
texture is, and how big it is. For channel 0, those values are CHANNEL_0_POSITION
and
CHANNEL_0_SIZE
, respectively.
Using texture2D
Trivia: The functiontexture2D()
comes from WebGL 1.0, in WebGL 2.0 it's just calledtexture()
, but it was convenient to keep the old terminology to help disambiguate terms, and Ultraviolet translates it to the right name for you based on the compilation target.
As usual, we need to first assign a texture to a channel.
val channel0: Option[AssetPath] = Option(AssetPath("assets/fire-background.png"))
Now we can render our stretched texture.
First we need to calculate the stretched UV coordinates. We do this by taking the entity's UV value passed over by the vertex shader; multiply it by the texture size so that we have translated from UV space (0.0 to 1.0) to pixels; then add the position of the texture in the atlas to move to the correct location.
val coords = env.CHANNEL_0_POSITION + (env.UV * env.CHANNEL_0_SIZE)
Finally, we can sample the texture at the calculated coordinates. texture2D
returns a
vec4
of the color of the texture at the given coordinates.
texture2D(env.SRC_CHANNEL, coords)