Unity的Shader学习笔记(20)[21/01/15_周五][66-69]

昨天年会,开了一天的会议....Shader(基础)学完后要好好学习研究一下Unity的性能优化,还得抽空把Unity的基础补一下。工作中没有直接用到的知识,其实都没掌握,以前过了一遍的那本Shader书,现在看来根本没掌握,没掌握的东西,很容易忘记掉。

目录

课时66:FragmentShader-程序纹理水波仿真1

课时67:FragmentShader-程序纹理水波仿真2

课时68:FragmentShader-程序纹理水波仿真3

课时69:FragmentShader-程序纹理水波仿真4


课时66:FragmentShader-程序纹理水波仿真1

视频:https://www.bilibili.com/video/BV1YK41157AC?p=66

这节课就是一个脚本的一部分,WaveTexture

using UnityEngine;

public class WaveTexture : MonoBehaviour
{
    public int waveWidth;
    public int waveHeight;
    float[,] waveA;
    float[,] waveB;

    Texture2D tex_uv;

    // Start is called before the first frame update
    void Start()
    {
        waveA = new float[waveWidth, waveHeight];
        waveB = new float[waveWidth, waveHeight];
        tex_uv = new Texture2D(waveWidth, waveHeight);

    }

    // Update is called once per frame
    void Update()
    {
        //if()
        ComputeWave();
    }

    void ComputeWave()
    {
        for(int w = 1; w < waveWidth-1; w++)
        {
            for(int h = 1; h < waveHeight-1; h++)
            {
                waveB[w, h] = (waveA[w-1, h]+waveA[w+1,h]+waveA[w,h-1]+waveA[w,h+1]+
                    waveA[w-1,h-1]+waveA[w+1,h-1]+waveA[w-1,h+1]+waveA[w+1,h+1]) /4 -waveB[w,h];//>>2 == /4 
                float value = waveB[w, h];
                if (value > 1)
                {
                    waveB[w, h] = 1;
                }
                if (value < -1)
                {
                    waveB[w, h] = -1;
                }

                float offset_u = (waveB[w - 1, h] - waveB[w + 1, h]) / 2;
                float offset_v = (waveB[w, h - 1] - waveB[w, h + 1]) / 2;
            }
        }
    }
}

课时67:FragmentShader-程序纹理水波仿真2

完整的脚本

public class WaveTexture : MonoBehaviour
{
    public int waveWidth = 128;
    public int waveHeight = 128;
    float[,] waveA;
    float[,] waveB;
    Texture2D tex_uv;
    // Start is called before the first frame update
    void Start()
    {
        waveA = new float[waveWidth, waveHeight];
        waveB = new float[waveWidth, waveHeight];
        tex_uv = new Texture2D(waveWidth, waveHeight);

        GetComponent<Renderer>().material.SetTexture("_MainTex", tex_uv);
        Putpop();
    }

    // Update is called once per frame
    void Update()
    {
        ComputeWave();
    }

    void ComputeWave()
    {
        for(int w = 1; w < waveWidth-1; w++)
        {
            for(int h = 1; h < waveHeight-1; h++)
            {
                //根据waveA,算出waveB。如果初始都是1,8/4-1=1,不变
                waveB[w, h] = (waveA[w-1, h]+waveA[w+1,h]+waveA[w,h-1]+waveA[w,h+1]+
                    waveA[w-1,h-1]+waveA[w+1,h-1]+waveA[w-1,h+1]+waveA[w+1,h+1]) /4 -waveB[w,h];//>>2 == /4 
                float value = waveB[w, h];
                if (value > 1)
                {
                    waveB[w, h] = 1;
                }
                if (value < -1)
                {
                    waveB[w, h] = -1;
                }
                //限定范围

                float offset_u = (waveB[w - 1, h] - waveB[w + 1, h]) / 2;
                float offset_v = (waveB[w, h - 1] - waveB[w, h + 1]) / 2;

                float r = offset_u / 2 + 0.5f;
                float g = offset_v / 2 + 0.5f;
                tex_uv.SetPixel(w, h,new Color(r,g,0));

                //能量衰减
                waveB[w, h] -= waveB[w, h] * 0.01f;
            }
        }
        tex_uv.Apply();
        float[,] tmp = waveA;
        waveA = waveB;
        waveB = tmp;
    }

