- 观察生活中的雾
- Scenario 1 - Pure Depth Fog - 纯深度雾
- 提取有用信息
- Scenario 2 - Height Fog - 带高度的雾
- 提取有用信息
- Scenario 1 & 2 are same - 本质上两种雾都是一样的
- 先看看Unity自带的Fog效果
- Linear - 线性的
- Exponential - 指数的
- Exponential Squared - 指数平方的
- 再参考Unity Built-in shader的Fog部分
- UnityCG.cginc Fog Helper 开始
- sutrct v2f 中的 UNITY_FOG_COORDS
- vert 函数中的 UNITY_TRANSFER_FOG
- frag 函数中的 UNITY_APPLY_FOG
- 看看Linear/Exponential/Exponential Squared三个公式
- Linear
- Exponential
- Exponential Squared
- 总结Unity 内置雾
- 优点
- 缺点
- 后效雾实现
- 深度雾
- 运行效果
- view space与world space的区别
- 添加噪点
- 高度雾
- 添加噪点
- 深度高度雾
- 改进版本的高度雾
- 总结
- Project
- References
先不看效果,我们先细心想想,生活中看到的雾是怎么个表现?
Scenario 1 - Pure Depth Fog - 纯深度雾不过现在基本上都看不到雾了,很多年没有见过,印象中的雾应该在8~9年前,那时候坐着大巴,司机开得很慢很慢,因为雾太浓了,雾的颜色比奶白色暗一些,眼前只有2~5M可视,再远一点就全是雾了。
所以我们确定了一些有用的信息: 雾的颜色,眼前2~5M可视,再远一点就全是雾了。
提取有用信息- 雾的颜色
- 眼前 的眼,可以理解为我们的相机位置
- 2~5M 可以看过是雾化淡入的范围
- 再远一点全是雾
如:我们常常看到一些综艺节目里,或是音乐节目里整个舞台地表大概:0.2M都是雾。
提取有用信息就是有高度
Scenario 1 & 2 are same - 本质上两种雾都是一样的Scenario 1与2的两种雾起始都是一样的,为什么我们需要做两种区别能:分开实现,在不同场景使用性能更好。
当然你也可以使用Scenario 2中的雾来实现所有的场景,调整一下雾的高度为:很大的值(很高)即可。
我按照自己的理解就是:雾的固状体有密度区别。
密度低一些的雾就会飘得比较高,以至于高到我们整改眼前的视野都包括了,所以我们对这种雾处理为:纯深度雾。
密度高一些的雾就会飘得比较低,就像前面提到的舞台上的雾(一般是一些干冰雾),所以我们处理为:高度雾,只有部分高度可见。
上面只是我自己的理解,百度百科有比较全面的了解,有兴趣点击这里查看:雾
下面讲解以unity 2018.3.0f2 版本为主
先看看Unity自带的Fog效果 Linear - 线性的我们在创建shader选择unlit shader就得到一个代码模板生成的shader
代码如下
Shader "Unlit/Fog"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;
}
ENDCG
}
}
}
可以看到有各种 Fog
的字眼。
那么下面我们接着看看这些宏对应Built-in Shader里有是什么内容 (这里只看一些主要的部分,不会全面的去看)
UnityCG.cginc Fog Helper 开始// ------------------------------------------------------------------
// Fog helpers
//
// multi_compile_fog Will compile fog variants.
// UNITY_FOG_COORDS(texcoordindex) Declares the fog data interpolator.
// UNITY_TRANSFER_FOG(outputStruct,clipspacePos) Outputs fog data from the vertex shader.
// UNITY_APPLY_FOG(fogData,col) Applies fog to color "col". Automatically applies black fog when in forward-additive pass.
// Can also use UNITY_APPLY_FOG_COLOR to supply your own fog color.
multi_compile_fog
编译雾的变体shader的指令。UNITY_FOG_COORDS(texcoordindex)
定义雾需要的v2f
的插值数据。UNITY_TRANSFER_FOG(outputStruct,clipspacePos)
从vertex shader
顶点着色器输出数据UNITY_APPLY_FOG(fogData,col)
应用雾的颜色来着色。当使用forward-additive lightmode
的pass会自动应用黑色的雾颜色。- 也可以使用
UNITY_APPLY_FOG_COLOR
来当做是雾的颜色
struct v2f
{
float2 uv : TEXCOORD0;
UNITY_FOG_COORDS(1)
float4 vertex : SV_POSITION;
};
UNITY_FOG_COORDS
对应UnityCG.cginc 下的源码
// 1
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
#define UNITY_FOG_COORDS(idx) UNITY_FOG_COORDS_PACKED(idx, float1)、
// 2
#define UNITY_FOG_COORDS_PACKED(idx, vectype) vectype fogCoord : TEXCOORD##idx;
可以看到,就是定义一个float1 fogCoord : TEXCOORD[N]
的一个变量。
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
// 1
#define UNITY_TRANSFER_FOG(o,outpos) UNITY_CALC_FOG_FACTOR((outpos).z); o.fogCoord.x = unityFogFactor
// 2
#if defined(UNITY_REVERSED_Z)
#if UNITY_REVERSED_Z == 1
//D3d with reversed Z => z clip range is [near, 0] -> remapping to [0, far]
//max is required to protect ourselves from near plane not being correct/meaningfull in case of oblique matrices.
#define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) max(((1.0-(coord)/_ProjectionParams.y)*_ProjectionParams.z),0)
#else
//GL with reversed z => z clip range is [near, -far] -> should remap in theory but dont do it in practice to save some perf (range is close enough)
#define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) max(-(coord), 0)
#endif
#elif UNITY_UV_STARTS_AT_TOP
//D3d without reversed z => z clip range is [0, far] -> nothing to do
#define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) (coord)
#else
//Opengl => z clip range is [-near, far] -> should remap in theory but dont do it in practice to save some perf (range is close enough)
#define UNITY_Z_0_FAR_FROM_CLIPSPACE(coord) (coord)
#endif
#define UNITY_CALC_FOG_FACTOR(coord) UNITY_CALC_FOG_FACTOR_RAW(UNITY_Z_0_FAR_FROM_CLIPSPACE(coord))
// 3
#if defined(FOG_LINEAR)
// factor = (end-z)/(end-start) = z * (-1/(end-start)) + (end/(end-start))
#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = (coord) * unity_FogParams.z + unity_FogParams.w
#elif defined(FOG_EXP)
// factor = exp(-density*z)
#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.y * (coord); unityFogFactor = exp2(-unityFogFactor)
#elif defined(FOG_EXP2)
// factor = exp(-(density*z)^2)
#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = unity_FogParams.x * (coord); unityFogFactor = exp2(-unityFogFactor*unityFogFactor)
#else
#define UNITY_CALC_FOG_FACTOR_RAW(coord) float unityFogFactor = 0.0
#endif
// 4
CBUFFER_START(UnityFog)
fixed4 unity_FogColor;
// x = density / sqrt(ln(2)), useful for Exp2 mode
// y = density / ln(2), useful for Exp mode
// z = -1/(end-start), useful for Linear mode
// w = end/(end-start), useful for Linear mode
float4 unity_FogParams;
CBUFFER_END
// 5
// x = 1 or -1 (-1 if projection is flipped)
// y = near plane
// z = far plane
// w = 1/far plane
uniform vec4 _ProjectionParams;
可以看到一些有用的信息:
unity_FogParams
:用于CPU将一些除法结果计算好传入到GPU的CBuffer
中使用的,减少GPU中的除法运算,提升性能。FOG_LINEAR
、FOG_EXP
、FOG_EXP2
分别对应了Unity中的Fog 算法的选择。从三个宏定义的分支可以知道三种算法对应的公式。
从这里也可以看得出来,unity的 Fog 使用的是 ClipSpace下的z值来计算FogFactor。
- 先将
ClipSpace.z
映射到0~far
- 然后用上面
0~far
的ClipSpace.z
来作为z代入对应公式计算unityFogFactor
- 最后是下面的frag
UNITY_APPLY_FOG
应用这个unityFogFactor
来lerp(col, fragCol, untiyFogFactor)
从这里我们知道了三种公式,后面会继续说明
frag 函数中的 UNITY_APPLY_FOG#ifdef UNITY_PASS_FORWARDADD
#define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,fixed4(0,0,0,0))
#else
#define UNITY_APPLY_FOG(coord,col) UNITY_APPLY_FOG_COLOR(coord,col,unity_FogColor)
#endif
这个可以看到,对应之前的 Helper
里说的,forward add的话,会使用黑色作为雾颜色
#define UNITY_FOG_LERP_COLOR(col,fogCol,fogFac) col.rgb = lerp((fogCol).rgb, (col).rgb, saturate(fogFac))
#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)
#if (SHADER_TARGET
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?