Unity喷墨效果Shader实现
笔者介绍:姜雪伟,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)上,并将公共的着色器变量设为前面提到的着色器。
到此就可以向场景中喷射油墨啦!
Unity喷墨效果Shader实现的更多相关文章
- Unity镜子效果的实现(无需镜子Shader)
Unity镜子效果制作教程 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享. ...
- 【unity shaders】:Unity中的Shader及其基本框架
shader和Material的基本关系 Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出.绘图单元可以依据这个输出来将图 ...
- 【浅墨Unity3D Shader编程】之二 雪山飞狐篇:Unity的基本Shader框架写法&颜色、光照与材质
本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/40955607 作者:毛星云(浅墨) ...
- 【Unity Shaders】Shader学习资源和Surface Shader概述
写在前面 写这篇文章的时候,我断断续续学习Unity Shader半年了,其实还是个门外汉.我也能体会很多童鞋那种想要学好Shader却无从下手的感觉.在这个期间,我找到一些学习Shader的教程以及 ...
- unity描边效果
这里总结了几种在unity实现描边效果的方法,首先准备一个模型导入在unity中,使用默认shader,上传一张原始图,以便后面实现功能效果的对比 一.边缘光,这里参照官方的一个SurfaceShad ...
- unity 内置shader
几个有用的Unity 内置shader: (一)Standard RenderingMode:Opaque为实体渲染,更改Color的透明通道不会有影响:Cutout会把图片的透明通道显示出来,非严格 ...
- unity之初识shader
自己做个总结先.当然文中很多内容都是从各位大神的文档当中看的.我只是站在巨人的肩膀上. 首先什么是shader?其实就是一个在显示屏当中的显示程序,俗称着色器.它可以定义物体在硬件显示屏当 ...
- Unity内置shader 下载
Unity内置shader 4.3.1 版本的 其他版本可以自己修改名称 下载地址 http://download.unity3d.com/download_unity/builtin_shade ...
- Unity 3d中Shader是什么,可以吃吗?
众所周知,Unity3d是一款跨平台非常广的游戏引擎,上手容易,界面友好,集成功能众多,是目前开发手游的主流引擎.本人有幸使用Unity 3d进行开发已一年多时间,已领略了这歀引擎的强大之处. 编写s ...
随机推荐
- 20145216史婧瑶《Java程序设计》第2周学习总结
20145216 <Java程序设计>第2周学习总结 教材学习内容总结 第三章 基础语法 3.1 类型.变量与运算符 •类型 •基本类型 •整数:short(占2字节).int(占4字节) ...
- 20145310《Java程序设计》第4次实验报告
20145310<Java程序设计>第4次实验报告 实验内容 搭建Android环境 运行Android 修改代码并输出自己的学号 实验步骤 搭建Android环境 安装Android S ...
- ubifs文件系统挂载时提示ubi0: MTD device 5 is write-protected, attach in read-only mode
答:笔者遇到的这种情况是由于分区表未与nor flash的物理擦除块边界对齐而导致的,因此调整分区表即可解决此问题
- 【卷积神经网络】对BN层的解释
前言 Batch Normalization是由google提出的一种训练优化方法.参考论文:Batch Normalization Accelerating Deep Network Trainin ...
- Math.Round 四舍五入问题 解惑 !
前言: 一定要说 坑爹的微软! 坑爹的微软! 坑爹的微软! 重要的事情说 三遍 ! 摘录 SMDN 示例 下面的示例演示就近舍入. C# C++ VB Math.Round(3.44, 1); ...
- 分析java进程假死状况
摘自: http://www.myexception.cn/internet/2044496.html 分析java进程假死情况 1 引言 1.1 编写目的 为了方便大家以后发现进程假死的时候能够正常 ...
- 用gitolite搭建git server
在Ubuntu上测试安装一下git server,为后面项目的代码管理做准备.记录流水账如下, 中间关于git 命令的使用说明不做过多解释,需要了解的请google或者直接git help: 我用到了 ...
- Bigdecimal: Non-terminating decimal expansion; no exact representable decimal result.
做除法没有指定保留小数点后几位,就会抛出此异常. 因为会除不尽 Non-terminating decimal expansion; no exact representable decimal re ...
- python脚本7_打印九九乘法表
#打印九九乘法表 for i in range(1,10): s = "" for j in range(1,i+1): s += str(j) + '*' + str(i) + ...
- Js 操作 Cookies
<script language=javascript> // cookie其实是一个key=value就是一个cookie而不是 //获得coolie 的值 function cooki ...