1. 程式人生 > >unity3d 實現夜視儀效果

unity3d 實現夜視儀效果

說起夜視儀,肯定都會想到螢幕上發綠的遊戲特效
夜視儀效果經常用在FPS(第一人稱射擊)遊戲中,


先來看下我們的實現效果:


感覺還不錯


本次shader需要用到三種貼圖:

1.暈影貼圖
給人一種正帶著夜視儀的感覺



2.噪波貼圖
產生雪花狀噪波



3.掃描線貼圖
增加夜視儀的真實感




先建立一個shader
先瀏覽一下變數:

_ScanLineTileTex; 掃描線效果的貼圖

   
噪波貼圖
基於兩種顏色或材質的互動建立曲面的隨機擾動
    
通過對兩種顏色隨機混合,生成噪波效果
        
 _NoiseTex; 噪波貼圖
   
        
_VignetteTex

;暈影貼圖
   

_Contrast;對比度
  顏色的鮮明程度

        
_Brightness;亮度

            
_RandomValue;隨機值,用在噪波貼圖隨機uv擾動
   

_distortion;桶形畸變的扭曲程度

        
_scale;螢幕放縮比例
   
        
_ScanLineTileAmount;掃描線數量(不是確切數量,指程度大小)
   

_NoiseXSpeed;噪波x方向速度
_NoiseYSpeed;噪波y方向速度
   
    
_NightVisionColor;夜視儀顏色

	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}

		_Contrast("Contrast", Range(0, 4)) = 2 
		_Brightness ("Brightness", Range(0, 2)) = 1
		_NightVisionColor ("Night Vision Color", Color) = (1, 1, 1, 1) 
		_RandomValue ("RandomValue", Float) = 0
		_distortion("distortion", Float) = 0.2
		_scale("scale", Float) = 0.8
		_VignetteTex("Vignette Texture", 2D) = "white" {}
		_ScanLineTileTex("Scan Line Tile Texture", 2D) = "white" {}
		_ScanLineTileAmount("Scan Line Tile Amount", Float) = 4.0
		_NoiseTex("Noise Texture", 2D) = "white" {}
		_NoiseXSpeed("Noise X Speed", Float) = 100.0
		_NoiseYSpeed("Noise Y Speed", Float) = 100.0
	}
	SubSha


我們還要宣告一下:

#pragma vertex vert_img    傳入的畫素資訊為vert_img


#pragma fragment frag
    片元著色函式為frag


#pragma fragmentoption ARB_precision_hint_fastest 

片元著色選項。ARB_precision_hint_fastest使用這個標誌可以fp16的對畫素進行運算,加快渲染

#pragma vertex vert_img
#pragma fragment frag 
#pragma fragmentoption ARB_precision_hint_fastest




鏡頭桶形失真校正演算法,產生桶形畸變效果
將矩形物體拍攝成四邊向外凸形成桶形的影像,就稱鏡頭具有負畸變,或桶形畸變

一會需要用此對uv進行變換
傳入uv值float2 coord
傳出扭曲的uv值
產生了鏡頭的感覺,增加真實感
			float2 barrelDistortion(float2 coord) 
			{
				float2 h = coord.xy - float2(0.5, 0.5);
				float r2 = h.x * h.x + h.y * h.y;
				float f = 1.0 + r2 * (_distortion * sqrt(r2));

				return f * _scale * h + 0.5;
			}


然後我們開始在frag函式中對片元進行著色

從剛才的桶形畸變函式傳出經過處理得uv值distortedUV
獲得當前傳入攝像頭的畫素資訊renderTex

獲取暈影貼影象素資訊
vignetteTex = tex2D(_VignetteTex, distortedUV);    
    
        
掃描線uv 可控掃描線數量
        
scanLinesUV = half2(i.uv.x * _ScanLineTileAmount, i.uv.y * _ScanLineTileAmount);
獲取掃描線貼影象素資訊
scanLineTex = tex2D(_ScanLineTileTex, scanLinesUV);



