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
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。