【Unity3d Shader】景深效果

摄影常用的一招就是背景虚化,背景虚化的相片可以突出拍摄主题,强调自己要展现的事物。

摄影技巧为: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
		}
		
	}
}

 

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值