• Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
This post has been wikified, any user with enough reputation can edit it.
avatar image
Question by Iridescence · Feb 10, 2014 at 03:42 PM · shaderreflectionclipping plane

How to control the FarClipPlane of a reflection camera in Unity3d (4.3f1)

Okay, so I have the following scenario: I used [http://wiki.unity3d.com/index.php?title=MirrorReflection2][1] to add a mirror to my scene. Then I noticed that the amount of draw-calls skyrocketed to insane numbers (about the entire amount of draw-calls required to draw the entire scene).

I noticed that the reflection script generates a temporary camera to render the reflection texture, so I tried to lower the Far Clip Plane of that camera (in code) but it had no effect.

I've made a dozen attempts at reducing the Far Clip Plane and playing around with occlusion culling for that camera however it seems resistant to my changes. (I saw that the reflection camera was being recreated every frame so I tried caching it, but that didn't help either)

My updated code is :

 using UnityEngine;
 using System.Collections;
 
 // This is in fact just the Water script from Pro Standard Assets,
 // just with refraction stuff removed.
 
 [ExecuteInEditMode] // Make mirror live-update even when not in play mode
 public class MirrorReflection : MonoBehaviour
 {
     public Camera ReflectionCamera;
     public bool m_DisablePixelLights = true;
     public int m_TextureSize = 1024;
     public float m_ClipPlaneOffset = 0.07f;
     
     public LayerMask m_ReflectLayers = -1;
         
     private Hashtable m_ReflectionCameras = new Hashtable(); // Camera -> Camera table
     
     private RenderTexture m_ReflectionTexture = null;
     private int m_OldReflectionTextureSize = 0;
     
     private static bool s_InsideRendering = false;
 
     // This is called when it's known that the object will be rendered by some
     // camera. We render reflections and do other updates here.
     // Because the script executes in edit mode, reflections for the scene view
     // camera will just work!
     public void OnWillRenderObject()
     {
         if( !enabled || !renderer || !renderer.sharedMaterial || !renderer.enabled )
             return;
             
         Camera cam = Camera.current;
         if( !cam )
             return;
     
         // Safeguard from recursive reflections.        
         if( s_InsideRendering )
             return;
         s_InsideRendering = true;
         
         Camera reflectionCamera;
         if(m_ReflectionCameras.ContainsKey(cam))
         {
             reflectionCamera = m_ReflectionCameras[cam] as Camera;
         }
         else
         {
             Debug.Log("Creating and Updating Reflection Camera");
             CreateMirrorObjects(cam, out reflectionCamera);
             UpdateCameraModes( cam, reflectionCamera );
 
             reflectionCamera.farClipPlane = 4.0f;
             m_ReflectionCameras[cam] = reflectionCamera;
             this.ReflectionCamera = reflectionCamera;
         }
         
         // find out the reflection plane: position and normal in world space
         Vector3 pos = transform.position;
         Vector3 normal = transform.up;
         
         // Optionally disable pixel lights for reflection
         int oldPixelLightCount = QualitySettings.pixelLightCount;
         if( m_DisablePixelLights )
             QualitySettings.pixelLightCount = 0;
         
         //UpdateCameraModes( cam, reflectionCamera );
         
         // Render reflection
         // Reflect camera around reflection plane
         float d = -Vector3.Dot (normal, pos) - m_ClipPlaneOffset;
         Vector4 reflectionPlane = new Vector4 (normal.x, normal.y, normal.z, d);
     
         Matrix4x4 reflection = Matrix4x4.zero;
         CalculateReflectionMatrix (ref reflection, reflectionPlane);
         Vector3 oldpos = cam.transform.position;
         Vector3 newpos = reflection.MultiplyPoint( oldpos );
         reflectionCamera.worldToCameraMatrix = cam.worldToCameraMatrix * reflection;
     
         // Setup oblique projection matrix so that near plane is our reflection
         // plane. This way we clip everything below/above it for free.
         Vector4 clipPlane = CameraSpacePlane( reflectionCamera, pos, normal, 1.0f );
         Matrix4x4 projection = cam.projectionMatrix;
         //CalculateObliqueMatrix (ref projection, clipPlane);
         reflectionCamera.projectionMatrix = projection;
         
         reflectionCamera.cullingMask = ~(1<<4) & m_ReflectLayers.value; // never render water layer
         reflectionCamera.targetTexture = m_ReflectionTexture;
         GL.SetRevertBackfacing (true);
         reflectionCamera.transform.position = newpos;
         Vector3 euler = cam.transform.eulerAngles;
         reflectionCamera.transform.eulerAngles = new Vector3(0, euler.y, euler.z);
         Debug.Log(reflectionCamera.farClipPlane);
         reflectionCamera.Render();
         reflectionCamera.transform.position = oldpos;
         GL.SetRevertBackfacing (false);
         Material[] materials = renderer.sharedMaterials;
         foreach( Material mat in materials ) {
             if( mat.HasProperty("_ReflectionTex") )
                 mat.SetTexture( "_ReflectionTex", m_ReflectionTexture );
         }
         
         // Set matrix on the shader that transforms UVs from object space into screen
         // space. We want to just project reflection texture on screen.
         Matrix4x4 scaleOffset = Matrix4x4.TRS(
             new Vector3(0.5f,0.5f,0.5f), Quaternion.identity, new Vector3(0.5f,0.5f,0.5f) );
         Vector3 scale = transform.lossyScale;
         Matrix4x4 mtx = transform.localToWorldMatrix * Matrix4x4.Scale( new Vector3(1.0f/scale.x, 1.0f/scale.y, 1.0f/scale.z) );
         mtx = scaleOffset * cam.projectionMatrix * cam.worldToCameraMatrix * mtx;
         foreach( Material mat in materials ) {
             mat.SetMatrix( "_ProjMatrix", mtx );
         }
         
         // Restore pixel light count
         if( m_DisablePixelLights )
             QualitySettings.pixelLightCount = oldPixelLightCount;
         
         s_InsideRendering = false;
     }
     
     
     // Cleanup all the objects we possibly have created
     void OnDisable()
     {
         if( m_ReflectionTexture ) {
             DestroyImmediate( m_ReflectionTexture );
             m_ReflectionTexture = null;
         }
         foreach( DictionaryEntry kvp in m_ReflectionCameras )
             DestroyImmediate( ((Camera)kvp.Value).gameObject );
         m_ReflectionCameras.Clear();
     }
     
     
     private void UpdateCameraModes( Camera src, Camera dest )
     {
         if( dest == null )
             return;
         // set camera to clear the same way as current camera
         dest.clearFlags = src.clearFlags;
         dest.backgroundColor = src.backgroundColor;        
         if( src.clearFlags == CameraClearFlags.Skybox )
         {
             Skybox sky = src.GetComponent(typeof(Skybox)) as Skybox;
             Skybox mysky = dest.GetComponent(typeof(Skybox)) as Skybox;
             if( !sky || !sky.material )
             {
                 mysky.enabled = false;
             }
             else
             {
                 mysky.enabled = true;
                 mysky.material = sky.material;
             }
         }
         // update other values to match current camera.
         // even if we are supplying custom camera&projection matrices,
         // some of values are used elsewhere (e.g. skybox uses far plane)
         dest.farClipPlane = src.farClipPlane;
         dest.nearClipPlane = src.nearClipPlane;
         dest.orthographic = src.orthographic;
         dest.fieldOfView = src.fieldOfView;
         dest.aspect = src.aspect;
         dest.orthographicSize = src.orthographicSize;
     }
     
     // On-demand create any objects we need
     private void CreateMirrorObjects( Camera currentCamera, out Camera reflectionCamera )
     {
         reflectionCamera = null;
         
         // Reflection render texture
         if( !m_ReflectionTexture || m_OldReflectionTextureSize != m_TextureSize )
         {
             if( m_ReflectionTexture )
                 DestroyImmediate( m_ReflectionTexture );
             m_ReflectionTexture = new RenderTexture( m_TextureSize, m_TextureSize, 16 );
             m_ReflectionTexture.name = "__MirrorReflection" + GetInstanceID();
             m_ReflectionTexture.isPowerOfTwo = true;
             m_ReflectionTexture.hideFlags = HideFlags.DontSave;
             m_OldReflectionTextureSize = m_TextureSize;
         }
         
         // Camera for reflection
         reflectionCamera = m_ReflectionCameras[currentCamera] as Camera;
         if( !reflectionCamera ) // catch both not-in-dictionary and in-dictionary-but-deleted-GO
         {
             GameObject go = new GameObject( "Mirror Refl Camera id" + GetInstanceID() + " for " + currentCamera.GetInstanceID(), typeof(Camera), typeof(Skybox) );
             reflectionCamera = go.camera;
             reflectionCamera.enabled = false;
             reflectionCamera.transform.position = transform.position;
             reflectionCamera.transform.rotation = transform.rotation;
             reflectionCamera.gameObject.AddComponent("FlareLayer");
             go.hideFlags = HideFlags.HideAndDontSave;
             m_ReflectionCameras[currentCamera] = reflectionCamera;
         }        
     }
     
     // Extended sign: returns -1, 0 or 1 based on sign of a
     private static float sgn(float a)
     {
         if (a > 0.0f) return 1.0f;
         if (a < 0.0f) return -1.0f;
         return 0.0f;
     }
     
     // Given position/normal of the plane, calculates plane in camera space.
     private Vector4 CameraSpacePlane (Camera cam, Vector3 pos, Vector3 normal, float sideSign)
     {
         Vector3 offsetPos = pos + normal * m_ClipPlaneOffset;
         Matrix4x4 m = cam.worldToCameraMatrix;
         Vector3 cpos = m.MultiplyPoint( offsetPos );
         Vector3 cnormal = m.MultiplyVector( normal ).normalized * sideSign;
         return new Vector4( cnormal.x, cnormal.y, cnormal.z, -Vector3.Dot(cpos,cnormal) );
     }
     
     // Adjusts the given projection matrix so that near plane is the given clipPlane
     // clipPlane is given in camera space. See article in Game Programming Gems 5.
     private static void CalculateObliqueMatrix (ref Matrix4x4 projection, Vector4 clipPlane)
     {
         Vector4 q = projection.inverse * new Vector4(
             sgn(clipPlane.x),
             sgn(clipPlane.y),
             1.0f,
             1.0f
         );
         Vector4 c = clipPlane * (2.0F / (Vector4.Dot (clipPlane, q)));
         // third row = clip plane - fourth row
         projection[2] = c.x - projection[3];
         projection[6] = c.y - projection[7];
         projection[10] = c.z - projection[11];
         projection[14] = c.w - projection[15];
     }
 
     // Calculates reflection matrix around the given plane
     private static void CalculateReflectionMatrix (ref Matrix4x4 reflectionMat, Vector4 plane)
     {
         reflectionMat.m00 = (1F - 2F*plane[0]*plane[0]);
         reflectionMat.m01 = (   - 2F*plane[0]*plane[1]);
         reflectionMat.m02 = (   - 2F*plane[0]*plane[2]);
         reflectionMat.m03 = (   - 2F*plane[3]*plane[0]);
 
         reflectionMat.m10 = (   - 2F*plane[1]*plane[0]);
         reflectionMat.m11 = (1F - 2F*plane[1]*plane[1]);
         reflectionMat.m12 = (   - 2F*plane[1]*plane[2]);
         reflectionMat.m13 = (   - 2F*plane[3]*plane[1]);
     
         reflectionMat.m20 = (   - 2F*plane[2]*plane[0]);
         reflectionMat.m21 = (   - 2F*plane[2]*plane[1]);
         reflectionMat.m22 = (1F - 2F*plane[2]*plane[2]);
         reflectionMat.m23 = (   - 2F*plane[3]*plane[2]);
 
         reflectionMat.m30 = 0F;
         reflectionMat.m31 = 0F;
         reflectionMat.m32 = 0F;
         reflectionMat.m33 = 1F;
     }
 }

