Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.

Scissor rectangle

Discussion in 'Editor & General Support' started by francoisvdv, Dec 23, 2009.

  1. francoisvdv

    francoisvdv

    Joined:
    Dec 22, 2009
    Posts:
    10
    Hi all,

    After working for about 1,5 years with XNA I'm now trying to learn Unity. Great experience so far! Really powerful engine/editor combo.

    Some things in Unity aren't very obvious though. As a starter, the GUI system is really too simple and I find it hard to create effective custom controls. Converting an old XNA UI control has been a pain in the behind and it still isn't working like I want. Drawing a simple 2D line (with LineRenderer) was quite the job for an Unity-newb like me.

    Anyway, here's my question:

    I want to use a scissor rectangle that cuts off everything outside of a certain rectangle. I want my custom control to be able to draw everywhere (this includes 2D GUI drawing), but only use the top 300px of the screen of what's drawn, and 'cut-off' the rest. What would be the best way to accomplish this?

    Thanks!
     
  2. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    There isn't built-in support for scissor rects, however, here is a custom script that'll get the job done!
     

    Attached Files:

    Piflik and atomicjoe like this.
  3. francoisvdv

    francoisvdv

    Joined:
    Dec 22, 2009
    Posts:
    10
    Thanks, that's exactly what I wanted! :D
     
  4. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I am having trouble getting this to work when the camera needs to have a varying near clip plane. (Otherwise, it works very well.) Solution?

    (The Editor is showing the near clip plane updating perfectly in the Scene View. the Game View doesn't reflect that, however.)

    To be clear, I need one camera to match another in all respects, except only for a scissored portion of the first's viewport rectangle, and with a different near clip plane. This camera to be matched is "tot.camera", here, and it does not take up the full height of the screen.

    Code (csharp):
    1. inverseWidth = 1 / rect.width;
    2. inverseHeight = 1 / rect.height;
    3. matrix1.SetTRS(
    4.     new Vector3(-rect.x  * 2 * inverseWidth, -rect.y * 2 * inverseHeight, 0),
    5.     Quaternion.identity,
    6.     Vector3.one);
    7. matrix2.SetTRS(
    8.     new Vector3(inverseWidth - 1, inverseHeight - 1, 0),
    9.     Quaternion.identity,
    10.     new Vector3(inverseWidth, tot.camera.rect.height * inverseHeight, 1) );
    11. camera.projectionMatrix = matrix1 * matrix2 * tot.camera.projectionMatrix;
    12.  
     
  5. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    Hi Jessy,

    Can you send me an example project?

    Thanks,
    Amir
     
  6. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I'll post it here in case anyone else has some ideas ready at hand. Thank you so much for having a look. I don't understand camera matrices well yet, so any schooling is appreciated.

    In this "Scene" The relationship of the "red camera" to the "red circle" mimics the relationship of the "blue camera" to the "blue circle".

    The "background camera" displays (in green) the area above and below the other cameras. It illustrates that the rect of the "red camera" does not go past the boundaries of the "blue camera" It renders at the depth of -100.

    As it is saved, the scene begins with the red camera disabled. Enabling it (with RedCamera.useProjectionMatrix enabled) displays how the projection matrix of the blue camera is mimicked, albeit in a smaller viewport rectangle. However, you can see how the near clip plane of the red camera, shown in the Editor, is not accurate, because the blue cube (that which should not be seen!) is visible in the Game View. Turning RedCamera.useProjectionMatrix off fixes the near clip issue, but of course, the projection matrix is all wrong then.

    Also, if you have any advice about how to avoid the pixel or two of error on the "red camera" rect, please let me know.
     

    Attached Files:

  7. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    I can't make heads or tails of your project. What are you trying to do? What is the expected result?
     
  8. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    The expected result is that the red camera sees the red circle as the blue camera sees the blue circle. But along with requiring a "scissor rectangle", the red camera needs its near clip plane adjusted so that it doesn't see anything closer to it than the red circle. (If you move or rotate the blue camera around, the red camera is updated automatically.)

    Ideally, I wouldn't be using the near clip plane for this anyway. Using the near clip method, there is an area of space near the red circle, closer to the red camera, in which the camera can see things. However, I don't know if or how the ideal method could be accomplished.

    (I handwrote this fast, sorry. The captions below are "red circle", "ideal "frustum" ", "near clip method frustum", and "red camera".)
     

    Attached Files:

  9. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    It sounds like you are needing an oblique near plane. Aras has written something about this actually:

    http://aras-p.info/texts/obliqueortho.html

    Keep this concept separate from scissor rects, which is an image plane modification and not a frustum modification.
     
  10. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    That's fantastic, thank you. I will look at it today.

    However, I need both that and a scissor rectangle. The red camera is not permitted to see anything outside of the bounds of the red circle. That is much more important than the oblique projection.
     
  11. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    Just use the original script I attached and add it to "red camera". You can set the scissor rect either via the scissorRect property or if you want to call the script manually, then you can call Scissor.SetScissorRect( <red camera>, <custom scissor rect> );

    If you need it to match the bounds of the red circle, then get the world coordinates of the top left/right, and bottom left/right and use Camera.WorldToViewportPoint() to get the custom scissor rect coords.
     
  12. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    That's already happening in the RedCamera script. You can see it illustrated by the solid color that the red camera renders as its background after enabling the camera. The problem, again, is that your code doesn't seem to account for near clip plane changing. I wasn't aware that the clipping planes were "built in" to the camera's projection matrix, but that would seem the case, from Aras's page.
     
  13. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    That's orthogonality at work :) If you want to set an oblique near clip plane, you would call CalculateObliqueMatrix() from Aras' page first on the camera. If you want a scissor rect, you would call SetScissorRect after. Both modify the projection matrix, however, the concepts are separate.
     
  14. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I guess before I get into this, I want to be sure:

    Is there any way to render a camera on screen in another type of quad than a rectangle? (I know how to block the view with "Colormask 0" triangles on the near clip plane, but I'd prefer a cleaner method.)
     
  15. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    What do you mean by other type of "quad than a rectangle"? Are you asking whether you can keep a square ratio of the sides?

    What are you trying to do overall? Maybe there is another way to go about this.
     
  16. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I want to be able to match the camera's viewport to a rectangular area in world space (like a window). In perspective, the rectangle can end up looking like a trapezium. I'd prefer for the camera's viewport to match that, instead of being a screen-aligned rectangle that fully contains it.


    You can think of it like Portal, but I can't use render textures because this is for the iPhone.
     
  17. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    It might be easier to render what's behind the window/portal first using an oblique near plane and then render the window container at a later stage (which would provide the trapezium mask that you are looking for). A stencil buffer would do the trick in a single pass, however, we don't have that support in Unity.

    I suppose there are some other tricks you could try like prepping the depth buffer.
     
  18. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    I'm having success with oblique projections these days, and I had been having success with the script from the first page of this thread. However, as soon as I made the camera that the scissor camera is based on have an assymetical normalized viewport rectangle, it stopped working well.

    In that script, this is used:

    Code (csharp):
    1. cam.rect = new Rect (0,0,1,1);
    2. cam.ResetProjectionMatrix ();
    However, I don't want the scissor camera to be based on a full-screen rendering. I want it based on a camera that actually needs to render. Is it simple to make the relevant code more robust, to account for this?

    Code (csharp):
    1. float inverseWidth = 1 / rect.width;
    2.     float inverseHeight = 1 / rect.height;
    3.     Matrix4x4 matrix1 = Matrix4x4.TRS(
    4.         new Vector3(-rect.x * 2 * inverseWidth, -rect.y * 2 * inverseHeight, 0),
    5.         Quaternion.identity, Vector3.one);
    6.     Matrix4x4 matrix2 = Matrix4x4.TRS(
    7.         new Vector3(inverseWidth - 1, inverseHeight - 1, 0),
    8.         Quaternion.identity,
    9.         new Vector3(inverseWidth, inverseHeight, 1) );
    10.     camera.projectionMatrix = matrix1 * matrix2 * projection;
    11.  
     
  19. amirebrahimi

    amirebrahimi

    Joined:
    Jan 27, 2008
    Posts:
    171
    I'm not entirely sure, however, you could try just setting the rect to the portion of the screen that you want and then calling reset. I forget why I did that to begin with. Either it will blow up in your face or maybe point you in the right direction, if not just work outright :)
     
  20. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Well, in your case, you're basing the scissor rect on a full screen rendering. That is, the field of view parameter that you give to the camera will be used, but relate to the top and bottom of the physical screen. Then, a portion of that rendering is used.

    I don't need to call reset, because I already have another camera that "renders normally" based on the Unity Editor's parameters. But it only uses the lower 90% of the screen's vertical space.

    Although your code has worked perfectly for me, when used as you wrote it, it doesn't work for all normalized viewport rectangles. It's really designed to work with 0-1 in both axes, and I don't understand it well enough yet, to know how to modify the code to account for alternatives.

    I think my workaround for the time being will be to render the main camera using a stored scissor rect. Both it and the second camera will be based off the same full-screen potential render, so it should work perfectly. The problem is that the field of view will be greater below the camera than above it, given my main camera's viewport rect of (0-1 in x; 0-.9 in Y). If you ever come up with something that would allow for basing the scissor rects on alternatives to 0-1, I would definitely appreciate if you could share that. I'm sure it's possible, but I don't yet know how to modify the matrix calculations you wrote to account for it.
     
  21. PurpleMonkey

    PurpleMonkey

    Joined:
    Sep 26, 2012
    Posts:
    3
    I know this is old, but I haven't seen this mentioned. When I want a scissor rect, I simply set the X-Y ranges of the rectangle as a uniform float4 in my pixel shader, and then I use the "clip" function to reject any pixels outside of that. This solution avoids messing with (potentially messing up) projection matrices, or having special cameras to support this functionality which conceptually has nothing to do with cameras or projection.
     
  22. thaFreakazoid

    thaFreakazoid

    Joined:
    Oct 11, 2012
    Posts:
    8
    can you be more specific? how is the clip function used I can't seem to find much information on it?
     
  23. thaFreakazoid

    thaFreakazoid

    Joined:
    Oct 11, 2012
    Posts:
    8
    Never mind i found a way to just discard when out of range.

    Code (csharp):
    1. Shader "GLSL shading in world space" {
    2.    Properties {
    3.       _MainTex ("Texture Image", 2D) = "white" {}
    4.          // a 2D texture property that we call "_MainTex", which should
    5.          // be labeled "Texture Image" in Unity's user interface.
    6.          // By default we use the built-in texture "white"  
    7.          // (alternatives: "black", "gray" and "bump").
    8.    }
    9.    SubShader {
    10.       Pass {
    11.          GLSLPROGRAM
    12.          uniform sampler2D _MainTex;
    13.          uniform mat4 _Object2World;
    14.          varying vec4 position_in_world_space;
    15.         varying vec4 textureCoordinates;
    16.          #ifdef VERTEX
    17.  
    18.          void main()
    19.          {
    20.             textureCoordinates = gl_MultiTexCoord0;
    21.             position_in_world_space = _Object2World * gl_Vertex;
    22.                // transformation of gl_Vertex from object coordinates
    23.                // to world coordinates;
    24.  
    25.             gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    26.          }
    27.  
    28.          #endif
    29.  
    30.          #ifdef FRAGMENT
    31.  
    32.          void main()
    33.          {
    34.             float dist = distance(position_in_world_space,
    35.                vec4(0.0, 0.0, 0.0, 1.0));
    36.                // computes the distance between the fragment position
    37.                // and the origin (the 4th coordinate should always be
    38.                // 1 for points).
    39.  
    40.             if (dist < 90.0)
    41.             {
    42.                gl_FragColor = texture2D(_MainTex, vec2(textureCoordinates));
    43.             }
    44.             else
    45.             {
    46.                discard;
    47.                
    48.             }
    49.          }
    50.  
    51.          #endif
    52.  
    53.          ENDGLSL
    54.       }
    55.    }
    56. }
     
  24. Rooch

    Rooch

    Joined:
    Sep 4, 2013
    Posts:
    3
    Thanks for this file. I've been using it to synchronise three instances of Unity running on a tiled display wall. The problem I'm having is that the three instances need to be different sizes. You can see from the image that the wider window at the top is incorrect, but I can't get my head around the projection matrix enough to get it right.

    Any ideas what I need to do to fix it?

    Cheers

    $Capture.JPG
     
  25. Rooch

    Rooch

    Joined:
    Sep 4, 2013
    Posts:
    3
    Never mind, I've just realised that all instances need to have the same aspect ratio. It works a treat now.

    Cheers
     
  26. atomicjoe

    atomicjoe

    Joined:
    Apr 10, 2013
    Posts:
    1,617
    YOU ARE MY SAVIOUR! THANK YOU FROM THE FUTURE !!!!
     
  27. PendingFox

    PendingFox

    Joined:
    Jan 15, 2017
    Posts:
    42

    Can you elaborate a little bit? how to use this shader on the camera?
     
    arturmandas likes this.
  28. arturmandas

    arturmandas

    Joined:
    Sep 29, 2012
    Posts:
    240
    Necro bumping up
     
unityunity