Unity中实现类似纪念碑谷中地面的雾化效果
2018-02-02 14:43
681 查看
看一下纪念碑谷中的这个效果:
想要实现一个这种地面的雾化效果,在网上找了很久,也没找到满意的解决方案,最后只能自己做一个了。
首先想到的就是shader,思路就是从y坐标=0开始往下的位置都使用雾的颜色,中间过度部分用原颜色和雾的颜色做一个插值即可。但是因为是整个游戏的效果,肯定不能只作用在某一个模型上,所以只能使用后期特效,后期特效的话就会有一个问题,如何获取像素点的世界坐标,于是我就搜索到了这篇文章:http://blog.csdn.net/zzxiang1985/article/details/59581376
思路就是通过深度贴图中的深度,进行一次与世界坐标投影到屏幕坐标相反的操作,即可得到原世界坐标,
获取世界坐标的shader代码如下:
GetWorldPos方法中取得线性深度,然后传入GetWorldPositionFromDepthValue方法中获取原世界坐标。
然后实现我们的fragment方法:
其中_ChangeDis为渐变的距离,y/_ChangeDis即实现的过度效果,大于1的部分直接取1,然后与原颜色进行一个插值运算。
因为这里我们需要获取深度贴图,所以我们还要设置一下摄像机的模式:
最后来看看效果:
下面给出完整的shader代码
摄像机脚本:
想要实现一个这种地面的雾化效果,在网上找了很久,也没找到满意的解决方案,最后只能自己做一个了。
首先想到的就是shader,思路就是从y坐标=0开始往下的位置都使用雾的颜色,中间过度部分用原颜色和雾的颜色做一个插值即可。但是因为是整个游戏的效果,肯定不能只作用在某一个模型上,所以只能使用后期特效,后期特效的话就会有一个问题,如何获取像素点的世界坐标,于是我就搜索到了这篇文章:http://blog.csdn.net/zzxiang1985/article/details/59581376
思路就是通过深度贴图中的深度,进行一次与世界坐标投影到屏幕坐标相反的操作,即可得到原世界坐标,
获取世界坐标的shader代码如下:
float4 GetWorldPositionFromDepthValue( float2 uv, float linearDepth ) { float camPosZ = _ProjectionParams.y + (_ProjectionParams.z - _ProjectionParams.y) * linearDepth; // unity_CameraProjection._m11 = near / t,其中t是视锥体near平面的高度的一半。 // 投影矩阵的推导见:http://www.songho.ca/opengl/gl_projectionmatrix.html。 // 这里求的height和width是坐标点所在的视锥体截面(与摄像机方向垂直)的高和宽,并且 // 假设相机投影区域的宽高比和屏幕一致。 float height = 2 * camPosZ / unity_CameraProjection._m11; float width = _ScreenParams.x / _ScreenParams.y * height; float camPosX = width * uv.x - width / 2; float camPosY = height * uv.y - height / 2; float4 camPos = float4(camPosX, camPosY, camPosZ, 1.0); return mul(unity_CameraToWorld, camPos); } fixed4 GetWorldPos(float2 uv){ float rawDepth = SAMPLE_DEPTH_TEXTURE( _CameraDepthTexture, uv ); // 注意:经过投影变换之后的深度和相机空间里的z已经不是线性关系。所以要先将其转换为线性深度。 // 见:https://developer.nvidia.com/content/depth-precision-visualized float linearDepth = Linear01Depth(rawDepth); fixed4 worldpos = GetWorldPositionFromDepthValue( uv, linearDepth ); return worldpos; }
GetWorldPos方法中取得线性深度,然后传入GetWorldPositionFromDepthValue方法中获取原世界坐标。
然后实现我们的fragment方法:
float4 frag( v2f_img o ) : COLOR { //获取世界坐标 fixed4 worldpos = GetWorldPos( o.uv); fixed4 renderTex = tex2D(_MainTex, o.uv); fixed y = worldpos.y; //上下变色 fixed tmp = step(0,y); fixed lp1 = - y / _ChangeDis; fixed lp = lp1 * (1-tmp) ; fixed4 c = _Color * (1-tmp); return lerp(renderTex ,c,min(lp,1)) ; }
其中_ChangeDis为渐变的距离,y/_ChangeDis即实现的过度效果,大于1的部分直接取1,然后与原颜色进行一个插值运算。
因为这里我们需要获取深度贴图,所以我们还要设置一下摄像机的模式:
GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth;
最后来看看效果:
下面给出完整的shader代码
Shader "Custom/FotTest" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _Color ("Ground Color", Color) = (1,0,1,1) //颜色 _ChangeDis ("Change Dis", float) = 3.0 //渐变速度 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Pass{ CGPROGRAM #include "UnityCG.cginc" #pragma vertex vert_img #pragma fragment frag uniform sampler2D _MainTex; uniform fixed4 _Color; uniform float _ChangeDis; sampler2D _CameraDepthTexture; float4 GetWorldPositionFromDepthValue( float2 uv, float linearDepth ) { float camPosZ = _ProjectionParams.y + (_ProjectionParams.z - _ProjectionParams.y) * linearDepth; // unity_CameraProjection._m11 = near / t,其中t是视锥体near平面的高度的一半。 // 投影矩阵的推导见:http://www.songho.ca/opengl/gl_projectionmatrix.html。 // 这里求的height和width是坐标点所在的视锥体截面(与摄像机方向垂直)的高和宽,并且 // 假设相机投影区域的宽高比和屏幕一致。 float height = 2 * camPosZ / unity_CameraProjection._m11; float width = _ScreenParams.x / _ScreenParams.y * height; float camPosX = width * uv.x - width / 2; float camPosY = height * uv.y - height / 2; float4 camPos = float4(camPosX, camPosY, camPosZ, 1.0); return mul(unity_CameraToWorld, camPos); } fixed4 GetWorldPos(float2 uv){ float rawDepth = SAMPLE_DEPTH_TEXTURE( _CameraDepthTexture, uv ); // 注意:经过投影变换之后的深度和相机空间里的z已经不是线性关系。所以要先将其转换为线性深度。 // 见:https://developer.nvidia.com/content/depth-precision-visualized float linearDepth = Linear01Depth(rawDepth); fixed4 worldpos = GetWorldPositionFromDepthValue( uv, linearDepth ); return worldpos; } float4 frag( v2f_img o ) : COLOR { //获取世界坐标 fixed4 worldpos = GetWorldPos( o.uv); fixed4 renderTex = tex2D(_MainTex, o.uv); fixed y = worldpos.y; //上下变色 fixed tmp = step(0,y); fixed lp1 = - y / _ChangeDis; fixed lp = lp1 * (1-tmp) ; fixed4 c = _Color * (1-tmp); return lerp(renderTex ,c,min(lp,1)) ; } ENDCG } } }
摄像机脚本:
public class MainCamera : MonoBehaviour { public Material m; Camera mainCamera; public Color newColor; public float distance; // Use this for initialization void Start () { m.SetColor("_Color", newColor); m.SetFloat("_ChangeDis", distance); GetComponent<Camera>().depthTextureMode = DepthTextureMode.Depth; } // Update is called once per frame void Update () { } void OnRenderImage(RenderTexture src, RenderTexture dest) { Graphics.Blit(src, dest, m); } }
相关文章推荐
- Unity_实现类似黑洞的效果__逻辑方面
- unity中一个类似纪念碑谷里的三角图形组成的海面效果
- unity传送门类似效果实现
- unity中实现一个类似x光扫描效果
- Unity3D 实现类似“纪念碑谷”扭曲物体的效果
- 【Unity快速实现小功能】实现一个类似跑马灯的效果
- ue4类似unity多相机分屏与小地图效果实现教程
- 讲解如何在Unity的Inspector面板实现类似摄像机层次遮罩的多选效果
- ue4类似unity多相机分屏与小地图效果实现教程
- css实现跨浏览器的盒阴影效果告别图片实现类似效果
- 使用Unity实现动态2D水效果
- Python实现好玩的雨滴下落到地面的效果
- Three.js如何实现雾化效果示例代码
- 实现类似(Home+关机键)截图效果)
- 如何在 iOS 7 中设置 barTintColor 实现类似网易和 Facebook 的 navigationBar 效果
- 属性动画实现控件类似贝塞尔曲线轨迹移动效果
- android实现类似在短信图标右上角显示短信个数的效果
- AJAX与Jquery实现类似Google Suggest的提示框效果
- Android 类似未读短信,电话图标显示数字效果如何实现的
- 一个可以实现 左右联动JTable 的完全Dialog 实现类似 Excel的冻结第一列 效果