顶点渐变着色器旋转?

时间:2018-11-13 16:38:00

标签: unity3d opengl shader fragment-shader vertex-shader

我有一个着色器,可让我创建和旋转2或3个颜色渐变。我的问题是它在GPU上非常繁重,因此我将这部分代码从片段着色器移到了顶点着色器:

 fixed4 frag (v2f i) : SV_Target
            {   
                //STARTS HERE
                float2 uv =  - (i.screenPos.xy / i.screenPos.w - 0.5)*2;

                fixed3 c;                        
                #if _BG_COLOR_GRADIENT2
                    c = lerp(_BgColor1,_BgColor3,clampValue(rotateUV(uv.xy,_BgColorRotation*PI).y,_BgColorPosition));                                            
                #elif _BG_COLOR_GRADIENT3
                    c = lerp3(_BgColor1,_BgColor2,_BgColor3,clampValue(rotateUV(uv.xy,_BgColorRotation*PI).y,_BgColorPosition),_BgColorPosition3);
                #endif 
                //ENDS HERE         

                return fixed4(c, i.color.a);
            }

现在我的着色器看起来像这样:

Shader "Custom/Gradient"
{
    Properties
    {
        [KeywordEnum(Gradient2, Gradient3)] _BG_COLOR ("Color Type", Float) = 1
        _Color("Color", Color) = (1, 1, 1, 1)
        _BgColor1 ("Start Color",Color) = (0.667,0.851,0.937,1)
        _BgColor2 ("Middle Color",Color) = (0.29, 0.8, 0.2,1)
        _BgColor3 ("End Color",Color) = (0.29, 0.8, 0.2,1)
        [GradientPositionSliderDrawer]
        _BgColorPosition ("Gradient Position",Vector) = (0,1,0)
        _BgColorRotation ("Gradient Rotation",Range(0,2)) = 0
        _BgColorPosition3 ("Middle Size",Range(0,1)) = 0

    }

    SubShader
    {
        Tags{ "Queue" = "Background" "IgnoreProjectors"="True" }

        Blend SrcAlpha OneMinusSrcAlpha
        AlphaTest Greater .01
        ColorMask RGB
        Cull Off Lighting Off ZWrite Off
        BindChannels {
        Bind "Color", color
        Bind "Vertex", vertex
        Bind "TexCoord", texcoord
    }

    Pass
    {
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #pragma shader_feature _BG_COLOR_GRADIENT2 _BG_COLOR_GRADIENT3

        #include "UnityCG.cginc"
        #include "GradientHelper.cginc"

        struct appdata
        {
           float4 vertex : POSITION;
           fixed4 color : COLOR;
        };

        struct v2f
        {                
            float4 pos : SV_POSITION;
            float4 screenPos : TEXCOORD4;
            fixed4 color : COLOR;
        };

        fixed4 _BgColor1;
        fixed4 _BgColor2;
        fixed4 _BgColor3;
        float _BgColorRotation;
        float2 _BgColorPosition;
        float _BgColorPosition3;
        float4 _Color;

        v2f vert (appdata v)
        {
            v2f o;
            o.pos = UnityObjectToClipPos(v.vertex);
            o.screenPos = ComputeScreenPos(o.pos);

            float2 uv =  - (o.screenPos.xy / o.screenPos.w - 0.5)*2;                         

            #if _BG_COLOR_GRADIENT2
                o.color = lerp(_BgColor1,_BgColor3,clampValue(rotateUV(uv.xy,_BgColorRotation*PI).y,_BgColorPosition)) * v.color;                                            
            #elif _BG_COLOR_GRADIENT3
                o.color = lerp3(_BgColor1,_BgColor2,_BgColor3,clampValue(rotateUV(uv.xy,_BgColorRotation*PI).y,_BgColorPosition),_BgColorPosition3) * v.color;
            #endif

            return o;
        }

        fixed4 frag (v2f i) : COLOR {
            return i.color;
        }
        ENDCG
        }
    }
    CustomEditor "Background.Editor.BackgroundGradientEditor"
}

(这是我的着色器助手):

#ifndef PI
#define PI 3.141592653589793
#endif
#ifndef HALF_PI
#define HALF_PI 1.5707963267948966
#endif

// Helper Funtions

inline float clampValue(float input, float2 limit) 
{
    float minValue = 1-limit.y;
    float maxValue = 1-limit.x;
    if(input<=minValue){
        return 0;
    } else if(input>=maxValue){
        return 1;
    } else {
        return (input - minValue )/(maxValue-minValue);
    }                       
}

inline float2 rotateUV(fixed2 uv, float rotation) 
{
    float sinX = sin (rotation);
    float cosX = cos (rotation);
    float2x2 rotationMatrix = float2x2(cosX, -sinX, sinX, cosX);
    return mul ( uv, rotationMatrix )/2 + 0.5;
}

inline fixed4 lerp3(fixed4 a, fixed4 b, fixed4 c, float pos, float size){

    float ratio2 = 0.5+size*0.5;
    float ratio1 = 1-ratio2;
    if(pos<ratio1)
        return lerp(a,b,pos/ratio1);
    else if(pos>ratio2)
        return lerp(b,c,(pos-ratio2)/ratio1);               
    else
        return b;
}    
#endif

现在的性能很棒,但是旋转完全混乱了(在3色渐变中最明显),我似乎无法弄清楚为什么。

1 个答案:

答案 0 :(得分:1)

我从不理解为什么人们要在着色器中进行渐变,除非您在每一帧更改值,否则它会非常有限,并且不一定具有更高的性能。我最好的解决方案是在CPU上将渐变生成为纹理,大小为1x128。使用Unity提供的Gradient类,然后循环:

Texture2D texture = new Texture2D(128, 1);
Color[] pixels = Color[128];
for (int i = 0; i < 128; i++) {
    pixels[i] = gradient.Evaluate(i/127f);
}
texture.SetPixels(pixels);
texture.Apply();

使用以下命令将其发送到着色器:

material.SetTexture("_Gradient", texture)

然后,您可以像以前一样使用2x2矩阵沿所有纹理旋转和滚动。只要确保将纹理溢出模式设置为钳制而不重复即可。请记住,您可以在自己的行为中实现OnValidate()以在编辑器中应用值更新,但是,如果您需要在构建中对其进行更新,则将需要以其他方式监听更改。

使用顶点颜色对于渐变确实有用,因为这些渐变是在硬件中插值的……但是据我了解,这是一个屏幕空间效果,因此,您将需要顶点与实际颜色对齐渐变带。