噪波貼圖uv
根據時間與隨機值變換uv產生擾動效果
noiseUV = half2(i.uv.x + (_RandomValue * _SinTime.z * _NoiseXSpeed),i.uv.y + (_Time.x * _NoiseYSpeed));
獲取噪波貼影象素資訊
fixed4 noiseTex = tex2D(_NoiseTex, noiseUV);


lum 即 luminosity 亮度值
lum = dot (fixed3(0.299, 0.587, 0.114), renderTex.rgb);
lum += _Brightness;//加上可自控的亮度
使飽和度調為零,變成黑白效果,再與夜視鏡顏色混合
fixed4 finalColor = (lum *2) + _NightVisionColor;


再與三種貼圖顏色混合得到最終顏色值
finalColor = pow(finalColor, _Contrast);
finalColor *= vignetteTex;
finalColor *= scanLineTex * noiseTex;



shader就ok了

			fixed4 frag(v2f_img i/*畫素資訊*/) : COLOR// 片元著色函式
			{
				half2 distortedUV = barrelDistortion(i.uv);  //桶形畸變uv
				fixed4 renderTex = tex2D(_MainTex, distortedUV); 
				fixed4 vignetteTex = tex2D(_VignetteTex, distortedUV); //暈影貼圖



				//掃描線uv 可控掃描線數量
				half2 scanLinesUV = half2(i.uv.x * _ScanLineTileAmount, i.uv.y * _ScanLineTileAmount);//_ScanLineTileAmount大小無限制
				fixed4 scanLineTex = tex2D(_ScanLineTileTex, scanLinesUV);
				//噪波貼圖uv
				half2 noiseUV = half2(i.uv.x + (_RandomValue * _SinTime.z * _NoiseXSpeed),i.uv.y + (_Time.x * _NoiseYSpeed));
				fixed4 noiseTex = tex2D(_NoiseTex, noiseUV);



				//lum = luminosity 亮度
				fixed lum = dot (fixed3(0.299, 0.587, 0.114), renderTex.rgb);
				lum += _Brightness;//加上可自控的亮度
				//飽和度調為零,變成黑白效果,再與夜視鏡顏色混合
				fixed4 finalColor = (lum *2) + _NightVisionColor;//



				finalColor = pow(finalColor, _Contrast);//對比度
				finalColor *= vignetteTex;//與暈影貼圖混合
				finalColor *= scanLineTex * noiseTex;

				return finalColor;
			}

接下來看看放入攝像頭中的c#指令碼



建立一個c#指令碼


先賦予變數,與上面的shader的變數都差不多,
這就是一會要傳入shader的值

<span style="font-size:12px;">	#region Variables
	public Shader nightVisionShader;
	
	public float contrast = 2.0f;
	public float brightness = 1.0f;
	public Color nightVisionColor = Color.white;
	
	public Texture2D vignetteTexture;
	
	public Texture2D scanLineTexture;
	public float scanLineTileAmount = 4.0f;
	
	public Texture2D nightVisionNoise;
	public float noiseXSpeed = 100.0f;
	public float noiseYSpeed = 100.0f;
	
	public float distortion = 0.2f;
	public float scale = 0.8f;
	
	private float randomValue = 0.0f;
	private Material curMaterial;
	#endregion</span>

動態建立一個紋理

<span style="font-size:12px;">	#region Properties
	Material material
	{
		get
		{
			if(curMaterial == null)
			{
				curMaterial = new Material(nightVisionShader);
				curMaterial.hideFlags = HideFlags.HideAndDontSave;
			}
			return curMaterial;
		}
	}
	#endregion</span>

依舊需要 OnRenderImage()這個函式抓取攝像機的影象
然後我們把各種變數傳入shader
通過 Graphics.Blit() 這個函式
可以經過shader的變換處理在輸出到我們的顯示器中

