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 Decentraland engine. Only standard materials and PBR (physically based rendering) materials are supported. See external 3D model considerations for more details.
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 shapeconst myEntity = new Entity()myEntity.addComponent(new BoxShape())//Create material and configure its fieldsconst myMaterial = new Material()myMaterial.albedoColor = Color3.Blue()myMaterial.metallic = 0.9myMaterial.roughness = 0.1//Assign the material to the entitymyEntity.addComponent(myMaterial)COPY CODE
See component reference) for a full list of all the fields that can be configured in a Material
of BasicMaterial
component.
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()COPY CODE
Note: Basic materials have some property names that are different from those in normal materials. For example it uses
texture
instead ofalbedoTexture
.
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)COPY CODE
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()COPY CODE
You can otherwise pick a random color using the following function:
// Pick a random colorlet green = Color3.Random()COPY CODE
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")COPY CODE
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:
Change a color gradually with linear interpolation between two colors, using the .Lerp()
function.
// This variable will store the ratio between both colorslet colorRatio = 0// Define colorsconst red = Color3.Red()const yellow = Color3.Yellow()// Create materialconst myMaterial = new Material()// This system changes the value of colorRatio every frame, and sets a new color on the materialexport 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 engineengine.addSystem(ColorSystem)COPY CODE
The example above changes the color of a material from red to yellow, incrementally shifting it on every frame.
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 shapeconst myEntity = new Entity()myEntity.addComponent(new BoxShape())//Create textureconst myTexture = new Texture("materials/wood.png")//Create a materialconst myMaterial = new Material()myMaterial.albedoTexture = myTexture//Assign the material to the entitymyEntity.addComponent(myMaterial)COPY CODE
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,})COPY CODE
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.decentraland.org/v2/collections/community_contest/wearables/cw_tuxedo_tshirt_upper_body/thumbnail")const myMaterial = new Material()myMaterial.albedoTexture = myTextureCOPY CODE
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.
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 shapeconst myEntity = new Entity()myEntity.addComponent(new BoxShape())//Create textureconst myTexture = new Texture("materials/wood.png")//Create material and configure its fieldsconst myMaterial = new BasicMaterial()myMaterial.texture = myTexture//Assign the material to the entitymyEntity.addComponent(myMaterial)COPY CODE
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 shapeconst myEntity = new Entity()myEntity.addComponent(new BoxShape())//Create textureconst myTexture = new Texture("materials/wood.png")//Create second textureconst myBumpTexture = new Texture("materials/woodBump.png")//Create material and configure its fieldsconst myMaterial = new Material()myMaterial.albedoTexture = myTexturemyMaterial.bumpTexture = myBumpTexture//Assign the material to the entitymyEntity.addComponent(myMaterial)COPY CODE
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.
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 fieldsconst myMaterial = new BasicMaterial()let myTexture = new Texture("materials/atlas.png", { wrap: 1, samplingMode: 0 })myMaterial.texture = myTexture//Create shape componentconst plane = new PlaneShape()// map the texture to each of the four corners of the planeplane.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 materialconst myEntity = new Entity()myEntity.addComponent(plane)myEntity.addComponent(myMaterial)COPY CODE
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 = myTextureconst 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 plane0, //lower-left corner0,cols, //lower-right corner0,cols, //upper-right cornerrows,0, //upper left-cornerrows,// South side of unrortated planecols, // lower-right corner0,0, // lower-left corner0,0, // upper-left cornerrows,cols, // upper-right cornerrows,]}COPY CODE
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 })COPY CODE
The example above sets the wrapping mode to MIRROR
.
Note: Uv properties are currently only available on
PlaneShape
and onBoxShape
components.
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 })COPY CODE
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.
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 = myTextureCOPY CODE
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)COPY CODE
To make a material with texture transparent:
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.
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.transparencyMode
to ALPHATEST
, you can fine tune the threshold used to determine if each pixel is transparent or not. Set the alphaTest
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 ALPHABLENDconst myMaterial = new Material()myMaterial.albedoTexture = myTexturemyMaterial.alphaTexture = alphaTexture// Material with ALPHATESTconst myMaterial2 = new Material()myMaterial2.albedoTexture = myTexturemyMaterial2.alphaTexture = alphaTexturemyMaterial.transparencyMode = 1 // ALPHATESTmyMaterial.alphaTest = 0.3COPY CODE
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 = falseCOPY CODE
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 shapesconst 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 fieldsconst myMaterial = new Material()myMaterial.albedoColor = Color3.Blue()//Assign same material to all entitiesmyEntity.addComponent(myMaterial)mySecondEntity.addComponent(myMaterial)myThirdEntity.addComponent(myMaterial)COPY CODE
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.