5

GC用于在里面编写着色器Unity3D

我正在使用顶点颜色属性将一些参数传递给着色器。它们不会用于定义颜色,并且应该从顶点着色器转发到像素着色器而不修改它们。

这是我从 Unity3D 输入到顶点着色器的结构:

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
};

这是顶点着色器作为片段输入返回的结构:

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

如果我只是简单地将参数转发给片段着色器,当然它会被插值:

v2f vert (appdata_full v)
{

  v2f output;
  //....
  output.col = v.color;
}

我想将v.color未插值的参数传递给片段着色器。这可能吗?如果是的话怎么办?


编辑

就像蒂姆指出的那样,这是预期的行为,因为如果颜色从顶点着色器传递到片段,着色器除了插值颜色之外什么也做不了。我将尝试更好地解释我想要实现的目标。我使用每个顶点颜色来存储颜色以外的其他类型的信息。在不告诉我正在做什么的所有细节的情况下,假设您可以将每个颜色顶点视为一个 id(同一三角形的每个顶点将具有相同的颜色。实际上是同一网格的每个顶点)。

所以我使用颜色技巧来掩盖一些参数,因为我没有其他方法可以做到这一点。现在,这条信息必须以某种方式在片段着色器中可用。如果作为顶点着色器的输出参数传递,则编码为颜色的此信息将被插值到无法再使用的片段中。

我正在寻找一种在片段着色器之前传播此信息不变的方法(也许可以使用全局变量或类似的东西?如果是的话如何?)。

4

6 回答 6

10

我不确定这是否可以作为答案,但对于评论来说有点多。正如 Bjorke 指出的那样,片段着色器总是会收到一个插值。如果/当 Unity 支持 Opengl 4.0 时,您可能可以访问Interpolation qualifiers,即禁用插值的 'flat',从激发的 vertex派生所有值。

也就是说,尝试为三角形的所有顶点分配相同的“颜色”值的问题是顶点着色器迭代顶点一次,而不是每个三角形。总会有一个“边界”区域,其中某个顶点与具有不同“颜色”或“id”的其他顶点共享多条边,请参见下面我的愚蠢示例。当应用于 (0,0,0) 处的框时,顶部为红色,底部为绿色,中间为蓝色。

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
}
} 
}

在您的附录说明中,您说“实际上是同一网格的每个顶点”。如果是这样,为什么不使用可修改的属性,就像我上面提到的那样。每个网格只需要一个脚本来更改 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 );
}
}
于 2012-12-18T22:07:38.667 回答
5

我知道这是一个旧线程,但无论如何都值得回答,因为这是谷歌搜索结果中的佼佼者。您现在可以对常规 CG 着色器中的变量使用 nointerpolation 选项。IE

nointerpolation fixed3 diff : COLOR0;
于 2018-11-22T12:37:28.400 回答
3

这是一个非常古老的线程,但我最近遇到了类似的问题,我找到了一个超级简单的答案。OSX Mavericks 现在支持 OpenGL 4.1,所以很快它就不会成为问题,但 Unity3d 可能需要一段时间才能接受它。无论如何,即使在早期的 OSX(例如 Mountain Lion)上,也有一种巧妙的方法可以在 Unity 中启用平面着色!

下面的着色器将完成这项工作(关键部分是带有#extension 的行,否则使用关键字 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
  }
  }
}

通过结合以下内容得到它: http://poniesandlight.co.uk/notes/flat_shading_on_osx/ http://en.wikibooks.org/wiki/GLSL_Programming/Unity/Debugging_of_Shaders

于 2014-02-14T09:54:52.143 回答
2

GPU 将始终在值之间进行插值。如果您想要一个三角形的常数值,您需要为该三角形的所有顶点设置相同的值。这有时可能效率低下,但这就是 OpenGL(和 DirectX)的工作方式。没有“面子”价值的固有概念。

于 2012-12-18T06:19:04.930 回答
1

你可以这样做:glShadeModel(GL_FLAT)。这会关闭所有片段着色器输入的插值,并且在较旧的 OpenGL 中也可用(4.0 之前)。

如果您有一些想要插值的输入而一些您没有,则使用 GL_FLAT 渲染一次到与输出分辨率相同的纹理,然后使用 GL_SMOOTH 再次渲染并采样纹理以读取每个像素的平面值(同时也以通常的方式获得插值)。

如果您可以改用 DirectX9,则可以在单个片段着色器输入(着色器模型 4 或更高版本)上使用nointerpolation修改器。

于 2012-12-20T19:47:48.443 回答
1

以下步骤对我有用。

不幸的是,DX 使用顶点 0 作为激发顶点,而 GL 默认使用 2。

您可以在 GL 中更改它,但 glProvokingVertex 似乎没有暴露。

我们正在做平面着色,这显着减少了我们的顶点数。

我们必须重新排序三角形并以特殊方式计算法线(如果有人感兴趣,我可以发布示例源)。

问题是我们必须在 GL 和 DX 上有不同的网格,因为三角形索引需要旋转,以便三角形使用适当的激发顶点。

也许有一些方法可以通过插件执行 GL 命令。

于 2019-04-22T06:45:18.610 回答