Unity体积光实现

{git

目录

}
体积光的项目分享:github

https://github.com/Claymoreno1/GodRay

体积光的实现方法有不少种,这里列出三种经常使用的,此篇博客详细讲解了经过后处理径向模糊方式的体积光实现,其它方式提供了方法和详细连接,由于人家讲的已经够好了web

  1. 最简单,手游比较常见的是特效贴片
    2.1
    咱们看见的光束其实就是下面这张图片,经过粒子效果很简单就可以实现
    2.2
  2. ShadowGun中经典的GodRays,Blinking GodRays和Blinking GodRays Billboarded,根据远近控制淡入淡出,稍微复杂一点的增长了闪烁和广告牌,若是想学习能够看女神的ShadowGun系列之二——雾和体积光
    1
  3. 屏幕后处理方式(消耗高,也更真实,也是本篇博客的重点内容)
    第一步:提取光源部分
    这一部分比较简单,咱们主要从深度和颜色两个方面作阈值,来获得体积光光源
    由于体积光的光源通常在距离比较远或者高的地方,必须防止把人物或者其余拥有强颜色的物体错当成光源。(固然,有时候咱们也不想让远处的物体发光,把step翻过来用就好了)
    片断着色器代码以下,本文会在最后提供后处理体积光的所有C#与ShaderLab代码
float4 frag(v2f i):SV_Target{
		float4 col=tex2D(_MainTex,i.uv);
		float linearDepth=LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv));
		col*=step(_StepColor.r+_StepColor.g,col.r+col.g)*step(_Depthfloor,linearDepth);
		return col;			
}

第一个pass结果:
3.1.13.1.2
第二步:径向模糊
径向模糊和高斯模糊的区别就在于一个是照着一条线采样求均值,另外一个是在像素周围采样利用权值计算均值。svg

  • 方法1
    首先获得一条从当前像素到模糊中心的向量
float2 center=_LightPos.xy;
float2 uv=i.uv-center;

当前点的像素颜色值,来自于从沿着这条向量采样的颜色均值函数

for(float j=0;j<_Level;j++){
	col+=tex2D(_MainTex,uv*(1-0.01*j)+center);
}
col/=_Level;
  • 方法2
    其实大致原理和方法1同样,由于径向模糊定义就是如此,不一样之处在于添加了采样步长控制向量,添加了一些setp来控制模糊方向
float2 blur=_LightDir.xy*(_LightPos.xy-i.uv);//*step(0,_LightPos.y-i.uv.y);
for(float j=0;j<_Level;j++){
	col+=tex2D(_MainTex,i.uv);
	i.uv+=blur;
}
col/=_Level;

3_3
这个效果须要在OnRenderImage函数中对采样步长修改,反复Blit几回
blurtype为ture,方法2学习

if (blurtype)
{
   for (int i = 0; i < LightIntencity; i++)
   {
     LightDirx_2 = LightDirx * (i * 2 + 2);
     LightDiry_2 = LightDiry * (i * 2 + 2);
     Graphics.Blit(midpic, midpic2, Volumelight, 1);
     LightDirx_2 = LightDirx * (i * 2 + 6);
     LightDiry_2 = LightDiry * (i * 2 + 6);
     Graphics.Blit(midpic2, midpic, Volumelight, 1);
   }
}
else
{
   Graphics.Blit(midpic, midpic2, Volumelight, 2);
   Graphics.Blit(midpic2, midpic, Volumelight, 2);
}

第三步:与源source图像相叠加,获得最后的效果
这一步和简单,LightIntencity是用来控制光的强度,固然,咱们也能够在第一步采样的时候只保留灰度,而后在这一步中,咱们用灰度乘咱们想要的光的颜色,能够更方便的控制体积光的效果。ui

float4 col=tex2D(_MainTex,i.uv);
float4 col2=tex2D(_BlurTex,i.uv);
return col+col2*_LightIntencity;

