Skip to content

Instantly share code, notes, and snippets.

@Farfarer
Created February 12, 2013 10:40
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Farfarer/4761486 to your computer and use it in GitHub Desktop.
Save Farfarer/4761486 to your computer and use it in GitHub Desktop.
Combines a base and a detail normal map to a new normal map texture, for Unity3D.
using UnityEditor;
using UnityEngine;
using System.IO;
// PLACE IN EDITOR FOLDER
class CombineNormalMapsWizard : ScriptableWizard {
public Texture2D baseNormal = null;
public Texture2D detailNormal = null;
public float detailStrength = 1.0f;
[MenuItem ("Tools/Combine Normal Maps")]
static void CreateWizard () {
ScriptableWizard.DisplayWizard<CombineNormalMapsWizard>("Combine Normal Maps", "Combine");
}
void OnWizardCreate () {
bool goodToGo = true;
if ( ( baseNormal == null ) || ( detailNormal == null ) )
{
Debug.LogWarning ( "You must specify a base and a detail normal map.");
goodToGo = false;
}
else if ( ( baseNormal.height != detailNormal.height ) || ( baseNormal.width != detailNormal.width ) )
{
Debug.LogWarning ( "Base and detail textures must be the same dimensions.");
goodToGo = false;
}
if (goodToGo) {
string path;
TextureImporter textureImporter;
bool baseNormalReadable;
bool baseNormalNormalmap;
TextureImporterFormat baseNormalFormat;
bool detailNormalReadable;
bool detailNormalNormalmap;
TextureImporterFormat detailNormalFormat;
// Set up textures as we want them.
path = AssetDatabase.GetAssetPath(baseNormal);
textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
baseNormalReadable = textureImporter.isReadable;
baseNormalNormalmap = textureImporter.normalmap;
baseNormalFormat = textureImporter.textureFormat;
textureImporter.isReadable = true;
textureImporter.normalmap = false;
textureImporter.textureFormat = TextureImporterFormat.RGBA32;
AssetDatabase.ImportAsset(path);
path = AssetDatabase.GetAssetPath(detailNormal);
textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
detailNormalReadable = textureImporter.isReadable;
detailNormalNormalmap = textureImporter.normalmap;
detailNormalFormat = textureImporter.textureFormat;
textureImporter.isReadable = true;
textureImporter.normalmap = false;
textureImporter.textureFormat = TextureImporterFormat.RGBA32;
AssetDatabase.ImportAsset(path);
Color[] detailNormalPixels;
Color[] combinedNormalPixels;
int width = baseNormal.width;
int height = baseNormal.height;
combinedNormalPixels = baseNormal.GetPixels (0);
detailNormalPixels = detailNormal.GetPixels (0);
int numPixels = width * height;
// Do the conversion.
for (int i = 0; i < numPixels; ++i)
{
// Method from http://blog.selfshadow.com/publications/blending-in-detail/ used.
// I find it gives the nicest results.
Vector3 t = new Vector3 (combinedNormalPixels[i].r * 2.0f - 1.0f, combinedNormalPixels[i].g * 2.0f - 1.0f, combinedNormalPixels[i].b * 2.0f);
Vector3 u = new Vector3 (detailNormalPixels[i].r * -2.0f + 1.0f, detailNormalPixels[i].g * -2.0f + 1.0f, detailNormalPixels[i].b * 2.0f - 1);
if ( detailStrength != 1.0f )
{
u = new Vector3 ( u.x * detailStrength, u.y * detailStrength, u.z );
u.Normalize();
}
Vector3 r = t * Vector3.Dot(t, u) - u * t.z;
r.Normalize ();
Color returnColor = new Color (r.x * 0.5f + 0.5f, r.y * 0.5f + 0.5f, r.z * 0.5f + 0.5f, combinedNormalPixels[i].a);
combinedNormalPixels[i] = returnColor;
}
Texture2D normal = new Texture2D(width, height, TextureFormat.ARGB32, false);
normal.SetPixels (combinedNormalPixels);
normal.Apply();
byte[] bytes = normal.EncodeToPNG();
DestroyImmediate(normal);
string assetPath = AssetDatabase.GetAssetPath(baseNormal);
string assetDir = Path.GetDirectoryName(assetPath);
string assetName = Path.GetFileNameWithoutExtension(assetPath) + "_combined.png";
string newAsset = Path.Combine(assetDir, assetName);
File.WriteAllBytes(newAsset, bytes);
// Revert textures back to the way they were.
path = AssetDatabase.GetAssetPath(baseNormal);
textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
textureImporter.isReadable = baseNormalReadable;
textureImporter.normalmap = baseNormalNormalmap;
textureImporter.textureFormat = baseNormalFormat;
AssetDatabase.ImportAsset(path);
path = AssetDatabase.GetAssetPath(detailNormal);
textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
textureImporter.isReadable = detailNormalReadable;
textureImporter.normalmap = detailNormalNormalmap;
textureImporter.textureFormat = detailNormalFormat;
AssetDatabase.ImportAsset(path);
// Import the new texture, set as a normal map.
AssetDatabase.ImportAsset(newAsset);
textureImporter = AssetImporter.GetAtPath(newAsset) as TextureImporter;
textureImporter.textureType = TextureImporterType.Bump;
textureImporter.normalmap = true;
textureImporter.convertToNormalmap = false;
AssetDatabase.ImportAsset(newAsset);
Debug.Log ("Normal maps combined to " + newAsset);
}
}
void OnWizardUpdate () {
helpString = "Press Combine to combine normal maps.\nSaved to folder with base normal map.";
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment