Unity喷墨效果Shader实现

原创 2017年07月07日 23:00:04

笔者介绍:姜雪伟,IT公司技术合伙人,IT高级讲师,CSDN社区专家,特邀编辑,畅销书作者,已出版书籍:《手把手教你架构3D游戏引擎》电子工业出版社和《Unity3D实战核心技术详解》电子工业出版社等。

CSDN视频网址:http://edu.csdn.net/lecturer/144

对于游戏中使用的类似喷墨效果,在射击类游戏中经常使用比如玩家射击的子弹会在墙上出现类似喷墨效果,效果如下所示:


在默认情况下,屏幕的整个Alpha通道都是黑色的,直到玩家开始喷射油墨,才会使被油墨溅到区域的Alpha通道变为白色。然后图像效果就是其原有颜色与灰度进行混合。

    实现方式如下所示:


从上图可以看出,使用Projector将喷漆绘制到物体表面并创建颜色遮罩。每个Projector都使用程序化动态生成,在子弹(空中飞行的白点)接触到某个表面时进行初始化。Projector带有一个盒式碰撞体,当子弹落在Projector上时,不会初始化新的Projector,而是让原先的Projector变大。这样漆量会变多,而场景中的Projector数量却保持不变。

 

默认情况下,Unity标准着色器会为所有不透明对象的Alpha通道写入1。所以下面使用自定义着色器来替换Unity标准着色器。新建一个标准表面着色器,将其表面函数替换为如下:

void surf(Input IN, inout SurfaceOutputStandard o)
{
     //Albedo 来自带颜色的纹理
     fixed4 c=tex2D(_MainTex, IN.uv_MainTex) * _Color;
     o.Albedo = c.rgb;
     o.Metallic = _Metallic;
     o.Smoothness = _Glossiness;
     o.Alpha = 0;  //只添加此行
}

下面这行很重要,用于避免Unity更改自定义的Alpha值。将#pragma那行代码改为如下:

CGPROGRAM
#pragma surface surf Standard fullforwardshadows keepalphs
注意,该技巧不可用于Unity中的延迟渲染管线,因为它重写了G-Buffer中的Alpha通道来存储遮罩数据。

当子弹撞击某个表面时就会在撞击处动态生成Unity Projector。这些Projector带有自定义材质与自定义着色器。材质纹理是一张带有Alpha通道喷溅形状图,本文示例使用的纹理如下图:


注意,纹理导入设置中要将Wrap Mode设为“Clamp”而非“Repeat”。用于Projector材质的着色器从Unity提供的ProjectorLight修改而来,代码如下:

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 : LagPosition;
				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);
				float3 normView = normalize(float3(unity_Projector[2][0], unity_Projector[2][1], unity_Projector[2][2]));
				float nv = dot(v.normal, normView);
				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);

				res.a = i.nv;
				UNITY_APPLY_FOG_COLOR(i.fogCoord, res, fixed4(1,1,1,1));
				return res;
			}
			EDNCG
		}
	}
}


再介绍一下,关于混合的部分:

当着色器计算某个像素的颜色时,该颜色必须作用于屏幕上该点已经存在的像素颜色之上。默认情况下,新的像素会完全覆盖原有像素,但新像素也可以与原有像素进行混合。混合通常用于让对象呈透明或半透明效果,当然也可以实现很多其它的炫酷特效。

 

关键字Blend可以包含在Subshader或Pass标签中,甚至对同一个着色器的不同Pass进行混合。添加Blend关键字后,必须写入混合因子。混合因子如下:

 


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,然后将片段代码替换为如下:

fixed4 frag(v2f i) : SV_Target
{
   fixed4 col = tex2D(_MainTex, i.uv);
   fixed3 bnw = dot(col.rgb, float3(0.3,0.59,0.11));
   col.rgb = lerp(bnw, col.rgb, col.a);
   
   return col;
}

可以随意更改bnw(Black&White)变量以达到理想的混合效果。最后还需要新建脚本来运行该图像特效。脚本非常简单,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityStandardAssets.ImageEffectBase;

[ExecuteInEditMode]
[ImageEffectAllowedInSceneView]
public class ALphaColorSwitch : ImageEffectBase{
	void OnRenderImage(RenderTexture source, RenderTexture destination)
	{
		Graphics.Blit (source, destination, material);
	}
}


