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 屏幕后效果——全局雾的更多相关文章

  1. Unity Shader 屏幕后效果——颜色校正

    屏幕后效果指的是,当前整个场景图已经渲染完成输出到屏幕后,再对输出的屏幕图像进行的操作. 在Unity中,一般过程通常是: 1.建立用于处理效果的shader和临时材质,给shader脚本传递需要控制 ...

  2. Unity Shader 屏幕后效果——边缘检测

    关于屏幕后效果的控制类详细见之前写的另一篇博客: https://www.cnblogs.com/koshio0219/p/11131619.html 这篇主要是基于之前的控制类,实现另一种常见的屏幕 ...

  3. Unity Shader 屏幕后效果——高斯模糊

    高斯模糊是图像模糊处理中非常经典和常见的一种算法,也是Bloom屏幕效果的基础. 实现高斯模糊同样用到了卷积的概念,关于卷积的概念和原理详见我的另一篇博客: https://www.cnblogs.c ...

  4. Unity Shader 屏幕后效果——景深

    景深效果的原理是,在摄像机的近裁剪平面和远裁剪平面之间可以设置一个焦距,在这个距离所在的平面上的物体最为清晰,而这个距离之前或之后的物体成像是一种模糊状态(根据距离逐渐模糊,最终达到最为模糊的状态). ...

  5. Unity Shader 屏幕后效果——Bloom外发光

    Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成. 一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客: https:/ ...

  6. Unity Shader 屏幕后效果——摄像机运动模糊(速度映射图实现)

    速度映射图主要是为了得到每个像素相对于前一帧的运动矢量,其中一种方法是使用摄像机的深度纹理来推导. 推导过程如下: 先由深度纹理逆推出NDC(归一化的设备坐标)下的顶点坐标,利用VP矩阵(视角*投影矩 ...

  7. Unity Shader实现描边效果

    http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用S ...

  8. Unity Shader 之 透明效果

    透明效果 透明效果一般有两种实现方法: 第一种,使用透明度测试(Alpha Test) 第二种,使用透明度混合(Alpha Blending) 透明度测试和透明度混合机制: 透明度测试(Alpha T ...

  9. Unity实现屏幕抖动效果(通过Camera Viewpoint实现)

    由于游戏死亡时一般都需要屏幕抖一下下. 所以百度了下相关写法,发现方法很多~~~ 找来找去,找到个简单粗暴地,啥都不需要,一个脚本拖动到Camera上就可以了 略微修改了一点点,share一下 usi ...

随机推荐

  1. c#汉字转拼音首字母全拼支持多音字

    1.首先在NuGet安装pingyinConverter 2.下载-安装-引用ChineseChar.dll到项目中 官网了解:http://www.microsoft.com/zh-cn/downl ...

  2. 给HttpClient添加请求头(HttpClientFactory)

    前言 在微服务的大环境下,会出现这个服务调用这个接口,那个接口的情况.假设出了问题,需要排查的时候,我们要怎么关联不同服务之间的调用情况呢?换句话就是说,这个请求的结果不对,看看是那里出了问题. 最简 ...

  3. Python - __name__=='__main__'是干啥的,以及python -m与python的区别

    1. __name__=='__main__'是干啥的 先看例子,准备a.py和b.py放在同一个文件夹中 vi a.py # coding: utf-8 print("i am just ...

  4. Java报错:java.math.BigDecimal cannot be cast to java.lang.String

    从数据库取数字,转为string,报错: java.math.BigDecimal cannot be cast to java.lang.String 错误代码 Integer.parseInt(( ...

  5. Shell(三):echo、printf、test命令

    一.echo 1.显示普通字符串: echo "today is a wonderful day" 这里的双引号可以省略. 2.显示转义字符: echo "\" ...

  6. Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的特点

    1.不变性 新的日期/时间API中,所有的类都是不可变的,这对多线程环境有好处. 比如:LocalDateTime 2.关注点分离 新的API将人可读的日期时间和机器时间(unix timestamp ...

  7. 区块链社交APP协议分析预告

    2017年,比特币的火热,直接导致了代币市场的繁荣: 2018年,作为信用体系的未来解决方案,区块链引发了互联网原住民的淘金热. 作为风口上的引流神器,区块链技术与社交网络结合起来,产生了一系列区块链 ...

  8. Github使用总结(添加ssh-key,新建仓库,添加协作者) 转

    http://jingyan.baidu.com/article/ab0b5630936ab6c15afa7d1c.html https://help.github.com/articles/gene ...

  9. LCD RGB 控制技术 时钟篇(下)【转】

    上一篇博文,我们介绍了LCD RGB控制模式的典型时钟.那么这一片我们要详细的去讨论剩下的细节部分. 我们先回顾一下之前的典型时序图 在这个典型的时序图里面,除了上篇博文讲述的HSYNC VSYNC ...

  10. try ... except...,好处是执行失败后,仍然可以继续运行

    import requeststry: a=requests.get("https:///www.baidu.com") print('连接成功')except: print('连 ...