Greetings! I’m trying to render a mesh which solely consists of points using a custom shader. So far I have succeeded in importing a GLB file with only vertex positions and colors, which renders as a cloud of black points. I have also created a shader material of type spatial, but I have yet been unable to influence the point colors or their positions. I have tried to set the render mode to unshaded, and the shader model to particles, but neither worked. I can neither displace the vertieces in the vertex stage nor change the albedo in the fragment stage.
My plan is to render stars the following way: the points are all normalized vectors, and my endgame is to transform them using only the linear component of the view matrix, without the affine transform, setting the depth value to one so they always render in the background. Then I simply want to render a point size 1 pixel with the given color. That’s it.
I would be extremely grateful for any pointers on how I may achieve this with a custom shader, or in some other way if it would be preferable. Cheers.
Point rendering is available in BaseMaterial3D by checking Use Point Size in the material properties. You can also specify the point size, which is in pixels. You can still give the material an albedo texture so that each point acts like a billboarded sprite, and set it to unshaded mode. Check Vertex Color > Use as Albedo so that the point’s color is affected by the vertex color.
In custom shaders, writing to POINT_SIZE with the point size as a float in vertex() will enable point rendering.
In any case, your custom shader should be of Spatial type, not Particles (although it is possible for particles nodes to use PointMesh for point-based rendering).
Then I made a material from it, and assigned it to my mesh in the GLB import properties. However, neither the point size nor the color changed; it’s still the same cloud of black points. Is there something I’m doing wrong?
Does it work if you switch to the Mobile rendering method (which doesn’t use a depth prepass), or write ALPHA = 1.0 in fragment() to force the material to be transparent? Transparent materials don’t write to depth.
Does it work if you switch to the Mobile rendering method
My bad, I neglected to mention that I am already in the mobile pipeline. I can try on forward+ if you’d like. I tried writing to alpha with the same result (no change).
@Calinou I figured it out. The issue wasn’t with my material at all, but with the GLB importer. Even though I double clicked the model, went to the ‘materials’ tab and checked ‘use external’ in the one unnamed material, it wasn’t getting applied to my model. Instead I made an inherited scene and used the “surface material override” option of the MeshInstance3D. The material is now being applied, and I can make a custom shader in the expected manner. Thank you for your time!
Now that I’ve gotten to play around with the shader I have a few questions:
Is it possible to disable frustum culling? My transform is at the origin, but in the shader I destroy the last column of the matrix, so it’s like they’re always centered on the camera, and as such they’re permanently visible. I tried cull_disabled in the render_mode, but that doesn’t seem to disable frustum culling (if I walk some distance and then look away from the origin, they disappear, which I’m pretty sure is frustum culling).
Is it possible to use floats for the vertex colors? My colors are HDR and range from like 10^-5 to 10^2, however if I try to transform them with the shader I only ever seem to see the same few stars, which makes me think that the vast majority of those are getting quantized to zero (which would make sense it it’s a fixed point byte mapping 0->255 to 0.0->1.0)
There’s RenderingServer.instance_set_ignore_culling() for that, but it currently lacks a high-level equivalent. You could set extra_cull_margin to a very high value on the MeshInstance3D node instead, but it will still perform culling routines (which is a tad slower than ignoring them). I have a PR that exposes the culling mode as a property: Add a Culling Mode property to GeometryInstance3D by Calinou · Pull Request #105199 · godotengine/godot · GitHub
Is it possible to use floats for the vertex colors? My colors are HDR and range from like 10^-5 to 10^2, however if I try to transform them with the shader I only ever seem to see the same few stars, which makes me think that the vast majority of those are getting quantized to zero (which would make sense it it’s a fixed point byte mapping 0->255 to 0.0->1.0)
Not in 4.x, as vertex colors are 8 bpc unsigned integers for performance reasons. (3.x used 16 bpc floats if I’m not mistaken, or perhaps even 32 bpc.)
Thanks! I added RenderingServer.instance_set_ignore_culling(self.get_instance(), true) to a _ready on the MeshInstance3D of my inherited scene and it seems to do the trick.
Instead, you could map the imported color data to one of the custom per-vertex properties, which can be floats or any other type. You can then use ALBEDO = CUSTOM0.rgb; to set the custom color based on the custom vertex data (you won’t need to read COLOR).
How do I set that mapping? As you mention in the PR the option is not currently in the gltf importer. For what it’s worth I tried referencing CUSTOM0 in the shader but it gave me an error (and it doesn’t show up on the tab completion).
Well this was a journey. I haven’t been able to coerce either Blender’s OBJ, PLY, or GLTF exporters to write both faceless vertices and color data at the same time, not even trying to hack it into the normal vectors or the texcoords. After banging my head against the wall for some time I decided to go off the deep end and write my vertices and colors as raw, packed, little endian, 32-bit float arrays to two files, which I then loaded in Godot with FileAccess, and then loaded into my mesh as such:
var arrays = []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = read_vertices('res://stars/star.verts')
arrays[Mesh.ARRAY_CUSTOM0] = read_colors('res://stars/star.colors')
var m: ArrayMesh = ArrayMesh.new()
var format = Mesh.ArrayFormat.ARRAY_FORMAT_VERTEX | Mesh.ArrayFormat.ARRAY_FORMAT_CUSTOM0 | (Mesh.ArrayCustomFormat.ARRAY_CUSTOM_R_FLOAT << Mesh.ArrayFormat.ARRAY_FORMAT_CUSTOM0_SHIFT)
m.add_surface_from_arrays(Mesh.PRIMITIVE_POINTS, arrays, [], {}, format)
m.surface_set_material(0, material)
self.set_mesh(m)
RenderingServer.instance_set_ignore_culling(self.get_instance(), true)
I gotta say though, the results are extremely pleasant. Orion says hi