开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

开发者专栏

关注:1972

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

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

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

[zhang273162308] Unity&Shader案例篇—声纳光波效果

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

108

主题

526

帖子

6227

积分

Rank: 9Rank: 9Rank: 9

UID
3579
好友
87
蛮牛币
2881
威望
0
注册时间
2013-9-10
在线时间
1286 小时
最后登录
2017-10-18

专栏作家社区QQ达人活力之星游戏蛮牛QQ群会员蛮牛哥

发表于 2016-12-17 16:43:30 | 显示全部楼层 |阅读模式

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

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

x
本帖最后由 zhang273162308 于 2016-12-19 21:38 编辑

一、前言
          终于周末了,心心挂念的依然还是那段代码。没办法,苦逼的命。还是废话少说,先上效果图如图所示,前面两个的效果图是两种模式下的
1fff.gif

2ddd.gif

3ggg.gif

不同效果,最后一章图片是在其中一种模式下,场景进行了精心布置后的效果图。



看效果图会不自觉的认为实现这个效果是不是要在场景中的物体中进行绘制,如果真是要这样做的话那是在是太耗性能了(毕竟都工作了,本人毕业



了就不想在搞那些不实用的)。其实,这个是通过控制摄像机的最后渲染来实现的效果的,后面我会给出这个案例的工程文件下载地址。读者



在运行模式的Scene视图里将看到场景的物体(立方体)并没有被额外渲染,有了这个思路对于后面理解代码的实现原理会很有帮助。



QQ截图20161217161153.png

二、原理
1、Shader部分—光照计算


      这里用到的光照计算都比较简单,光照模式我使用了最简单的漫反射模式,代码如下


