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