/// <summary> /// Render a reflection camera. /// </summary> /// <param name="info">Camera info</param> /// <param name="reflectionTransform">Reflection transform (parent of reflection camera)</param> /// <param name="reflectionNormal">Reflection normal vector (up for planes, forward for quads)</param> /// <param name="clipPlaneOffset">Clip plane offset for near clipping</param> /// <param name="stereoSeparationMultiplier">Stereo separation multiplier</param> public static void RenderReflection ( ReflectionCameraInfo info, Transform reflectionTransform, Vector3 reflectionNormal, float clipPlaneOffset, float stereoSeparationMultiplier ) { if (info.SourceCamera.stereoEnabled) { if (info.SourceCamera.stereoTargetEye == StereoTargetEyeMask.Both || info.SourceCamera.stereoTargetEye == StereoTargetEyeMask.Left) { RenderReflectionInternal(info, reflectionTransform, reflectionNormal, clipPlaneOffset, StereoTargetEyeMask.Left, info.TargetTexture, stereoSeparationMultiplier); } if (info.SourceCamera.stereoTargetEye == StereoTargetEyeMask.Both || info.SourceCamera.stereoTargetEye == StereoTargetEyeMask.Right) { RenderReflectionInternal(info, reflectionTransform, reflectionNormal, clipPlaneOffset, StereoTargetEyeMask.Right, info.TargetTexture2, stereoSeparationMultiplier); } } else { RenderReflectionInternal(info, reflectionTransform, reflectionNormal, clipPlaneOffset, StereoTargetEyeMask.Both, info.TargetTexture, stereoSeparationMultiplier); } }
/// <summary> /// Queue a reflection camera /// </summary> /// <param name="sourceCamera">Viewing camera</param> /// <returns>Reflection camera info</returns> public ReflectionCameraInfo QueueReflection(Camera sourceCamera) { bool isReflection; if (ReflectMaterial == null || sourceCamera == null || WeatherMakerLightManagerScript.Instance == null || ShouldIgnoreCamera(sourceCamera, out isReflection)) { return(null); } // HACK: Reflections need to ensure the frustum planes and corners are up to date. This is normally done in a pre-render // event, but we cannot do that as rendering a reflection in pre-render mucks up state and such WeatherMakerLightManagerScript.Instance.CalculateFrustumPlanes(sourceCamera); // this frustum cull test is expensive, but well worth it if it prevents a reflection from needing to be rendered // get up to date frustum info if (ReflectRenderer != null && !WeatherMakerGeometryUtility.BoxIntersectsFrustum(sourceCamera, WeatherMakerLightManagerScript.Instance.CurrentCameraFrustumPlanes, WeatherMakerLightManagerScript.Instance.CurrentCameraFrustumCorners, ReflectRenderer.bounds)) { return(null); } // Debug.LogFormat("WeatherMakerReflectionScript rendering in {0}, bounds: {1}", sourceCamera.name, ReflectMaterial.bounds); // start off some render textures for the reflection KeyValuePair <RenderTexture, RenderTexture> kv = new KeyValuePair <RenderTexture, RenderTexture> ( ReflectMaterial.GetTexture(ReflectionSamplerName) as RenderTexture, ReflectMaterial.GetTexture(ReflectionSamplerName2) as RenderTexture ); currentRenderTextures.Add(kv); ReflectionCameraInfo cam = CreateReflectionCamera(sourceCamera, isReflection); RenderReflectionCamera(cam); return(cam); }
private void RenderReflectionCamera(ReflectionCameraInfo info) { // bail if we don't have a camera or renderer if (info == null || info.ReflectionCamera == null || info.SourceCamera == null || ReflectRenderer == null || ReflectRenderer.sharedMaterial == null || !ReflectRenderer.enabled) { return; } CurrentRecursionLevel = currentCameras.Count + (info.SourceCameraIsReflection ? 1 : 0); renderCount++; Camera sourceCamera = info.SourceCamera; Camera reflectionCamera = info.ReflectionCamera; int oldPixelLightCount = QualitySettings.pixelLightCount; int oldCullingMask = reflectionCamera.cullingMask; bool oldSoftParticles = QualitySettings.softParticles; int oldAntiAliasing = QualitySettings.antiAliasing; ShadowQuality oldShadows = QualitySettings.shadows; SyncCameraSettings(reflectionCamera, sourceCamera); // MAGIC MIRROR RECURSION OPTIMIZATION if (currentCameras.Count > 1) { if (currentCameras.Count > 3) { QualitySettings.shadows = ShadowQuality.Disable; } QualitySettings.shadows = ShadowQuality.HardOnly; QualitySettings.antiAliasing = 0; QualitySettings.softParticles = false; QualitySettings.pixelLightCount = 0; reflectionCamera.cullingMask &= waterLayerInverse; } else { QualitySettings.pixelLightCount = MaximumPerPixelLightsToReflect; } Transform reflectionTransform = transform; Vector3 reflectionNormal = (NormalIsForward ? -reflectionTransform.forward : reflectionTransform.up); // use shared reflection render function RenderReflection(info, reflectionTransform, reflectionNormal, ClipPlaneOffset); // restore render state reflectionCamera.cullingMask = oldCullingMask; QualitySettings.pixelLightCount = oldPixelLightCount; QualitySettings.softParticles = oldSoftParticles; QualitySettings.antiAliasing = oldAntiAliasing; QualitySettings.shadows = oldShadows; ReflectRenderer.sharedMaterial.SetTexture(ReflectionSamplerName, info.TargetTexture); ReflectRenderer.sharedMaterial.SetTexture(ReflectionSamplerName2, info.TargetTexture2); ReflectRenderer.sharedMaterial.DisableKeyword(mirrorRecursionLimitKeyword); currentCameras.Remove(info); info.SourceCamera = null; info.TargetTexture = null; info.TargetTexture2 = null; cameraCache.Add(info); }
public ReflectionCameraInfo QueueReflection(Camera sourceCamera) { bool isReflection; if (ShouldIgnoreCamera(sourceCamera, out isReflection)) { return(null); } ReflectionCameraInfo cam = CreateReflectionCamera(sourceCamera, isReflection); RenderReflectionCamera(cam); return(cam); }
private void CleanupCamera(ReflectionCameraInfo info, bool destroyCamera) { if (info.ReflectionCamera == null) { return; } //info.ReflectCamera.targetTexture = null; if (destroyCamera #if UNITY_EDITOR && Application.isPlaying #endif ) { DestroyImmediate(info.ReflectionCamera.gameObject); } }
private ReflectionCameraInfo CreateReflectionCamera(Camera sourceCamera, bool sourceCameraIsReflection) { // don't render if we are not enabled if (ReflectRenderer == null || !ReflectRenderer.enabled || ReflectRenderer.sharedMaterial == null || sourceCamera == null) { return(null); } // only render reflection cameras with this script MagicMirrorScript reflScript = (sourceCameraIsReflection && sourceCamera.transform.parent != null ? sourceCamera.transform.parent.GetComponent <MagicMirrorScript>() : null); if (sourceCameraIsReflection && (reflScript == null || reflScript == this)) { // don't render ourselves in our camera ReflectRenderer.sharedMaterial.EnableKeyword(mirrorRecursionLimitKeyword); return(null); } // recursion limit hit, bail... if (reflScript != null && currentCameras.Count >= #if UNITY_EDITOR (Application.isPlaying ? RecursionLimit : 0) #else RecursionLimit #endif ) { ReflectRenderer.sharedMaterial.EnableKeyword(mirrorRecursionLimitKeyword); return(null); } ReflectionCameraInfo info; if (cameraCache.Count == 0) { GameObject obj = new GameObject("MirrorReflectionCamera"); obj.hideFlags = HideFlags.HideAndDontSave; obj.SetActive(false); obj.transform.parent = transform; Camera newReflectionCamera = obj.AddComponent <Camera>(); newReflectionCamera.enabled = false; info = new ReflectionCameraInfo { SourceCamera = sourceCamera, ReflectionCamera = newReflectionCamera }; } else { int idx = cameraCache.Count - 1; info = cameraCache[idx]; cameraCache.RemoveAt(idx); CleanupCamera(info, false); } info.SourceCamera = sourceCamera; info.SourceCameraIsReflection = sourceCameraIsReflection; int size = Math.Max(32, (int)(Mathf.Pow(RecursionRenderTextureSizeReducerPower, (float)CurrentRecursionLevel) * (float)RenderTextureSize)); info.TargetTexture = RenderTexture.GetTemporary(size, size, 16, RenderTextureFormat.DefaultHDR); info.TargetTexture.wrapMode = TextureWrapMode.Clamp; info.TargetTexture.filterMode = FilterMode.Bilinear; if (AntiAliasingSamples > AntiAliasingSampleCount.None) { info.TargetTexture.antiAliasing = (int)AntiAliasingSamples; } AddRenderTextureForSourceCamera(sourceCamera, info.TargetTexture, StereoTargetEyeMask.Left); if (sourceCamera.stereoEnabled) { info.TargetTexture2 = RenderTexture.GetTemporary(size, size, 16, RenderTextureFormat.DefaultHDR); info.TargetTexture2.wrapMode = TextureWrapMode.Clamp; info.TargetTexture2.filterMode = FilterMode.Bilinear; if (AntiAliasingSamples > AntiAliasingSampleCount.None) { info.TargetTexture2.antiAliasing = (int)AntiAliasingSamples; } AddRenderTextureForSourceCamera(sourceCamera, info.TargetTexture2, StereoTargetEyeMask.Right); } else { info.TargetTexture2 = info.TargetTexture; } currentCameras.Add(info); return(info); }
/// <summary> /// Render a reflection camera. Reflection camera should already be setup with a render texture. /// </summary> /// <param name="info">Camera info</param> /// <param name="reflectionTransform">Reflection transform</param> /// <param name="reflectionNormal">Reflection normal vector</param> /// <param name="clipPlaneOffset">Clip plane offset for near clipping</param> /// <param name="eye">Stereo eye mask</param> /// <param name="targetTexture">Target texture</param> private static void RenderReflectionInternal ( ReflectionCameraInfo info, Transform reflectionTransform, Vector3 reflectionNormal, float clipPlaneOffset, StereoTargetEyeMask eye, RenderTexture targetTexture ) { bool oldInvertCulling = GL.invertCulling; // find out the reflection plane: position and normal in world space Vector3 pos = reflectionTransform.position; // Render reflection // Reflect camera around reflection plane if (info.SourceCameraIsReflection && GL.invertCulling) { reflectionNormal = -reflectionNormal; } float d = -Vector3.Dot(reflectionNormal, pos) - clipPlaneOffset; Vector4 reflectionPlane = new Vector4(reflectionNormal.x, reflectionNormal.y, reflectionNormal.z, d); Matrix4x4 reflection; CalculateReflectionMatrix(out reflection, reflectionPlane); Vector3 savedPos = info.SourceCamera.transform.position; Vector3 reflectPos = reflection.MultiplyPoint(savedPos); Matrix4x4 worldToCameraMatrix = info.SourceCamera.worldToCameraMatrix; if (eye == StereoTargetEyeMask.Left) { worldToCameraMatrix[12] += (info.SourceCamera.stereoSeparation * 0.5f); info.ReflectionCamera.projectionMatrix = info.SourceCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left); } else if (eye == StereoTargetEyeMask.Right) { worldToCameraMatrix[12] -= (info.SourceCamera.stereoSeparation * 0.5f); info.ReflectionCamera.projectionMatrix = info.SourceCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Right); } else { info.ReflectionCamera.projectionMatrix = info.SourceCamera.projectionMatrix; } info.ReflectionCamera.worldToCameraMatrix = worldToCameraMatrix * reflection; if (info.ReflectionCamera.actualRenderingPath != RenderingPath.DeferredShading) { // Optimization: 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(info.ReflectionCamera, pos, reflectionNormal, clipPlaneOffset, GL.invertCulling ? -1.0f : 1.0f); info.ReflectionCamera.projectionMatrix = info.ReflectionCamera.CalculateObliqueMatrix(clipPlane); } GL.invertCulling = !GL.invertCulling; info.ReflectionCamera.transform.position = reflectPos; if (++renderCount < maxRenderCount) { info.ReflectionCamera.targetTexture = targetTexture; info.ReflectionCamera.Render(); info.ReflectionCamera.targetTexture = null; } info.ReflectionCamera.transform.position = savedPos; GL.invertCulling = oldInvertCulling; }
private void RenderReflectionCamera(ReflectionCameraInfo info) { // bail if we don't have a camera or renderer if (info == null || info.ReflectionCamera == null || info.SourceCamera == null || ReflectMaterial == null) { return; } CurrentRecursionLevel = currentCameras.Count + (info.SourceCameraIsReflection ? 1 : 0); renderCount++; Camera sourceCamera = info.SourceCamera; Camera reflectionCamera = info.ReflectionCamera; int oldPixelLightCount = QualitySettings.pixelLightCount; int oldCullingMask = reflectionCamera.cullingMask; bool oldSoftParticles = QualitySettings.softParticles; int oldAntiAliasing = QualitySettings.antiAliasing; ShadowQuality oldShadows = QualitySettings.shadows; SyncCameraSettings(reflectionCamera, sourceCamera); if (WeatherMakerScript.Instance != null && QualitySettings.shadows != ShadowQuality.Disable && WeatherMakerLightManagerScript.ScreenSpaceShadowMode != UnityEngine.Rendering.BuiltinShaderMode.Disabled) { QualitySettings.shadows = WeatherMakerScript.Instance.PerformanceProfile.ReflectionShadows; } // MAGIC MIRROR RECURSION OPTIMIZATION if (currentCameras.Count > 1) { if (currentCameras.Count > 3) { QualitySettings.shadows = ShadowQuality.Disable; } else if (WeatherMakerScript.Instance == null && QualitySettings.shadows != ShadowQuality.Disable && WeatherMakerLightManagerScript.ScreenSpaceShadowMode != UnityEngine.Rendering.BuiltinShaderMode.Disabled) { QualitySettings.shadows = ShadowQuality.HardOnly; } QualitySettings.antiAliasing = 0; QualitySettings.softParticles = false; QualitySettings.pixelLightCount = 0; reflectionCamera.cullingMask &= waterLayerInverse; } else { QualitySettings.pixelLightCount = MaximumPerPixelLightsToReflect; } // get reflection normal vector Transform reflectionTransform = transform; Vector3 reflectionNormal = (NormalIsForward ? -reflectionTransform.forward : reflectionTransform.up); if (TransformNormalNegate) { reflectionNormal = -reflectionNormal; } // use shared reflection render function RenderReflection(info, reflectionTransform, reflectionNormal, ClipPlaneOffset, ReflectionOffsetFunc); // restore render state reflectionCamera.cullingMask = oldCullingMask; QualitySettings.pixelLightCount = oldPixelLightCount; QualitySettings.softParticles = oldSoftParticles; QualitySettings.antiAliasing = oldAntiAliasing; QualitySettings.shadows = oldShadows; ReflectMaterial.SetTexture(ReflectionSamplerName, info.TargetTexture); ReflectMaterial.SetTexture(ReflectionSamplerName2, info.TargetTexture2); ReflectMaterial.DisableKeyword(mirrorRecursionLimitKeyword); currentCameras.Remove(info); info.SourceCamera = null; info.TargetTexture = null; info.TargetTexture2 = null; cameraCache.Add(info); }