Beispiel #1
0
        public RenderTexture CreateTexture(Portal.RenderOptions renderOptions, Camera targetCamera)
        {
            if (texture) {
            ReturnTexture(texture);
            texture = null;
            }

            //Portal render texture
            //Get the desired size of the RT
            int portalW, portalH;
            renderOptions.textureSize.GetTextureSize(targetCamera, out portalW, out portalH);

            var aa = QualitySettings.antiAliasing;
            //So, http://docs.unity3d.com/ScriptReference/QualitySettings-antiAliasing.html
            //says 0, 2, 4, or 8 and http://docs.unity3d.com/ScriptReference/RenderTexture.GetTemporary.html
            //says 1, 2, 4, or 8
            if (aa == 0) aa = 1;

            texture = GetTexture(portalW, portalH, aa);

            if (!texture) Debug.LogWarning("Insufficient video memory for new render texture");

            texture.filterMode = renderOptions.textureSize == Portal.TextureSize.CameraFull ? FilterMode.Point : FilterMode.Bilinear;

            return texture;
        }
Beispiel #2
0
        /**
         * Given a camera and a position-independent ray, determines the vanishing point of the ray and how much to scale it.
         * Returns a vector of center (x and y in texture space) and how much to scale (w)
         *
         * TODO: This is not complete.
         */
        public static Vector4 GetVanishingPointAndScale(Camera prevCamera, Camera currentCamera, Portal portal)
        {
            var ret = new Vector4(0, 0, 0, 1);

            //get vector relative to camera
            var dirInCameraSpace = currentCamera.transform.InverseTransformDirection(portal.transform.forward);

            //do some math to determine the vanishing point (derived somewhat empirically, this may not be exact)
            ret.x = (1 / currentCamera.aspect) * dirInCameraSpace.x / dirInCameraSpace.z;
            ret.y = dirInCameraSpace.y / dirInCameraSpace.z;

            // ret *= .75f;

            //determine how much to scale by
            var prevCameraToPortalDistance = Vector3.Distance(portal.transform.position, prevCamera.transform.position);
            var cameraToPortalDistance = Vector3.Distance(portal.transform.position, currentCamera.transform.position);
            ret.w = prevCameraToPortalDistance / cameraToPortalDistance;
            // ret.w = Mathf.Cos(prevCamera.fieldOfView * Mathf.Deg2Rad) / cameraToPortalDistance;
            // ret.w = Mathf.Cos(prevCamera.fieldOfView * Mathf.Deg2Rad);

            //move results (relative to zero) into texture space
            ret.x += 0.5f;
            ret.y += 0.5f;
            return ret;
        }
Beispiel #3
0
        /**
         * Creates and returns a new ShadowClone that will mimic the appearance of the given object on the other side of
         * the given portal.
         */
        public static ShadowClone Create(Transform realObject, Portal portal)
        {
            if (!Application.isPlaying) {
            Debug.LogWarning("FIXME: Attempting to create ShadowClone in edit mode");
            return null;
            }

            var name = realObject.name + " Portal Clone for " + portal.name + " " + (++id);

            var cloneGO = new GameObject(name);

            cloneGO.hideFlags = HideFlags.NotEditable;
            //note: don't add the "don't save" flag; we don't run in edit mode and setting the "don't save" flag really confuses the editor in this case.

            var clone = cloneGO.AddComponent<ShadowClone>();
            clone.portal = portal;
            clone.realObject = realObject.gameObject;
            clone.transform.localScale = realObject.lossyScale;

            //Also clone current position (while building the object tree) until we Update() to our real location.
            clone.transform.position = realObject.position;
            clone.transform.rotation = realObject.rotation;

            CopyLook(realObject.gameObject, cloneGO);
            CloneChildren(realObject.gameObject, cloneGO);

            return clone;
        }
Beispiel #4
0
 /** The portal script will call this method in OnWillRenderObject while the scene is being culled. */
 public void PortalIsVisible(Portal portal)
 {
     //render the portal right now and save the result
     var renderResult = RenderedFrame.Get();
     portal.RenderSlaveCamera(GetComponent<Camera>(), renderResult);
     //in a minute we'll apply the results to the portal so it will look right when we render
     renderedPortals.Add(portal);
     renderedPortalData.Add(renderResult);
 }
