本书版本为“占红来 译”版,笔记会持续更新,有错误的地方欢迎指正,谢谢!
引言
顶点和碎片着色器得不到判断光线在表面如何反射所需的物理属性信息,但它不受任何物理限制,对于某些非真实特效而言非常合适。顶点和碎片着色器的工作方式更接近于GPU的架构。这一章我们讲 抓取(grab pass) 技术,通过含有这个技术的着色器可以模拟出一些神奇的变形效果。
理解顶点和碎片(片元)着色器
顶点和碎片着色器的代码和表面着色器有很大不同:
顶点和碎片着色器的工作方式分两步:1.模型各个顶点数据首先传递到顶点函数;2.再将顶点函数的运行结果作为碎片函数的输入。
思路:
1.更改着色器所有属性;
2.更改SubShader代码块中的所有代码(在SubShader中写Pass)。
代码如下:
Shader "Custom/6-2" {
Properties {
_Color("Color",Color)=(1,0,0,1)
_MainTex("Base Texture",2D) = "white"{}
}
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
half4 _Color;
sampler2D _MainTex;
//顶点和碎片着色器中,struct的每一个属性都必须用语义绑定进行修饰。
//POSITION表示pos包含当前顶点的位置,TEXCOORD0表示texcoord是顶点中保存的第0个纹理的UV值。
struct vertInput
{
float4 pos:POSITION;
float2 texcoord:TEXCOORD0;
};
struct vertOutput
{
float4 pos:SV_POSITION;
float2 texcoord:TEXCOORD0;
};
//这里的顶点函数是用来将模型坐标投影到场景中去的。
//vertInput结构已经被Unity自动初始化为模型的每一个顶点的信息,而需要我们手动
//在vert()中根据vertInput结构中的各个顶点数据初始化vertOutput结构。
vertOutput vert(vertInput input) {
vertOutput o;
//要将相对于模型的坐标pos 转换为 相对于场景的坐标pos。mul()将矩阵与向量相乘。
//UNITY_MATRIX_MVP是模型—视图—投影矩阵,通过该矩阵可找到顶点在场景中的位置。
o.pos = mul(UNITY_MATRIX_MVP,input.pos);
o.texcoord = input.texcoord;
return o;
}
//vertOutput结构中的信息被初始化后,就被传递到了碎片函数。
half4 frag(vertOutput output) :COLOR{
//output.texcoord是模型每个顶点的第一个纹理的uv坐标,也就是uv坐标连接了顶点和纹理。
//根据output.texcoord这个uv坐标对主纹理_MainTex进行采样。
//把纹理通过 UV 坐标映射到3D 物体表面。
half4 mainColor = tex2D(_MainTex,output.texcoord);
return mainColor*_Color;
}
ENDCG
}
}
FallBack "Diffuse"
}
效果图:
顶点和碎片着色器虽然没有光照函数,但效果看起来还不错嘛,还节约性能。
简单的说就是:在顶点函数中得到顶点在场景中的位置,在碎片函数中给顶点赋色。
通常将顶点和碎片着色器称为像素着色器~
使用抓取
使用抓取可以访问屏幕上渲染出来的所有信息,有了这些信息就可以在着色器中没有限制地修改它们。
来练手~我们创建一个新材质,添加了含有抓取技术的着色器的新材质 能够抓取到它背后渲染的东西,然后在屏幕上将其背后的内容再次渲染。(这种效果算是一个投影~)
思路:
1.删除Properties部分,因为这个着色器不需要任何属性;2.在SubShader代码块中添加抓取代码:GrabPass{//这里面没代码};3.在抓取代码后,添加Pass{//这里面有很多代码}。
Shader "Custom/6-3 Grab" {
SubShader{
//下面是两个通行函数:
GrabPass{ }
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _GrabTexture;
struct vertInput
{
float4 pos:POSITION;
};
struct vertOutput
{
float4 pos:POSITION;
float4 uvgrab:TEXCOORD0;
};
vertOutput vert(vertInput input) {
vertOutput o;
//Unity自动更换的:replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
o.pos = UnityObjectToClipPos(input.pos);
//计算该模型顶点在屏幕坐标的纹理的uv坐标。
o.uvgrab = ComputeGrabScreenPos(o.pos);
return o;
}
half4 frag(vertOutput output) :COLOR{
//tex2Dproj():二维投影纹理查询,并进行深度值比较。
// UNITY_PROJ_COORD传入四元纹理坐标给tex2Dproj()读取,返回和参数一样的值。
fixed col = tex2Dproj(_GrabTexture,UNITY_PROJ_COORD(output.uvgrab));
return col+half4(0.25,0,0,0);
}
ENDCG
}
}
FallBack "Diffuse"
}
效果图:
每次使用带有GrabPass{}的材质时,Unity都需要将屏幕渲染成一个纹理,代价特别昂贵,请适当使用~
实现玻璃着色器(变形效果)
可用于实现玻璃或爆炸的畸变效果。
思路:
结合 抓取通行技术 和 顶点和碎片着色器,对纹理进行采样,再对其UV值做一些修改来制作出一些细微的变形效果。
给2D游戏添加水面着色器(水波效果)
可用于实现水面波动效果。
需要噪声纹理来获得伪随机数(典型的无缝的噪声纹理:Perlin噪声),另外,给着色器添加随机行为的最通用的办法就是引入噪声纹理。这样做出来的材质是一个动态的材质,可达到实时变化的效果~