【游戏技术群】959392658  【游戏出海群】12067810
游戏蛮牛 手机端
开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:2372

当前位置:游戏蛮牛 技术专区 开发者专栏

__________________________________________________________________________________
开发者干货区版块规则:

  1、文章必须是图文形式。(至少2幅图)
      2、文章字数必须保持在1500字节以上。(编辑器右下角有字数检查)
      3、本版块只支持在游戏蛮牛原创首发,不支持转载。
      4、本版块回复不得无意义,如:顶、呵呵、不错......【真的会扣分的哦】
      5、......
__________________________________________________________________________________
查看: 3699|回复: 157

[大锅的案例] Unity 利用compute shader制作鼠标滑动水纹特效

  [复制链接]  [移动端链接]
排名
4413
昨日变化
2

17

主题

85

帖子

988

积分

Rank: 9Rank: 9Rank: 9

UID
97961
好友
5
蛮牛币
1323
威望
0
注册时间
2015-5-6
在线时间
509 小时
最后登录
2018-12-14
发表于 2018-1-1 13:21:20 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

x
前段时间研究了一会compute shader,于是想到了可以利用compute shader做点有趣的效果,恰巧在一个网站上看到了和这个类似的图片效果,于是便着手做了这个案例,这个案例中用到了compute shader和geometry shader 的知识点(我对于compute shader也只是初步了解而已),所以对于这方面不是太了解的同学们可以去百度了解一下。
项目源码在文尾处
具体效果如图:
effects.gif

具体思路如下图:
未命名文件.png

C#代码,需要用于赋值、传参、读取鼠标位置等:
[C#] 纯文本查看 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SetPoints : MonoBehaviour {

    public ComputeShader comShader;
    public Shader shader;
    public Transform point;
    ComputeBuffer dataBuffer;
    public Material mat;
    int kernel;
    int init = 0;
    public float radius;
        // Use this for initialization
        void Start () {
        dataBuffer = new ComputeBuffer(4 * 4 * 32 * 32, 36);//算出buffer数量
        kernel = comShader.FindKernel("CSMain");
        comShader.SetBuffer(kernel, "dataBuffer", dataBuffer);
        comShader.SetFloat("radius", radius);
    }
    
    private void OnRenderObject()
    {

        comShader.Dispatch(kernel, 32, 32, 1);

        mat.SetBuffer("dataBuffer", dataBuffer);

        comShader.SetVector("mousePos", point.position);
        comShader.SetFloat("deltaTime", Time.deltaTime);
        comShader.SetVector("originPos", transform.position);
        comShader.SetInt("init", init);

        mat.SetPass(0);

        Graphics.DrawProcedural(MeshTopology.Points, 4 * 4 * 32 * 32);
        init = 1;
    }

    private void OnDestroy()
    {
        dataBuffer.Release();
    }

    // Update is called once per frame
    void Update () {
        point.position = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, transform.position.z - Camera.main.transform.position.z));
        }

}


解释一下核心的算法,就是如何模拟水纹效果,这部分算法是放在compute shader中的:
[C] 纯文本查看 复制代码
// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain

// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> Result;
float3 mousePos;
float3 originPos;
float deltaTime;
int init;
float radius;
struct Data
{
        float3 pos;
        float3 velocity;
        float3 uv;
};
RWStructuredBuffer<Data> dataBuffer;

