您的位置:首页 > 移动开发 > Unity3D

unity之shader学习笔记(五)--纹理

2016-11-26 18:36 603 查看
纹理映射

纹理坐标(也称为u/v坐标,横坐标是u,纵坐标是v,长宽是1)

一般将纹理的颜色代替漫反射的颜色,这样就会看到图像,一般是片元函数处理

纹理的类型有

Normalmap            法线贴图
EditorGUI andLegacy  菜单栏图标
Sprite               2D图片和UI界面
Cubemap              立方体纹理,做环境盒子
cookie               制作阴影,使手电筒的光从圆形变成另一种形状,类似蝙蝠灯的中间阴影,
lightmap             光照贴图,使用避免实时计算
Advanced             自定义


如何使用纹理呢,代码如下

Shader "Custom/10-FirstShader" {
Properties{
//_Diffuse("Diffuse Color",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
_Color("Color",Color) = (1,1,1,1)
_Specular("Specular Color",Color) = (1,1,1,1)
_Gloss("Gloss",Range(10,200)) = 20
}
SubShader{
Pass{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag

//fixed4 _Diffuse;
fixed4 _Specular;
half _Gloss;
sampler2D _MainTex;
fixed4 _Color;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
float4 texcoord:TEXCOORD0;//获取纹理坐标点
};
struct v2f {
float4 svPos:SV_POSITION;
float3 worldN
4000
ormal:TEXCOORD0;//世界空间下法线向量
float4 worldVertex:TEXCOORD1;//用于传递世界空间下顶点坐标
float4 uv:TEXCOORD2;//用于传递uv坐标
};

v2f vert(a2v v) {
v2f f;
f.svPos = mul(UNITY_MATRIX_MVP, v.vertex);
f.worldNormal = UnityObjectToWorldNormal(v.normal);
f.worldVertex = mul(v.vertex, unity_WorldToObject);
f.uv = v.texcoord;//将纹理坐标传递到片元函数
return f;
}
fixed4 frag(v2f f) :SV_Target{

//法线向量
fixed3 normalDir = normalize(f.worldNormal);

//灯光向量
fixed3 lightDir = normalize(WorldSpaceLightDir(f.worldVertex));
//内置函数,可以获取到纹理上某个纹理坐标点的颜色值
//这里只用到了u,v两个值
fixed3 texColor = tex2D(_MainTex,f.uv.xy)*_Color.rgb;

//使用获取到的颜色值替换掉原先漫反射光产生的颜色值
fixed3 diffuse = _LightColor0.rgb * texColor * max(dot(normalDir, lightDir), 0);

//相机视野向量
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(f.worldVertex));

//获取半分线
fixed3 halfDir = normalize(lightDir + viewDir);

//获取高光
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(dot(normalDir, halfDir), 0), _Gloss);

//最后环境光和纹理做了一个融合   这样修改环境光纹理也不会受到太大影响,不会使得纹理模糊
fixed3 tempColor = diffuse + specular + UNITY_LIGHTMODEL_AMBIENT.rgb*texColor;

return fixed4(tempColor, 1);

}

ENDCG
}
}
Fallback "Specular"

}




上述代码中,定义了一个纹理参数_MainTex,还有一个_Color用来与纹理叠加,能够控制纹理的颜色。

再看Inspector面板,可以看到纹理属性不仅可以选择纹理图案,还有Tiling属性和Offset属性。

Tilling 是控制纹理进行压缩,Tilling值越大,那么模型上显示的纹理数量也就越多,x控制纹理的宽度,y控制纹理的高度

Offset属性是控制材质的偏移,通过修改Offset下的x的值来使纹理进行左右移动,通过修改Offset下的y的值来使纹理进行上下的移动

那么如何使用这两个值呢,Tilling和Offset是纹理自带的属性,可以直接使用:

定义变量

fixed4 _MainTex_ST;

变量名称是固定的,后面加_ST,其中前两个属性代表S 理解为Scale,后两个属性代表T 理解为Transform

使用缩放变量的话,将

f.uv = v.texcoord;


替换为

f.uv = v.texcoord*_MainTex_ST.xy


使用平移变量的话,将

f.uv = v.texcoord;


f.uv = v.texcoord+_MainTex_ST.zw;映射

可以使得物体有凹凸效果。

法线贴图

