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.利用向量的基本运算求得

因为第一种方法需要在片元着色器中进行矩阵乘法,若想得到性能更优的实现方式,考虑使用第二种。

向量的基本运算方式如下:

float4 worldPos=_WorldSpaceCameraPos+linearDepth*interpolatedRay;

上面的运算中,_WorldSpaceCameraPos表示摄像机在世界空间中的位置,linearDepth*interpolatedRay是为了求得世界空间下的像素相对于摄像机的偏移量,根据向量的加法,就可以求出这个向量在世界空间中的位置。

linearDepth线性深度值可以利用摄像机的深度纹理来求,关键就是求一个插值射线interpolatedRay。

分析interpolatedRay的含义可以知道,它主要表示该像素到摄像机的方向向量,可以由顶点着色器的各个顶点输出并插值得到。

基于这一点,在顶点着色器中对屏幕四个顶点(左上,左下,右上,右下)的向量进行传值,利用外部脚本提前计算好四个向量即可,这样不需要在Shader中进行繁杂的数学运算。

参数控制脚本,同时计算顶点相对于摄像机的方向向量。此脚本挂载在摄像机上:

 1 using UnityEngine;
 2
 3 public class FogWithDepthTexCtrl : ScreenEffectBase
 4 {
 5     private const string _FrustumCornersRay = "_FrustumCornersRay";
 6
 7     private const string _FogDensity = "_FogDensity";
 8     private const string _FogColor = "_FogColor";
 9     private const string _FogUnderStart = "_FogUnderStart";
10     private const string _FogTopEnd = "_FogTopEnd";
11
12     private Camera myCamera;
13     public Camera MyCamera
14     {
15         get
16         {
17             if (myCamera == null)
18                 myCamera = GetComponent<Camera>();
19             return myCamera;
20         }
21     }
22
23     private Transform myCameraTran;
24     public Transform MyCameraTran
25     {
26         get
27         {
28             if (myCameraTran == null)
29                 myCameraTran = MyCamera.transform;
30             return myCameraTran;
31         }
32     }
33
34     [Range(0, 3)]
35     public float fogDensity = 1.0f;//控制雾的浓度
36     public Color fogColor = Color.white;
37     public float fogUnderStart = 0.0f;//雾起始高度
38     public float fogTopEnd = 2.0f;//雾结束高度
39
40     private void OnEnable()
41     {
42         MyCamera.depthTextureMode |= DepthTextureMode.Depth;
43     }
44
45     private void OnDisable()
46     {
47         MyCamera.depthTextureMode &= ~DepthTextureMode.Depth;
48     }
49
50     private void OnRenderImage(RenderTexture source, RenderTexture destination)
51     {
52         if (Material != null)
53         {
54             //需要传递的四个角相对于摄像机的方向向量,这里用矩阵的每一行来表示
55             Matrix4x4 frustumCorners = Matrix4x4.identity;
56
57             float fov = MyCamera.fieldOfView;
58             float near = MyCamera.nearClipPlane;
59             float aspect = MyCamera.aspect;
60
61             //计算近裁剪平面三个标准方向
62             float halfHeight = near * Mathf.Tan(fov * .5f * Mathf.Deg2Rad);
63             Vector3 toTop = halfHeight * MyCameraTran.up;
64             Vector3 toRight = halfHeight * MyCameraTran.right * aspect;
65             Vector3 toForward = near * MyCameraTran.forward;
66
67             //用三个标准方向重构四个顶点关于摄像机的向量
68             Vector3 topRight = toForward + toRight + toTop;
69             topRight /= near;
70
71             Vector3 topLeft = toForward - toRight + toTop;
72             topLeft /= near;
73
74             Vector3 bottomRight = toForward + toRight - toTop;
75             bottomRight /= near;
76
77             Vector3 bottomLeft = toForward - toRight - toTop;
78             bottomLeft /= near;
79
80             //用矩阵的每一行来存储这些向量,这里的顺序要与之后解析的顺序对应
81             frustumCorners.SetRow(0, topLeft);
82             frustumCorners.SetRow(1, topRight);
83             frustumCorners.SetRow(2, bottomLeft);
84             frustumCorners.SetRow(3, bottomRight);
85
86             //传递向量矩阵和对应的参数
87             Material.SetMatrix(_FrustumCornersRay, frustumCorners);
88
89             Material.SetFloat(_FogDensity, fogDensity);
90             Material.SetColor(_FogColor, fogColor);
91             Material.SetFloat(_FogUnderStart, fogUnderStart);
92             Material.SetFloat(_FogTopEnd, fogTopEnd);
93
94             Graphics.Blit(source, destination, Material);
95         }
96         else
97             Graphics.Blit(source, destination);
98     }
99 }

