最近几天断了更,毕竟是周末,还是很不要脸地休息了,出去跟同学骑行,骑了七十多公里,还是很佩服自己的耐力(更甚的是同行的还有两个女生坚持了下来),也算干了件牛逼的事。今天回看上面几篇博客发现格式出了些问题,发表前和发表后明显不一样,不知道是审核时出了什么问题,我尽量写的格式规范一点,以免出现问题。 今天继续上面几篇的内容,前面我们学到了静态图片怎么通过高斯模糊来达到一定的效果,那么今天我们就学学运动模糊。运动模糊可以让物体运动起来更加真实、平滑。游戏中实现高速运动的物体时就用到了运动模糊。实现运动的方法有很多,一种就是利用一块累积缓存来混合多张图像,当物体快速移动产生多张图像后,我们取它们之间的平均值作为最后的运动模糊图像,但是这种方法对性能消耗很大;另一种是创建和使用速度缓存,速度映射图存储了每一个像素的速度,然后使用这个速度来决定模糊的大小和方向。 好,现在我们讲第二种方法: 首先,我们需要创建一个C#脚本继承上一篇讲到的PostEffectBase脚本,代码如下: “` public class MotionBlurWithDepthTexture:PostEffectBase{ public Shader motionBlurShader; PRivate Material motionBlurMaterial=null; public Material material{ get{ motionBlurMaterial=CheckShaderAndCreateMaterial(motionBlurShader,motionBlurMaterial); return motionBlurMaterial; }
}
这里我们还是和上一篇类似定义一个纹理和一个shader。[Range(0.0f,1.0f)] public float blurSize=0.5f;
private Camera myCamera; public Camera camera{ get{ if(myCamera==null){ myCamera=GetComponent(); } return myCamera; } }
这里我们首先定义了一个模糊图像大小的变量,然后下面定义一个Camera的变量,然后获取其组建。private Matrix4*4 previousViewProjectionMatrix; void OnEnable(){ camera.depthTextureMode|=DepthTextureMode.depth; }定义了一个4*4的矩阵,这些矩阵知识我建议去看看《Unity shader入门精要》里面的有关的数学基础,这里我就不赘述,这里定义的是上一帧摄像机视角*投影矩阵。然后定义了一个函数设置摄像机模式。void OnRenderImage(RenderTexture src ,RenderTexture dest){ if(material!=null){ material.SetFloat(“_BlurSize”,blurSize); material.SetMatrix(_PreviousViewProjectionMatrix”,previousViewProjectionMatrix); Matrix4*4 currentViewProjectionMatrix=camera.projectionMatrix*camera.worldToCameraMatrix; Matrix 4*4 currentViewProjectionInverseMatrix=currentViewProjectionMatrix.inverse; material.SetMaterix(“_CurrentViewProjectionInverseMatrix”,currentViewProjectionInverseMatrix); proviousViewProjectionMatrix=currentViewProjectionMatrix; Graphics.Blit(src,dest,material);}else{ Graphics.Blit(src,dest); }
}首先传递运动模糊的属性,然后使用了两个变换矩阵,前一帧的视角*投影矩阵和当前帧的视角投影矩阵。然后camera.worldToCamera和camera.projectionMatrix分别求视角矩阵和投影矩阵相乘后再取逆矩阵。接下来就是最重要的shader实现了。Properties{ _MainTex(“Base(RGB)”,2D)=”white”{} _BlurSize(“Blur Size”,Float)=1.0 } SubShader{ CGINCLUDE #include”UnityCG.cginc” sampler2D _MainTex; half4 _MainTex_TexelSize; sampler2D _CameraDepthTexture; float4x4 _CurrentViewProjectionInverseMatrix; float4x4 _PreviousViewProjectionMatrix; half _BlurSize;
这里就相比之前多了三个属性_CameraDepthTexture是Unity传给我们的深度纹理,另外两个由脚本传递struct v2f { float4 pos:SV_POSITION; half2 uv:TEXCOORD0; half2 uv_depth:TEXCOORD1; }; v2f vert(appdata_img v) { v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = v.texcoord; o.uv_depth = v.texcoord;
if (_MainTex_TexelSize.y < 0) o.uv_depth.y = 1 - o.uv_depth.y;
return o; } “`
这里定义了 一个结构体,然后在顶点着色器中处理多张渲染纹理,最后处理平台差异导致的图像问题。 fixed4 frag(v2f i) :SV_Target{ float d = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture,i.uv_depth);//获取像素深度值 float4 H = float4(i.uv.x * 2 - 1, i.uv.y * 2 - 1, d * 2 - 1, 1);//使用原函数的反函数 float4 D = mul(_CurrentViewProjectionInverseMatrix, H); float4 worldPos = D / D.w; float4 currentPos = H; float4 previousPos = mul(_PreviousViewProjectionMatrix, worldPos); previousPos /= previousPos.w; float2 velocity = (currentPos.xy - previousPos.xy) / 2.0f;//求得两个帧之间的速度 float2 uv = i.uv; float4 c = tex2D(_MainTex, uv); uv += velocity*_BlurSize; for (int it = 1; it < 3; it++, uv += velocity*_BlurSize) { float4 currentColor = tex2D(_MainTex, uv); c += currentColor; } c /= 3; return fixed4(c.rgb, 1.0); } ENDCG 片元着色器是重点,我们首先利用深度纹理和当前帧视角*投影矩阵的逆矩阵来求世界空间下坐标,然后根据两帧之间世界坐标距离求得速度,最后利用该速度值与领域像素进行采样,相加后去取平均值得到一个模糊的效果。 Pass{ ZTest Always Cull Off ZWrite Off CGPROGRAM
ENDCG } } FallBack Off }运行结果如下:
新闻热点
疑难解答