The Debug.Log line near the reflectionCamera.render(...); prints 4 as expected, but neither the amount of draw-calls nor what is viewed in the mirror change.

Thanks in Advance, Nathan Dortman

Comment

People who like this

0 Show 0
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image

Answer by Daniel-Williams · Aug 10, 2016 at 07:54 PM

This solution added 50 fps to my game: Inside the default water script, you can set the layerCullDistances to smaller numbers, then enable layerCullSpherical. Do this for every reflection camera.

  Vector4 clipPlane = CameraSpacePlane(reflectionCamera, pos, normal, 1.0f);
  reflectionCamera.projectionMatrix = cam.CalculateObliqueMatrix(clipPlane);
  
  float[] distances = new float[32];
  for (int i = 0; i < distances.Length; i++) {
      distances[i] = 20f; //the culling distance
  }
  reflectionCamera.layerCullDistances = distances;
  reflectionCamera.layerCullSpherical = true;
  reflectionCamera.cullingMask = ~(1 << 4) & reflectLayers.value; // never render water layer
  //rest of script

Comment
renem

People who like this

1 Show 0 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

Follow this Question

Answers Answers and Comments

19 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

can i have reflections on a self illuminated bumped diffuse object ? 3 Answers

The name 'Joystick' does not denote a valid type ('not found') 2 Answers

How can I Control the Strength of the Reflection in the Pro FX Water Shader 4 Answers

Material doesn't have a color property '_Color' 4 Answers

Image Projector 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges