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

Shader Issue

Discussion in 'Shaders' started by Jackk_, Nov 20, 2017.

  1. Jackk_

    Jackk_

    Joined:
    May 2, 2017
    Posts:
    49
    The Issue: I'm using an outline shader to make my game look more cartoon like, the only problem is I only want the outlines to render on the parts of the model that are actually viewable, to explain what I mean by that, on the photo below you can see that the clouds and building have the shader attatched, the only problem being i do not want to be able to see the clouds through the trees in front of them, also on the building i don't want there to be a line going across the part of the model that touches the ground as It doesn't look right.


    Here is the script that is assigned to the camera, which allows the lines to be seen


    OutlineEffect.cs
    Code (CSharp):
    1.  
    2.  
    3. using UnityEngine;
    4. using System.Collections.Generic;
    5. using UnityEngine.Rendering;
    6. using UnityEngine.VR;
    7.  
    8. namespace cakeslice
    9. {
    10.     [DisallowMultipleComponent]
    11.     [RequireComponent(typeof(Camera))]
    12.     [ExecuteInEditMode]
    13.     public class OutlineEffect : MonoBehaviour
    14.     {
    15.         private static OutlineEffect m_instance;
    16.         public static OutlineEffect Instance
    17.         {
    18.             get
    19.             {
    20.                 if(Equals(m_instance, null))
    21.                 {
    22.                     return m_instance = FindObjectOfType(typeof(OutlineEffect)) as OutlineEffect;
    23.                 }
    24.  
    25.                 return m_instance;
    26.             }
    27.         }
    28.         private OutlineEffect() { }
    29.  
    30.         private readonly LinkedSet<Outline> outlines = new LinkedSet<Outline>();
    31.  
    32.         [Range(1.0f, 6.0f)]
    33.         public float lineThickness = 1.25f;
    34.         [Range(0, 10)]
    35.         public float lineIntensity = .5f;
    36.         [Range(0, 1)]
    37.         public float fillAmount = 0.2f;
    38.  
    39.         public Color lineColor0 = Color.red;
    40.         public Color lineColor1 = Color.green;
    41.         public Color lineColor2 = Color.blue;
    42.  
    43.         public bool additiveRendering = false;
    44.  
    45.         public bool backfaceCulling = true;
    46.  
    47.         [Header("These settings can affect performance!")]
    48.         public bool cornerOutlines = false;
    49.         public bool addLinesBetweenColors = false;
    50.  
    51.         [Header("Advanced settings")]
    52.         public bool scaleWithScreenSize = true;
    53.         [Range(0.1f, .9f)]
    54.         public float alphaCutoff = .5f;
    55.         public bool flipY = false;
    56.         public Camera sourceCamera;
    57.  
    58.         [HideInInspector]
    59.         public Camera outlineCamera;
    60.         Material outline1Material;
    61.         Material outline2Material;
    62.         Material outline3Material;
    63.         Material outlineEraseMaterial;
    64.         Shader outlineShader;
    65.         Shader outlineBufferShader;
    66.         [HideInInspector]
    67.         public Material outlineShaderMaterial;
    68.         [HideInInspector]
    69.         public RenderTexture renderTexture;
    70.         [HideInInspector]
    71.         public RenderTexture extraRenderTexture;
    72.  
    73.         CommandBuffer commandBuffer;
    74.  
    75.         Material GetMaterialFromID(int ID)
    76.         {
    77.             if(ID == 0)
    78.                 return outline1Material;
    79.             else if(ID == 1)
    80.                 return outline2Material;
    81.             else
    82.                 return outline3Material;
    83.         }
    84.         List<Material> materialBuffer = new List<Material>();
    85.         Material CreateMaterial(Color emissionColor)
    86.         {
    87.             Material m = new Material(outlineBufferShader);
    88.             m.SetColor("_Color", emissionColor);
    89.             m.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
    90.             m.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
    91.             m.SetInt("_ZWrite", 0);
    92.             m.DisableKeyword("_ALPHATEST_ON");
    93.             m.EnableKeyword("_ALPHABLEND_ON");
    94.             m.DisableKeyword("_ALPHAPREMULTIPLY_ON");
    95.             m.renderQueue = 3000;
    96.             return m;
    97.         }
    98.  
    99.         private void Awake()
    100.         {
    101.             m_instance = this;
    102.         }
    103.  
    104.         void Start()
    105.         {
    106.             CreateMaterialsIfNeeded();
    107.             UpdateMaterialsPublicProperties();
    108.  
    109.             if(sourceCamera == null)
    110.             {
    111.                 sourceCamera = GetComponent<Camera>();
    112.  
    113.                 if(sourceCamera == null)
    114.                     sourceCamera = Camera.main;
    115.             }
    116.  
    117.             if(outlineCamera == null)
    118.             {
    119.                 GameObject cameraGameObject = new GameObject("Outline Camera");
    120.                 cameraGameObject.transform.parent = sourceCamera.transform;
    121.                 outlineCamera = cameraGameObject.AddComponent<Camera>();
    122.                 outlineCamera.enabled = false;
    123.             }
    124.  
    125.             renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default);
    126.             extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default);
    127.             UpdateOutlineCameraFromSource();
    128.  
    129.             commandBuffer = new CommandBuffer();
    130.             outlineCamera.AddCommandBuffer(CameraEvent.BeforeImageEffects, commandBuffer);
    131.         }
    132.  
    133.         public void OnPreRender()
    134.         {
    135.             if(commandBuffer == null)
    136.                 return;
    137.  
    138.             CreateMaterialsIfNeeded();
    139.  
    140.             if(renderTexture == null || renderTexture.width != sourceCamera.pixelWidth || renderTexture.height != sourceCamera.pixelHeight)
    141.             {
    142.                 renderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default);
    143.                 extraRenderTexture = new RenderTexture(sourceCamera.pixelWidth, sourceCamera.pixelHeight, 16, RenderTextureFormat.Default);
    144.                 outlineCamera.targetTexture = renderTexture;
    145.             }
    146.             UpdateMaterialsPublicProperties();
    147.             UpdateOutlineCameraFromSource();
    148.             outlineCamera.targetTexture = renderTexture;
    149.             commandBuffer.SetRenderTarget(renderTexture);
    150.  
    151.             commandBuffer.Clear();
    152.             if(outlines != null)
    153.             {
    154.                 foreach(Outline outline in outlines)
    155.                 {
    156.                     LayerMask l = sourceCamera.cullingMask;
    157.  
    158.                     if(outline != null && l == (l | (1 << outline.gameObject.layer)))
    159.                     {
    160.                         for(int v = 0; v < outline.Renderer.sharedMaterials.Length; v++)
    161.                         {
    162.                             Material m = null;
    163.  
    164.                             if(outline.Renderer.sharedMaterials[v].mainTexture != null && outline.Renderer.sharedMaterials[v])
    165.                             {
    166.                                 foreach(Material g in materialBuffer)
    167.                                 {
    168.                                     if(g.mainTexture == outline.Renderer.sharedMaterials[v].mainTexture)
    169.                                     {
    170.                                         if(outline.eraseRenderer && g.color == outlineEraseMaterial.color)
    171.                                             m = g;
    172.                                         else if(g.color == GetMaterialFromID(outline.color).color)
    173.                                             m = g;
    174.                                     }
    175.                                 }
    176.  
    177.                                 if(m == null)
    178.                                 {
    179.                                     if(outline.eraseRenderer)
    180.                                         m = new Material(outlineEraseMaterial);
    181.                                     else
    182.                                         m = new Material(GetMaterialFromID(outline.color));
    183.                                     m.mainTexture = outline.Renderer.sharedMaterials[v].mainTexture;
    184.                                     materialBuffer.Add(m);
    185.                                 }
    186.                             }
    187.                             else
    188.                             {
    189.                                 if(outline.eraseRenderer)
    190.                                     m = outlineEraseMaterial;
    191.                                 else
    192.                                     m = GetMaterialFromID(outline.color);
    193.                             }
    194.  
    195.                             if(backfaceCulling)
    196.                                 m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Back);
    197.                             else
    198.                                 m.SetInt("_Culling", (int)UnityEngine.Rendering.CullMode.Off);
    199.  
    200.                             commandBuffer.DrawRenderer(outline.GetComponent<Renderer>(), m, 0, 0);
    201.                             MeshFilter mL = outline.GetComponent<MeshFilter>();
    202.                             if(mL)
    203.                             {
    204.                                 if(mL.sharedMesh != null)
    205.                                 {
    206.                                     for(int i = 1; i < mL.sharedMesh.subMeshCount; i++)
    207.                                         commandBuffer.DrawRenderer(outline.GetComponent<Renderer>(), m, i, 0);
    208.                                 }
    209.                             }
    210.                             SkinnedMeshRenderer sMR = outline.GetComponent<SkinnedMeshRenderer>();
    211.                             if(sMR)
    212.                             {
    213.                                 if(sMR.sharedMesh != null)
    214.                                 {
    215.                                     for(int i = 1; i < sMR.sharedMesh.subMeshCount; i++)
    216.                                         commandBuffer.DrawRenderer(outline.GetComponent<Renderer>(), m, i, 0);
    217.                                 }
    218.                             }
    219.                         }
    220.                     }
    221.                 }
    222.             }
    223.  
    224.             outlineCamera.Render();
    225.         }
    226.  
    227.         private void OnEnable()
    228.         {
    229.             Outline[] o = FindObjectsOfType<Outline>();
    230.  
    231.             foreach(Outline oL in o)
    232.             {
    233.                 oL.enabled = false;
    234.                 oL.enabled = true;
    235.             }
    236.         }
    237.  
    238.         void OnDestroy()
    239.         {
    240.             if(renderTexture != null)
    241.                 renderTexture.Release();
    242.             if(extraRenderTexture != null)
    243.                 extraRenderTexture.Release();
    244.             DestroyMaterials();
    245.         }
    246.  
    247.         void OnRenderImage(RenderTexture source, RenderTexture destination)
    248.         {
    249.             outlineShaderMaterial.SetTexture("_OutlineSource", renderTexture);
    250.  
    251.             if(addLinesBetweenColors)
    252.             {
    253.                 Graphics.Blit(source, extraRenderTexture, outlineShaderMaterial, 0);
    254.                 outlineShaderMaterial.SetTexture("_OutlineSource", extraRenderTexture);
    255.             }
    256.             Graphics.Blit(source, destination, outlineShaderMaterial, 1);
    257.         }
    258.  
    259.         private void CreateMaterialsIfNeeded()
    260.         {
    261.             if(outlineShader == null)
    262.                 outlineShader = Resources.Load<Shader>("OutlineShader");
    263.             if(outlineBufferShader == null)
    264.             {
    265.                 outlineBufferShader = Resources.Load<Shader>("OutlineBufferShader");
    266.             }
    267.             if(outlineShaderMaterial == null)
    268.             {
    269.                 outlineShaderMaterial = new Material(outlineShader);
    270.                 outlineShaderMaterial.hideFlags = HideFlags.HideAndDontSave;
    271.                 UpdateMaterialsPublicProperties();
    272.             }
    273.             if(outlineEraseMaterial == null)
    274.                 outlineEraseMaterial = CreateMaterial(new Color(0, 0, 0, 0));
    275.             if(outline1Material == null)
    276.                 outline1Material = CreateMaterial(new Color(1, 0, 0, 0));
    277.             if(outline2Material == null)
    278.                 outline2Material = CreateMaterial(new Color(0, 1, 0, 0));
    279.             if(outline3Material == null)
    280.                 outline3Material = CreateMaterial(new Color(0, 0, 1, 0));
    281.         }
    282.  
    283.         private void DestroyMaterials()
    284.         {
    285.             foreach(Material m in materialBuffer)
    286.                 DestroyImmediate(m);
    287.             materialBuffer.Clear();
    288.             DestroyImmediate(outlineShaderMaterial);
    289.             DestroyImmediate(outlineEraseMaterial);
    290.             DestroyImmediate(outline1Material);
    291.             DestroyImmediate(outline2Material);
    292.             DestroyImmediate(outline3Material);
    293.             outlineShader = null;
    294.             outlineBufferShader = null;
    295.             outlineShaderMaterial = null;
    296.             outlineEraseMaterial = null;
    297.             outline1Material = null;
    298.             outline2Material = null;
    299.             outline3Material = null;
    300.         }
    301.  
    302.         public void UpdateMaterialsPublicProperties()
    303.         {
    304.             if(outlineShaderMaterial)
    305.             {
    306.                 float scalingFactor = 1;
    307.                 if(scaleWithScreenSize)
    308.                 {
    309.                     // If Screen.height gets bigger, outlines gets thicker
    310.                     scalingFactor = Screen.height / 360.0f;
    311.                 }
    312.  
    313.                 // If scaling is too small (height less than 360 pixels), make sure you still render the outlines, but render them with 1 thickness
    314.                 if(scaleWithScreenSize && scalingFactor < 1)
    315.                 {
    316.                     if(VRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None)
    317.                     {
    318.                         outlineShaderMaterial.SetFloat("_LineThicknessX", (1 / 1000.0f) * (1.0f / VRSettings.eyeTextureWidth) * 1000.0f);
    319.                         outlineShaderMaterial.SetFloat("_LineThicknessY", (1 / 1000.0f) * (1.0f / VRSettings.eyeTextureHeight) * 1000.0f);
    320.                     }
    321.                     else
    322.                     {
    323.                         outlineShaderMaterial.SetFloat("_LineThicknessX", (1 / 1000.0f) * (1.0f / Screen.width) * 1000.0f);
    324.                         outlineShaderMaterial.SetFloat("_LineThicknessY", (1 / 1000.0f) * (1.0f / Screen.height) * 1000.0f);
    325.                     }
    326.                 }
    327.                 else
    328.                 {
    329.                     if(VRSettings.isDeviceActive && sourceCamera.stereoTargetEye != StereoTargetEyeMask.None)
    330.                     {
    331.                         outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / VRSettings.eyeTextureWidth) * 1000.0f);
    332.                         outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / VRSettings.eyeTextureHeight) * 1000.0f);
    333.                     }
    334.                     else
    335.                     {
    336.                         outlineShaderMaterial.SetFloat("_LineThicknessX", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.width) * 1000.0f);
    337.                         outlineShaderMaterial.SetFloat("_LineThicknessY", scalingFactor * (lineThickness / 1000.0f) * (1.0f / Screen.height) * 1000.0f);
    338.                     }
    339.                 }
    340.                 outlineShaderMaterial.SetFloat("_LineIntensity", lineIntensity);
    341.                 outlineShaderMaterial.SetFloat("_FillAmount", fillAmount);
    342.                 outlineShaderMaterial.SetColor("_LineColor1", lineColor0 * lineColor0);
    343.                 outlineShaderMaterial.SetColor("_LineColor2", lineColor1 * lineColor1);
    344.                 outlineShaderMaterial.SetColor("_LineColor3", lineColor2 * lineColor2);
    345.                 if(flipY)
    346.                     outlineShaderMaterial.SetInt("_FlipY", 1);
    347.                 else
    348.                     outlineShaderMaterial.SetInt("_FlipY", 0);
    349.                 if(!additiveRendering)
    350.                     outlineShaderMaterial.SetInt("_Dark", 1);
    351.                 else
    352.                     outlineShaderMaterial.SetInt("_Dark", 0);
    353.                 if(cornerOutlines)
    354.                     outlineShaderMaterial.SetInt("_CornerOutlines", 1);
    355.                 else
    356.                     outlineShaderMaterial.SetInt("_CornerOutlines", 0);
    357.  
    358.                 Shader.SetGlobalFloat("_OutlineAlphaCutoff", alphaCutoff);
    359.             }
    360.         }
    361.  
    362.         void UpdateOutlineCameraFromSource()
    363.         {
    364.             outlineCamera.CopyFrom(sourceCamera);
    365.             outlineCamera.renderingPath = RenderingPath.Forward;
    366.             outlineCamera.backgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
    367.             outlineCamera.clearFlags = CameraClearFlags.SolidColor;
    368.             outlineCamera.rect = new Rect(0, 0, 1, 1);
    369.             outlineCamera.cullingMask = 0;
    370.             outlineCamera.targetTexture = renderTexture;
    371.             outlineCamera.enabled = false;
    372. #if UNITY_5_6_OR_NEWER
    373.             outlineCamera.allowHDR = false;
    374. #else
    375.             outlineCamera.hdr = false;
    376. #endif
    377.         }
    378.  
    379.         public void AddOutline(Outline outline)
    380.         {
    381.             if(!outlines.Contains(outline))
    382.                 outlines.Add(outline);
    383.         }
    384.  
    385.         public void RemoveOutline(Outline outline)
    386.         {
    387.             if(outlines.Contains(outline))
    388.                 outlines.Remove(outline);
    389.         }
    390.     }
    391. }

    Here is the script assigned to both the house and the clouds

    Code (CSharp):
    1.  
    2.  
    3. using UnityEngine;
    4. using System.Linq;
    5. using System.Collections.Generic;
    6.  
    7. namespace cakeslice
    8. {
    9.     [ExecuteInEditMode]
    10.     [RequireComponent(typeof(Renderer))]
    11.     public class Outline : MonoBehaviour
    12.     {
    13.         public Renderer Renderer { get; private set; }
    14.  
    15.         public int color;
    16.         public bool eraseRenderer;
    17.  
    18.         private void Awake()
    19.         {
    20.             Renderer = GetComponent<Renderer>();
    21.         }
    22.  
    23.         void OnEnable()
    24.         {
    25.             IEnumerable<OutlineEffect> effects = Camera.allCameras.AsEnumerable()
    26.                 .Select(c => c.GetComponent<OutlineEffect>())
    27.                 .Where(e => e != null);
    28.  
    29.             foreach (OutlineEffect effect in effects)
    30.             {
    31.                 effect.AddOutline(this);
    32.             }
    33.         }
    34.  
    35.         void OnDisable()
    36.         {
    37.             IEnumerable<OutlineEffect> effects = Camera.allCameras.AsEnumerable()
    38.                 .Select(c => c.GetComponent<OutlineEffect>())
    39.                 .Where(e => e != null);
    40.  
    41.             foreach (OutlineEffect effect in effects)
    42.             {
    43.                 effect.RemoveOutline(this);
    44.             }
    45.         }
    46.     }
    47. }
     
  2. LukasCh

    LukasCh

    Unity Technologies

    Joined:
    Mar 9, 2015
    Posts:
    102
    Hey, In order to understand more correctly ure issue I'm going to need shader, but looking from the script I guessing it just draws only mesh corners. If this is correct assumption - does your shader respect the depth, because it seems u only need some depth testing for it?
     
  3. Jackk_

    Jackk_

    Joined:
    May 2, 2017
    Posts:
    49
    It is not my script but looking at the code i would assume that only draws mesh corners, do you think you could explain how I can modify it to achieve my desired result (adding depth to the shader so that my error doesn't occur)?
     
  4. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,967
    Without the shader will be difficult
     
  5. Jackk_

    Jackk_

    Joined:
    May 2, 2017
    Posts:
    49
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329
    That outline effect is explicitly designed to add highlight style outlines. That is to say an outline that shows up over everything. It also is setup such that the outline from overlapping outlined objects merge together (like the clouds and the building in your example). Basically this asset is not one you want to use if you want toon like per object outlines.
     
  7. Jackk_

    Jackk_

    Joined:
    May 2, 2017
    Posts:
    49
    Then what shader do i need to achieve that look?
     
  8. Kumo-Kairo

    Kumo-Kairo

    Joined:
    Sep 2, 2013
    Posts:
    343
    Usually it's the replacement shader with "flip normals, move them outside and render everything in some color" logic. I couldn't find the exact shader right away, but the internet swarms with different solutions / tutorials absolutely free of charge. If no one will answer here with the exact solution, you'll be able to solve this yourself no problem
     
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,329