2012-12-14 24 views
5

我使用GCUnity3D內部編寫着色器。CG:指定一個不能在頂點和片段着色器之間插入的變量

我使用頂點顏色屬性將一些參數傳遞給着色器。它們不會用於定義顏色,並且應該從頂點着色器轉發到像素着色器而不用修改它們。

這是我作爲輸入從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(同一個三角形的每個頂點,將具有相同的顏色,實際上是相同的網格的每個頂點)。

所以我使用顏色技巧來掩蓋一些參數,因爲我沒有其他方法來做到這一點。現在這條信息必須以某種方式在片段着色器中可用。 如果通過作爲頂點着色器的輸出參數,則編碼爲顏色的該信息將在該片段處插入,不能再使用它。

我正在尋找一種傳播這些信息的方式,直到片段着色器(也許可以使用全局變量或類似的東西?如果是的話)。

+0

你將如何協調每個像素的三個輸入(頂點),如果你不插值你能指望什麼發生,如果三角形的每個頂點具有不同的值 – Tim

+0

@Tim? :好吧,你說得對,我明白你的反對意見。重點是,我的着色器中實際上顏色不是顏色。我使用顏色值來存儲其他類型的信息。我需要將每個頂點信息從頂點轉發到片段着色器,最終不會將其作爲頂點着色器的參數傳遞,而是使用某些「全局變量」(如果可能的話)。我希望清楚。 – Heisenbug

+0

如果它是一種顏色或索引或其他東西,這並不重要。可以說你正在畫一個三角形。基於一些任意的每頂點計算;頂點0輸出「5」,頂點1輸出「10」,頂點2輸出「15」。您要在片段着色器中顯示這三個數字中的哪一個?如果它的確是一種全球價值而不是使用制服,但是否則我認爲你並沒有完全想到這一點。除非你只是說「我總是希望頂點0的值而忽略其他兩個」,這沒有多大意義。 – Tim

回答

10

我不確定這是否是答案,但對評論有點意義。正如Bjorke指出的那樣,片段着色器將始終接收插值。如果/當Unity支持Opengl 4.0時,您可能有權訪問Interpolation qualifiers,即「平坦」,禁用插值,從provoking 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); 
} 
} 
+0

嗯,謝謝您的回答。我無法使用SetFloat,因爲我正在通過StaticBatchingUtility對繪製調用進行批處理。如果我修改一個Material的值,Unity將爲每個對象實例化一個新的Material實例,從而阻止它使用sharedMaterial,因此它不再被批量處理。所以我不得不使用每個頂點分配的顏色參數來爲每個網格指定自定義着色器值。 – Heisenbug

+0

其實我使用每個顏色頂點作爲id和紋理間接索引。不幸的是,這些值應該在片段着色器中使用。使用在片段和頂點程序之間共享的3色全局變量(或者指定一些不需要插值的輸出參數)可以幫助我實現一些間接點的更有效的實現。 – Heisenbug

+0

臭蟲,是不會爲你工作的。一個「解決方案」,我不喜歡這個,因爲它是hacky和緩慢的,是通過只允許每個頂點形成2個邊緣使網格的所有三角形獨一無二。通過這種方式,線性插值僅發生在單個三角形的頂點之間,從而使您回到正確的「ID」。 – Jerdak

2

GPU將始終在值之間插值。如果你想要一個三角形的常量值,你需要爲該三角形的所有頂點設置相同的值。這有時可能是低效的,但它是OpenGL(和DirectX)的工作原理。沒有「面子」價值的固有概念。

+0

我不能使用頂點着色器實例和所有相關片段着色器之間共享的一些全局變量嗎? – Heisenbug

+0

這不是硬件的佈局方式,常量是每個網格,而不是每個三角形,每個網格只有一個頂點和一個片段着色器,插入的「連接器」寄存器是這兩者之間唯一通信的通道 – bjorke

+0

感謝您的澄清 – Heisenbug

1

您可能會這樣做:glShadeModel(GL_FLAT)。這將關閉所有片段着色器輸入的插值,並且也可以在舊版OpenGL中使用(4.0之前版本)。

如果你有一些你想要插入的輸入,有些你不需要,用GL_FLAT渲染一次與你的輸出相同分辨率的紋理,然後用GL_SMOOTH再次渲染紋理並讀取平坦值對於每個像素(同時也以通常的方式獲得插值)。

如果您可以改爲使用DirectX9,則可以在各個片段着色器輸入(着色器模型4或更高版本)上使用nointerpolation修改器。

+1

glShadeModel不通過Unity API公開。 – Jerdak

+0

也不在OpenGL ES中(也就是webgl) – bjorke

3

這是一個相當古老的線程,但我最近有一個類似的問題,我發現了一個超級簡單的答案。 OSX Mavericks現在很快就會支持OpenGL 4.1,但這並不是問題,但在Unity3d拿起它之前還需要一段時間。 無論如何,在早期的OSX(例如Mountain Lion)中,有一個很好的方法可以在Unity中啓用平面着色!

着色器下面將做的工作(關鍵部分是#extension行,否則,你會得到一個編譯錯誤使用關鍵字平」由合併的事情

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

相關問題