【Unity Shaders】Unity里的雾效模拟
写在前面
熟悉Unity的都知道,Unity可以进行基本的雾效模拟。所谓雾效,就是在远离我们视角的方向上,物体看起来像被蒙上了某种颜色(通常是灰色)。这种技术的实现实际上非常简单,就是根据物体距离摄像机的远近,来混合雾的颜色和物体本身的颜色即可。
Unity里设置雾效有两种方式,一种最简单的就是直接开启全局雾效,也就是在Edit->Render Settings里配置,如下图所示:
而我们只需要把“Fog”选项后面的勾选框打开即可。上图包含了一些设置:雾的颜色,模拟雾采用的方法,雾的浓度(只在采用指数方法时有用),受雾影响的距离起点和终点(只在采用线性方法时有效)。其中,比较重要的是模拟雾采用的方法,即“Fog Mode”这一选项。它可以选择三种方法:
还有一种方法就是在shader中用Fog指令设置。这里有官网的说明。
三种模式
Linear、Exponential和Exp2这三种模式实际上是使用了不同的公式计算雾的影响因子。这个影响因子会作为混合雾的颜色和物体原始颜色的参数,来计算最终的混合颜色。例如,我们使用下面的语句来计算:
float3 afterFog = mix(_FogColor.rgb, col.rgb, fogFactor);
如果影响因子为1,则表明完全没有雾效;如果为0,则表示完全被雾覆盖。而三种模式使用的公式分别如下所示:
- Linear:
,其中Dmax和Dmin分别表示受雾影响的距离起点和终点。 - Exponential:
,其中d表示雾的浓度。 - Exp2:
,其中d表示雾的浓度。
三个等式中的z,表示距离摄像机的远近。
为了充分理解雾效的实现原理和这三种方法的不同之处,我们这篇会自己在Fragment Shader中模拟雾效。
Unity模拟的雾效
我们采用如下简单的卡通苹果场景(小苹果真是我的最爱。。。)来检验雾效。原始的场景如图所示:
其中距离相机最远的小苹果的距离大约是25单位。
我们开启Unity的全局雾效后,分别采用三种方法模拟,结果如下:
它们的雾效配置如下所示:
我们在后面会解释这些参数的含义,现在我们只需要知道“Fog Density”仅在“Fog Mode”为“Exponential”或“Exp2”时有用,而“Linear Fog Start”和“Linear Fog End”仅在“Fog Mode”为“Linear”时有用即可。注意,上面的“Linear Fog Start”和“Linear Fog End”参数是基于“距离相机最远的小苹果的距离大约是25单位”这一条件设置的,只是为了让雾效更加明显而已。
现在,我们可以从视觉上了解三种方法的异同。
Fog实现的内部原理
为了充分了解雾效算法,我们现在在小苹果现有的shader里添加雾效算法的实现。
- 首先,我们需要在Properties块中添加雾效的几个设置参数:
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Ramp ("Ramp Texture", 2D) = "white" {}
_Tooniness ("Tooniness", Range(0.1,20)) = 4
_Outline ("Outline", Range(0,1)) = 0.1 _FogColor("Fog Color", Color) = (1, 0, 0, 0)
_FogIntensity("Fog Intensity", float) = 0.1
_FogStart("Fog Start", float) = 0
_FogEnd("Fog End", float) = 300
} - 下面是添加模拟雾效的函数:
float4 SimulateFog(float4 pos, float4 col)
{
pos.w = 0.0;
float dist = length(pos);
// float dist = pos.z;
// Linear
// float fogFactor = (_FogEnd - abs(dist)) / (_FogEnd - _FogStart);
// fogFactor = clamp(fogFactor, 0.0, 1.0); // Exponential
// float fogFactor = exp(-abs(_FogIntensity * dist));
// fogFactor = clamp(fogFactor, 0.0, 1.0); // Exp2
float fogFactor = exp(-(_FogIntensity * dist) * (_FogIntensity * dist));
fogFactor = clamp(fogFactor, 0.0, 1.0); float3 afterFog = mix(_FogColor.rgb, col.rgb, fogFactor); return float4(afterFog, col.a);
}解释:有了上面的公式,这个函数很好理解。值得说明的是,函数参数pos是指在view space中顶点的位置,因为只有在这个坐标系中,摄像机的位置总是位于原点。在计算距离摄像机远近时,我们有两种可选方式:一种直接使用pos.z得到近似值,一种是使用真正距离摄像机的距离,即计算xyz平方和后开根号的结果。第二种方法由于使用了计算根号这种操作,因此在性能上略微比第一种查一点,但效果也更真实。
Shader "Custom/FogSimulation" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_Ramp ("Ramp Texture", 2D) = "white" {}
_Tooniness ("Tooniness", Range(0.1,20)) = 4
_Outline ("Outline", Range(0,1)) = 0.1 _FogColor("Fog Color", Color) = (1, 0, 0, 0)
_FogIntensity("Fog Intensity", float) = 0.1
_FogStart("Fog Start", float) = 0
_FogEnd("Fog End", float) = 300
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200 CGINCLUDE
#include "UnityCG.cginc" sampler2D _MainTex;
sampler2D _Ramp; float4 _MainTex_ST; float _Tooniness;
float _Outline; float4 _FogColor;
float _FogIntensity;
float _FogStart;
float _FogEnd; float4 SimulateFog(float4 pos, float4 col)
{
pos.w = 0.0;
float dist = length(pos);
// float dist = pos.z;
// Linear
// float fogFactor = (_FogEnd - abs(dist)) / (_FogEnd - _FogStart);
// fogFactor = clamp(fogFactor, 0.0, 1.0); // Exponential
// float fogFactor = exp(-abs(_FogIntensity * dist));
// fogFactor = clamp(fogFactor, 0.0, 1.0); // Exp2
float fogFactor = exp(-(_FogIntensity * dist) * (_FogIntensity * dist));
fogFactor = clamp(fogFactor, 0.0, 1.0); float3 afterFog = mix(_FogColor.rgb, col.rgb, fogFactor); return float4(afterFog, col.a);
} ENDCG Pass {
Tags { "LightMode"="ForwardBase" } Cull Front
Lighting Off
ZWrite On CGPROGRAM #pragma vertex vert
#pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
}; struct v2f
{
float4 pos : POSITION;
float4 viewSpacePos : TEXCOORD0;
}; v2f vert (a2v v)
{
v2f o; float4 pos = mul( UNITY_MATRIX_MV, v.vertex);
float3 normal = mul( (float3x3)UNITY_MATRIX_IT_MV, v.normal);
normal.z = -0.5;
pos = pos + float4(normalize(normal),0) * _Outline;
o.pos = mul(UNITY_MATRIX_P, pos); o.viewSpacePos = mul( UNITY_MATRIX_MV, v.vertex); return o;
} float4 frag(v2f i) : COLOR
{
return SimulateFog(i.viewSpacePos, float4(0, 0, 0, 1));
} ENDCG
} Pass {
Tags { "LightMode"="ForwardBase" } Cull Back
Lighting On CGPROGRAM #pragma vertex vert
#pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
#include "UnityShaderVariables.cginc" struct a2v
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 tangent : TANGENT;
}; struct v2f
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
float3 normal : TEXCOORD1;
float4 viewSpacePos : TEXCOORD2;
LIGHTING_COORDS(3,4)
}; v2f vert (a2v v)
{
v2f o; //Transform the vertex to projection space
o.pos = mul( UNITY_MATRIX_MVP, v.vertex);
o.normal = mul((float3x3)_Object2World, SCALED_NORMAL);
//Get the UV coordinates
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex); o.viewSpacePos = mul( UNITY_MATRIX_MV, v.vertex); // pass lighting information to pixel shader
TRANSFER_VERTEX_TO_FRAGMENT(o);
return o;
} float4 frag(v2f i) : COLOR
{
//Get the color of the pixel from the texture
float4 c = tex2D (_MainTex, i.uv);
//Merge the colours
c.rgb = (floor(c.rgb*_Tooniness)/_Tooniness); //Based on the ambient light
float3 lightColor = UNITY_LIGHTMODEL_AMBIENT.xyz; //Work out this distance of the light
float atten = LIGHT_ATTENUATION(i);
//Angle to the light
float diff = dot (normalize(i.normal), normalize(_WorldSpaceLightPos0.xyz));
diff = diff * 0.5 + 0.5;
//Perform our toon light mapping
diff = tex2D(_Ramp, float2(diff, 0.5));
//Update the colour
lightColor += _LightColor0.rgb * (diff * atten);
//Product the final color
c.rgb = lightColor * c.rgb * 2; return SimulateFog(i.viewSpacePos, c);
} ENDCG
}
}
FallBack "Diffuse"
}
写在最后
【Unity Shaders】Unity里的雾效模拟的更多相关文章
- 【Unity Shaders】ShadowGun系列之二——雾和体积光
写在前面 体积光,这个名称是God Rays的中文翻译,感觉不是很形象.God Rays其实是Crepuscular rays在图形学中的说法,而Crepuscular rays的意思是云隙光.曙光. ...
- Unity中雾效的开启
原文:https://blog.csdn.net/Rhett_Yuan/article/details/54425236 1.对于雾效的开启在新版的Unity中通过界面菜单Windows->Li ...
- Re:《Unity Shader入门精要》13.3全局雾效--如何从深度纹理重构世界坐标
如何从深度纹理重构世界坐标 游戏特效,后处理是必不可少的,而后处理经常需要我们得到当前画面的像素对应世界空间的所有信息. 思路 通过深度纹理取得NDC坐标,然后再通过NDC坐标还原成世界空间坐标 // ...
- 【Unity Shaders】学习笔记——渲染管线
[Unity Shaders]学习笔记——Shader和渲染管线 转载请注明出处:http://www.cnblogs.com/-867259206/p/5595924.html 写作本系列文章时使用 ...
- 【Unity Shaders】学习笔记——SurfaceShader(八)生成立方图
[Unity Shaders]学习笔记——SurfaceShader(八)生成立方图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5630261.html ...
- 【Unity Shaders】学习笔记——SurfaceShader(七)法线贴图
[Unity Shaders]学习笔记——SurfaceShader(七)法线贴图 转载请注明出处:http://www.cnblogs.com/-867259206/p/5627565.html 写 ...
- 【Unity Shaders】学习笔记——SurfaceShader(四)用纹理改善漫反射
[Unity Shaders]学习笔记——SurfaceShader(四)用纹理改善漫反射 转载请注明出处:http://www.cnblogs.com/-867259206/p/5603368.ht ...
- 【Unity Shaders】法线纹理(Normal Mapping)的实现细节
写在前面 写这篇的目的是为了总结我长期以来的混乱.虽然题目是"法线纹理的实现细节",但其实我想讲的是如何在shader中编程正确使用法线进行光照计算.这里面最让人头大的就是各种矩阵 ...
- 【Unity Shaders】游戏性和画面特效——创建一个夜视效果的画面特效
本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...
随机推荐
- ●BZOJ 3894 文理分科
题链: https://vijos.org/d/ljt12138/p/58c696b8d3d8a16c62a248d4 (要权限号啊...用这个交吧) 题解: 题目大意: N*M的矩阵,每个位置 ...
- 【Remmarguts' Date】
一道来自POJ2449的题,它融合了单源点最短路算法.启发式搜索,让我们对“启发式”有更深的理解和体会.Wow! ·英文题,述大意: 读入n,m(n<=1000,m<=1000 ...
- 新版Eclipse打开jsp、js等为文本编辑,没有JSP Editor插件问题
刚从官网下载安装的Eclipse Java Oxygen.2但是打开的jsp文件尽然默认文本编辑器打开,就js文件也是一样,纳闷! 网上搜索一番,原来缺少web开发相关工具, 下面给插件安装方法: 1 ...
- linux办公软件的使用和病毒防范
今天看了linux办公软件的使用和病毒防范,特做此记录,将不熟悉的内容总结一下: openoffice 和liberoffice是可以跨平台的两款办公软件.odt是openoffice的扩展名.lib ...
- USB_ESD处理
今天收到客户反馈说碰到USB后机器会死机,之前一直没有关注ESD问题. 现在整理之前用过的成熟的ESD电路: 电感为 PZ3216D101-3R0TF,1206封装. 用ESD枪测试OK, 用打火机持 ...
- Quartz + Tablesaw 报表统计
场景 在12 月份做的报表功能中,直接从 ES 查询一个月的数据.当数据量特别大时,查询速度会非常缓慢甚至查询失败.解决方案是使用定时任务,在每天凌晨指定时间自动查询前一天的数据,然后写入 CSV 文 ...
- [ Java学习基础 ] Java的抽象类与接口
一.抽象类 1. 抽象类 Java语言提供了两种类:一种是具体类:另一种是抽象子类. 2. 抽象类概念: 在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的 ...
- 一口一口吃掉Hibernate(四)——多对一单向关联映射
hibernate对于数据库的操作,全部利用面向对象的思维来理解和实现的.一般的单独表的映射,相信大家都没有问题,但是对于一些表之间的特殊关系,Hibernate提供了一些独特的方式去简化它. 今天就 ...
- 底部导航栏实现一 Fragment-replace
[效果](这里下载的软件收费的试用有水印) [推荐]这里推荐一个图标网http://iconfont.cn/.以上图标来自此图标网 [项目结构] [步骤] ①创建布局文件,写底部导航栏 <?xm ...
- Windows下的DOM操作
前言:这学期学Linux之后,发现用命令行的方式来操作电脑还是很好玩的~写这篇随笔来总结下Windows环境下通过DOS来执行命令的操作,希望能帮到您~自己忘记的时候能回来复习emmm 启动DOS环境 ...