顶点渐变着色器旋转?

问题描述 投票:-1回答:1

我有一个着色器,允许我创建和旋转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色渐变),我似乎无法弄明白为什么。

unity3d opengl shader fragment-shader vertex-shader
1个回答
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()以在编辑器中应用值更新,但如果您需要在构建中更新它,则需要以其他方式监听更改。

使用顶点颜色确实对渐变很有用,因为它们是在硬件中插值的......但根据我的理解,这是一个屏幕空间效果,因此你需要顶点与实际的渐变带对齐。

© www.soinside.com 2019 - 2024. All rights reserved.