我之前的写过一个关于渲染流水线,接下来聊聊shader。 shader的数据类型和c#是有一些相似的,我下面的Properties里面写了几个类型(这个函数里面写的类型都会在unity里面以显示的形式展现出来,下面一个图片就是在unity界面里面所展示的一个效果),只是列举了几个常用的类型。然后它还有一些其他的类型,我写在Pass函数里面,因为这些类型是不去显示的。 首先在unity里面定义一个shader,在Proect中右键选择Shader下的StandardSurfaceShader这个shader是基本的shader框架,其他的几个都是不同的shader框架,所有我们选择基本就可以。 shader有两个必须有的函数Properties只能定义一个,它是用来和用户进行一个交互方式存在,可以显示在Inspector里面,让用户进行选择以及设置一些值,还有一个是SubShader,这个函数可以定义很多个,但一般情况下是只用一个,如果定义多个的情况下就是当前显卡不支持第一个SubShader,然后一次筛选后面的SubShader选择当前电脑显卡所支持的SubShader。然后还会有一个备用的显示方式–Fallback “VertexLit”。我在下面也加了备注,这是一种后备方案。
Shader "MyShaderOne"
{
Properties{
_Color("color",Color)=(1,1,1,1)
_Verctor("Verctor",Vector)=(1,2,3,4)
_Int("Int",Int)=123
_Float("Float",Float)=4.5
_Range("Range",Range(1,20))=5
_2D("Texture",2D)="white"{}
_Cube("Cube",Cube)="while"{}
_3D("Texure",3D)="black"{}
}
SubShader
{
Pass
{
CGPROGRAM
float4 _Color;
float4 _Vector;
float _Int;
float _Range;
sampler2D _2D;
samplerCube _Cube;
sampler3D _3D;
ENDCG
}
}
Fallback "VertexLit"
}
上面对shader的一些基本数据有了一个了解之后我们再了解一下必须要有的两个函数(#pragma vertex vert)、(#pragma fragment frag)这两个是必须要定义的,它们是进行一些shader功能的必要函数,在vertex 中会进行定点函数的处理,就是对于买一个定点数据的处理,fragment 是对于片元的处理,可以参考渲染流水线的几何阶段。 在函数定义的方法里面,有人会想玩什么会加一个:还有后面的参数名是个什么意思,首先我们第一个函数(float4 vert(float4 v:POSITION) :SV_POSITION)POSITION他是一个宏,相当于将物体的数据传入函数中变成float4 v的参数,然后括号后面的SV_POSITION是类似以返回值,在前面将参数获取到之后得返回回去,不然我们获取、计算值就没有什么意义,所以在函数定义前面会加一个float4,这个相当于c#中定义函数会写一个返回值类型意义下面这个函数。 下面这个函数中SV_Target 也是一样的,将片元的参数返回到物体中,让物体显示出来。在下面我们返回了一个float4的值,物体就会将这个值进行转换成颜色,然后显示出来,这个参数会显示出来下面这个物体的颜色。
Shader "MyShaderTwo"
{
SubShader
{
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) :SV_POSITION {
return UnityObjectToClipPos(v);
}
float4 frag() : SV_Target {
return float4(0.5,0.5,1,1);
}
ENDCG
}
}
Fallback "VertexLit"
}
然后会有人想之前说的Properties为什么一直没有用,因为上面只是对shader让你有一个基本的了解,得有金刚钻才能揽瓷器活。所以在写shader之前,要先将框架有一个基本的了解,以及知道每一个步骤是干什么的,为什么要这么定义,以及一些必要的宏的使用方法。 当了解了框架之后我们来了解一下基本的逻辑处理。
Shader "MyShaderFour"
{
Properties{
_Diffuse("Diffuse Color",Color) = (1,1,1,1)
}
SubShader{
Pass{
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#include "Lighting.cginc"
#pragma vertex vert
#pragma fragment frag
fixed4 _Diffuse;
struct a2v {
float4 vertex:POSITION;
float3 normal:NORMAL;
};
struct v2f {
float4 position:SV_POSITION;
fixed3 color : COLOR;
};
v2f vert(a2v v){
v2f f;
f.position = UnityObjectToClipPos(v.vertex);
fixed3 normalDir = normalize( mul(v.normal,(float3x3) unity_WorldToObject));
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * max(dot(normalDir, lightDir), 0)*_Diffuse.rgb;
f.color = diffuse;
return f;
}
fixed4 frag(v2f f): SV_Target {
return float4(f.color,1);
}
ENDCG
}
}
Fallback "VertexLit"
}
上面我所写的这个函数就是对shader的一个基础的编写,首先Properties中定义的是一个对于颜色的修改,在我们的Inspector里面会出现下面这个图片中红框所标记的。 然后下面(Tags{“LightMode” = “ForwardBase”})与(#include “Lighting.cginc”)是获取场景中直射光的数据在后面的函数中科院使用。在Properties中定义的_Diffuse需要在Pass里面定义一个(fixed4 _Diffuse;)才可能在线面的函数中使用,不然会报错。然后定义了(a2v)函数和(v2f)函数是通过这两个函数进行传值的一些操作,用函数会比直接传值增加效率。 对于定点函数(vert)中的一些方法具体是干什么的,我在函数里面也做了相对应的注释,在最后(f.color = diffuse; )就是将值进行一个赋值可以使这个值在下面的片元函数(frag)中使用。 在片元函数中会有人想一个float4的值为什么float3的值也可以用,还有(f.color)后面的,1是干什么的,其实在上面我们定义的(f.color)是一个现对于unity中(Vector3)的一个参数,但我们在Properties中定义的(_Diffuse)是一个float4的值相当于unity中(Quaternion)是有四个参数的,所以我们在(f.color)后面加了一个(,1)是补全他们所相差的一个参数值。 上述所说三个基本案例只是对shader的一个基本了解,仅限于入门,shader更重要的对于数学的一个逻辑思维能力,以及算法的研究,对于定点函数以及片元函数而言,片元函数所执行的次数会更多。
|