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

forcing vertex lighting

Discussion in 'General Graphics' started by laurentlavigne, Jul 10, 2020.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    Forcing vertex lighting on most lights is done by setting pixel light to 1, keeping the sun as "important", all other lights will be vertex.
    I was wondering if there is another way.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    You can set all other lights to be non-important which will force them to be per-vertex lights.

    You can also set the current rendering path to Legacy Vertex Lit. But what you're talking about where you have 1 directional light as a per pixel light and the rest as per vertex lights ... isn't really pure vertex lighting.

    Setting the pixel lights to 1 is really the best option.
     
    NotaNaN and laurentlavigne like this.
  3. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    Thanks i'll keep that.
    Or switch to deferred. What's the cost curve in deferred as light count and range grows? linear?
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    CPU cost is mostly linear for deferred. GPU cost depends a lot on how big your lights are compared to the view, and how many overlap, but it’s roughly linear with screen coverage w/o shadows. Generally faster than multiple per pixel forward lights, especially if you frequently have large or complex meshes with multiple lights affecting them.

    Though Unity’s default deferred point lights aren’t super efficient on the CPU as they’re still individual draws. They can be made much faster by writing your own using instanced lights if you need to support more than a few hundred at a time. Thousands are doable using custom lights.
     
    NotaNaN likes this.
  5. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    Thanks. That is promising for my project.

    I don't know how to do that. Are particle lights "instances" you speak of and more efficient than multiple light objects?
    The effect I'm after is to light the scene with multiple lightning arcing. At the moment I'm moving very fast a light along each lightning and it looks alright but with about 40 lightning a 2060 fps drop to low 10s.
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    Nope, no different than normal lights. If anything a little more expensive.
     
    laurentlavigne likes this.
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    I’m guessing your lights are quite large in radius? 40 lights that the radius of are a significant part of the screen is going to be bad regardless of deferred or forward.

    Deferred is good at a lot of small lights. Big lights are still expensive, regardless of what approach you use. The cost is linear based on how much of the screen the sphere of the light covers, not how much of the screen appears lit by it.

    Most games that do something like lightning arcs or the like will try to keep the radius low, or only have a small number (often only one) at a time actually lighting.
     
    protopop, NotaNaN and laurentlavigne like this.
  8. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
  9. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    The stencil stuff mentioned is explicitly not using instancing, and is instead using multi-pass rendering. For a smaller number of large lights, it's potentially faster. That thread is discussing the pros & cons of instancing vs heavy stencil usage. The tiled deferred approach used by the HDRP is even more efficient as it doesn't require multiple passes but does limit the cost to only those areas the light actually touches.

    There used to be some assets on the store with instanced lights, but they've all disappeared now since most were abandoned years ago (to be fair, likely nothing really needed to be done to keep them working, except Unity's been pulling assets that don't update, and just generally being an asset store dev can be a huge pain for not a lot of benefit.) It's not a terribly difficult shader to write. Take the existing deferred light shader, remove anything related to shadows, directional lights, or spot lights, and make the color, radius, and position come from a instanced data. Then render your lights manually using command buffers. That does take some shader know-how, and a bit of code, but it is a lot faster ... again, for a lot of small radius lights, not large radius lights.

    You can also do stuff like change the shader to be diffuse only, which will make them much less expensive, as most of the expensive calculations are in the specular aspect. Depending on the look you're going for this might be good enough. You could also look at using cheaper approximations, like Unity's existing forward Standard shader uses for mobile.
     
    NotaNaN and laurentlavigne like this.
  10. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    I'll leave light instancing for another project, from your description, it's well beyond my understanding. Thanks for explaining all that though.

    I'm already using a wraplambert surface shader that I think is efficient... here I'll attach it, what do you think?
    Code (CSharp):
    1. Shader "SG/SG Diffuse Wrap" {
    2.     Properties {
    3.         _Color ("Color",Color) = (1,1,1,1)
    4.           _MainTex ("Texture", 2D) = "white" {}
    5.         _DamageColor ("Damage Color", Color) = (1,0,0,1)
    6.  
    7.       _DamagePulseVal ("Damage Pulse", RANGE(0,1)) = 0
    8.       _BumpMap ("Bumpmap", 2D) = "bump" {}
    9.       _RimColor ("Rim Color", Color) = (0.26,0.19,0.16,0.0)
    10.       _RimPower ("Rim Power", Range(0.5,8.0)) = 3.0
    11.                     _OutlineColor ("Outline Color", Color) = (0,0,0,1)
    12.         _Outline ("Outline width", float) = .005
    13.  
    14.     }
    15.     SubShader {
    16.     LOD 900
    17.       Tags { "RenderType" = "Opaque" }
    18.          UsePass "Self-Illumin/VertexLit/BASE"
    19.  
    20.       CGPROGRAM
    21.         #pragma surface surf WrapLambert approxview  vertex:vert
    22.         #pragma target 3.0
    23.              
    24.        float4 _Color;
    25.       sampler2D _MainTex;
    26.       sampler2D _BumpMap;
    27.       float4 _RimColor;    
    28.       float _RimPower;
    29.       float4 _DamageColor;
    30.       float _DamagePulseVal;
    31.      
    32.            half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) {
    33.           half NdotL = dot (s.Normal, lightDir);
    34.           half diff = NdotL * 0.7 + 0.3;
    35.           half4 c;
    36.           c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
    37.           c.a = s.Alpha;
    38.           return c;
    39.       }
    40.  
    41.       struct Input {
    42.           float2 uv_MainTex;
    43.           float2 uv_BumpMap;
    44.           float3 viewDir;
    45.           float4 vertexColor;
    46.           float factor;
    47.       };
    48.  
    49.       void vert (inout appdata_full v, out Input o) {
    50.               UNITY_INITIALIZE_OUTPUT(Input,o);
    51.           o.vertexColor = v.color;                                      
    52.       }
    53.      
    54.       void surf (Input IN, inout SurfaceOutput o) {
    55.         half4 c = tex2D (_MainTex, IN.uv_MainTex);        
    56.         c *= IN.vertexColor;
    57.         c *= _Color;
    58.         c = c*(1-_DamagePulseVal) + _DamagePulseVal*_DamageColor;
    59.         o.Albedo = c;
    60.         o.Normal = UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));
    61.         half rim = 1.0 - saturate(dot (normalize(IN.viewDir), o.Normal));
    62.         o.Emission = _RimColor.rgb * pow (rim, _RimPower) ;//+ c.a*c.rgb;
    63.       }
    64.       ENDCG
    65.     UsePass "Toon/Basic Outline/OUTLINE"
    66.     }
    67.    

    I'm thinking of removing the rim or making it react to the light position, it's a bit much in the shadows and doesn't provide a nice translucent/furry look since it's a simple fresnel at the moment
     
  11. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    If you're using a custom lighting function in a surface shader, deferred won't help at all. The built in deferred only works with the Standard shading model. That'll get rendered using forward rendering no matter what. Also per-vertex lighting ignores custom lighting functions too, though that's already about as cheap as you can get.

    With a lot of lights, at this point you're almost entirely fill rate and CPU limited. Making the shader just output a solid color with no lighting at all probably won't make it much faster.
     
  12. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    Are you sure? in the documentation:

    upload_2020-7-14_1-12-26.png

    which matches performance gain when I switched everything to defefrrred and how it looks in the frame debugger.

    I'll lower the light count, pool them with the PerformanceManager singleton or something that handle many lightning based on intensity.
     
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    Yes.

    https://docs.unity3d.com/Manual/SL-SurfaceShaderLighting.html

    By default custom lighting models don't support deferred. Custom lighting models that do work with deferred can only modify ambient lighting and the gbuffer outputs.

    If you use the built in lighting models, standard, blinn phong, or Lambert, those will work with deferred, though the last two are approximated with the Standard lighting model, as that is the only thing it can do.
     
    NotaNaN and laurentlavigne like this.
  14. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
  15. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    And yep I cannot reconcile these two pages:
    The lighting example looks like this:
    half4 LightingWrapLambert (SurfaceOutput s, half3 lightDir, half atten) {
    with no UnityGI in the signature
    and in this page the lighting functions have a very different signature with no atten and lightDir
    https://docs.unity3d.com/Manual/SL-SurfaceShaderLighting.html
    Why is that?
    And can you point me to examples for the second page? That UnityGI parameter seems full of potential to play with probe lighting... (shame these can't be moved)
     
  16. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    Because Surface Shaders support a surprising range of different Lighting function configurations. The ones with the
    UnityGI
    input are the "new" way (adding sometime during Unity 5), with the others are from before that, but can still be used. There are even versions that aren't in the documentation. It's just something that's poorly documented.

    But that UnityGI parameter doesn't really give you that much more control. It's basically just a struct that packs the same information you already had access to with the old forms, plus maybe a pre-calculated ambient color.
     
    rasamfard, NotaNaN and laurentlavigne like this.
  17. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203
    I get it: I switched the doc to 5.2 and I'm seeing the old form.
    I couldn't find
    half4 Lighting<Name>_Deferred (SurfaceOutput s, UnityGI gi, out half4 outDiffuseOcclusion, out half4 outSpecSmoothness, out half4 outNormal);
    and the samples are super outdated.
    Do you have an example on how to use this form?

    Also https://forum.unity.com/threads/deferred-rendering-and-non-standard-lighting.386215/#post-2514077
    does it mean that when I (eventually) make my own lighting wraplambert, which ain't standard, it won't be deferred anyway? So as you say in that thread, wraplambert is only possible in deferred if I change a bunch of other shaders that do the deferred lighting - ouch.

    finally: ouch again! This is forward indeed. Which is so bizarre because it looks very different than when I switch camera to forward.
    the cannot batch because different lightprobe has me stumped - how do I fix that? having only one light probe in my light probe group? (my guess: fix the shaders so they're deferred and get the probe lighting for free)
    upload_2020-7-14_20-49-39.png
     
  18. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,321
    Correct.

    Correct, only if you make all deferred lighting use the "wraplambert" lighting model can you make it render deferred, or modify the deferred pipeline and all opaque shaders you use to allow for more than one shading model, which is obviously a non-trivial change and can make deferred a lot slower. Basically, if you change the internal deferred shader that does the actual deferred lighting to use "wraplambert", all Standard shaders will suddenly start rendering using wraplambert as well.

    Light probes will also prevent deferred rendered objects from batching. You can either not use any light probes, which will use the default scene probe (which is going to be based on the skybox material), or try to use one light probe which I think works, but I've never tried. There's also light probe proxy volumes which are supposed to allow for batching & instancing, but last I looked that didn't actually work and I haven't followed up to see if it got fixed since I've not been using light probes for a while.
     
    rasamfard, NotaNaN and laurentlavigne like this.
  19. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,203