    //投入石头
    void Putpop()
    {
        waveA[waveWidth / 2, waveHeight / 2] = 1;
        waveA[waveWidth / 2 - 1, waveHeight / 2] = 1;
        waveA[waveWidth / 2 + 1, waveHeight / 2] = 1;
        waveA[waveWidth / 2, waveHeight / 2 - 1] = 1;
        waveA[waveWidth / 2, waveHeight / 2 + 1] = 1;
        waveA[waveWidth / 2 - 1, waveHeight / 2 - 1] = 1;
        waveA[waveWidth / 2 - 1, waveHeight / 2 + 1] = 1;
        waveA[waveWidth / 2 + 1, waveHeight / 2 - 1] = 1;
        waveA[waveWidth / 2 + 1, waveHeight / 2 + 1] = 1;
        //中间的八个点,周围的能量都设置为1
    }
}

加上一个简单的显示贴图的shader,效果:

在贴图基础上做水波效果

Shader "Custom/NewSurfaceShader 28"
{
    Properties
    {
        _MainTex("MainTex",2D) = ""{}
        _F("F",range(0,1))=0.1
    }
    SubShader
    {
        pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "unitycg.cginc"
            struct v2f{
                float4 pos:POSITION;
                float2 uv:TEXCOORD0;
            };
            sampler2D _MainTex;//原图
            sampler2D _WaveTex;
            float4 _MainTex_ST;
            float _F;
            v2f vert(appdata_base v){
                v2f o;
                o.pos=UnityObjectToClipPos(v.vertex);//屏幕坐标
                o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);//uv坐标
                return o;
            }
            fixed4 frag(v2f IN) :COLOR
            {
                float2 offset_uv= tex2D(_WaveTex,IN.uv).xy; //uv偏移
                offset_uv = offset_uv * 2 - 1;//(0,1)=>(-1,1)
                offset_uv *= _F;//_F:水波幅度
                IN.uv += offset_uv;
                fixed4 mainColor=tex2D(_MainTex,IN.uv);
                fixed4 color=mainColor;
                return color;
            }
            ENDCG
        }
    }
}

脚本里面设置_WaveTex

最终效果:

课时68:FragmentShader-程序纹理水波仿真3

脚本设置点击点的能量

