制作自己想要的曲线以达成目标效果【shader】

在创作shader时经常需要一些曲线帮助实现效果,比如我想要一个间隔波动的曲线,在想象中我希望他是这个样子的

并且,我希望间隔时间和波动时间都可受我调控且互不影响

推荐使用这样的一个数学网站去逐步实现想要获得的曲线Graphtoy

网站中的x在着色语言中多为时间变量,网站中的t变量我认为多为提供预览曲线变化的功能。

首先这样的波动比较好实现,利用sin,abs,step和lerp即可。

Share Link:

Graphtoy

但是这样并不能够达到目的,如果改动这个式子中的唯一变量x,变化的是波动时间和间隔时间整体。

就像这样子,给x乘以Π缩短至单位周期。

利用取余和step,可以实现控制间隔区间的曲线。

将Π归为单位1,所以一个上升弧度单位是半格

定义f1是间隔数

定义f2是持续数

f3:首先映射f1到f2内的增长为直线0~Π

f4:利用f1、f2计算周期和取余,创建取余周期曲线

f5:利用step和取余,区分出波动段和间隔段

f6:lerp两个曲线,获得最终的值(注意随后的-2是为了让图像移动到下方以方便观察)

Share Link:

Graphtoy

这里的自定义函数实在太少看起来很臃肿,貌似不能自由添加。

总之这样就实现了一个根据数值可随意调整的上升间隔曲线。

接下来把他转换到代码中。

         
            _RiseTime ("RiseTime ", range (0,10)) = 0
            _GapTime ("GapTime ", range (0,10)) = 0

            float _RiseTime;
            float _GapTime;

            float Myfunction1()
            {
                return  abs(sin(fmod((2/_RiseTime)*_Time.y,(2/_RiseTime)*(_RiseTime+_GapTime)*0.5)*UNITY_PI/2));
            }
            float Myfunction2()
            {
                return  step(_RiseTime*0.5,fmod(_Time.y,(_RiseTime+_GapTime)*0.5));
            }
            float Myfunction3()
            {
                return  lerp(Myfunction1(),1-Myfunction2(),Myfunction2());
            }

以颜色表示上升四单位间隔两单位就是

 在一些静态的情况下可以使用Gradiet来替代制作曲线方程

为什么说是在静态的情况下?

因为Gradient的定义非常复杂,下面是这样一个Gradient的定义

        static Gradient _FlowGradient = {0,4,2,{float4(1,1,1,0),float4(0.5676471,0.5676471,0.5676471,0.1088273),float4(0.09179813,0.09179813,0.09179813,0.3882353),float4(0,0,0,1),float4(0,0,0,0),float4(0,0,0,0),float4(0,0,0,0),float4(0,0,0,0)},{float2(1,0),float2(1,0.9941252),float2(0,0),float2(0,0),float2(0,0),float2(0,0),float2(0,0),float2(0,0)}};

而且shader在对外开放的属性中没有Gradient属性。读取Gradient如下。

        void Unity_SampleGradientV1_float(Gradient Gradient, float Time, out float4 Out)
        {
            float3 color = Gradient.colors[0].rgb;
            [unroll]
            for (int c = 1; c < Gradient.colorsLength; c++)
            {
                float colorPos = saturate((Time - Gradient.colors[c - 1].w) / (Gradient.colors[c].w - Gradient.colors[c - 1].w)) * step(c, Gradient.colorsLength - 1);
                color = lerp(color, Gradient.colors[c].rgb, lerp(colorPos, step(0.01, colorPos), Gradient.type));
            }
        #ifdef UNITY_COLORSPACE_GAMMA
            color = LinearToSRGB(color);
        #endif
            float alpha = Gradient.alphas[0].x;
            [unroll]
            for (int a = 1; a < Gradient.alphasLength; a++)
            {
                float alphaPos = saturate((Time - Gradient.alphas[a - 1].y) / (Gradient.alphas[a].y - Gradient.alphas[a - 1].y)) * step(a, Gradient.alphasLength - 1);
                alpha = lerp(alpha, Gradient.alphas[a].x, lerp(alphaPos, step(0.01, alphaPos), Gradient.type));
            }
            Out = float4(color, alpha);
        }

或许可以使用传入数组的方式,从脚本中创建曲线对象,动态改变读取值。但其一是精度问题、其二这也需要计算并动态改变曲线。所以想要什么曲线直接去函数计算我认为是一种合理方式。

-------------------------------------------------------------------------------------------------------------

后记:

在浏览Unity官方示例的时候,发现示例中使用了一种巧妙的解决办法

首先示例使用这样一个ScriptableObject去转化Gradient到textuer2D再传入shader的方法

