playMaker

Author Topic: Realtime Cubemaps  (Read 6096 times)

Krileon

  • Full Member
  • ***
  • Posts: 107
Realtime Cubemaps
« on: December 31, 2013, 09:41:37 AM »
THIS WILL ONLY WORK IN UNITY PRO

Had to make that warning pretty clear. This is a Unity limitation so there is no workaround for this.

Ok, so I've been looking into realtime reflections and the best I could find at the moment is realtime cubemap generation. The alternative is a post processing affect that pulls from buffer, but that's above my current experience.

The below PlayMaker action will generate cubemaps at realtime for the target object and update the shader as needed. It has built in optimization to prevent performance from dieing. Basically what it does is on start will populate all 6 sides then every 5 frames it'll populate 1 face. This keeps performance from going to crap while still giving realtime reflections. You can customize a lot of the camera properties from the playmaker action as well as debug it (helps to make sure your positioning is correct).

The camera generated to pull the cubemaps is added as a child object of the target object so all position/rotation supplied is local. This lets the cubemap generation follow the object (cars, marbles, etc..).

The action can also be used as a fire and forget usage. Meaning it'll generate the 6 sides then destroy the camera and end the action. This is good for static objects that you're reusing throughout your level and need each with their own cubemap.

Code: [Select]
using UnityEngine;

namespace HutongGames.PlayMaker.Actions {
[ActionCategory(ActionCategory.Camera)]
public class CameraRenderToCubemap : FsmStateAction {
[RequiredField]
public FsmOwnerDefault targetGameObject;
[ActionSection("Camera")]
public FsmVector3 position;
public FsmQuaternion rotation;
public FsmColor backgroundColor;
[RequiredField, HasFloatSlider(1,179)]
public FsmFloat fieldOfView;
[RequiredField]
public FsmFloat nearCipPlane;
[RequiredField]
public FsmFloat farClipPlane;
public FsmBool HDR;
[ActionSection("Culling")]
[UIHint(UIHint.Layer)]
public FsmInt[] cullingMask;
public FsmBool invertMask;
[ActionSection("Cubemap")]
[RequiredField]
public FsmInt cubemapSize;
[RequiredField]
public FsmString[] namedCubemaps;
[ActionSection("Realtime")]
public bool everyFrame;
public int frameDelay;
public bool oneFacePerFrame;
public bool debugCamera;

private GameObject target;
private GameObject camera;
private RenderTexture texture;
private int frames;
private int[] faces;
private int face;

public override void Reset() {
targetGameObject = null;
position = new FsmVector3 { UseVariable = true };
rotation = new FsmQuaternion { UseVariable = true };
backgroundColor = new FsmColor { UseVariable = true };
fieldOfView = new FsmFloat { Value = 60f };
nearCipPlane = new FsmFloat { Value = 0.01f };
farClipPlane = new FsmFloat { Value = 100f };
HDR = new FsmBool { Value = false };
namedCubemaps = new FsmString[1];
namedCubemaps[0] = new FsmString { Value = "_Cube" };
cubemapSize = new FsmInt { Value = 128 };
cullingMask = new FsmInt[0];
invertMask = false;
everyFrame = true;
frameDelay = 0;
oneFacePerFrame = true;
debugCamera = false;

faces = new int[6];
faces[0] = ( 1 << (int) CubemapFace.NegativeX ); // 2
faces[1] = ( 1 << (int) CubemapFace.NegativeY ); // 8
faces[2] = ( 1 << (int) CubemapFace.NegativeZ ); // 32
faces[3] = ( 1 << (int) CubemapFace.PositiveX ); // 1
faces[4] = ( 1 << (int) CubemapFace.PositiveY ); // 4
faces[5] = ( 1 << (int) CubemapFace.PositiveZ ); // 16

frames = 0;
face = 0;
}

public override void OnEnter() {
target = Fsm.GetOwnerDefaultTarget( targetGameObject );

if ( target == null ) {
return;
}

camera = new GameObject( "CubemapCamera", typeof( Camera ) );
camera.hideFlags = ( debugCamera ? HideFlags.DontSave : HideFlags.HideAndDontSave );
camera.transform.parent = target.transform;
camera.transform.localPosition = position.Value;
camera.transform.localRotation = rotation.Value;

camera.camera.backgroundColor = backgroundColor.Value;
camera.camera.cullingMask = ActionHelpers.LayerArrayToLayerMask( cullingMask, invertMask.Value );
camera.camera.fieldOfView = fieldOfView.Value;
camera.camera.nearClipPlane = Mathf.Min( 0.01f, nearCipPlane.Value );
camera.camera.farClipPlane = farClipPlane.Value;
camera.camera.hdr = HDR.Value;
camera.camera.enabled = false;

texture = new RenderTexture( cubemapSize.Value, cubemapSize.Value, 16 );
texture.isCubemap = true;
texture.hideFlags = HideFlags.HideAndDontSave;

if ( namedCubemaps.Length > 0 ) foreach ( FsmString namedCubemap in namedCubemaps ) {
target.renderer.material.SetTexture( namedCubemap.Value, texture );
}

camera.camera.RenderToCubemap( texture, 63 );

if ( ! everyFrame ) {
Object.Destroy( camera );
Finish();
}
}

public override void OnLateUpdate() {
if ( ( target == null ) || ( camera == null ) ) {
return;
}

if ( frameDelay > 0 ) {
if ( frames == frameDelay ) {
frames = 0;
} else {
frames++;
return;
}
}

if ( oneFacePerFrame ) {
camera.camera.RenderToCubemap( texture, faces[face] );

if ( face == 5 ) {
face = 0;
} else {
face++;
}
} else {
camera.camera.RenderToCubemap( texture, 63 );
}
}

public override void OnExit() {
if ( camera == null ) {
return;
}

Object.Destroy( camera );
}
}
}

