摄影常用的一招就是背景虚化,背景虚化的相片可以突出拍摄主题,强调自己要展现的事物。
摄影技巧为:1,开大光圈;2,拉长焦距;3,主体离镜头近;4,背景离主体远
有了背景虚化,照片会变的有艺术感见下图(2014年 拍摄于上海共青深林公园)
游戏方面可喜的是Unity3d也可以实现类似效果,先看效果图(gif图损失比较严重)
左面为无景深,距离远近清晰度不变
右图为有景深,原理主角远的物体会模糊
实现原理为:
一张清晰原图(OnRenderImage 第一个参数已经为我们提供好了)
一张高斯模糊的图(生成方式参考高斯模糊效果)
一张深度图(好巧,unity3d给我们内置了一张_CameraDepthTexture不过要开启相机深度)
camera.depthTextureMode = DepthTextureMode.Depth;
相机深度模式开启后,对场景内的shader也有一定要求
1,shader要有一个shadow casting pass,简单处理方式为在你shader最后一行加上:Fallback "Diffuse";
2,只有不透明物体render queue <=2500 的物体才会被渲染到深度图;
也可参考官方文档:https://docs.unity3d.com/Manual/SL-CameraDepthTexture.html
有了以上素材后最终景深图为:清晰图和模糊图,根据深度图和权值做插值,离目标焦距越近的越清晰,离目标焦距越远的越模糊。
很简单,以上方式就实现了。
下面上代码:
using UnityEngine;
namespace GameBase.Effect
{
[RequireComponent(typeof (Camera))]
[AddComponentMenu("")]
public class ImageEffectBase : MonoBehaviour
{
/// Provides a shader property that is set in the inspector
/// and a material instantiated from the shader
public Shader shader;
private Material m_Material;
protected virtual void Start()
{
// Disable if we don't support image effects
if (!SystemInfo.supportsImageEffects)
{
enabled = false;
return;
}
// Disable the image effect if the shader can't
// run on the users graphics card
if (!shader || !shader.isSupported)
enabled = false;
}
protected Material material
{
get
{
if (m_Material == null)
{
m_Material = new Material(shader);
m_Material.hideFlags = HideFlags.HideAndDontSave;
}
return m_Material;
}
}
protected virtual void OnDisable()
{
if (m_Material)
{
DestroyImmediate(m_Material);
}
}
}
}
using UnityEngine;
namespace GameBase.Effect
{
public class DepthOfFieldEffect : ImageEffectBase
{
[Range(0, 10)]
[SerializeField]
private int _downSample = 1;//分辨率降低值
private int ID_BlurSize;
private int ID_FocusDistance;
private int ID_NearBlurSize;
private int ID_FarBlurSize;
private int ID_BlurTex;
[SerializeField]
[Range(0, 8)]
private float _blurSize = 1;//取周围多远的像素
//[SerializeField]
private const float _focusDistanceMin = 0f;
//[SerializeField]
private const float _focusDistanceMax = 100f;
[SerializeField]
[Range(_focusDistanceMin, _focusDistanceMax)]
private float _focusDistance = 20f;
[SerializeField]
[Range(1, 100)]
private float _nearBlurSize = 30;
[SerializeField]
[Range(1, 100)]
private float _farBlurSize = 20 ;
private Camera _camera;
private void OnEnable()
{
_camera = GetComponent<Camera>();
_camera.depthTextureMode |= DepthTextureMode.Depth;
ID_BlurSize = Shader.PropertyToID("_BlurSize");
ID_FocusDistance = Shader.PropertyToID("_FocusDistance");
ID_NearBlurSize = Shader.PropertyToID("_NearBlurSize");
ID_FarBlurSize = Shader.PropertyToID("_FarBlurSize");
ID_BlurTex = Shader.PropertyToID("_BlurTex");
if (material.HasProperty(ID_BlurSize))
{
material.SetFloat(ID_BlurSize, _blurSize);
}
if (material.HasProperty(ID_FocusDistance))
{
material.SetFloat(ID_FocusDistance, (_focusDistance - _focusDistanceMin) / (_camera.farClipPlane - _focusDistanceMax));
}
if (material.HasProperty(ID_NearBlurSize))
{
material.SetFloat(ID_NearBlurSize, _nearBlurSize);
}
if (material.HasProperty(ID_FarBlurSize))
{
material.SetFloat(ID_FarBlurSize, _farBlurSize);
}
}
protected override void OnDisable()
{
_camera.depthTextureMode &= ~DepthTextureMode.Depth;
base.OnDisable();
}
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if (_focusDistanceMin > _focusDistanceMax || _focusDistance < _focusDistanceMin || _focusDistance > _focusDistanceMax)
return;
if (_blurSize <= 0 )
{
Graphics.Blit(source, destination);
return;
}
int width = source.width >> _downSample;//降低分辨率,提高效率
int height = source.height >> _downSample;
if (width <= 0)
width = 1;
if (height <= 0)
height = 1;
RenderTexture bufferRT1 = RenderTexture.GetTemporary(width, height, 0, source.format);
RenderTexture bufferRT2 = RenderTexture.GetTemporary(width, height, 0, source.format);
Graphics.Blit(source, bufferRT1, material, 0);//得到的bufferRT1为水平高斯模糊
Graphics.Blit(bufferRT1, bufferRT2, material, 1);//二次滤波得到的bufferRT2为模糊图(水平+垂直高斯模糊)
material.SetTexture(ID_BlurTex, bufferRT2);
Graphics.Blit(source, destination, material, 2);//插值得到最终效果
RenderTexture.ReleaseTemporary(bufferRT1);
RenderTexture.ReleaseTemporary(bufferRT2);
}
}
}
Shader "mgo/depth_of_field"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}
_BlurSize("_BlurSize", range(0, 300)) = 1
_FocusDistance("_FocusDistance", range(0, 10000)) = 1
_NearBlurSize("_NearBlurSize", range(0, 300)) = 1
_FarBlurSize("_FarBlurSize", range(0, 300)) = 1
}
SubShader
{
Tags{ "RenderType" = "Opaque" }
ZTest Off
cull Off
ZWrite Off
UsePass "mgo/blur/Horizontal"//参见《高斯模糊效果》一文中的Pass
UsePass "mgo/blur/Vertical"//参见《高斯模糊效果》一文中的Pass
Pass
{
ZTest Off
Cull Off
ZWrite Off
ColorMask RGBA
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_TexelSize;
sampler2D _CameraDepthTexture;
sampler2D _BlurTex;
float _FocusDistance;
float _NearBlurSize;
float _FarBlurSize;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed4 mainColor = tex2D(_MainTex, i.uv);
fixed4 blurColor = tex2D(_BlurTex, i.uv);
float depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv);
depth = Linear01Depth(depth);
float distance = (depth - _FocusDistance);
if (distance < 0)
distance *= _NearBlurSize;
else
distance *= _FarBlurSize;
distance = clamp(abs(distance), 0 , 1);
//mainColor.r = distance;//用于测试模糊效果
//blurColor.r = distance;
return lerp(mainColor, blurColor, distance);
}
ENDCG
}
}
}