Shader "Custom/TestShader" {
Properties {
_MainTex("主贴图",2D) = "white" {}
_Eliminate("剔除",range(0,1)) = 0
_Strength("亮度",range(0,30)) = 1
_Color("color",color) = (1,1,1,1)
}
SubShader {
Tags { "RenderType"="Opaque" }
// Blend SrcAlpha OneMinusSrcAlpha // 这个是透明混合
// 透明混合说明 http://blog.sina.com.cn/s/blog_471132920101d8z5.html
LOD 200
pass{
CGPROGRAM
#include "unityCG.cginc"
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
float _Eliminate;
fixed4 _Color;
float _Strength;
struct v2f {
float2 uv : TEXCOORD;
float4 vertex : POSITION;
};
fixed luminance(fixed4 color) // 计算片元亮度
{
return 0.2125 * color.r + 0.7154 * color.g + 0.0721 * color.b;
}
v2f vert(appdata_img v)
{
v2f o;
o.uv = v.texcoord;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
half4 frag(v2f v) : SV_Target
{
fixed4 color = tex2D(_MainTex,v.uv) * _Strength;
clip(color - _Eliminate);
clip(color.a + color.g + color.b -0.9);
return lerp(color,_Color,_Eliminate);
}
ENDCG
}
}
FallBack "diffuse"
}
Shader "Custom/TestShader" {
Properties {
_MainTex("主贴图",2D) = "white" {}
_Eliminate("剔除",range(0,1)) = 0
_Strength("亮度",range(0,30)) = 1
_Color("color",color) = (1,1,1,1)
}
...
sampler2D _MainTex;
float _Eliminate;
fixed4 _Color;
float _Strength;
shader "" 也就是第一行,代表的是这个shader的位置(文件夹/shader名)
第二行之后的大括号代表的则是这个shader里面的一些声明,但是这些只是声明,类似于c++里面的include.c,对,是.c而不是.cpp,实现还需要再次在下面重新声明,真正用到的是下面的声明,而上面的则是显示到面板上的一些参数,如果下面没有同样的参数重载,则本参数无效
SubShader {
// Tags 的一些说明 https://blog.csdn.net/treepulse/article/details/53484505
Tags { "RenderType"="Opaque" } // 标签,如同程序里面的Attribute属性一样,指定这个shader可以拥有什么属性
LOD 200
pass{ // 一个渲染代码块 一个shader可以拥有很多个pass 其实看来有些类似于代码中的多态,
//但是在一些情况下我可以将一个参数(贴图) 通过一个子类进行参数的变化 在传入另一个子类的多态中 就是通常所说的多pass渲染
CGPROGRAM // Unity的Cg程序预编译命令
#include "unityCG.cginc" // 头文件 路径为 C:\Program Files\Unity\Editor\Data\CGIncludes
#pragma vertex vert // 指定为 顶点 片元渲染
#pragma fragment frag
ENDCG
struct v2f {
float2 uv : TEXCOORD; // 第一个贴图 获取其他的贴图可以 用TEXCOORDX //x为数值
// 这个的解释在 https://blog.csdn.net/zhao_92221/article/details/46797969
float4 vertex : POSITION; // 代表每个像素点在屏幕上的位置
};
/*
struct appdata_img //结构体原型
{
float4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
*/
v2f vert(appdata_img v) // 函数体 顶点着色器 逐顶点进行操作
{
v2f o; // 初始化结构体
o.uv = v.texcoord; // 给我们初始化的结构体赋值
o.vertex = UnityObjectToClipPos(v.vertex); // 将顶点变换到裁剪空间 也就是相机可以看到的区域
// 等同于 mul(UNITY_MATRIX_MVP,v.vertex); mul 矩阵相乘
return o;
}
half4 frag(v2f v) : SV_Target // 片元着色器 逐像素进行操作 那个SV_Target暂时没有找到相关定义 但是我尝试省略的时候 会爆出一个 返回值错误
{
fixed4 color = tex2D(_MainTex,v.uv) * _Strength; // tex2D(sampler2D,float2) 获取uv坐标对应的颜色数值,后面的相乘则是加强亮度
clip(color - _Eliminate); // clip(float - float) // 如果最终数值小于0 则将这个像素点舍弃
clip(color.a + color.g + color.b -0.9); // 黑色代表的是0,0,0 这个是舍弃黑色 也可以使用discard; 同样都是舍弃本像素,但是discard需要自己手动判断
return lerp(color,_Color,_Eliminate); // 插值
}
也就是说,一个标准的顶点片元着色器的书写格式为
Shader "FolderName/ShaderName"
{
Properties
{
显示在面板上的变量,注意,变量必须要有初始值
如 :
变量名("显示在面板上变量的名字",变量的类型) = 初始值
_Color("color",color) = (1,1,1,1)
}
SubShader
{
Tags { Attribute (一些需要的属性) 如-> “Queue”="Transparent" // 渲染优先级 Transparent == 3000}
Zwrite Off Blend SrcAlpha OneMinusSrcAlpha 一些其他的设置 如是否进行深度写入,透明混合等
pass // 注意,每个顶点片元shader至少要有一个pass
{
CGPROGRAM // CG预编译指令,没有则抛出异常
#include ... 头文件声明位置
#pragma vertex vert // 顶点函数
#pragam fragment frag // 片元函数
fixed4 _Color; 变量的声明位置,注意于上面变量名和参数的一致
struct XXX 结构体位置,结构体更多的是为了顶点函数和片元函数之间进行参数的传递
{
float2 uv : TEXCOORD; 第一个纹理
float4 vertex : POSITION; 像素的位置
};
顶点函数位置,shader是类C++写法,所以在写一些计算函数的时候要注意书写顺序
appdata_img 是unity里面自带的一个结构体,当unity调用这个shader时,会将相应的参数直接传递进来
v2f vert(appdata_img v)
{
v2f o;
方法体,其实就是处理v,并且给v2f赋值
return o;
}
SV_Target 为语义,注意上面的结构体,我们在声明的时候都会在后面添加上相应的语义,目的则是告诉GPU
我这个参数时干嘛的,它拥有什么意义,否则的话,就拿上面的uv来说,GPU根本无法识别一个float[2]的数组是什么意思
同样的,还有POSITION COLOR等
参考 -> https://shenjun4shader.github.io/shaderhtml/1.%20ShaderLab%20%E6%A1%86%E6%9E%B6/12-shader-%E8%AF%AD%E4%B9%89%EF%BC%88semantics%EF%BC%89.html
fixed4 frag(v2f v) : SV_Target
{
片元代码块,处理最终显示的像素
return (1,1,1,1);
}
ENDCG
}
}
FallBack "diffuse"; 如果这个shader因为目标配置等问题无法使用,则使用其他指定shader
}
=======================================函数的实验========================================
mul(UNITY_MATRIX_MVP,v.vertex); //效果如下
对,如果我没有变换坐标到裁剪空间,他会直接变成这样,只要相机可以看到物体,这个材质就会一直出现在屏幕正中心
Clip的效果图如下
一些内置的变量说明(这里暂时就只我用到过的,或者我在更新这篇博客的时候想起来的 - - )
以下变量为我本身使用或是书籍获得,所以有可能有多重参数重载
-----------------------------------------------------------------------------------------
#UNITY_SHADER_NO_UPGROUP 1 禁止unity自动优化,比如MVP会被优化为 objecxxx,不过偶尔有用,不知道为啥
Cull Back || Front 宏指令,放在pass最前方,指定这个shader是否渲染背面 || 正面 有时候我们不用Zwrite 时,可以考虑一下这个
Clip(float - float) 如果里面的表达式在计算之后小于0,则直接忽略这个像素,clip后的语句不在执行
mul(x , y) 矩阵相乘,必须满足矩阵乘法,一般用于将某个矩阵变换到另一个空间,比如世界空间到裁剪空间
UnityObjectToClipPos(float4) 将目标变换到裁剪空间 等同于 mul(UNITY_PATRIX_MVP , float4)
dot(float3 , float3) 计算两个向量的夹角,比如我们需要光照的强度,那么就可以使用点乘来进行计算本像素是否应该受到完整的光照
unity_WorldToObject float4*4的矩阵,一般用在矩阵相乘里,用以将模型坐标从世界空间变换到模型空间
unity_ObjectToWrold 同上,用以将模型坐标到世界坐标的变换矩阵
normalize 归一化,如果我们只需要方向,而不需要长度可以使用
_WorldSpaceLightPos0 获取到世界光照的方向,多光源暂时没涉及,不知道他这个0用处 - -
saturate(float) 程序里面的Clamp,将一个数值限制在 0 - 1
reflect(x,y) x = 入射方向 ,y = 法线方向->获取反射方向->如果本物体自发光,则返回光线进入本物体后,本物体应该
反射光线的方向 《Shader入门精要 P.134出现》
pow(floatNum,float) 第一个参数的第二个参数的次方,
_WorldSpaceCameraPos 相机在世界空间的位置,我们可以很简单的相减来获取某个向量对比相机的方向
tex2D(_MainTex,uv.xy)获取普通贴图像素,后面的uv则是顶点传递给片元的数据
UnpackNormal 获取法线贴图,这个略微有点不一样,他需要先对法线贴图进行一个tex2D操作,对这个值进行取法线操作,目前没看源码不知道为什么(估计看也看不懂 = =),例如:
{
float4 normalColor = tex2D(_NormalTex,v.uv.xy);
fixed3 normal = UnpackNormal(normalColor);
}
===========================================================================================
头文件 Lighting.cginc
===========================================================================================
fixed3->UNITY_LIGHTMODLE_AMBIENT 周围的环境光,如果周围又可以自发光的物体并且本物体在影响范围,则可以直接获取到受到间接光照的强度和颜色
_LightColor0 获取到光照颜色,多光源暂时没涉及,不知道他这个0用处 - -
=============================================未完。。。========================================