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. IT兄弟连 HTML5教程 HTML文档头部元素head

    HTML头部标记是<head>,主要包括页面的一些基本描述语句,以及CSS和JavaScript,一般都可以定义在头部元素中.它用于包含当前文档的有关信息,例如网页标题和关键字等.通常位于 ...

  2. Java开发桌面程序学习(七)——ImageView设置图片以及jar包读取fxml文件

    ImageView设置图片 JavaFx的ImageView,设置图片不能直接通过属性设置,只能通过代码来设置 ImageView设置图片 首先,我们让fxml对应的那个controller的java ...

  3. PlayJava Day011

    今日所学: /* 2019.08.19开始学习,此为补档. */ Java异常处理 1.异常的概念:程序运行过程中发生的问题,从而引发了中断. 2.捕获和处理异常:Java中,用try ... cat ...

  4. JS基础语法---分支语句之:if语句,if-else语句,if-ever if语句

    //if语句只有一个分支 //if-else语句有两个分支,最终执行一个分支 //if-else if-else if-else if-else if..........else---多分支,最终也是 ...

  5. 使用JS通过Web API执行批量操作,多个操作是一个事务!

    关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复235或者20161105可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...

  6. iOS多线程比较

    .iOS的三种多线程技术 .NSThread 每个NSThread对象对应一个线程,量级较轻(真正的多线程) .以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去关心线程的具体使用问题 ØNS ...

  7. 实时数据推送webSocket

    实时数据推送 在Web或移动项目中,服务器向客户端实时推送消息是一种常见的业务需求. 实现方式 Polling:轮询(俗称“拉”),即定期重新请求数据. Long-Polling:长轮询,是 Poll ...

  8. java实现序列化的两种方式

    1.Serializable接口 2.Externalizable接口 public class Demo2 implements Externalizable{ transient private ...

  9. [视频教程] 最新版swoole安装和TASKS功能测试

    今天我们来安装和测试一下php的多并发高性能网络通信扩展,这个扩展是使用C语音开发的,加载到PHP以后,在PHP的层面上实现了多并发异步通信,模拟了go语音的很多特性,极大的拓宽了PHP的应用场景. ...

  10. (入门SpringBoot)SpringBoot项目创建基本配置(二)

    SpringBoot的环境搭建和基本开发:1.环境开发就不说了,一个程序员的基本功:2.基本开发-使用自定义的配置:2.1.配置文件.properties和yml文件.2.2.SpringBoot配置 ...