**
小结
**
知乎:善于学习并记住别人归纳好的知识点或者经验,同时失去了自己分析解决问题的能力。这些人面对问题的第一个反应就是我该到哪里去学习(咨询),而不是尝试用自己的知识去解决它。
上面这句话是我今天看到的,虽然不一定全是对的,但是我确实该好好反思反思!
今天主要重新看了一遍关于矩阵方面的知识。深刻的感觉到了数学就是个坑,昨天看代码的时候因为觉得线代以前学过一点就跳过了书前面的数学部分。今天重新把书里关于矩阵的部分看了一遍,再去看代码,总算是看的清楚多了。首先关于昨天的问题为什么只有反射的颜色数据? 我觉得是因为在使用兰伯特定理时。计算的就是反射光的颜色,color的第四个数据,在片元着色器输出时手动补上1了。
模型空间到裁剪空间的变换细节?
下面这张图就是描述这个过程了 模型坐标为参考系的顶点坐标在通过了顶点着色器之后输出的就是以裁剪空间为参考系的坐标。昨天的时候,我以为这个过程是一步到位的。(看起来确实是如此)但是实际上经过了M ,V,P 3个变换矩阵将矩阵合成一个MVP变换矩阵。(例如:模型空间到世界空间是通过M矩阵)此外还要注意
裁剪空间坐标=MVP*模型空间坐标,这是一个向左乘的过程!
以上是关于顶点坐标的变换。法线从一个空间变换到另一个空间,我们是采用顶点变换矩阵的【逆】【转置】矩阵。
shader学习
兰伯特定律:反射光线的颜色与表面法线和光源方向之间夹角的余弦值成正比。(注意:前提是光源的方向应该不依赖于物体的位置)
标准光照模型:
【环境光】:全局变量 【自发光】:材质自发光颜色 ,且不当做光源
【漫反射】:(描述当光线从光源照射到模型表面时,该表面会向每个方向散射多少”辐射量“)
逐顶点光照:对于细分程度较低的模型,会导致在交界处产生锯齿。
逐像素光照:可以得到更加平滑的效果,改善不明显。
以下是标准光照模型下的以逐顶点方式实现漫反射模型(读起来好像怪怪的,其实这还是昨天的代码,不过今天继续吃,感觉还是有很多收获的)
Shader “Myshader” { //标准光照模型之逐像素漫反射实现
Properties{
_Diffuse(“Diffuse”, Color) = (1, 1, 1, 1)
}
SubShader{
Pass {
Tags { “LightMode” = “ForwardBase” }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v {
float4 vertex : POSITION; //顶点坐标
float3 normal : NORMAL; //表面法线
};
struct v2f {
float4 pos:SV_POSITION; //剪辑空间坐标
float3 normal:TEXCOORD0; //表面法线 如果我们需要把一些自定义的数据从顶点着色器传递到
};//如果我们需要把一些自定义的数据从顶点着色器传递到片元着色器,一般选用TEXCOORD0
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 将顶点坐标变换到剪辑空间
o.normal = v.normal; //获得表面法线
return o;
}
fixed4 frag(v2f i) : SV_Target{
// 漫反射颜色=【入射颜色】*【材质漫反射系数(颜色)】*【max(0,法线方向与光源方向的余弦值)】max是防止余弦值为负数;
//(世界坐标)表面法线=【顶点模型空间—剪切空间变换矩阵MVP的逆转置矩阵】*【表面法线(模型左边)】
float3 world_light_normal = normalize(mul(i.normal,(float3x3)unity_WorldToObject));
// 计算表面法线在世界坐标中的表示
float3 world_source_normal = normalize(_WorldSpaceLightPos0.xyz);
// 计算光源方向在世界坐标中的表示
float3 diffuse = _LightColor0.rgb*_Diffuse.rgb*saturate(dot(world_light_normal, world_source_normal));
// 计算漫反射颜色(公式见上述)
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 定义变量ambient获得环境光参数
return fixed4(ambient + diffuse,1.0); //输出;需要转换格式
}
ENDCG
}
}
FallBack "Diffuse"
}
--------------------------------------------------------------------------分界线----------------------------------------------
Shader "逐像素漫反射" { //标准光照模型之逐像素漫反射实现
Properties{
_Diffuse("Diffuse", Color) = (1, 1, 1, 1)
}
SubShader{
Pass {
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse;
struct a2v {
float4 vertex : POSITION; //顶点坐标
float3 normal : NORMAL; //表面法线
};
struct v2f {
float4 pos:SV_POSITION; //剪辑空间坐标
float3 normal:TEXCOORD0; //表面法线 如果我们需要把一些自定义的数据从顶点着色器传递到
};//如果我们需要把一些自定义的数据从顶点着色器传递到片元着色器,一般选用TEXCOORD0
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 将顶点坐标变换到剪辑空间
o.normal = v.normal; //获得表面法线
return o;
}
fixed4 frag(v2f i) : SV_Target{
// 漫反射颜色=【入射颜色】*【材质漫反射系数(颜色)】*【max(0,法线方向与光源方向的余弦值)】max是防止余弦值为负数;
//(世界坐标)表面法线=【顶点模型空间—剪切空间变换矩阵MVP的逆转置矩阵】*【表面法线(模型左边)】
float3 world_light_normal = normalize(mul(i.normal,(float3x3)unity_WorldToObject));
// 计算表面法线在世界坐标中的表示
float3 world_source_normal = normalize(_WorldSpaceLightPos0.xyz);
// 计算光源方向在世界坐标中的表示
float3 diffuse = _LightColor0.rgb*_Diffuse.rgb*(0.5*dot(world_light_normal, world_source_normal)+0.5);
// 计算漫反射颜色(公式见上述)
float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 定义变量ambient获得环境光参数
return fixed4(ambient + diffuse,1.0); //输出;需要转换格式
}
ENDCG
}
}
FallBack "Diffuse"
}
Shader “高光逐顶点”
{
Properties
{
_Diffuse(“Diffuse”,Color) = (1,1,1,1)
_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 "UnityCG.cginc"
#include "Lighting.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct appdata
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION;
fixed3 color : COLOR;
};
v2f vert (appdata v)
{/*在顶点着色器中计算高光反射模型。环境光颜色+漫反射颜色+高光反射颜色
漫反射颜色=【入射光颜色】*【材质漫反射颜色(_Diffuse)】*【光源方向与法线方向的余弦值(非负)】
高光反射颜色=【入射光颜色】*【材质高光反射系数】*【视角方向及反射方向余弦值的(Gloss)幂】
*/
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT;
fixed3 world_ver_direction = normalize(UnityObjectToWorldDir(v.normal));
fixed3 world_light_direction = normalize(_WorldSpaceLightPos0.xyz);
float3 diffuse = _LightColor0.rgb*_Diffuse.rgb*(0.5*dot(world_ver_direction, world_light_direction) + 0.5);
float3 r= normalize(reflect(world_light_direction, world_ver_direction)); //反射方向
float3 m = normalize(WorldSpaceViewDir(v.vertex));
fixed3 specular = _LightColor0.rgb*_Specular.rgb*(pow(saturate(dot(m, r)), _Gloss));
o.color = ambient + diffuse + specular;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return fixed4(i.color,1);
}
ENDCG
}
}
}
Shader “高光逐像素”
{
Properties
{
_Diffuse(“Diffuse”,Color) = (1,1,1,1)
_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
// make fog work
#include "Lighting.cginc"
#include "UnityCG.cginc"
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
struct appdata
{
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f
{
float4 pos:SV_POSITION; //裁剪空间坐标
float3 normal:TEXCOORD0; //法线
float4 poss:TEXCOORD1; //顶点坐标
};
v2f vert (appdata v)
{
v2f o;
o.pos =UnityObjectToClipPos( v.vertex); //顶点坐标转换到裁剪空间
o.normal = normalize(UnityObjectToWorldNormal(v.normal));// 世界坐标系下的法线(并归一化处理)
o.poss = v.vertex; //将顶点坐标传给片元
return o;
}
fixed4 frag (v2f i) : SV_Target
{ //计算高光反射颜色,漫反射颜色,并输出最终反射颜色
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT;
fixed3 world_light_normal = i.normal;
// 世界坐标系的法线方向
fixed3 world_source_light = normalize(_WorldSpaceLightPos0.xyz);
// 世界坐标系光源入射方向
fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*(0.5*dot(world_light_normal,world_source_light)+0.5);
//漫反射颜色计算
fixed3 j = UnityObjectToWorldDir(normalize(ObjSpaceLightDir(i.poss))); //世界坐标系光源的方向(入射)
fixed3 r = reflect(j,i.normal); //高光的反射方向
fixed3 v = normalize(WorldSpaceViewDir(i.poss)); //视角方向
float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(v, r)),_Gloss); //高光颜色计算
float3 ALL = specular + diffuse + ambient; // 反射颜色的计算
return fixed4(ALL, 1);
}
ENDCG
}
}
}
总的来说逐像素的效果是优于逐顶点方式的(边缘的锯齿较为明显),采用基本光照模型漫反射的计算公式容易造成背面无明暗变化。所以可以采用半兰伯特光照模型(这图不知道对不对啊,大概就这个意思,我没有标注)
高光模型
精读的收获:
当使用逐顶点处理时,片元着色器应有颜色数据的输入,当使用逐像素处理时,顶点着色器传给片元着色器的应该是裁剪空间的坐标(不可直接将顶点坐标传过去)
要注意数据的精度问题 fixed4 fixed3
在使用Unity的内置函数时要导入相应的包
理解顶点着色器输入,输出结构体的定义【重要!!】 体会数据是怎么流动的。
明天是国庆最后一天,考虑得写下作业了。那我就稍微练一下max操作吧。加油!