[SOLVED] Flat shading

I am trying to make a smooth mesh look flat, flattening the normals of each polygons with this shader but getting errors, can someone help out with GLSL (would use sorface shading but I don’t think the flat attribute exists)

Shader "Flattener" {
    SubShader {
        Pass {
            GLSLPROGRAM

            #extension GL_EXT_gpu_shader4 : require
            flat varying vec4 color;

            #ifdef VERTEX
            void main()
            {
                color = gl_Color;
                gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
            }
            #endif

            #ifdef FRAGMENT
            void main()
            {
                gl_FragColor = color; // set the output fragment color
            }
            #endif

            ENDGLSL
        }
    }
}
1 Like

What do the errors you are getting say? If there is more than one, it is usually the top one that matters.

GLSL compilation failed:
ERROR: 0:7: ‘’ : extension ‘GL_EXT_gpu_shader4’ is not supported
ERROR: 0:8: ‘vec4’ : syntax error syntax error

Can you just not smooth the normals?

I want smooth mesh to be flat shaded and this is the way in GLSL

That extension being not supported seems very clear to me, don’t know what you need help with. (did you try to remove that extension line? not sure what needs it, then again I really don’t know much about glsl).

I’m pretty sure vec4 is not valid in Unity. Change it to half4 or float4. As for the other error, it looks like GL_EXT_gpu_shader4 simply isn’t supported, either by Unity or your computer.

Also, can you describe what you mean by “flat shaded”? Try selecting your model, and in the import settings change “Normals” from “Import” to “Calculate”, then mode the slider to 0. Is this what you want?

these changes don’t work.

calculate normal to zero doubles up vertices to break the normal

try adding : #pragma only_renderers glcore

I believe flat was added in OpenGL 3.0, maybe 3.1, but Unity might be trying to compile to 2.0.

You might try adding:
#version 130
Or
#version 140

To force OpenGL 3.0 or 3.1 respectively. I assume those work in Unity with GLSLPROGRAM blocks, I don’t know for sure as I rarely do direct GLSL or HLSL programming with in Unity.

Alternately you could try writing it as a normal Unity shader within a CGPROGRAM and use the nointerpolation qualifier along with #pragma target 4.0

3 Likes

THANKS bgolus ! Just adding nointerpolation in front of the color v2f does the magic!
Most of my shaders are surface shader, nointerpolation gives a syntax error, do you know what the surface shader equivalent is?

magic flat shader

Shader "Diffuse With Shadows"
{
    Properties
    {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            Tags {"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            #include "Lighting.cginc"

            // compile shader into multiple variants, with and without shadows
            // (we don't care about any lightmaps yet, so skip these variants)
            #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
            // shadow helper functions and macros
            #include "AutoLight.cginc"

            struct v2f
            {
                float2 uv : TEXCOORD0;
                SHADOW_COORDS(1) // put shadows data into TEXCOORD1
                nointerpolation fixed3 diff : COLOR0;
                nointerpolation fixed3 ambient : COLOR1;
                float4 pos : SV_POSITION;
            };
            v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.uv = v.texcoord;
                half3 worldNormal = UnityObjectToWorldNormal(v.normal);
                half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
                o.diff = nl * _LightColor0.rgb;
                o.ambient = ShadeSH9(half4(worldNormal,1));
                // compute shadows data
                TRANSFER_SHADOW(o)
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                // compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
                fixed shadow = SHADOW_ATTENUATION(i);
                // darken light's illumination with shadow, keep ambient intact
                fixed3 lighting = i.diff * shadow + i.ambient;
                col.rgb *= lighting;
                return col;
            }
            ENDCG
        }

        // shadow casting support
        UsePass "Legacy Shaders/VertexLit/SHADOWCASTER"
    }
}
3 Likes

I don’t believe there’s a way to do it with surface shaders. Instead you’ll likely have to take the generated vert/frag shader and modify that.

The generated shader was too messy so I transcribed the surface shader to CG. Thanks for the help.

Hello, can anyone explain what happens when you use the keyword “nointerpolation”, the entire triangle is filled with the value calculated for the first vertex?

Correct.

3 Likes

Thank you)

You do know that any of the Unlit shaders that are built into Unity flat shade models right?

Unlit shaders are, well, unlit, sometimes erroneously referred to as “flat shading” or “flatly lit”, but unlit shaders aren’t lit or really “shaded” at all. The flat shading being discussed here, and specifically the GLSL “flat” and HLSL “nointerpolation”, allows for faceted surface shading without having faceted model normals. What the OP is referring to is the “PolyWorld” style faceted surface shading, like this:

The example shader at the start is just testing the GLSL “flat” attribute designation rather than an attempt at the final flat shaded result.

2 Likes

Does that mean it is possible to keep a low vertices count while having the flat style? A visually faceted cube (custom one not the Unity one) will still have 8 vertices instead of 24?

I guess my real question behind it is more how good is it performance wise?

Yep, you can have every vertex welded, though there’s a chance two polygons will end up with the same values if they share the same first vertex.

Performance wise there should be zero performance difference vs the same smooth mesh with a normal shader, and plausibly though unlikely even a very (very, very) minor perf gain over traditional interpolated attributes. On very high poly objects it might even be noticeably faster than manually faceted surfaces.

Unfortunately for the platforms that this would actually matter at these low poly counts, they don’t support nointerpolation / flat (low end mobile) so it’s kind of moot.

3 Likes