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

扫一扫,访问微社区

开发者专栏

关注:1810

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

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

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

[蛮牛干货] 巧用Shader,游戏玩法新思路

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

5312

主题

5761

帖子

2万

积分

Rank: 16

UID
1231
好友
183
蛮牛币
1437
威望
30
注册时间
2013-7-29
在线时间
2524 小时
最后登录
2017-8-17

社区QQ达人活力之星原创精华达人突出贡献奖财富之证游戏蛮牛QQ群会员蛮牛妹VIP

发表于 7 天前 | 显示全部楼层 |阅读模式

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

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

x
  今天为大家分享一位游戏开发学生与他的小型团队,在为期5天的课堂Game Jam中使用Unity制作小游戏《Color Wars》的过程,以及其中使用着色器为游戏画面添加各种颜色的方法。
  他们制作的《Color Wars》是一款非常简单的2.5D多人对战游戏,玩家可以射击敌人,为敌人或者为场景加上颜色。游戏效果如下:

1.gif

  图形部分
  所有游戏的特效魔法背后,都是最原始的3D模型、图片与人物精灵来组成整个场景。这些组成场景的元素都有其颜色及纹理。

2.jpg

  其中比较棘手的部分是需要在Alpha通道中屏蔽所有元素的颜色。换而言之,默认情况下,屏幕的整个Alpha通道都是黑色的,直到玩家开始喷射油漆,才会使被油漆溅到区域的Alpha通道变为白色。然后图像效果就是其原有颜色与灰度进行混合。
  如下:

3.jpg

4.jpg

  从上图可以看出,使用Projector将喷漆绘制到物体表面并创建颜色遮罩。每个Projector都使用程序化动态生成,在子弹(空中飞行的白点)接触到某个表面时进行初始化。Projector带有一个盒式碰撞体,当子弹落在Projector上时,不会初始化新的Projector,而是让原先的Projector变大。这样漆量会变多,而场景中的Projector数量却保持不变。
  默认情况下,Unity标准着色器会为所有不透明对象的Alpha通道写入1。所以下面使用自定义着色器来替换Unity标准着色器。新建一个标准表面着色器,将其表面函数替换为如下:
[C#] 纯文本查看 复制代码
void surf (Input IN, inout SurfaceOutputStandard o) 
{
        // Albedo 来自带颜色的纹理
        fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
        o.Albedo = c.rgb;
        // Metallic 和 smoothness 来自滑块变量
        o.Metallic = _Metallic;
        o.Smoothness = _Glossiness;
        o.Alpha = 0;  // 我只添加了这一行!
}


  下面这行很重要,用于避免Unity更改自定义的Alpha值。将#pragma那行代码改为如下:
[C#] 纯文本查看 复制代码
CGPROGRAM
#pragma surface surf Standard fullforwardshadows keepalpha
// 添加 "keepalpha" 告诉Unity不要覆盖我们的alpha值!


  注意,该技巧不可用于Unity中的延迟渲染管线,因为它重写了G-Buffer中的Alpha通道来存储遮罩数据。
  油漆喷射
  当子弹撞击某个表面时就会在撞击处动态生成Unity Projector。这些Projector带有自定义材质与自定义着色器。材质纹理是一张带有Alpha通道喷溅形状图,本文示例使用的纹理如下图:

5.jpg

  注意,纹理导入设置中要将Wrap Mode设为“Clamp”而非“Repeat”。用于Projector材质的着色器从Unity提供的ProjectorLight修改而来,代码如下:
[C#] 纯文本查看 复制代码
Shader "Projector/ProjectAlpha"
{
        Properties
        {
                _ShadowTex ("Cookie", 2D) = "gray" {}
        }
        Subshader
        {
                Tags { "Queue"="Transparent"}
                Pass
                {
                        ZWrite Off
                        Blend Zero One, One One
                        Offset -1, -1
 
                        CGPROGRAM
                        #pragma vertex vert
                        #pragma fragment frag
                        #pragma multi_compile_fog
                        #include "UnityCG.cginc"
                         
                   struct Input
                   {
                        float4 vertex : POSITION;
                        float3 normal : NORMAL;
                   };
 
                        struct v2f
                        {
                                float4 uvShadow : TEXCOORD0;
                                UNITY_FOG_COORDS(2)
                                float4 pos : SV_POSITION;
                                fixed nv : COLOR0;
                        };
                         
                        float4x4 unity_Projector;
                        float4x4 unity_ProjectorClip;
                         
                        v2f vert (Input v)
                        {
                                v2f o;
                                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                                o.uvShadow = mul(unity_Projector, v.vertex);
                                UNITY_TRANSFER_FOG(o,o.pos);
 
                                // For me, splatters were being projected on both sides of the
                                // object, so I used the view direction and the surface normal
                                // to check if it was facing the camera.
                                float3 normView = normalize(float3(unity_Projector[2][0], unity_Projector[2][1], unity_Projector[2][2]));
                                float nv = dot(v.normal, normView);
                                // negative values means surface isn't facing the camera
                                o.nv = nv < 0 ? 1 : 0;
                                 
                                return o;
                        }
                         
                        sampler2D _ShadowTex;
                        sampler2D _FalloffTex;
                         
                        fixed4 frag (v2f i) : COLOR
                        {
                                fixed4 texS = tex2Dproj (_ShadowTex, UNITY_PROJ_COORD(i.uvShadow));
                                fixed4 res = fixed4(1, 1, 1, texS.a );
                                // Multiply by alpha channel to
                                // remove back-side projection.
                                res.a *= i.nv;
 
                                UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(1,1,1,1));
                                return res;
                        }
                        ENDCG
                }
        }
}


  下面来介绍其中最为重要的混合部分。
  混合原理
  当着色器计算某个像素的颜色时,该颜色必须作用于屏幕上该点已经存在的像素颜色之上。默认情况下,新的像素会完全覆盖原有像素,但新像素也可以与原有像素进行混合。混合通常用于让对象呈透明或半透明效果,当然也可以实现很多其它的炫酷特效。
  关键字Blend可以包含在Subshader或Pass标签中,甚至对同一个着色器的不同Pass进行混合。添加Blend关键字后,必须写入混合因子。混合因子如下:

6.jpg

  Src指向着色器用于计算的颜色。Dst指向屏幕上已有的像素颜色。着色器用于计算的颜色会与第一个因子相乘,而屏幕上已有颜色会与第二个因子相乘。将两个结果相加,就是最终写到屏幕的颜色。
  所以"Blend SrcAlpha One"会将自身Alpha值与当前着色器计算的颜色相乘,此时屏幕上的颜色暂未改动。然后再将屏幕颜色计算后的结果与前者相加。还可以使用逗号分隔两组因子,逗号前的混合选项用于计算颜色,逗号后的混合选项仅计算Alpha通道。可以查阅Unity文档了解更多关于混合的内容。
  用于Projector的着色器就是“Blend Zero One, One One”,“Zero One”移除了飞溅纹理的颜色,使用子弹所飞溅到的表面颜色。“One One”将飞溅物的Alpha值与表面Alpha值相加。
  现在使用上面的着色器与材质来生成Projector,应该将场景视图的Alpha通道设为白色。
  颜色与灰度
  现在可以随意修改Alpha通道,但还未达到最终效果。下面利用Alpha遮罩来创建游戏所需的图像特效。
  首先,创建要使用图像特效的着色器。在Unity中新建默认的Image Effect Shader,然后将片段代码替换为如下:
[C#] 纯文本查看 复制代码
fixed4 frag (v2f i) : SV_Target
{
        fixed4 col = tex2D(_MainTex, i.uv);
 
        // This lines generates a Black&White version of the screen
        fixed3 bnw = dot(col.rgb, float3(0.3, 0.59, 0.11));
        // Switch between B&W and Color based on alpha channel
        col.rgb = lerp(bnw, col.rgb, col.a);
 
        return col;
}


  可以随意更改bnw变量以达到理想的混合效果。最后还需要新建脚本来运行该图像特效。脚本非常简单,代码如下:
[C#] 纯文本查看 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityStandardAssets.ImageEffects;
 
[ExecuteInEditMode]
[ImageEffectAllowedInSceneView]
public class AlphaColorSwitch : ImageEffectBase
{
        void OnRenderImage ( RenderTexture source, RenderTexture destination )
        {
                Graphics.Blit ( source, destination, material );
        }
}


  注意,这里用到了ImageEffectBase,该资源在Unity标准资源库中。导入标准资源库后,将脚本绑定到相机(确保将相机的渲染模式设为Forward)上,并将公共的着色器变量设为前面提到的着色器。
  到此就可以向场景中喷射油漆啦!
  已知限制
  本文提到的实现方式还存在一些限制,不一定适合所有的应用场景,但对于《Color War》这款游戏来说已足够。主要存在以下两点限制:
  1、采用Alpha遮罩就意味着没有Alpha通道,也不支持延迟渲染。这也许可以使用模板、命令缓冲区甚至多渲染目标来解决。
  2、项目使用的自定义着色器过多,这种做法并不推荐。如果您的项目可以使用延迟渲染解决其它问题,那么这个问题也就不存在了。
  结语

  本文为大家分享了在Unity中利用着色器来实现喷绘效果的过程,文中的解决方法仅作为一种思路参考,不一定适用于所有项目。大家也可以按照项目的实际需求,来选取更加适合的解决方案。
来源:Unity官方中文社区

[发帖际遇]: 清风 发帖时在路边捡到 2 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

跟我念“站长妹纸萌萌哒!”我说站长,你说YO!爱你们么么哒~
回复

使用道具 举报

5熟悉之中
618/1000
排名
3500
昨日变化
16

11

主题

139

帖子

618

积分

Rank: 5Rank: 5

UID
159315
好友
2
蛮牛币
3559
威望
0
注册时间
2016-7-28
在线时间
202 小时
最后登录
2017-8-17
QQ
发表于 7 天前 | 显示全部楼层
新颖                             

回复 支持 反对

使用道具 举报

5熟悉之中
801/1000
排名
2838
昨日变化
8

9

主题

152

帖子

801

积分

Rank: 5Rank: 5

UID
174370
好友
1
蛮牛币
2864
威望
0
注册时间
2016-10-10
在线时间
256 小时
最后登录
2017-8-16

活力之星

QQ
发表于 7 天前 | 显示全部楼层
先码了,休息的时候研究一下。

回复 支持 反对

使用道具 举报

排名
30532
昨日变化
6246

2

主题

40

帖子

69

积分

Rank: 2Rank: 2

UID
165351
好友
0
蛮牛币
81
威望
0
注册时间
2016-9-1
在线时间
21 小时
最后登录
2017-8-16
发表于 7 天前 | 显示全部楼层
{:106:}

回复

使用道具 举报

排名
22310
昨日变化
1839

0

主题

13

帖子

37

积分

Rank: 1

UID
196662
好友
0
蛮牛币
53
威望
0
注册时间
2016-12-28
在线时间
12 小时
最后登录
2017-8-16
发表于 7 天前 | 显示全部楼层
感谢分享,日后看

回复

使用道具 举报

7日久生情
1691/5000
排名
821
昨日变化
2

1

主题

223

帖子

1691

积分

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

UID
27816
好友
3
蛮牛币
5088
威望
0
注册时间
2014-6-3
在线时间
513 小时
最后登录
2017-8-17
发表于 7 天前 | 显示全部楼层
这个有点意思哈

回复

使用道具 举报

3偶尔光临
294/300
排名
5252
昨日变化
50

0

主题

20

帖子

294

积分

Rank: 3Rank: 3Rank: 3

UID
178205
好友
0
蛮牛币
588
威望
0
注册时间
2016-10-25
在线时间
80 小时
最后登录
2017-8-17
发表于 7 天前 | 显示全部楼层
这不是之前那个喷漆游戏的原理!!~~哈哈,完了看下

回复 支持 反对

使用道具 举报

2初来乍到
132/150
排名
11940
昨日变化
7

1

主题

50

帖子

132

积分

Rank: 2Rank: 2

UID
18962
好友
0
蛮牛币
162
威望
0
注册时间
2014-3-26
在线时间
33 小时
最后登录
2017-8-10
发表于 7 天前 | 显示全部楼层
不错 创意给力~~~~~~~~~~~~~~

回复

使用道具 举报

5熟悉之中
555/1000
排名
5085
昨日变化
40

0

主题

187

帖子

555

积分

Rank: 5Rank: 5

UID
168312
好友
0
蛮牛币
999
威望
0
注册时间
2016-9-13
在线时间
166 小时
最后登录
2017-8-16
发表于 7 天前 | 显示全部楼层
路过学习了
[发帖际遇]: 1169895688 发帖时在路边捡到 1 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复

使用道具 举报

3偶尔光临
213/300
排名
8369
昨日变化
9

0

主题

62

帖子

213

积分

Rank: 3Rank: 3Rank: 3

UID
224729
好友
0
蛮牛币
850
威望
0
注册时间
2017-6-2
在线时间
51 小时
最后登录
2017-8-17
发表于 6 天前 | 显示全部楼层
感觉不简单

回复

使用道具 举报

3偶尔光临
213/300
排名
8369
昨日变化
9

0

主题

62

帖子

213

积分

Rank: 3Rank: 3Rank: 3

UID
224729
好友
0
蛮牛币
850
威望
0
注册时间
2017-6-2
在线时间
51 小时
最后登录
2017-8-17
发表于 6 天前 | 显示全部楼层
楼主有源码吗?可以交流分享吗?

回复 支持 反对

使用道具 举报

5熟悉之中
558/1000
排名
4648
昨日变化
24

3

主题

149

帖子

558

积分

Rank: 5Rank: 5

UID
159631
好友
1
蛮牛币
548
威望
0
注册时间
2016-7-30
在线时间
178 小时
最后登录
2017-8-17
发表于 6 天前 | 显示全部楼层
谢谢分享

回复

使用道具 举报

5熟悉之中
637/1000
排名
3182
昨日变化
3

1

主题

68

帖子

637

积分

Rank: 5Rank: 5

UID
121885
好友
0
蛮牛币
804
威望
0
注册时间
2015-9-9
在线时间
228 小时
最后登录
2017-8-14
QQ
发表于 6 天前 | 显示全部楼层
喷射战士?

回复

使用道具 举报

5熟悉之中
508/1000
排名
3847
昨日变化
1

0

主题

74

帖子

508

积分

Rank: 5Rank: 5

UID
161694
好友
0
蛮牛币
1183
威望
0
注册时间
2016-8-11
在线时间
152 小时
最后登录
2017-8-17
QQ
发表于 6 天前 | 显示全部楼层
谢谢分享

回复

使用道具 举报

4四处流浪
413/500
排名
6151
昨日变化
64

0

主题

151

帖子

413

积分

Rank: 4

UID
122160
好友
0
蛮牛币
683
威望
0
注册时间
2015-9-10
在线时间
104 小时
最后登录
2017-8-17
发表于 6 天前 | 显示全部楼层
感谢分享

回复

使用道具 举报

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

本版积分规则

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