Procedural mesh color glitch

I’m procedurally generating a simple mesh and I get this little color glitch. One of the vertices has a different vertex color than the rest. Which is weird to begin with because I don’t assign vertex colors to any of the vertices. Also, it doesn’t happen every time I create a GameObject with that script on it. And even vertex where color difference is on keeps changing, sometimes it’s on the second vertex, sometimes the third vertex of the array.

Here’s an image of the problem. I’ve used a different shader to better illustrate the problem

Here’s the code I’m using to update the mesh

Vector3[] vertices = new Vector3[waveSegmentCount * 2];
Vector2[] uvCoords = new Vector2[waveSegmentCount * 2];
int[] triangles = new int[waveSegmentCount * 6];

// Generate mesh data
for (int i = 0; i < waveSegmentCount; i++)
{
	float rad = (float)i * (360.0f / (float)waveSegmentCount) * Mathf.Deg2Rad;
	
	Vector3 v = new Vector3(Mathf.Cos(rad), Mathf.Sin(rad), 0.0f);
	
	vertices[i * 2] = v * TimeAlive * propagationVelocity;
	vertices[i * 2 + 1] = v * TimeAlive * propagationVelocity + v * waveThickness;
	
	uvCoords[i * 2] = Vector2.up;
	uvCoords[i * 2 + 1] = Vector2.zero;
	
	triangles[i * 6] = i * 2;
	triangles[i * 6 + 1] = i * 2 + 2;
	triangles[i * 6 + 2] = i * 2 + 1;
	triangles[i * 6 + 3] = i * 2 + 1;
	triangles[i * 6 + 4] = i * 2 + 2;
	triangles[i * 6 + 5] = i * 2 + 3;
}

// Merge the ends
triangles[waveSegmentCount * 6 - 5] = 0;
triangles[waveSegmentCount * 6 - 2] = 0;
triangles[waveSegmentCount * 6 - 1] = 1;

// Apply mesh data
mesh.Clear();

mesh.vertices = vertices;
mesh.uv = uvCoords;
mesh.triangles = triangles;

I have used the debugger and found that the glitch is indeed in the vertex color data of the mesh.

The behaviour is so unpredictable that I cannot imagine it could a problem with my code, but more likely a bug in underlying systems.

I can just assign null to mesh.colors to fix the problem, but I’d really like get to the bottom of this.

I had a graphics card a while ago which had a few bad bits in VRAM, which would cause things like this…depending on what exactly happened to be using that VRAM, it would randomly manifest as wrongly-colored pixels in textures, wrong vertex colors, or incorrectly positioned vertices. Try a different computer and see what happens.

–Eric

Thanks for the quick reply.

I have tested on a different computer and actually a different platform (Mac), and the exact same problem occurs.

  • Aleksi

does it occur if you disable mesh optimisations in player build preferences?

Because with that I still get everything overbright vert colours and I’m not sure why that is (3.5.7)

It would be good to post a complete working script, or maybe a project.

–Eric

To hippocoder: Optimize mesh data is off already

Here’s the complete script

using UnityEngine;
using System.Collections.Generic;

public class AudioWave : MonoBehaviour
{
	public float propagationVelocity = 12.0f;
	public float propagationTime = 2.0f;
	public int waveSegmentCount = 32;
	public float waveThickness = 1.0f;
	
	public GameObject sonarAudioPrefab;
	
	private Transform _transform;
	private Material _material;
	private Mesh mesh;
	
	private List<AudioReflectiveEntity> hitReflectors;
	private float startTime;
	
	private Vector3 emitterMovement;
	public Vector3 EmitterMovement
	{
		get
		{
			return emitterMovement;
		}
		set
		{
			if (emitterMovement.sqrMagnitude == 0.0f)
				emitterMovement = value;
		}
	}
	
	public float Strength
	{
		get; private set;
	}
	
	public float TimeAlive
	{
		get
		{
			return Time.time - startTime;
		}
	}
	
	void Awake()
	{
		hitReflectors = new List<AudioReflectiveEntity>();
		
		_transform = GetComponent<Transform>();
		_material = GetComponent<MeshRenderer>().material;
		mesh = new Mesh();
		mesh.MarkDynamic();
		
		GetComponent<MeshFilter>().mesh = mesh;
		
		startTime = Time.time;
		Strength = 1.0f;
		
		if (sonarAudioPrefab != null)
		{
			GameObject go = (GameObject)Instantiate(sonarAudioPrefab, _transform.position, Quaternion.identity);
			
			Destroy(go, 1.5f);
		}
	}
	
