Unity-Shader 03 基础纹理
资源与声明:1-本文章部分图片来源 UCSB 闫令琪 老师《GAMES101 现代计算机图形学入门》
https://sites.cs.ucsb.edu/~lingqi/teaching/games101.html
2-本文章代码来源 冯乐乐老师 《Unity Shader入门精要》
https://github.com/candycat1992/Unity_Shaders_Book/tree/unity_2017_1
3-本文章部分图片与素材来源 知乎 九猫:Shader学习(8)各种坐标空间的定义和变换演示
九猫:Shader学习(8)各种坐标空间的定义和变换演示
4-本文章部分图片与素材来源 博客园 可可西
谈谈法线贴图 - 可可西 - 博客园
5-Unity Shader入门精要笔记(十四):渐变纹理
// 如有侵权 欢迎联系删除
// 若文章有误,欢迎指正讨论
1-单张纹理
1-1 纹理的原理
纹理不会改变模型,只会来改变模型的法线分布
高度纹理和渲染效果
然后这些texture的法线们也是垂直于切线的;这类纹理成为高度纹理,是一张灰度的JPG记录法线的改变值;
高度纹理(height map):模拟表面位移(displacement),然后得到一个修改后的法线值,这种方法也被称为高度映射(height mapping);
高度纹理改变法线的效果
法线垂直切线
高度纹理的边缘是光滑的,因为并不会改变顶点坐标;
我们还能采用位移贴图,除了改变表面法线,还能改变表面顶点的位置,这样的边缘就不会露馅;
位移贴图
甚至还有三维纹理,定义了一个三维空间里每一点的颜色;
三维纹理
1-2 单张纹理实现
也叫作UV纹理,OpenGL原点左下角,DirectX原点左上角;
还是用布林-冯 光照模型;
定义的所有Properties 都可以在Unity界面中修改
材质也可以随便拖过来;是一张JPG;
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 7/Single Texture" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {} //纹理;White是默认的白色纹理
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" } //这是在声明前向渲染
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
// 声明
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST; //这个是配合前面的纹理,ST是缩放和平移的缩写;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0; //texcooro Unity会吧第一组纹理坐标存到该变量中
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2; //以便在片元着色器中用uv做纹理采样
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//这里是做一个纹理坐标的变换;
//*_MainTex_ST.xy 表示的是缩放
//+ _MainTex_ST.zw; 表示的是平移
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// Or just call the built-in function 内置函数 YYDS!!
// o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
// 对纹理坐标采样,用uv对mainTex采样,会乘上Clolor
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
// 环境光 纹理有参与
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 漫反射 纹理有参与
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
//高光,纹理好像没有参与
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
和普通光照模型相比主要多了MainTex;还有需要用tex2D通过UV对纹理贴图进行采样;
1-3 纹理的属性
Wrap Mode 循环模式
纹理的属性,可以点击纹理jpg调节;
我们能够定义很多的属性,比如说阿尔法通道和透明程度;还有纹理超出了之后,要重复又还是平铺(Clamp)
这个是不重复的效果;上面那个是重复的效果;
Filter Mode 滤波模式
这几个 得到的滤波效果会依次提升,同理,消耗也更大;
能改善我们的纹理在放大缩小时候改变的质量;
这里用的就是多线性差值和双线性插值;改善的是纹理贴图过大或者过小的问题;
双线性差值
Point 就是每个像素是一个值,Bilinear 和 Trilinear 就是做了插值,我们可以对比一下;
Trilinear 做了线型差值,看起来就像给了一个低通滤波很平滑;
Point 就是非常像素风,没有任何插值模糊
Max Size
Format
选定了 Format,就相当于确定了一张纹理的大小;
可以计算我们单张纹理的尺寸,很大尺寸的纹理导入进来也会被压缩成一样的大小;
这里的 Automatic 就会0.7mb
2-凹凸纹理
2-1 切线空间、模型空间、世界空间中的变化矩阵
简明扼要:从一个空间T变换到空间N的变换矩阵,就是T空间的三个基向量xyz在N空间下的表示。
详细可参见这个blog
https://zhuanlan.zhihu.com/p/361417740
(1)切线空间->模型空间
相当于切线空间中三个基坐标(TBN)在模型空间三个基坐标(XYZ)上的投影
(2)模型空间->切线空间
是(1)切线空间->模型空间 的转置,因为是逆向操作;
(3)切线空间->世界空间
使用法线贴图,如果光照在世界空间中计算,
就需要把法线从切线空间转换到世界空间。
Matrix = 切线空间三分量在世界空间的表示
模型空间的T、N默认可以获得,副切线垂直于TN平面,TN叉乘可以获得B。
这时我们已知模型空间下的TBN三分量。
再将模型空间TBN转换到世界空间,获得矩阵。
矩阵再乘上切线空间下的法线,获得世界空间法线。
2-2 纹理在切线空间与模型空间
2-2-1 概述
谈谈法线贴图 - 可可西 - 博客园
左右两边分别对应的是无法线贴图和有法线贴图的效果。很明显右边(有法线贴图)脸部细节更丰富、刀疤更清晰,脖子上的肌肉细条相对于左图也要更清晰和立体一些。
法线贴图(NormalMap)存储的是表面的法线方向,即向量n = (x, y, z)。而方向是相对于坐标空间而言的。通常法线有两种坐标空间:Tangent Space(切线空间)、Object Space(对象空间或模型空间),如下:
谈谈法线贴图 - 可可西 - 博客园
Tangent Space法线贴图看上去通常大部分是浅蓝色,Object Space法线贴图则五颜六色。法线存储在哪个坐标系中都是可以的。
模型空间:对于模型顶点自带的法线,它们是定义在模型空间中的,因此一种直接的想法就是将修改后的模型空间中的表面法线存储在一张纹理。
切线空间:对于模型的每个顶点,它都有一个属于自己的切线空间,这个切线空间的原点就是该顶点本身,而z轴是顶点的法线方向(n), x轴是顶点的切线方向(t),而y轴可由法线和切线叉积而得,也被称为是副切线(bitangent, b)或副法线。使用切线空间可以重用法线纹理,所以应用很广。
(这里重点,代码实现里最重要一部分就是构件副法线B,和构建切线空间)
2-2-2 优缺点
Object Space的优点:
(1)实现简单,更加直观。我们甚至都不需要模型原始的法线和切线等信息,也就是说,计算更少。
生成它也非常简单,而如果要生成Tangent Space Normal Map,由于模型的切线一般是和uv方向相同,因此要得到效果比较好的法线效果就要求纹理映射也是连续的。
(2)在纹理坐标的缝合处和尖锐的边角部分,可见的突变(缝隙)较少,即可以提供平滑的边界。
这是因为Object Space Normal Map存储的同一坐标系下的法线信息,因此在边界外通过插值得到法线可以平滑变换。
而Tanget Space Normal Map中法线信息是依靠纹理坐标uv的方向来得到结果,可能在边缘处或尖锐的部分造成更多可见的缝合迹象。
Tangent Space有更多优点:
(1)自由度很高。Tangent-Space Normal Map记录的是相对法线信息,这意味着,即便把该纹理应用到一个完全不同的网格上,也可以得到一个合理的结果。
(2)可进行UV动画。如:我们可以移动一个纹理的UV坐标来实现一个凹凸移动的效果,这种UV动画在水或者火山熔岩这种类型的物体会用到。
(3)可重用。如:一个砖块,我们可以仅使用一张Normal Map就可以用到所有的六个面上。
(4)可压缩。由于Tangent-Space Normal Map中法线的Z方向总是正方向的,因此我们可以仅存储XY方向,而推导得到Z方向。
而Object Space Normal Map由于每个方向都是可能的,因此必须存储3个方向XYZ的值,不可压缩。
通常游戏中使用Tangent Space(切线空间)来存放法线贴图。
2-2-3 计算副切线和构件切线空间:
在Tangent Space中,坐标原点就是顶点的位置,其中z轴是该顶点本身的法线方向(N),另外两个坐标轴在与法向方向相垂直的切平面上。
这样的切线在切平面上有无数条,可以按照模型顶点的位置坐标随纹理坐标(u, v)的变化作为切线空间,来定义切线(Tangent,T)和副法线(Bitangent,Binormal,B)。
但这样做T(u方向)和B(v方向)并不一定是互相垂直的,所以一般会用:
就是副切线B,是由主切线T, 和法线N 叉乘得到的;
注:T为u方向,N为u和v的叉乘,B为N和T的叉乘
谈谈法线贴图 - 可可西 - 博客园
其中,原点对应顶点坐标,x轴是切线方向(T),y轴是副切线方向(B),z轴是法线方向(N)
UE4中StaticMesh的Normals、Tangents、Binormals
2-2-4 反映射操作
可以想象我们要构件单位一个半球,球心是向量起点,半球表面任意一点是向量的终点;那 x, y取值范围为[-1, 1],z取值范围为。
Tangent Space存储的是法线的扰动方向。如果一个顶点的法线方向不变,Normal值就是z轴方向(0, 0, 1)。由于z方向只会朝外,因此,x, y取值范围为[-1, 1],z取值范围为。
通常情况下我们存储在贴图中的值为正,一般通过一个简单的变换pixel = (normal + 1) / 2来完成。这一步是映射;
经过该变换后,之前的法线值(0, 0, 1)实际上对应了法线纹理中RGB的值为(0.5, 0.5, 1),转换成RGB为(128, 128, 255)。
而这个颜色也就是法线纹理中那大片的蓝色。这些蓝色实际上说明顶点的大部分法线是和模型本身法线一样的,不需要改变。
相应地,在ps中对法线纹理采样后,要进行一次反映射,以得到原先的法线方向。为上面变换的逆函数:normal = pixel * 2 - 1
而且最后得出XY之后,因为是法线是单位向量,所以 Z 能用1-X-Y 就能得到Z;
2-3 高度纹理与法线纹理
高度纹理(height map):模拟表面位移(displacement),然后得到一个修改后的法线值,这种方法也被称为高度映射(height mapping);
高度纹理就是,用一个高度纹理,不改变表面顶点,但是改变表面法线值;
是一张灰度图,颜色越深,越往里面凹陷;
法线纹理(normal map):直接存储表面法线,这种方法又被称为法线映射(normal mapping)。
由于法线方向的分量范围在[-1, 1],而像素的分量范围为,因此我们需要做一个映射,通常使用的映射就是:pixel=(normal+1)/2。
2-4 切线空间中的凹凸纹理实现
重点是自己构件一个切线空间,而且会切线空间下的bumpMap进行反映射
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 7/Normal Map In Tangent Space" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {} //bump 是法线,当我们没有提供Bump的时候,就对应模型法线
_BumpScale ("Bump Scale", Float) = 1.0 //bump scale 对应的是法线的凹凸程度
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Pass {
Tags { "LightMode"="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; //这里是切线方向 Unity 会通过这个信息在顶点上以切线方向建立切线空间
float4 texcoord : TEXCOORD0;//texcooro Unity会吧第一组纹理坐标存到该变量中
};
// 用到切线空间的话,就不用转到世界空间了
struct v2f {
float4 pos : SV_POSITION;
float4 uv : TEXCOORD0; //纹理位置,之前是float2,这里是两张纹理,所以就是float4
float3 lightDir: TEXCOORD1; //光照方向(切线空间中)
float3 viewDir : TEXCOORD2; //视角方向(切线空间中)
};
//Part1 顶点着色器
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
//两张纹理都存储在一张UV上,所以xy表示纹理坐标,zw表示的是法线凹凸;
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
//**构建切线空间
// 计算副法线(Binormal) 为了是计算切线空间中的法线和视角方向
// float3 binormal = cross( normalize(v.normal), normalize(v.tangent.xyz) ) * v.tangent.w;
// // Construct a matrix which transform vectors from object space to tangent space
// float3x3 rotation = float3x3(v.tangent.xyz, binormal, v.normal);
// 用一个宏就能解决,这里是为了计算得到切线空间下的法线?
TANGENT_SPACE_ROTATION;
// 光照方向变化到切线空间中
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
// 视角方向变化到切线空间中
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
return o;
}
//Part2 片元着色器
fixed4 frag(v2f i) : SV_Target {
//之前得到的是世界坐标下法线和光照的值,现在是切线坐标下视线和光线的值
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
// 用Text2D 函数 对凹凸法线采样
fixed4 packedNormal = tex2D(_BumpMap, i.uv.zw);
//切线空间中的法线
fixed3 tangentNormal;
// If the texture is not marked as "Normal map"
// tangentNormal.xy = (packedNormal.xy * 2 - 1) * _BumpScale;
// tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
// 用UnpackNormal,是对法线纹理采样结果的一个反映射操作
// 因为进过RGB压缩的切线变化都是映射到的,这里要反映射回去
tangentNormal = UnpackNormal(packedNormal);
//法线纹理乘上scale 得到bumpscale 实际的凹凸XY分量
tangentNormal.xy *= _BumpScale;
//法线都是单位矢量,所以z能用1-X-Y 就能得到Z
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
// 对纹理内容采样,
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
// 环境光,有纹理参与
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 漫反射,有纹理参与,而且法线和光照,都变成了切线坐标下的
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
// 高光,也有纹理参与,而且都是变到了切线坐标下的
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
2-5 世界空间中的凹凸纹理实现
世界空间的主要问题就是,把法线从切线空间转换到世界空间下,并传递给片元着色器
最重要的就是变换矩阵,切线空间到世界空间的变换矩阵;
在切线空间和在世界空间中计算好像区别不大;
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 7/Single Texture" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {} //纹理;White是默认的白色纹理
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" } //这是在声明前向渲染
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
// 声明
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST; //这个是配合前面的纹理,ST是缩放和平移的缩写;
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0; //texcooro Unity会吧第一组纹理坐标存到该变量中
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2; //以便在片元着色器中用uv做纹理采样
};
//Part1 顶点着色器 主要输出UV坐标
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//这里是做一个纹理坐标的变换;
//*_MainTex_ST.xy 表示的是缩放
//+ _MainTex_ST.zw; 表示的是平移
o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
// Or just call the built-in function 内置函数 YYDS!!
// o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
//Part2 片元着色器,主要是用UV坐标对纹理采样
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
// 对纹理坐标采样,用uv对mainTex采样,会乘上Clolor
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
// 环境光 纹理有参与
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
// 漫反射 纹理有参与
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
//高光,纹理好像没有参与
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular" //如果都失败,就执行默认代码
}
不同的法线纹理类型,表示了不同的存储格式,他们的可操作性也不同;
像这个就是把高度图变成法线纹理;
3-渐变纹理
3-1 渐变纹理概念
渐变纹理就是其中一种,比如用渐变纹理控制漫反射效果。前面光照篇里我们介绍过漫反射,是用法线和光照方向的点积与材质的反射率相乘得到。这里我们可以用渐变纹理更加灵活的控制光照。
可以看出,用不同的渐变纹理可以自由控制物体漫反射效果,左边是用一张从紫色到浅黄色的渐变纹理的效果;中间比较通用,是和《军团要塞2》类似,从黑色向浅灰色渐变,中间分界线微微发红。右侧常用于卡通风格的渲染,这种色调通常是突变的,模拟卡通的阴影色块。
实现更自由的漫反射效果;
3-2 渐变纹理的实现
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 7/Ramp Texture" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_RampTex ("Ramp Tex", 2D) = "white" {} //没有Bump 也没有其他Tex 只有一个Ramp tex
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _RampTex;
float4 _RampTex_ST;//声明的时候,也只是有一个RampTex,ST是缩放和平移的缩写
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0; //就是Tex的位置
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2; //以便在片元着色器中用uv做纹理采样这里是float2 就是没有凹凸纹理贴图;
};
//Part1 顶点着色器
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
//用了内置函数,来计算缩放和偏移之后的纹理值
o.uv = TRANSFORM_TEX(v.texcoord, _RampTex);
return o;
}
//Part2 片元着色器
fixed4 frag(v2f i) : SV_Target {
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 用的是半兰伯特模型计算漫反射
// Use the texture to sample the diffuse color
fixed halfLambert= 0.5 * dot(worldNormal, worldLightDir) + 0.5;
//用tex2D函数对纹理图采样
fixed3 diffuseColor = tex2D(_RampTex, fixed2(halfLambert, halfLambert)).rgb * _Color.rgb;
fixed3 diffuse = _LightColor0.rgb * diffuseColor;
// 高光,纹理也没参与
fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
4-遮罩纹理
4-1 遮罩纹理的概念
遮罩纹理可以使我们保护某些区域,使他们免于某些修改;
应用01
我们之前,都是把高光反射应用到模型的所有地方;即我们所有的像素都是用同样的高光强度和高光指数,但是我们希望模型表面某些区域反光强一些,某些区域反光弱一些,我们就可以使用一张纹理遮罩来控制光照;
应用02
制作地形的时候,可以用来混合石头子,裸露的土地,这些纹理;感觉有点像一个蒙版;
就是可以像素级别的控制一些材质
4-2 遮罩纹理的实现
是在凹凸纹理的切线空间的代码上做的;
就像是一个蒙版值,还能控制蒙版的密度;
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
// 是在凹凸纹理的切线空间的代码上做的;
Shader "Unity Shaders Book/Chapter 7/Mask Texture" {
Properties {
_Color ("Color Tint", Color) = (1, 1, 1, 1)
_MainTex ("Main Tex", 2D) = "white" {}
_BumpMap ("Normal Map", 2D) = "bump" {}
_BumpScale("Bump Scale", Float) = 1.0
_SpecularMask ("Specular Mask", 2D) = "white" {}
_SpecularScale ("Specular Scale", Float) = 1.0
_Specular ("Specular", Color) = (1, 1, 1, 1)
_Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader {
Pass {
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float _BumpScale;
//多个纹理使用的是同一个平铺的系数
sampler2D _SpecularMask;//遮罩纹理
float _SpecularScale;//控制遮罩影响度的系数
fixed4 _Specular;
float _Gloss;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 lightDir: TEXCOORD1;
float3 viewDir : TEXCOORD2;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
TANGENT_SPACE_ROTATION;
o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)).xyz;
o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex)).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed3 tangentLightDir = normalize(i.lightDir);
fixed3 tangentViewDir = normalize(i.viewDir);
fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uv));
tangentNormal.xy *= _BumpScale;
tangentNormal.z = sqrt(1.0 - saturate(dot(tangentNormal.xy, tangentNormal.xy)));
fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color.rgb;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentNormal, tangentLightDir));
fixed3 halfDir = normalize(tangentLightDir + tangentViewDir);
// 高光区域就需要用到Mask
// 采样遮罩值,得到遮罩掩码值,和Spectular Scale相乘
fixed specularMask = tex2D(_SpecularMask, i.uv).r * _SpecularScale;
// 计算高光反射和遮罩
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(tangentNormal, halfDir)), _Gloss) * specularMask;
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
页:
[1]