栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Unity3D

Unity 做一个自己的后处理堆栈

Unity3D 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity 做一个自己的后处理堆栈

using NaughtyAttributes;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectsManager : MonoBehaviour
{
    private Camera cam;
    public Camera Camera
    {
        get
        {
            if (cam == null)
            {
                cam = GetComponent();
            }
            return cam;
        }
    }
    public Vector3 CamPos { get { return Camera.transform.position; } }

    public Shader lerpShader;
    private Material weightMaterial;
    public Material WeightMaterial
    {
        get
        {
            if (weightMaterial == null && lerpShader != null)
                weightMaterial = new Material(lerpShader);
            return weightMaterial;
        }
    }
    private Material blendMaterial;
    public Material BlendMaterial
    {
        get
        {
            if (blendMaterial == null && lerpShader != null)
                blendMaterial = new Material(lerpShader);
            return blendMaterial;
        }
    }

    private Dictionary> sortedVolumes = new Dictionary>();

    private List sortPriority = new List();

    private PostEffectsVolume lastVolume = null;

    private PostEffectsVolume currentVolume = null;

    private void LateUpdate()
    {
        //获取场景中的后处理体并进行排序
        PostEffectsVolume[] volumes = FindObjectsOfType();
        sortedVolumes.Clear();
        sortPriority.Clear();
        for (int i = 0; i < volumes.Length; i++)
        {
            if (sortedVolumes.ContainsKey(volumes[i].priority))
            {
                sortedVolumes[volumes[i].priority].Add(volumes[i]);
            }
            else
            {
                sortPriority.Add(volumes[i].priority);
                sortedVolumes.Add(volumes[i].priority, new List() { volumes[i] });
            }
        }
        sortPriority.Sort((a, b) => b.CompareTo(a));

        //获取当前所在后处理体
        PostEffectsVolume volume = currentVolume;
        for (int i = 0; i < sortPriority.Count; i++)
        {
            List currentPriorityVolumes = sortedVolumes[sortPriority[i]];
            for (int j = 0; j < currentPriorityVolumes.Count; j++)
            {
                if (currentPriorityVolumes[j].IsInVolume(CamPos) && currentPriorityVolumes[j].enabled)
                {
                    currentVolume = currentPriorityVolumes[j];
                    break;//我们只处理一个
                }
                else
                {
                    currentVolume = null;
                }
            }

            if (currentVolume != null)
            {
                break;
            }
        }

        if(volume != currentVolume) 
        {
            lastVolume = volume;
        }

        if (currentVolume == null) 
        {
            lastVolume = null;
        }
    }

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        //后处理
        if (currentVolume != null)
        {
            //Set Camera
            if (lastVolume != null) 
                Camera.depthTextureMode = (DepthTextureMode)Mathf.Max(
                    lastVolume.depthTextureMode, currentVolume.depthTextureMode);
            //PostEffectRender
            RenderTexture current = PostEffectRender(source, currentVolume);

            if (currentVolume.IsInBlendField(CamPos))
            {
                //Blend Distance
                BlendMaterial.SetTexture("_PostEffectTex", current);
                BlendMaterial.SetFloat("_LerpValue", currentVolume.GetCurrentBlendDistance(CamPos));

                if (lastVolume != null && lastVolume != currentVolume)
                {
                    RenderTexture last = PostEffectRender(source, lastVolume);
                    Graphics.Blit(last, destination, BlendMaterial);
                    RenderTexture.ReleaseTemporary(last);
                }
                else 
                {
                    Graphics.Blit(source, destination, BlendMaterial);
                }
            }
            else 
            {
                Graphics.Blit(current, destination);
            }
            
            RenderTexture.ReleaseTemporary(current);
        }
        else
        {
            Camera.depthTextureMode = DepthTextureMode.None;
            Graphics.Blit(source, destination);
        }

    }

    private RenderTexture PostEffectRender(RenderTexture source, PostEffectsVolume volume) 
    {
        //PostEffectRender
        PostEffectSettings[] postEffects = volume.profile.postEffects;
        RenderTexture rt = RenderTexture.GetTemporary(source.width, source.height);
        Graphics.Blit(source, rt);
        for (int i = 0; i < postEffects.Length; i++)
        {
            if (postEffects[i].Enable)
            {
                RenderTexture rt2 = RenderTexture.GetTemporary(rt.width, rt.height);
                postEffects[i].Renderer(rt, rt2);
                RenderTexture.ReleaseTemporary(rt);
                rt = rt2;
            }
        }
        //Weight
        WeightMaterial.SetTexture("_PostEffectTex", rt);
        WeightMaterial.SetFloat("_LerpValue", currentVolume.weight);
        RenderTexture result = RenderTexture.GetTemporary(source.width, source.height);
        Graphics.Blit(source, result, WeightMaterial);

        RenderTexture.ReleaseTemporary(rt);
        return result;
    }
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NaughtyAttributes;

