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

Unity Shader入门精要 阅读笔记八

2018-01-30 23:26 423 查看

逐顶点光照

效果图



从效果里里可以看出,逐顶点光在边缘处有很多锯齿和凸起,并且在光暗分界线部分出现不平滑。这个问题是因为,在计算光照效果的时候,逐顶点光照其实是对每个顶点部分的颜色效果值进行计算,而中间部分则使用插值计算得到,所以会出现这些问题。在顶点密度较低的情况下,这个问题很明显,而在顶点密度较高的时候,这个问题就现实较好。

逐顶点光照shader脚本:

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader"Custom/2.1.1"{//逐顶点光照
Properties{
_Diffuse("Diffuse",Color) = (1.0,1.0,1.0,1.0)//定义了一个颜色属性,并给了一个初值
}
Subshader{
Pass{
Tags{"LightMode"="ForwardBase"}//添加标签,表示这个pass的光照渲染方式
//渲染通道,用来完整描述一个渲染流水线
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"

float4 _Diffuse;//需要定义一个名称相同的变量。与properties中的相同

//应用2顶点着色器
struct a2v {//名称不能自定义
//这里面定义顶点着色器的输入
//每个输入都需要填写类型,名称,语义
float4 v:POSITION;//每一个定义以分号结尾
float4 color:COLOR0;//输入一个纹理信息
float3 normal:NORMAL;
};//这里需要注意,需要有个分号

struct v2f {//顶点着色器与片元着色器之间传值的结构体
float4 v:SV_POSITION;//不可缺少,传递剪裁空间的顶点
float3 color:COLOR0;//输入颜色
};

//定义基本的顶点着色器的编程函数,以及片元着色器的编程函数。
v2f vert(a2v v)  {
//当有自定义结构体的时候,取消语义描述,因为语义已经在结构体中了
v2f o;
o.v = UnityObjectToClipPos(v.v);
//模型空间到剪裁空间。中间需要经历模型空间到世界空间,世界空间到齐次剪裁空间
//矩阵乘法,将模型空间中的点,转到齐次剪裁空间中。
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//获取环境光
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unit
4000
y_WorldToObject));
//这里为什么乘的是世界到模型?
//因为在之间的计算中,法线的变化不能直接使用变化矩阵,因为这样会是的法相不在垂直于切线
//需要将转换矩阵求逆,即世界到模型的转化矩阵的转置。如程序中显示。
//获得世界坐标系中的光线,并归一化处理
fixed3 light = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal , light));

o.color = diffuse + ambient;//环境光
return o;
}
float4 frag(v2f i) :SV_Target {//SVTarget是着色器的语义

return fixed4(i.color,1.0);
//定义所有的片元光栅化的颜色为白色。
}
ENDCG
}

}
}


这里最核心的是使用到了漫反射的计算。而在计算之前需要对所需要的数据进行处理。

首先是对法线和光照放置在同一坐标空间中,这里是放在了世界空间中。

对于法线进行转换的时候,需要注意,法线不能直接转化,需要使用模型空间到世界空间的转换矩阵的逆矩阵,因此需要乘_World2Object。

算法里将光源的颜色和漫反射的颜色相乘在乘上光照在垂直上的分量,就可以求出对应顶点的漫反射颜色值。

最后将漫反射与环境光相加得到最后的颜色。

逐像素光照

效果



可以看到逐像素在边缘处和逐顶点相比效果会更好一些。

