当前位置:   article > 正文

Unity Shader 学习笔记(16) 渲染纹理(Render Texture)_rendertexture 镜像

rendertexture 镜像

Unity Shader 学习笔记(16) 渲染纹理(Render Texture)

参考书籍:《Unity Shader 入门精要》
Unity 制作小地图(Minimap)系统 两种方法
官网文档:Graphics Command Buffers


渲染纹理(Render Texture)

GPU允许把三维场景渲染到一个中间缓冲中,即渲染目标纹理(Render Target Texture, RTT),而不是传统的帧缓冲或后备缓冲。延迟渲染使用多重渲染目标(Multiple Render Target, MRT),即同时渲染到多个渲染目标纹理。
  Unity中使用渲染纹理通常有两种方式:

  1. 在Project目录创建渲染纹理,摄像机中设置渲染目标为该纹理。可以模拟镜子效果、投影(如制作游戏的小地图)。
  2. 屏幕后处理时使用GrabPass命令或OnRenderImage函数来获取当前屏幕图像,实现各种屏幕特效。

镜子效果(Mirror)

在这里插入图片描述

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

玻璃效果(Glass)

反射从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:只折射)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

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);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65

GrabPass支持的形式

  • 直接使用GrabPass{ },在后面Pass中直接使用_GrabTexture来访问图像。如果多个物体都这样抓取屏幕,Unity都会单独抓取屏幕,消耗较大。但每个物体屏幕图像不同。
  • 使用GrabPass {“TextureName”},如上例子。但Unity只会在每一帧时,为第一个使用TextureName纹理的物体抓一次屏幕,同时意味着所有物体都用这一张屏幕图像。

渲染纹理 和 GrabPass的比较

-渲染纹理GrabPass
实现难度复杂,要创建纹理,额外相机,设置相机渲染目标,最后传入Shader或者Raw Image。简单,只需几行代码即可抓取屏幕。
效率高,尤其是在移动设备。低,在移动设备上,GrabPass虽然不会重新渲染场景,但要从CPU读取后备缓冲的数据,破坏了CPU和GPU的并行性,比较耗时。
自由度高,虽然要重新渲染,但可以控制哪些进行渲染,分辨率大小等等。如最上面链接的游戏小地图制作。低,获取的图形分辨率和屏幕一致。

除了上面两种方法,还可以使用命令缓冲(Command Buffers)实现抓取屏幕效果。见最上面链接。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/108342
推荐阅读
相关标签
  

闽ICP备14008679号