Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Separate Shader for Scene Editor

Discussion in 'Shaders' started by R-Type, Mar 4, 2013.

  1. R-Type

    R-Type

    Joined:
    Oct 31, 2012
    Posts:
    44
    Hi,

    I have developped a special shader to visualize scientific data, by applying some rules, determined only during runtime. Now, this shader does not work properly in the scene editor. To provide the user a meaningful object representation in the scene editor, I want to attach another shader to be used in scene preview. Is that possible?

    Thanks.
     
  2. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Not as far as I know, but this is definitely something that I would like too.
     
  3. Jessy

    Jessy

    Joined:
    Jun 7, 2007
    Posts:
    7,325
    Likewise. Often, I'm working directly in clip space, and the only option is to not render layers with those objects in the Scene view. Sometimes that's fine, but other times, it would be nice to have a visualization in 3D space, only for the Scene view. Also, for 2D games, you don't have to waste the calculations necessary for perspective:

    Code (csharp):
    1. position = UNITY_MATRIX_MVP[3];
    2. position.xy += mul( float2x2(UNITY_MATRIX_MVP), vertex.xy );
    But then, you can't use a perspective camera in the scene view without that mesh disappearing. I'd like that not to be the case, without having to account for the last two entries of the fourth row of UNITY_MATRIX_MVP changing based on camera type.
     
    Last edited: Mar 4, 2013
  4. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Do the preprocessor defines work in shaders?

    #ifdef UNITY_EDITOR and such?

    edit; Actually, even play mode would still count as Unity editor, so that's pointless.
     
  5. Lulucifer

    Lulucifer

    Joined:
    Jul 8, 2012
    Posts:
    358
    lod? that will require one more subshader
     
  6. R-Type

    R-Type

    Joined:
    Oct 31, 2012
    Posts:
    44
    As I am still interested in a solution, I bring this thread back to live....

    @Lulucifer: can you share some more thoughts on your LOD approach? Is it possible to decide for which shader to use in scene editor only based on LOD?

    Otherwise, I think it could be done programaticaly in the shader code by passing a boolean which is different during play mode. But this would blow up shader code, which already is multi-pass.
     
  7. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    I assume, by the LOD thing... you add in another subshader with a really high LOD value, that'll get used in the editor.

    Then at run-time in your game (when it starts) you tell it to limit the shader LOD to something below that value (meaning any SubShader with a LOD above that will be ignored).

    http://docs.unity3d.com/Documentation/Components/SL-ShaderLOD.html
     
  8. zotai

    zotai

    Joined:
    Jan 23, 2015
    Posts:
    6
    Great suggestion with LOD usage. It works for me really well, it'll do until perhaps there's a built in conditional compile for the Editor view.
     
  9. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    725
    Is there still not a way to do this?
     
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    There is nothing provided by Unity, no. There's no consistent way to detect the scene view purely in a shader either.

    The best solution I know of is to use
    Camera.onPreRender
    to detect and set a global keyword or property.
    Code (csharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. [ExecuteInEditMode]
    5. public class SceneViewShaderHelper : MonoBehaviour {
    6.     #if UNITY_EDITOR
    7.     private int _isSceneViewID = Shader.PropertyToID("_IsSceneView");
    8.  
    9.     public void OnEnable()
    10.     {
    11.         Camera.onPreRender += SetIfSceneViewCamera;
    12.     }
    13.  
    14.     public void OnDisable()
    15.     {
    16.         Camera.onPreRender -= SetIfSceneViewCamera;
    17.     }
    18.  
    19.     public void SetIfSceneViewCamera(Camera cam)
    20.     {
    21.         // Scene View camera is named "SceneCamera"
    22.         if (cam.gameObject.name == "SceneCamera")
    23.         {
    24.             Shader.EnableKeyword("SCENE_VIEW");
    25.             Shader.SetGlobalFloat(_isSceneViewID, 1f);
    26.         }
    27.         // Inspector preview for materials, models, and prefabs is named "Preview Scene Camera"
    28.         // else if (cam.gameObject.name == "Preview Scene Camera")
    29.         // {
    30.         //     Shader.EnableKeyword("SCENE_VIEW");
    31.         //     Shader.SetGlobalFloat(_isSceneViewID, 2f);
    32.         // }
    33.         // Otherwise this is a game view or other user camera
    34.         else
    35.         {
    36.             Shader.DisableKeyword("SCENE_VIEW");
    37.             Shader.SetGlobalFloat(_isSceneViewID, 0f);
    38.         }
    39.  
    40.         // You can double check the camera names if something breaks in the future
    41.         // Debug.Log(cam);
    42.     }
    43.     #endif
    44. }
    I then add this to a game object in the scene which I set to be editor only so it gets excluded from builds.
    upload_2021-6-16_10-41-31.png

    In the shader, depending on what you're doing, you can use a
    #pragma multi_compile _ SCENE_VIEW
    and / or
    int _isSceneView;
    to switch to scene view specific code.

    The one thing to be mindful of is there's no way to prevent that shader variant / code from being included in standalone builds, though if you use the keyword they shouldn't "cost" anything apart from an increase in package size for the unused shader variants.


    The other option is to use command buffers to render entirely different objects / materials in the main camera and scene camera.
     
    Tunmix, rockin, matt_cauldron and 2 others like this.
  11. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    725
    It's funny I tried this callback way, but it didn't work for me. I wasn't even getting callbacks from the scene camera, only the game cameras. I must have been doing something wrong. I also tried adding a component onto the scene camera, and it kinda worked, but also unity instantly removed it.

    What ended up working for me was creating two CommandBuffers with their only job being to enable and disable a shader keyword. One runs before forward opaque, the other after forward alpha. Then I also added that keyword to our shader stripper.

    The EditorOnly tag is really cool though! Didn't know about that. (Now don't get me started on why the unity tag system is a broken mess...)
     
  12. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    My guess is you used
    cam.OnPreRender
    .
    cam.OnPreRender
    and
    Camera.onPreRender
    are subtly different, with the later being a delegate for all cameras, and the prior being only for a specific camera. The component script above does not need to be on a game object with a camera, it doesn't even need to be a child of a camera ... there doesn't even need to be a camera in the scene!

    https://docs.unity3d.com/ScriptReference/Camera.OnPreRender.html
    https://docs.unity3d.com/ScriptReference/Camera-onPreRender.html
     
  13. OVRSuppleTeet

    OVRSuppleTeet

    Joined:
    Sep 21, 2018
    Posts:
    3