Godot Version
Godot 4.x
Question
Hey! I’m trying to blend some GradientTexture1Ds across a procedurally generated surface, based on a height typed into an exported field in the editor. Instead of having the intended effect, I’m getting an odd color band across the center that has the colors of both of my test gradients, that shifts up and down based on the aforementioned height value.
Here is the odd behavior:
Here is what I would expect from the given values (roughly)
Here is the code for the shader itself:
shader_type spatial;
uniform float min_height;
uniform float max_height;
uniform sampler2D height_color;
varying float height;
void fragment(){
float t = height / (max_height - min_height) - (min_height / (max_height - min_height));
vec3 color = texture(height_color, vec2(t + 0.01, UV.y)).rgb;
ALBEDO.rgb = color;
}
void vertex() {
// Called for every vertex the material is visible on.
height = length(VERTEX);
}
//void light() {
// Called for every pixel for every light affecting the material.
// Uncomment to replace the default light processing function with this one.
//}
Here is (most) of the code that provides the shader with it’s information:
func regen_mesh(planet_data : PlanetData):
var arrays := []
arrays.resize(Mesh.ARRAY_MAX)
# Declare an array for the verticies and arrays for the properties of each vertex
var vertex_array := PackedVector3Array()
var uv_array := PackedVector2Array()
var normal_array := PackedVector3Array()
var index_array := PackedInt32Array()
# Resolution determines # of faces across the shape
var resolution := planet_data.resolution
var num_vertices : int = resolution * resolution
var num_indicies : int = (resolution - 1) * (resolution - 1) * 6
vertex_array.resize(num_vertices)
uv_array.resize(num_vertices)
normal_array.resize(num_vertices)
index_array.resize(num_indicies)
var triangle_index : int = 0
# Grab x and y vectors based off of the normals provided from the editor
var axisA := Vector3(normal.y, normal.z, normal.x)
var axisB : Vector3 = normal.cross(axisA)
# Iterate through all possible vertices and apply their properties to the relevant array
for y in range(resolution):
for x in range(resolution):
var index : int = x + y * resolution
# Normalize the x and y values as a percentage across the resolution
var percent := Vector2(x,y) / (resolution - 1)
# Modify the normal vector by applying the normalized percentages to shift it across the face of the cube
#to get the actual location
var pointOnUnitCube : Vector3 = normal + (percent.x - 0.5) * 2.0 * axisA + (percent.y - 0.5) * 2.0 * axisB
var pointOnUnitSphere : Vector3 = pointOnUnitCube.normalized()
var biome_percent : float = planet_data.biome_percent_from_point(pointOnUnitSphere)
var pointOnPlanet := planet_data.point_on_planet(pointOnUnitSphere)
# Insert the vertex into the array based on the index.
vertex_array[index] = pointOnPlanet
uv_array[index] = Vector2(0.0, biome_percent )
## print("Biome percent: ", biome_percent)
## print("UV array val: ", uv_array[index])
var l = pointOnPlanet.length()
if l < planet_data.min_height:
planet_data.min_height = l
if l > planet_data.max_height:
planet_data.max_height = l
# Pass indices into the index array for triangle generation
if x != resolution - 1 and y != resolution - 1:
index_array[triangle_index + 2] = index
index_array[triangle_index + 1] = index + resolution + 1
index_array[triangle_index] = index + resolution
index_array[triangle_index + 5] = index
index_array[triangle_index + 4] = index + 1
index_array[triangle_index + 3] = index + resolution + 1
triangle_index += 6
#for i in range(0, uv_array.size()):
#print("UV value: ", uv_array[i])
# Loop through the verticies of the mesh to recalculate the normals
for i in range(0, index_array.size(), 3):
var j : int = i + 1
var k : int = i + 2
# Grabs the vectors that make the edges of a given triangle
var ij : Vector3 = vertex_array[index_array[j]] - vertex_array[index_array[i]]
var jk : Vector3 = vertex_array[index_array[k]] - vertex_array[index_array[j]]
var ki : Vector3 = vertex_array[index_array[i]] - vertex_array[index_array[k]]
# Acquire the face normals, -1.0 faces them outwards
var cross_ijjk : Vector3 = ij.cross(jk) * -1.0
var cross_jkki : Vector3 = jk.cross(ki) * -1.0
var cross_kiij : Vector3 = ki.cross(ij) * -1.0
# Assigns the nomals to their relevant index
#while accounting for shared vertices across triangles
normal_array[index_array[i]] += cross_ijjk + cross_jkki + cross_kiij
normal_array[index_array[j]] += cross_ijjk + cross_jkki + cross_kiij
normal_array[index_array[k]] += cross_ijjk + cross_jkki + cross_kiij
for i in range(normal_array.size()):
normal_array[i] = normal_array[i].normalized()
arrays[Mesh.ARRAY_VERTEX] = vertex_array
arrays[Mesh.ARRAY_NORMAL] = normal_array
arrays[Mesh.ARRAY_TEX_UV] = uv_array
arrays[Mesh.ARRAY_INDEX] = index_array
call_deferred("_update_mesh", arrays, planet_data)
func _update_mesh(arrays : Array, planet_data : PlanetData):
var _mesh := ArrayMesh.new()
_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
self.mesh = _mesh
material_override.set_shader_parameter("min_height", planet_data.min_height)
material_override.set_shader_parameter("max_height", planet_data.max_height)
material_override.set_shader_parameter("height_color", planet_data.update_biome_texture())
func update_biome_texture() -> ImageTexture:
var image_texture = ImageTexture.new()
var dyn_image = Image.new()
var h : int = biomes.size()
if h > 0:
var data : PackedByteArray = []
var w : int = biomes[0].gradient.width
for b in biomes:
data.append_array(b.gradient.get_image().get_data())
dyn_image = Image.create_from_data(w, h, false, Image.FORMAT_RGBA8, data)
image_texture = ImageTexture.create_from_image(dyn_image)
image_texture.resource_name = "Biome Texture"
return image_texture
func biome_percent_from_point(point_on_unit_sphere : Vector3) -> float:
var normalized_height : float = (point_on_unit_sphere.y + 1.0) / 2.0
var biome_index : float = 0.0 # Index of the biome being modified
var num_biome : float = biomes.size() # Number of biomes
for i in range(num_biome):
if normalized_height < biomes[i].start_height:
break
biome_index = i
print("Biome Index: ", biome_index)
return biome_index / max(1.0, num_biome - 1.0)