Please understand I do not have time to provide support. I'm providing this as it was a nightmare for me to figure this out on my own and Unity documentation sucks. So hopefully this will reduce the pain and suffering for another.
« Last Edit: December 31, 2013, 09:46:22 AM by Krileon »

airleeeeee

  • Playmaker Newbie
  • *
  • Posts: 2
Re: Realtime Cubemaps
« Reply #1 on: January 07, 2014, 03:14:28 AM »
Please "Realtime Cubemaps" give up the sample files designed to appreciate it.

Lane

  • Administrator
  • Hero Member
  • *****
  • Posts: 2511
  • Mender of the past
    • Cleverous
Re: Realtime Cubemaps
« Reply #2 on: January 07, 2014, 09:38:33 AM »
I got it to work, but maybe I'm misunderstanding what it does?

It needs a mesh renderer (or any renderer) component on the target object and updates the Reflection Cubemap in its material.

But it doesn't update the actual material which means it's only good for the one target object... That's still pretty cool, but seems like it has limited application when you can only target one object. If this had fed back into the original material then it would be significantly more capable.
« Last Edit: January 07, 2014, 01:17:35 PM by Lane »
Products by Cleverous
|| Vault Core : Database
|| Vault Inventory : Multiplayer Inventory
|| Vault Attributes : Character Stats
|| That Hurt! : Dmg Floaties
|| Quinn : 3D

Krileon

  • Full Member
  • ***
  • Posts: 107
Re: Realtime Cubemaps
« Reply #3 on: January 25, 2014, 01:39:13 PM »
Please "Realtime Cubemaps" give up the sample files designed to appreciate it.
What sample files? It's just an action that adds the camera, creates the cubemap, then maps it to your material. Use it however you want.


It needs a mesh renderer (or any renderer) component on the target object and updates the Reflection Cubemap in its material.

That's exactly what it's supposed to do.


But it doesn't update the actual material which means it's only good for the one target object... That's still pretty cool, but seems like it has limited application when you can only target one object. If this had fed back into the original material then it would be significantly more capable.
I don't feed into the material, because then it modifies the material. The point is for it to function at runtime only without modifying the material (prefab by prefab usage). If you want it to feed into the material then modify it to do so. Setting it to run once is the best way to have prefabs that you can reuse anywhere with a accurate cubemap based off their location. The alternative is you need to make a cubemap for every single prefab for each location it's going to be in, which is annoying to do. If you feed into the material then you change the cubemap for every prefab using that material, which doesn't make sense if those prefabs are in completely different locations.