[numthreads(4,4,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
        // TODO: insert actual code here!
        uint f = id.x + id.y * 4 * 32;
        float3 pos = float3(id.x,id.y,id.z)/2 + originPos;//计算出原始坐标
        dataBuffer[f].uv = ((pos - originPos)/128)*2;//计算出每个点的uv

        if(init == 0)//初始化点
        {
                dataBuffer[f].pos = pos ;
                dataBuffer[f].velocity = float3(0,0,0);
        }
        else
        {
                float3 dis = dataBuffer[f].pos - mousePos;//计算出当前坐标与鼠标点间的向量

                float vel = clamp(radius - length(dis),0.01,radius) * deltaTime * 20;//计算出力

                dataBuffer[f].velocity += normalize(dis) * vel;//加上力

                dataBuffer[f].velocity = normalize(dataBuffer[f].velocity) * clamp((length(dataBuffer[f].velocity) - deltaTime * 2),0,radius);//限制力的范围,并且加上指向原始点的力

                dataBuffer[f].pos = pos + dataBuffer[f].velocity;//根据力计算出坐标

        }
}


在上面的compute shader代码中,我们首先创建了128*128个buffer(4*4个线程组,指定每个线程组里有32*32个线程),这些buffer将用于存储点的坐标、UV、所受到的力等信息。
然后我们在程序的入口函数CSMain中进行主要的算法,这个算法将会根据鼠标的坐标模拟力的运算,思路如下:
每个点都有自己的原始位置,这个原始位置是固定不变的。每个点会接受两个力,一个是原始位置对它的引力,我们称为A,这个力是一直存在的;第二个是鼠标位置给它的排斥力,我们称为B,这个力主要根据鼠标位置来定。鼠标位置会给附近一定范围(radius)内的点添加B,越近的话B越大,附近的点会被排斥到以radius为半径的圆的边界附近,当鼠标移走后,每个点的A会把点向原始坐标慢慢拉回,这就有了复原的效果。
当每个buffer 的坐标、UV、力等信息都赋值完成后,我们便可以在shader中进行输出颜色:
[C] 纯文本查看 复制代码
Shader "Custom/c_s" {
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {} 
                _Width("每个方形点宽度",FLOAT) = 0.25
    }                                            
                                                 

    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        Pass{
            Blend Off Lighting Off Cull Off ZWrite Off ZTest Off AlphaTest Off
            CGPROGRAM
            #pragma target 5.0
            #pragma vertex vert 
                        #pragma geometry Geom
            #pragma fragment frag 
            #include "UnityCG.cginc"

                        struct Data
                        {
                                float3 pos;
                                float3 velocity;
                                float3 uv;
                        };

            StructuredBuffer<Data> dataBuffer;//存有信息

            sampler2D _MainTex;
                        float _Width;

            struct vertIN{
                uint id : SV_VertexID;
            };

            struct vertOUT{
                float4 pos : SV_POSITION;
                float3 uv : TEXCOORD0;
                                float3 velocity:TEXCOORD1;
            };

            vertOUT vert(vertIN i){
                                vertOUT o;
                float3 pos = dataBuffer[i.id].pos ; //读取buffer中位置信息

                o.pos = float4(pos ,1);

                o.uv = dataBuffer[i.id].uv; 
                                o.velocity = dataBuffer[i.id].velocity;

                return o;
            }

                        //将存储的点以面片形式表现
                        [maxvertexcount(4)]
                        void Geom(point vertOUT p[1],inout TriangleStream<vertOUT> triStream)
                        {
                                vertOUT os[4];
                                os[0].pos =  mul(UNITY_MATRIX_VP,p[0].pos + float4(_Width,_Width,0,0));
                                os[1].pos =  mul(UNITY_MATRIX_VP,p[0].pos + float4(-_Width,_Width,0,0));
                                os[2].pos =  mul(UNITY_MATRIX_VP,p[0].pos + float4(_Width,-_Width,0,0));
                                os[3].pos =  mul(UNITY_MATRIX_VP,p[0].pos + float4(-_Width,-_Width,0,0));
                                os[0].uv = p[0].uv;
                                os[1].uv = p[0].uv;
                                os[2].uv = p[0].uv;
                                os[3].uv = p[0].uv;
                                os[0].velocity = p[0].velocity;
                                os[1].velocity = p[0].velocity;
                                os[2].velocity = p[0].velocity;
                                os[3].velocity = p[0].velocity;

                                triStream.Append(os[0]);
                                triStream.Append(os[1]);
                                triStream.Append(os[2]);
                                triStream.Append(os[3]);
                        }

            fixed4 frag(vertOUT ou):COLOR{

                fixed4 c = tex2D(_MainTex,ou.uv);
                                c+= half4( ou.velocity,0);
                return c;

            }
            ENDCG
        }
    } 
}


shader中必须要有compute shader中计算出来的buffer,我们用StructuredBuffer<Data> dataBuffer进行存储。其中重点讲一下geometry shader在这个工程里的作用:它的作用就是读取根据点的位置信息,创建出一个正方形面片。如果没有geometry shader的话,我们只能得到一些不连续的像素点组成的的图像,这样效果并不好,所以我们根据每个点的位置,创建出一个正方形面片(有点马赛克的感觉),然后在面片上显示每个点的颜色。

最后贴上源码:
游客,如果您要查看本帖隐藏内容请回复






201811-130348.jpg

回复

使用道具 举报

7日久生情
2137/5000
排名
1840
昨日变化
7

7

主题

718

帖子

2137

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
183904
好友
6
蛮牛币
4713
威望
0
注册时间
2016-11-15
在线时间
696 小时
最后登录
2018-12-14
QQ
发表于 2018-1-3 16:59:39 | 显示全部楼层
66666666666666
[发帖际遇]: zzzzzzZ 捡了钱没交公 蛮牛币 降了 2 . 幸运榜 / 衰神榜

回复

使用道具 举报

3偶尔光临
183/300
排名
12731
昨日变化
6

0

主题

65

帖子

183

积分

Rank: 3Rank: 3Rank: 3

UID
229818
好友
0
蛮牛币
2
威望
0
注册时间
2017-7-2
在线时间
48 小时
最后登录
2018-7-11
发表于 2018-1-4 10:00:22 | 显示全部楼层
效果满满

回复

使用道具 举报

5熟悉之中
914/1000
排名
3310
昨日变化
17