基类见:

https://www.cnblogs.com/koshio0219/p/11131619.html

Shader脚本:

 1 Shader "MyUnlit/FogWithDepthTex"
 2 {
 3     Properties
 4     {
 5         _MainTex ("Texture", 2D) = "white" {}
 6     }
 7     SubShader
 8     {
 9         Pass
10         {
11             CGPROGRAM
12             #pragma vertex vert
13             #pragma fragment frag
14
15             #include "UnityCG.cginc"
16
17             //对应四个顶点的射线矩阵
18             float4x4 _FrustumCornersRay;
19
20             sampler2D _MainTex;
21             half4 _MainTex_TexelSize;
22             sampler2D _CameraDepthTexture;
23             half _FogDensity;
24             fixed4 _FogColor;
25             float _FogUnderStart;
26             float _FogTopEnd;
27
28             struct appdata
29             {
30                 float4 vertex : POSITION;
31                 float2 uv : TEXCOORD0;
32             };
33
34             struct v2f
35             {
36                 half4 uv : TEXCOORD0;
37                 float4 vertex : SV_POSITION;
38                 //顶点着色器输出的插值射线
39                 float4 interpolatedRay:TEXCOORD1;
40             };
41
42             v2f vert (appdata v)
43             {
44                 v2f o;
45                 o.vertex = UnityObjectToClipPos(v.vertex);
46                 o.uv.xy = v.uv;
47                 o.uv.zw=v.uv;//zw存深度纹理
48
49                 //对插值射线的索引进行解析,判定该顶点是四个角中的哪一个
50                 int idx=0;
51                 if(v.uv.x>.5f&&v.uv.y>.5f)
52                     idx=1;
53                 else if(v.uv.x<.5f&&v.uv.y<.5f)
54                     idx=2;
55                 else if(v.uv.x>.5f&&v.uv.y<.5f)
56                     idx=3;
57
58                 //主纹理外的纹理要进行平台差异化处理,同时对顶点的索引也需要进行处理(左上对左下,右上对右下)
59                 #if UNITY_UV_STARTS_AT_TOP
60                 if(_MainTex_TexelSize.y<0){
61                     o.uv.w=1-o.uv.w;
62                     idx=idx<2?idx+2:idx-2;
63                 }
64                 #endif
65
66                 //按照解析的索引值得到需要传递的插值射线
67                 o.interpolatedRay=_FrustumCornersRay[idx];
68
69                 return o;
70             }
71
72             fixed4 frag (v2f i) : SV_Target
73             {
74                 //计算像素在世界空间中的位置
75                 float linearDepth=LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv.zw));
76                 float3 worldPos=_WorldSpaceCameraPos+linearDepth*i.interpolatedRay.xyz;
77
78                 //计算雾效系数,这里主要用的关于世界空间高度的线性雾计算
79                 float fogDensity=(_FogTopEnd-worldPos.y)/(_FogTopEnd-_FogUnderStart);
80                 fogDensity=saturate(fogDensity*_FogDensity);
81
82                 //插值得到最终雾效
83                 fixed4 col = tex2D(_MainTex, i.uv);
84                 col.rgb=lerp(col.rgb,_FogColor.rgb,fogDensity);
85
86                 return col;
87             }
88             ENDCG
89         }
90     }
91 }

效果如下:

