从简单纹理到凹凸贴图和遮罩实现
学些shader间间断断的总算觉得有点眉目了。总结一下学习。
首先实现一个简单纹理贴图
- 新建shader
改名字为
Shader实现
Shader "Practice/SimpleTexture"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (a2v v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
return col;
}
ENDCG
}
}
}
新建顶点shader时 上面已经基本全部实现了基本的逻辑和结构。顶点着色器方法vert 和片元着色器方法 frag, 结构体a2v和v2f分别储存计算需要的信息。UnityObjectToClipPos函数可以从
找到定义:
TRANSFORM_TEX函数:
tex2D 这是CG程序中用来在一张贴图中对一个点进行采样的方法。
实现加上环境颜色和面板调色
Shader "Practice/SimpleTexture"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_MainColor("MainColor",COLOR)=(1,1,1,1)
}
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2v
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 uv : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _MainColor;
v2f vert (a2v v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 ambientCol=UNITY_LIGHTMODEL_AMBIENT;
fixed4 col = tex2D(_MainTex, i.uv);
fixed4 finalCol=ambientCol+col*_MainColor;
return finalCol;
}
ENDCG
}
}
}
法线纹理
高度贴图, 通常是有高模的模型烘焙将高模的法线信息烘焙到一张贴图用颜色储存,然后用低模型在计算法线是使用烘焙的高模的法线。营造更细节的感觉。以下是在shader入门精要的介绍:
凹凸映射:
纹理的另一种常见的应用的就是凹凸映射。凹凸映射的主要目的是使用一张纹理来修改模型表面的法线,以便为模型提供更多细节。这种方法不会真的改变模型的顶点位置,只是让模型看起来好像是一个“凹凸不平”的,但可以从轮廓处看出“破绽”。
有两种方法可以用来进行凹凸映射:
一种方法是使用一张高度纹理(height map)来模拟表面位移,然后得到一个修改后的法线值,这种方法也被称为高度映射(height mapping);
另一种方法是使用一张法线纹理(normal map)来直接储存表面法线,这种方法又被称为法线映射(normal mapping)。
高度纹理
高度纹理储存的是强度值,它用于表示模型表面局部的海拔高度。颜色越浅表明该位置的表面越向外凸起,而颜色越深表明该位置越向里凹
法线纹理
法线纹理储存的就是表面的法线方向,由于法线方向的分量范围在[-1,1],而像素的分量范围为[0.1]。需要做一个+1除2的映射。由于方向是相对于坐标空间来说的, 法线纹理储存的法线方向是在哪一个坐标空间呢?
对于模型自带的法线,它们是定义在模型空间中的,因此一种直接的想法就是将修改后的模型空间中的表面法线储存在一张纹理中,这种纹理称为是模型空间的法线纹理。
然而在实际制作中,我们往往会采用另一个坐标空间,即模型顶点的切线空间来储存法线。对于模型的每个顶点,他都有一个属于自己的切线空间,这个切线空间的原点就是该点本身,而Z轴就是顶点的法线方向,X轴是顶点的切线方向,Y轴可有XZ叉积得到也被称为是副切线。这种纹理被称为切线空间的法线纹理。下面为一个在切线空间下的计算
Shader "Practice/SimpleTexture"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_MainColor("MainColor",COLOR)=(1,1,1,1)
_BumpTex("BumpTex",2D)="white"{}
}
SubShader
{
Tags{"LightMode"="ForwardBase"}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct a2v
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
float4 tangent:TANGENT;
};
struct v2f
{
float4 vertex : SV_POSITION;
float4 uv : TEXCOORD0;
float3 tangentLightDir:TEXCOORD1;
// float4 tangent:TEXCOORD2;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpTex;
float4 _BumpTex_ST;
fixed4 _MainColor;
v2f vert (a2v v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.uv, _BumpTex);
float3 binormal=cross(v.normal,v.tangent.xyz)*v.tangent.w;
float3x3 rotation=float3x3(normalize(v.tangent.xyz),normalize(binormal),normalize(v.normal));
o.tangentLightDir=mul(rotation,ObjSpaceLightDir(v.vertex)).xyz;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 ambientCol=UNITY_LIGHTMODEL_AMBIENT;
fixed4 col = tex2D(_MainTex, i.uv)*_MainColor;
fixed4 packedNormal= tex2D(_BumpTex, i.uv.zw); //得到法线的大小在0-1;需要映射的-1-1
fixed4 diffuse=packedNormal*2-1;
fixed3 tangentNormal;
tangentNormal.xy=diffuse.xy;
tangentNormal.z=sqrt(1.0-saturate(dot(tangentNormal.xy,tangentNormal.xy)));
fixed4 col2=_LightColor0*col*saturate(dot(tangentNormal,normalize(i.tangentLightDir)));
fixed4 finalCol=ambientCol+col2;
return finalCol;
}
ENDCG
}
}
}