Skip to content

Instantly share code, notes, and snippets.

@radiatoryang
Created May 27, 2013 23:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save radiatoryang/5659480 to your computer and use it in GitHub Desktop.
Save radiatoryang/5659480 to your computer and use it in GitHub Desktop.
Use with the rest of the EnvCubemap*.cs scripts to implement an environment probe system in your Unity project, with models that'll automatically fetch the closest visible probe. For more info, see: http://www.blog.radiator.debacle.us/2013/05/cubemapped-environment-probes-source.html
using UnityEngine;
using System.Collections;
public class EnvCubemap : MonoBehaviour {
public Cubemap cubemap;
public int cubemapRes = 128; // powers of two only
public CameraClearFlags clearFlags = CameraClearFlags.Color;
public Color clearColor = Color.black;
public LayerMask cullingMask;
public float near = 0.1f;
public float far = 10000f;
public void KillThis( GameObject go ) {
DestroyImmediate( go );
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
class CubemapUtility {
[MenuItem( "Cubemaps/Bake all static cubemaps" )]
static void BakeCubemaps() {
// default path to save cubemaps if the scene hasn't been saved yet
string assetPath = "Assets/radiator/cubemaps/";
// ... otherwise, save the cubemaps in a folder next to the scene;
// NOTE: this will NOT delete the existing cubemaps, so you might end up having to clean the folder yourself
if (EditorApplication.currentScene != "") {
List<string> pathTemp = new List<string>(EditorApplication.currentScene.Split(char.Parse("/")));
pathTemp[pathTemp.Count-1] = pathTemp[pathTemp.Count-1].Substring(0, pathTemp[pathTemp.Count-1].Length - 6);
assetPath = string.Join( "/", pathTemp.ToArray() ) + "/";
}
// begin baking cubemaps
float progress = 0f; // how far along we are in the whole baking process, from 0-1
int counter = 0; // number of cubemaps we've processed so far
EnvCubemap[] cubemaps = GameObject.FindObjectsOfType( typeof( EnvCubemap ) ) as EnvCubemap[]; // grab all cubemaps in the scene
foreach ( EnvCubemap env_cubemap in cubemaps ) {
EditorUtility.DisplayProgressBar("Baking cubemaps...", "Baking cubemap at " + env_cubemap.transform.position.ToString(), progress);
if (env_cubemap.enabled) {
Cubemap cube = BakeCubemapStatic(env_cubemap);
env_cubemap.cubemap = cube; // I probably should've named these variables better
string cubeAsset = assetPath + cube.name + ".cubemap"; // cubemap files must end with .cubemap file extension
AssetDatabase.CreateAsset( cube, cubeAsset);
EditorUtility.SetDirty( cube ); // use SetDirty() to tell Unity to save new info
EditorUtility.SetDirty( env_cubemap );
}
counter++; // we finished processing a cubemap! yay!
progress = (counter * 1f) / cubemaps.Length; // multiply by 1f to cast int to a float
}
EditorUtility.ClearProgressBar();
}
static Cubemap BakeCubemapStatic(EnvCubemap env_cubemap) {
// create new cubemap
Cubemap cubemap = new Cubemap( env_cubemap.cubemapRes, TextureFormat.RGB24, true );
// create temporary camera for rendering
var go = new GameObject( "CubemapCamera", typeof( Camera ) );
// place it on the object
go.transform.position = env_cubemap.transform.position;
go.transform.rotation = Quaternion.identity;
// render into cubemap
go.camera.near = env_cubemap.near;
go.camera.far = env_cubemap.far;
go.camera.clearFlags = env_cubemap.clearFlags;
go.camera.backgroundColor = env_cubemap.clearColor;
go.camera.cullingMask = env_cubemap.cullingMask;
go.camera.RenderToCubemap( cubemap, 63 );
// destroy temporary camera
env_cubemap.KillThis( go );
// some cubemappy stuff
cubemap.SmoothEdges();
cubemap.mipMapBias = 0.5f;
cubemap.name = env_cubemap.cubemapRes.ToString() + "@" + env_cubemap.transform.position.ToString();
cubemap.Apply( true, false );
return cubemap;
}
// if the cubemap baker crashes, you'll have to use this to clear the progress bar
[MenuItem( "Cubemaps/Debug, clear progress bar" )]
static void Clear() {
EditorUtility.ClearProgressBar();
}
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class EnvCubemapCatcher : MonoBehaviour {
public EnvCubemap cubemap;
public Renderer myRenderer; // optional, assign in inspector
public static List<EnvCubemap> allCubemaps = new List<EnvCubemap>();
public bool useOnlyLosCubemaps = false; // if true: use only cubemaps with LoS... more expensive, must raycast
public LayerMask losMask; // the raycast mask used to determine if cubemap has LoS to this script
void Awake() {
allCubemaps.Clear();
}
// Use this for initialization
void Start () {
if ( !myRenderer )
myRenderer = renderer;
if ( allCubemaps.Count == 0 ) {
RefreshCubemapList();
}
StartCoroutine( UpdateCubemaps() );
}
public static void RefreshCubemapList() { // could be called in-game, perhaps, too?
allCubemaps.AddRange( GameObject.FindObjectsOfType( typeof( EnvCubemap ) ) as EnvCubemap[] );
Debug.Log( "EnvCubemapCatcher.Refresh(), grabbed " + allCubemaps.Count + " env_cubemaps!" );
}
IEnumerator UpdateCubemaps() {
const float timestep = 0.5f; // cubemap detection doesn't have to happen that frequently
while ( true ) {
// cache old env_cubemap
EnvCubemap oldCubemap = cubemap;
// find the closest env_cubemap
float closestSqrMagnitude = 100000f;
foreach ( EnvCubemap cube in allCubemaps ) {
// use a sqrMagnitude check as the cheapest possible early-out
float sqrMagnitude = (cube.transform.position - transform.position).sqrMagnitude;
if ( sqrMagnitude < closestSqrMagnitude && (!useOnlyLosCubemaps || !Physics.Raycast(transform.position, cube.transform.position - transform.position, Mathf.Sqrt(sqrMagnitude), losMask ) ) ) {
closestSqrMagnitude = sqrMagnitude;
cubemap = cube;
}
}
if ( myRenderer ) {
if ( oldCubemap != cubemap ) { // if the new cubemap doesn't match the old one, change it
Debug.DrawLine( transform.position, cubemap.transform.position, Color.magenta, 1f );
myRenderer.material.SetTexture( "_Cube", cubemap.cubemap );
}
} else {
Debug.LogWarning( "EnvCubemapCatcher can't find its renderer! " + transform.position.ToString() );
}
yield return new WaitForSeconds(timestep);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment