大家好,我是Shawn。如何实现“雾效”?”在Unity中,是有自带的雾效的,在Lighting窗口下,在other Settings就可以找到fog选项了,启用fog就能使用Unity自带的雾效了,但是,需要注意的是,只有在向前渲染下才能实现Unity自带的雾效,Unity有三种模式:Linear,Exp 和 Exp2 分别对应,线性、指数、和指数的平方的增长模式。

雾效因子分别对应:

E是end、S是start、c是coordinate、d就是density,通过上述式子我们不难发现这是一个关于c的减函数,即距离越远,雾效因子越小。

我们已经知道Unity是如何实现雾效的了,其实就是计算雾效因子,然后进行差值就行了

现在我们试着创建一个自己的雾效。

首先需要添加#pragma multi_compile_fog让Unity为我们生成正确的变体。然后我们还需要

设置一个宏控制雾效的开启。

#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)#define APPLY_FOG 1            #endif

在顶点着色器之前,编写一个自定义的函数:

    float4 ApplyFog(float4 color , v2f i){float viewDistance = length(_WorldSpaceCameraPos - i.worldPos);UNITY_CALC_FOG_FACTOR_RAW(viewDistance);color.rgb = lerp(unity_FogColor.rgb, color.rgb, saturate(unityFogFactor));return color ;}

这个函数计算了顶点到摄像机的距离,并且我们用Unity自带的宏为我们计算雾效因子,由于雾效因子的范围不在0-1之间所以我们用需要限定一下,这个宏可以在UnityCG.cginc里面查看到:

#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

其实就是Unity判断我们选择的模式,传入c就算雾效因子,unity_FogParams可以在UnityShaderVariables里查看,这里我们直接给出:

unity_FogParams:(density / sqrt(ln(2)), density / ln(2), –1/(end-start), end/(end-start))

在片元着色器中代码就更简单了,只需要调用即可:

fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// apply fog#if APPLY_FOGcol = col*ApplyFog(col ,i);#endifreturn col;}

返回Unity查看效果,我们没有用光照模型,只输出颜色:

其中一个球是标准材质,用来做对比

在上一个方法中我们使用的c是物体到摄像机的距离,下面我们将基于深度来实现雾效。

我们需要用一个变量来储存深度值,这里我们把顶点的世界坐标改成float4类型,用w储存深度值:

float4 worldPos : TEXCOORD2;

在顶点着色器中添加一段代码:

#if APPLY_FOGo.worldPos.w = o.vertex.z;
#endif

我们记录了clip space下的深度值,由于平台的差异我们取到的z值的范围也不同,我们使用

宏UNITY_Z_0_FAR_FROM_CLIPSPACE来针对不同平台做不同处理,把深度转换为从近切面到远切面线性增大的值。

float4 ApplyFog(float4 color , v2f i){//float viewDistance = length(_WorldSpaceCameraPos - i.worldPos.xyz);float viewDistance =UNITY_Z_0_FAR_FROM_CLIPSPACE(i.worldPos.w);UNITY_CALC_FOG_FACTOR_RAW(viewDistance);color.rgb = lerp(unity_FogColor.rgb, color.rgb, saturate(unityFogFactor));return color ;}

其他代码不变,我们得到的效果几乎和上面的一样。

但其实还是差别的,当我们基于深度来实现雾效,我们不需要开根号,运行速度更快了,但是,我们还会发现一些问题,当我们的摄像机旋转时,我们的深度变了,从而影响了雾效,尽管他在逻辑上是不会变化的

源自catlike coding

我们可以查看做一个简单的测试,查看旋转时摄像机的深度图:

我们可以发现虽然是同一个位置,长方体的颜色却有细微的不同

下面是完整代码:(同时包含了基于距离和深度的方法)

Shader "Unlit/TestFog"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{Tags { "RenderType"="Opaque" }LOD 100Pass{//Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag// make fog work#pragma multi_compile_fog#include "UnityCG.cginc"#define FOG_DISTANCE#if defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2)#if !defined(FOG_DISTANCE)#define FOG_DEPTH 1#endif#define APPLY_FOG 1  #endifstruct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float4 worldPos : TEXCOORD2;};sampler2D _MainTex;float4 _MainTex_ST;float4 ApplyFog(float4 color , v2f i){float viewDistance = length(_WorldSpaceCameraPos - i.worldPos.xyz);#if FOG_DEPTHviewDistance =UNITY_Z_0_FAR_FROM_CLIPSPACE(i.worldPos.w);#endifUNITY_CALC_FOG_FACTOR_RAW(viewDistance);color.rgb = lerp(unity_FogColor.rgb, color.rgb, saturate(unityFogFactor));return color ;}v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = TRANSFORM_TEX(v.uv, _MainTex);o.worldPos.xyz = mul(unity_ObjectToWorld , v.vertex).xyz;#if APPLY_FOGo.worldPos.w = o.vertex.z;#endifreturn o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col = tex2D(_MainTex, i.uv);// apply fog#if APPLY_FOGcol = col*ApplyFog(col ,i);#endifreturn col;}ENDCG}}
}

前文提到:这种方法只能用在向前渲染中,在延迟渲染中我们就要使用屏幕后处理。

我们先写一个最基本的后处理脚本:

[ExecuteInEditMode]
public class PostScreenEffect : MonoBehaviour
{public Shader shader;Material material;[ImageEffectOpaque]private void OnRenderImage(RenderTexture source, RenderTexture destination){if (material == null){material= new Material(shader);}Graphics.Blit(source, destination, material);}

然后创建一个Imageeffect的shader文件,思路也很简单,就是通过采样深度纹理来获取深度值,从而计算距离。我们要声明_CameraDepthTexture来采样深度纹理,然后再采样他,核心代码如下:

 fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv);float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);depth = Linear01Depth(depth);if(depth<1){float distance = depth * _ProjectionParams.z-_ProjectionParams.y;UNITY_CALC_FOG_FACTOR_RAW(distance);unityFogFactor = saturate(unityFogFactor);col.rgb = lerp(unity_FogColor.rgb,col.rgb,unityFogFactor);}return col;}

我们用Linear01Depth函数是为了能够得到一个0-1范围的线性的深度值,你可能已经不止一次听到类似“MVP矩阵的P导致了非线性”,所以我再唠叨一遍,哈哈。然后转换为距离,同样你也可以在UnityShaderVariables里查看_ProjectionParams. 这里z就代表远裁面,y代表近裁面。

我们把深度值限定在小于1的范围内是因为我们不想渲染天空盒。

返回Unity查看效果:

最后,我们再用真正的距离实现屏幕后处理实现雾效:

这里的距离我们可以从摄像机发射一道光线。如果没有没有物体的话,这道光线可以一直延伸最后达到远裁面,如果有物体,我们我们只要记录下它的深度值与远裁面的比,然后乘这个光线向量得到一个新的向量,就是一个简单的相似三角形的知识~最后计算这个向量的模,我们就得到了距离了。

理论上来说要为每个像素发射一次射线,但是,我们这里我们只计算远裁面的四个角的射线,剩下的就交给差值去做吧。

幸运的是,我们不用自己手动计算这四个射线,Unity有给定函数方便了我们的计算。

点击这里了解这个函数。

我们在camera的脚本中添加一些代码:

Camera cam = null;Vector3[] frustumCorners;
Vector4[] vectorArray;
/****下面代码添加至OnRenderImage函数中****/
if (material == null){material= new Material(shader);cam = GetComponent<Camera>();frustumCorners = new Vector3[4];}cam.CalculateFrustumCorners(new Rect(0, 0, 1, 1), cam.farClipPlane, cam.stereoActiveEye, frustumCorners);vectorArray[0] = frustumCorners[0];vectorArray[1] = frustumCorners[3];vectorArray[2] = frustumCorners[1];vectorArray[3] = frustumCorners[2];material.SetVectorArray("_FrustumCorners", vectorArray);

在calculateFrustumCorners函数里我们传入的是Vector3,而我们SetVectorArray需要传入Vector4 ,所以我们变化一下,这样我们的数值就能正确被传入了 ;

在我们的shader文件下我们声明一个宏,来控制是否启用distance的算法。

#define FOG_DISTANCE 

接着要在顶点结构体声明ray向量:

#ifdef FOG_DISTANCE float3 ray : TEXCOORD1;
#endif

最后我们修改片元着色器中的核心语句:

if(depth<1){float distance = depth * _ProjectionParams.z-_ProjectionParams.y;#ifdef FOG_DISTANCEdistance = length(depth*i.ray);#endifUNITY_CALC_FOG_FACTOR_RAW(distance);unityFogFactor = saturate(unityFogFactor);col.rgb = lerp(unity_FogColor.rgb,col.rgb,unityFogFactor);}

效果:

以上就是雾效的所有内容了

最后的最后(真的是最后了)我们来实现一个类似firewatch的效果:

代码如下:

Shader "Custom/GradientColor"
{Properties{_MainTex ("Texture", 2D) = "white" {}_FogAmount("Fog amount", float) = 1_ColorRamp("Color ramp", 2D) = "white" {}_FogIntensity("Fog intensity", float) = 1}SubShader{// No culling or depthCull Off ZWrite Off ZTest AlwaysPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#pragma multi_compile_fog#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;float4 scrPos : TEXCOORD1;};v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;o.scrPos = ComputeScreenPos(o.vertex);return o;}sampler2D _MainTex;sampler2D _CameraDepthTexture;sampler2D _ColorRamp;float _FogAmount;float _FogIntensity;fixed4 frag (v2f i) : SV_Target{fixed4 orCol = tex2D(_MainTex, i.uv);float depthValue = Linear01Depth (tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(i.scrPos)));//float depthValue = Linear01Depth (SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv));float distance = depthValue*_ProjectionParams.z-_ProjectionParams.y;UNITY_CALC_FOG_FACTOR_RAW(distance);unityFogFactor = saturate(unityFogFactor);float depthValueMul = depthValue * _FogAmount;fixed4 fogCol = tex2D(_ColorRamp, (float2(depthValueMul, 0)));if(depthValue<1){#if !defined(FOG_LINEAR) && !defined(FOG_EXP) && !defined(FOG_EXP2)orCol =  lerp(orCol, fogCol, fogCol.a * _FogIntensity) ;#elseunityFogFactor =1-unityFogFactor;orCol = lerp(orCol,fogCol,unityFogFactor*_FogIntensity);#endif}return orCol;}ENDCG}}
}

我们通过深度对一张梯度图采样,这样不同的深度就能显示不同的颜色,如果你觉的这样很不自然的话,当然也可以使用雾效因子来对他进行采样。

如果你的渲染路径是向前渲染的话,你就需要在脚本中加入Camera.main.depthTextureMode = DepthTextureMode.Depth;

这样你就可以访问深度图了

我们找一张梯度图看看效果:

也可以这样:

当然如果你找的颜色是彩虹色的话:

Enjoy!

unity 继承会调用start吗_【浅入浅出】Unity 雾效相关推荐

  1. 处理中文乱码_浅入深出:一次提问引发的深思,从此再也不怕“乱码”问题

    这是恋习Python之浅入深出系列第3篇原创首发文章 作者|丁彦军 来源|恋习Python(ID:sldata2017) 转载请联系授权(微信ID:2394608316) 近日,有位粉丝向我请教,在爬 ...

  2. [科普]浅入浅出Liunx Shellcode

    创建时间:2008-05-13 文章属性:原创 文章提交: pr0cess  (pr0cess_at_cnbct.org) 浅入浅出Liunx Shellcode /*---------------- ...

  3. Java 注解 (Annotation)浅入深出

    Java 注解 (Annotation)浅入深出 本文主要参考与借鉴frank909 文章,但更为简单,详细. Annotation 中文译过来就是注解.标释的意思.Annotation是一种应用于类 ...

  4. 浅入深出之Java集合框架(上)

    Java中的集合框架(上) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到浅入深出之Java集合框架(下). ...

  5. 浅入深出之Java集合框架(中)

    Java中的集合框架(中) 由于Java中的集合框架的内容比较多,在这里分为三个部分介绍Java的集合框架,内容是从浅到深,如果已经有java基础的小伙伴可以直接跳到浅入深出之Java集合框架(下). ...

  6. SegmentFault 技术周刊 Vol.16 - 浅入浅出 JavaScript 函数式编程

    函数式编程(Functional Programming),一看这个词,简直就是学院派的典范. 以至于从 Lisp 的创世,到 Scheme.Haskell.Clean.Erlang.Miranda. ...

  7. Spring MVC浅入浅出——不吹牛逼不装逼

    Spring MVC浅入浅出--不吹牛逼不装逼 前言 上文书说了Spring相关的知识,对Spring来了个浅入浅出,大家应该了解到,Spring在三层架构中主做Service层,那还有Web层,也就 ...

  8. Spring的数据库编程浅入浅出——不吹牛逼不装逼

    Spring的数据库编程浅入浅出--不吹牛逼不装逼 前言 上文书我写了Spring的核心部分控制反转和依赖注入,后来又衔接了注解,在这后面本来是应该写Spring AOP的,但我觉得对于初学者来说,这 ...

  9. Spring浅入浅出——不吹牛逼不装逼

    Spring浅入浅出--不吹牛逼不装逼 前言: 今天决定要开始总结框架了,虽然以前总结过两篇,但是思维是变化的,而且也没有什么规定说总结过的东西就不能再总结了,是吧.这次总结我命名为浅入浅出,主要在于 ...

最新文章

  1. MBG 相关资源链接
  2. SSH框架整合-慕课课程
  3. php大商创 安装,大商创X2020最新纯净服务器安装教程
  4. 数据结构有哪些?数据结构的特点?算法与数据结构
  5. 数组的最长平台c语言,2010台湾省C语言版高级
  6. GridView调用setAdapter()函数时发生错误
  7. memcache 缓存命中率   状态查询
  8. [置顶] Embedded Server:像写main函数一样写Web Server
  9. 小黑相关预搞书籍杂志等
  10. excel有的单元文字可以超出单元格_在excel单元格超过字数限制的设置方法 excel单元格字数限制...
  11. webstorm,idea 右键菜单管理
  12. flyme android 7 root,魅族PRO7怎么Root?魅族PRO7一键ROOT权限获取图文教程(系统自带Root功能)...
  13. 化学分子溶解度预测模型(python-Rdkit构建)
  14. macOS High Sierra 10.13.6 英伟达显卡Nvidia显卡 失效处理方案
  15. 工具分享:图片水印工具(WaterMarker)
  16. 阅读 redis 源码,学习缓存淘汰算法 W-TinyLFU
  17. nvidia nano平台tca9546接入两路imx219调试记录
  18. 期货价格的技术分析及原理
  19. Java中容器大全(Java基础篇)
  20. 基于STM32F030的ADC功能实现

热门文章

  1. 异步加载在Vue生命周期哪个阶段更合理
  2. PHP:第三章——PHP中控制函数的函数
  3. 图形—9patch,shape ,selector
  4. 数据范围BZOJ 3209(花神的数论题-数位统计+1,被数据范围坑了)
  5. GridView 梆定一个实体类
  6. easyui-window 关闭事件,只要关闭窗口就会触发
  7. NHibernate介绍
  8. 在IDEA中`New`没有`Mapper`文件选项(亲测)
  9. 深入分析Nginx 502 Bad Gateway和Nginx 504 Gateway(亲测)
  10. Python日志模块的两种用法