OpenGL DirectX
OpenGL 使用GLSL编写shader
DirectX 使用HLSL编写shader
英伟达 使用CG编写shder
shader分类
1.表面着色器 surface shader
2.顶点/片元着色器 Vertex/Fragment shader
3.固定函数着色器 Fixed Function shader
shader基本代码
Shader "ym/ymshader01"{//此处是指定shader的名字,不要求跟文件名一致,且在选择shader时会按照这个路径选择
Properties{
//属性
// _Color是在接下来的subshader里面用到
//color是在unity里面的名字
//Color则是属性类型名
//其他属性都是这种命名格式
_Color("color",Color)=(1,1,1,1)
_Vector("vector",vector)=(1,2,3,4)
}
//子Shader,可以有很多个
//显卡运行效果的时候,会从第一个SubShader进行判断,如果第一个效果都可以实现,那么就使用第一个
//如果显卡对于当前SubShader一些效果实现不了,会自动去判断下一个子Shader
SubShader{
//必须要有一个pass
pass{
//要在CGPROGRAM和ENDCG之间用CG语言编写shader代码
CGPROGRAM
ENDCG
}
}
//如果所有的SubShader都无法实现,便实现此Shader
Fallback "VertexLit"
}
常见属性类型
Properties{
//属性
// _Color是在接下来的subshader里面用到
//color是在unity里面的名字
//Color则是属性类型名
//其他属性都是这种命名格式
_Color("color",Color)=(1,1,1,1)
_Vector("vector",vector)=(1,2,3,4)
//常见的属性类型
//int类型
_int("int",int)=123
//float类型
_float("float",float)=2.3
//范围类型 限定好了范围
_range("range",Range(1,11))=3
//2D图片类型 前面的"red"是默认颜色
_2d("texture",2D)="red"{}
//cube类型 ??
_cube("cube",Cube)="white"{}
//3D类型
_3d("3d",3D)="black"{}
}
属性的使用
属性在用之前,要再定义一遍
//子Shader,可以有很多个
//显卡运行效果的时候,会从第一个SubShader进行判断,如果第一个效果都可以实现,那么就使用第一个
//如果显卡对于当前SubShader一些效果实现不了,会自动去判断下一个子Shader
SubShader{
//必须要有一个pass
pass{
//要在CGPROGRAM和ENDCG之间用CG语言编写shader代码
CGPROGRAM
//属性在用之前,要再定义一遍
float4 _Color;
float4 _Vector;
float _int;
float _float;
float _range;
sampler2D _2d;
sampler3D _3d;
samplerCUBE _cube;
ENDCG
}
}
小tips
fixed half float 基本可以互换,只不过float范围大于half大于fixed
顶点函数与片元函数
Shader "ym/shader02"{
SubShader{
pass{
CGPROGRAM
//声明顶点函数的函数名
//顶点函数作用:完成顶点坐标从模型空间到剪裁空间的转换(即从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
//声明片元函数的函数名
//片元函数作用:返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
//可以暂时用xxx表示返回值与参数
xxx vert(xxx){
}
xxx frag(xxx){
}
ENDCG
}
}
Fallback "VertexLit"
}
完善顶点函数
//:表示语义
// float4 v : POSITION 表示参数需要的是POSITION
//: SV_POSITION 表示返回值是剪裁空间下的顶点坐标
float4 vert(float4 v : POSITION) : SV_POSITION{
//此功能需要使用unity写好的一个矩阵来实现
return mul(Unity_Matrix_MVP,v);
}
完善片元函数
此处可以有能看得到的效果了
fixed4 frag() :SV_TARGET{
return fixed4(0.5,1,0.5,1);
}
法线方向和纹理坐标
//结构体,传参时便可以传入多个参数
struct a2v{
float4 vertex:POSITION;//语义是将顶点坐标给vertex变量
float3 normal:NORMAL;//法线方向
float4 texcoord0:TEXCOORD0;//第一套纹理坐标
}
//:表示语义
// a2v a2v 使用结构体传参
//: SV_POSITION 表示返回值是剪裁空间下的顶点坐标
float4 vert(a2v a2v) : SV_POSITION{
//此功能需要使用unity写好的一个矩阵来实现
return mul(Unity_Matrix_MVP,a2v.vertex);
}
此处踩了一个坑,该加分号的地方不要忘记
片元函数与顶点函数之间数据传递
此处采取将顶点函数获得的法线方向传给片元函数并展示成颜色的形式
Shader "ym/shader01"{
SubShader{
pass{
CGPROGRAM
//声明顶点函数的函数名
//顶点函数作用:完成顶点坐标从模型空间到剪裁空间的转换(即从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
//声明片元函数的函数名
//片元函数作用:返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
//结构体,传参时便可以传入多个参数
struct a2v{
float4 vertex:POSITION;//语义是将顶点坐标给vertex变量
float3 normal:NORMAL;//法线方向
float4 texcoord0:TEXCOORD0;//第一套纹理坐标
};
struct v2f{
float4 position:SV_POSITION;//顶点坐标
float3 temp:COLOR0;//法线的方向
};
//:表示语义
// a2v a2v 使用结构体传参
//: SV_POSITION 表示返回值是剪裁空间下的顶点坐标
v2f vert(a2v v){
v2f f;
//此功能需要使用unity写好的一个矩阵来实现
//f.position = mul(Unity_Matrix_MVP,a2v.vertex);
f.position= UnityObjectToClipPos(v.vertex);
f.temp = v.normal;
return f;
}
fixed4 frag(v2f f) :SV_TARGET{
return fixed4(f.temp,1);
}
ENDCG
}
}
Fallback "VertexLit"
}
此处有个坑:mul(Unity_Matrix_MVP,a2v.vertex);不能用了,要改成UnityObjectToClipPos(v.vertex);
标准光照模型
光照模型是一个公式,使用这个公式来计算在某个点的光照效果
光分为四部分:
自发光
高光反射
漫反射
环境光
漫反射公式
Diffuse = 直射光颜色 * max(0,cos(光和法线的夹角))
其中,夹角的计算是使用点乘来得到的,即
同时还需要定义正确的LightMode来得到Unity的内置光照变量
Tags{"LightMode" = "ForwardBase"}
CGPROGRAM
#include "Lighting.cginc"
Tags{“LightMode” = “ForwardBase”}在pass内部,CGPROGRAM外部
#include "Lighting.cginc"在CGPROGRAM内部