Beispiel #5
0
        /**
         * This will render the slave camera to a texture as it would be seen by the given camera.
         * Render results are placed in {result}. Pass this to AppearAsPreviouslyRendered to apply the rendered
         * texture to our current model/appearance.
         */
        internal void RenderSlaveCamera(Camera cam, RenderedFrame result)
        {
            //skip if not on or ready
            if (!enabled || !GetComponent<Renderer>() || !GetComponent<Renderer>().sharedMaterial || !GetComponent<Renderer>().enabled || !destination || !cam)
            return;

            CameraInfo trackingInfo = null;
            if (!cameraInfo.TryGetValue(cam, out trackingInfo)) {
            //Not actually being tracked? Just use a temp. Generally shouldn't happen.
            trackingInfo = new CameraInfo();
            }

            Vector3 entryFaceDirection = transform.rotation * entryFace;

            var isBehind = Vector3.Dot(transform.position - cam.transform.position, entryFaceDirection) >= 0;
            var isReallyNear = PortalMath.DistanceFromPointToPlane(transform.forward, transform.position, cam.transform.position) <= cam.nearClipPlane;

            //Depending on where we've been and are, we might or might not want to render. Keep reading.
            if (isReallyNear) {
            if (isBehind) {
                //we are just behind the portal
                if (trackingInfo.nearFront) {
                    //We were in front of it earlier and can we can still see the portal (the portal mesh has more geometry behind the front plane),
                    //Render.
                    //(If we're going to teleport something, we'll usually do it just after this frame.)
                } else {
                    //We weren't just in front of the portal. Perhaps we teleported in from somewhere or walked up to a portal that's
                    //invisible from behind while looking backwards.
                    //Don't render.
                    return;
                }
            } else {
                //We are in front of the portal (and rather close to boot).
                //Render.

                //Also set the "I was close to the front of the portal" flag so we know to keep rendering if we move behind it.
                trackingInfo.nearFront = true;
            }
            } else {
            //We are not close.

            //Reset this flag.
            trackingInfo.nearFront = false;

            if (isBehind) {
                //Don't render
                return;
            } else {
                //Render.
            }
            }

            //Note that, if we don't (try to) render the portal for a frame or so the tracking information will be deleted.
            //This covers the corner case where you are behind the portal with it rendering, then teleport away and back.
            //Without clearing the nearFront flag while we're away, we would incorrectly show the inside of the portal if we jumped back.

            //If we have a two-sided portal on the other end as our destination, the opposite face on the destination portal can sometimes
            //block the view as we look through.
            //Therefore, if we are the destination of the currently rendering portal, don't render at all.
            if (lastRecursivePortal && lastRecursivePortal.destination == this.transform) {
            return;
            }

            //Stop rendering if we are too recursively deep.
            if (currentPortalDepth + 1 > renderOptions.maximumPortalDepth) {
            result.renderOpaque = true;
            return;
            }

            #if UNITY_EDITOR
            if (!Application.isPlaying && currentPortalDepth + 1 > 1) {
            //don't render more than one deep in the editor (todo: make this configurable)
            result.renderOpaque = true;
            return;
            }
            #endif

            currentPortalDepth++;

            var lastLastRecursiveCamera = lastRecursiveCamera;
            var lastLastRecursivePortal = lastRecursivePortal;
            lastRecursiveCamera = cam;
            lastRecursivePortal = this;

            try {

            var camInfo = CreateCameraObjects(cam);

            var portalCamera = camInfo.portalCamera;

            UpdateCameraModes(cam, portalCamera);

            //Move the render target camera to where we'll be rendering from.
            TeleportRelativeToDestination(cam.transform, portalCamera.transform);

            //get the portal's plane
            var pos = destination.transform.position;
            var normal = destination.transform.rotation * exitFace;

            if (renderOptions.useOblique) {
                /*
                Normally, when you do a projection, the near and far (clipping) planes are perpendicular to the camera.

                They don't have to be, however, and here we take advantage of this fact to cull unwanted geometry.

                Here we set up an oblique projection matrix so that near clipping plane coincides with our portal plane.
                (Then shim it a bit, to avoid z-fighting.)
                This way the z-buffer will automatically clip out everything between the camera and portal.
                You'll only see things beyond the destination portal.
                 */
                Vector4 clipPlane = PortalMath.CameraSpacePlane(portalCamera, pos, normal, 1.0f, renderOptions.clipPlaneOffset);

                Matrix4x4 projection;
                if (currentPortalDepth > 1) {
                    //If we have a regular projection matrix we can just go ahead and turn it into an oblique matrix.
                    //But if we started with an oblique matrix (as happens when a portal renders a portal), re-obliquifying it
                    //messes up the far clipping plane.
                    //Instead, start with a fresh matrix for the camera and tweak that.

                    //Note that we don't want to modify the src camera's matrix, just get a copy of what its normal matrix would be.
                    //(Too bad Unity doesn't have an API to just fetch it.)
                    //Also note: If we do this to a scene camera inside the Unity Editor (even though we put it back) the scene cameras might FREAK OUT.
                    //(That's not a concern, however, because we only do this to slave cameras we generated.)
                    var origMatrix = cam.projectionMatrix;//backup
                    cam.ResetProjectionMatrix();
                    projection = cam.projectionMatrix;//get what we need
                    cam.projectionMatrix = origMatrix;//leave the original camera unmodified
                } else {
                    projection = cam.projectionMatrix;
                }

                //how far is the camera on this side from the portal entrance?
                var cameraDistanceFromPortal = PortalMath.DistanceFromPointToPlane(transform.forward, transform.position, cam.transform.position);
                if (cameraDistanceFromPortal < cam.nearClipPlane * 3) {
                    //When the camera's this close, the math we're using to construct the oblique matrix tends to break down and construct a matrix
                    //with a far plane that intersects the original frustum.

                    //If we're this close, we'll rely on the empty space that should be behind the portal actually being empty and just use a
                    //regular near plan on our frustum.
                } else {
                    if (portalCamera.orthographic)
                        PortalMath.CalculateOrthographicObliqueMatrix(ref projection, clipPlane);
                    else
                        PortalMath.CalculatePerspectiveObliqueMatrix(ref projection, clipPlane);
                }

                //we don't use the normal near clip plane, but still need to tell culling algorithms about where we're looking
                //Never mind, occlusion culling is broken in Unity. //portalCamera.nearClipPlane = PortalMath.DistanceFromPointToPlane(destination.forward, destination.position, portalCamera.transform.position);//Vector3.Distance(portalCamera.transform.position, destination.transform.position);

                portalCamera.projectionMatrix = projection;
            }

            var renderTexture = result.CreateTexture(renderOptions, cam);

            portalCamera.cullingMask = renderOptions.renderLayers.value;
            portalCamera.targetTexture = renderTexture;
            portalCamera.Render();
            // Debug.Log("portal texture (after render) is " + this+ "-"+camInfo.portalTexture.GetInstanceID());

            camInfo.renderedLastFrame = true;
            } finally {
            currentPortalDepth--;
            lastRecursiveCamera = lastLastRecursiveCamera;
            lastRecursivePortal = lastLastRecursivePortal;
            }
        }