首页 > 代码库 > 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 = http://www.mamicode.com/0.0f;>


动态建立一个纹理

<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 = http://www.mamicode.com/0.0f;>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






unity3d 实现夜视仪效果