materials
Materials can be applied to entities that use primitive shapes (cube, sphere, plane, etc) by setting them as a component.
You can either set a Material
or a BasicMaterial
component. Each entity can only have one of these. Both components have several fields that allow you to configure the properties of the material, add a texture and set the texture's mapping.
You can't add material components to glTF models. glTF models include their own materials that are implicitly imported into a scene together with the model.
When importing a 3D model with its own materials, keep in mind that not all shaders are supported by the Andverse engine. Only standard materials and PBR (physically based rendering) materials are supported. See [external 3D model considerations] for more details.
Create and apply a material
The following example creates a material, sets some of its fields to give it a red color and metallic properties, and then applies the material to an entity that also has a boxShape
component.
//Create entity and assign shape
const myEntity = new Entity()
myEntity.addComponent(new BoxShape())
//Create material and configure its fields
const myMaterial = new Material()
myMaterial.albedoColor = Color3.Blue()
myMaterial.metallic = 0.9
myMaterial.roughness = 0.1
//Assign the material to the entity
myEntity.addComponent(myMaterial)
See component reference) for a full list of all the fields that can be configured in a Material
of BasicMaterial
component.
Basic materials
Instead of the Material
component, you can define a material through the BasicMaterial
entity. This creates materials that are shadeless and are not affected by light. This is useful for creating user interfaces that should be consistently bright, it can also be used to give your scene a more minimalist look.
const myMaterial = new BasicMaterial()
Note: Basic materials have some property names that are different from those in normal materials. For example it uses
texture
instead ofalbedoTexture
.
Material colors
Give a material a plain color. In a BasicMaterial
component, you set the color
field. In a Material
component, you set the albedoColor
field. Albedo colors respond to light and can include shades on them.
All color fields are either of type Color3
or Color4
. Color3
holds three values, for Red, Green and Blue. Each of these numbers is between 0 and 1. Color4
holds those same three values and a fourth value for Alpha, also between 0 and 1, where 0 is completely transparent and 1 is completely opaque.
myMaterial.albedoColor = new Color3(0.5, 0, 0.5)
Note: If you set any color in
albedoColor
to a value higher than 1, it will appear as emissive, with more intensity the higher the value. So for example,new Color3(15, 0, 0)
produces a very bright red glowing color.
You can also pick predetermined colors using the following functions of the Color3
object:
let red = Color3.Red()
let green = Color3.Green()
let blue = Color3.Blue()
let black = Color3.Black()
let white = Color3.White()
let purple = Color3.Purple()
let magenta = Color3.Magenta()
let yellow = Color3.Yellow()
let gray = Color3.Gray()
let teal = Color3.Teal()
You can otherwise pick a random color using the following function:
// Pick a random color
let green = Color3.Random()
If you prefer to specify a color using hexadecimal values, as is often done in JavaScript web development, you can do so using the .FromHexString()
function
let gray = Color3.FromHexString("#CCCCCCC")
The Color3
object also includes a lot of other functions to add, substract, compare, lerp, or convert the format of colors.
You can also edit the following fields in a Material
component to fine-tune how its color is percieved:
- emissiveColor: The color emitted from the material.
- ambientColor: AKA Diffuse Color in other nomenclature.
- reflectionColor: The color reflected from the material.
- reflectivityColor: AKA Specular Color in other nomenclature.
Change a color gradually
Change a color gradually with linear interpolation between two colors, using the .Lerp()
function.
// This variable will store the ratio between both colors
let colorRatio = 0
// Define colors
const red = Color3.Red()
const yellow = Color3.Yellow()
// Create material
const myMaterial = new Material()
// This system changes the value of colorRatio every frame, and sets a new color on the material
export class ColorSystem implements ISystem {
update(dt: number) {
myMaterial.albedoColor = Color3.Lerp(red, yellow, colorRatio)
if (colorRatio < 1) {
colorRatio += 0.01
}
}
}
// Add the system to the engine
engine.addSystem(ColorSystem)
The example above changes the color of a material from red to yellow, incrementally shifting it on every frame.
Using textures
Reference an image file as a texture by creating a Texture
component. You can then reference this texture component in the fields of both Material
and BasicMaterial
components.
In a Material
component, you can set the albedoTexture
field to a texture image. Albedo textures respond to light and can include shades on them.
//Create entity and assign shape
const myEntity = new Entity()
myEntity.addComponent(new BoxShape())
//Create texture
const myTexture = new Texture("materials/wood.png")
//Create a material
const myMaterial = new Material()
myMaterial.albedoTexture = myTexture
//Assign the material to the entity
myEntity.addComponent(myMaterial)
While creating a texture, you can also pass additional parameters:
samplingMode
: Determines how pixels in the texture are stretched or compressed when renderedwrap
: Determines how a texture is tiled onto an object (see Texture Wrapping)
let smokeTexture = new Texture("textures/smoke-puff3.png", {
wrap: 0,
})
Textures from an external URL
You can point the texture of your material to an external URL instead of an internal path in the scene project.
const myTexture = new Texture(
"https://wearable-api.andverse.org/v2/collections/community_contest/wearables/cw_tuxedo_tshirt_upper_body/thumbnail"
)
const myMaterial = new Material()
myMaterial.albedoTexture = myTexture
The URL must start with https
, http
URLs aren't supported. The site where the image is hosted should also have CORS policies (Cross Origin Resource Sharing) that permit externally accessing it.
Textures on basic materials
In a BasicMaterial
component, you can set the texture
field to an image texture. This will render a texture that isn't affected by lighting.
//Create entity and assign shape
const myEntity = new Entity()
myEntity.addComponent(new BoxShape())
//Create texture
const myTexture = new Texture("materials/wood.png")
//Create material and configure its fields
const myMaterial = new BasicMaterial()
myMaterial.texture = myTexture
//Assign the material to the entity
myEntity.addComponent(myMaterial)
Multi-layered textures
It also allows you to use several image files as layers to compose more realistic textures, for example including a bumpTexture
and a refractionTexture
.
//Create entity and assign shape
const myEntity = new Entity()
myEntity.addComponent(new BoxShape())
//Create texture
const myTexture = new Texture("materials/wood.png")
//Create second texture
const myBumpTexture = new Texture("materials/woodBump.png")
//Create material and configure its fields
const myMaterial = new Material()
myMaterial.albedoTexture = myTexture
myMaterial.bumpTexture = myBumpTexture
//Assign the material to the entity
myEntity.addComponent(myMaterial)
In the example above, the image for the material is located in a materials
folder, which is located at root level of the scene project folder.
Tip: We recommend keeping your texture image files separate in a
/materials
folder inside your scene.
Tip: A material can have multiple layers of texture, you can see what these are on a source code editor by clicking
.
and letting the autocomplete menu show you the list.
Texture wrapping
If you want the texture to be mapped to specific scale or alignment on your entities, then you need to configure uv properties on the [shape components].
You set u and v coordinates on the 2D image of the texture to correspond to the vertices of the shape. The more vertices the entity has, the more uv coordinates need to be defined on the texture, a plane for example needs to have 8 uv points defined, 4 for each of its two faces.
//Create material and configure fields
const myMaterial = new BasicMaterial()
let myTexture = new Texture("materials/atlas.png", { wrap: 1, samplingMode: 0 })
myMaterial.texture = myTexture
//Create shape component
const plane = new PlaneShape()
// map the texture to each of the four corners of the plane
plane.uvs = [
0, 0.75,
0.25, 0.75,
0.25, 1,
0, 1,
0, 0.75,
0.25, 0.75,
0.25, 1,
0, 1,
]
//Create entity and assign shape and material
const myEntity = new Entity()
myEntity.addComponent(plane)
myEntity.addComponent(myMaterial)
The following example includes a function that simplifies the setting of uvs. The setUVs
function defined here receives a number of rows and columns as parameters, and sets the uvs so that the texture image is repeated a specific number of times.
const myMaterial = new BasicMaterial()
let myTexture = new Texture("materials/atlas.png", { wrap: 1, samplingMode: 0})
myMaterial.texture = myTexture
const myPlane = new Entity()
const plane = new PlaneShape()
myPlane.addComponent(plane)
engine.addEntity(myPlane)
myPlane.addComponent(myMaterial)
plane.uvs = setUVs(3, 3)
function setUVs(rows: number, cols: number) {
return [
// North side of unrortated plane
0, //lower-left corner
0,
cols, //lower-right corner
0,
cols, //upper-right corner
rows,
0, //upper left-corner
rows,
// South side of unrortated plane
cols, // lower-right corner
0,
0, // lower-left corner
0,
0, // upper-left corner
rows,
cols, // upper-right corner
rows,
]
}
For setting the UVs for a BoxShape
component, the same structure applies. Each of the 6 faces of the cube takes 4 values, one for each corner. All of these 24 values are listed as a single array.
You can also define how the texture is tiled if the mapping spans more than the dimensions of the texture image. The Texture
component lets you configure the wrapping mode by setting the wrap
field. The wrapping mode can be CLAMP
, WRAP
or MIRROR
.
CLAMP
: The texture is only displayed once in the specified size. The rest of the surface of the mesh is left transparent.WRAP
: The texture is repeated as many times as it fits in the mesh, using the specified size.MIRROR
: As in wrap, the texture is repeated as many times as it fits, but the orientation of these repetitions is mirrored.
Note: The
wrap
property must be set when instancing the texture, after that it's a read-only property.
let myTexture = new Texture("materials/atlas.png", { wrap: 2 })
The example above sets the wrapping mode to MIRROR
.
Note: Uv properties are currently only available on
PlaneShape
and onBoxShape
components.
Texture scaling
When textures are stretched or shrinked to a different size from the original texture image, this can sometimes create artifacts. In a 3D environment, the effects of perspective cause this naturally. There are various texture filtering algorithms that exist to compensate for this in different ways. The Texture
object uses the bilinear algorithm by default, but it lets you configure it to use the nearest neighbor or trilinear algorithms instead by setting the samplingMode
property.
const myTexture = new Texture("materials/myTexture.png", { samplingMode: 1 })
The example above uses a nearest neighbor algorithm. This setting is ideal for pixel art style graphics, as the contours will remain sharply marked as the texture is seen larger on screen instead of being blurred.
Avatar Portraits
To display a thumbnail image of any player, create an AvatarTexture
, passing the address of an existing player. This creates a texture from a 256x256 image of the player, showing head and shoulders. The player is displayed wearing the set of wearables that the current server last recorded.
let myTexture = new AvatarTexture("0xAAAAAAAAAAAAAAAAA")
const myMaterial = new Material()
myMaterial.albedoTexture = myTexture
Transparent materials
To make a material with a plain color transparent, simply define the color as a Color4
, and set the 4th value to something between 0 and 1. The closer to 1, the more opaque it will be.
let transparentRed = Color4(1, 0, 0, 0.5)
To make a material with texture transparent:
- Set an image in
alphaTexture
.
Note: This must be a single-channel image. In this image use the color red to determine what parts of the real texture should be transparent.
- Optionally set the
transparencyMode
to: -OPAQUE
: No transparency at all -ALPHATEST
: Each pixel is either completely opaque or completely transparent, based on a threshold. -ALPHABLEND
: Intermediate values are possible based on the value of each pixel.
- If you set the
transparencyMode
toALPHATEST
, you can fine tune the threshold used to determine if each pixel is transparent or not. Set thealphaTest
property between 0 and 1. By default its value is 0.5.
const myTexture = new Texture("materials/texture.png")
const alphaTexture = new Texture("materials/alpha.png")
// Material with ALPHABLEND
const myMaterial = new Material()
myMaterial.albedoTexture = myTexture
myMaterial.alphaTexture = alphaTexture
// Material with ALPHATEST
const myMaterial2 = new Material()
myMaterial2.albedoTexture = myTexture
myMaterial2.alphaTexture = alphaTexture
myMaterial.transparencyMode = 1 // ALPHATEST
myMaterial.alphaTest = 0.3
Casting no shadows
To prevent a material from casting shadows over other objects, both Material
and BasicMaterial
have a castShadows
property that you can set to false. This property is always true by default.
let noShadowMaterial = new Material()
noShadowMaterial.albedoColor = Color4.White()
noShadowMaterial.castShadows = false
Reuse materials
If multiple entities in your scene use a same material, there's no need to create an instance of the material component for each. All entities can share one same instance, this keeps your scene lighter to load and prevents you from exceeding the maximum amount of materials per scene.
//Create entities and assign shapes
const box = new BoxShape()
const myEntity = new Entity()
myEntity.addComponent(box)
const mySecondEntity = new Entity()
mySecondEntity.addComponent(box)
const myThirdEntity = new Entity()
myThirdEntity.addComponent(box)
//Create material and configure fields
const myMaterial = new Material()
myMaterial.albedoColor = Color3.Blue()
//Assign same material to all entities
myEntity.addComponent(myMaterial)
mySecondEntity.addComponent(myMaterial)
myThirdEntity.addComponent(myMaterial)
Video playing
To stream video from a URL into a material, or play a video from a file stored in the scene, see [video playing].
The video is used as a texture on a material, you can set any of the other properties of materials to alter how the video screen looks.