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.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Normal mapping particles - only shows on half

Discussion in 'Shaders' started by Alabatross, Aug 21, 2015.

  1. Alabatross

    Alabatross

    Joined:
    Dec 15, 2012
    Posts:
    223
    In my spare time I've been messing around trying to get normal maps on particles, but every single time it seems like only one triangle is receiving normals:

    What am I doing wrong?


    Code (CSharp):
    1. Shader "Particle Test" {
    2.         Properties {
    3.                 _MainColor ("Base Color", Color) = (0.5,0.5,0.5,1)
    4.                 _MainTex ("Base (RGB)", 2D) = "white" {}
    5.                 _BumpMap ("Base (RGB)", 2D) = "bump" {}
    6.              
    7.         }
    8.         SubShader {
    9.                 Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
    10.                 LOD 200
    11.              
    12.                 CGPROGRAM            
    13.                 #pragma exclude_renderers gles
    14.                 #pragma surface surf pLight alpha
    15.                 #pragma vertex vert
    16.              
    17.                 half4 LightingpLight (SurfaceOutput s, half3 lightDir, half atten)
    18.                 {
    19.                   half dotL = dot (s.Normal, lightDir);
    20.                   half diff = dotL;
    21.                   half4 c;
    22.                   c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);              
    23.                
    24.                   c.a = 1;
    25.                  
    26.                   return c;
    27.                     }
    28.              
    29.                 fixed4 _MainColor;
    30.                 sampler2D _MainTex;
    31.                 sampler2D _BumpMap;
    32.              
    33.                 struct Input
    34.                 {
    35.                         float2 uv_MainTex;
    36.                         float3 viewDir;
    37.                         float4 color : COLOR;
    38.                 };
    39.              
    40.                 void vert (inout appdata_full v, out Input o)
    41.                 {
    42.                     UNITY_INITIALIZE_OUTPUT(Input,o);
    43.                     o.color = v.color;
    44.                  
    45.                                  
    46.                 }
    47.              
    48.                 void surf (Input IN, inout SurfaceOutput o)
    49.                 {                      
    50.                         half4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainColor * IN.color;
    51.                         o.Albedo = c.rgb;
    52.                           o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));                        
    53.                         o.Emission = c.rgb;
    54.                 }
    55.                 ENDCG
    56.         }
    57.         FallBack "Diffuse"
    58. }
     
  2. jistyles

    jistyles

    Joined:
    Nov 6, 2013
    Posts:
    34
    I noticed this recently on a few effects - it appeared to be related to the "normal direction" parameter in the particle system being < 1 (eg, bent normals on the billboard)
    I assume it's a mesh generation bug with shuriken, most likely the tangents are getting borked on one of the tri's (at least it looks very similar to when I last screwed up tangents on a particle :D )
     
  3. jack-riddell

    jack-riddell

    Joined:
    Feb 11, 2014
    Posts:
    326
    Did you ever find a solution? im having trouble setting up particle normal mapping myself
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    Particles don't have valid tangents so your basic normal mapping doesn't work on them. If your particles never turn you can use a camera transform to set the tangents to something useful, or you can look into generating the tangents in the pixel shader using derivatives.
     
  5. Alabatross

    Alabatross

    Joined:
    Dec 15, 2012
    Posts:
    223
    Did bgolus' reply about tangents finally get it working for you? Would you mind sharing how you calculated them?
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    Here's what I use. You use it in the fragment shader. The position you pass to it should be in the same space you have your light directions, which Unity passes to shaders in world positions / directions, so you'll need to calculate the world position in the vertex shader and pass it to the fragment shader. (Note, particle vertices by default are already in world space positions as of Unity 5.2).

    Code (CSharp):
    1.  
    2. // Unity version of http://www.thetenthplanet.de/archives/1180
    3. float3x3 cotangent_frame( float3 normal, float3 position, float2 uv )
    4. {
    5.     // get edge vectors of the pixel triangle
    6.     float3 dp1 = ddx( position );
    7.     float3 dp2 = ddy( position ) * _ProjectionParams.x;
    8.     float2 duv1 = ddx( uv );
    9.     float2 duv2 = ddy( uv ) * _ProjectionParams.x;
    10.     // solve the linear system
    11.     float3 dp2perp = cross( dp2, normal );
    12.     float3 dp1perp = cross( normal, dp1 );
    13.     float3 T = dp2perp * duv1.x + dp1perp * duv2.x;
    14.     float3 B = dp2perp * duv1.y + dp1perp * duv2.y;
    15.     // construct a scale-invariant frame
    16.     float invmax = rsqrt( max( dot(T,T), dot(B,B) ) );
    17.     return transpose(float3x3( T * invmax, B * invmax, normal ));
    18. }
    19.  
    20. // Variant with no input normal, construct purely from geometry gradients
    21. float3x3 cotangent_frame( float3 position, float2 uv )
    22. {
    23.     // get edge vectors of the pixel triangle
    24.     float3 dp1 = ddx( position );
    25.     float3 dp2 = ddy( position ) * _ProjectionParams.x;
    26.  
    27.     float3 normal = normalize(cross(dp1, dp2));
    28.  
    29.     float2 duv1 = ddx( uv );
    30.     float2 duv2 = ddy( uv ) * _ProjectionParams.x;
    31.     // solve the linear system
    32.     float3 dp2perp = cross( dp2, normal );
    33.     float3 dp1perp = cross( normal, dp1 );
    34.     float3 T = dp2perp * duv1.x + dp1perp * duv2.x;
    35.     float3 B = dp2perp * duv1.y + dp1perp * duv2.y;
    36.     // construct a scale-invariant frame
    37.     float invmax = rsqrt( max( dot(T,T), dot(B,B) ) );
    38.     return transpose(float3x3( T * invmax, B * invmax, normal ));
    39. }
    edit: Note that the transpose in these functions aren't usually something you want to do, but exists for simplicity of use because most people expect to do mul(matrix, normal), but if you remove the transpose you can do mul(normal, matrix) and get some perf back.
     
    Last edited: Mar 9, 2016
    hippocoder likes this.
  7. Alabatross

    Alabatross

    Joined:
    Dec 15, 2012
    Posts:
    223
    Really appreciate that bgolus, I'll give this a shot :)!