Search Unity

How should I proceed learning shaders?

Discussion in 'Shaders' started by Hotpockets26, Dec 5, 2018.

  1. Hotpockets26

    Hotpockets26

    Joined:
    Dec 16, 2017
    Posts:
    32
    Hello! I am new to shader writing and I just cant seem to wrap my head around some of the basic math in beginner tutorials on youtube. like for example I am doing this great tutorial by N3K.



    After 10:30 in the video I am almost completely lost.

    Code (CSharp):
    1. Shader "N3K/N3KSpotlight"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _CharacterPosition("Char Pos", vector) = (0,0,0,0)
    7.         _CircleRadius("Spotlight size", Range(0,20)) = 3
    8.         _RingSize("Ring Size", Range(0,5)) = 1
    9.         _ColorTint("Outside of the spotlight color", Color) = (0,0,0,0)
    10.     }
    11.     SubShader
    12.     {
    13.         Tags { "RenderType"="Opaque" }
    14.         LOD 100
    15.  
    16.         Pass
    17.         {
    18.             CGPROGRAM
    19.             #pragma vertex vert
    20.             #pragma fragment frag
    21.            
    22.             #include "UnityCG.cginc"
    23.  
    24.             struct appdata
    25.             {
    26.                 float4 vertex : POSITION;
    27.                 float2 uv : TEXCOORD0;
    28.             };
    29.  
    30.             struct v2f
    31.             {
    32.                 float2 uv : TEXCOORD0;
    33.                 float4 vertex : SV_POSITION;
    34.  
    35.                 float3 worldPos : TEXCOORD1; // World postion of that vertex
    36.             };
    37.  
    38.             sampler2D _MainTex;
    39.             float4 _MainTex_ST;
    40.  
    41.             float4 _CharacterPosition;
    42.             float _CircleRadius;
    43.             float _RingSize;
    44.             float4 _ColorTint;
    45.  
    46.            
    47.             v2f vert (appdata v)
    48.             {
    49.                 v2f o;
    50.                 o.vertex = UnityObjectToClipPos(v.vertex);
    51.                 o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    52.  
    53.                 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
    54.  
    55.  
    56.                 return o;
    57.             }
    58.            
    59.             fixed4 frag (v2f i) : SV_Target
    60.             {
    61.                 // By default the plane is black
    62.                 fixed4 col = _ColorTint; // = (0,0,0,0)
    63.                
    64.                 // Distance from the center of the plane to the player. // distance is a helper function.
    65.                 float dist = distance(i.worldPos, _CharacterPosition.xyz);
    66.  
    67.                 //This is the players spotlight
    68.                 if (dist < _CircleRadius)
    69.                 {
    70.                     col = tex2D(_MainTex, i.uv);
    71.                 }
    72.  
    73.                 // This is the blending section
    74.                 else if (dist > _CircleRadius && dist < _CircleRadius + _RingSize)
    75.                 {
    76.                     float blendStrength = dist - _CircleRadius;
    77.                     col = lerp(tex2D(_MainTex, i.uv), _ColorTint, blendStrength / _RingSize);
    78.                 }
    79.                 // This is past bost the players spotlight and the blending section
    80.  
    81.  
    82.                 return col;
    83.             }
    84.             ENDCG
    85.         }
    86.     }
    87. }
    Everything in the fragment function of that code is completely going over my head.

    This isn't the first beginner tutorial where I have felt lost on what seems like should be basic stuff.

    I've always gotten straight A's in algebra and trig. I have no problem with math in unity C# programming, but when it comes to this stuff I am lost. I don't know if I am just in the wrong mindset, maybe I need to go back through my math books and go through them all again, or maybe I just need to keep doing more tutorials till it finally clicks?

    Anyway, if someone has any advice I would appreciate it.

    Thanks!
     
  2. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493
    Well the math in that is not even at the level you confess, so my guess is that it's not the math that is te problem but the logic, shader semantics and presentation.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    Can you give an example of what you're confused by? Maybe try to explain your best guess for what one of the parts you're confused by does, and we can try to see where it's not clicking?

    However my guess at where the confusion is happening is coming from c# the mindset the is usually "do this one set of commands with this one set of data" where shaders are more "do this one set of commands with many sets of data", where the many sets of data come from the GPU in semi-magic and opaque ways.
     
    neoshaman likes this.
  4. Hotpockets26

    Hotpockets26

    Joined:
    Dec 16, 2017
    Posts:
    32
    I think that the most confusing part for me is from the fragment function.

    Code (CSharp):
    1. fixed4 frag (v2f i) : SV_Target
    2.             {
    3.                 // By default the plane is black
    4.                 fixed4 col = _ColorTint; // = (0,0,0,0)
    5.              
    6.                 // Distance from the center of the plane to the player. // distance is a helper function.
    7.                 float dist = distance(i.worldPos, _CharacterPosition.xyz);
    8.  
    9.                 //This is the players spotlight
    10.                 if (dist < _CircleRadius)
    11.                 {
    12.                     col = tex2D(_MainTex, i.uv);
    13.                 }
    14.  
    15.                 // This is the blending section
    16.                 else if (dist > _CircleRadius && dist < _CircleRadius + _RingSize)
    17.                 {
    18.                     float blendStrength = dist - _CircleRadius;
    19.                     col = lerp(tex2D(_MainTex, i.uv), _ColorTint, blendStrength / _RingSize);
    20.                 }
    21.                 // This is past bost the players spotlight and the blending section
    22.  
    23.  
    24.                 return col;
    25.             }
    To get

    I understand setting the green section to be green with
    Code (CSharp):
    1. fixed4 col = _ColorTint; // = (0,0,0,0)
    I understand that
    Code (CSharp):
    1. // Distance from the center of the plane to the player. // distance is a helper function.
    2.                 float dist = distance(i.worldPos, _CharacterPosition.xyz);
    Is just getting the distance from the center of the object to the player.

    But this bit of code
    Code (CSharp):
    1.                 //This is the players spotlight
    2.                 if (dist < _CircleRadius)
    3.                 {
    4.                     col = tex2D(_MainTex, i.uv);
    5.                 }
    I dont understand how checking if the distance is smaller than the _CircleRadius property makes the output on screen a circle.

    and with
    Code (CSharp):
    1.                 // This is the blending section
    2.                 else if (dist > _CircleRadius && dist < _CircleRadius + _RingSize)
    3.                 {
    4.                     float blendStrength = dist - _CircleRadius;
    5.                     col = lerp(tex2D(_MainTex, i.uv), _ColorTint, blendStrength / _RingSize);
    6.                 }
    I dont exactly get what exactly the else if statement is checking for, and how _RingSize fits into it, and why you would add _RingSize to _CircleRadius
    I understand the lerp until it divides the blendStrength by the _RingSize, but that probably has to do with me not understanding how blendStrength or _RingSize comes into play and what exactly they are doing.



    So do you think I should go back to my math books? or am I just in the wrong mindset right now and need to spend more time on tutorials so I can get used to it?

    Thanks!
     
  5. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    That’s your problem right there. That’s getting the distance from the player to the world position of the polygon surface at every pixel on screen the mesh covers.

    You’re missing a fundamental understanding of what the fragment shader is, and how real time rendering works.

    You've got a mesh, which is an array of vertices with data like position, and texture UVs, and an array of vertex indices which are used to define triangles, three vertices at a time.

    You've got a material, which is really just an abstraction to hold a list of additional data, like textures, floats, and vectors, and the shader to use.

    And you've got a shader which is what takes the data and uses it to draw stuff on screen. Shaders in Unity are actually multiple parts that have been grouped together for ease of use, but I'll focus on the main two important parts, the vertex and fragment functions. These are each actually individually separate shaders on the GPU, but we'll ignore that for now.

    The vertex shader's job is to take the vertex data and transform the positions into clip space (think screen space for now), as well as prepare any other data that should be transfered to the fragment shader. This shader function runs multiple times, once for each and every vertex in the mesh being rendered regardless of if it's visible or not. The GPU doesn't know if it's visible yet, that's part of what it's calculating. The GPU takes the screen space positions for each vertex and calculates the on screen pixel coverage of each triangle. Then, for each pixel that's covered, the GPU calculates the data to pass to each fragment shader invokation using barycentric interpolation. Basically blending the three vertex values that make up the triangle depending on how close the pixel as to the vertex within the triangle. The fragment shader then takes that data and calculates a color value as an output. It does this for every single pixel on screen that the triangle is visible at!

    I explain all of that to help explain the worldPos value. In the vertex shader you are calculating the world position for each vertex, then the fragment shader is getting the interpolated value which is the world position on the triangle surface at that pixel.

    If you want to dive into this more, read up on GPU rasterization. But it's a deep rabbit hole once you get past the above overview.
     
    neoshaman likes this.
  6. Hotpockets26

    Hotpockets26

    Joined:
    Dec 16, 2017
    Posts:
    32
    Thank you very much for taking the time to go so in depth with me, I think I need to take a step back to read and research more on more of the inner workings of shaders.

    I found a book on amazon that looks pretty promising by Mike Bailey and Steve Cunningham.
    https://www.amazon.com/Graphics-Sha...f=mt_kindle?_encoding=UTF8&me=&qid=1544058271
    But is says it focuses on GLSL, which from what I understand unity runs HLSL, would this be a huge problem for me since im really only trying to understand the process of writing shaders and how all the inner workings connect?

    If not that book do you have a book or a consistent resource to start with?

    Thanks again for taking the time to help me!
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,343
    GLSL is a common language to use to learn shader programming, and GLSL shares a lot in common with the D3D9 style HLSL you'll see in most Unity shader examples. I've never read that book, so I can't say anything about it.

    There are a ton of resources free online, and threads here in the forum listing some of them. Maybe start there?
     
    Hotpockets26 likes this.
  8. neoshaman

    neoshaman

    Joined:
    Feb 11, 2011
    Posts:
    6,493