试了下unity自带的雾效,感觉最大的问题就是没有包括天空盒,远处处于天空背景的物体显得不真实,如下图。
因此决定还是自己动手写一个脚本,最终效果如下图:(雾的垂直厚度和水平浓度可调)
思路:很简单,用屏幕后效果实现距离越远雾越浓,但同时从地表向上应该越来越稀薄,不然天空全是雾了,不真实。这主要是shader的作用,脚本很简单,关键代码如下,最后面有3个脚本下载地址。
PostEffectsBase.cs(不需要挂在场景物体上)
using UnityEngine;
using System.Collections;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
//在编辑状态下也可以执行该脚本
public class PostEffectsBase : MonoBehaviour
{
protected void CheckResources()
{
bool isSupported = CheckSupport();
if (isSupported == false)
{
NotSupported();
}
}
// 调用CheckResources检查平台上的支持
protected bool CheckSupport()
{
if (SystemInfo.supportsImageEffects == false )
{
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}
return true;
}
// Called when the platform doesn't support this effect
protected void NotSupported()
{
enabled = false;
}
protected void Start()
{
CheckResources();
}
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
{
if (shader == null)
{
return null;
}
if (shader.isSupported && material && material.shader == shader)
return material;
if (!shader.isSupported)
{
return null;
}
else
{
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
//CheckShaderAndCreateMaterial函数接受两个参数,第一个参数指定了该特效需要使用Shader,
//第二个参数则是用于后期处理的材质。该函数首先检查Shader的可用性,检查通过后就返回一个
//使用了该Shader的材质,否则返回null。
}
}
Fog_global.cs(继承上面的脚本,需要挂在主摄像机上)
using UnityEngine;
using System.Collections;
public class Fog_global : PostEffectsBase
{
public Shader fogShader;
private Material fogMaterial = null;
public Material material
{
get
{
fogMaterial = CheckShaderAndCreateMaterial(fogShader, fogMaterial);
return fogMaterial;
}
}
private Camera myCamera;
public new Camera camera
{
get
{
if (myCamera == null)
{
myCamera = GetComponent<Camera>();
}
return myCamera;
}
}
private Transform myCameraTransform;
public Transform cameraTransform
{
get
{
if (myCameraTransform == null)
{
myCameraTransform = camera.transform;
}
return myCameraTransform;
}
}
[Range(0.1f, 40.0f)]
public float fogDensity = 25.0f;
public Color fogColor = Color.white;
public float fogStart = 5f;
public float fogEnd = 2000f;
public float H_floor = -600f;
public float H_top = 200f;
void OnEnable()
{
GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;
}
//首先计算近裁剪平面的4个角对应的向量,并把他们存储在一个矩阵类型的变量)
//(frustumCorners)中。 随后把结果和其他参数传递给材质,并调用Graphics.Blit(src,dest,material)
//把渲染结果显示在屏幕上。
void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (material != null)
{
Matrix4x4 frustumCorners = Matrix4x4.identity;
float fov = camera.fieldOfView;
float near = camera.nearClipPlane;
float aspect = camera.aspect;
float halfHeight = near * Mathf.Tan(fov * 0.5f * Mathf.Deg2Rad);
Vector3 toRight = cameraTransform.right * halfHeight * aspect;
Vector3 toTop = cameraTransform.up * halfHeight;
Vector3 topLeft = cameraTransform.forward * near + toTop - toRight;
float scale = topLeft.magnitude / near;
topLeft.Normalize();
topLeft *= scale;
Vector3 topRight = cameraTransform.forward * near + toRight + toTop;
topRight.Normalize();
topRight *= scale;
Vector3 bottomLeft = cameraTransform.forward * near - toTop - toRight;
bottomLeft.Normalize();
bottomLeft *= scale;
Vector3 bottomRight = cameraTransform.forward * near + toRight - toTop;
bottomRight.Normalize();
bottomRight *= scale;
frustumCorners.SetRow(0, bottomLeft);
frustumCorners.SetRow(1, bottomRight);
frustumCorners.SetRow(2, topRight);
frustumCorners.SetRow(3, topLeft);
material.SetMatrix("_FrustumCornersRay", frustumCorners);
material.SetFloat("_FogDensity", fogDensity);
material.SetColor("_FogColor", fogColor);
material.SetFloat("_FogStart", fogStart);
material.SetFloat("_FogEnd", fogEnd);
material.SetFloat("_H_floor", H_floor);
material.SetFloat("_H_top", H_top);
material.SetVector("_CamPos", cameraTransform.position);
Graphics.Blit(src, dest, material);
}
else
{
Graphics.Blit(src, dest);
}
}
}
Fog_global.shader(拖入Fog_global.cs槽中)
Shader "custom/Fog" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
_FogDensity ("Fog Density", Float) = 1.0
_FogColor ("Fog Color", Color) = (1, 1, 1, 1)
_FogStart ("Fog Start", Float) = 0.0
_FogEnd ("Fog End", Float) = 1.0
}
SubShader {
CGINCLUDE
#include "UnityCG.cginc"
float4x4 _FrustumCornersRay;
sampler2D _MainTex;
half4 _MainTex_TexelSize;
//深度纹理
sampler2D _CameraDepthTexture;
half _FogDensity;
fixed4 _FogColor;
float _FogStart;
float _FogEnd;
float4 _CamPos;
float _H_floor;
float _H_top;
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float2 uv_depth : TEXCOORD1;
float4 interpolatedRay : TEXCOORD2;
};
v2f vert(appdata_img v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
o.uv_depth = v.texcoord;
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
o.uv_depth.y = 1 - o.uv_depth.y;
#endif
int index = 0;
if (v.texcoord.x < 0.5 && v.texcoord.y < 0.5) {
index = 0;
} else if (v.texcoord.x > 0.5 && v.texcoord.y < 0.5) {
index = 1;
} else if (v.texcoord.x > 0.5 && v.texcoord.y > 0.5) {
index = 2;
} else {
index = 3;
}
#if UNITY_UV_STARTS_AT_TOP
if (_MainTex_TexelSize.y < 0)
index = 3 - index;
#endif
o.interpolatedRay = _FrustumCornersRay[index];
return o;
}
fixed4 frag(v2f i) : SV_Target {
float linearDepth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv_depth));
float3 worldPos = _WorldSpaceCameraPos + linearDepth * i.interpolatedRay.xyz;
float _distance = distance(worldPos,_CamPos);
float fogDensity = (_distance-_FogStart)*0.3 / (_FogEnd - _FogStart); //越远越浓
fogDensity *=(_H_top - worldPos.y) / (_H_top - _H_floor);//离地面越高越稀薄
fogDensity = saturate(fogDensity * _FogDensity) ;
fixed4 finalColor= tex2D(_MainTex, i.uv);
finalColor.rgb = lerp(finalColor.rgb, _FogColor.rgb, fogDensity*0.3);
return finalColor ;
}
ENDCG
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
}
FallBack Off
}