Unity纹理基础
单张纹理材质实现:
Shader "Custom/SimpleTextureMat"
{
Properties{
//TintColor:色调
_Color("TintColor", Color) = (1.0, 1.0, 1.0, 1.0)
_MianTex("Maintex", 2D) = "white"{}
_Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
_Gloss("Gloss", Range(8.0, 256)) = 20
}
SubShader{
Pass{
Tags{
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "UnityCG.cginc"
half4 _Color;
sampler2D _MianTex;
float4 _MianTex_ST;
half4 _Specular;
half _Gloss;
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD3;
};
v2f vert(a2v i){
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f, o);
o.vertex = UnityObjectToClipPos(i.vertex);
o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
o.worldPos = mul(unity_ObjectToWorld, i.vertex);
o.uv = TRANSFORM_TEX(i.texcoord, _MianTex);
//o.uv = i.texcoord;
return o;
}
half4 frag(v2f i) : SV_TARGET{
half3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
half3 albedo = tex2D(_MianTex, i.uv) * _Color;
half3 diffuse = _LightColor0.rgb * albedo * saturate(dot(i.worldNormal, lightDir));
half3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
half3 reflectDir = normalize(reflect(-lightDir, i.worldNormal));
half3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
half3 specular = _LightColor0.rgb * albedo * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return half4(diffuse + ambient + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
部分代码段分析:
o.uv = TRANSFORM_TEX(i.texcoord, _MianTex);
TRANSFROM_TEX:使用纹理的属性值 MianTex_ST 对顶点纹理坐标进行变化,使得可以在材质面板中对贴图进行缩放和偏移操作。
TRANSFROM_TEX原理为:
ambient(环境光),diffuse(固有色),specular(高光)都可以选择性乘以纹素(Texel) 以达到不同的效果。
ambient,diffuse:
ambient,diffuse,specular:
diffuse,specular:
纹理应用:凹凸映射
实现凹凸映射主要有两种方法:高度映射和法线映射。
而法线纹理中有一种特殊的纹理,称为切线空间的法线纹理(tangent-space normal map) ,与模型空间的法线纹理不同,切线空间的法线纹理记录的是每个点在各自的切线空间中的法线扰动方向,切线空间以顶点本身为原点,切线方向为x轴,法线方向为z轴。所以切线空间的法线贴图有大部分的蓝色区域,因为对于不变的法线,在切线空间中的矢量为(0,0,1),对应为RGB中的蓝色。
基于切线空间法线贴图的材质:
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Custom/TangentSpaceNormalMapMat"
{
Properties{
//TintColor:色调
_Color("TintColor", Color) = (1.0, 1.0, 1.0, 1.0)
_MianTex("Maintex", 2D) = "white"{}
_BumpMap("BumpMap", 2D) = "blue"{}
_BumpScale("BumpScale", float) = 1.0
_Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
_Gloss("Gloss", Range(8.0, 256)) = 20
}
SubShader{
Pass{
Tags{
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "UnityCG.cginc"
half4 _Color;
sampler2D _MianTex;
float4 _MianTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _BumpScale;
half4 _Specular;
half _Gloss;
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texcoord : TEXCOORD0;
};
struct v2f{
float4 vertex : SV_POSITION;
float3 tangentLightDir : TEXCOORD0;
float3 tangentViewDir : TEXCOORD1;
float4 uv : TEXCOORD2;
};
v2f vert(a2v i){
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f, o);
o.vertex = UnityObjectToClipPos(i.vertex);
float3 binormal = cross(i.normal, i.tangent.xyz ) * i.tangent.w;
float3x3 rotation = float3x3(i.tangent.xyz, binormal, i.normal);
o.tangentLightDir = normalize(mul(rotation, ObjSpaceLightDir(i.vertex)).xyz);
o.tangentViewDir = normalize(mul(rotation, ObjSpaceViewDir(i.vertex)).xyz);
o.uv.xy = TRANSFORM_TEX(i.texcoord, _MianTex);
o.uv.zw = TRANSFORM_TEX(i.texcoord, _BumpMap);
//o.uv = i.texcoord;
return o;
}
half4 frag(v2f i) : SV_TARGET{
half3 albedo = tex2D(_MianTex, i.uv.xy) * _Color;
half4 mapTangent = tex2D(_BumpMap, i.uv.zw);
fixed3 normalTangentSpace;
normalTangentSpace = UnpackNormal(mapTangent);
normalTangentSpace.xy *= _BumpScale;
normalTangentSpace.z = sqrt(1.0 - saturate(dot(normalTangentSpace.xy, normalTangentSpace.xy)));
half3 diffuse = _LightColor0.rgb * albedo * saturate(dot(normalTangentSpace, i.tangentLightDir));
half3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
half3 reflectDir = normalize(reflect(-i.tangentLightDir, normalTangentSpace));
half3 specular = _LightColor0.rgb * albedo * pow(saturate(dot(reflectDir, i.tangentViewDir)), _Gloss);
return half4(diffuse + ambient + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
分析:以上实现是基于切线空间法线贴图完成的,在顶点着色器中将光照方向和视角方向变换到切线空间,然后在片元着色器中对模型求环境光,固有色和高光。同样,也可以将法线贴图求得的法线转换到世界空间中,来计算光照。
Unity同样可以使用灰度(高度)图生成法线贴图。
渐变纹理(BumpMap)实现
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Custom/RampMapMat"
{
Properties{
//TintColor:色调
_Color("TintColor", Color) = (1.0, 1.0, 1.0, 1.0)
_MianTex("Maintex", 2D) = "white"{}
_RampMap("RampMap", 2D) = "white"{}
_Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
_Gloss("Gloss", Range(8.0, 256)) = 20
}
SubShader{
Pass{
Tags{
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "UnityCG.cginc"
half4 _Color;
sampler2D _MianTex;
float4 _MianTex_ST;
sampler2D _RampMap;
float4 RampMap_ST;
half4 _Specular;
half _Gloss;
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD3;
};
v2f vert(a2v i){
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f, o);
o.vertex = UnityObjectToClipPos(i.vertex);
o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
o.worldPos = mul(unity_ObjectToWorld, i.vertex);
o.uv = TRANSFORM_TEX(i.texcoord, _MianTex);
//o.uv = i.texcoord;
return o;
}
half4 frag(v2f i) : SV_TARGET{
half3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
half3 albedo = tex2D(_MianTex, i.uv) * _Color;
half lightScale = 0.5 * dot(i.worldNormal, lightDir) + 0.5;
half3 diffuseColor = tex2D(_RampMap, float2(lightScale, lightScale)).rgb * _Color.rgb;
half3 diffuse = _LightColor0.rgb * diffuseColor;
half3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
half3 reflectDir = normalize(reflect(-lightDir, i.worldNormal));
half3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
half3 specular = _LightColor0.rgb * albedo * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return half4(diffuse + ambient + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
注意:使用渐变纹理时,渐变贴图需要设置Warp Mode为Clamp,而不是默认的Repeat。否则容易出现问题,在HalfLambert光照模型中可能无法明显体现出,但在Blinn-Phong模型中可以明显看出BUG。
这是由于采样时,虽然理论上lightScale的值在[0, 1]之间,但由于精度问题也可能会有1.0001这样的值出现,而Repeat模式下对将整数部分舍弃,只得到0.0001,对应渐变贴图最左侧的值。而Clamp模型下会将大于1的值按照1处理。
遮罩纹理(MaskMap)
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Custom/MaskMapMat"
{
Properties{
//TintColor:色调
_Color("TintColor", Color) = (1.0, 1.0, 1.0, 1.0)
_MianTex("Maintex", 2D) = "white"{}
_Specular("Specular", Color) = (1.0, 1.0, 1.0, 1.0)
_MaskMap("MaskMap", 2D) = "white"{}
_Gloss("Gloss", Range(8.0, 256)) = 20
}
SubShader{
Pass{
Tags{
"LightMode" = "ForwardBase"
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
#include "UnityCG.cginc"
half4 _Color;
sampler2D _MianTex;
float4 _MianTex_ST;
sampler2D _MaskMap;
float4 _MaskMap_ST;
half4 _Specular;
half _Gloss;
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 texcoord : TEXCOORD0;
};
struct v2f{
float4 vertex : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD3;
};
v2f vert(a2v i){
v2f o;
UNITY_INITIALIZE_OUTPUT(v2f, o);
o.vertex = UnityObjectToClipPos(i.vertex);
o.worldNormal = normalize(UnityObjectToWorldNormal(i.normal));
o.worldPos = mul(unity_ObjectToWorld, i.vertex);
o.uv = TRANSFORM_TEX(i.texcoord, _MianTex);
//o.uv = i.texcoord;
return o;
}
half4 frag(v2f i) : SV_TARGET{
half3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
half3 albedo = tex2D(_MianTex, i.uv) * _Color;
half mask = tex2D(_MaskMap, i.uv).r;
half3 diffuse = _LightColor0.rgb * albedo * saturate(dot(i.worldNormal, lightDir));
half3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * albedo;
half3 reflectDir = normalize(reflect(-lightDir, i.worldNormal));
half3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
half3 specular = _LightColor0.rgb * albedo * pow(saturate(dot(reflectDir, viewDir)), _Gloss) * half3(mask, mask, mask);
return half4(diffuse + ambient + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
本质上就是在高光绘制过程中增加了根据遮罩贴图某个通道的值,确定高光强度的方法。
上图为主要对砖块部分添加高光,下图为主要对砖块间缝隙添加高光。