赞
踩
参考书籍:《Unity Shader 入门精要》
Unity 制作小地图(Minimap)系统 两种方法
官网文档:Graphics Command Buffers
GPU允许把三维场景渲染到一个中间缓冲中,即渲染目标纹理(Render Target Texture, RTT),而不是传统的帧缓冲或后备缓冲。延迟渲染使用多重渲染目标(Multiple Render Target, MRT),即同时渲染到多个渲染目标纹理。
Unity中使用渲染纹理通常有两种方式:
Properties { _MainTex ("Main Tex", 2D) = "white" {} // 传入自己创建的RenderTexture } v2f vert(a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = v.texcoord; o.uv.x = 1 - o.uv.x; // 反转x分量 return o; } fixed4 frag(v2f i) : SV_Target { return tex2D(_MainTex, i.uv); // 直接输出反转后的纹理 }
反射从1到0,折射从0到1。玻璃后面的图像被“扭曲”。
通过GrabPass获取屏幕图像,通常用于渲染透明物体。及对物体后面的图像做更复杂的处理,模拟折射效果等。虽然代码不包含混合指令,但往往需要把物体的渲染队列设置成透明队列(“Queue” = “Transparent”)。
实现原理:结合法线纹理修改法线信息,然后使用上一篇的Cubemap来模拟玻璃的反射,再用GrabPass获取玻璃后面的屏幕图像,最后用切线空间下的法线对屏幕做偏移。
Properties {
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {} // 法线纹理
_Cubemap ("Environment Cubemap", Cube) = "_Skybox" {} // 反射纹理
_Distortion ("Distortion", Range(0, 100)) = 10 // 折射扭曲程度
_RefractAmount ("Refract Amount", Range(0.0, 1.0)) = 1.0 // 控制折射程度(0:只反射,1:只折射)
}
SubShader中:
// Transparent:在所有不透明物体渲染后再渲染。Opaque:确保在着色器替换时,该物体在需要时正确渲染。 Tags { "Queue"="Transparent" "RenderType"="Opaque" } // 抓取屏幕图像的Pass,存入到纹理变量_RefractionTex中。 GrabPass { "_RefractionTex" } Pass { ... sampler2D _MainTex; float4 _MainTex_ST; sampler2D _BumpMap; float4 _BumpMap_ST; samplerCUBE _Cubemap; float _Distortion; fixed _RefractAmount; sampler2D _RefractionTex; // 对应GrabPass指定的纹理名 float4 _RefractionTex_TexelSize; // 纹理大小。如256x512的纹理,值为(1/256, 1/512) ... struct v2f { float4 pos : SV_POSITION; float4 scrPos : TEXCOORD0; // 屏幕图像的采样坐标 ... }; v2f vert (a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.scrPos = ComputeGrabScreenPos(o.pos); // 计算抓取的屏幕图像采样坐标,在UnityCG.cginc中定义。 ... // 法线纹理采样,切线空间转换到世界空间等 return o; } fixed4 frag (v2f i) : SV_Target { float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w); fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); // 获取切线空间下的法线方向 fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw)); // 对屏幕图像采样坐标进行偏移,模拟折射 float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy; i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy; fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy/i.scrPos.w).rgb; // 透射除法 计算得到屏幕坐标 // 法线再转到世界空间,原理见前面第8篇 bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump))); // 法线再转到世界空间 bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump))); // 计算反射方向 fixed3 reflDir = reflect(-worldViewDir, bump); // 主纹理的采样,和立方体纹理的采样 fixed4 texColor = tex2D(_MainTex, i.uv.xy); fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb; fixed3 finalColor = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount; return fixed4(finalColor, 1); } }
_GrabTexture
来访问图像。如果多个物体都这样抓取屏幕,Unity都会单独抓取屏幕,消耗较大。但每个物体屏幕图像不同。- | 渲染纹理 | GrabPass |
---|---|---|
实现难度 | 复杂,要创建纹理,额外相机,设置相机渲染目标,最后传入Shader或者Raw Image。 | 简单,只需几行代码即可抓取屏幕。 |
效率 | 高,尤其是在移动设备。 | 低,在移动设备上,GrabPass虽然不会重新渲染场景,但要从CPU读取后备缓冲的数据,破坏了CPU和GPU的并行性,比较耗时。 |
自由度 | 高,虽然要重新渲染,但可以控制哪些进行渲染,分辨率大小等等。如最上面链接的游戏小地图制作。 | 低,获取的图形分辨率和屏幕一致。 |
除了上面两种方法,还可以使用命令缓冲(Command Buffers)实现抓取屏幕效果。见最上面链接。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。