使用法线贴图的颜色值修改某一点的法线,以此来实现更真实的效果。ps:法线贴图应该和贴图大小相同,一一配套对应

需要注意的是:法线每个轴取值范围是-1到1,而颜色的值范围是0到1,因此会进行变换运算

像素值 = (法线某轴的值+1)/2

这样像素值就能存下法线的值,需要使用时只需要还原一下就行

法线某轴的值 = 像素值*2-1

法线贴图一般都是采用模型顶点的切线空间的坐标来存储法线,,这种纹理被称为切线空间的法线纹理,而有些贴图是将模型空间中的表面进行直接修改,然后将法线存储在一张纹理中,这种纹理被称之为模型空间的法线纹理,不过该纹理只能应用于一种模型,不如切线空间的法线纹理应用范围广。

那么如何在shader中使用法线贴图呢

代码如下

Shader "Custom/11-FirstShader"{
Properties{
//_Diffuse("Diffuse Color",Color) = (1,1,1,1)
_Color("Color",Color) = (1,1,1,1)
_MainTex("Main Tex",2D) = "white"{}
//定义一个法线贴图变量
//bump是一个内置模型,意思是当没有指定法线贴图时,使用bump的法线贴图
//一般指定的纹理是切线空间下的法线纹理,也可以使用模型空间下的法线纹理,只要注意坐标所在空间相同即可
_NormalMap("Normal Map",2D) = "bump"{}
_BumpScale("Bump Scale",Float)=1
}
SubShader{
Pass{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag

//fixed4 _Diffuse;
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
//引用法线贴图变量
sampler2D _NormalMap;
float4 _NormalMap_ST;
float _BumpScale;

struct a2v {
float4 vertex:POSITION;
//切线空间的确定是通过存储到模型里面的法线和切线确定的
float3 normal:NORMAL;
float4 tangent:TANGENT;//tangent.w是用来确定切线空间中坐标轴的方向的
float4 texcoord:TEXCOORD0;
};
struct v2f {
float4 svPos:SV_POSITION;
//float3 worldNormal:TEXCOORD0;
//float4 worldVertex:TEXCOORD1;
float3 lightDir : TEXCOORD0;
float4 uv:TEXCOORD1;//xy用来存储纹理的uv坐标,zw用来存储法线贴图的纹理坐标
};

//切线空间根据顶点一直在变化,因此只能在顶点函数中获取
//因此该顶点光照的方向向量也在顶点函数中获取
v2f vert(a2v v) {
v2f f;
f.svPos = mul(UNITY_MATRIX_MVP, v.vertex);
//  f.worldNormal = UnityObjectToWorldNormal(v.normal);
//  f.worldVertex = mul(v.vertex, _World2Object);
f.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
f.uv.zw = v.texcoord.xy * _NormalMap_ST.xy + _NormalMap_ST.zw;

TANGENT_SPACE_ROTATION;//调用这个后之后,会得到一个矩阵 rotation 这个矩阵用来把模型空间下的方向转换成切线空间下

//ObjSpaceLightDir(v.vertex)//得到模型空间下的平行光方向
f.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));

return f;
}
//把所有跟法线方向相关的运算都放在切线空间下
//从法线贴图里取到的法线方向都是切线空间下的

fixed4 frag(v2f f) :SV_Target{

//fixed3 normalDir = normalize(f.worldNormal);
//获取纹理坐标该点的颜色值
fixed4 normalColor = tex2D(_NormalMap,f.uv.zw);
//将颜色值转换为法线的值
//  fixed3 tangentNormal = normalize(  normalColor.xyz * 2 - 1 ) ; //切线空间下的法线
//UnpackNormal 是系统自带的转换方法
fixed3 tangentNormal = UnpackNormal(normalColor);
tangentNormal.xy = tangentNormal.xy*_BumpScale;
tangentNormal = normalize(tangentNormal);

fixed3 lightDir = normalize(f.lightDir);

fixed3 texColor = tex2D(_MainTex, f.uv.xy)*_Color.rgb;

fixed3 diffuse = _LightColor0.rgb * texColor * max(dot(tangentNormal, lightDir), 0);

fixed3 tempColor = diffuse + UNITY_LIGHTMODEL_AMBIENT.rgb*texColor;

return fixed4(tempColor, 1);

}

ENDCG
}
}
Fallback "Specular"
}


有无法线贴图对比

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  unity