最近在学习shader 由于是为了2d游戏 所以不用太深
今天看了冯乐乐那本入门精要前五章就开始上手
没耐心老毛病了
半复制半改出了一个shader
来逐句分析下 加强自己的理解
由于是初学 也希望大家能指出我理解上的问题 谢谢
首先代码如下
Shader "Custom/basicGoGrey"
{
Properties{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {
}
_Red("Red", Float) = 0.21
_Blue("Blue", Float) = 0.07
_Green("Green", Float) = 0.72
}
SubShader
{
Blend SrcAlpha OneMinusSrcAlpha
Pass{
CGPROGRAM
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma vertex vert
#pragma fragment frag
struct a2v{
float4 vertex :POSITION;
float4 texcoord :TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
v2f vert(a2v IN) {
v2f OUT;
OUT.pos = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
return OUT;
}
sampler2D _MainTex;
float _Red;
float _Blue;
float _Green;
fixed4 frag(v2f IN) : SV_Target{
fixed4 colorTex = (tex2D(_MainTex, IN.texcoord));
colorTex.r = colorTex.g = colorTex.b = colorTex.r * _Red + colorTex.b * _Blue + colorTex.g * _Green;
return colorTex;
}
ENDCG
}
}
FallBack "Diffuse"
}
效果
我们首先全局看看
分两块Properties (属性)和 subShader(shader“本身”)
subShader中
Blend SrcAlpha OneMinusSrcAlpha
Blend SrcAlpha设置了混合效果
没有的话就会
接下来
Pass{
CGPROGRAM
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma vertex vert
#pragma fragment frag
struct a2v{
float4 vertex :POSITION;
float4 texcoord :TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
v2f vert(a2v IN) {
v2f OUT;
OUT.pos = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
return OUT;
}
sampler2D _MainTex;
float _Red;
float _Blue;
float _Green;
fixed4 frag(v2f IN) : SV_Target{
fixed4 colorTex = (tex2D(_MainTex, IN.texcoord));
colorTex.r = colorTex.g = colorTex.b = colorTex.r * _Red + colorTex.b * _Blue + colorTex.g * _Green;
return colorTex;
}
ENDCG
}
是真正的主体部分 CGPROGRAM 和 ENDCG 是CG代码片段
1
#include "UnityCG.cginc"
#include "UnityUI.cginc"
用很C语言的方式 引用了常用的Unity内置方法和变量 但首先很多方法变量都会自动引入 另外这个例子并没有用到引用的东西
2
#pragma vertex vert
#pragma fragment frag
vertex是顶点着色器 fragment是片元着色器 我目前的理解是
“vertex着色器进行矩阵变换位置,计算光照公式生成逐顶点颜色,生成/变换纹理坐标”
“fragment色器的作用是处理由光栅化阶段生成的每个片元,最终计算出每个像素的最终颜色”
啊我的感觉就是
顶点着色器拿到图片 把图片信息转换为很多个数据块(片元) 每个数据块都带有关于“某个点”的信息 包括位置深度等
然后片元着色器得到一个数据块(叫片元)然后处理这一个个数据块来呈现在屏幕上
啊大概吧 先这样理解吧
3
struct a2v{
float4 vertex :POSITION;
float4 texcoord :TEXCOORD0;
};
struct v2f{
float4 pos : SV_POSITION;
half2 texcoord : TEXCOORD0;
};
这里声明了两个方便顶点着色器和片元着色器操作的结构
我们从中看出这两个着色器所处理产出的数据
a2v :a to v 表示从应用到vertex着色器 其中包含
POSITION 表示模型的顶点坐标
TEXCOORD0 模型的第一套纹理坐标
另外说一下 这种声明格式
Type Name : Semantic
用Semantic语义来告诉某数据(比如模型的顶点坐标)放进这个名字(比如vertex)的变量里。
再说v2f 自然是顶点着色器到片元着色器的结构
SV_POSITION 包含顶点在裁剪空间中的位置信息
TEXCOORD0 模型的第一套纹理坐标(也叫UV坐标)
4
v2f vert(a2v IN) {
v2f OUT;
OUT.pos = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = IN.texcoord;
return OUT;
}
填充v2f即 片元着色器需要的片元信息
OUT.pos = UnityObjectToClipPos(IN.vertex);
这里初看有些懵逼 但其实蛮讲道理:
OUT.pos也就是SV_POSITION 包含顶点在裁剪空间中的位置信息
我们自然需要把 “模型的顶点坐标”(IN.vertex/POSITION) 转换为 “裁剪空间中模型的顶点坐标”
便是UnityObjectToClipPos(IN.vertex);(既然我们不需要特殊处理这部分)
OUT.texcoord = IN.texcoord;
模型的第一套纹理坐标不变 没什么好说的
5
sampler2D _MainTex;
float _Red;
float _Blue;
float _Green;
fixed4 frag(v2f IN) : SV_Target{
fixed4 colorTex = (tex2D(_MainTex, IN.texcoord));
colorTex.r = colorTex.g = colorTex.b = colorTex.r * _Red + colorTex.b * _Blue + colorTex.g * _Green;
return colorTex;
}
首先自然声明sampler2D _MainTex为原图
然后一会儿要用到的三原色常数 要从properties里“引导”一下
然后
fixed4 colorTex = tex2D(_MainTex, IN.texcoord);
首先确认片元着色器输出的应该是颜色值 或者说这个片元的一个或多个颜色值
那么tex2D这个函数是干什么的?简单来说取出_MainTex在IN.texcoord这个地方的颜色值 这个_MainTex将在每次render时从我们的图片上获取(大概吧)
得到这个点或者块(片元)的颜色
自然要操作成黑白
colorTex.r = colorTex.g = colorTex.b = colorTex.r * _Red + colorTex.b * _Blue + colorTex.g * _Green;
其实也很好理解 我们平时的图片 黑白的话一定是r=g=b这样
怎么确认这个值呢 自然取决于颜色的“深度”我们就可以用
colorTex.r * _Red + colorTex.b * _Blue + colorTex.g * _Green
这样来算 当然如果你的三个常数过大那就全黑了
啊暂时先这样吧 还有很多其实我也不太明白
之后应该会更新针对UI的shader需要的东西(因为UI会有需要特别的properties等)
一边学一边搞吧。