public enum PostEffectsVolumeMode
{
    Global,
    Box,
    Sphere,
    ConvexMesh,
}

public class PostEffectsVolume : MonoBehaviour
{
    ///  效果范围 
    public PostEffectsVolumeMode mode = PostEffectsVolumeMode.Global;

    ///  后处理范围碰撞体 
    private Collider volumeCollider;

    ///  优先级:当相机位于多个后处理体中时优先选择最大的 
    public int priority = 0;

    ///  权重 
    [Range(0, 1)]
    public float weight = 1;

    ///  过渡距离 
    [Min(0)]
    [ShowIf("UseBlend")]
    public float blendDistance = 1;
    private bool UseBlend() { return mode != PostEffectsVolumeMode.Global; }

    ///  纹理信息模式 
    [Dropdown("depthTextureModes")]
    public int depthTextureMode = 0;
    private List depthTextureModes
    {
        get
        {
            return new List() { 0, 1, 2, 4 };
        }
    }

    ///  后处理列表 
    //[Expandable]
    public PostEffectsProfile profile;

    ///  是否在后处理体影响范围内 
    public bool IsInVolume(Vector3 worldPos) 
    {
        if (volumeCollider == null) 
            volumeCollider = GetComponent();

        if (mode == PostEffectsVolumeMode.Global)
        {
            return profile != null && profile.CanUse;
        }
        else if (volumeCollider != null)
        {
            return profile != null && profile.CanUse && volumeCollider.bounds.Contains(worldPos);
        }
        else 
        {
            return false;
        }
    }

    ///  是否在过渡区域 
    public bool IsInBlendField(Vector3 CamPos) 
    {
        switch (mode)
        {
            case PostEffectsVolumeMode.Global:
                return false;
            case PostEffectsVolumeMode.Box:
                BoxCollider box = GetComponent();
                Bounds blendOut = box.bounds;
                Bounds blendIn = new Bounds(box.bounds.center,
                    box.bounds.size - blendDistance * box.bounds.size.normalized);
                return blendOut.Contains(CamPos) && !blendIn.Contains(CamPos);
            case PostEffectsVolumeMode.Sphere:
                SphereCollider sphere = GetComponent();
                float dis = Vector3.Distance(sphere.bounds.center, CamPos);
                return dis < sphere.radius && dis > sphere.radius - blendDistance;
            case PostEffectsVolumeMode.ConvexMesh:
                //TODO 射线检测 Collider.Raycast
                return false;
        }

        return false;
    }

    ///  过渡值 
    public float GetCurrentBlendDistance(Vector3 CamPos) 
    {
        if (IsInBlendField(CamPos))
        {
            if (volumeCollider == null)
                volumeCollider = GetComponent();

            float dis = Vector3.Distance(volumeCollider.bounds.center, CamPos);
            Vector3 dir = (volumeCollider.bounds.center - CamPos).normalized;
            Debug.DrawRay(volumeCollider.bounds.center, dir, Color.red);
            float blendStep = 1;
            switch (mode)
            {
                case PostEffectsVolumeMode.Box:
                    BoxCollider box = GetComponent();
                    Ray ray = new Ray(box.bounds.center, dir);
                    Bounds blendOut = box.bounds;
                    Bounds blendIn = new Bounds(box.bounds.center,
                        box.bounds.size - blendDistance * box.bounds.size.normalized);
                    float outDis, inDis;
                    blendOut.IntersectRay(ray, out outDis);
                    blendIn.IntersectRay(ray, out inDis);
                    float blendDis = Mathf.Abs(outDis) - Mathf.Abs(inDis);
                    blendStep = (blendDis - (dis - Mathf.Abs(inDis))) / blendDis;
                    break;
                case PostEffectsVolumeMode.Sphere:
                    SphereCollider sphere = GetComponent();
                    blendStep = (blendDistance - (dis - (sphere.radius - blendDistance))) / blendDistance;
                    break;
                case PostEffectsVolumeMode.ConvexMesh:
                    //TODO 射线检测 Collider.Raycast
                    blendStep = 1;
                    break;
            }
            return Mathf.Clamp01(blendStep);
        }
        else 
        {
            return 1;
        }
    }

