unity shader:初探纹理
纹理用途:纹理主要是使用纹理映射技术,将纹理当中的贴图附着到模型上,然后逐纹素控制模型的颜色,进而控制模型的外观。
uv坐标:等价与纹理映射坐标,其中u表示的是横向坐标,v表示的是纵向坐标,表示顶点在纹理中的2D坐标。
纹理基本属性:
1.纹理类型(Texture Type):Texture,NormalMap,CubeMap等。
2.平铺模式(Warp Mode):
Repeat重复模式,表示超过[0,1]之间值时取小数部分,这样相同小数部分可能存在多份,造成重复的效果。
Clamp裁剪模式,表示超过[0,1]之间值时取整数部分,从而不会超过对应边界。
3.多级渐远纹理技术:就是将纹理预先使用滤波模式得到多个小图,形成一个图像金字塔,然后每一层都是对上一层的图像采样结果,这样在实时运行时就可以快速
得到结果像素。但是空间消耗会多33%左右,这是典型的用空间换时间的技术,通常用来解决缩放纹理时计算耗时等问题。
4.滤波模式(Filter Mode):
Point表示最近邻滤波,采样的像素是临近的一个像素,所以看起来有种像素风格效果。
Biliner表示线性滤波,采样的像素是临近的四个像素进行线性插值得到的像素,所以看起来有种模糊风格效果。
Trilner表示线性滤波,采样的像素是临近的四个像素和多级渐远纹理的像素进行线性插值得到的像素,所以看起来有种模糊风格效果。
5.最大大小(Max Size):限制纹理的大小上限,超过这个上限的,unity会自动缩放到上限大小,而且纹理长宽最好使用2的幂,这样存储才是最小的,而且GPU加载时也是最快的。
6.压缩格式(Format):
etc1:被少部分老旧android机型使用。
etc2:被绝大部分新型android机型使用,不支持的机型会自动变换成rgba模式。
pvrtc:ios机型使用。
rgba:所有的机型使用。
单纹理处理颜色:通常将纹理中的缩放和平移对顶点纹理坐标进行变换处理,得到一个变换坐标,用这个坐标去和纹理进行采样,得到的颜色值作为变换系数,与最终的颜色进行混合处理。代码如下:
Shader "Custom/SingleTexture" {
Properties {
_Specular("Specular", Color) = (1, 1, 1, 1) // 高光反射系数
_Gloss("Gloss", Range(8.0, 256)) = 20 // 高光反射区域大小
_MainTex("MainText", 2D) = "white"{} // 主纹理对象,默认是全白纹理
_Color("ColorTint", Color) = (1, 1, 1, 1) // 颜色属性
}
SubShader {
Pass {
Tags { "LightModel" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
// 定义材质检视面板属性关联
fixed4 _Specual;
float _Gloss;
sampler2D _MainTex;
fixed4 _Color;
float4 _MainTex_ST; //主纹理缩放S和偏移T属性
// 顶点着色器输入参数
struct a2v {
float4 vertex : POSITION; // 顶点坐标
float3 normal : NORMAL; // 法线方向
float4 texcoord : TEXCOORD0; // 第一组纹理坐标
};
// 片元着色器输入参数
struct v2f {
float4 pos : SV_POSITION; // 裁剪空间中的顶点坐标
float3 world_normal : TEXCOORD0; // 世界空间中的法线方向
float3 world_pos : TEXCOORD1; // 世界空间中的顶点坐标
float2 uv : TEXCOORD2; // 变换后的纹理坐标
};
// 顶点着色器处理函数
v2f vert(a2v i) {
// 获取裁剪空间中顶点坐标
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
// 获取世界空间中法线方向
o.world_normal = UnityObjectToWorldNormal(i.normal);
// 获取世界空间中顶点坐标
o.world_pos = mul(unity_ObjectToWorld, i.vertex).xyz;
// 获取变换后的纹理坐标:先缩放处理(_MainTex_ST.xy),然后平移处理(_MainTex_ST.zw)
o.uv = i.texcoord * _MainTex_ST.xy + _MainTex_ST.zw;
return o;
}
// 片元着色器处理函数
fixed4 frag(v2f i) :SV_Target {
// 对主纹理进行采样,作为漫反射系数
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
// 获取归一化法线方向
fixed3 world_normal = normalize(i.world_normal);
// 获取归一化光照方向
fixed3 world_light_dir = normalize(UnityWorldSpaceLightDir(i.world_pos));
// 获取兰伯特漫反射
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(world_normal, world_light_dir));
// 获取世界空间视角方向
fixed3 world_view_dir = normalize(UnityWorldSpaceViewDir(i.world_pos));
// 获取Blinn-Phong高光反射
fixed3 world_half_dir = world_light_dir + world_view_dir;
fixed3 specular = _LightColor0.rgb * _Specual.rgb * pow(saturate(dot(world_normal, world_half_dir)), _Gloss);
// 获取环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 获取差值处理后的光照结果
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
Fallback "Specular"
}
凹凸映射:就是使用一张纹理,以不改变顶点位置的前提下来改变法线,以实现凹凸不平的效果。常见的凹凸映射有两种模式,分别如下:
1.高度映射:就是使用一张高度纹理来计算偏移数据,从而得到法线值。
2.法线映射:就是使用一张法线纹理(模型空间的法线纹理或者切线空间的法线纹理)来直接存储表面法线。
切线空间法线纹理特性:
1.由于切线空间法线纹理存储的是相对法线信息,不论换到什么网格上都能得到合理的结果,所以*度很高。
2.由于切线空间法线纹理存储的法线Z方向都是正方向,此时就可以通过XY方向计算得到Z方向,所以可以压缩。
切线空间法线纹理实现法线映射:此时由于法线信息是切线空间中,所以其他光照信息也应该转化到切线空间中,从而在相同的空间中进行变化,代码如下:
Shader "Custom/NormalMapTangentSpace" {
Properties {
_Color("ColorTint", Color) = (1, 1, 1, 1) // 主纹理颜色大小
_MainTex("MainTex", 2D) = "white"{} // 主纹理
_BumpMap("BumpMap", 2D) = "bump"{} // 法线映射纹理对象
BumpScale("BumpScale", Float) = 1.0 // 法线映射纹理的凹凸程度
_Specular("Specular", Color) = (1, 1, 1, 1) // 高光反射系数
_Gloss("Gloss", Range(8, 256)) = 20 // 高光反射区域大小
}
SubShader {
Pass {
Tags { "LightModel" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
// 材质检视面板关联属性
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float BumpScale;
fixed4 _Specular;
float _Gloss;
// 顶点着色器输入参数
struct a2v {
float4 vertex : POSITION; // 顶点坐标
float3 normal : NORMAL; // 法线方向
float4 tangent : TANGENT; // 切线方向
float4 texcoord : TEXCOORD0; // 第一套纹理坐标
};
// 片元着色器输入参数
struct v2f {
float4 pos : SV_POSITION; // 齐次空间顶点坐标
float4 uv : TEXCOORD0; // 变换后的纹理坐标,其中xy存储主纹理uv坐标,zw存储法线纹理uv坐标
float3 light_dir : TEXCOORD1; // 光照方向
float3 view_dir : TEXCOORD2; // 视角方向
};
// 顶点着色器处理函数
v2f vert(a2v i) {
// 获取齐次空间顶点坐标
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
// 获取主纹理坐标
o.uv.xy = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// 获取法线纹理坐标
o.uv.zw = i.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
// 获取副切线方向:法线方向与切线方向的叉积乘以副切线方向
float3 binormal = cross(normalize(i.normal), normalize(i.tangent.xyz)) * i.tangent.w;
// 获取模型空间到切线空间的转换矩阵,其中x是切线方向,y是副切线方向,z是法线方向
float3x3 rotation = float3x3(i.tangent.xyz, binormal, i.normal);
// 获取模型空间到切线空间光照方向
o.light_dir = mul(rotation, ObjSpaceLightDir(i.vertex)).xyz;
// 获取模型空间到切线空间的视角方向
o.view_dir = mul(rotation, ObjSpaceViewDir(i.vertex)).xyz;
return o;
}
// 片元着色器处理函数
fixed4 frag(v2f i) : SV_Target {
// 获取归一化的切线空间光照方向
float3 tangent_light_dir = normalize(i.light_dir);
// 获取归一化的切线空间视角方向
float3 tangent_view_dir = normalize(i.view_dir);
// 对法线纹理进行采样
fixed4 packed_normal = tex2D(_BumpMap, i.uv.zw);
// 获取切线空间法线方向:normal = pixel * 2 - 1
float3 tangent_normal;
tangent_normal.xy = (packed_normal.xy * 2 - 1) * BumpScale;
tangent_normal.z = sqrt(1.0 - saturate(dot(tangent_normal.xy, tangent_normal.xy)));
// 获取漫反射变换系数:对主纹理进行采样
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
// 获取兰伯特漫反射
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(tangent_normal, tangent_light_dir));
// 获取Blinn-Phong高光反射
fixed3 half_dir = normalize(tangent_light_dir + tangent_view_dir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(tangent_normal, half_dir)), _Gloss);
// 获取环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 获取混合后的光照颜色
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
模型空间法线纹理特性:
1.法线纹理存储绝对位置法线信息,实现更加简单方便。
2.法线纹理和法线信息都在同一个模型空间下,插值计算时也都是线性的,可以提供平滑的边界。
模型空间法线纹理实现法线映射:我们通常在世界空间中进行操作模型,所以此处获取世界空间中对应的变换矩阵进行操作,从而实现法线映射。代码如下:
Shader "Custom/NormalMapWorldSpace" {
Properties {
_Color("ColorTint", Color) = (1, 1, 1, 1) // 主纹理颜色大小
_MainTex("MainTex", 2D) = "white"{} // 主纹理
_BumpMap("BumpMap", 2D) = "bump"{} // 法线映射纹理对象
BumpScale("BumpScale", Float) = 1.0 // 法线映射纹理的凹凸程度
_Specular("Specular", Color) = (1, 1, 1, 1) // 高光反射系数
_Gloss("Gloss", Range(8, 256)) = 20 // 高光反射区域大小
}
SubShader {
Pass {
Tags { "LightModel" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
// 材质检视面板关联属性
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float BumpScale;
fixed4 _Specular;
float _Gloss;
// 顶点着色器输入参数
struct a2v {
float4 vertex : POSITION; // 顶点坐标
float3 normal : NORMAL; // 法线方向
float4 tangent : TANGENT; // 切线方向
float4 texcoord : TEXCOORD0; // 第一套纹理坐标
};
// 片元着色器输入参数
struct v2f {
float4 pos : SV_POSITION; // 齐次空间顶点坐标
float4 uv : TEXCOORD0; // 变换后的纹理坐标,其中xy存储主纹理uv坐标,zw存储法线纹理uv坐标
float4 TtoW0 : TEXCOORD1; // 切线空间转换到世界空间切线xyz坐标,第四位w记录顶点世界空间x坐标
float4 TtoW1 : TEXCOORD2; // 切线空间转换到世界空间副切线xyz坐标,第四位w记录顶点世界空间y坐标
float4 TtoW2 : TEXCOORD3; // 切线空间转换到世界空间副法线xyz坐标,第四位w记录顶点世界空间z坐标
};
// 顶点着色器处理函数
v2f vert(a2v i) {
// 获取齐次空间顶点坐标
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
// 获取主纹理坐标
o.uv.xy = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// 获取法线纹理坐标
o.uv.zw = i.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
// 获取顶点世界空间坐标
float3 world_pos = mul(unity_ObjectToWorld, i.vertex).xyz;
// 获取世界空间法线坐标
float3 world_normal = UnityObjectToWorldNormal(i.normal);
// 获取世界空间切线方向
float3 world_tangent = UnityObjectToWorldDir(i.tangent.xyz);
// 获取世界空间副切线方向
float3 world_binormal = cross(world_normal, world_tangent) * i.tangent.w;
// 获取切线空间到世界空间的变换矩阵:变换公式就是切线空间坐标按列存放
o.TtoW0 = float4(world_tangent.x, world_binormal.x, world_normal.x, world_pos.x);
o.TtoW1 = float4(world_tangent.y, world_binormal.y, world_normal.y, world_pos.y);
o.TtoW2 = float4(world_tangent.z, world_binormal.z, world_normal.z, world_pos.z);
return o;
}
// 片元着色器处理函数
fixed4 frag(v2f i) : SV_Target {
// 获取世界空间坐标
float3 world_pos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
// 获取世界空间光照方向
float3 world_light_dir = normalize(UnityWorldSpaceLightDir(world_pos));
// 获取世界空间视角方向
float3 world_view_dir = normalize(UnityWorldSpaceViewDir(world_pos));
// 对法线纹理进行采样
fixed4 packed_normal = tex2D(_BumpMap, i.uv.zw);
// 获取切线空间法线方向:normal = pixel * 2 - 1
float3 tangent_normal;
tangent_normal.xy = (packed_normal.xy * 2 - 1) * BumpScale;
tangent_normal.z = sqrt(1.0 - saturate(dot(tangent_normal.xy, tangent_normal.xy)));
// 将切线空间法线方向转换成世界空间法线方向:用切线空间到世界空间变换矩阵进行变换
float3 world_normal = normalize(half3(dot(i.TtoW0.xyz, tangent_normal), dot(i.TtoW1.xyz, tangent_normal), dot(i.TtoW2.xyz, tangent_normal)));
// 获取漫反射变换系数:对主纹理进行采样
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
// 获取兰伯特漫反射
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(world_normal, world_light_dir));
// 获取Blinn-Phong高光反射
fixed3 half_dir = normalize(world_light_dir + world_view_dir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(world_normal, half_dir)), _Gloss);
// 获取环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 获取混合后的光照颜色
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
注意点:
1.使用DXTnm压缩后的法线纹理中,法线xy方向等于2乘以法线纹理贴图采样后得到的wy分量像素大小减去1得到,法线z方向可以根据法线是单位矢量,用单位长度计算获取。
2.没有使用压缩的法线纹理中,法线xyz方向等于2乘以法线纹理贴图采样后得到的xyz分量像素大小减去1得到。当然由于法线是单位矢量,法线z方向也可以用单位长度计算获取。
3.在法线映射中我们应该尽量使用切线空间法线纹理去操作,而不是使用模型空间法线纹理去操作,因为模型空间存在很多矩阵变换,尤其是法线空间到世界空间的变换矩阵,用这些变换矩阵会大量占用寄存器和指令数,着色器容易报错。很不幸的是unity5中法线映射大量使用模型空间法线纹理去操作,这也是为什么unity5中着色器容易经常出错的原因。
渐变纹理:通过使用兰伯特模型或者半兰伯特模型中的光照方向和法线方向的点积结果作为采样大小,并从渐变纹理中进行采样,得到的采样结果用来来控制漫反射光照。代码如下:
Shader "Custom/RampShader" {
Properties {
_Specular("Specular", Color) = (1, 1, 1, 1) // 高光反射系数
_Gloss("Gloss", Range(8, 256)) = 20 // 高光反射区域大小
_RampTex("RampTex", 2D) = "white"{} // 渐变纹理
_Color("ColorTint", Color) = (1, 1, 1, 1) // 渐变纹理颜色
}
SubShader {
Pass {
Tags { "LightModel" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _RampTex;
float4 _RampTex_ST;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float3 world_normal : TEXCOORD0;
float3 world_pos : TEXCOORD1;
float2 uv : TEXCOORD2;
};
v2f vert(a2v i) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
o.world_normal = UnityObjectToWorldNormal(i.normal);
o.world_pos = mul(unity_ObjectToWorld, i.vertex);
o.uv = TRANSFORM_TEX(i.texcoord, _RampTex); // 等价与i.texcoord.xy * _RampTex_ST.xy + _RampTex_ST.zw
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 world_normal = normalize(i.world_normal);
fixed3 world_light_dir = normalize(UnityWorldSpaceLightDir(i.world_pos));
fixed3 world_view_dir = normalize(UnityWorldSpaceViewDir(i.world_pos));
// 获取半兰伯特光照模型中光照方向和法线方向点积结果
fixed half_lambert = 0.5 * dot(world_normal, world_light_dir) + 0.5;
// 使用半兰伯特点积结果作为渐变纹理采样大小
fixed2 uv = fixed2(half_lambert, half_lambert);
// 对渐变纹理进行采样的结果作为漫反射系数和点积结果
fixed3 diffuse_color = tex2D(_RampTex, uv).rgb * _Color.rgb;
// 获取漫反射结果
fixed3 diffuse = _LightColor0.rgb * diffuse_color;
// 获取Blin-Phong高光反射结果
fixed3 half_dir = normalize(world_light_dir + world_view_dir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(world_normal, half_dir)), _Gloss);
// 获取环境光结果
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 返回光照插值结果
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
遮罩纹理:用来保护某些区域免于某些修改。同时也可以用来记录逐像素控制的表面属性,如:高光反射强度,高光反射系数,自发光强度,边缘光照强度等。代码如下:
Shader "Custom/MaskShader" {
Properties {
_Color("ColorTint", Color) = (1, 1, 1, 1) // 主纹理颜色大小
_MainTex("MainTex", 2D) = "white"{} // 主纹理
_BumpMap("BumpMap", 2D) = "bump"{} // 法线映射纹理对象
BumpScale("BumpScale", Float) = 1.0 // 法线映射纹理的凹凸程度
_Specular("Specular", Color) = (1, 1, 1, 1) // 高光反射系数
_Gloss("Gloss", Range(8, 256)) = 20 // 高光反射区域大小
_MaskTex("MaskTex", 2D) = "white"{} // 遮罩纹理对象
_MaskScale("MaskScale", Float) = 1.0 // 遮罩纹理影响程度系数
}
SubShader {
Pass {
Tags { "LightModel" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
// 材质检视面板关联属性
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST; // 此处所有纹理都使用这个公共的纹理缩放和平移变量,用来节省指令数和寄存器
sampler2D _BumpMap;
float BumpScale;
fixed4 _Specular;
float _Gloss;
sampler2D _MaskTex;
float _MaskScale;
// 顶点着色器输入参数
struct a2v {
float4 vertex : POSITION; // 顶点坐标
float3 normal : NORMAL; // 法线方向
float4 tangent : TANGENT; // 切线方向
float4 texcoord : TEXCOORD0; // 第一套纹理坐标
};
// 片元着色器输入参数
struct v2f {
float4 pos : SV_POSITION; // 齐次空间顶点坐标
float4 uv : TEXCOORD0; // 变换后的纹理采样坐标
float3 light_dir : TEXCOORD1; // 光照方向
float3 view_dir : TEXCOORD2; // 视角方向
};
// 顶点着色器处理函数
v2f vert(a2v i) {
// 获取齐次空间顶点坐标
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
// 获取纹理采样坐标
o.uv.xy = i.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// 获取副切线方向:法线方向与切线方向的叉积乘以副切线方向
float3 binormal = cross(normalize(i.normal), normalize(i.tangent.xyz)) * i.tangent.w;
// 获取模型空间到切线空间的转换矩阵,其中x是切线方向,y是副切线方向,z是法线方向
float3x3 rotation = float3x3(i.tangent.xyz, binormal, i.normal);
// 获取模型空间到切线空间光照方向
o.light_dir = mul(rotation, ObjSpaceLightDir(i.vertex)).xyz;
// 获取模型空间到切线空间的视角方向
o.view_dir = mul(rotation, ObjSpaceViewDir(i.vertex)).xyz;
return o;
}
// 片元着色器处理函数
fixed4 frag(v2f i) : SV_Target {
// 获取归一化的切线空间光照方向
float3 tangent_light_dir = normalize(i.light_dir);
// 获取归一化的切线空间视角方向
float3 tangent_view_dir = normalize(i.view_dir);
// 对法线纹理进行采样,并获取切线空间法线的方向
fixed3 tangent_normal = UnpackNormal(tex2D(_BumpMap, i.uv));
// 获取指定凹凸程度后的切线空间的法线方向
tangent_normal.xy *= BumpScale;
tangent_normal.z = sqrt(1.0 - saturate(dot(tangent_normal.xy, tangent_normal.xy)));
// 获取漫反射变换系数:对主纹理进行采样
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
// 获取兰伯特漫反射
fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(tangent_normal, tangent_light_dir));
// 对遮罩纹理进行采样,并获得高光遮罩大小
fixed specular_mask = tex2D(_MaskTex, i.uv).r * _MaskScale;
// 获取Blinn-Phong高光反射
fixed3 half_dir = normalize(tangent_light_dir + tangent_view_dir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(tangent_normal, half_dir)), _Gloss) * specular_mask;
// 获取环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 获取混合后的光照颜色
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
立方体纹理:就是具有6个面(上下左右前后)的立方体,并且每个面对应一个纹理。在进行纹理采样时,通常使用一个3D坐标从立方体中心出发,并向外部延伸与某一面相交,此时这个相交点就是采样坐标点。通常用来反射使用该立方体纹理物体周围的环境,但是不反射该物体本身。常见应用如下:
1.天空盒:用使用立方体纹理的材质来创建,通常用来模拟背景。通过在Window->Lighting->Scene->SkyBox中指定所有相机共享的天空盒,也可以在摄像机中添加Skybox组件来覆盖全局设定的天空盒。
2.环境映射:可以使用特殊布局的纹理,或者手动创建一个cubemap资源,或者通过脚本代码来创建,通常用来模拟金属质感。常见的应用如下:
反射:通过入射光线方向和法线方向,利用reflect函数来获取反射方向,并通过texCUBE函数来对指定的立方体纹理进行采样,得到反射颜色。
菲涅尔反射:用来通过视角方向控制反射程度,具有两种计算公式,如下所示:
折射:通过入射光线方向和法线方向,以及入射所在介质的折射率与折射所在介质的折射率的比值,利用refract函数来获取折射方向,并通过texCUBE函数来对指定的立方体纹理进行采样,得到折射颜色。
渲染纹理:通过手动创建Render Texture,并将它赋值给摄像机的目标渲染纹理属性中,用来将GPU渲染的结果输出到该纹理中,而不是输出到屏幕上。从而达到抓取屏幕图像的作用。
程序纹理:通过算法代码生成的纹理图像。可以使用各种参数来控制纹理的每一个像素,进而控制纹理的外观。
注意点:
1.纹理采样坐标等于顶点纹理大小乘以纹理缩放率并加上纹理偏移值获取到。而在进行纹理采样时,通常使用tex2D函数对指定纹理对象按照指定纹理采样坐标进行采样。
2.实现抓取渲染图像的方式除了渲染纹理外还可以使用GrabPass { “Texture Name” }来抓取渲染结果到”Texture Name”中。两者之间各有优缺点,对比如下:
.渲染纹理实现起来步骤繁多,但是可以自定义纹理大小和抗锯齿等参数,具有比较大的灵活性。
.GrabPass实现起来就几行代码,实现简单,但是抓取的图像分辨率和显示屏分辨率一致,这样往往会占用较大的带宽,而且也不可以配置,所以性能往往不高。
综上对比,我们最好还是使用渲染纹理的方式来实现抓取屏幕。
3.程序材质是由Substance Desinger软件工具生成的,后缀为sbsar的unity资源文件,该程序材质中包含了多个程序纹理,每个纹理又包含很多控制属性,所以具有很强的多变性,我们可以把这个程序材质按照unity中普通材质的使用方式进行同样的使用就行。