<span style="font-size:12px;">void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
	{
		if(nightVisionShader != null)
		{	
			material.SetFloat("_Contrast", contrast);
			material.SetFloat("_Brightness", brightness);
			material.SetColor("_NightVisionColor", nightVisionColor);
			material.SetFloat("_RandomValue", randomValue);
			material.SetFloat("_distortion", distortion);
			material.SetFloat("_scale",scale);
			
			if(vignetteTexture)
			{
				material.SetTexture("_VignetteTex", vignetteTexture);
			}
			
			if(scanLineTexture)
			{
                material.SetTexture("_ScanLineTileTex", scanLineTexture);
                material.SetFloat("_ScanLineTileAmount", scanLineTileAmount);
			}
			
			if(nightVisionNoise)
			{
				material.SetTexture("_NoiseTex", nightVisionNoise);
				material.SetFloat("_NoiseXSpeed", noiseXSpeed);
				material.SetFloat("_NoiseYSpeed", noiseYSpeed);
			}
			
			Graphics.Blit(sourceTexture, destTexture, material);
		}
		else
		{
			Graphics.Blit(sourceTexture, destTexture);
		}
	}</span>

一切ok之後,在指令碼調好各種值之後
讓我們來看看效果



立馬FPS了的感覺= =;

以下全部程式碼

c#:

<span style="font-size:12px;">using UnityEngine;
using System.Collections;

public class night : MonoBehaviour
{
	#region Variables
	public Shader nightVisionShader;
	
	public float contrast = 2.0f;
	public float brightness = 1.0f;
	public Color nightVisionColor = Color.white;
	
	public Texture2D vignetteTexture;
	
	public Texture2D scanLineTexture;
	public float scanLineTileAmount = 4.0f;
	
	public Texture2D nightVisionNoise;
	public float noiseXSpeed = 100.0f;
	public float noiseYSpeed = 100.0f;
	
	public float distortion = 0.2f;
	public float scale = 0.8f;
	
	private float randomValue = 0.0f;
	private Material curMaterial;
	#endregion
	
	#region Properties
	Material material
	{
		get
		{
			if(curMaterial == null)
			{
				curMaterial = new Material(nightVisionShader);
				curMaterial.hideFlags = HideFlags.HideAndDontSave;
			}
			return curMaterial;
		}
	}
	#endregion
	
	void Start()
	{
		if(!SystemInfo.supportsImageEffects)
		{
			enabled = false;
			return;
		}
		
		if(!nightVisionShader && !nightVisionShader.isSupported)
		{
			enabled = false;
		}
	}
	
	void OnRenderImage(RenderTexture sourceTexture, RenderTexture destTexture)
	{
		if(nightVisionShader != null)
		{	
			material.SetFloat("_Contrast", contrast);
			material.SetFloat("_Brightness", brightness);
			material.SetColor("_NightVisionColor", nightVisionColor);
			material.SetFloat("_RandomValue", randomValue);
			material.SetFloat("_distortion", distortion);
			material.SetFloat("_scale",scale);
			
			if(vignetteTexture)
			{
				material.SetTexture("_VignetteTex", vignetteTexture);
			}
			
			if(scanLineTexture)
			{
                material.SetTexture("_ScanLineTileTex", scanLineTexture);
                material.SetFloat("_ScanLineTileAmount", scanLineTileAmount);
			}
			
			if(nightVisionNoise)
			{
				material.SetTexture("_NoiseTex", nightVisionNoise);
				material.SetFloat("_NoiseXSpeed", noiseXSpeed);
				material.SetFloat("_NoiseYSpeed", noiseYSpeed);
			}
			
			Graphics.Blit(sourceTexture, destTexture, material);
		}
		else
		{
			Graphics.Blit(sourceTexture, destTexture);
		}
	}
	
	void Update()
	{
		contrast = Mathf.Clamp(contrast, 0f,4f);
		brightness = Mathf.Clamp(brightness, 0f, 2f);
		randomValue = Random.Range(-1f,1f);
		distortion = Mathf.Clamp(distortion, -1f,1f);
		scale = Mathf.Clamp(scale, 0f, 3f);
	}
	
	void OnDisable()
	{
		if(curMaterial)
		{
			DestroyImmediate(curMaterial);
		}
	}
}</span>

