CG: Specify a variable not to be interpolated between vertex and fragment shader

13,574

Solution 1

I'm not sure this counts for an answer but it's a little much for a comment. As Bjorke points out, the fragment shader will always receive an interpolated value. If/when Unity supports Opengl 4.0 you might have access to Interpolation qualifiers, namely 'flat' that disables interpolation, deriving all values from a provoking vertex.

That said, the problem with trying to assign the same "color" value to all vertices of a triangle is that the vertex shader iterates over the vertices once, not per triangle. There will always be a "boundary" region where some vertex shares multiple edges with other vertices of a different "color" or "id", see my dumb example below. When applied to a box at (0,0,0), the top will be red, the bottom green, and the middle blue.

Shader "custom/colorbyheight" {
Properties {
 _Unique_ID ("Unique Identifier", float) = 1.0
}
SubShader {
Pass {
  CGPROGRAM
  #pragma vertex vert
  #pragma fragment frag
  #include "UnityCG.cginc"
  struct v2f {
      float4 pos : SV_POSITION;
      fixed4 color : COLOR;
  };
  uniform float _Unique_ID;
  v2f vert (appdata_base v)
  {
      v2f o;
      o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
      float3 worldpos = mul(_Object2World, v.vertex).xyz;
      if(worldpos[1] >= 0.0)
        o.color.xyz = 0.35;  //unique_id = 0.35
      else
        o.color.xyz = 0.1;   //unique_id = 0.1
      o.color.w = 1.0;
      return o;
  }


  fixed4 frag (v2f i) : COLOR0 { 
    // local unique_id's set by the vertex shader and stored in the color
    if(i.color.x >= 0.349 && i.color.x <=0.351)
        return float4(1.0,0.0,0.0,1.0); //red
    else if(i.color.x >= 0.099 && i.color.x <=0.11)
        return float4(0.0,1.0,0.0,1.0); //green

    // global unique_id set by a Unity script
    if(_Unique_ID == 42.0)
        return float4(1.0,1.0,1.0,1.0); //white

    // Fallback color = blue
    return float4(0.0,0.0,1.0,1.0);
  }
  ENDCG
}
} 
}

In your addendum note you say "Actually each vertex of the same mesh." If that's the case, why not use a modifiable property, like I have included above. Each mesh just needs a script then to change the unique_id.

public class ModifyShader : MonoBehaviour {
public float unique_id = 1;
// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
    renderer.material.SetFloat( "_Unique_ID", unique_id );
}
}

Solution 2

I know this is an old thread, but it's worth answering anyway since this is one of the top google results. You can now use the nointerpolation option for your variables in regular CG shaders. i.e.

nointerpolation fixed3 diff : COLOR0;

Solution 3

This is a pretty old thread, but I recently had a similar issue and I found a super simple answer. OSX Mavericks now supports OpenGL 4.1 so soon it won't be an issue at all, but it still may take a while before Unity3d picks it up. Anyway, there is a neat way to enable flat shading in Unity even on earlier OSX (e.g. Mountain Lion) !

The shader below will do the job (the crucial part is the line with #extension, otherwise you'd get a compilation error for using a keyword flat"

Shader "GLSL flat shader" {
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
  }
  }
}

Got to it by combining things from: http://poniesandlight.co.uk/notes/flat_shading_on_osx/ http://en.wikibooks.org/wiki/GLSL_Programming/Unity/Debugging_of_Shaders

Solution 4

The GPU will always interpolated between values. If you want a constant value for a triangle, you need to set the same value for all vertices of that triangle. This can at times be inefficient, but it's how OpenGL (and DirectX) works. There is no inherent notion of a "face" value.

Solution 5

You might do this: glShadeModel(GL_FLAT). This turns off interpolation for all fragment shader inputs, and is available in older OpenGL also (pre 4.0).

If you have some inputs you want to interpolate and some you don't, render once with GL_FLAT to a texture of the same resolution as your output, and then render again with GL_SMOOTH and sample the texture to read the flat values for each pixel (while also getting interpolated values in the usual way).

If you could use DirectX9 instead, you can use the nointerpolation modifier on individual fragment shader inputs (shader model 4 or later).

Share:
13,574
Heisenbug
Author by

Heisenbug

Updated on June 04, 2022

Comments

  • Heisenbug
    Heisenbug almost 2 years

    I'm using GC for writing shaders inside Unity3D.

    I'm using vertex colors attributes for passing some parameters to the shader. They won't be used so for defining colors, and should be forwarded from vertex shader to pixel shader without modifyng them.

    This is the structure I'm taking as input from Unity3D to the vertex shader:

    struct appdata_full {
        float4 vertex : POSITION;
        float4 tangent : TANGENT;
        float3 normal : NORMAL;
        float4 texcoord : TEXCOORD0;
        float4 texcoord1 : TEXCOORD1;
        fixed4 color : COLOR;
    #if defined(SHADER_API_XBOX360)
        half4 texcoord2 : TEXCOORD2;
        half4 texcoord3 : TEXCOORD3;
        half4 texcoord4 : TEXCOORD4;
        half4 texcoord5 : TEXCOORD5;
    #endif
    };
    

    This is the structure returned by vertex shader as input to the fragment:

    struct v2f {
      float4 pos : SV_POSITION;
      float2  uv : TEXCOORD0;
      fixed4 col: COLOR;           
    };
    

    If I simply forward the parameter to the fragment shader, of course it will be interpolated:

    v2f vert (appdata_full v)
    {
    
      v2f output;
      //....
      output.col = v.color;
    }
    

    I'd like to pass v.color parameter not interpolated to the fragment shader. Is this possible?if yes how?


    EDIT

    like Tim pointed out, this is the expected behavior, because of the shader can't do anything else than interpolating colors if those are passed out from vertex shader to fragment. I'll try to explain better what I'm trying to achieve. I'm using per vertex colors to store other kind of information than colors. Without telling all details on what I'm doing with that, let's say you can consider each color vertex as an id(each vertex of the same triangle, will have the same color. Actually each vertex of the same mesh).

    So I used the color trick to mask some parameters because I have no other way to do this. Now this piece of information must be available at the fragment shader in some way. If a pass as an out parameter of the vertex shader, this information encoded into a color will arrive interpolated at the fragment, that can't no longer use it.

    I'm looking for a way of propagating this information unchanged till the fragment shader (maybe is possible to use a global variable or something like that?if yes how?).