You copied the Doc URL to your clipboard.

The C# script

You must place the C# script in the Unity Assets/Editor directory. This enables the script to add a new option to the GameObject menu in the Unity editor. Create this directory if it does not already exist.

The following code shows how to add a new option in the Unity editor:

[MenuItem("GameObject/World Space Normals Creator")]

static void CreateWorldSpaceNormals ()
	ScriptableWizard.DisplayWizard("Create World Space Normal",

The following image shows the GameObject menu option added by the script.

Figure 6-88 GameObject menu option added by the script

The class defined in the C# script derives from the Unity ScriptableWizard class and has access to some of its members. Derive from this class to create an editor wizard. Editor wizards are typically opened using a menu item.

In the OnWizardUpdate code, the helpString variable holds a help message that is displayed in the window that the wizard creates.

The isValid member is used to define when all the correct parameters are selected and the Create button is available. In this case the _currentObj member is checked to ensure it points to a valid object.

The fields of the Wizard windows are the public members of the class. In this case only the _currentObj is public so the Wizard window only has one field.

The following image shows the Custom Wizard window:

Figure 6-89 Custom Wizard window

When an object is selected and the Create button is clicked, the OnWizardCreate() function is called.

The OnWizardCreate() function performs the main work of the conversion.

To convert the normal, the tool creates a temporary camera that renders the new World space normal to a RenderTexture. To do this, the camera is set to orthographic mode and the layer of the object is changed to an unused level. This means it can render the object on its own, even if it is already part of the scene.

The following code shows how the camera is set up:

// Set antialiasing
QualitySettings.antiAliasing = 4;
Shader wns = Shader.Find ("Custom/WorldSpaceNormalCreator");
GameObject go = new GameObject( "WorldSpaceNormalsCam", typeof(Camera) );
_renderCamera = go.GetComponent<Camera> ();
_renderCamera.orthographic = true;
_renderCamera.nearClipPlane = 0.0f;
_renderCamera.farClipPlane = 10f;
_renderCamera.orthographicSize = 1.0f;
int prevObjLayer = _currentObj.layer;
_currentObj.layer = 30; //0x40000000 

The script sets a replacement shader that executes the conversion:

_renderCamera.SetReplacementShader (wns,null); 
_renderCamera.useOcclusionCulling = false;

The camera is pointed at the object. This prevents the object being removed from rendering during frustum culling:

_renderCamera.transform.rotation = Quaternion.LookRotation (_currentObj.transform.position -

For each material assigned to the object, the script locates the _BumpMap texture. This texture is set as source texture for the replacement shader using the shader global functions.

The clear color is set to (0.5,0.5,0.5) because normals pointing at negative directions must be represented.

foreach (Material m in materials) 
	Texture t = m.GetTexture("_BumpMap");
	if( t == null )
	   Debug.LogError("the material has no texture assigned named Bump Map");
	Shader.SetGlobalTexture ("_BumpMapGlobal", t);
	RenderTexture rt = new RenderTexture(t.width,t.height,1);
	_renderCamera.targetTexture = rt;
	_renderCamera.pixelRect = new Rect(0,0,t.width,t.height);	
	_renderCamera.backgroundColor = new Color( 0.5f, 0.5f, 0.5f);
	_renderCamera.clearFlags = CameraClearFlags.Color;
	_renderCamera.cullingMask = 0x40000000;
	Shader.SetGlobalTexture ("_BumpMapGlobal", null);

After the camera renders the scene, the pixels are read back and saved as a PNG image.

	Texture2D outTex = new Texture2D(t.width,t.height); = rt;
	outTex.ReadPixels(new Rect(0,0,t.width,t.height), 0, 0);
	outTex.Apply(); = null;
	byte[] _pixels = outTex.EncodeToPNG();

The camera culling mask uses a binary mask represented in hexadecimal format, to specify what layers to render.

In this case layer 30 was used:

_currentObj.layer = 30;

The hexadecimal is 0x40000000 because its 30th bit is set to 1.

Was this page helpful? Yes No