I do not know how to get mesh to show lighting based on where it hits the mesh or how to color the mesh when using ArrayMesh as the mesh.
This code creates concentric circles of specified size and width. Currently, it responds uniformly to light, where the whole mesh is lit up when only a section is hit by directional light. Omnidirectional light has no effect. The color is white even though colors was filled with Color.RED. Material_override can change the color, but I wanted to avoid using that unless it was absolutely necessary.
func _ready() -> void:
#Circles is a MeshInstance3D node
%Circles.mesh = create_circle_mesh(1, 0.5, 1, 10)
print(%Circles.mesh)
func create_circle_mesh(smallest_circle_radius: float, circle_width: float, space_between_circles: float, number_of_circles: int) -> ArrayMesh:
var array_mesh: ArrayMesh = ArrayMesh.new()
var num_points: int = 100
var vertices: PackedVector3Array = PackedVector3Array()
var indices: PackedInt32Array = PackedInt32Array()
var normals: PackedVector3Array = PackedVector3Array()
var colors: PackedColorArray = PackedColorArray()
var sbc: float = space_between_circles
vertices.resize(num_points*2*number_of_circles)
normals.resize(num_points*2*number_of_circles)
colors.resize(num_points*2*number_of_circles)
normals.fill(Vector3.UP)
colors.fill(Color.RED)
for c in range(number_of_circles):
var scr: float = smallest_circle_radius * (sbc*(c+1))
var lcr: float = scr + circle_width
var start_index: int = c*num_points*2
for i in range(num_points):
var angle: float = i*TAU/num_points
vertices[start_index+i*2] = Vector3(sin(angle)*scr, 0, cos(angle)*scr)
vertices[start_index+i*2+1] = Vector3(sin(angle)*lcr, 0, cos(angle)*lcr)
for c in range(number_of_circles):
var start_index: int = c*num_points*2
for i in range(num_points):
var next_i = (i+1) % num_points
indices += PackedInt32Array([start_index+i*2, start_index+next_i*2, start_index+i*2+1, start_index+next_i*2, start_index+next_i*2+1, start_index+i*2+1])
var array: Array = []
array.resize(Mesh.ARRAY_MAX)
array[Mesh.ARRAY_VERTEX] = vertices
array[Mesh.ARRAY_INDEX] = indices
array[Mesh.ARRAY_NORMAL] = normals
array[Mesh.ARRAY_COLOR] = colors
array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, array)
return array_mesh
A DirecitonalLight3D is meant to light up all geometry; it’s primary use-case is to emulate sunlight. Only occluders will actually block light from a directional light source. Lights that have limited range/influence are:
OmniLight3D
Spotlight3D
Use these if you wish to only light up parts of a surface.
The COLOR array is used to provide vertices with color data. While this vertex data is referred to as colors, you typically don’t use it to inform the color of the mesh. Instead, vertex colors are used in a variety of specialized shaders to achieve unique effects.
Specialized shader example
A tree swaying in the wind is usually achieved through vertex displacement where vertex colors are used to modulate the degree of displacement for each vertex. Leaves are painted white (1, 1, 1) for maximum displacement while the trunk ranges from white to black (0, 0, 0) to achieve a tipping-like motion.
However, there are times when this color data is used to modulate color (e.g. particle systems). If you wish to color your mesh with the vertex color data, you have to provide your MeshInstance3D/ArrayMesh with a material that has Use as Albedo set to true. If you don’t do this, you will not be able to use the vertex data to color your mesh.
If you don’t mind me asking, what problem do you have with using a material?
Thank you for the insights provided.
As I mentioned before, omnidirectional light seemed to have no effect. Do you have any idea as to why this may be?
In answer to your question, it seemed strange that using material_override would be the only way to change the color of the mesh. Is it normal to use material_override to color custom meshes?
Also, I do not know how to provide the MeshInstance3D/ArrayMesh with a material. By “material that has use as albedo”, are you referring to these: BaseMaterial3D, CanvasItemMaterial, FogMaterial, PanoramaSkyMaterial, ParticleProcessMaterial, PhysicalSkyMaterial, PlaceholderMaterial, ProceduralSkyMaterial, ShaderMaterial?
Not really. I copied your code and have no problems with any of the light sources (see image below).
Your “custom mesh”, or any other mesh for that matter, will need a material to get rendered. This is the case even if you don’t supply the mesh with one; in that case, a default material is then used by Godot.
Meshes are shaded by a variety of information: albedo, roughness, metallic, normal, and so on. Albedo information (a.k.a. the base color channel) is what determines the color of the surface prior to shading.
This standardized rendering setup comes from what is commonly known as Physically-Based Rendering (PBR).
There are a few ways to set the material for a mesh:
Assign a material to the material field in the mesh resource i.e. your custom mesh
Assign a material to the material_override field in the MeshInstance3D node
It doesn’t necessarily matter which option you choose. However, you may encounter situations in which the material_override is undesirable to set as it overrides the mesh’s assigned material (hence the name).
I’m referring to BaseMaterial3D which has the option under the Vertex Color category.
It’s not immediately obvious but there’s a function from Mesh which allows you to provide specialized classes (i.e. your ArrayMesh) with a material (see class reference).
var mesh: ArrayMesh
var material: Material
mesh._surface_set_material(0, material)
I got the light to work. It turned out that toggle preview sunlight was enabled (which I didn’t even know existed before), so everything was lit up. Thank you for your help!