Smooth gradient between parts textures

How would I go about changing the color in between these parts for a procedural island to match like a gradient?

Example of what it does:

You can see how the colors just stop

Code for the coloring portion:

-- Generation
	local w1 = wedge:Clone();
	w1.Size = Vector3.new(0, height, math.abs(ab:Dot(back)));
	w1.CFrame = CFrame.fromMatrix((a + b)/2, right, up, back);
	w1.Parent = game.Workspace.Island.Terrain;

	local w2 = wedge:Clone();
	w2.Size = Vector3.new(0, height, math.abs(ac:Dot(back)));
	w2.CFrame = CFrame.fromMatrix((a + c)/2, -right, up, -back);
	w2.Parent = game.Workspace.Island.Terrain;
	
-- Coloring

	local w2height, w1height = w2.Position.Y, w1.Position.Y
	
	if w2height <= -4 and w1height <= -4 then -- if the wedge heights are under -4 it will set that to sand.
		w1.BrickColor = BrickColor.new("Cool yellow") -- sets w1 wedge to a sandy color
		w2.BrickColor = BrickColor.new("Cool yellow") -- sets w2 wedge to a sandy color also
	end

How I would go about doing this would be to set each wedge’s color based on how far it is way from a given height; in your case, the change from grass to sand happens at y-level -4.

You choose two points around that height, a start and end for your gradient, then find the ratio of your wedge’s position between those points. This ratio would control how far into the gradient you are, with 0.5 being the middle of the two colors. Basically just linear interpolation, like a tween or lerp.

Something like this should, I tested what I could, without actually making a terrain generation system:

-- Make some variables or a table for your colors
local TerrainColors = {
	Grass = BrickColor.new("Parsley green"),
	Sand = BrickColor.new("Cool yellow")
}

-- getColorGradient(colors : table, gradientPos : float)
function getColorGradient(colors, gradientPos)
	-- Calculates the R, G, B values based on position in the gradient
	local R = colors[1].R + (colors[2].R - colors[1].R) * gradientPos
	local G = colors[1].G + (colors[2].G - colors[1].G) * gradientPos
	local B = colors[1].B + (colors[2].B - colors[1].B) * gradientPos

	return Color3.fromRGB(R * 255, G * 255, B * 255) -- Reformats the values and converts them into a Color3
end

-- getGradientPosition(wedgeHeight: int, height : int, spread : int)
function getGradientPosition(wedgeHeight, height, spread)
	spread = spread or 1
	local startHeight = (height + spread)
	local endHeight = (height - spread)

	return math.clamp((wedgeHeight - endHeight)/ (startHeight - endHeight), 0, 1)
end



local w2height, w1height = w2.Position.Y, w1.Position.Y

local sandHeight = -4
if w2height <= sandHeight and w1height <= sandHeight then
	-- Sets the color to the returned color gradient (based on the Gradient Position) for each wedge
	w1.Color = getColorGradient({TerrainColors.Grass, TerrainColors.Sand}, getGradientPosition(w1height, sandHeight, 5))
	w2.Color = getColorGradient({TerrainColors.Grass, TerrainColors.Sand}, getGradientPosition(w2height, sandHeight, 5))
end
  • getColorGradient(): creates a color between two given colors in a table (the start and end colors). While using a gradient position, the ratio between the two points that the wedge is, to decide how much of each color should be present within the returned color.

  • getGradientPosition(): calculates the ratio of a given wedge height between two points. The points are calculated using a height (the point where the colors should change) and the spread (how far the gradient should spread in studs) around that height.

2 Likes

Thank you for replying! I tested it and got and error about the R G B on colors[]

'R' is not a valid member of BrickColor

Any idea how to fix that?

Oh my bad BrickColor uses lowercase for its RGB values. For the colors inside the TerrainColors table, if you are going to use BrickColor values make sure to add .Color to the end of each color.

local TerrainColors = {
	Grass = BrickColor.new("Parsley green").Color,
	Sand = BrickColor.new("Cool yellow").Color
}

Sorry, it was sort of inverted…

Change the order of the colors in the colors table of the getColorGradient function. If it’s inverted it’s because the start and end points are swapped.

w1.Color = getColorGradient({TerrainColors.Sand, TerrainColors.Grass}, getGradientPosition(w1height, sandHeight, 5))
w2.Color = getColorGradient({TerrainColors.Sand, TerrainColors.Grass}, getGradientPosition(w2height, sandHeight, 5))

See if that helps :smiley:

1 Like