[C#] 纯文本查看 复制代码
                                //光照计算

                                float4x4 modelMatrix = _Object2World;
                                float4x4 modelMatrixInverse = _World2Object;

                                float3 normalDirection = normalize(
                                        mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
                                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);

                                float3 diffuseReflection = _LightColor0.rgb * max(0.0, dot(normalDirection, lightDirection));

                                output.col = float4(diffuseReflection, 1.0);
                                output.pos = mul(UNITY_MATRIX_MVP, input.vertex);



如图所示为漫反射的示意图,它的计算公式为




详细的介绍和代码可以参考这里


QQ截图20161217164503.png


QQ截图20161217164427.png



2、Shader部分—声纳光波的计算


     Shader代码如下:
[C#] 纯文本查看 复制代码
//声纳光波计算
#ifdef SONAR_DIRECTIONAL
                                float w = dot(output.pos.xyz, _SonarWaveVector);
#else
                                float w = length(output.pos.xyz - _SonarWaveVector);
#endif
                                // Moving wave.
                                w -= _Time.y * _SonarWaveParams.w;

                                // Get modulo (w % params.z / params.z)
                                w /= _SonarWaveParams.z;
                                w = w - floor(w);

                                // Make the gradient steeper.
                                float p = _SonarWaveParams.y;
                                w = (pow(w, p) + pow(1 - w, p * 4)) * 0.5;

                                // Amplify.
                                w *= _SonarWaveParams.x;

                                fixed3 col = _SonarWaveColor * w + _SonarAddColor;




代码中使用了预编译命令,是考虑了前面说的效果图中的两种模式的切换。为此,还必须在前面添加预编译命令


[C#] 纯文本查看 复制代码
#pragma multi_compile SONAR_DIRECTIONAL SONAR_SPHERICAL




这个命令的大概意思就是说可以将这两个模式的Shader一起编译,然后就可以在C#代码里通过
[C#] 纯文本查看 复制代码
  Shader.DisableKeyword("SONAR_SPHERICAL");
或者
[C#] 纯文本查看 复制代码
Shader.EnableKeyword("SONAR_SPHERICAL");


来进行切换


完整顶点片段Shader代码如下:


[C#] 纯文本查看 复制代码
Shader "CgInUnity/SonarFxVF"
{
        Properties
        {
                _SonarBaseColor("Base Color",  Color) = (0.1, 0.1, 0.1, 0)
                _SonarWaveColor("Wave Color",  Color) = (1.0, 0.1, 0.1, 0)
                _SonarWaveParams("Wave Params", Vector) = (1, 20, 20, 10)
                _SonarWaveVector("Wave Vector", Vector) = (0, 0, 1, 0)
                _SonarAddColor("Add Color",   Color) = (0, 0, 0, 0)
        }
        SubShader
        {
                Tags{ "LightMode" = "ForwardBase" }
                // make sure that all uniforms are correctly set

                Pass
                {
                        CGPROGRAM
                        #pragma vertex vert
                        #pragma fragment frag
                        #pragma multi_compile SONAR_DIRECTIONAL SONAR_SPHERICAL
                        
                        #include "UnityCG.cginc"

                        struct vertexInput
                        {
                                float4 vertex : POSITION;
                                float3 normal:NORMAL;
                        };

                        struct vertexOutput
                        {
                                float4 pos:SV_POSITION;
                                float4 col:COLOR;
                        };

                        float3 _SonarBaseColor;
                        float3 _SonarWaveColor;
                        float4 _SonarWaveParams; // Amp, Exp, Interval, Speed
                        float3 _SonarWaveVector;
                        float3 _SonarAddColor;
                        uniform float4 _LightColor0;
                        
                        vertexOutput vert (vertexInput input)
                        {
                                vertexOutput output;

                                //光照计算

                                float4x4 modelMatrix = _Object2World;
                                float4x4 modelMatrixInverse = _World2Object;

                                float3 normalDirection = normalize(
                                        mul(float4(input.normal, 0.0), modelMatrixInverse).xyz);
                                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);

                                float3 diffuseReflection = _LightColor0.rgb * max(0.0, dot(normalDirection, lightDirection));

                                output.col = float4(diffuseReflection, 1.0);
                                output.pos = mul(UNITY_MATRIX_MVP, input.vertex);

                                //声纳光波计算
#ifdef SONAR_DIRECTIONAL
                                float w = dot(output.pos.xyz, _SonarWaveVector);
#else
                                float w = length(output.pos.xyz - _SonarWaveVector);
#endif
                                // Moving wave.
                                w -= _Time.y * _SonarWaveParams.w;

                                // Get modulo (w % params.z / params.z)
                                w /= _SonarWaveParams.z;
                                w = w - floor(w);

                                // Make the gradient steeper.
                                float p = _SonarWaveParams.y;
                                w = (pow(w, p) + pow(1 - w, p * 4)) * 0.5;

                                // Amplify.
                                w *= _SonarWaveParams.x;

                                fixed3 col = _SonarWaveColor * w + _SonarAddColor;

                                output.col += float4(col, 1);

                                return output;
                        }
                        
                        float4 frag (vertexOutput input) : COLOR
                        {

                                return  input.col;
                        }
                        ENDCG
                }
        }
}


喜欢用简洁的Surface Shader代码的童鞋可以用如下代码替换:
[C#] 纯文本查看 复制代码
Shader "CgInUnity/SonarFxSurf"
{
    Properties
    {
        _SonarBaseColor  ("Base Color",  Color)  = (0.1, 0.1, 0.1, 0)
        _SonarWaveColor  ("Wave Color",  Color)  = (1.0, 0.1, 0.1, 0)
        _SonarWaveParams ("Wave Params", Vector) = (1, 20, 20, 10)
        _SonarWaveVector ("Wave Vector", Vector) = (0, 0, 1, 0)
        _SonarAddColor   ("Add Color",   Color)  = (0, 0, 0, 0)
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }

        CGPROGRAM

        #pragma surface surf Lambert
        #pragma multi_compile SONAR_DIRECTIONAL SONAR_SPHERICAL

        struct Input
        {
            float3 worldPos;
        };

        float3 _SonarBaseColor;
        float3 _SonarWaveColor;
        float4 _SonarWaveParams; // Amp, Exp, Interval, Speed
        float3 _SonarWaveVector;
        float3 _SonarAddColor;

        void surf(Input IN, inout SurfaceOutput o)
        {
#ifdef SONAR_DIRECTIONAL
            float w = dot(IN.worldPos, _SonarWaveVector);
#else
            float w = length(IN.worldPos - _SonarWaveVector);
#endif

            // Moving wave.
            w -= _Time.y * _SonarWaveParams.w;

            // Get modulo (w % params.z / params.z)
            w /= _SonarWaveParams.z;
            w = w - floor(w);

            // Make the gradient steeper.
            float p = _SonarWaveParams.y;
            w = (pow(w, p) + pow(1 - w, p * 4)) * 0.5;

            // Amplify.
            w *= _SonarWaveParams.x;

            // Apply to the surface.
            o.Albedo = _SonarBaseColor;
            o.Emission = _SonarWaveColor * w + _SonarAddColor;
        }

        ENDCG
    } 
    Fallback "Diffuse"
}


这个代码看似简单,但是对于处在学习基础原理阶段的童鞋,我的建议还是多动手写写顶点片段Shader,实现简单的光照模式、顶点变换等等对于学


习Shader会非常有帮助的。


3、C#脚本部分
     脚本部分最关键的就是使用上述的Shader去渲染摄像机,这个需要通过如下的代码来实现吗,这段代码的意思是替换成这个Shader来渲染摄像机
[C#] 纯文本查看 复制代码
  GetComponent<Camera>().SetReplacementShader(shader, null);


其他的功能就是出传递参数给Shader来实现最终随时间变换的效果,完整的代码如下:


[C#] 纯文本查看 复制代码
///By 凯尔八阿哥 专栏地址[url=http://www.manew.com/forum-47-132-1.html]http://www.manew.com/forum-47-132-1.html[/url]
///2016年12月17日 
using UnityEngine;

[RequireComponent(typeof(Camera))]
public class SonarFxControl : MonoBehaviour
{
    // 声纳的模式
    public enum SonarMode { Directional, Spherical }
    [SerializeField] SonarMode _mode = SonarMode.Directional;
    public SonarMode mode { get { return _mode; } set { _mode = value; } }

    // 声纳波的方向(仅仅在Directional模式下)
    [SerializeField] Vector3 _direction = Vector3.forward;
    public Vector3 direction { get { return _direction; } set { _direction = value; } }

    // 声纳波区域(仅仅在Spherical模式下)
    [SerializeField] Vector3 _origin = Vector3.zero;
    public Vector3 origin { get { return _origin; } set { _origin = value; } }

    // 背景颜色(Surfface Shader下有用)
    [SerializeField] Color _baseColor = new Color(0.2f, 0.2f, 0.2f, 0);
    public Color baseColor { get { return _baseColor; } set { _baseColor = value; } }

    // 声纳波的颜色
    [SerializeField] Color _waveColor = new Color(1.0f, 0.2f, 0.2f, 0);
    public Color waveColor { get { return _waveColor; } set { _waveColor = value; } }

    // 波的高度\振幅
    [SerializeField] float _waveAmplitude = 2.0f;
    public float waveAmplitude { get { return _waveAmplitude; } set { _waveAmplitude = value; } }

    // 波的颜色指数
    [SerializeField] float _waveExponent = 22.0f;
    public float waveExponent { get { return _waveExponent; } set { _waveExponent = value; } }

    // 波的时间间隔
    [SerializeField] float _waveInterval = 20.0f;
    public float waveInterval { get { return _waveInterval; } set { _waveInterval = value; } }

    // 波的速度
    [SerializeField] float _waveSpeed = 10.0f;
    public float waveSpeed { get { return _waveSpeed; } set { _waveSpeed = value; } }

    // 额外的颜色
    [SerializeField] Color _addColor = Color.black;
    public Color addColor { get { return _addColor; } set { _addColor = value; } }

    
    [SerializeField] Shader shader;

    int baseColorID;
    int waveColorID;
    int waveParamsID;
    int waveVectorID;
    int addColorID;

    void Awake()
    {
        baseColorID = Shader.PropertyToID("_SonarBaseColor");
        waveColorID = Shader.PropertyToID("_SonarWaveColor");
        waveParamsID = Shader.PropertyToID("_SonarWaveParams");
        waveVectorID = Shader.PropertyToID("_SonarWaveVector");
        addColorID = Shader.PropertyToID("_SonarAddColor");
    }

    void OnEnable()
    {
        GetComponent<Camera>().SetReplacementShader(shader, null);
        Update();
    }

    void OnDisable()
    {
        GetComponent<Camera>().ResetReplacementShader();
    }

    void Update()
    {
        Shader.SetGlobalColor(baseColorID, _baseColor);
        Shader.SetGlobalColor(waveColorID, _waveColor);
        Shader.SetGlobalColor(addColorID, _addColor);

        var param = new Vector4(_waveAmplitude, _waveExponent, _waveInterval, _waveSpeed);
        Shader.SetGlobalVector(waveParamsID, param);

        if (_mode == SonarMode.Directional)
        {
            Shader.DisableKeyword("SONAR_SPHERICAL");
            Shader.SetGlobalVector(waveVectorID, _direction.normalized);
        }
        else
        {
            Shader.EnableKeyword("SONAR_SPHERICAL");
            Shader.SetGlobalVector(waveVectorID, _origin);
        }
    }
}




三、结语
最后可以看看这种方式实现的声纳波效果的性能消耗如何,我们可以在运行的时候打开state看到如图所示的统计图,瞬间有没有觉得这个效果


QQ截图20161217163819.png



其实还蛮有实用价值的。



      每次写到这里其实内心并没有放松下来,反而紧张,继而三省乎,代码有bug乎,语句通顺乎,逻辑完整乎。无关梦想和利益,纯粹喜好,好好


学习,天天向上。附上工程文件,原创文章,转载请注明出处凯尔八阿哥专栏
游客,如果您要查看本帖隐藏内容请回复










250px-Diffuse_Reflection_Vectors.svg.png

评分

参与人数 10蛮牛币 +8 鲜花 +42 收起 理由
Gray1314 + 2 很给力!
gxguixin123 -8 69楼已经贴出证据了,楼主没注明出处.
LiuHanxu + 5 赞一个!
GameIsOver -2 抄袭的内容,69楼贴了证据,抄的时候记得改改.
osblow + 5 很给力!
雨松MOMO + 20 赞一个!
z7232124 + 8 + 4 很给力!
wenbobo + 5 赞一个!
winning11jc + 6 很给力!
死骑很厉害 + 5 赞一个!

查看全部评分


回复

使用道具 举报

6蛮牛粉丝
1281/1500
排名
791
昨日变化
1

1

主题

75

帖子

1281

积分

Rank: 6Rank: 6Rank: 6

UID
8475
好友
0
蛮牛币
2830
威望
0
注册时间
2013-11-19
在线时间
189 小时
最后登录
2017-10-3
发表于 2016-12-17 23:00:13 | 显示全部楼层
好东西啊

回复

使用道具 举报

排名
28972
昨日变化
26

0

主题

14

帖子

35

积分

Rank: 1

UID
150566
好友
0
蛮牛币
50
威望
0
注册时间
2016-6-1
在线时间
13 小时
最后登录
2017-6-19
发表于 2016-12-17 23:11:13 | 显示全部楼层
太好了,谢谢分享

回复

使用道具 举报

4四处流浪
445/500
排名
4184
昨日变化
2

0

主题

41

帖子

445

积分

Rank: 4

UID
184503
好友
0
蛮牛币
570
威望
0
注册时间
2016-11-17
在线时间
130 小时
最后登录
2017-8-15
发表于 2016-12-18 08:21:27 | 显示全部楼层

好东西啊!谢谢分享!!
[发帖际遇]: 時間與人 发帖时在路边捡到 2 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复 支持 反对

使用道具 举报

5熟悉之中
816/1000
排名
3775
昨日变化
2

1

主题

344

帖子

816

积分

Rank: 5Rank: 5

UID
110768
好友
0
蛮牛币
1022
威望
0
注册时间
2015-6-27
在线时间
165 小时
最后登录
2017-11-9
QQ
发表于 2016-12-18 10:55:11 | 显示全部楼层
很棒的效果

回复

使用道具 举报

6蛮牛粉丝
1113/1500
排名
2205
昨日变化
2

19

主题

284

帖子

1113

积分

Rank: 6Rank: 6Rank: 6

UID
52577
好友
4
蛮牛币
390
威望
0
注册时间
2014-11-2
在线时间
294 小时
最后登录
2017-7-21

活力之星

发表于 2016-12-18 11:45:06 | 显示全部楼层
6666666666666666不得不佩服!!!!

回复 支持 反对

使用道具 举报

7日久生情
4368/5000
排名
30
昨日变化

40

主题

311

帖子

4368

积分

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

UID
1591
好友
7
蛮牛币
6458
威望
0
注册时间
2013-8-6
在线时间
1533 小时
最后登录
2017-11-23

社区QQ达人

发表于 2016-12-18 12:55:53 | 显示全部楼层
66666666666666666666666661

回复 支持 反对

使用道具 举报

5熟悉之中
931/1000
排名
1994
昨日变化
4

0

主题

136

帖子

931

积分

Rank: 5Rank: 5

UID
168193
好友
0
蛮牛币
1061
威望
0
注册时间
2016-9-12
在线时间
231 小时
最后登录
2017-11-18
发表于 2016-12-18 13:23:28 | 显示全部楼层
很给力  学习学校

回复 支持 反对

使用道具 举报

头像被屏蔽
排名
107
昨日变化
1

2

主题

2638

帖子

5871

积分

UID
47709
好友
1
蛮牛币
4003
威望
0
注册时间
2014-10-5
在线时间
1193 小时
最后登录
2017-10-12
发表于 2016-12-18 14:08:55 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

回复 支持 反对

使用道具 举报

6蛮牛粉丝
1342/1500
排名
1423
昨日变化
5

1

主题

228

帖子

1342

积分

Rank: 6Rank: 6Rank: 6

UID
65040
好友
1
蛮牛币
355
威望
0
注册时间
2015-1-1
在线时间
405 小时
最后登录
2017-11-24
发表于 2016-12-18 15:14:59 | 显示全部楼层
谢谢楼主分享!!!

回复

使用道具 举报

5熟悉之中
702/1000
排名
2681
昨日变化
11

0

主题

54

帖子

702

积分

Rank: 5Rank: 5

UID
23794
好友
0
蛮牛币
1910
威望
0
注册时间
2014-5-4
在线时间
218 小时
最后登录
2017-11-24
发表于 2016-12-18 22:06:48 | 显示全部楼层
好高深 好酷炫!!

回复

使用道具 举报

2初来乍到
115/150
排名
26059
昨日变化
22

0

主题

39

帖子

115

积分

Rank: 2Rank: 2

UID
53132
好友
0
蛮牛币
174
威望
0
注册时间
2014-11-4
在线时间
66 小时
最后登录
2017-8-14
发表于 2016-12-18 23:01:51 | 显示全部楼层
谢谢楼主共享

回复

使用道具 举报

5熟悉之中
569/1000
排名
4491
昨日变化
1

4

主题

115

帖子

569

积分

Rank: 5Rank: 5

UID
159158
好友
0
蛮牛币
756
威望
0
注册时间
2016-7-27
在线时间
200 小时
最后登录
2017-11-7
发表于 2016-12-19 08:57:43 | 显示全部楼层
66666666666666666

回复 支持 反对

使用道具 举报

2初来乍到
115/150
排名
11437
昨日变化
6

0

主题

19

帖子

115

积分

Rank: 2Rank: 2

UID
174777
好友
0
蛮牛币
142
威望
0
注册时间
2016-10-11
在线时间
36 小时
最后登录
2016-12-27
发表于 2016-12-19 09:04:26 | 显示全部楼层
6666666666666666666666

回复 支持 反对

使用道具 举报

7日久生情
3229/5000
排名
1046
昨日变化
3

136

主题

777

帖子

3229

积分

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

UID
141908
好友
2
蛮牛币
80707
威望
0
注册时间
2016-3-18
在线时间
1192 小时
最后登录
2017-11-24

活力之星七夕浪漫情人

发表于 2016-12-19 09:13:36 | 显示全部楼层
果您要查看本

本人分享所有资源,仅供学习,请勿商用,谢谢合作!
回复

使用道具 举报

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

本版积分规则

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