As a Roblox developer, it is currently impossible to blend textures together to create a composite image. This is currently something that would be very useful. In the future with PBR materials this will be something that’s necessary in order to layer textures such as makeup or dirt onto skin, scratches onto cars, paint onto walls, and any other situation where an object partially changes appearance.
My proposal is a family of objects that facilitate texture blending while keeping in mind that roblox users are kids and need simple interfaces.
Instance → TextureBlendingComponent
:OutputToContent(String content)
When a blending operation is ready to output, the developer calls OutputToContent. The provided content must start with “runtime://” to prevent the developer from writing non-runtime content. When OutputToContent is called the game will save the generated image to that content id and update any objects that are using that content id.
TextureBlendingComponent → ImageContentSource
.Source [Content]
This is how you get an image into the blending pipeline. Outputting it will output that exact image to the target content id.
TextureBlendingComponent → OpacityModifier
.Input [TextureBlendingComponent]
.Opacity [Content]
This is the first operation-type component. Operation-type components take an input TextureBlendingComponent and apply an operation to them. You could make an image semi-transparent using this object.
TextureBlendingComponent → HSVModifier
.Input [TextureBlendingComponent]
.Hue [Number]
.Saturation [Number]
.Value [Number]
Like the one before it, HueShiftModifier takes TB component and applies an operation with an argument.
TextureBlendingComponent → TextureBlend
.Top [TextureBlendingComponent]
.Bottom [TextureBlendingComponent]
.BlendingMode [BlendingMode]
This one takes two arguments. It is used to layer two textures together.
TextureBlendingComponent → AlphaMask
.Input [TextureBlendingComponent]
.Mask [TextureBlendingComponent]
This one masks the input texture using mask as a reference. It can be used to cut textures up and make them fit together more nicely. In this way it can generate a new shape.
TextureBlendingComponent → NoiseTexture
.Seed [Number]
.Scale [Number]
This one generates a noise texture using the given seed.
TextureBlendingComponent → TransformModifier
.Input [TextureBlendingComponent]
.Offset [UDim2]
.Rotate [Number]
.Scale [Vector2]
.Skew [Vector2]
This one performs several actions all at once!
Usage Example
To blend dirt and customizeable makeup onto a character:
-- Generate skin texture by modifying its HSV
local skin_source = Instance.new('ImageContentSource')
skin_source.Source = 'rbxgameasset://skin'
local skin_hsv = Instance.new('HSVModifier')
skin_hsv.Input = skin_source
skin_hsv.Hue = data.skinHue
skin_hsv.Saturation = data.skinSat
skin_hsv.Value = data.skinVal
-- Generate dirt texture by adding random noise then making it transparent
local dirt_source = Instance.new('ImageContentSource')
dirt_source.Source = 'rbxgameasset://dirt'
local dirt_noise = Instance.new('NoiseTexture')
local dirt_blend = Instance.new('TextureBlend')
dirt_blend.Top = dirt_source
dirt_blend.Bottom = dirt_noise
dirt_blend.BlendMode = Enum.TextureBlendMode.Multiply
local dirt_opacity = Instance.new('OpacityModifier')
dirt_opacity.Input = dirt_blend
dirt_opacity.Opacity = data.dirtiness
-- Generate makeup texture by changing colors and opacity
local makeup_source = Instance.new('ImageContentSource')
makeup_source.Source = 'rbxgameasset://' .. data.selectedMakeup
local makeup_hsv = Instance.new('HSVModifier')
makeup_hsv.Input = makeup_source
makeup_hsv.Hue = data.makeupHue
makeup_hsv.Saturation = data.makeupSat
makeup_hsv.Value = data.makeupVal
local makeup_opacity = Instance.new('OpacityModifier')
makeup_opacity.Input = makeup_hsv
makeup_opacity.Opacity = 1 - data.makeupTransparency
-- Blend those 3 final textures to create the composite character texture
local makeup_skin = Instance.new('TextureBlend')
makeup_skin.Top = makeup_opacity
makeup_skin.Bottom = skin_hsv
local with_dirt = Instance.new('TextureBlend')
with_dirt.Top = dirt_opacity
with_dirt.Bottom = makeup_skin
-- Pick a contentId and output to it
local contentId = 'runtime://' .. player.Name .. 'Skin'
with_dirt:OutputToContent(contentId)
-- And apply the final image to the character
character.Texture = contentId
There are tons of options for a comprehensive texture API, such as
TextTexture - Generates text as a texture
Blur
MotionBlur
RadialBlur
ZoomBlur
Isolate - Performs a magic wand operation, returning the selected pixels
Bulge
Mosaic
VectorNoise - Like noise but uses all 3 channels instead of black and white
GridLinesTexture - Generates grid lines
CheckerboardTexture - Generates a checkerboard pattern
Crop
Resize
Invert
Curves
Tile
ViewportFrameSource
CameraSource
... and more!
Using chained APIs like this you could generate simple textures and patterns on the fly, or even generate complex textures like dynamically generated UI. If you do it on the server there doesn’t even have to be any load on the client, and the intermediate textures are never even generated. Only the final output sticks around in memory and only when you request it, and then it automatically applies to everything referencing the given content id.