'Planet' height based, Texture2D setPixel performance queries.

Hello,

So I’ve been working on generating random meshes for planets, and then coloring those meshes based on the height of the UV map at that point. The mesh generation is relatively quick, less than a second for 40 segments down and 80 segments across. But when it comes to setting pixel colours, even a small texture size of 126x126 takes around 5 seconds to complete.

Currently my UV map stretches across the entire planet, after the mesh is fully generated I run another script with moves across the UV in a for loop using x and y values, proportionately to the texture size.

So with a texture of 126x126 the script would do a total of 15876 calculations, for each square in the texture. In each calculation, the real world position of the UV map at that point is grabbed, the distance to the center of the planet calculated and then a corresponding color is assigned to that point in the texture.

I’m wondering if anyone knows how to rapidly speed this process up, it’s my first time doing it so I feel like I’m being really inefficient somewhere. I want this to be part of a game and I don’t want users to have to wait more than 10 seconds for a new scene to load. I would much prefer a larger texture size as well. At the moment, 126 isn’t really cutting it, as you can see.

Current textured planet

Incoming wall of code:

var radius :float = 10.0;
var perlinRange :float = 0.05;
var segments :int =20;

private var Planet : GameObject; 
Planet = transform.parent.gameObject;

private var textureMaxHeight =  (radius*1.0);
private var textureMinHeight =  (radius*(1.0-perlinRange));

private var defaultMaterial = Resources.Load("Materials/Generation/Terrain/PlanetMaterial") as Material;
private var newMaterial :Material = new Material(Shader.Find("Diffuse"));
newMaterial.color = defaultMaterial.color;

private var colorDefault = newMaterial.color;
private var PlanetTexture :Texture2D = new Texture2D(126, 126);

private var worldPosition:Vector3;
private var heightVariance:float;
private var colorOutput:Color;

private var mesh: Mesh;
private var tris: int[];
private var uvs: Vector2[];
private var verts: Vector3[];

function Start(){

  mesh = GetComponent(MeshFilter).mesh;
  tris = mesh.triangles;
  uvs = mesh.uv;
  verts = mesh.vertices;

        for (var yC : int = 0; yC < PlanetTexture.height; ++yC){
            for (var xC : int = 0; xC <  PlanetTexture.width; ++xC){

                worldPosition = UvTo3D(Vector2((xC*1.0)/PlanetTexture.width,(yC*1.0)/PlanetTexture.height));

                heightVariance = (Vector3.Distance(worldPosition,Planet.transform.position)-textureMinHeight)/
                (textureMaxHeight-textureMinHeight);

                colorOutput = colorDefault;

                if( heightVariance < 0.75){
                    colorOutput.r = 0.2;
                    colorOutput.g = 0.2;
                    colorOutput.b = 0.6;

                }else if( heightVariance > 0.8){
                    colorOutput.r = 0.0;
                    colorOutput.g = 0.4;
                    colorOutput.b = 0.0;

                }

                	colorOutput.r += heightVariance/2;
                    colorOutput.g += heightVariance/2;
                    colorOutput.b += heightVariance/2;


                PlanetTexture.SetPixel (xC, yC, colorOutput);
            }
        }

        PlanetTexture.Apply();

        renderer.material = newMaterial;
        renderer.material.mainTexture = PlanetTexture;
        renderer.material.mainTexture.wrapMode = TextureWrapMode.Clamp;
}

function UvTo3D(checkUv: Vector2): Vector3 {
  for (var i: int = 0; i < tris.length; i += 3){
    var u1: Vector2 = uvs[tris*]; // get the triangle UVs*

var u2: Vector2 = uvs[tris[i+1]];
var u3: Vector2 = uvs[tris[i+2]];
// calculate triangle area - if zero, skip it
var a: float = Area(u1, u2, u3); if (a == 0) continue;
// calculate barycentric coordinates of u1, u2 and u3
// if anyone is negative, point is outside the triangle: skip it
var a1: float = Area(u2, u3, checkUv)/a; if (a1 < 0) continue;
var a2: float = Area(u3, u1, checkUv)/a; if (a2 < 0) continue;
var a3: float = Area(u1, u2, checkUv)/a; if (a3 < 0) continue;
// point inside the triangle - find mesh position by interpolation…
var p3D: Vector3 = a1verts[tris_]+a2verts[tris[i+1]]+a3verts[tris[i+2]];_
_
// and return it in world coordinates:_
return transform.TransformPoint(p3D);
_
}_
_
// point outside any uv triangle: return Vector3.zero*_
return Vector3.zero;
}

// calculate signed triangle area using a kind of “2D cross product”:
function Area(p1: Vector2, p2: Vector2, p3: Vector2): float {
var v1: Vector2 = p1 - p3;
var v2: Vector2 = p2 - p3;
return (v1.x * v2.y - v1.y * v2.x)/2;
}

The fastest way would be to allocate a Color32 the width*height of the texture, set those values using (y * width) + x (or even better, just an incrementing index), then use SetPixels32 to assign the array just before you Apply.