shader:
<span style="font-size:12px;">Shader "Custom/shaderTest" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}

		_Contrast("Contrast", Range(0, 4)) = 2 
		_Brightness ("Brightness", Range(0, 2)) = 1
		_NightVisionColor ("Night Vision Color", Color) = (1, 1, 1, 1) 
		_RandomValue ("RandomValue", Float) = 0
		_distortion("distortion", Float) = 0.2
		_scale("scale", Float) = 0.8
		_VignetteTex("Vignette Texture", 2D) = "white" {}
		_ScanLineTileTex("Scan Line Tile Texture", 2D) = "white" {}
		_ScanLineTileAmount("Scan Line Tile Amount", Float) = 4.0
		_NoiseTex("Noise Texture", 2D) = "white" {}
		_NoiseXSpeed("Noise X Speed", Float) = 100.0
			_NoiseYSpeed("Noise Y Speed", Float) = 100.0
	}
	SubShader {
		Pass {  
			Tags { "RenderType"="Opaque" }
			LOD 200
				CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag 
#pragma fragmentoption ARB_precision_hint_fastest//使用這個標誌可以fp16的對畫素進行運算
#include "UnityCG.cginc"

				uniform sampler2D _MainTex;
			uniform sampler2D _ScanLineTileTex;//掃描線效果的貼圖
			//噪波貼圖基於兩種顏色或材質的互動建立曲面的隨機擾動
			//通過對兩種顏色隨機混合,生成噪波效果
			uniform sampler2D _NoiseTex;//噪波貼圖
			uniform sampler2D _VignetteTex;//裝飾圖案,小插圖,此處為暈影貼圖
			fixed _Contrast;//對比度
			fixed _Brightness;//亮度
			fixed _RandomValue;//隨機值,用在噪波貼圖隨機uv擾動
			fixed _distortion;//扭曲
			fixed _scale;//螢幕比例
			fixed _ScanLineTileAmount;//掃描線數量
			fixed _NoiseXSpeed;//噪波x方向速度
			fixed _NoiseYSpeed;//噪波y方向速度
			fixed4 _NightVisionColor;//夜視鏡顏色

			struct Input {
				float2 uv_MainTex;
			};
			float2 barrelDistortion(float2 coord) 
			{
				float2 h = coord.xy - float2(0.5, 0.5);
				float r2 = h.x * h.x + h.y * h.y;
				float f = 1.0 + r2 * (_distortion * sqrt(r2));

				return f * _scale * h + 0.5;
			}

			fixed4 frag(v2f_img i/*畫素資訊*/) : COLOR// 片元著色函式
			{
				half2 distortedUV = barrelDistortion(i.uv);  //桶形畸變uv
				fixed4 renderTex = tex2D(_MainTex, distortedUV); 
				fixed4 vignetteTex = tex2D(_VignetteTex, distortedUV); //暈影貼圖



				//掃描線uv 可控掃描線數量
				half2 scanLinesUV = half2(i.uv.x * _ScanLineTileAmount, i.uv.y * _ScanLineTileAmount);//_ScanLineTileAmount大小無限制
				fixed4 scanLineTex = tex2D(_ScanLineTileTex, scanLinesUV);
				//噪波貼圖uv
				half2 noiseUV = half2(i.uv.x + (_RandomValue * _SinTime.z * _NoiseXSpeed),i.uv.y + (_Time.x * _NoiseYSpeed));
				fixed4 noiseTex = tex2D(_NoiseTex, noiseUV);



				//lum = luminosity 亮度
				fixed lum = dot (fixed3(0.299, 0.587, 0.114), renderTex.rgb);
				lum += _Brightness;//加上可自控的亮度
				//飽和度調為零,變成黑白效果,再與夜視鏡顏色混合
				fixed4 finalColor = (lum *2) + _NightVisionColor;//



				finalColor = pow(finalColor, _Contrast);//對比度
				finalColor *= vignetteTex;//與暈影貼圖混合
				finalColor *= scanLineTex * noiseTex;

				return finalColor;
			}
			ENDCG
		}  
	}
	FallBack "Diffuse"  
}  </span>


                                                                                                                                               ------------- by wolf96