Repository navigation
Textures
render/texture.mojo and render/texture_store.mojo. A Texture is an RGBA image with a wrap mode, a filter, a color space, an alpha mode and an optional mip chain. A material names one by id.

three.js: Texture, DataTexture, DepthTexture, WebGLRenderTarget.texture, wrapS, wrapT, magFilter, minFilter, generateMipmaps, colorSpace.
checkerboard(size, squares, first, second, wrap=REPEAT, filter=BILINEAR, mipmapped=True, alpha=COVERAGE)
texture_from(image, wrap=REPEAT, filter=BILINEAR, color_space=None, mipmapped=True, alpha=COVERAGE)
Texture(width, height, pixels, wrap=REPEAT, filter=BILINEAR, color_space=SRGB, mipmapped=True, alpha=COVERAGE)
data_texture(width, height, numbers, channels=4, wrap=CLAMP, filter=NEAREST, mipmapped=False, alpha=COVERAGE)
texture_of(framebuffer, wrap=CLAMP, filter=BILINEAR, mipmapped=True, alpha=COVERAGE)
depth_texture_of(framebuffer, wrap=CLAMP)checkerboard builds a test pattern. texture_from takes a DecodedImage from the PNG reader. Texture takes row-major RGBA bytes from the top. The last three are below.
data_texture is three.js's DataTexture: a texture from raw numbers rather than from an image file. Each number is a fraction from zero to one and becomes one byte, quantized without any transfer function. The texture is LINEAR, so the byte samples back as the fraction it was. A number outside zero to one is clamped.
var ramp = data_texture(3, 1, [0.35, 0.7, 1.0], channels=1, alpha=IGNORED)
var cel = toon_material(Color(255, 170, 60), gradient_map=assets.textures.add(ramp^))channels |
three.js | Each texel holds |
|---|---|---|
1 |
RedFormat |
Red. Green and blue are zero, alpha is one. |
2 |
RGFormat |
Red and green. Blue is zero, alpha is one. |
3 |
RGBFormat |
Red, green and blue. Alpha is one. |
4 |
RGBAFormat |
All four. The default. |
The numbers fill the channels in order, as WebGL samples these formats. A one-channel texture is red, not gray. A toon ramp reads red alone, so one channel serves it. A gray image needs three equal numbers a texel.
The defaults are three.js's own for a DataTexture: nearest, no mip chain, and the edges clamped. Data is read where it was written. The length must be width * height * channels, and every number must be finite.
texture_of takes a Framebuffer and returns the texture that holds it: three.js's WebGLRenderTarget.texture. A framebuffer holds eight-bit sRGB with unassociated alpha, which is what a color texture holds, so nothing is converted. RenderTarget.texture() resolves a target and does the same in one call. A later draw can then sample what an earlier one drew.
var target = RenderTarget(160, 120, Color(30, 60, 90))
renderer.render_into(target, stage, props, studio_camera)
var picture = assets.textures.add(target.texture())
var screen = Material(Color(255, 255, 255), picture, kind=BASIC)The texture is a copy, not a view. Drawing into the target again changes nothing the texture holds. The edges are clamped by default, as three.js clamps a render target's texture.
It is a snapshot in bytes. Light above one was clamped or tone mapped when the image was resolved, and a later exposure cannot bring it back. That suits a picture on a screen in the scene. A linear texture that keeps a render's range for a later pass is a different thing, and this is not it.
depth_texture_of takes the depth the framebuffer carries and returns it as a texture: a preview of three.js's DepthTexture, at eight bits. Each texel is the window-space depth a GPU stores, as one gray byte in every channel. It is zero at the near plane and one at the far plane. A pixel nothing was drawn into is at the far plane. The texture is LINEAR, ignores its alpha, and is read nearest with no chain: two depths averaged are the depth of nothing.
RenderTarget.depth_texture() does the same for a target as it stands. depth_texture_of_buffer takes a bare depth buffer.
It is a picture of the depth, not the depth. A byte holds 256 steps, and a perspective projection spends most of them near the near plane. Take planes at a tenth of a meter and a hundred. A surface one meter away is byte 230, ten meters is 253, and forty meters is 255, the far plane's own byte. three.js's DepthTexture holds a real depth format. Use this to look at a depth, not to compare or reconstruct one.
examples/television.mojo renders a box into a small target every frame and shows its picture and its depth on two screens.
| Value | A coordinate of 1.5 reads |
|---|---|
REPEAT |
The same texel as 0.5. |
CLAMP |
The edge texel. |
MIRROR |
The image reflected, so tiles meet without a seam. |
Under REPEAT, coordinates 0 and 1 name the same texel. Under CLAMP they name opposite edges.
| Value | Meaning |
|---|---|
NEAREST |
The texel the sample lands in. Hard edges. |
BILINEAR |
The four nearest texels, blended by distance. |
mipmapped=True builds a chain of halved copies down to one texel. Sampling then picks the level whose texels match the pixel footprint, and blends between the two nearest levels. This stops a distant surface from shimmering.
The chain is built by default, as three.js's Texture sets generateMipmaps and LinearMipmapLinearFilter, and the filter is bilinear by default, as three.js's LinearFilter is. Pass mipmapped=False or NEAREST to turn either off.
The chain costs a third more memory. It is built in premultiplied linear light, unless the texture ignores its alpha.
| Value | Meaning |
|---|---|
SRGB |
An image. Bytes are decoded to linear light when read. The default. |
LINEAR |
Data. Bytes are used as stored. |
UNKNOWN_SPACE |
A decoder's answer for a file it cannot interpret. A texture refuses it. |
Alpha is never decoded. It is coverage, not color.
| Value | Meaning |
|---|---|
COVERAGE |
Alpha hides color. Filtering and the mip chain weight each texel by it, and a sample carries it. The default. |
IGNORED |
Alpha is not read. Every alpha byte counts as 255. Color is filtered as it is, and every sample is opaque. |
An emissive map must ignore its alpha. Its alpha is not coverage. Filtered as coverage, a white texel with alpha zero turns black under BILINEAR, and the whole mip chain darkens with it. The renderer refuses an emissive map built with COVERAGE.
An alpha map must ignore its alpha too, and must be LINEAR. Its green channel is a coverage rather than a color. See Materials.
ignoring_alpha() copies a texture into the other mode and rebuilds its mip chain from the full-size image. Use it when one image is both a base map and an emissive map. The blank texture's copy is blank and ignores its alpha, so it passes as an emissive map and samples as white.
u runs from left to right and v from bottom to top. Rows in memory run from the top. Sampling flips once, as three.js's flipY does.
A texture can move, tile and turn on a surface. three.js: offset, repeat, rotation, center, matrix.
| Field | Default | Meaning |
|---|---|---|
offset |
Vector2(0, 0) |
How far the coordinates move, after the rest. |
repeat |
Vector2(1, 1) |
How many times the texture fits across each axis. |
rotation |
zero | How far the image turns, counter-clockwise. An Angle. |
center |
Vector2(0, 0) |
The point the turn and the scale are about. |
Set the fields after construction, as in three.js. uv_transform() returns the matrix they make. See Math for its order.
The renderer carries every coordinate of a mesh through its map's matrix before the fragment samples with it. The texture itself does not change. The wrap mode still decides what a coordinate past the edge reads. Both rasterizers get the same coordinates.
A fragment samples the map, the emissive map and the alpha map at one coordinate. A material that names more than one must give them all the same transform. The renderer refuses them otherwise. ignoring_alpha() copies the transform, so two maps from one image agree.
var board = checkerboard(64, 8, white, blue)
board.repeat = Vector2(4, 4)
board.rotation = Angle(45.0, DEGREE)
board.center = Vector2(0.5, 0.5)
var id = assets.textures.add(board^)| Member | Meaning |
|---|---|
data_texture(width, height, numbers, channels) |
A texture from fractions. See From numbers. |
texture_of(framebuffer), depth_texture_of(framebuffer)
|
A texture from a render, and from its depth. See From a render. |
sample(u, v) -> FloatColor |
The color at a coordinate, level zero. |
sample_level(u, v, level) -> FloatColor |
Trilinear, between two mip levels. |
texel(x, y) -> Color |
One stored texel. |
is_blank() -> Bool |
The blank texture, which samples as opaque white. |
ignoring_alpha() -> Texture |
A copy that ignores its alpha, with its chain rebuilt. |
uv_transform() -> Matrix3 |
The transform on the coordinates, from the four fields above. |
validate() |
Refuse a wrap, filter, color space or alpha mode that is none of the named values. |
levels, width, height, alpha
|
The chain length, the base size and the alpha mode. |
offset, repeat, rotation, center
|
The transform's fields. |
assets.textures.add(texture) returns a TextureId. get(id) borrows it. NO_TEXTURE is the id of no texture, and samples as white.
- Dimensions must be positive, and the buffer length must match.
- A wrap, filter, color space or alpha mode that is none of its named values raises. The GPU upload checks again.
- A
checkerboardsize must divide evenly by its square count. - A
data_texturewith a channel count outside one through four, a length that does not match, or a number that is not finite. - The renderer refuses a map and an emissive map on one material whose transforms differ.
See Why mipmaps and Why color is linear.
Tutorials
How-to
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- C ABI and hosts
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Streamline
- Animals
- Buildings
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Segment inertia
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- CUDA teardown hang
- Mojo compiler hang