9

主题

127

帖子

914

积分

Rank: 5Rank: 5

UID
210360
好友
0
蛮牛币
77
威望
0
注册时间
2017-3-7
在线时间
346 小时
最后登录
2018-12-14
发表于 2018-1-4 10:24:53 | 显示全部楼层
666666谢谢分享

回复

使用道具 举报

0

主题

13

帖子

41

积分

Rank: 1

UID
175242
好友
0
蛮牛币
8
威望
0
注册时间
2016-10-13
在线时间
28 小时
最后登录
2018-12-11
发表于 2018-1-4 10:30:18 | 显示全部楼层
666顶顶顶

回复

使用道具 举报

5熟悉之中
717/1000
排名
3433
昨日变化
2

2

主题

158

帖子

717

积分

Rank: 5Rank: 5

UID
58
好友
1
蛮牛币
1598
威望
0
注册时间
2013-5-29
在线时间
209 小时
最后登录
2018-10-14

社区QQ达人

发表于 2018-1-4 11:37:40 | 显示全部楼层
谢谢分享

回复

使用道具 举报

3偶尔光临
165/300
排名
14365
昨日变化
5

0

主题

78

帖子

165

积分

Rank: 3Rank: 3Rank: 3

UID
262097
好友
0
蛮牛币
232
威望
0
注册时间
2018-1-1
在线时间
34 小时
最后登录
2018-6-20
发表于 2018-1-4 12:11:59 | 显示全部楼层
很有启发。

回复

使用道具 举报

4四处流浪
366/500
排名
21622
昨日变化
11

9

主题

95

帖子

366

积分

Rank: 4

UID
232256
好友
3
蛮牛币
369
威望
0
注册时间
2017-7-15
在线时间
234 小时
最后登录
2018-12-13
发表于 2018-1-4 13:21:57 | 显示全部楼层
可疑的大啊

回复

使用道具 举报

5熟悉之中
783/1000
排名
4658
昨日变化
26

6

主题

170

帖子

783

积分

Rank: 5Rank: 5

UID
232957
好友
1
蛮牛币
230
威望
0
注册时间
2017-7-18
在线时间
305 小时
最后登录
2018-12-13
发表于 2018-1-4 14:36:37 | 显示全部楼层
666666666666666

回复 支持 反对

使用道具 举报

7日久生情
4183/5000
排名
168
昨日变化

18

主题

316

帖子

4183

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
67323
好友
1
蛮牛币
3358
威望
0
注册时间
2015-1-10
在线时间
1647 小时
最后登录
2018-12-12
发表于 2018-1-5 05:18:03 | 显示全部楼层
简直无敌于世

回复

使用道具 举报

8常驻蛮牛
6737/10000
排名
29
昨日变化

1

主题

559

帖子

6737

积分

Rank: 8Rank: 8

UID
30356
好友
0
蛮牛币
8477
威望
0
注册时间
2014-6-19
在线时间
3175 小时
最后登录
2018-12-14
发表于 2018-1-5 08:41:22 | 显示全部楼层
6666666666666666666666666666

回复 支持 反对

使用道具 举报

排名
773
昨日变化

68

主题

420

帖子

3339

积分

Rank: 9Rank: 9Rank: 9

UID
35466
好友
26
蛮牛币
5368
威望
0
注册时间
2014-7-21
在线时间
1123 小时
最后登录
2018-12-11

专栏作家

发表于 2018-1-5 08:57:19 | 显示全部楼层
必须支持,谢谢分享

回复 支持 反对

使用道具 举报

排名
46974
昨日变化
20

0

主题

3

帖子

11

积分

Rank: 1

UID
263019
好友
0
蛮牛币
15
威望
0
注册时间
2018-1-6
在线时间
4 小时
最后登录
2018-2-26
发表于 2018-1-6 15:40:16 | 显示全部楼层
dddddddddddddd

回复

使用道具 举报

7日久生情
4183/5000
排名
168
昨日变化

18

主题

316

帖子

4183

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
67323
好友
1
蛮牛币
3358
威望
0
注册时间
2015-1-10
在线时间
1647 小时
最后登录
2018-12-12
发表于 2018-1-7 09:13:34 | 显示全部楼层
lz  咱想问一下 ComputeBuffer 的第一个参数是怎么确定要计算的数量的?
如果说第二个参数是 每个需要计算的数据的长度的话。

回复 支持 反对

使用道具 举报

7日久生情
4183/5000
排名
168
昨日变化

18

主题

316

帖子

4183

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
67323
好友
1
蛮牛币
3358
威望
0
注册时间
2015-1-10
在线时间
1647 小时
最后登录
2018-12-12
发表于 2018-1-7 12:20:36 | 显示全部楼层
求问  在Compute Shader 里  这个参数的取值和需要计算的数值的数量关系 [numthreads(8,8,1)]

回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

快速回复 返回顶部 返回列表