Texture flickering when mesh includes normals

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Shaolin_Funk
:warning: Old Version Published before Godot 3 was released.

I’m still working on getting procedural meshes right and I’ve hit another snag. It’s a little hard to describe, so I’ll show you a video first. The cube on the left is one I’ve created in code, the one on the right is an instance of TestCube for comparison.

The flickering only occurs on two faces of the cube, the ones that are perpendicular to the Z axis. The other 4 faces render correctly. It only happens when the mesh includes normal data, whether I add the normals manually using SurfaceTool.add_normal() or if I automatically generate them with SurfaceTool.generate_normals(). If I omit normals entirely there is no flickering. Code listing follows.

func make_cube(): #mesh
	var uv_array = []
	var faces = []
	var normals = []
	var st = SurfaceTool.new()
	var mat = FixedMaterial.new()
	var tex = ImageTexture.new()
	var nm = ImageTexture.new()
	
	tex.load("res://textures/grass.png")
	nm.load("res://textures/grass-nm.png")

	mat.set_texture(mat.PARAM_DIFFUSE, tex)
	mat.set_texture(mat.PARAM_NORMAL, nm)
	
	#same uv mapping for each face
	uv_array.push_back(Vector2(0,0))
	uv_array.push_back(Vector2(1,1))
	uv_array.push_back(Vector2(0,1))
	uv_array.push_back(Vector2(0,0))
	uv_array.push_back(Vector2(1,0))
	uv_array.push_back(Vector2(1,1))
	
	#face 0
	normals.push_back(Vector3(0,0,-1))
	faces.push_back([])
	faces[0].push_back(Vector3(-1,-1,-1))
	faces[0].push_back(Vector3(1,1,-1))
	faces[0].push_back(Vector3(-1,1,-1))
	faces[0].push_back(Vector3(-1,-1,-1))
	faces[0].push_back(Vector3(1,-1,-1))
	faces[0].push_back(Vector3(1,1,-1))
	
	#face 1
	normals.push_back(Vector3(-1,0,0))
	faces.push_back([])
	faces[1].push_back(Vector3(1,-1,-1))
	faces[1].push_back(Vector3(1,1,1))
	faces[1].push_back(Vector3(1,1,-1))
	faces[1].push_back(Vector3(1,-1,-1))
	faces[1].push_back(Vector3(1,-1,1))
	faces[1].push_back(Vector3(1,1,1))
	#face 2
	normals.push_back(Vector3(0,0,1))
	faces.push_back([])
	faces[2].push_back(Vector3(1,-1,1))
	faces[2].push_back(Vector3(-1,1,1))
	faces[2].push_back(Vector3(1,1,1))
	faces[2].push_back(Vector3(1,-1,1))
	faces[2].push_back(Vector3(-1,-1,1))
	faces[2].push_back(Vector3(-1,1,1))
	#face 3
	normals.push_back(Vector3(1,0,0))
	faces.push_back([])
	faces[3].push_back(Vector3(-1,-1,1))
	faces[3].push_back(Vector3(-1,1,-1))
	faces[3].push_back(Vector3(-1,1,1))
	faces[3].push_back(Vector3(-1,-1,1))
	faces[3].push_back(Vector3(-1,-1,-1))
	faces[3].push_back(Vector3(-1,1,-1))
	#top
	normals.push_back(Vector3(0,1,0))
	faces.push_back([])
	faces[4].push_back(Vector3(-1,1,-1))
	faces[4].push_back(Vector3(1,1,1))
	faces[4].push_back(Vector3(-1,1,1))
	faces[4].push_back(Vector3(-1,1,-1))
	faces[4].push_back(Vector3(1,1,-1))
	faces[4].push_back(Vector3(1,1,1))
	#bottom
	normals.push_back(Vector3(0,-1,0))
	faces.push_back([])
	faces[5].push_back(Vector3(-1,-1,-1))
	faces[5].push_back(Vector3(1,-1,1))
	faces[5].push_back(Vector3(1,-1,-1))
	faces[5].push_back(Vector3(-1,-1,-1))
	faces[5].push_back(Vector3(-1,-1,1))
	faces[5].push_back(Vector3(1,-1,1))
	
	st.begin(Mesh.PRIMITIVE_TRIANGLES)
	st.set_material(mat)
	
	for f in range(6):
		for v in range(6):
			st.add_uv(uv_array[v])
			st.add_normal(normals[f])
			st.add_vertex(faces[f][v] * radius)
	#st.generate_normals()
	return st.commit()
:bust_in_silhouette: Reply From: henkz

Since no one else is answering this I can at least try to point you in the right direction.
Your mesh needs tangent data when using normalmaps, you can add them with SurfaceTool.add_tangent(). If the tangents point in the same direction as your normal you will get flickering. I’m not sure why your normal map renders at all, if you tried this on an android device the normal map wouldn’t render.
I don’t know why SurfaceTool can’t generate them automatically like the collada importer can.

If the tangents point in the same direction as your normal you will get flickering

Ah that must be why only two faces are flickering. Presumably the engine creates default tangents which happen to point in the same direction as the Z axis, which is why only the triangles whose normals point down the Z axis were affected.

I was under the impression that if I provided normals I wouldn’t need tangents, and vice-versa. Thanks for correcting my misunderstanding. I’ll try this out later on and report back.

Shaolin_Funk | 2016-02-28 20:29

Nope, I’m still not getting it. I’ve refactored the code to make it a little easier to work with, so now I’m working on a per-face basis, generating 6 vertices (2 tris), 1 normal and 1 tangent per face, like so:

var vert_array = #Vector3
var uv_array = #Vector2
var normal #Vector3
var tangent #plane
const draw_order = [0, 2, 1, 0, 3, 2]

func _init(v0, v1, v2, v3): #v1, v2, v3, v4 : Vector3
var u_max = (v3-v0).length()
var v_max = (v1-v0).length()
vert_array = [v0, v1, v2, v3]
uv_array = [Vector2(0,0), Vector2(0,v_max), Vector2(u_max,v_max), Vector2(u_max,0)]
normal = ((v1 - v0).cross(v3 - v0)).normalized()
tangent = Plane(v0, v1, v2)

func create_tris(st): #st : SurfaceTool
for v in draw_order:
st.add_uv(uv_array[v])
st.add_tangent(tangent)
st.add_normal(normal)
st.add_vertex(vert_array[v])

Result is that all 6 faces now flicker.

Shaolin_Funk | 2016-03-01 16:23