using System;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "GradientTexture", menuName = "Gradient Texture", order = 310)]
public class GradientTexture : ScriptableObject
{
    [Delayed]
    public int width = 64;
    
    public List<Gradient> gradients = new List<Gradient>();
    private Texture2D _texture;
    
    private void OnEnable()
    {
        if(_texture == null && gradients.Count > 0)
            Generate();
    }

    [ContextMenu("Generate")]
    private void Generate()
    {
        // create texture that is width x gradients.Count
        // for each gradient, draw a line from left to right with the gradient
        // save to disk
        
        if(_texture != null)
            DestroyImmediate(_texture);
        _texture = new Texture2D(width, gradients.Count)
        {
            name = name
        };
        
        for (int i = 0; i < gradients.Count; i++)
        {
            var gradient = gradients[i];
            for (int x = 0; x < width; x++)
            {
                var color = gradient.Evaluate((float)x / width);
                _texture.SetPixel(x, i, color);
            }
        }
        _texture.Apply();
        
        #if UNITY_EDITOR
        // delayed call to save the icon
        UnityEditor.EditorApplication.delayCall += SaveIcon;
        #endif
    }

    private void OnValidate()
    {
        if(gradients.Count > 0)
            Generate();
    }

    [ContextMenu("Save")]
    private void SaveToDisk()
    {
#if UNITY_EDITOR
        // save to disk the texture using the AssetDatabase API
        var path = UnityEditor.AssetDatabase.GetAssetPath(this);
        path = path.Replace(".asset", ".png");
        var bytes = _texture.EncodeToPNG();
        System.IO.File.WriteAllBytes(path, bytes);
        UnityEditor.AssetDatabase.SaveAssets();
        UnityEditor.AssetDatabase.Refresh();

#endif
    }
    
    #if UNITY_EDITOR

    private void SaveIcon()
    {
        // use _texture as the icon for this object
        var path = UnityEditor.AssetDatabase.GetAssetPath(this);
        
        // add as sub asset or replace existing
        var icon = UnityEditor.AssetDatabase.LoadAssetAtPath<Texture2D>(path);
        
        if (icon == null)
        {
            UnityEditor.AssetDatabase.AddObjectToAsset(_texture, this);
        }
        else
        {
            UnityEditor.EditorUtility.CopySerialized(_texture, icon);
        }
        
        UnityEditor.AssetDatabase.SaveAssets();
        UnityEditor.AssetDatabase.Refresh();
    }
    
    #endif
    
}

运行SaveToDisk后去根据Gradient生成一张texture2D,以此传入shader属性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
制作迷雾效果Shader 可以使用 Unity Standard Shader,并在其基础上进行修改。以下是迷雾效果Shader 实现方式: 1. 创建一个新的材质并为其指定 Standard Shader。 2. 在 Shader 中添加一个新的 Pass 并设置其渲染顺序,以便将迷雾效果渲染在场景的最后面。 3. 在新的 Pass 中添加一个新的 SubShader,并将其设置为最后一个 SubShader。这是因为 SubShader 的渲染顺序是从上到下的,因此需要将迷雾效果放在最后一个 SubShader 中。 4. 在 SubShader 中添加一个新的 Surface Shader,以便在表面上渲染迷雾效果。 5. 在 Surface Shader 中添加一个新的 Function,用于计算迷雾效果的颜色。该函数需要接收表面颜色和表面法线作为输入,并返回修改后的颜色。 6. 在 Surface Shader 中的函数中,根据表面到摄像机的距离计算出迷雾效果的强度,然后使用该强度调整表面颜色和透明度。 以下是示例代码: ``` Shader "Custom/FogShader" { Properties { _FogColor ("Fog Color", Color) = (1,1,1,1) _FogStart ("Fog Start", Range(0,1)) = 0.2 _FogEnd ("Fog End", Range(0,1)) = 1.0 } SubShader { Tags {"Queue"="Transparent" "RenderType"="Transparent"} Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 vertex : SV_POSITION; float3 worldPos : TEXCOORD0; float3 worldNormal : TEXCOORD1; }; float4 _FogColor; float _FogStart; float _FogEnd; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); return o; } float4 frag (v2f i) : COLOR { float fog = saturate((i.worldPos.z - _FogStart) / (_FogEnd - _FogStart)); float4 fogColor = lerp(_FogColor, i.color.rgb, fog); return fogColor; } ENDCG } } FallBack "Diffuse" } ``` 在这个示例代码中,我们使用了一个名为 `_FogColor` 的属性来指定迷雾的颜色,使用 `_FogStart` 和 `_FogEnd` 属性来指定迷雾的起始和结束位置。在 Surface Shader 的 `frag` 函数中,我们计算出了表面到摄像机的距离,并使用该距离来计算迷雾的强度。最后将修改后的颜色返回。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值