CG:指定不在顶点着色器和片段着色器之间插值的变量



我使用GCUnity3D内部编写着色器。

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

这是我从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参数传递给片段着色器。这可能吗?如果是,如何?


编辑

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

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

我正在寻找一种方法来传播这些信息,直到片段着色器(也许可以使用全局变量或类似的东西?如果可以,如何?)。

我不确定这是否算是一个答案,但这对于一个评论来说有点过分。正如比约克所指出的,片段着色器将始终接收插值。如果/当Unity支持Opengl 4.0时,您可能可以访问插值限定符,即禁用插值的"平面",从激发顶点派生所有值。

也就是说,尝试为三角形的所有顶点指定相同的"颜色"值的问题是顶点着色器在顶点上迭代一次,而不是每个三角形。总会有一个"边界"区域,其中某个顶点与不同"颜色"或"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 );
}
}

我知道这是一个老话题,但无论如何都值得回答,因为这是谷歌最热门的结果之一。现在,您可以在常规CG着色器中对变量使用nointerpolation选项。即

nointerpolation fixed3 diff : COLOR0;

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

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

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

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

如果有一些输入要插值,而另一些没有,请使用GL_FLAT渲染一次,使其成为与输出分辨率相同的纹理,然后使用GL_SMOOTH再次渲染,并对纹理进行采样,以读取每个像素的平面值(同时以通常的方式获取插值)。

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

以下步骤对我有效。

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

您可以在GL中更改此项,但glProvokingVertex似乎未公开。

我们正在进行平面着色,这会显著减少顶点数。

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

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

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

最新更新