Unity Shader 屏幕后效果——全局雾
Unity内置的雾效需要在每个shader中分别编写,造成了极大的不便。这里利用屏幕后处理产生可单独控制且自由度更高的雾效。
屏幕后雾效的本质在于,通过深度纹理重构出每个像素在世界空间中的位置,根据得到的世界坐标计算出雾效系数,最后利用雾效系数与雾的颜色相乘并与原始颜色进行插值运算得出最终效果。
float3 afterFog=f*fogColor+(1-f)*origColor;
上面的插值运算中f代表雾效系数,
它有多种计算方法:
1.线性运算:
f=(dmax-Abs(z))/dmax-dmin;
其中dmax和dmin分别代表受雾影响的最大和最小距离,z为给定的距离位置(像素位置)
2.指数运算:
f=pow(e,-d*Abs(z));
其中d控制雾的浓度,e为数学常量
3.二次指数:
f=pow(e,-pow(d*z,2));
为了更方便的对参数进行控制,需要重构每个像素在世界空间中的位置,常规实现方法如下:
1.构建像素的NDC坐标然后用VP矩阵的逆矩阵反向推导
2.通过向量的基本运算求得
方法1需要在片元着色器中进行矩阵乘法,若想得到性能更优的实现方式,考虑使用方法2。
向量的基本运算方式如下:
float4 worldPos=_WorldSpaceCameraPos+linearDepth*interpolatedRay;
_WorldSpaceCameraPos表示摄像机在世界空间中的位置,linearDepth*interpolatedRay是为了求得世界空间下的像素相对于摄像机的偏移量。根据向量的加法,就可以求出该像素在世界空间中的位置。
linearDepth线性深度值可以利用摄像机的深度纹理来求,关键在于求一个插值射线interpolatedRay。
分析interpolatedRay的含义可以知道,它主要表示该像素到摄像机的方向向量,可以由顶点着色器的各个顶点输出并插值得到。
基于这一点,可以直接在C#脚本中计算出屏幕四个顶点(左上,左下,右上,右下)的向量,传值给顶点着色器即可,这样避免在Shader中进行繁杂的数学运算。
参数控制脚本,同时计算顶点相对于摄像机的方向向量。此脚本挂载在摄像机上:
using UnityEngine; public class FogWithDepthTexCtrl : ScreenEffectBase
{
private const string _FrustumCornersRay = "_FrustumCornersRay"; private const string _FogDensity = "_FogDensity";
private const string _FogColor = "_FogColor";
private const string _FogUnderStart = "_FogUnderStart";
private const string _FogTopEnd = "_FogTopEnd"; private Camera myCamera;
public Camera MyCamera
{
get
{
if (myCamera == null)
myCamera = GetComponent<Camera>();
return myCamera;
}
} private Transform myCameraTran;
public Transform MyCameraTran
{
get
{
if (myCameraTran == null)
myCameraTran = MyCamera.transform;
return myCameraTran;
}
} [Range(, )]
public float fogDensity = 1.0f;//控制雾的浓度
public Color fogColor = Color.white;
public float fogUnderStart = 0.0f;//雾起始高度
public float fogTopEnd = 2.0f;//雾结束高度 private void OnEnable()
{
MyCamera.depthTextureMode |= DepthTextureMode.Depth;
} private void OnDisable()
{
MyCamera.depthTextureMode &= ~DepthTextureMode.Depth;
} private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (Material != null)
{
//需要传递的四个角相对于摄像机的方向向量,这里用矩阵的每一行来表示
Matrix4x4 frustumCorners = Matrix4x4.identity; float fov = MyCamera.fieldOfView;
float near = MyCamera.nearClipPlane;
float aspect = MyCamera.aspect; //计算近裁剪平面三个标准方向
float halfHeight = near * Mathf.Tan(fov * .5f * Mathf.Deg2Rad);
Vector3 toTop = halfHeight * MyCameraTran.up;
Vector3 toRight = halfHeight * MyCameraTran.right * aspect;
Vector3 toForward = near * MyCameraTran.forward; //用三个标准方向重构四个顶点关于摄像机的向量
Vector3 topRight = toForward + toRight + toTop;
topRight /= near; Vector3 topLeft = toForward - toRight + toTop;
topLeft /= near; Vector3 bottomRight = toForward + toRight - toTop;
bottomRight /= near; Vector3 bottomLeft = toForward - toRight - toTop;
bottomLeft /= near; //用矩阵的每一行来存储这些向量,这里的顺序要与之后解析的顺序对应
frustumCorners.SetRow(, topLeft);
frustumCorners.SetRow(, topRight);
frustumCorners.SetRow(, bottomLeft);
frustumCorners.SetRow(, bottomRight); //传递向量矩阵和对应的参数
Material.SetMatrix(_FrustumCornersRay, frustumCorners); Material.SetFloat(_FogDensity, fogDensity);
Material.SetColor(_FogColor, fogColor);
Material.SetFloat(_FogUnderStart, fogUnderStart);
Material.SetFloat(_FogTopEnd, fogTopEnd); Graphics.Blit(source, destination, Material);
}
else
Graphics.Blit(source, destination);
}
}
基类见:
https://www.cnblogs.com/koshio0219/p/11131619.html
Shader脚本:
Shader "MyUnlit/FogWithDepthTex"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
ZTest Always Cull Off ZWrite Off CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" //对应四个顶点的射线矩阵
float4x4 _FrustumCornersRay; sampler2D _MainTex;
half4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;
half _FogDensity;
fixed4 _FogColor;
float _FogUnderStart;
float _FogTopEnd; struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
}; struct v2f
{
half4 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
//顶点着色器输出的插值射线
float4 interpolatedRay:TEXCOORD1;
}; v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.uv;
o.uv.zw=v.uv;//zw存深度纹理 //对插值射线的索引进行解析,判定该顶点是四个角中的哪一个
int idx=;
if(v.uv.x>.5f&&v.uv.y>.5f)
idx=;
else if(v.uv.x<.5f&&v.uv.y<.5f)
idx=;
else if(v.uv.x>.5f&&v.uv.y<.5f)
idx=; //主纹理外的纹理要进行平台差异化处理,同时对顶点的索引也需要进行处理(左上对左下,右上对右下)
#if UNITY_UV_STARTS_AT_TOP
if(_MainTex_TexelSize.y<){
o.uv.w=-o.uv.w;
idx=idx<?idx+:idx-;
}
#endif //按照解析的索引值得到需要传递的插值射线
o.interpolatedRay=_FrustumCornersRay[idx]; return o;
} fixed4 frag (v2f i) : SV_Target
{
//计算像素在世界空间中的位置
float linearDepth=LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv.zw));
float3 worldPos=_WorldSpaceCameraPos+linearDepth*i.interpolatedRay.xyz; //计算雾效系数,这里主要用的关于世界空间高度的线性雾计算
float fogDensity=(_FogTopEnd-worldPos.y)/(_FogTopEnd-_FogUnderStart);
fogDensity=saturate(fogDensity*_FogDensity); //插值得到最终雾效
fixed4 col = tex2D(_MainTex, i.uv);
col.rgb=lerp(col.rgb,_FogColor.rgb,fogDensity); return col;
}
ENDCG
}
}
}
效果如下:

Unity Shader 屏幕后效果——全局雾的更多相关文章
- Unity Shader 屏幕后效果——颜色校正
屏幕后效果指的是,当前整个场景图已经渲染完成输出到屏幕后,再对输出的屏幕图像进行的操作. 在Unity中,一般过程通常是: 1.建立用于处理效果的shader和临时材质,给shader脚本传递需要控制 ...
- Unity Shader 屏幕后效果——边缘检测
关于屏幕后效果的控制类详细见之前写的另一篇博客: https://www.cnblogs.com/koshio0219/p/11131619.html 这篇主要是基于之前的控制类,实现另一种常见的屏幕 ...
- Unity Shader 屏幕后效果——高斯模糊
高斯模糊是图像模糊处理中非常经典和常见的一种算法,也是Bloom屏幕效果的基础. 实现高斯模糊同样用到了卷积的概念,关于卷积的概念和原理详见我的另一篇博客: https://www.cnblogs.c ...
- Unity Shader 屏幕后效果——景深
景深效果的原理是,在摄像机的近裁剪平面和远裁剪平面之间可以设置一个焦距,在这个距离所在的平面上的物体最为清晰,而这个距离之前或之后的物体成像是一种模糊状态(根据距离逐渐模糊,最终达到最为模糊的状态). ...
- Unity Shader 屏幕后效果——Bloom外发光
Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成. 一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客: https:/ ...
- Unity Shader 屏幕后效果——摄像机运动模糊(速度映射图实现)
速度映射图主要是为了得到每个像素相对于前一帧的运动矢量,其中一种方法是使用摄像机的深度纹理来推导. 推导过程如下: 先由深度纹理逆推出NDC(归一化的设备坐标)下的顶点坐标,利用VP矩阵(视角*投影矩 ...
- Unity Shader实现描边效果
http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用S ...
- Unity Shader 之 透明效果
透明效果 透明效果一般有两种实现方法: 第一种,使用透明度测试(Alpha Test) 第二种,使用透明度混合(Alpha Blending) 透明度测试和透明度混合机制: 透明度测试(Alpha T ...
- Unity实现屏幕抖动效果(通过Camera Viewpoint实现)
由于游戏死亡时一般都需要屏幕抖一下下. 所以百度了下相关写法,发现方法很多~~~ 找来找去,找到个简单粗暴地,啥都不需要,一个脚本拖动到Camera上就可以了 略微修改了一点点,share一下 usi ...
随机推荐
- H5生成二维码
要用H5生成二维码: 1.引入js库,可自行点击链接复制使用 <script type="text/javascript" src="http://static.r ...
- Docker - 卷组管理(三)
一.不指定宿主机目录 首先运行一个nginx容器 docker run -d --name mynginx -p 8080:80 -v /usr/share/nginx/html nginx --na ...
- 使用zabbix监控linux的io
zabbix自带的监控linux的模板中并没有监控io这项,而实际生产中又需要监控io,如何监控呢. 错误的示例 这里我特意贴出错误的示例出来,是因为我在网上搜如何使用zabbix监控io的文章时,好 ...
- .Net ADO拼接带参数的SQL语句
首先是在DAL数据访问层中的代码://数据更新的方法public static int shuxing_update(s_passnature model) { string sql = " ...
- MVC教程:MVC区域路由
一.区域路由 为了管理网站中大量的文件,在ASP.NET MVC 2.0版本中引入了一个新概念:区域(Area). 有了区域以后,可以让我们的项目不至于太复杂而导致管理混乱.每个模块的页面都放入相应的 ...
- cl_demo_output=>display 介绍
Methods of CL_DEMO_OUTPUT PS:自己测试是display后的内表不能带表头. 类CL_DEMO_OUTPUT 在示例程序中创造了很多简单的数据输出的方法而不需要经典的list ...
- ABAP 7.4 新语法-内嵌生命和内表操作(转)
转自:https://www.cnblogs.com/mingdashu/p/6744637.html ABAP 7.4 新语法-内嵌生命和内表操作 1.内嵌声明 2.内表操作 3.opensql ...
- Python爬取Boss直聘,帮你获取全国各类职业薪酬榜
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 王翔 清风Python PS:如有需要Python学习资料的小伙伴 ...
- 通过SSH通道来访问MySQL
许多时候当要使用Mysql时,会遇到如下情况: 1. 信息比较重要,希望通信被加密.2. 一些端口,比如3306端口,被路由器禁用. 对第一个问题的一个比较直接的解决办法就是更改mysql的代码,或 ...
- Python笔记:设计模式之观察者模式
观察者模式中的主题对象一般存在着一个其他服务依赖的核心服务,并且维护着其他依赖此核心服务的对象列表(即观察者或监视者列表),当主题对象发生变化时,观察者应该改变自己的状态或者进行某些操作 观察者模式中 ...