注意,这里用到了ImageEffectBase,该资源在Unity标准资源库中(Unity 5.5及以上版本推荐使用Post Processing Stack资源库代替ImageEffects)。导入标准资源库后,将脚本绑定到相机(确保将相机的渲染模式设为Forward)上,并将公共的着色器变量设为前面提到的着色器。

 

到此就可以向场景中喷射油墨啦!



版权声明:本文为博主原创文章,未经博主允许不得转载。

汽车喷漆效果的实现

图1:双色调,悬浮微鳞片轿车油漆在Direct3D实时象素着色器的效果。 给汽车身体喷漆是一个复杂的过程,车身油漆是昂贵分层形式,往往包含染料层,搪瓷金属箔悬浮层. 由于这些接二连三油漆表面层...
  • pizi0475
  • pizi0475
  • 2013年10月20日 12:44
  • 2337

unity projector相关探讨和使用

projector用到的组件Projector组件可以实现0 角色选择光圈 1 普通圆点阴影 2 伪动态阴影 http://qiankanglai.me/2016/11/14/unity-pro...
  • qq_18229381
  • qq_18229381
  • 2017年10月19日 17:13
  • 182

Unity3D游戏制作(三)——移动平台上的角色阴影制作

本系列文章由 Amazonzx 编写,欢迎转载,转载请注明出处。 http://blog.csdn.net/amazonzx/article/details/7973740   本文将重点介绍两...
  • Amazonzx
  • Amazonzx
  • 2012年09月13日 10:21
  • 30492

Unity Shader Example 22 (Simulate Projector)

主要参考: http://tgerm.org/U3D-Cg-03/ 下面的主要就是模拟Unity3D 中的Projector的效果。 CustomMainCamera...
  • aa20274270
  • aa20274270
  • 2016年11月22日 14:43
  • 244

Unity教程之-RenderTexture实现实时阴影绘制

一般实时阴影主要还在出现在角色、怪物的脚底为了然场景表现的更加逼真,实现起来通常有三种方式:脚底放个阴影图片跟着主角动,通过摄像机的RenderRexture绘制显示,直接使用Unity的Projec...
  • andyhebear
  • andyhebear
  • 2016年01月08日 09:08
  • 3179

Unity 5 使用Projector实现纹理投射

Unity 5 使用Projector实现纹理投射游戏中,我们经常需要实现将纹理投射到场景中其他物体上的效果,如地上的光环、石块上的logo等。很多情况下我们可以通过灯光或者其他方式达到我们想要的效果...
  • winchyy
  • winchyy
  • 2016年06月02日 10:35
  • 7913

Unity3D流体插件FluidSim使用总结

这个流体插件是2D的,想用3D的同学可以不用看了。 用JS语言写的,不过这问题不大,用C#也是可以调用JS的。整体的效果还是不错了,用到了GPU(Unity3D必须是Pro版本)。 作者把很多接口都弄...
  • a117653909
  • a117653909
  • 2013年12月24日 16:59
  • 9800

Unity&Shader案例篇—屏幕渐暗效果

一、前言 本文使用版本为Unity5.3.3,两种实现方法得到的效果是一样的,侧重点不一样,择其良者而用之。 二、第一种方案 1、效果图如图所示 2、制作步骤 首先是C#脚本部分,前面的几章基础篇...
  • zhangxiao13627093203
  • zhangxiao13627093203
  • 2016年11月28日 21:44
  • 1448

Unity3D-实现水墨渲染效果

原文:http://gad.qq.com/user/myarticle?id=438905
  • zhangming19901120
  • zhangming19901120
  • 2016年12月23日 17:00
  • 1714

解读Unity中的CG编写Shader系列1——初识CG

前提知识点: 1.CG代码必须用 CGPROGRAM 。。。 ENDCG括起来 2.顶点着色器与片段着色器的主函数名称可随意,但需要再#pragma vert 与#pragma fragment中声明...
  • zay109327402
  • zay109327402
  • 2014年08月18日 19:40
  • 6327
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Unity喷墨效果Shader实现
举报原因:
原因补充:

(最多只允许输入30个字)