More in this category:
Posted on December 10, 2014, by Richard Knol
In my project, users can upload their own textures and also their own normal maps. But chances are, they will not upload an actual normal map and provide a regular image instead. So I need to generate a normal map from that image.
public bool IsNormalMap(Texture2D aTexture) {
Color avgColor = aTexture.GetAverageColor();
float rg = (avgColor.r + avgColor.g) / 2f;
if(rg <= 0f) rg = 1f;
float relativeBlue = avgColor.b / rg;
if(relativeBlue > 1.8f && Mathf.Abs(avgColor.r - avgColor.g) < 0.1f) return true;
return false;
}
public Texture2D ColorTextureToNormalMap(Texture2D aTexture, float strength) {
Texture2D normalTexture = new Texture2D(aTexture.width, aTexture.height, TextureFormat.RGB24, aTexture.mipmapCount > 1);
Color[] pixels = aTexture.GetPixels(0);
Color[] nPixels = new Color[pixels.Length];
for (int y=0; y<aTexture.height; y++) {
for (int x=0; x<aTexture.width; x++) {
int x_1 = x-1;
if(x_1 < 0) x_1 = aTexture.width - 1; // repeat the texture so use the opposit side
int x1 = x+1;
if(x1 >= aTexture.width) x1 = 0; // repeat the texture so use the opposit side
int y_1 = y-1;
if(y_1 < 0) y_1 = aTexture.height - 1; // repeat the texture so use the opposit side
int y1 = y+1;
if(y1 >= aTexture.height) y1 = 0; // repeat the texture so use the opposit side
float grayX_1 = pixels[(y * aTexture.width) + x_1].GrayScale();
float grayX1 = pixels[(y * aTexture.width) + x1].GrayScale();
float grayY_1 = pixels[(y_1 * aTexture.width) + x].GrayScale();
float grayY1 = pixels[(y1 * aTexture.width) + x].GrayScale();
Vector3 vx = new Vector3(0, 1, (grayX_1 - grayX1) * strength);
Vector3 vy = new Vector3(1, 0, (grayY_1 - grayY1) * strength);
Vector3 n = Vector3.Cross(vy, vx).normalized;
nPixels[(y * aTexture.width) + x] = (Vector4)((n + Vector3.one) * 0.5f);
}
}
normalTexture.SetPixels(nPixels, 0);
normalTexture.Apply(true);
return normalTexture;
}
public Texture2D NormalMapToUnityFormat(Texture2D aTexture) {
Texture2D normalTexture = new Texture2D(aTexture.width, aTexture.height, TextureFormat.ARGB32, aTexture.mipmapCount > 1);
Color[] pixels = aTexture.GetPixels(0);
Color[] nPixels = new Color[pixels.Length];
for (int y=0; y<aTexture.height; y++) {
for (int x=0; x<aTexture.width; x++) {
Color p = pixels[(y * aTexture.width) + x];
Color np = new Color(0,0,0,0);
np.r = p.g;
np.g = p.g; // waste of memory space if you ask me
np.b = p.g;
np.a = p.r;
nPixels[(y * aTexture.width) + x] = np;
}
}
normalTexture.SetPixels(nPixels, 0);
normalTexture.Apply(true);
return normalTexture;
}
public void ApplyTextureAsNormalMap(Texture2D aTexture, Material aMaterial) {
if(IsNormalMap(aTexture)) {
Debug.Log("this is a normal map");
aMaterial.SetTexture("_BumpMap", NormalMapToUnityFormat(aTexture));
} else {
Debug.Log("this is not a normal map");
Texture2D normalTexture = ColorTextureToNormalMap(aTexture, scale);
aMaterial.SetTexture("_BumpMap", NormalMapToUnityFormat(normalTexture));
Texture2D.Destroy(normalTexture); // clean up the mess
}
}