void Update()
    {
        ComputeWave();
        if (Input.GetMouseButton(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if(Physics.Raycast(ray,out hit))
            {
                Vector3 pos = hit.point;
                Vector3 localPos = transform.worldToLocalMatrix.MultiplyPoint(pos);
                Debug.Log(string.Format("pos:{0},localPos:{1}", pos, localPos));//(-0.5,0.5)
                int w = (int)((localPos.x + 0.5) * waveWidth);
                int h = (int)((localPos.y + 0.5) * waveHeight);
                PutDrop(w,h);
            }
        }
    }

    public int Radius = 10;

    private void PutDrop(int x,int y)
    {
        Debug.LogWarning(string.Format("PutDrop:({0},{1})",x,y));
        int radius = Radius;
        double dist;
        for(int i=-radius;i<=radius;i++)
        {
            for(int j=-radius;j<=radius;j++)
            {
                if(((x+i>=0) && (x+i<waveWidth-1)) && ((y+j>=0)&&(y+j<waveHeight)))
                {
                    dist = Mathf.Sqrt(i * i + j * j);
                    if (dist < radius)
                    {
                        waveA[x + i, y + j] = Mathf.Cos((float)(dist * Mathf.PI / radius));
                    }
                }
            }
        }
    }

结果来看,基本都是在脚本中计算的,传递给Shader,Shader再拿来做uv偏移。

说实话,ComputeWave这个不断计算能量衰减的算法不是很理解,直观的理解,让我从头自己写是写不出来的。

代码没多少,得到的效果还是很神奇的。

研究了一下,有点直观的概念

                //waveB[w, h] = waveA[w, h];//不动
                //waveB[w, h] = waveA[w - 1, h];//往右平移,能量幅度不变
                //waveB[w, h] = waveA[w - 1, h] + waveA[w + 1, h];//往左右扩散,能量幅度不变
                //waveB[w, h] = (waveA[w, h-1] + waveA[w, h+1])/2;//往上下扩散,能量幅度稀释
                //waveB[w, h] = waveA[w - 1, h] + waveA[w + 1, h]+ waveA[w, h - 1] + waveA[w, h + 1];//棱形扩散
                //waveB[w, h] = waveA[w - 1, h - 1] + waveA[w + 1, h - 1] + waveA[w - 1, h + 1] + waveA[w + 1, h + 1];//正方形平移

                //waveB[w, h] = waveA[w - 1, h] + waveA[w + 1, h] + waveA[w, h - 1] + waveA[w, h + 1] +
                //    waveA[w - 1, h - 1] + waveA[w + 1, h - 1] + waveA[w - 1, h + 1] + waveA[w + 1, h + 1] ;//正方形扩散

                //waveB[w, h] = (waveA[w - 1, h] + waveA[w + 1, h] + waveA[w, h - 1] + waveA[w, h + 1] +
                //    waveA[w - 1, h - 1] + waveA[w + 1, h - 1] + waveA[w - 1, h + 1] + waveA[w + 1, h + 1]) / 2;//圆角正方形扩散

                //waveB[w, h] = (waveA[w - 1, h] + waveA[w + 1, h] + waveA[w, h - 1] + waveA[w, h + 1] +
                //    waveA[w - 1, h - 1] + waveA[w + 1, h - 1] + waveA[w - 1, h + 1] + waveA[w + 1, h + 1])/4;//圆形扩散

似乎算法是将能量往四周传播,同时衰减。

能量是一个累计,传播,衰减的过程。

有个问题,无法用shader实现该过程吗?

现在学到的shader是没有“记忆"功能的,当前帧是不知道上一帧的效果的,根据不知道上上帧的。

有个时间变化的变量_Time,一个点的波的扩散可以实现,多个点的扩散,不是应该和热力图类似,计算出当前点距离各个扩散中心的距离,进而算出波的幅度吗?

 

课时69:FragmentShader-程序纹理水波仿真4

效率问题

无论多少个扩散中心,算法的特性决定不影响性能。

128*128,FPS:200+

256*256,PFS:65+

512*512,FPS:10-20

   public int waveWidth = 128;
    public int waveHeight = 128;
    float[,] waveA;
    float[,] waveB;
    Color[] colorBuffer;
    Texture2D tex_uv;

    Thread thread;
    public bool IsRun = true;
    public int SleepInterval = 200;

    void Start()
    {
        waveA = new float[waveWidth, waveHeight];
        waveB = new float[waveWidth, waveHeight];
        tex_uv = new Texture2D(waveWidth, waveHeight);

        colorBuffer = new Color[waveWidth * waveHeight];

        GetComponent<Renderer>().material.SetTexture("_WaveTex", tex_uv);

        //Putpop();

        thread = new Thread(ComputeWaveThread);
        thread.Start();
    }

    // Update is called once per frame
    void Update()
    {
        //ComputeWave();
        SleepInterval = (int)(Time.deltaTime * 1000);
        tex_uv.SetPixels(colorBuffer);
        tex_uv.Apply();

        if (Input.GetMouseButton(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;
            if(Physics.Raycast(ray,out hit))
            {
                Vector3 pos = hit.point;
                Vector3 localPos = transform.worldToLocalMatrix.MultiplyPoint(pos);
                Debug.Log(string.Format("pos:{0},localPos:{1}", pos, localPos));//(-0.5,0.5)
                int w = (int)((localPos.x + 0.5) * waveWidth);
                int h = (int)((localPos.y + 0.5) * waveHeight);
                PutDrop(w,h);
            }
        }
    }

    public int Radius = 10;

    void ComputeWaveThread()
    {
        while (IsRun)
        {
            ComputeWave();
            Thread.Sleep(SleepInterval);
        }
    }

线程运行

128*128,FPS:2000+

256*256,PFS:1100+

512*512,FPS:400+

1024*1024,FPS:100+

差别好大,用和不用多线程。

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

垂直同步

这个差别不大,我这台电脑配置挺高的。

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

和CPU协同的计算,异构平台的通用计算,从CPU移植到GPU,GUGUP。

通用计算

openGL

openCL

CUPA

DirectCompute

将计算工作放到GPU上,Unity下的ComputeShader

异构平台,将CPU和GPU联合起来,共享内存,计算。

我的默认就是DX11

教程中性能提高很多很多,从200->1000。

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

ComputeShader资料:

https://blog.csdn.net/csharpupdown/article/details/79385544

https://www.bilibili.com/video/BV1Pa4y1H7wh?from=search&seid=12776133673435752104

https://www.youtube.com/results?search_query=ComputeShader

在进阶课程(¥599 https://edu.manew.com/course/198)的最后有ComputeShader的教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值