大白话版 UnityShader学习(2)-基础纹理研究(二)
根据冯乐乐大小姐的书来进行学习,总结学习经验,分享心得。
单张纹理
光说不练假把式,代码是码着码着就会了。
(1). 创建一个界面scene,然后把天空盒关了,在window/Lighting/skybox里去掉
(2). 新建个材质球,再新建个shader,把shader放到材质球上,双击shader打开,删掉里面的代码,准备开始
(ps:个人建议shader用Surface Shader新手嘛,不能一开始就太难)
(3). 首先我取个名字,工作中取名字很关键,可以让你迅速找到你想到的代码,所以这边我的习惯是:
Shader"ShaderLab/Lesson01_UV"
{
(4). 那么接下来,我们为了使用到纹理,那必须先给他一个可填入的区域,当然这边冯姐姐为了给这个纹理的外形变得更加好看,在这里增加了Blinn-Phong光照模型。这边“white”{}学习的时候我在想,这是个什么玩意,怎么还有{}这个括号,还有这个white是个啥,这边解释一下white是Unity里内置的纹理的名字,可能还有black,blue对吧,我们这边就一般默认white好了。而这个**{}**东西,你乍一看是不是感觉里面应该要有点啥都东西吧,没有错,这个是为了作为他的初始值而存在的,这里面是空出来的,在底层里,如果你的图片放进去了,那么就会在里面填了一堆数字参数,所以不能忘记。
Properties
{
_Color("光照颜色",Color) = (1.0,1.0,1.0,1.0) //ps:我这边是为了方便理解所以在里面写的是中文, 工作中是英文最好
_MainTex("材质放入点",2D) = "white"{}
_Specular("反射",Color) = (1.0,1.0,1.0,1.0)
_Gloss("光源程度范围",Range(8.0,256)) = 20
}
(5). 好,名字,属性,写完,要写语义块了,这里因为我们要使用光照给予材质一些效果,所以我们在Tags里填写模式,这玩意就相当于在各种游戏里的职业,假设一个游戏里你是农民的职业,那么你不仅可以用通用的技能,还能多个种田技能;你是商人的职业,你没有农民的种田技能,但是你会经商,可以用通用的技能,一样的道理。那么既然我们要使用光照,我们就需要选择这个**“职业”**,光照!
SubShader
{
Pass
{
Tags{ “LightMode” = “ForwardBase” }
(6). 然后我们需要使用HLSL里的代码块,所以我们的内容需要写在CGPROGRAM和ENDCG之间,然后定义出来,片元着色器和表面着色器,便于后面进行使用。
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
(7). 接下来,选完**“职业”了,还要选“装备”**,#include里的内容就是各种各样强大的装备内容,可以让你有额外的技能可以使用,我这么说有点不严谨,严谨的说这个是Unity内容的一些变量,这些变量可以让你使用,比如说_LightColor0,那么我们使用的话就得获取,所有主流代码中里获取的方法基本上类似,都是这类操作
#include"Lighting.cginc"
(8). Shader里凡是你在Properties里增加的属性,都需要在里面申明出来,否则只是一个名字没有它的意义
uniform fixed4 _Color ; //为啥定义颜色?因为Unity里的所有光照,实际上就是相当于按照光照的类型给物体上色而已,所以 Light = Color
uniform sampler2D _MainTex ; //这个sampler2D,顾名思义便是纹理的类型
uniform float4 _MainTex_ST //你会发现,我擦,这边咋多了一个ST,对,这个地方不是瞎起的,ST是缩放(Scale)和平移(Transformlation)的缩写,前面的名字你可以按你的想法改,当然要跟上面对应,取名字方法**纹理名_ST**。
uniform fixed4 _Specular ; //反射光
uniform float _Gloss ; //滑动条也要写进去,申明一下
(9). 接下来就可以使用,我们刚才定义完的表面着色器和片元着色器了,输入和输出是必不可少的。
struct VertexInput
{
float4 vertex : POSITION; //必须要有,你用顶点要转换到屏幕呢!
float3 normal : NORMAL; //既然有光照,就需要法线来进行计算
float4 texcoord : TEXCOORD0; //对,没错,存储纹理的,小伙子聪明~
};
struct VertexOutput
{
float4 pos : SV_POSITION; //这边是与vertex进行的对应,必须写!
float3 worldNormal : TEXCOORD0; //世界法线
float3 worldPos : TEXCOORD1; //世界纹理坐标
half2 uv:TEXCOORD2; //增加uv,以便在片元中用该坐标进行纹理采样
};
(10). 定义顶点着色器啦
VertexOutput vert(VertexInput v)
{
VertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex); //UnityObjectToClipPos这个是unity新版本后内置宏,以此来代替,mul(UNITY_MATRIX_MVP,v.vertex)
o.worldNormal = UnityObjectToWorldNormal(v.normal); //UnityObjectToWorldNormal也是一样,计算模型坐标转到世界坐标的法线
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; //世界纹理坐标,计算模型坐标转到世界坐标的法线
//o.uv = v.texcoord.xy * _MainTex_ST.xy+_MainTex_ST.zw; //这个为啥注掉一个,这个是本身的公式,v.texcoord.xy * _MainTex_ST.xy这个结果得出最终的纹理坐标,后面的就是你可以调整它进行偏移。注掉的原因是,下面这个是Unity的内置宏,当然你要使用它的话,得在前面增加一个#include"UnityCG.cginc"
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
(11). 接下来我们需要获得的系统参数都获得差不多了,最后我们要在片元着色器上计算纹素值还有光照:
fixed4 frag(VertexOutput i):SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal); //所有光照系统都需要建立在标准化的基础进行,不然计算会出错,计算世界法线的坐标
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); //同理计算世界光照方向的标准化参数
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)); //标准化一下+1,= =
fixed3 halfDir = normalize(worldLightDir+viewDir);//标准化一下+2,= =
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal,halfDir)),_Gloss); //以上是为了计算出反射做的铺垫,光照部分我就不强调了,之后会补上
return fixed4 (ambient+diffuse+specular,1.0); //加起来就是最终的光照了
}
(12).设置了一个合适的Fallback,但是对于新手村还没出来的学者而言,我不建议加,因为你一旦发现,哎我去怎么粉了,你会慢慢的回去找错误,这样学的会很快
Fallback“Specular”
好,这些内容就是基础纹理的步骤了,上面的代码不能全盘抄,会报错,所以下面附上完整版,但是我还是建议自己写一遍会比较好
Shader"ShaderLab/Lesson01_UV"
{
Properties
{
_Color("颜色",Color) = (1,1,1,1)
_MainTex("MainTex",2D)="White"{}
_Specular("反射",Color) = (1,1,1,1)
_Gloss("反射范围",Range(5.0,256))=20
}
SubShader
{
Pass
{
Tags{"LightMode"="ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"Lighting.cginc"
#include"UnityCG.cginc"
uniform float4 _Color;
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST; //这个地方要强调一下,因为这边是需要_ST的
uniform float4 _Specular;
uniform float _Gloss;
struct VertexInput
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct VertexOutput
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
half2 uv:TEXCOORD2; //增加uv,以便在片元中用该坐标进行纹理采样
};
VertexOutput vert(VertexInput v)
{
VertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
//o.uv = v.texcoord.xy * _MainTex_ST.xy+_MainTex_ST.zw;
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
return o;
}
fixed4 frag(VertexOutput i):SV_Target
{
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
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
}
}
}