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. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Implementing a texture into a flatshader

Discussion in 'Shaders' started by PanzerPotato, Sep 4, 2018.

  1. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    Hey guys,

    Recently I found a flat shader that works better than the few I have seen, but the one major problem with it is that I only takes a color and not a texture. So if anyone suggest any modifications so it would take a texture (or even optimize the shader), that would be greatly appreciated!

    Here's the code I found:

    Code (CSharp):
    1. Shader "Custom/Flat Shader 2" {
    2.     Properties
    3.     {
    4.         _Color("Color", Color) = (1,0,0,1)
    5.     }
    6.     SubShader
    7.     {
    8.  
    9.         Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Opaque" }
    10.         Blend SrcAlpha OneMinusSrcAlpha
    11.  
    12.         Pass
    13.         {
    14.  
    15.             CGPROGRAM
    16.             #include "UnityCG.cginc"
    17.             #pragma vertex vert
    18.             #pragma geometry geom
    19.             #pragma fragment frag
    20.          
    21.             uniform float4 _LightColor0;
    22.  
    23.             uniform float4 _Color;
    24.  
    25.             struct v2g
    26.             {
    27.                 float4  pos : SV_POSITION;
    28.                 float3    norm : NORMAL;
    29.                  float2  uv : TEXCOORD0;
    30.             };
    31.          
    32.             struct g2f
    33.             {
    34.                 float4  pos : SV_POSITION;
    35.                 float3  norm : NORMAL;
    36.                 float2  uv : TEXCOORD0;          
    37.                 float3 diffuseColor : TEXCOORD1;
    38.             };
    39.  
    40.             v2g vert(appdata_full v)
    41.             {
    42.                 float3 v0 = mul(unity_ObjectToWorld, v.vertex).xyz;
    43.  
    44.                 v.vertex.xyz = mul((float3x3)unity_WorldToObject, v0);
    45.  
    46.                 v2g OUT;
    47.                 OUT.pos = v.vertex;
    48.                 OUT.norm = v.normal;
    49.                 OUT.uv = v.texcoord;
    50.                 return OUT;
    51.             }
    52.          
    53.             [maxvertexcount(3)]
    54.             void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
    55.             {
    56.                 float3 v0 = IN[0].pos.xyz;
    57.                 float3 v1 = IN[1].pos.xyz;
    58.                 float3 v2 = IN[2].pos.xyz;
    59.  
    60.                 float3 centerPos = (v0 + v1 + v2) / 3.0;
    61.  
    62.                 float3 vn = normalize(cross(v1 - v0, v2 - v0));
    63.              
    64.                 float4x4 modelMatrix = unity_ObjectToWorld;
    65.                 float4x4 modelMatrixInverse = unity_WorldToObject;
    66.  
    67.                 float3 normalDirection = normalize(
    68.                     mul(float4(vn, 0.0), modelMatrixInverse).xyz);
    69.                 float3 viewDirection = normalize(_WorldSpaceCameraPos
    70.                     - mul(modelMatrix, float4(centerPos, 0.0)).xyz);
    71.                 float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
    72.                 float attenuation = 1.0;
    73.  
    74.                 float3 ambientLighting =
    75.                     UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;
    76.  
    77.                 float3 diffuseReflection =
    78.                     attenuation * _LightColor0.rgb * _Color.rgb
    79.                     * max(0.0, dot(normalDirection, lightDirection));
    80.  
    81.                 g2f OUT;
    82.                 OUT.pos = UnityObjectToClipPos(IN[0].pos);
    83.                 OUT.norm = vn;
    84.                 OUT.uv = IN[0].uv;
    85.                 OUT.diffuseColor = ambientLighting + diffuseReflection;
    86.                 triStream.Append(OUT);
    87.  
    88.                 OUT.pos = UnityObjectToClipPos(IN[1].pos);
    89.                 OUT.norm = vn;
    90.                 OUT.uv = IN[1].uv;
    91.                 OUT.diffuseColor = ambientLighting + diffuseReflection;
    92.                 triStream.Append(OUT);
    93.  
    94.                 OUT.pos = UnityObjectToClipPos(IN[2].pos);
    95.                 OUT.norm = vn;
    96.                 OUT.uv = IN[2].uv;
    97.                 OUT.diffuseColor = ambientLighting + diffuseReflection;
    98.                 triStream.Append(OUT);
    99.              
    100.             }
    101.          
    102.             half4 frag(g2f IN) : COLOR
    103.             {
    104.                 return float4(IN.diffuseColor, 1.0);
    105.             }
    106.          
    107.             ENDCG
    108.  
    109.         }
    110.     }
    111.     FallBack "Diffuse"
    112. }
    113.  
    Thanks!
     
    Last edited: Sep 4, 2018
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    The only property the shader has is for a texture. If that texture isn't showing up on your model as anything other than a solid color, I suspect you're missing UV maps on your model. If you slap a material using this shader on a built in mesh, like the sphere or box, you'll see the texture just fine.
     
  3. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    Whoops, wrong shader! I've changed the above code to the correct one now (where I would like texture implementation).
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    I would highly recommend against using that shader. Geometry shaders are great, but there are cheaper ways of doing flat shading if geometry shaders are available. Specifically using screen space derivatives.

    https://catlikecoding.com/unity/tutorials/advanced-rendering/flat-and-wireframe-shading/

    As for how to add texture support, the previous shader was very simple, and did have texture support. I would look at that shader and see what difference it has. Look through the file and see where _MainTex is used, and what values it uses (mainly stuff related to the uv / texcoord).
     
  5. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    Yeah, I was initally going to use the first shader, but the problem with it is that on the sides facing away from the sun (or ~1/3 of a sphere with this shader), it appears to be pitch-black. The second one doesn't have that isse however, but I'll take a look at your link and see what I learn.

    Thanks!
     
  6. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    Being a guy who knows nothing about shaders, I attempted to intergrate the use of a texture with the second shader, and here's what I thought it would look like:
    Code (CSharp):
    1. Shader "Custom/Flat Shader 4" {
    2.     Properties
    3.     {
    4.         _Color("Color", Color) = (1,1,1,1)
    5.         _MainTex("Albedo", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.  
    10.         Tags { "Queue"="Geometry" "RenderType"="Opaque" "IgnoreProjector"="True"  }
    11.         Blend SrcAlpha OneMinusSrcAlpha
    12.  
    13.         Pass
    14.         {
    15.  
    16.             CGPROGRAM
    17.             #include "UnityCG.cginc"
    18.             #pragma vertex vert
    19.             #pragma geometry geom
    20.             #pragma fragment frag
    21.          
    22.             uniform float4 _LightColor0;
    23.  
    24.             uniform float4 _Color;
    25.             sampler2D _MainTex;
    26.  
    27.             struct v2g
    28.             {
    29.                 float4  pos : SV_POSITION;
    30.                 float3    norm : NORMAL;
    31.                  float2  uv : TEXCOORD0;
    32.             };
    33.          
    34.             struct g2f
    35.             {
    36.                 float4  pos : SV_POSITION;
    37.                 float3  norm : NORMAL;
    38.                 float2  uv : TEXCOORD0;          
    39.                 float3 diffuseColor : TEXCOORD1;
    40.             };
    41.  
    42.             v2g vert(appdata_full v)
    43.             {
    44.                 float3 v0 = mul(unity_ObjectToWorld, v.vertex).xyz;
    45.  
    46.                 v.vertex.xyz = mul((float3x3)unity_WorldToObject, v0);
    47.  
    48.                 v2g o;
    49.                 o.pos = v.vertex;
    50.                 o.norm = v.normal;
    51.                 o.uv = v.texcoord;
    52.  
    53.                 return o;
    54.             }
    55.          
    56.             [maxvertexcount(3)]
    57.             void geom(triangle v2g IN[3], inout TriangleStream<g2f> triStream)
    58.             {
    59.                 float3 v0 = IN[0].pos.xyz;
    60.                 float3 v1 = IN[1].pos.xyz;
    61.                 float3 v2 = IN[2].pos.xyz;
    62.  
    63.                 float3 centerPos = (v0 + v1 + v2) / 3.0;
    64.  
    65.                 float3 vn = normalize(cross(v1 - v0, v2 - v0));
    66.              
    67.                 float4x4 modelMatrix = unity_ObjectToWorld;
    68.                 float4x4 modelMatrixInverse = unity_WorldToObject;
    69.  
    70.                 float3 normalDirection = normalize(
    71.                     mul(float4(vn, 0.0), modelMatrixInverse).xyz);
    72.                 float3 viewDirection = normalize(_WorldSpaceCameraPos
    73.                     - mul(modelMatrix, float4(centerPos, 0.0)).xyz);
    74.                 float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
    75.                 float attenuation = 1.0;
    76.  
    77.                 float3 ambientLighting =
    78.                     UNITY_LIGHTMODEL_AMBIENT.rgb * _Color.rgb;
    79.  
    80.                 float3 diffuseReflection =
    81.                     attenuation * _LightColor0.rgb * _Color.rgb
    82.                     * max(0.0, dot(normalDirection, lightDirection));
    83.  
    84.                 g2f OUT;
    85.                 OUT.pos = UnityObjectToClipPos(IN[0].pos);
    86.                 OUT.norm = vn;
    87.                 OUT.uv = IN[0].uv;
    88.                 OUT.diffuseColor = ambientLighting + diffuseReflection;
    89.                 triStream.Append(OUT);
    90.  
    91.                 OUT.pos = UnityObjectToClipPos(IN[1].pos);
    92.                 OUT.norm = vn;
    93.                 OUT.uv = IN[1].uv;
    94.                 OUT.diffuseColor = ambientLighting + diffuseReflection;
    95.                 triStream.Append(OUT);
    96.  
    97.                 OUT.pos = UnityObjectToClipPos(IN[2].pos);
    98.                 OUT.norm = vn;
    99.                 OUT.uv = IN[2].uv;
    100.                 OUT.diffuseColor = ambientLighting + diffuseReflection;
    101.                 triStream.Append(OUT);
    102.              
    103.             }
    104.          
    105.             half4 frag(g2f i) : COLOR
    106.             {
    107.                 //return float4(i.diffuseColor, 1.0); This was what it originally returned
    108.                 float4 col = tex2D(_MainTex, i.uv);
    109.                 col.rgb *= i.diffuseColor;
    110.                 return col;
    111.             }
    112.          
    113.             ENDCG
    114.  
    115.         }
    116.     }
    117.     FallBack "Diffuse"
    118. }
    119.  
    This only resulted in it being lighted from the wrong angle, and not appearing (in scene view, but black in game view) if a texture was used. As such:
    screenshot.png
     
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    I honestly have no idea why your version of the shader is breaking things. From a cursory glance there's nothing obviously wrong, and doing the same modifications myself produced correct results. However that shader is doing some weird stuff I don't understand the purpose of. Specifically in the vertex shader it's applying the object to world transform, then applying only the rotation and scale of the world to object transform back. The object to world transform gets applied again as part of the UnityObjectToClipPos() function, so it's getting it's position offset applied twice in the most expensive way possible. Delete lines 44 and 46 unless you want that for some reason.

    edit: figured it out.
    You're changing the queue of the shader between the example above and this one. Technically they're both wrong, but it's working as a bit of a fluke of how Unity sets global shader properties when it's set to use the transparent queue. You're missing this line:

    Tags { "LightMode"="ForwardBase" }

    That should go in after the Pass and following {, around line 15. Without that Unity doesn't know that it should be setting the _WorldSpaceLightPos0 value to anything valid. For reasons I don't know it defaults to something similar to the inverse of the expected _WorldSpaceLightPos0 value, but not always.
     
    Last edited: Sep 4, 2018
  8. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    After applying your suggestions, my mesh is now invisible :confused: What could I have done wrong?

    Additionally, I remembering seeing in one of your posts something about "flat" vs. "non-interpolation". Are there any advantages to choosing one over the other?
     
  9. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
  10. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    Code (CSharp):
    1. Shader "Derivative Flat Shading"
    2. {
    3.     Properties {
    4.         _Color ("Color", Color) = (1,1,1,1)
    5.         [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.  
    8.     SubShader
    9.     {
    10.         Tags { "RenderType"="Opaque" }
    11.         Pass {
    12.             Tags { "LightMode"="ForwardBase" }
    13.  
    14.             CGPROGRAM
    15.             #pragma vertex vert
    16.             #pragma fragment frag
    17.             #pragma target 3.0
    18.  
    19.             #include "UnityCG.cginc"
    20.  
    21.             float4 _LightColor0;
    22.  
    23.             fixed4 _Color;
    24.             sampler2D _MainTex;
    25.  
    26.             struct v2f {
    27.                 float4 pos : SV_POSITION;
    28.                 float2 uv : TEXCOORD0;
    29.                 float3 cameraRelativeWorldPos : TEXCOORD1;
    30.             };
    31.  
    32.             v2f vert (appdata_full v)
    33.             {
    34.                 v2f o;
    35.                 o.pos = UnityObjectToClipPos(v.vertex);
    36.                 o.uv = v.texcoord;
    37.                 o.cameraRelativeWorldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1)).xyz - _WorldSpaceCameraPos.xyz;
    38.                 return o;
    39.             }
    40.  
    41.             fixed4 frag (v2f i) : SV_Target
    42.             {
    43.                 half3 normal = normalize(cross(ddy(i.cameraRelativeWorldPos), ddx(i.cameraRelativeWorldPos)));
    44.  
    45.                 half ndotl = saturate(dot(normal, _WorldSpaceLightPos0.xyz));
    46.                 half3 lighting = ndotl * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.rgb;
    47.  
    48.                 fixed4 col = tex2D(_MainTex, i.uv) * _Color;
    49.  
    50.                 return fixed4(col.rgb * lighting, 1.0);
    51.             }
    52.  
    53.             ENDCG
    54.         }
    55.     }
    56. }
     
    DavidSWu likes this.
  11. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    Wow, thanks! This is probably the most efficient flat shader I have seen so far (and now I realize how much I should learn about writing shaders). Should I do anything to make this more accessable to other people?
     
  12. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    Also, how do I add fog to the shader you provided? Here's what I have so far:
    Code (CSharp):
    1. Shader "Custom/Flat Shader"
    2. {
    3.     Properties {
    4.         _Color ("Color", Color) = (1,1,1,1)
    5.         [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Opaque" }
    10.         Pass {
    11.             Tags { "LightMode"="ForwardBase" }
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.             #pragma multi_compile_fog
    16.             #pragma target 3.0
    17.             #include "UnityCG.cginc"
    18.             float4 _LightColor0;
    19.             fixed4 _Color;
    20.             sampler2D _MainTex;
    21.             struct v2f {
    22.                 float4 pos : SV_POSITION;
    23.                 float2 uv : TEXCOORD0;
    24.                 float3 cameraRelativeWorldPos : TEXCOORD1;
    25.                 UNITY_FOG_COORDS (2)
    26.             };
    27.             v2f vert (appdata_full v)
    28.             {
    29.                 v2f o;
    30.                 o.pos = UnityObjectToClipPos(v.vertex);
    31.                 o.uv = v.texcoord;
    32.                 o.cameraRelativeWorldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1)).xyz - _WorldSpaceCameraPos.xyz;
    33.  
    34.                 UNITY_TRANSFER_FOG (o, o.pos);
    35.  
    36.                 return o;
    37.             }
    38.             fixed4 frag (v2f i) : SV_Target
    39.             {
    40.                 half3 normal = normalize(cross(ddy(i.cameraRelativeWorldPos), ddx(i.cameraRelativeWorldPos)));
    41.                 half ndotl = saturate(dot(normal, _WorldSpaceLightPos0.xyz));
    42.                 half3 lighting = ndotl * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.rgb;
    43.                 fixed4 col = tex2D(_MainTex, i.uv) * _Color;
    44.                 UNITY_APPLY_FOG (i.fogCoord, col);
    45.                 return fixed4(col.rgb * lighting, 1.0);
    46.             }
    47.             ENDCG
    48.         }
    49.     }
    50. }
    But this only results in the mesh getting tinted the color of the fog, rather than being affected with Unity's fog effect.
     
  13. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
    You need to apply the fog after lighting, not before.
     
  14. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    Sorry, I still know zero cg :oops: But would that mean I put the
    UNITY_APPLY_FOG
    after col is multiplied by lighting?
     
  15. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,230
  16. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    Cool!
     
  17. PanzerPotato

    PanzerPotato

    Joined:
    Oct 28, 2017
    Posts:
    21
    For those who are watching this thread and would like the shader, here's where it is now:
    Code (CSharp):
    1. Shader "Custom/Flat Shader"
    2. {
    3.     Properties {
    4.         _Color ("Color", Color) = (1,1,1,1)
    5.         [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.     SubShader
    8.     {
    9.         Tags { "RenderType"="Opaque" }
    10.         Pass {
    11.             Tags { "LightMode"="ForwardBase" }
    12.             CGPROGRAM
    13.             #pragma vertex vert
    14.             #pragma fragment frag
    15.             #pragma multi_compile_fog
    16.             #pragma target 3.0
    17.             #include "UnityCG.cginc"
    18.             float4 _LightColor0;
    19.             fixed4 _Color;
    20.             sampler2D _MainTex;
    21.             struct v2f {
    22.                 float4 pos : SV_POSITION;
    23.                 float2 uv : TEXCOORD0;
    24.                 float3 cameraRelativeWorldPos : TEXCOORD1;
    25.                 UNITY_FOG_COORDS (2)
    26.             };
    27.             v2f vert (appdata_full v)
    28.             {
    29.                 v2f o;
    30.                 o.pos = UnityObjectToClipPos(v.vertex);
    31.                 o.uv = v.texcoord;
    32.                 o.cameraRelativeWorldPos = mul(unity_ObjectToWorld, float4(v.vertex.xyz, 1)).xyz - _WorldSpaceCameraPos.xyz;
    33.  
    34.                 UNITY_TRANSFER_FOG (o, o.pos);
    35.  
    36.                 return o;
    37.             }
    38.             fixed4 frag (v2f i) : SV_Target
    39.             {
    40.                 half3 normal = normalize(cross(ddy(i.cameraRelativeWorldPos), ddx(i.cameraRelativeWorldPos)));
    41.                 half ndotl = saturate(dot(normal, _WorldSpaceLightPos0.xyz));
    42.                 half3 lighting = ndotl * _LightColor0.xyz + UNITY_LIGHTMODEL_AMBIENT.rgb;
    43.                 fixed4 col = tex2D(_MainTex, i.uv) * _Color;
    44.                 col = fixed4(col.rgb * lighting, 1.0);
    45.  
    46.                 UNITY_APPLY_FOG (i.fogCoord, col);
    47.                 return col;
    48.             }
    49.             ENDCG
    50.         }
    51.     }
    52. }