3_6
后处理体积光已经基本实现了,那么,咱们如何控制光照方向呢?
很简单,(数位板借出去了。。。我用鼠标作一个示意图好了)
后处理体积光
演示
这里个人模糊中心是左上角Cube所在的屏幕坐标
在Chan项目中的效果预览
Chan
如下是C#与ShaderLab的所有代码
C#this

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class Volumelight_1 : MonoBehaviour
{
    public bool blurtype;
    public Material Volumelight;
    public Color StepColor;
    [Range(1,100)]
    public float Depthfloor;
    public Camera thiscamera;
    [Range(0.01f,.5f)]
    public float LightDirx;
    [Range(0.01f, .5f)]
    public float LightDiry;
    private Vector4 LightPos;
    public Transform lighttrans;
    [Range(0.5f,3.0f)]
    public float Blurtimes;
    [Range(0.01f,2f)]
    public float LightIntencity;
    [Range(1,20)]
    public float Level;
    private float LightDirx_2;
    private float LightDiry_2;

    private void OnEnable()
    {
        thiscamera = this.GetComponent<Camera>();
        thiscamera.depthTextureMode |= DepthTextureMode.Depth;
    }
    [ImageEffectOpaque]
    private void OnRenderImage(RenderTexture source, RenderTexture dest)
    {
        RenderTexture midpic = RenderTexture.GetTemporary(source.width, source.height, 0);
        RenderTexture midpic2 = RenderTexture.GetTemporary(source.width, source.height, 0);
        Volumelight.SetColor("_StepColor", StepColor);
        Volumelight.SetFloat("_Depthfloor", Depthfloor);
        Graphics.Blit(source, midpic, Volumelight, 0);
        LightPos = thiscamera.WorldToViewportPoint(lighttrans.transform.position);
        Volumelight.SetVector("_LightPos", LightPos);
        //Volumelight.SetFloat("_LightIntencity", LightIntencity);
        Volumelight.SetFloat("_Level", Level);
        Volumelight.SetVector("_LightDir", new Vector4(LightDirx,LightDiry,0,0));
        if (blurtype)
        {
            for (int i = 0; i < Blurtimes; i++)
            {
                LightDirx_2 = LightDirx * (i * 2 + 2);
                LightDiry_2 = LightDiry * (i * 2 + 2);
                Graphics.Blit(midpic, midpic2, Volumelight, 1);
                LightDirx_2 = LightDirx * (i * 2 + 6);
                LightDiry_2 = LightDiry * (i * 2 + 6);
                Graphics.Blit(midpic2, midpic, Volumelight, 1);
            }
        }
        else
        {
            Graphics.Blit(midpic, midpic2, Volumelight, 2);
            Graphics.Blit(midpic2, midpic, Volumelight, 2);
        }
        Volumelight.SetFloat("_LightIntencity", LightIntencity);
        Volumelight.SetTexture("_BlurTex", midpic);
        Graphics.Blit(source, dest, Volumelight, 3);
        RenderTexture.ReleaseTemporary(midpic);
        RenderTexture.ReleaseTemporary(midpic2);

    }
}

ShaderLab:spa

Shader "myshaders/VolumeLight"
{
	Properties
	{
		_MainTex("_MainTex",2D)="white"{}
		_StepColor("_StepColor",Color)=(1,1,1,1)
		_Depthfloor("_Depthfloor",float)=5
		_LightPos("_LightPos",vector)=(0,0,0,0)
		_LightDir("_LightDir",vector)=(0,-1,0,0)
		_LightIntencity("_LightIntencity",float)=1
		_Level("_Level",float)=8
		_BlurTex("_BlurTex",2D)="white"{}
	}
	CGINCLUDE
		#include "UnityCG.cginc"
		struct v2a{
			float2 uv:TEXCOORD0;
			float4 vertex:POSITION;
		};
		struct v2f{
			float2 uv:TEXCOORD0;
			float4 pos:SV_POSITION;
		};
		float4 _StepColor;//颜色阈值
		sampler2D _MainTex;
		sampler2D _BlurTex;
		float4 _MainTex_TexelSize;
		sampler2D _CameraDepthTexture;
		float _Depthfloor;//距离阈值
		float _LightIntencity;
		float4 _LightDir;
		float _Level;
		float4 _LightPos;
		v2f vert(v2a v){
			v2f o;
			o.pos=UnityObjectToClipPos(v.vertex);
			o.uv=v.uv;
			#if UNITY_UV_STARTS_AT_TOP
				if(_MainTex_TexelSize.y<0) o.uv.y=1-o.uv.y;
			#endif
			return o;
		}
		float4 frag_1(v2f i):SV_Target{
			float4 col=tex2D(_MainTex,i.uv);
			float linearDepth=LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv));
			col*=step(_StepColor.r+_StepColor.g,col.r+col.g)*step(_Depthfloor,linearDepth)*step(linearDepth,50);
			return col;			
		}
		float4 frag_2_1(v2f i):SV_Target{
			float4 col=float4(0,0,0,0);
			float2 blur=_LightDir.xy*(_LightPos.xy-i.uv);//*step(0,_LightPos.y-i.uv.y);
			for(float j=0;j<_Level;j++){
				col+=tex2D(_MainTex,i.uv);
				i.uv+=blur;
			}
			col/=_Level;
			return col;
		}
		float4 frag_2_2(v2f i):SV_Target{
			float2 center=_LightPos.xy;
			float2 uv=i.uv-center;
			float4 col=float4(0,0,0,0);
			for(float j=0;j<_Level;j++){
				col+=tex2D(_MainTex,uv*(1-0.01*j)+center);
			}
			col/=_Level;
			return col;
		}
		float4 frag_3(v2f i):SV_Target{
			float4 col=tex2D(_MainTex,i.uv);
			float4 col2=tex2D(_BlurTex,i.uv);
			return col+col2*_LightIntencity;
		}
	ENDCG
	SubShader
	{
		Pass
		{	
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag_1
			ENDCG
		}
		Pass
		{	
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag_2_1
			ENDCG
		}
		Pass
		{	
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag_2_2
			ENDCG
		}
		Pass
		{	
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag_3
			ENDCG
		}
	}
}