练习项目(二):消融效果
概述
本篇是“练习项目”系列的第二篇,主要介绍一下利用消融实现的效果。在游戏开发的过程中,有很多看起来很神奇的效果,都是使用消融的原理实现的。
原理
主要的原理,就是使用噪声图和透明度测试,根据噪声图中采样的值,对某些像素进行剔除。
1、基本原理实现
这里会实现一个最基础的项目,来简单了解消融的基本原理。主要的代码如下:
float cutout = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, input.uv.zw).r;
AlphaDiscard(cutout, _Threshold);
注意,这里要使用AlphaDiscard方法的话,必须设置正确的KeyWord。
代码
2、边缘颜色
在上面的动图中可以看到,只是简单实现了消融,但看起来效果不太好,比较单调。下面将使用几种方式来丰富效果。
2.1 纯颜色
第一种实现方式比较简单,只是在未消融的边界留下一段缓冲,显示边界的颜色。这里需要开放两个属性接口:_EdgeLength、_EdgeColor。即边缘长度和边缘颜色。先根据噪声图进行透明度剔除,然后根据透明度确定一段范围内显示边界的颜色。代码如下:
float cutout = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, input.uv.zw).r;
AlphaDiscard(cutout, _Threshold);if (cutout - _Threshold < _EdgeLength)return _EdgeColor;
代码
2.2 两种颜色混合
一种颜色的效果看起来还是有点单调,使用两种颜色混合的效果可能会更好。根据_EdgeLength可以确定一个“边界”范围。“边界”与剔除区域的交界可以使用第一种颜色,“边界”与正常区域的交界可以使用第二种颜色,而在“边界”内部,则可以在第一种颜色和第二种颜色之间进行插值。代码如下:
float cutout = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, input.uv.zw).r;
AlphaDiscard(cutout, _Threshold);if (cutout - _Threshold < _EdgeLength)
{float degree = (cutout - _Threshold) / _EdgeLength;return lerp(_EdgeFirstColor, _EdgeSecondColor, degree);
}
代码
2.3 边界颜色混合物体颜色
从上面的动图可以看到,在“边界”区域只是边界的颜色,看起来有点不自然。下一步,就是对边界颜色和物体颜色进行混合,从而看起来更加地自然。主要代码如下:
float cutout = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, input.uv.zw).r;
AlphaDiscard(cutout, _Threshold);float degree = saturate((cutout - _Threshold) / _EdgeLength);
half4 edgeColor = lerp(_EdgeFirstColor, _EdgeSecondColor, degree);half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv.xy);half4 finalColor = lerp(edgeColor, col, degree);
在上面的代码中,degree的范围是[0,1],剔除区域是0,正常区域是1,而在“边界”区域,则在[0,1]之间。使用degree进行第一次插值,得到边界颜色,第二次插值,则混合了边界颜色和物体本身的颜色。
代码
2.4 使用渐变纹理
为了让“边界”颜色更加丰富,可以使用渐变纹理。
然后就可以使用degree对渐变纹理进行采样,来得到边界的颜色。得到边界颜色后,与物体本身的颜色混合的过程,就与上面相同了。主要代码如下:
float cutout = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, input.uv.zw).r;
AlphaDiscard(cutout, _Threshold);float degree = saturate((cutout - _Threshold) / _EdgeLength);
half4 edgeColor = SAMPLE_TEXTURE2D(_RampTex, sampler_RampTex, float2(degree, degree));half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv.xy);half4 finalColor = lerp(edgeColor, col, degree);
代码
3、从特定点开始消融
为了从特定点开始消融,必须将片元到特定点的距离考虑进来。
第一步,定义消融开始点的属性,该属性是在世界空间中定义的。在顶点着色器中将该点转换到物体的本地空间,然后将顶点的本地坐标和该点的本地空间坐标传递给片元着色器。在片元着色器中求出片元到该点的距离。代码如下:
//Properties
_StartPoint("Start Point",Vector) = (1,1,1,1) //需要找到该点的世界坐标//Vert
output.objPos = input.positionOS.xyz;
output.objStartPos = TransformWorldToObject(_StartPoint.xyz);//Frag
float distance = length(input.objPos - input.objStartPos);
第二步,求出网格内任意两点之间的最大距离,用来对上面求出的距离进行归一化处理。这一步需要在C#中实现,思路是遍历任意两点,然后求出最大距离。这里求出的是网格内任意两点之间的最大距离,所以上面定义的消融开始点最好在网格上面,这样效果才是对的。代码如下:
public class Dissolve : MonoBehaviour {void Start () {Material mat = GetComponent<MeshRenderer> ().material;mat.SetFloat ("_MaxDistance", CalculateMaxDistance ());}float CalculateMaxDistance () {float maxDistance = 0;Vector3[] vertices = GetComponent<MeshFilter> ().mesh.vertices;for (int i = 0; i < vertices.Length; i++) {Vector3 v1 = vertices[i];for (int k = 0; k < vertices.Length; k++) {if (i == k) continue;Vector3 v2 = vertices[k];float mag = (v1 - v2).magnitude;if (maxDistance < mag) maxDistance = mag;}}return maxDistance;}
}
同时,也要定义_MaxDistance属性来存放最大距离值。
_MaxDistance("Max Distance",Float) = 0
第三步就是归一化距离值。
float normalizedDistance = saturate(distance / _MaxDistance);
第四步是定义_DistanceEffect属性,来控制距离对整个消融效果的影响程度。
//Properties
_DistanceEffect("Distance Effect",Range(0,1)) = 0.5//Frag
float cutout = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, input.uv.zw).r * (1.0 - _DistanceEffect) + normalizedDistance * _DistanceEffect;
AlphaDiscard(cutout, _Threshold);
代码
4、应用:场景切换
利用上面的从特定点开始消融的原理,我们可以用来实现场景切换的效果。
如下图所示,就是我们要实现的效果。
因为我们在上面实现的是从特点点开始消融,而上图是从从外部向特定点开始消融,所以这里要做一些修改。
float normalizedDistance = 1.0 - saturate(distance / _MaxDistance);
这样,就会从四周向中心点开始消融了。
然后,我们的距离是在局部空间计算的。但是这里有很多物体,再使用局部空间的话,就不太方便,所以,这里转到世界空间计算。
//Vert
output.worldPos = TransformObjectToWorld(input.positionOS.xyz);
//Frag
float distance = length(input.worldPos - _StartPoint.xyz);
接下来,需要获得场景所有物体的顶点到消融点的最大距离,用来对上面的距离做归一化处理,这一步,需要在C#中处理,代码在这里。
这样,场景中建一个空物体Environment,然后给Environment添加上面的C#脚本,再把其它物体都放到Environment下面即可。
代码
5、从特定方向开始消融
理解了上面的从特定点开始消融,那么这里的从特定方向开始消融就很好理解了。
这里实行的是从X方向消融。
第一步,求出X方向的边界,传递给Shader。
public class DissolveDirection : MonoBehaviour {void Start () {Material mat = GetComponent<Renderer>().material;float minX, maxX;CalculateMinMaxX(out minX, out maxX);mat.SetFloat("_MinBorderX", minX);mat.SetFloat("_MaxBorderX", maxX);}void CalculateMinMaxX(out float minX, out float maxX){Vector3[] vertices = GetComponent<MeshFilter>().mesh.vertices;minX = maxX = vertices[0].x;for(int i = 1; i < vertices.Length; i++){float x = vertices[i].x;if (x < minX)minX = x;if (x > maxX)maxX = x;}}
}
第二步,定义从X的正方向还是负方向开始消融,确定边界,然后求出各个片元在X方向上与边界的距离。再进行归一化处理。
float range = _MaxBorderX - _MinBorderX;
float border = _MinBorderX;
if (_Direction == 1) //1表示从X正方向开始,其他值则从负方向border = _MaxBorderX;float distance = abs(input.objPosX - border);
float normalizedDistance = saturate(distance / range);
代码
6、灰烬飞散效果
第一步,灰烬向特定方向飞散。这一步可以在顶点着色器中通过顶点动画实现。
float cutout = GetNormalizeDistance(output.positionWS.y);
float3 localFlyDirection = TransformWorldToObjectDir(_FlyDirection.xyz);
float flyDegree = (_Threshold- cutout) / _EdgeLength;
float val = saturate(flyDegree * _FlyIntensity);
input.positionOS.xyz += localFlyDirection * val;
第二步,从特定方向开始消融。上面已经介绍了。这里注意,因为要生成灰烬的效果,所以要延迟透明度剔除的时机。
float edgeCutout = cutout - _Threshold;
clip(edgeCutout + _AshWidth);
这样,可以在消融边缘留下大片的颜色。而我们需要的是细碎的灰烬,所以需要再次使用噪声图对这片颜色区域进行消融处理。
if(degree < 0.001)
{clip(whiteNoise * _AshDensity + normalizedDistance * _DistanceEffect - _Threshold);finalColor = _AshColor;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZSKmByXl-1614487076825)(https://cdn.jsdelivr.net/gh/bzyzhang/ImgHosting//img/2020-11-28/20201128210450.gif)]
代码
7、镜头遮挡消融
这里要实现的,是当角色和镜头之间有障碍物时,对障碍物进行消融处理。
第一步,将角色的坐标传递给Shader,这一步是在C#中实现的。
public class SendPlayerPos : MonoBehaviour {public Transform player;public Material blockMat;void Update () {blockMat.SetVector ("_PlayerPos", player.position);}
}
第二步,使用屏幕空间的遮罩纹理,对消融区域进行控制。对角色和镜头之间的片元,进行剔除处理。
float toCamera = distance(input.positionWS, _WorldSpaceCameraPos);
float playerToCamera = distance(_PlayerPos.xyz, _WorldSpaceCameraPos);float2 wcoord = input.positionNDC.xy / input.positionNDC.w;
float mask = SAMPLE_TEXTURE2D(_ScreenSpaceMaskTex, sampler_ScreenSpaceMaskTex, wcoord).r;
half4 col = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
float gradient = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, input.uv).r;if (toCamera < playerToCamera)clip(gradient - mask + (toCamera - _WorkDistance) / _WorkDistance);
代码
参考
- [1] Unity Shader - 消融效果原理与变体
- [2] 《Unity Shader入门精要》
- [3] Unity技术分享 |《Trifox》中的遮挡处理和溶解着色器(上)
- [4] A Burning Paper Shader
- [5] Tutorial - Burning Edges Dissolve Shader in Unity
练习项目(二):消融效果相关推荐
- 表决器c语言课程设计,项目二:玩转RGB点阵屏——表情表决器
项目二:玩转RGB点阵屏--表情表决器项目二:玩转RGB点阵屏--表情表决器(建议2课时) [情境导入] 图2.1 医护人员"逆行"湖北 是她们不畏生死驰援湖北,为中国抗疫带来胜利 ...
- Vue购物商城项目(二) 数据请求使用
Vue购物商城项目(二) 文章目录 Vue购物商城项目(二) 前言 一.请求数据 request.js home.js Home.vue 二.使用数据 总结 前言 1.这里面包含了大量的.我的个人理解 ...
- 常见的预设分栏包括_计算机应用基础_实训项目二Word综合应用
. 专业学习资料 . 实训项目二 Word 综合应用示例 实训项目二 Word 综合应用 实训满分 20 分 . 以日常应用为基础 , 设计一个综合运用 Word 基本操作技能解决实际问题的文档 . ...
- 计算机基础实训项目二 Word 综合应用,计算机应用基础-实训项目二 Word 综合应用[优质文档]...
实训项目二Word 综合应用 实训满分20分. 以日常应用为基础,设计一个综合运用Word 基本操作技能解决实际问题的文档.文档内容要求包括以下基本操作和元素: 标题.正文.页眉/页脚/页码.首行缩进 ...
- 计算机应用基础模块三项目二,计算机应用基础 高职计算机大类专业 刁爱军模块三 项目二 海报的制作.pptx...
计算机应用基础 高职计算机大类专业 刁爱军模块三 项目二 海报的制作.pptx (31页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.90 积分 模块 ...
- 【项目二、蜂巢检测项目】一、串讲各类经典的卷积网络:InceptionV1-V4、ResNetV1-V2、MobileNetV1-V3、ShuffleNetV1-V2、ResNeXt、Xception
目录 前言 一.InceptionV1-V4 1.1.InceptionV1(GoogLeNet) - 2014 1.2.InceptionV2.InceptionV3 - 2015 1.3.Ince ...
- 信创操作系统--麒麟Kylin桌面版 (项目二 桌面环境)
信创操作系统–麒麟Kylin桌面版 (项目二 桌面环境) 目录 桌面布局 新建文件夹/文档 设置排序方式 调整图标 设置显示器 壁纸与屏保 剪贴板 任务栏 回收站 开始菜单 开始菜单模式 应用管理 窗 ...
- 项目二 PLC与RobotStudio联合仿真激光切割工作站——仿真模型搭建
项目二 PLC与RobotStudio联合仿真激光切割工作站--仿真模型搭建 一.任务描述 上图给出了整个任务的描述和基础分析.本任务牵涉到两台机器人联合仿真,整体思路有很多种.这里为了配合PLC练习 ...
- CocosCreator消融效果
效果预览 前期准备 首先我们准备一张噪声图用于后续的噪声消融 核心思路 片段着色器中有一个discard可以将片元的颜色丢弃,那我们的可以读取当前片元的某一个颜色基色(r.g.b),与我们的消融阈值b ...
最新文章
- 获取用户电脑的上网IP地址
- 系统启动时,spring配置文件解析失败,报”cvc-elt.1: 找不到元素 'beans' 的声明“异常...
- 在VMware Workstation中安装Ubuntu设置网络连接
- boost::make_tuple用法的测试程序
- VMware 扩展磁盘容量
- MVC之排球比赛计分程序 ——(二)架构概要设计
- MySQL查询更新所有满足条件的数据
- 一个页面同时发起多个ajax请求,会出现阻塞情况
- 工作总结8:关于Vue中的slot-scope=“scope“
- Oracle对表空间操作的sql
- java 中反射的使用_java中反射的基本使用
- 【白皮书分享】2022新职业教育洞察白皮书:“职”成机遇,“育”见未来.pdf...
- java 通过类名创建类,通过类名动态生成对象
- 在AIX 5.3+HACMP 5.4以上环境安装10gR2 10.2.0.1 RAC CRS Clusterware必须先运行Patch 6718715中的rootpre.sh...
- 刮刮乐html5效果擦除,HTML5实现刮刮卡的效果
- 怎么不能锁门_镜子能不能对着床
- 【谷粒商城】k8s、devops集群篇(4/4)
- ArcGIS 各版本产品补丁荟萃
- 异数OS-星星之火(二)--远程实验室注册开放
- 一文让你彻底弄清failfast、failsafe、failover、failback、failsilent