一.简介
现代GPU允许我们把整个三维场景渲染到一个中间缓冲中,即渲染目标纹理(Render Target Texture,RTT)。与之相关的是多重渲染目标(Multiple Render Target,MRT),这种技术指的是GPU允许我们把场景同时渲染到多个渲染目标纹理中,而不需要为每个渲染目标纹理单独渲染完整的场景。
二.摄像机渲染目标设置实现镜子效果
1.创建一个RenderTexture
2.将摄像机的渲染目标设置为该RenderTexture
3.创建一个shader,代码如下:
Shader "Custom/Edu/Mirror" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" "Queue" = "Geometry" }
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
struct a2v
{
float4 vertex:POSITION;
float3 texcoord:TEXCOORD0;
};
struct v2f
{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
};
v2f vert(a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
//o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
o.uv = v.texcoord;
o.uv.x = 1 - o.uv.x;
return o;
}
fixed4 frag(v2f i):SV_Target
{
return tex2D(_MainTex,i.uv) * _Color;
}
ENDCG
}
}
FallBack "Diffuse"
}
4.创建一个材质,使用刚刚创建的shader,并把RenderTexture赋予该Material
5.场景搭建以及效果:
三.使用GrabPass实现玻璃效果
1.基本思路
在Shader中定义一个GrabPass后,Unity会把当前屏幕的图像绘制在一张纹理中,以便我们在后续Pass中访问它。
渲染队列设置成透明队列,以此保证渲染该物体时,所有的不透明物体都已经被绘制在屏幕上了。
使用一张凹凸纹理(BumpMap来模拟光线的折射效果)
使用该BumpMap对GrabPass中获得的纹理进行uv偏移,获得扭曲效果
同时也使用该BumpMap来计算反射方向,来对环境立方体纹理进行采样
2.代码实践
Shader "Custom/Edu/Glass" {
Properties {
//潜意识中错误写法
//_Cubemap ("Environment CubeMap",texCUBE) = "_Skybox"{}
_Cubemap ("Environment CubeMap",CUBE) = "_Skybox"{}
_MainTex ("MainTex", 2D) = "white"{}
_BumpMap ("BumpMap", 2D) = "bump"{}
_Distortion ("Distortion",Range(0,100))= 10
_RefractAmount ("RefractAmount",Range(0.0,1.0)) = 0.5
}
SubShader {
Tags { "RenderType"="Opaque" "Queue" = "Transparent"}
//注意这里一定要在名字前面加”_“!!!!!!!!
GrabPass{"_RefractionTex"}
Pass
{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
samplerCUBE _Cubemap;
sampler2D _BumpMap;
float4 _BumpMap_ST;
fixed _Distortion;
fixed _RefractAmount;
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
struct a2v
{
float4 vertex:POSITION;
fixed3 normal:NORMAL;
fixed4 tangent:TANGENT;
float2 texcoord:TEXCOORD0;
};
struct v2f
{
float4 pos:SV_POSITION;
float4 TtoW0:TEXCOORD0;
float4 TtoW1:TEXCOORD1;
float4 TtoW2:TEXCOORD2;
float4 uv:TEXCOORD3;
float4 scrPos:TEXCOORD4;
};
v2f vert(a2v v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP,v.vertex);
//输入齐次剪裁空间下的坐标,得到屏幕图像的采样坐标?
o.scrPos = ComputeGrabScreenPos(o.pos);
o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord,_BumpMap);
float3 worldPos = mul(_Object2World,v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent);
fixed3 worldBionormal = cross(worldNormal,worldTangent) * v.tangent.w;
o.TtoW0 = float4(worldTangent.x,worldBionormal.x,worldNormal.x,worldPos.x);
o.TtoW1 = float4(worldTangent.y,worldBionormal.y,worldNormal.y,worldPos.y);
o.TtoW2 = float4(worldTangent.z,worldBionormal.z,worldNormal.z,worldPos.z);
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));
//记得要Unpack!!
fixed3 bump = UnpackNormal(tex2D(_BumpMap,i.uv.zw)).xyz;
//_Refraction_TexelSize可以让我们得到该纹理的纹素大小,如一个大小为256X512的纹理
//它的纹素大小为(1/256,1/512)
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy = offset * i.scrPos.z + i.scrPos.xy;
//i.scrPos.xy = offset + i.scrPos.xy;
fixed3 refrCol = tex2D(_RefractionTex,i.scrPos.xy/i.scrPos.w).rgb;
//将法线转换到世界空间
bump = normalize( float3(dot(i.TtoW0.xyz,bump),dot(i.TtoW1.xyz,bump),dot(i.TtoW2.xyz,bump)) );
fixed3 reflDir = reflect(-worldViewDir,bump);
fixed3 reflCol = texCUBE(_Cubemap,reflDir).rgb;
fixed3 finalColor = _RefractAmount * refrCol + (1-_RefractAmount)*reflCol;
return fixed4(finalColor,1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
3.效果图