原理和概念
法线是一个向量(x,y,z),每一个顶点都有一个法线,用一个纹理去存储的话,那就是(r,g,b),由于法线是垂直于一个面的,对于2d图片来说,那他的z值就是1
用一张纹理来存储法线的值,法线的xyz方向取值是【-1,1】,而rgb的取值是【0,1】,所以这里有一个转换
rgb = (normal+1)/2=0.5*normal+0.5
资源
在unity中引入两个cube,为其分别制作一个材质,一个材质有法线贴图,一个没有,用作对比
资源1:
将图片导入unity,设置类型为normalmap
资源2:
shader 如下:
法线贴图shader
Shader "Custom/normalMapNormal" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
//基本贴图
_MainTex ("Albedo (RGB)", 2D) = "white" {}
//法线贴图
_NormalMaping("NormalMaping", 2D) = "bump" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
sampler2D _NormalMaping; //在CG模块重新声明一下 _NormalMaping
struct Input {
float2 uv_MainTex;
float2 uv_NormalMaping;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
// 提取法线值,UnpackNormal接受一个fixed4的输入,并将其转换为相应的法线值(fixed3)
o.Normal = UnpackNormal(tex2D(_NormalMaping, IN.uv_NormalMaping));
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
/*o.Metallic = _Metallic;
o.Smoothness = _Glossiness;*/
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
不加法线贴图shader
Shader "Custom/normalMapBase" {
/*
在Properties{}中定义着色器属性,在这里定义的属性将被作为输入提供给所有的子着色器。每一条属性的定义的语法是这样的:
_Name("Display Name", type) = defaultValue[{options}]
_Name - 属性的名字,简单说就是变量名,在之后整个Shader代码中将使用这个名字来获取该属性的内容
Display Name - 这个字符串将显示在Unity的材质编辑器中作为Shader的使用者可读的内容
type - 这个属性的类型,可能的type所表示的内容有以下几种:
Color - 一种颜色,由RGBA(红绿蓝和透明度)四个量来定义;
2D - 一张2的阶数大小(256,512之类)的贴图。这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终被显示出来;
Rect - 一个非2阶数大小的贴图;
Cube - 即Cube map texture(立方体纹理),简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),
也会被转换为对应点的采样;
Range(min, max) - 一个介于最小值和最大值之间的浮点数,一般用来当作调整Shader某些特性的参数(比如透明度渲染的截止值可以是从0至1的值等);
Float - 任意一个浮点数;
Vector - 一个四维数;
defaultValue 定义了这个属性的默认值,通过输入一个符合格式的默认值来指定对应属性的初始值(某些效果可能需要某些特定的参数值来达到需要的效果,虽然这些值可以在之后在进行调整,
但是如果默认就指定为想要的值的话就省去了一个个调整的时间,方便很多)。
Color - 以0~1定义的rgba颜色,比如(1,1,1,1);
2D/Rect/Cube - 对于贴图来说,默认值可以为一个代表默认tint颜色的字符串,可以是空字符串或者”white”,”black”,”gray”,”bump”中的一个
Float,Range - 某个指定的浮点数
Vector - 一个4维数,写为 (x,y,z,w)
另外还有一个{option},它只对2D,Rect或者Cube贴图有关,在写输入时我们最少要在贴图之后写一对什么都不含的空白的{},
当我们需要打开特定选项时可以把其写在这对花括号内。如果需要同时打开多个选项,可以使用空白分隔。
可能的选择有ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal中的一个,这些都是OpenGL中TexGen的模式
*/
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
//光滑度
_Glossiness ("Smoothness", Range(0,1)) = 0.5
//金属光泽
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
//表面着色器可以被若干个标签所修饰,通过判断标签来决定硬件什么时候该调用着色器
/*
RendererType赋值有:
1.Opaque:绝大部分的不透明物体渲染时使用
2.Transparent:绝大部分的透明物体渲染时使用
3.BackGround:天空盒
4.Overlay:用来渲染叠加效果,镜头光晕等
*/
Tags { "RenderType"="Opaque" }
/*
LOD是LevelofDetail的缩写,我们能用什么样的shader由它来决定,如果我们在Unity的QualitySetting中设定最大LOD值小于subshader所指定的值时,
则subshader不可使用
Unity的Shader制定了一组LOD值,我们在写shader的时候作为参考,方便以后对设备图形性能调整,和画质有更精确的控制
Vertexlitkindofshaders(顶点光照类型的shader)=100
Diffuse(漫反射)=200
ReflectiveBumpedUnlit(凸起反射不发光)=250
ReflectiveBumpedVertexlit(顶点反射不发光)=250
Bumped,Specular(凸起,高光)=300
凹凸高光=400
Parallax(视差)=500
ParallaxSpecular(视差高光)=600
*/
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
// 输入结构体重一般包含着色器所需要的纹理坐标,命名规范:uv + 纹理名称
//当使用第二纹理,命名为:uv2+纹理名称
float3 viewDir;//视角方向
float4 COLOR; //每个顶点的颜色差值
float4 screenPos;//屏幕坐标(使用.xy/.wh来获取屏幕2D坐标)
float3 worldRefl;// 世界坐标系中反射向量
float3 worldNormal;// 世界坐标系中法线向量
float2 uv_MainTex;//纹理坐标
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
/*void myvert(inout appdata_full v) {
v.vertex.xyz += v.normal * _Amount;
}*/
/*
其作用是结构输入的uv或者附加数据,然后进行处理,最后将结构填充到输出结构体中,所以只能按照规定来写
参数1:Input:是我们需要定义的输入结构体,可以吧所需要参与计算的数据放到这个结构体中,传入方法使用
参数2:inout:SurfaceOutput定义输出结构体的类型,需要把方法里面得到的结果赋值给该结构体里面的成员变量即可完成着色
SurfaceOutput的输出结果结构定义如下:
half3Albedo:像素的颜色
half3Normal:法线
half3Emission:自发光,不受光照影响
halfSpecular:高光指数
halfGloss:光泽度
halfAlpha:透明度
SurfaceOutPutStandard输出结果定义如下:
fixed3Albedo:基础颜色
fixed3Normal:切线空间法线
halfSmoothness:是否光滑,0=粗糙,1=光滑
halfMetallic:是否有金属光泽,0=非金属,1=金属
halfOcclusion:遮挡(默认为1)
fixedAlpha:透明度
*/
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
/*half4 LightingCustomLambert(SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot(s.Normal, lightDir);
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
c.a = s.Alpha;
return c;
}
void mycolor(Input IN, SurfaceOutput o, inout fixed4 color) {
color *= _ColorTint;
}*/
ENDCG
}
FallBack "Diffuse"
}