	void Update()
	{
		// Decrease wave strength
		Strength -= Time.deltaTime / propagationTime;
		
		if (Strength > 0.1f)
		{
			foreach (AudioReflectiveEntity reflectiveEntity in AudioEntityManager.Instance.GetReflectorList())
			{
				if (HasHitReflector(reflectiveEntity))
				{
					reflectiveEntity.Hit(this);
				}
			}
			
			UpdateMesh();

			
			UpdateMaterialOpacity();
		}
		else
		{
			Destroy(gameObject);
		}
	}
	
	private bool HasHitReflector(AudioReflectiveEntity reflectiveEntity)
	{
		if (hitReflectors.Count > 0  hitReflectors.Contains(reflectiveEntity))
		{
			return false;
		}
		else
		{
			float treshold = TimeAlive * propagationVelocity + waveThickness * 0.5f;
			
			if ((_transform.position - reflectiveEntity.Position).sqrMagnitude < treshold * treshold)
			{
				hitReflectors.Add(reflectiveEntity);
				return true;
			}
			else
				return false;
		}
	}
	
	private void UpdateMesh()
	{
		Vector3[] vertices = new Vector3[waveSegmentCount * 2];
		Vector2[] uvCoords = new Vector2[waveSegmentCount * 2];
		int[] triangles = new int[waveSegmentCount * 6];
		
		// Generate mesh data
		for (int i = 0; i < waveSegmentCount; i++)
		{
			float rad = (float)i * (360.0f / (float)waveSegmentCount) * Mathf.Deg2Rad;
			
			Vector3 v = new Vector3(Mathf.Cos(rad), Mathf.Sin(rad), 0.0f);
			
			vertices[i * 2] = v * TimeAlive * propagationVelocity;
			vertices[i * 2 + 1] = v * TimeAlive * propagationVelocity + v * waveThickness;
			
			uvCoords[i * 2] = Vector2.up;
			uvCoords[i * 2 + 1] = Vector2.zero;
			
			triangles[i * 6] = i * 2;
			triangles[i * 6 + 1] = i * 2 + 2;
			triangles[i * 6 + 2] = i * 2 + 1;
			triangles[i * 6 + 3] = i * 2 + 1;
			triangles[i * 6 + 4] = i * 2 + 2;
			triangles[i * 6 + 5] = i * 2 + 3;
		}
		
		// Merge the ends
		triangles[waveSegmentCount * 6 - 5] = 0;
		triangles[waveSegmentCount * 6 - 2] = 0;
		triangles[waveSegmentCount * 6 - 1] = 1;
		
		// Apply mesh data
		mesh.Clear();
		
		mesh.vertices = vertices;
		mesh.uv = uvCoords;
		mesh.triangles = triangles;
	}
	
	private void UpdateMaterialOpacity()
	{
		float alpha = 0.0f;
		float rise = 0.2f;
		
		if (TimeAlive < rise)
			alpha = (TimeAlive) / rise;
		else
			alpha = (startTime + rise - Time.time) / (propagationTime - rise) + 1.0f;
		
		// Scale to appropriate visibility
		alpha *= 0.05f;
		
		_material.SetColor("_TintColor", new Color(0.3f, 0.3f, 0.3f, alpha));
	}
}

It has external dependencies, so I can’t test it.

–Eric

Here’s a link to the zipped project: https://dl.dropbox.com/u/1769111/Diving%20Chamber.zip

  • Aleksi

EDIT: Removed the file for safety reasons since the problem is solved

It seems to be an issue with Mesh.Clear(), since when I removed that the effect stopped happening. You don’t need Clear in this case since you’re not resizing the mesh, but this still seems like a bug so I’d recommend reporting it with the bug reporter app.

On a tangential note, for the sake of efficiency, I’d suggest computing the UVs and triangles once, rather than recomputing them every frame, since the number of vertices seems to be constant. Only the vertex positions change, so the vertices are the only thing you need to update every frame. I’d also recommend reusing the mesh instead of destroying/recreating it every time.

–Eric

Thanks Eric for being so helpful. It never occurred to me you wouldn’t need to call Clear(), and obviously there’s no reason for recalculating uvs or triangles in that case.

  • Aleksi