For the last few days I have been working on wave generation script. As there are a lot of calculations involved I was looking for possible optimizations to improve the performance of the code.
This specifically applies to the update function, as most of the calculations are done in here. As the full code is quite large, I made a pastebin of it which can be found over here.
void Update()
{
//Get sun reflection dir from sun object
if (Sun != null)
{
SunDirection = Sun.transform.forward;
OceanMaterial.SetVector("_SunDir", SunDirection);
}
if (this.IsRenderingReflection)
RenderObject();
OceanMaterial.SetTextureOffset("_Foam", Time.time * new Vector2(33,33));
var halfHeight = height / 2f;
var halfWidth = width / 2f;
var time = Time.time;
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var idx = width * y + x;
var yc = y < halfHeight ? y : -height + y;
var xc = x < halfWidth ? x : -width + x;
Vector2 vec_k = new Vector2(2.0f * Mathf.PI * xc / ChunkSize.x, 2.0f * Mathf.PI * yc / ChunkSize.z);
var sqrtMagnitude = (float)System.Math.Sqrt((vec_k.x * vec_k.x) + (vec_k.y * vec_k.y));
var iwkt = (float)System.Math.Sqrt(9.81f * sqrtMagnitude) * time * WaveSpeed;
ComplexF coeffA = new ComplexF((float)System.Math.Cos(iwkt), (float)System.Math.Sin(iwkt));
ComplexF coeffB;
coeffB.Re = coeffA.Re;
coeffB.Im = -coeffA.Im;
var ny = y > 0 ? height - y : 0;
var nx = x > 0 ? width - x : 0;
waterHeightMatrix[idx] = h0[idx] * coeffA + h0[width * ny + nx].GetConjugate() * coeffB;
t_x[idx] = waterHeightMatrix[idx] * new ComplexF(0.0f, vec_k.x) - waterHeightMatrix[idx] * vec_k.y;
// Choppy wave calculations
if (x + y > 0)
waterHeightMatrix[idx] += waterHeightMatrix[idx] * vec_k.x / sqrtMagnitude;
}
}
Fourier.FFT2(waterHeightMatrix, width, height, FourierDirection.Backward);
Fourier.FFT2(t_x, width, height, FourierDirection.Backward);
// Get base values for vertices and uv coordinates.
if (baseHeight == null)
{
baseHeight = baseMesh.vertices;
vertices = new Vector3[baseHeight.Length];
normals = new Vector3[baseHeight.Length];
tangents = new Vector4[baseHeight.Length];
}
var wh = width * height;
var scaleA = ChoppyScale / wh;
var scaleB = WaveScale / wh;
var scaleBinv = 1.0f / scaleB;
for (var i = 0; i < wh; i++)
{
var iw = i + i / width;
vertices[iw] = baseHeight[iw];
vertices[iw].x += waterHeightMatrix[i].Im * scaleA;
vertices[iw].y = waterHeightMatrix[i].Re * scaleB;
normals[iw] = Vector3.Normalize(new Vector3(t_x[i].Re, scaleBinv, t_x[i].Im));
if (((i + 1) % width) == 0)
{
var iwi = iw + 1;
var iwidth = i + 1 - width;
vertices[iwi] = baseHeight[iwi];
vertices[iwi].x += waterHeightMatrix[iwidth].Im * scaleA;
vertices[iwi].y = waterHeightMatrix[iwidth].Re * scaleB;
normals[iwi] = Vector3.Normalize(new Vector3(t_x[iwidth].Re, scaleBinv, t_x[iwidth].Im));
}
}
var offset = geometryWidth * (geometryHeight - 1);
for (var i = 0; i < geometryWidth; i++)
{
var ioff = i + offset;
int mod = i % width;
vertices[ioff] = baseHeight[ioff];
vertices[ioff].x += waterHeightMatrix[mod].Im * scaleA;
vertices[ioff].y = waterHeightMatrix[mod].Re * scaleB;
normals[ioff] = Vector3.Normalize(new Vector3(t_x[mod].Re, scaleBinv, t_x[mod].Im));
}
var geoWidthHeight = geometryWidth * geometryHeight - 1;
for (var i = 0; i < geoWidthHeight; i++)
{
//Need to preserve w in refraction/reflection mode
if (!reflectionRefractionEnabled)
{
if (((i + 1) % geometryWidth) == 0)
{
tangents[i] = Vector3.Normalize((vertices[i - width + 1] + new Vector3(ChunkSize.x, 0.0f, 0.0f) - vertices[i]));
}
else
{
tangents[i] = Vector3.Normalize((vertices[i + 1] - vertices[i]));
}
tangents[i].w = 1.0f;
}
else
{
Vector3 tmp; // = Vector3.zero;
if (((i + 1) % geometryWidth) == 0)
{
tmp = Vector3.Normalize(vertices[i - width + 1] + new Vector3(ChunkSize.x, 0.0f, 0.0f) - vertices[i]);
}
else
{
tmp = Vector3.Normalize(vertices[i + 1] - vertices[i]);
}
tangents[i] = new Vector4(tmp.x, tmp.y, tmp.z, tangents[i].w);
}
}
//In reflection mode, use tangent w for foam strength
if (reflectionRefractionEnabled)
{
for (var y = 0; y < geometryHeight; y++)
{
for (var x = 0; x < geometryWidth; x++)
{
var item = x + geometryWidth * y;
if (x + 1 >= geometryWidth)
{
tangents[item].w = tangents[geometryWidth * y].w;
continue;
}
if (y + 1 >= geometryHeight)
{
tangents[item].w = tangents[x].w;
continue;
}
var right = vertices[(x + 1) + geometryWidth * y].x - vertices[item].x;
var foam = right / (ChunkSize.x / geometryWidth);
if (foam < 0.0f)
tangents[item].w = 1f;
else if (foam < 0.5f)
tangents[item].w += 3.0f * Time.deltaTime;
else
tangents[item].w -= 0.4f * Time.deltaTime;
if (Player != null)
{
Vector3 player2Vertex = (Player.position - vertices[item] - transform.position);
// foam around boat
if (player2Vertex.x >= ChunkSize.x)
player2Vertex.x -= ChunkSize.x;
if (player2Vertex.x <= -ChunkSize.x)
player2Vertex.x += ChunkSize.x;
if (player2Vertex.z >= ChunkSize.z)
player2Vertex.z -= ChunkSize.z;
if (player2Vertex.z <= -ChunkSize.z)
player2Vertex.z += ChunkSize.z;
player2Vertex.y = 0;
if (player2Vertex.sqrMagnitude < WakeDistance * WakeDistance)
tangents[item].w += 3.0f * Time.deltaTime;
}
tangents[item].w = Mathf.Clamp(tangents[item].w, 0.0f, 2.0f);
}
}
}
tangents[geoWidthHeight] = Vector4.Normalize(vertices[geoWidthHeight] + new Vector3(ChunkSize.x, 0.0f, 0.0f) - vertices[1]);
for (var L0D = 0; L0D < maxLOD; L0D++)
{
var den = (int)System.Math.Pow(2f, L0D);
var itemcount = (int)((height / den + 1) * (width / den + 1));
Vector4[] tangentsLOD = new Vector4[itemcount];
Vector3[] verticesLOD = new Vector3[itemcount];
Vector3[] normalsLOD = new Vector3[itemcount];
var idx = 0;
for (var y = 0; y < geometryHeight; y += den)
{
for (var x = 0; x < geometryWidth; x += den)
{
var idx2 = geometryWidth * y + x;
verticesLOD[idx] = vertices[idx2];
tangentsLOD[idx] = tangents[idx2];
normalsLOD[idx++] = normals[idx2];
}
}
for (var k = 0; k < tilesLOD[L0D].Count; k++)
{
Mesh meshLOD = tilesLOD[L0D][k];
meshLOD.vertices = verticesLOD;
meshLOD.normals = normalsLOD;
meshLOD.tangents = tangentsLOD;
}
}
}