shader脚本:

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader"Custom/2.1.2"{//逐顶点光照
Properties{
_Diffuse("Diffuse",Color) = (1.0,1.0,1.0,1.0)//定义了一个颜色属性,并给了一个初值
}
Subshader{
Pass{
Tags{"LightMode"="ForwardBase"}//添加标签,表示这个pass的光照渲染方式
//渲染通道,用来完整描述一个渲染流水线
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"

float4 _Diffuse;//需要定义一个名称相同的变量。与properties中的相同

//应用2顶点着色器
struct a2v {//名称不能自定义
//这里面定义顶点着色器的输入
//每个输入都需要填写类型,名称,语义
float4 v:POSITION;//每一个定义以分号结尾
float4 color:COLOR0;//输入一个纹理信息
float3 normal:NORMAL;
};//这里需要注意,需要有个分号

struct v2f {//顶点着色器与片元着色器之间传值的结构体
float4 v:SV_POSITION;//不可缺少,传递剪裁空间的顶点
float3 worldNormal:TEXCOORD0;//输入颜色
};

//定义基本的顶点着色器的编程函数,以及片元着色器的编程函数。
v2f vert(a2v v)  {//当有自定义结构体的时候,取消语义描述,因为语义已经在结构体中了
v2f o;
o.v = UnityObjectToClipPos(v.v);//模型空间到剪裁空间。中间需要经历模型空间到世界空间,世界空间到齐次剪裁空间
//矩阵乘法,将模型空间中的点,转到齐次剪裁空间中。

fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//这里为什么乘的是世界到模型?
//因为在之间的计算中,法线的变化不能直接使用变化矩阵,因为这样会是的法相不在垂直于切线
//需要将转换矩阵求逆,即世界到模型的转化矩阵的转置。如程序中显示。
o.worldNormal = worldNormal;
return o;
}
float4 frag(v2f i) :SV_Target {//SVTarget是着色器的语义
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//获取环境光
//获得世界坐标系中的光线,并归一化处理
fixed3 light = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(i.worldNormal, light));

return fixed4(diffuse + ambient,1.0);
//定义所有的片元光栅化的颜色为白色。
}
ENDCG

}
}
Fallback "Diffuse"
}


可以看到在脚本里面,光照的计算被放置到了片元着色器中,其他不变。

半兰伯特模型

效果图



半兰伯特模型在原有的基础上对散射渲染进行了一些修改。

对原来的散射计算中已有的强制将颜色范围[0,1],改成了先将范围缩小至-0.5-0.5,然后再偏移0.5,到[0,1]。

shader脚本

// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader"Custom/2.1.3"{//逐顶点光照
Properties{
_Diffuse("Diffuse",Color) = (1.0,1.0,1.0,1.0)//定义了一个颜色属性,并给了一个初值
}
Subshader{
Pass{
Tags{"LightMode"="ForwardBase"}//添加标签,表示这个pass的光照渲染方式
//渲染通道,用来完整描述一个渲染流水线
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"

float4 _Diffuse;//需要定义一个名称相同的变量。与properties中的相同

//应用2顶点着色器
struct a2v {//名称不能自定义
//这里面定义顶点着色器的输入
//每个输入都需要填写类型,名称,语义
float4 v:POSITION;//每一个定义以分号结尾
float4 color:COLOR0;//输入一个纹理信息
float3 normal:NORMAL;
};//这里需要注意,需要有个分号

struct v2f {//顶点着色器与片元着色器之间传值的结构体
float4 v:SV_POSITION;//不可缺少,传递剪裁空间的顶点
float3 worldNormal:TEXCOORD0;//输入颜色
};

//定义基本的顶点着色器的编程函数,以及片元着色器的编程函数。
v2f vert(a2v v)  {//当有自定义结构体的时候,取消语义描述,因为语义已经在结构体中了
v2f o;
o.v = UnityObjectToClipPos(v.v);//模型空间到剪裁空间。中间需要经历模型空间到世界空间,世界空间到齐次剪裁空间
//矩阵乘法,将模型空间中的点,转到齐次剪裁空间中。

fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//这里为什么乘的是世界到模型?
//因为在之间的计算中,法线的变化不能直接使用变化矩阵,因为这样会是的法相不在垂直于切线
//需要将转换矩阵求逆,即世界到模型的转化矩阵的转置。如程序中显示。
o.worldNormal = worldNormal;
return o;
}
float4 frag(v2f i) :SV_Target {//SVTarget是着色器的语义
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;//获取环境光
//获得世界坐标系中的光线,并归一化处理
fixed3 light = normalize(_WorldSpaceLightPos0.xyz);
fixed3 halfLambert = dot(i.worldNormal, light) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;

return fixed4(diffuse + ambient,1.0);
//定义所有的片元光栅化的颜色为白色。
}
ENDCG
}
}
Fallback "Diffuse"
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: