阴影:
光源看不到,但相机看得到的地方,就是阴影
变体:
一个普通的Shader可能会有很多种效果
例如一个火焰溶解效果,写在Shader里,但其实在未触发之前我们不需要去计算该效果
因此需要在未触发前,将火焰溶解的效果计算关闭
这就用到了变体,把火焰溶解的效果计算变成变体
无论如何都会被编译的变体multi_compile
#pragma multi_compile _ _NAME
变体名必须要全大写
Properties { //在属性面部设置一个开关,来表示是否要启用变体(需求全大小) [Toggle]_DISSLOVEEABLED("Disslove Eabled", int) = 0 } SubShader { pass { ... //在pass中的命名,前面的‘_’表示空变体,之所以有这个是为了不默认开启 _DISSLOVEEABLED 变体 #pragma multi_compile _ _DISSLOVEEABLED_ON ... fixed4 func() { ... #if _DISSLOVEEABLED_ON fixed4 dis = tex2D(_Dissolve, i.uv.zw); fixed weight = dis.r - _Clip; clip(weight); if(weight < _Clip) { tex = tex * lerp(_Color0, _Color1, weight/_Scope); } #endif ... } } }通过材质使用情况来决定是否进行编译的变体shader_feature
一般用于特效,即运行过程中不会去开启该变体
属性中就是要暴露一个开关:
[Toggle]_MaskEnabled("启用遮罩", int) = 0
pass中要启用变体:
#pragma shader_feature _MASKENABLED_ON
然后就是和上面的判断一样了
#if
xxx;
#else
xxx;
#endif
这就可以做到:
同一个Shader可以给不同的材质球使用;
是否开启某个变体由材质球决定,并且在打包时不会将该材质球未开启的变体打进包里。
然后信息存在于这里
常用函数:
abs(x) 绝对值 frac(x) 取小数 floor(x) 向下取整 ceil(x) 向上取整 max(x, y) 取大的 min(x, y) 取小的 pow(x, y) x的y次方 rcp(x) x的倒数 exp(x) e的x次方 exp2(x) 2的x次方 fmod(x, y) x%y saturate(x) 将x限制在0~1 clamp(x, a, b) 将x限制在a~b sqrt(x) 对x开方 rsqrt(x) 对x开方后取倒数 lerp(x, y, a) x+(y-x)*a sin(x) cos(x) distance(x, y) 返回xy之间的距离(几维都可以) length(x) 返回模长(必须是二维及以上) step(x, y) 返回 x >=y ?1 : 0 smoothstep(a, b, x) 返回介于0和1之间的平滑 Hermite 内插
渲染队列Queue
2500以下会被认为是不透明物体,从前往后渲染,效率更高
2500以上就是透明物体,从后往前渲染,效率低,但这是为了保证显示效果正确
Queue=Background,1000,最先渲染
Queue=Geometry,2000,默认场景中的渲染对象
Queue=AlphaTest,2450,要么不透要么全透,常常用来实现美术部分的透贴
Queue=Transparent,3000,场景中的半透明对象
Queue=Overlay,4000,叠加效果,最后渲染的东西放这,如镜头光晕等
混合Blend
Blend后面一般跟随两至三个参数
如
Blend One Zero
Blend One One
第一个参数就是针对片段着色器pass计算出来的SrcFactor源颜色,要对该参数干什么
第二个参数就是针对存在于帧缓冲区的DstFactor目标颜色,要对该参数干什么
其实中间还有一个,不填就是默认加法
Blend SrcAlpha OneMinusSrcAlpha - 传统透明度
Blend One OneMinusSrcAlpha - 自左乘透明度
Blend One One
Blend OneMinusDstColor One - 软合并
Blend DstColor Zero - 乘法
Blend DstColor SrcColor - 双倍乘法
面剔除Cull
剔除不需要的面,优化渲染速度
Cull Front - 剔除正面
Cull Back - 剔除背面
正面背面是指模型的正面背面(顶点环绕方向,及法线),不是玩家看到的是正面
Cull Off - 关闭剔除
屏幕后处理
实现热扭曲的效果
1.做好场景
2.由代码抓取当前一帧的内容
3.获取受扭曲影响的部分屏幕坐标
4.利用屏幕坐标对抓取的图片采样
5.再采样扰动贴图做扭曲
使用unity参数组成屏幕坐标
查看代码
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float2 uv : TEXCOORD0;
};
sampler2D _GrabTex;
sampler2D _MainTex;float4 _MainTex_ST;
fixed _SpeedX, _SpeedY, _Distort;
v2f vert (float4 vertex : POSITION, float2 uv : TEXCOORD0, out float4 pos : SV_POSITION)
{
pos = UnityObjectToClipPos(vertex);
v2f o;
o.uv = TRANSFORM_TEX(uv, _MainTex) + float2(_SpeedX, _SpeedY) * _Time.x;
return o;
}
//由于VPOS和SV_POSITION冲突,因此需要在vert里out
fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos:VPOS) : SV_Target
{
//最初的版本,通过unity参数去计算,_ScreenParams.xy是屏幕的宽高
fixed2 uv = lerp(screenPos.xy / _ScreenParams.xy, tex2D(_MainTex, i.uv).xy, _Distort);
fixed4 grabTex = tex2D(_GrabTex, uv);
return grabTex;
}
ENDCG
}
使用正交空间下的pos计算屏幕坐标
查看代码
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2f
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float2 screenUV : TEXCOORD1;
};
sampler2D _GrabTex;
sampler2D _MainTex;float4 _MainTex_ST;
fixed _SpeedX, _SpeedY, _Distort;
v2f vert (a2f v)
{
v2f o;
o.uv = TRANSFORM_TEX(v.uv, _MainTex) + float2(_SpeedX, _SpeedY) * _Time.x;
o.pos = UnityObjectToClipPos(v.vertex);
//1.此时pos是放在正交矩形中的顶点
//2.因此可以直接拿该值当做屏幕坐标
//3.但要注意pos的范围是-1到1
//4.因此需要把它规范到0到1,但OpenGL和DX11的屏幕坐标原点不同
//5.OpenGL的在左下角,仅需将xy * 0.5 + 0.5即可
//6.但DX11的在左上角,需要将y轴反转,即y = 1 - y
// o.screenUV = o.pos.xy / o.pos.w * 0.5 + 0.5;
// o.screenUV.y = 1 - o.screenUV.y;
//7.上面就是根据正交中的顶点坐标转换成屏幕坐标的操作
//8.可以注意到稍微有点扭曲,原因是顶点不是片元,顶点和顶点之间的参数都是靠插值算出来的
// o.screenUV = ComputeScreenPos(o.pos) / o.pos.w;
//9.当然,unitycg提供了方法,上面
//10.这里除以一个w是因为:正交矩形中是正交的,而相机是透视的,要将其还原
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed2 uv = lerp(i.screenUV, tex2D(_MainTex, i.uv).xy, _Distort);
fixed4 grabTex = tex2D(_GrabTex, uv);
return grabTex;
}
ENDCG
}
使用片元计算后的pos计算屏幕坐标
查看代码
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct a2f
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float2 screenUV : TEXCOORD1;
};
sampler2D _GrabTex;
sampler2D _MainTex;float4 _MainTex_ST;
fixed _SpeedX, _SpeedY, _Distort;
v2f vert (a2f v)
{
v2f o;
o.uv = TRANSFORM_TEX(v.uv, _MainTex) + float2(_SpeedX, _SpeedY) * _Time.x;
o.pos = UnityObjectToClipPos(v.vertex);
o.screenUV = o.pos;
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// return i.screenUV.x;
// return i.pos.x;
//可以看到,我明明对screenUV赋了pos的值,但上面两个居然不一样
//那是因为SV_POSITION在中间经过了改变
//在经过计算后,pos就是屏幕像素大小了,不再是-1到1了,那这样就更方便了;
i.screenUV = i.pos.xy / _ScreenParams.xy;
//OK,就上面这一行就解决战斗了
fixed2 uv = lerp(i.screenUV, tex2D(_MainTex, i.uv).xy, _Distort);
fixed4 grabTex = tex2D(_GrabTex, uv);
return grabTex;
}
ENDCG
}
[PerRendererData]
属性的特性
Properties
{
_MainTex ("Texture", 2D) = "white" {}
[PerRendererData]_Color ("Color", Color) = (0,0,0,0)
}
该材质cs被调用时,会被打包进cs 的MaterialPropertyBlock
每帧修改一万个对象的颜色,
方法1,使用材质的方法去修改颜色,开销是16ms每帧
方法2,使用GetPropertyBlock(MaterialPropertyBlock prop)的方法修改颜色,开销是7ms每帧
Color
模板测试
模板缓冲区
自己的话
是屏幕大小的区域,例如1920*1080的大小
每个像素是8位
当某一个物体被渲染到屏幕上时,它看到带有材质,也就是肯定带有shader
而shader中可以定义,当前我被渲染出来的区域的模板值
因此,当另一个物体被渲染时,另一个shader上面的也会有模板值
两个模板值比较,新的模板值大会怎么样,小会怎么样,可以自己定义
因此就可以做出类似PS遮罩的效果
官方的话
模板缓冲区可以为屏幕上的每个像素点保存一个无符号的整数值
这个值的意义视程序的具体操作而定
在渲染过程中
可以用这个值与一个预先设定的参考值相比较
根据比较的结果来决定是否更新相应的像素点的颜色值,这个比较的过程被称为模板测
代码部分
公式
将StencilBuffer的值与ReadMask进行与运算,然后与Ref值进行Comp比较,结果为True时进行Pass操作,否则进行Fail操作
操作值写入StencilBuffer前先与WriteMask进行与运算
(Ref & ReadMask)Comp(StencilBuffer & ReadMask)? Pass : Fail
Comp
Less <
Greater >
Lequal <=
Gequal >=
Equal =
NotEqual !=
Always 总为true
Never 总为false
实践
UI组件加了Mask之后
会有这个,这是由UGUI自己去写的
Id就是公式中的StencilBuffer ,已经被写到深度缓冲区中了
我们写的新shader就是要和这个Id:1去比
Properties
{
[PerRendererData]_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (0,0,0,0)
_Ref("Stencil Ref", int) = 0
[Enum(UnityEngine.Rendering.CompareFunction)]_Comp("Stencil Comp", int) = 0
[Enum(UnityEngine.Rendering.StencilOp)]_OP("Stencil OP", int) = 0
}
Stencil//深度测试
{
Ref [_Ref]
//ReadMask [0~255]
//WriteMask [0~255]
Comp [_Comp]
Pass [_OP]
}
这就是Enum
此时_Ref = 1
这是Mask
这是效果
标签:MainTex,uv,Shader,pos,Unity,xy,v2f,screenUV,随笔 From: https://www.cnblogs.com/LateUpdate/p/17702778.html