01.导读
02.学前须知
03.计算机图形学相关理论
GLSL
OpenGL 着色器编程语言(OpenGL Shading Language), 是用来在OpenGL中编写着色器的语言, 是一种具有C/C++风格的高级语言.
HLSL
高级着色器语言(High Level Shader Language), 是由微软拥有及开发的一种着色器语言, 主要用于Direct3D, 不与OpenGL标准兼容, 与Nvidia的CG非常相似.
CG
C for Graphic, 简称 Cg语言, Cg是由Nvidia与微软相互协作开发的一种高级着色器语言, 与HLSL非常相似.
Unity 中编写ShaderLab来通过OpenGL/DirectX告诉显卡驱动要做什么, 然后显卡驱动在指挥GPU进行计算并输出到显示器上.
04.Shader编程初识
Shader 是什么?
- Shader 就是着色器.
- Shader 其实就是专门用来渲染图形的一种技术, 通过Shader, 我们可以自定义显卡渲染画面的算法,使画面达到我们想要的效果.
- Shader 其实就是一段代码, 用于告诉GPU如何去绘制模型每一个顶点的颜色(顶点相关的一些数据, 顶点的法线, 顶点的UV…)以及最终像素的颜色.
Shader 可视化编辑器
- Shader forge
- Amplify Shader Editor
- Shader Graph
ShaderLab 的几种形式
- 固定管线着色器(Fixed Function Shader)..(已淘汰, 仅了解)
- 表面着色器(Surface Shader) (更符合人类的思维逻辑, 偏表面属性的着色器)
- 顶点片断着色器(Vertex/Fragment Shader)
ShaderLab 模版
- Standard Surface Shader (对应上面的表面着色器)
- Unlit Shader (不受光照的Shader, 但是仍然可以添加各种光照, 代码层面上是顶点片断着色器)
- Image Effect Shader (后处理Shader, 屏幕后处理, 屏幕整体调色)
- Compute Shader (运行在GPU上的代码, 独立于普通渲染管线以外的, 通常做一些大量的并行运算)
- Ray Tracing Shader (光线跟踪Shader)
Shader 代码框架
1234567891011121314151617181920212223242526272829303132333435363738 Shader "BadZhang/Class01" // Shader 路径名称{Properties // 材质属性{_MainTex ("Texture", 2D) = "white" {}}// SubShader 子着色器// 一个Shader 可以有多个 SubShader (必须要有一个) GPU进来以后会找第一个SubShader// 如果第一个SubShader 不支持 显卡特性会找下一个, 如果没有下一个会直接跳到 Fallback ...SubShader // 子着色器 01{// GPU 进入 SubShader 后会查找 Pass, Pass 就是渲染一次// Pass 也可以有多个, 考虑到性能问题, 尽量写到一个里面Pass{// 具体的绘制}Pass{}}SubShader // 子着色器 02{Pass{}}CustomEditor "" // 自定义 GUIFallback "" // 如果没有备用或者备用也不支持, 或者出错, 会显示粉红色}Shader 方法函数
CGPROGRAM/ENDCG
- 用于定义CG语言的开始与结束
- Cg/HLSL代码放于此内
- 顶点着色器和片断着色器都在这其中
#Pragma vertex name
- #pragma 是编译指令, 这里表示定义了叫name的顶点着色器
#Pragma fragment name
- 这里表示定义了叫name的片断着色器
123456789101112131415161718192021222324 Shader "BadZhang/Class02"{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragfloat4 vert(float4 pos:POSITION): SV_POSITION{}float4 frag():SV_TARGET{}ENDCG}}}Shader 变量与数据类型
- float 常用于 位置属性或者说UV的时候, 需要很大的一个精度
- half 常用于 方向, 颜色(非HDR颜色可以用fixed), 法线 (URP中使用half代替了fixed, half的精度为16位,范围从-60000到60000)
- fixed (淘汰)
- Integer 常用于 数组索引, 在OpenGL 2.0 / DirectX3D 9 会自动把定义的整形转换成 float, 因为硬件不支持, 在 OpenGL 3.0+ / DirectX3D 11+ 支持整形
- sampler2D 采样贴图, 最常见的贴图
- samplerCUBE 6个面的贴图
PC 只认 float, 移动端可以按需使用 float / half / fixed 做到一些性能的提升.
05.顶点着色器
1.顶点与三角面
…
2.结构与语义
顶点的多个属性信息(Struct) -> 顶点数据 -> 应用程序阶段数据 -> 顶点着色器
- 顶点位置
- 顶点UV坐标
- 顶点法线
- 顶点颜色
- 顶点索引Id
- …
123456789101112131415161718192021222324252627282930313233343536 Shader "BadZhang/Class02"{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragstruct appdata{// float4 数据类型 pos 变量名 POSITION 语义 ; 结尾float4 pos:POSITION;float4 color:COLOR;};struct v2f{float4 pos: SV_POSITION;}; };v2f vert(appdata v){}float4 frag(v2f i):SV_TARGET{}ENDCG}}}
3.结构的初始化与返回
顶点的多个属性信息(appdata) -> 顶点着色器(-> v2f)片断着色器
123456789101112131415161718192021222324252627282930313233343536373839 Shader "BadZhang/Class02"{SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment fragstruct appdata{// float4 数据类型 pos 变量名 POSITION 语义 ; 结尾float4 vertex:POSITION;float4 color:COLOR;};struct v2f{float4 pos: SV_POSITION;};v2f vert(appdata v){v2f o = (v2f)0;o.pos = v.vertex;return o;}float4 frag(v2f i):SV_TARGET{return 1;}ENDCG}}}
4.坐标系
- 2D坐标系: 只有两个维度的坐标系,用于描述二维空间,比如X轴和Y轴
- 3D坐标系: 拥有三个轴向的坐标系,主要用于描述三维空间,比如X轴、Y轴和Z轴
- Unity 是左手坐标系, 3DMAX 是右手.
- …
5.裁剪和投影
6.模型本地空间
7.CGInclude
- HLSLSupport.cginc 编译CGPROGRAM时自动包含此文件, 其中声明了很多预处理宏帮助多平台开发.
- UnityShaderVariables.cginc 编译CGPROGRAM时自动包含此文件, 其中声明了很多各种内置的全局变量.
- UnityCG.cginc 需要手动添加, 其中声明了很多内置的帮助函数与结构.
- Windows {unity install path}/Data/CGIncludes/xxx.cginc
- Max /Applications/Unity/Unity.app/Contents/CGIncludes/xxx.cginc
8.空间变换
本地空间->(模型变换)->世界空间->(视图变换)->相机空间->(投影变换)->裁剪空间
矩阵变换运算符 mul(M,V); M=矩阵 V=向量
模型的本地空间到齐次裁剪空间
UnityObjectToClipPos(v.vertex);
o.pos = mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, v.vertex));
9.屏幕映射
10.为什么一个Cube有24个顶点
“如果一个顶点, 它真的是一个顶点,那么,它就只有一个顶点!”
06.片段着色器
1.光栅化
- 三角形设置 根据点组装成一个个三角形
- 三角形遍历 遍历找出所有属于对应三角形的片断
- 插值 利用顶点数据对片断进行插值
2.片断着色器
在片断着色器里, 可以自由的去定义每一个像素的颜色, 可以用各种算法去决定每一个像素最终输出到屏幕上的时候是什么样的效果
分量 rgba xyzw
3.顶点与片断的区别
能在顶点里面做的事情绝不再片断里面做.
07.作业
1.输出一个颜色
…
08.材质属性
1.材质属性 Properties
- Type -> Color 颜色
- Type -> Int 整数
- Type -> Float 浮点数
- Type -> Vector 四维向量
- Type -> 2D 纹理
- Type -> 3D 纹理
- Type -> Cube 立方体纹理
2.Color、Int、Float、Vector、2D纹理, 常用通用特征
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263 Shader "BadZhang/Class03"{Properties{[Header(Base)]_Color("颜色", Color) = (1,1,1,1)// (面板向下取整) 这里int 其实并不重要 重要的是CG代码中的类型声明_Int("整数", int) = 0.5[Toggle]_Int2("开关", int) = 0[IntRange]_Int3("整数1", Range(0, 10)) = 0.5[PowerSilder(3)]_Float("浮点", Range(0, 10)) = 0.5[Space(40)]_Vector("四维向量", Vector) = (0, 0, 0, 0)_2DTex("2D纹理", 2D) = "black" {}[NoScaleOffset]_2DTex1("2D纹理", 2D) = "black" {}[NoScaleOffset][HideInInspector]_2DTex2("2D纹理", 2D) = "black" {}}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex:POSITION;};struct v2f{float4 pos: SV_POSITION;};fixed4 _Color; // 与属性变量名字必须保持一致//int _Int; // 这俩设置面板参数时有区别fixed _Int; // 面板的细节取决于 这里的类型是 int 还是 float 而不是属性定义的类型fixed _Float;float4 _Vector;v2f vert(appdata v){v2f o = (v2f)0;o.pos = UnityObjectToClipPos(v.vertex);return o;}float4 frag(v2f i):SV_TARGET{return _Vector;}ENDCG}}}
09.自发光角色材质
1.需求分析
2.纹理相关概念
像素和纹素
3.纹理的采样
- 属性定义 _MainTex(“Tex”, 2D) = “white”{}
- CG中声明 sampler2D _MainTex;
- 着色器中采样 fixed4 tex = tex2D(_MainTex, i.uv);
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 Shader "BadZhang/Class9"{Properties{_MainTex("Tex", 2D) = "white"{} // 1.属性定义}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex :POSITION;float4 uv :TEXCOORD;};struct v2f{float4 pos :SV_POSITION;float4 uv :TEXCOORD;};sampler2D _MainTex; // 2.CG 中声明v2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = v.uv;// OpenGL 2.0 不支持在顶点里面采样return o;}fixed4 frag(v2f i): SV_TARGET{fixed4 tex = tex2D(_MainTex, i.uv); // 3.着色器中采样return tex;}ENDCG}}}4.Shader与材质的关系
一个Shader对应多个材质
5.基本运算符
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758 Shader "BadZhang/Class9"{Properties{_MainTex("Tex", 2D) = "white"{} // 1.属性定义_Color("Color", Color) = (1, 1, 1, 1)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex :POSITION;float4 uv :TEXCOORD;};struct v2f{float4 pos :SV_POSITION;float4 uv :TEXCOORD;};sampler2D _MainTex; // 2.CG 中声明fixed4 _Color;v2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = v.uv;// OpenGL 2.0 不支持在顶点里面采样return o;}fixed4 frag(v2f i): SV_TARGET{fixed4 c;fixed4 tex = tex2D(_MainTex, i.uv); // 3.着色器中采样c = tex;// 加与乘区别// c = c * _Color;// c = c + _Color;c += _Color;return c;}ENDCG}}}6.片断的舍弃Clip
Clip(x) 如果x < 0 则舍弃掉此片断 对片断的舍弃所以只能在片断里使用
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 Shader "BadZhang/Class9"{Properties{[Header(Base)]_MainTex("Tex", 2D) = "white"{} // 1.属性定义_Color("Color", Color) = (0, 0, 0, 0)[Space(10)][Header(Dissolve)]_DissolveTex("DissolveTex(R)", 2D) = "white"{}_Clip("Clip", Range(0, 1)) = 0}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex :POSITION;float4 uv :TEXCOORD;};struct v2f{float4 pos :SV_POSITION;float4 uv :TEXCOORD;};sampler2D _MainTex; // 2.CG 中声明sampler2D _DissolveTex;fixed4 _Color;fixed _Clip;v2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv = v.uv;// OpenGL 2.0 不支持在顶点里面采样return o;}fixed4 frag(v2f i): SV_TARGET{fixed4 c;fixed4 tex = tex2D(_MainTex, i.uv); // 3.着色器中采样c = tex;// 加与乘区别// c = c * _Color;// c = c + _Color;c += _Color;// 片断的舍弃 (溶解)fixed4 dissolveTex = tex2D(_DissolveTex, i.uv);clip(dissolveTex.r - _Clip);return c;}ENDCG}}}7.UV的Tiling与Offset
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 Shader "BadZhang/Class9"{Properties{[Header(Base)][NoScaleOffset]_MainTex("Tex", 2D) = "white"{} // 1.属性定义_Color("Color", Color) = (0, 0, 0, 0)[Space(10)][Header(Dissolve)]_DissolveTex("DissolveTex(R)", 2D) = "white"{}_Clip("Clip", Range(0, 1)) = 0}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex :POSITION;float4 uv :TEXCOORD;};struct v2f{float4 pos :SV_POSITION;float4 uv :TEXCOORD; // 2D贴图UV只用到了 float4 的 xy, zw可以给第二套贴图使用 (小优化)//float4 dissolveUV :TEXCOORD1;};sampler2D _MainTex; // 2.CG 中声明sampler2D _DissolveTex;float4 _DissolveTex_ST; // 如果要使用 _DissolveTex 的 Tiling 和 Offset 相同声明后面加 _STfixed4 _Color;fixed _Clip;v2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv.xy = v.uv.xy;//o.dissolveUV.xy = v.uv * _DissolveTex_ST.xy + _DissolveTex_ST.zw; // 拆两套UV//o.uv.zw = v.uv * _DissolveTex_ST.xy + _DissolveTex_ST.zw;o.uv.zw = TRANSFORM_TEX(v.uv, _DissolveTex);// OpenGL 2.0 不支持在顶点里面采样return o;}fixed4 frag(v2f i): SV_TARGET{//return _DissolveTex_ST.xyzw; // Tiling.xy = _DissolveTex_ST.xy offset.zw = _DissolveTex_ST.zwfixed4 c;fixed4 tex = tex2D(_MainTex, i.uv.xy); // 3.着色器中采样c = tex;// 加与乘区别// c = c * _Color;// c = c + _Color;c += _Color;// 片断的舍弃 (溶解)fixed4 dissolveTex = tex2D(_DissolveTex, i.uv.zw /*i.dissolveUV*/ /*i.uv * _DissolveTex_ST.xy + _DissolveTex_ST.zw 在顶点着色器里优化此代码*/);clip(dissolveTex.r - _Clip);return c;}ENDCG}}}8.图形计算器
- https://www.desmos.com
- https://www.geogebra.org/graphing
- Grapher(MacOS自带)
9.优化溶解效果 (处理溶解边缘信息)
smoothstep(min, max, x); // 在两个值直接平滑过渡 (尽量不使用此函数)
- 如果 x 比 min 小, 返回 0
- 如果 x 比 max 大, 返回 1
- 如果 x 处于范围[min, max]中, 则返回0 和 1 之间的平滑值
saturate(x)
- 如果 x 比 min 小, 返回 0
- 如果 x 比 max 大, 返回 1
- 如果 x 在0~1之间, 返回 x
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889 Shader "BadZhang/Class9"{Properties{[Header(Base)][NoScaleOffset]_MainTex("Tex", 2D) = "white"{} // 1.属性定义_Color("Color", Color) = (0, 0, 0, 0)[Space(10)][Header(Dissolve)]_DissolveTex("DissolveTex(R)", 2D) = "white"{}[NoScaleOffset]_RampTex("RampTex(RGB)", 2D) = "white"{}_Clip("Clip", Range(0, 1)) = 0}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex :POSITION;float4 uv :TEXCOORD;};struct v2f{float4 pos :SV_POSITION;float4 uv :TEXCOORD; // 2D贴图UV只用到了 float4 的 xy, zw可以给第二套贴图使用 (小优化)//float4 dissolveUV :TEXCOORD1;};sampler2D _MainTex; // 2.CG 中声明sampler2D _DissolveTex;//sampler2D _RampTex; 整个溶解纹理都是相同的纹理, 只需要采样UV中U方向纹理, 优化换成 samplersampler _RampTex;float4 _DissolveTex_ST; // 如果要使用 _DissolveTex 的 Tiling 和 Offset 相同声明后面加 _STfixed4 _Color;fixed _Clip;v2f vert(appdata v){v2f o;o.pos = UnityObjectToClipPos(v.vertex);o.uv.xy = v.uv.xy;//o.dissolveUV.xy = v.uv * _DissolveTex_ST.xy + _DissolveTex_ST.zw; // 拆两套UV//o.uv.zw = v.uv * _DissolveTex_ST.xy + _DissolveTex_ST.zw;o.uv.zw = TRANSFORM_TEX(v.uv, _DissolveTex);// OpenGL 2.0 不支持在顶点里面采样return o;}fixed4 frag(v2f i): SV_TARGET{//return _DissolveTex_ST.xyzw; // Tiling.xy = _DissolveTex_ST.xy offset.zw = _DissolveTex_ST.zwfixed4 c;fixed4 tex = tex2D(_MainTex, i.uv.xy); // 3.着色器中采样c = tex;// 加与乘区别// c = c * _Color;// c = c + _Color;c += _Color;// 片断的舍弃 (溶解)fixed4 dissolveTex = tex2D(_DissolveTex, i.uv.zw /*i.dissolveUV*/ /*i.uv * _DissolveTex_ST.xy + _DissolveTex_ST.zw 在顶点着色器里优化此代码*/);clip(dissolveTex.r - _Clip);// 处理溶解边缘信息fixed dissolveVal = saturate((dissolveTex.r - _Clip) / (_Clip + 0.1- _Clip));//fixed4 rampTex = tex2D(_RampTex, dissolveVal/*smoothstep(_Clip, _Clip + 0.1, dissolveTex.r)*/);fixed4 rampTex = tex1D(_RampTex, dissolveVal/*smoothstep(_Clip, _Clip + 0.1, dissolveTex.r)*/); // 优化成 1d 减少计算c+=rampTex;return c;}ENDCG}}}10.变体之multi_complile
什么是变体?
变体的优缺点
- 可将多个功能与效果集成到一个Shader内,便于使用与管理.
- 变体数量过多会导致加载时间过长与内存占用增加.
变体的类型 (有两种不同的类型)
- 1.无论如何都会被编译的变体. (动态开关的功能..)
- 2.通过材质的使用情况来决定是否编译的变体.
变体的声明
#pragma multi_compile _ NAME
变体名必须为全大写
可通过添加 _ 来定义一个默认空的变体
变体名字必须跟属性名一致, 变体名保持大写 后加 _ON
10.作业
模型的本地坐标当做颜色输出.
11.常用内置函数解析(上)
1.基本运算符
2.UV的引用
3.abs和frac
4.floor和ceil
5.max和min
6.pow和rcp
7.exp和exp2
8.fmod
9.saturate和clamp
10.sqrt和rsqrt
1234567891011121314151617181920212223242526272829303132 fixed4 frag(v2f i): SV_Target{// 基本运算符/*fixed a=0.5;fixed b=0.2;fixed c = a+b;fixed2 d = fixed2(1, 0);fixed2 e = fixed2(0, 1);fixed4 g = fixed4(d, e);fixed4 h = fixed4(d.r, e.r, d.g, e.g);fixed2 f = d + e;*/// 模型没有UV信息,所以都是零,就是纯黑色//return float4(i.uv, 0, 1);//return frac(i.uv.r * 20); // 取小数部分//return abs(i.uv.r); // 取绝对值//return floor(i.uv.x * 5); // 向下取整//return ceil(i.uv.x * 5) / 5; // 向上取整//return max(i.uv.x, 0.5); // 取最大值//return min(i.uv.x, 0.5); // 取最小值//return i.uv.x * i.uv.x * i.uv.x; // x 的3次方 (3次方幂)//return pow(i.uv.x, 2); // x 的3次方 (3次方幂)//return rcp(i.uv.x * 10); // 倒数 5分之一//return exp(i.uv.x); // e 在数学里面是自然常数, 为数学中一个常数, 是一个无限不循环小数, 其值约为 2.718281828459045 需要e的n次方的时候//return exp2(i.uv.x)-1; // 2 的 x 次方 与 exp 没关系//return fmod(i.uv.x, 0.2); // 取余数 fmod 5 / 2 取余 与 5 % 0.2 相同//return saturate(i.uv.x * 2)-1; // 小于 0 的都是 0 大于 1 的都是 1 以0和1为标准界限 和 clamp(i.uv.x, 0, 1); 一样//return clamp(i.uv.x, 0.3, 0.8); // 以0.3和0.8为标准界限//return sqrt(i.uv.x); // 平方根 对一个值开平方return rsqrt(i.uv.x)-1; // 平方根 对一个值开平方 然后在取倒数 等价于 pow(i.uv.x, -0.5)-1; x 的 -0.5 次方}
12.常用内置函数解析(下)
1.lerp
12345678 lerp (A, B, alpha) 线性插值.如果alpha=0,则返回A;如果alpha=1,则返回B;否则返回A与B的混合值;内部执行:A + alpha*(B-A)float3 lerp(float3 A, float3 B, float alpha){return A + alpha*(B-A);}2.sin和cos
3.distance和length
123456789101112 distance(a, b) 返回a,b间的距离.float distance(a, b){float3 v = b - a;return sqrt(dot(v, v));}length (v)返回一个向量的模,即 sqrt(dot(v,v))float circle = smoothstep(_Radius, (_Radius + _CircleFade), length(uv * 2 - 1));利用UV来画圆,通过_Radius来调节大小,_CircleFade来调节边缘虚化程序。4.step
12 step(a,b)如果a<=b返回1,否则返回0.5.smoothstep
123456789 float smoothstep (float min, float max, float x){float t = saturate ((x - min) / (max - min));return t * t * (3.0 - (2.0 * t));}如果 x 比min 小,返回 0如果 x 比max 大,返回 1如果 x 处于范围 [min,max]中,则返回 0 和 1 之间的值(按值在min和max间的比例).如果只想要线性过渡,并不需要平滑的话,可以直接使用saturate((x - min)/(max - min))示例
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768 Shader "BadZhang/Class12"{Properties{_MainTex("Tex", 2D) = "white"{} // 1.属性定义_Value("Value", Vector) = (0,0,0)}SubShader{Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex :POSITION;float2 uv :TEXCOORD0;};struct v2f{float4 vertex :SV_POSITION; // 代表其次裁剪空间float2 uv :TEXCOORD0;};sampler2D _MainTex;fixed3 _Value;v2f vert(appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;return o;}fixed4 frag(v2f i): SV_Target{//fixed4 a = fixed4(1, 0, 0, 1);//fixed4 b = fixed4(0, 0, 1, 1);//return lerp(a, b, i.uv.x); // 开关可以省去宏定义//return sin(i.uv.x * 30) * sin(i.uv.y * 30); // 正弦波//return cos(i.uv.x * 30) * cos(i.uv.y * 30); // 余弦波//return cos(i.uv.x * 30) * 0.5 + 0.5 * cos(i.uv.y * 30); // 余弦波// 上面写法等价于下面//half2 a = sin(i.uv.xy * 30) * 0.5 + 0.5;//return a.x * a.y; // 余弦波//return 1 - distance(i.uv.xy, 0.5);//return distance(i.uv.xy, 0.5);//return length(i.uv.x-0.5);//return step(i.uv.x, 0.5);//return step(length(i.uv.xy - 0.5), 0.5); // 硬边圆形fixed fade = length(i.uv.xy - 0.5);return smoothstep(_Value.x, _Value.y, fade);}ENDCG}}}
13.缓冲区
1.帧缓冲区
帧缓冲区也叫做帧缓存, 是用于存放一帧中数据信息的容器.
片断在写入帧缓冲之前会按顺序经历一系列的测试
- Alpha测试
- 模版测试
- 深度测试
片断在写入帧缓冲之时也会进行一些运算操作
- 混合
帧缓冲跟显示器的关系
- 随机扫描显示器 (很多年就被淘汰了)
- 光栅扫描显示器 (现在都是这种)
帧缓冲的方式
- 单缓冲
- 双缓冲
有哪些缓冲区
- Depth Buffer (深度缓冲区)
- Color Buffer (颜色缓冲区)
- Stencil Buffer (模版缓冲区)
- 自定义缓冲区
2.颜色缓冲区
存储每帧颜色信息的缓冲区
1bit / 24bit
1920×1080分辨率的颜色缓冲占用多少显存呢?
1920*1080*3/1024/1024 = 5.93MB
清除缓冲区
Clear (color + Z + stencil)