I’m currently trying to port/write a wireframe shader using barycentric coordinates for the game I’m working on. I tried a lot a different shader code I found online in order to get the same result when using barycentric coords in Unity, but nothing I was able to find seems to correspond.
Are there any tried and tested ways in Godot to get accurate wireframes (in game) using barycentric coords?
Everything I tried either didn’t work or doesn’t work predictably on all edges.
This specific shader mentions a problem with smooth shading, but i haven’t been able to get it right even on flat shaded models.
Here is the important part of the Unity shader i’m trying to port (not the entire shader):
I get very inconsistent results. As pictured, I can get it to work only on models inserted by godot, not any imported ones (which are standard blender cubes).
The comment on the second link might have a hint:
“Most of this is also possible without GL_EXT_fragment_shader_barycentric, but it requires adding an explicit vec3 vertex attribute that’s (1,0,0) on one vertex of each triangle, (0,1,0) on another, and (0,0,1) on the third. (The order doesn’t matter though.) This makes it hard to use on general meshes since you may need to duplicate vertices in some places in order to ensure that each vertex of a triangle has a different “color”, but if you’re preparing your mesh with wireframe rendering in mind, that’s a possible solution.”
Does anyone have any idea on how to make this work, or whether there are other options for wireframe rendering in godot?
i’m pretty sure the problem is that in your blender exports, the triangles aren’t separated out (they have shared vertices); so VERTEX_ID % 3 returns nothing sensible
for it to work the primitive must be PRIMITIVE_TRIANGLES (not triangle strip, triangle fan, etc) without use of an index buffer
from what i remember modeling for “flat shading” in blender accomplishes this, because it needs different normals at the face corners
Having worked on this some more i found there is no way to make blender export. a gltf file without index buffer. i eventually ended up with the following gdscript function duplicate_vertices that takes an ArrayMesh and “unrolls” it:
func new_array_from_typeid(typeid: Variant.Type) -> Variant:
return type_convert(null, typeid)
func array_custom_granularity(custom_format: Mesh.ArrayCustomFormat) -> int:
match custom_format:
Mesh.ARRAY_CUSTOM_RGBA8_UNORM, Mesh.ARRAY_CUSTOM_RGBA8_SNORM, Mesh.ARRAY_CUSTOM_RG_HALF:
return 4
Mesh.ARRAY_CUSTOM_RGBA_HALF:
return 8
Mesh.ARRAY_CUSTOM_R_FLOAT:
return 1
Mesh.ARRAY_CUSTOM_RG_FLOAT:
return 2
Mesh.ARRAY_CUSTOM_RGB_FLOAT:
return 3
Mesh.ARRAY_CUSTOM_RGBA_FLOAT:
return 4
return 0
func array_granularity(arr_id: Mesh.ArrayType, format: Mesh.ArrayFormat) -> int:
match arr_id:
Mesh.ARRAY_VERTEX, Mesh.ARRAY_NORMAL, Mesh.ARRAY_COLOR, Mesh.ARRAY_TEX_UV, Mesh.ARRAY_TEX_UV2:
return 1
Mesh.ARRAY_TANGENT:
return 4
Mesh.ARRAY_CUSTOM0:
return array_custom_granularity((format >> Mesh.ARRAY_FORMAT_CUSTOM0_SHIFT) & Mesh.ARRAY_FORMAT_CUSTOM_MASK)
Mesh.ARRAY_CUSTOM1:
return array_custom_granularity((format >> Mesh.ARRAY_FORMAT_CUSTOM1_SHIFT) & Mesh.ARRAY_FORMAT_CUSTOM_MASK)
Mesh.ARRAY_CUSTOM2:
return array_custom_granularity((format >> Mesh.ARRAY_FORMAT_CUSTOM2_SHIFT) & Mesh.ARRAY_FORMAT_CUSTOM_MASK)
Mesh.ARRAY_CUSTOM3:
return array_custom_granularity((format >> Mesh.ARRAY_FORMAT_CUSTOM3_SHIFT) & Mesh.ARRAY_FORMAT_CUSTOM_MASK)
Mesh.ARRAY_BONES, Mesh.ARRAY_WEIGHTS:
if (format & Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS) != 0:
return 8
else:
return 4
# Unhandled:
# ARRAY_INDEX
return 0
## "Unroll" array mesh.
## Known limitations:
## - ignores blend shapes
## - ignores LODs
func duplicate_vertices(input: ArrayMesh) -> ArrayMesh:
var output := ArrayMesh.new()
for surf in input.get_surface_count():
var arrays := input.surface_get_arrays(surf)
var indices = arrays[Mesh.ARRAY_INDEX]
var format := input.surface_get_format(surf)
var new_arrays: Array = []
new_arrays.resize(Mesh.ARRAY_MAX)
for arr_id in len(arrays):
if arr_id == Mesh.ARRAY_INDEX: # this is the one we want to eliminate
continue
if arrays[arr_id] == null:
continue
var arr_in = arrays[arr_id]
var typeid := typeof(arr_in)
var new_arr: Variant = new_array_from_typeid(typeid)
assert(typeof(new_arr) == typeid)
var granularity := array_granularity(arr_id, format)
for idx in indices:
for subidx in granularity:
new_arr.push_back(arr_in[idx * granularity + subidx])
print(arr_id, ' ', typeid, ' ', granularity, ' ', len(arr_in), '->', len(new_arr))
new_arrays[arr_id] = new_arr
output.add_surface_from_arrays(input.surface_get_primitive_type(surf), new_arrays, [], {}, format & ~Mesh.ARRAY_FORMAT_INDEX)
return output
Passing a mesh to this will output a mesh that works with the vectors[VERTEX_ID % 3] shader, while preserving all vertex attributes.
i truly wish we had something like GL_EXT_fragment_shader_barycentric in godot so that this isn’t needed, but it works for me.
Edit: apparently it is possible to make blender split out separate triangles by using “Mesh->Split->Faces by edges”, however this doesn’t help, because the exported GLTF file still has an index buffer which accesses them in random order
Cool! I tried to take a look, though i have a stupid question since i’m not familiar with mesh generating scripts.
How would i run this?
Do you create a script that has a reference of a model and then run the duplicate_vertices function once in the editor for it to replace the geometry permanently?
That’s what i tried but i wasn’t able to find how to get the initial gltf ArrayMesh data from the model to pass through
How i have it set up currently in my project is wth a custom import script postprocess_wireframe.py . This can be set in the import settings when you click on the glb, and will be run on import of that file.
This script extracts the mesh, processes the mesh and exports it to a res file under res://wireframes/, from which it can be used in your scene.
i first had it set up differently that it would do the processing at load every time, but that wasn’t quite optimal.
Mind that my wireframe shader uses an extra channel in the vertex data to remove inner triangle edges (added by a custom blender exporter ), but it should work fine with the one mentioned here.