    private void OnDrawGizmos()
    {
        if (mode == PostEffectsVolumeMode.Box)
        {
            BoxCollider box = GetComponent();
            Gizmos.color = new Color(0, 1, 0, 0.2f);
            Gizmos.DrawCube(box.bounds.center, box.bounds.size);
            Gizmos.DrawWireCube(box.bounds.center, box.size);
            Gizmos.color = new Color(0, 1, 0, 0.3f);
            Gizmos.DrawCube(box.bounds.center, box.bounds.size - blendDistance * box.bounds.size.normalized);
            Gizmos.DrawWireCube(box.bounds.center, box.bounds.size - blendDistance * box.bounds.size.normalized);
        } 
        else if (mode == PostEffectsVolumeMode.Sphere) 
        {
            SphereCollider sphere = GetComponent();
            Gizmos.color = new Color(0, 1, 0, 0.2f);
            Gizmos.DrawSphere(sphere.bounds.center, sphere.radius);
            Gizmos.DrawWireSphere(sphere.center, sphere.radius);
            Gizmos.color = new Color(0, 1, 0, 0.3f);
            Gizmos.DrawSphere(sphere.bounds.center, sphere.radius - blendDistance);
            Gizmos.DrawWireSphere(sphere.bounds.center, sphere.radius - blendDistance);
        }
        else if (mode == PostEffectsVolumeMode.ConvexMesh)
        {
            Mesh mesh = GetComponent().sharedMesh;
            
            if (mesh != null) 
            {
                Gizmos.color = new Color(0, 1, 0, 0.2f);
                Gizmos.DrawMesh(mesh, transform.position, transform.rotation, transform.localScale);
                Gizmos.color = new Color(0, 1, 0, 0.3f);
                Vector3 size = mesh.bounds.size;
                Gizmos.DrawMesh(mesh, transform.position, transform.rotation, transform.localScale - blendDistance * size.normalized);
            }
        }
    }
}

using NaughtyAttributes;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "PostEffectsProfile", menuName = "PostEffects/PostEffectsProfile")]
public class PostEffectsProfile : ScriptableObject
{
    public bool CanUse { get { return postEffects != null && postEffects.Length > 0; } }

    [Expandable]
    public PostEffectSettings[] postEffects;
}

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PostEffectSettings : ScriptableObject
{
	public bool Enable { get { return Material != null && enable; } set { enable = value; } }

	public bool enable;
	public Shader shader;

	private Material material;
	public Material Material
	{
		get
		{
			if (material == null && shader != null)
				material = new Material(shader);
			return material;
		}
	}

	///  后处理渲染 
	public virtual void Renderer(RenderTexture source, RenderTexture destination)
	{
		if (Material != null)
		{
			SetMaterialData(Material);
			Graphics.Blit(source, destination, Material);
		}
	}

	///  配置材质 
	protected virtual void SetMaterialData(Material material)
	{

	}

}

Shader "LSQ/PostProcessing/Lerp"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            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;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            sampler2D _PostEffectTex;

            fixed _LerpValue;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                fixed4 postCol = tex2D(_PostEffectTex, i.uv);
                return lerp(col, postCol, _LerpValue);
            }
            ENDCG
        }
    }
}

案例:

Shader "LSQ/PostEffect/RadialBlur" 
{
	Properties 
	{
		_MainTex ("Base (RGB)", 2D) = "" {}
	}
	
	CGINCLUDE
		
	#include "UnityCG.cginc"
	
	struct appdata
    {
        float4 vertex : POSITION;
        float2 uv : TEXCOORD0;
    };

	struct v2f {
		float4 pos : SV_POSITION;
		float2 uv : TEXCOORD0;
	};
		
	sampler2D _MainTex;
	float4 _MainTex_TexelSize;

	float2 _BlurRadius;
	float2 _RadialCenter;
	int _Iteration;

	v2f vert(appdata v) 
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.vertex);
		o.uv =  v.uv;
		return o; 
	}
	
	half4 frag(v2f i) : SV_Target 
	{
		half4 color = half4(0,0,0,0);
		float2 blurVector = (i.uv.xy - _RadialCenter.xy) * _BlurRadius.xy;
	
		for(int j = 0; j < _Iteration; j++)   
		{	
			half4 tmpColor = tex2D(_MainTex, i.uv.xy);
			color += tmpColor;
			i.uv.xy += blurVector; 
		}
		
		return color / (float)_Iteration;
	}

	ENDCG
	
	Subshader 
	{
		Blend One Zero
		Pass {
			ZTest Always Cull Off ZWrite Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
      
			ENDCG
		} 
	} 

	Fallback off

} 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[CreateAssetMenu(fileName = "RadialBlurSettings", menuName = "PostEffects/Blur/RadialBlur")]
public class RadialBlurSettings : PostEffectSettings
{
	public Vector2 _BlurRadius;
	public Vector2 _RadialCenter;
	public int _Iteration = 8;

	protected override void SetMaterialData(Material material)
	{
		material.SetVector("_BlurRadius", _BlurRadius);
		material.SetVector("_RadialCenter", _RadialCenter);
		material.SetInt("_Iteration", _Iteration);
	}
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/900848.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号