原文地址:https://www.cnblogs.com/koshio0219/p/11237284.html

时间: 2024-10-29 13:25:01

Unity Shader 屏幕后效果——全局雾的相关文章

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

Bloom的原理很简单,主要是提取渲染图像中的亮部区域,并对亮部区域进行模糊处理,再与原始图像混合而成. 一般对亮部进行模糊处理的部分采用高斯模糊,关于高斯模糊,详见之前的另一篇博客: https://www.cnblogs.com/koshio0219/p/11152534.html 计算方法: 总共需要用到4个Pass,它们的顺序如下: Pass 1:得到纹理的亮度值(灰度值),由此计算出亮部区域,传递给一个临时的新纹理,这里叫_Bloom Pass 2,3:单独对_Bloom进行高斯模糊(

Unity Shader实现描边效果

http://gad.qq.com/article/detail/28346 描边效果是游戏里面非常常用的一种效果,一般是为了凸显游戏中的某个对象,会给对象增加一个描边效果.本篇文章和大家介绍下利用Shader实现描边效果,一起来看看吧. 最近又跑回去玩了玩<剑灵>,虽然出了三年了,感觉在现在的网游里面画面仍然算很好的了,剑灵里面走近或者选中NPC的一瞬间,NPC就会出现描边效果,不过这个描边效果是渐变的,会很快减弱最后消失(抓了好久才抓住一张图....) 还有就是最常见的LOL中的塔,我们把

Unity Shader 模型流光效果

Shader "Custom/FlowColor" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _FlowColor("Flow Color", Color) = (1,1,1,1) _FlowRange("Flow Range", Float) = 0.01 } SubShader { Pass { Tags { "Queue&

Unity shader实现水效果(折射,反射,波浪,1.菲尼尔,深度颜色)

整个实现过程,包括水面的UV流动,折射,反射,根据深度进行透明值处理等等 原文地址:https://www.cnblogs.com/ubanck/p/9606626.html

Unity shader学习之屏幕后期效果之调整屏幕亮度,饱和度,对比度

Unity的屏幕后期处理效果,使用MonoBehaviour.OnRenderImage来实现. 如代码如下: PostEffectRenderer: 1 using UnityEngine; 2 3 [RequireComponent(typeof(Camera))] 4 public abstract class PostEffectRenderer : GameBehaviour 5 { 6 protected abstract string ShaderName { get; } 7 8

【Unity Shader编程】之十五 屏幕高斯模糊(Gaussian Blur)后期特效的实现

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/51871531 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 本文工程使用的Unity3D版本: 5.2.1  本篇文章将分析如何在Unity中基于Shader实现高斯模糊屏幕后期特效. 首先放出最终的实现效果.如下几幅图,是在Unity中使用本文所实现的Shader得到的高斯模糊屏幕

【Unity Shader】(九) ------ 高级纹理之渲染纹理及镜子与玻璃效果的实现

笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Shader](三) ------ 光照模型原理及漫反射和高光反射的实现 [Unity Shader](四) ------ 纹理之法线纹理.单张纹理及遮罩纹理的实现 [Unity Shader](五) ------ 透明效果之半透明效果的实现及原理 [Unity Shader](六) ------ 复杂的光照(上) [Unity

【我的书】Unity Shader的书 — 目录(2016.1.29更新)

写在前面 感谢所有点进来看的朋友.没错,我目前打算写一本关于Unity Shader的书. 出书的目的有下面几个: 总结我接触Unity Shader以来的历程,给其他人一个借鉴.我非常明白学Shader的艰难,在群里也见了很多人提出的问题.我觉得学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么好处呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader初学者,但要: 有一定的

【我的书】Unity Shader的书 — 文件夹(2015.12.21更新)

写在前面 感谢全部点进来看的朋友.没错.我眼下打算写一本关于Unity Shader的书. 出书的目的有以下几个: 总结我接触Unity Shader以来的历程,给其它人一个借鉴.我非常明确学Shader的艰难,在群里也见了非常多人提出的问题. 我认为学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么优点呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader刚開始学习的人,但要