Notes : Unity Shader
【教程】Unity Shader系列教程(广东话版Cantonese)_哔哩哔哩_bilibili<hr/>#001- Intro
reference
[*]Cg_language
[*]CgTutorial
rengdering API / shading language
recommend setting:
大部分操作都在右边三个窗口,project窗口可调整为单排
项目设置部分:
meta文件存储着组件的各种设置
这样便于用文本查看文件修改了什么设置
可以在本地找到这个 UnityCG.cginc,查看里面的 function
pixel shader
教程先讲了pixel shader 部分,也就是 fragment shader
注释方式是和C++一样的
常用的数据类型
shader的暴露参数方式
UV颜色tex coord的原理示意
tex coord
texture offset
texture rotation
旋转矩阵的公式
Shader &#34;MyShader/001&#34;
{
Properties
{
testFloat(&#34;testFloat&#34;, range(0,360)) = 1
testColor(&#34;testColor&#34;, Color) = (1, 1, 1, 1)
testVec(&#34;testVec&#34;, vector) = (0, 0, 0, 0)
testTex(&#34;testTexture&#34;, 2D) = &#34;white&#34; // &#34;black&#34;
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
Pass
{
CGPROGRAM
#pragma vertex vs_main
#pragma fragment ps_main
#include &#34;UnityCG.cginc&#34;
struct appdata
{
float4 pos : POSITION;
float4 color: COLOR;
float2 uv:TEXCOORD0;
};
// varying
struct v2f
{
float4 pos : SV_POSITION;
float4 color: COLOR;
float2 uv:TEXCOORD0;
};
v2f vs_main (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.pos);
o.color = v.color;
o.uv = v.uv;
return o;
}
// uniform
float testFloat;
float my_time;
float4 testColor;
float4 testVec;
sampler2D testTex;
float4 ps_main(v2f i) : SV_Target
{
//先把旋转中心点移动到想要为轴心的位置后,在最终结果再变回去
float x = i.uv.x - 0.5;
float y = i.uv.y - 0.5;
//float r = testFloat / 360.0 * 2 * 3.1416;
float r = radians(testFloat);
float a = cos(r);
float b = -sin(r);
float c = sin(r);
float d = cos(r);
float nx = a * x + b * y + 0.5;
float ny = c * x + d * y + 0.5;
//float4 o = tex2D(testTex, float2(nx, ny));
//--- calc by matrix
float2x2 rotateMat = float2x2(cos(r), -sin(r),
sin(r), cos(r));
float2 uv = mul(rotateMat, i.uv - 0.5) + 0.5;
// ------------
float4 o = tex2D(testTex, uv);
return o;
}
ENDCG
}
}
}
<hr/>#002 Testure Effect
doc : Accessing shader properties in Cg/HLSL
暴露材质纹理的tilling和offset参数 :
Shader &#34;MyShader/002&#34;
{
Properties
{
testTex(&#34;testTexture&#34;, 2D) = &#34;white&#34; // &#34;black&#34;
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
Pass
{
CGPROGRAM
#pragma vertex vs_main
#pragma fragment ps_main
#include &#34;UnityCG.cginc&#34;
struct appdata
{
float4 pos : POSITION;
float4 color: COLOR;
float2 uv:TEXCOORD0;
};
// varying
struct v2f
{
float4 pos : SV_POSITION;
float4 color: COLOR;
float2 uv:TEXCOORD0;
};
v2f vs_main (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.pos);
o.color = v.color;
o.uv = v.uv;
return o;
}
sampler2D testTex;
float4 testTex_ST;
//OpenGL的3Dtexture coordinate用RST,等同于Maya用UVW,或XYZ
float4 ps_main(v2f i) : SV_Target
{
float2 tiling = testTex_ST.xy;
float2 offset = testTex_ST.zw;
float4 o = tex2D(testTex, i.uv * tiling + offset);
return o;
}
ENDCG
}
}
}
色相分离
UV offset per channel
Shader &#34;MyShader/002 - Tex Effects&#34;
{
Properties
{
testFloat(&#34;testFloat&#34;, range(0,0.1)) = 0
testTex(&#34;testTexture&#34;, 2D) = &#34;white&#34; // &#34;black&#34;
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
Pass
{
CGPROGRAM
#pragma vertex vs_main
#pragma fragment ps_main
#include &#34;UnityCG.cginc&#34;
struct appdata
{
float4 pos : POSITION;
float4 color: COLOR;
float2 uv:TEXCOORD0;
};
// vertex shader
struct v2f
{
float4 pos : SV_POSITION;
float4 color: COLOR;
float2 uv:TEXCOORD0;
};
v2f vs_main (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.pos);
o.color = v.color;
o.uv = v.uv;
return o;
}
//pixel shader
sampler2D testTex;
float4 testTex_ST;
float testFloat;
float4 ps_main(v2f i) : SV_Target
{
float4 o ;
o.r = tex2D(testTex, i.uv - float2(testFloat, 0)).r;
o.g = tex2D(testTex, i.uv).g;
o.b = tex2D(testTex, i.uv - float2(testFloat, 0)).b;
o.a = 1;
return o;
}
ENDCG
}
}
}
Gray Scale
眼睛对于红绿蓝三个色的敏感度不一样, 所以用(R+G+B)/3会导致图像失真, 需要用一定的比例算出来更自然的效果.
lerp & clamp
Shader &#34;MyShader/002 - Tex Effects&#34;
{
Properties
{
testFloat(&#34;testFloat&#34;, range(0,0.1)) = 0
texWeight(&#34;texWeight&#34;, range(-1,1)) = 0
testTex(&#34;testTexture&#34;, 2D) = &#34;white&#34;
testTex2(&#34;testTexture2&#34;, 2D) = &#34;white&#34;
testMask(&#34;testMask&#34;,2D) = &#34;white&#34;
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
Pass
{
CGPROGRAM
#pragma vertex vs_main
#pragma fragment ps_main
#include &#34;UnityCG.cginc&#34;
struct appdata
{
float4 pos : POSITION;
float4 color: COLOR;
float2 uv:TEXCOORD0;
};
// vertex shader
struct v2f
{
float4 pos : SV_POSITION;
float4 color: COLOR;
float2 uv:TEXCOORD0;
};
v2f vs_main (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.pos);
o.color = v.color;
o.uv = v.uv;
return o;
}
//pixel shader
sampler2D testTex;
sampler2D testTex2;
sampler2D testMask;
float texWeight;
float testFloat;
//gray scale
float4 grayScale(float4 v) {
float g = v.r * 0.299 + v.g * 0.587 + v.b * 0.114;
return float4(g, g, g, v.a);
}
float4 ps_main(v2f i) : SV_Target
{
float4 a ;
a.r = tex2D(testTex, i.uv - float2(testFloat, 0)).r;
a.g = tex2D(testTex, i.uv).g;
a.b = tex2D(testTex, i.uv - float2(testFloat, 0)).b;
a.a = 1;
float4 b = tex2D(testTex2, i.uv);
float4 mask = tex2D(testMask, i.uv);
float w = mask + texWeight;
//if (w < 0) w = 0;
//if (w > 0) w = 1;
w = clamp(w, 0, 1);
//float4 o = a * (1 - w) + b * w;
float4 o = lerp(a, b, w);
//o = grayScale(o);
return o;
}
ENDCG
}
}
}
doc : Built-in shader variables
5th channel
(...突然遇到曾经面试题的答案)
一张mask有四个通道, 利用R+G+B+A=1这个特性, 可以获得第五个通道 5th channel=1-(R+G+B+A)
<hr/>#003 Culling, Depth Test, Blending, Flow Map
doc : Unity - Manual: ShaderLab: Pass
Face Culling
判断面的正反, 主要通过这个面上的点, 是顺时针绘制还是逆时针绘制的.
Depth Offset
artifact : 同一平面的两个面片会闪烁, 即使把其中一个稍向前移, 当离镜头较远时闪烁依然会出现(当然距离拉得很大能解决)
how : 在计算完物体的深度后再通过DepthOffset进行位移, 这样的话就不会造成越远精密越不够.
why : 因为在计算出一个像素的XYZ后需要除以它的深度W, 越远精细度不够误差就会比较大. 所以在shader里用 Depth Offset 会比较稳妥.
Blending
doc : ShaderLab: Blending
常见的混合类型写法, 及解释
半透明次序问题 : unity在绘制时会先将实心的物体先画, 然后再绘制半透的物体
若想要在半透物件中区分顺序, 可用+1的方式
多个透明物件用 ZWrite Off 可关闭深度写入
Flow Map
对纹理×2再-1的原因
[*]纹理在PS里面是0~255的值, 经过shader后我们把它normalize到0~1, offset是float2值域为-1~1
[*]此时纹理和offset的值域是不同的, 会导致效果出错. ( 想象颜色RG为坐标XY, 正常情况下是可以四个方向自由移动的, 但颜色0~1和位移-1~1的值域,在相乘后去掉了XY坐标中的所有负值. )
[*]需要对纹理进行×2再-1的操作, 将纹理的值域变成-1~1
SIGGRAPH 2010 - Water Flow in Portal 2 : https://cdn.cloudflare.steamstatic.com/apps/valve/2010/siggraph2010_vlachos_waterflow.pdf
Portal 2 制作方式 : 使用两层来回混合
<hr/>#004 Vertex Shader
Deformation 变换
doc : Built-in shader variables
Space
identity
transpose 形变
Vertex Normal
doc :Normal Transformation
<hr/>#005 Basic Lighting, Toon Shading
叉积(cross product)
[*]在3维空间中建立一个直角坐标系
[*]判定左/右
[*]判定内/外
点积(dot product)
[*]计算两个方向有多么接近
[*]前与后的信息
[*]compose a vector
ambient 环境光
[*]不计算间接光照
[*]注意区分 ambient occlusion : 寻找有多接近一个面, 并且多有机会被一个面挡住
diffuse 漫反射
点积 法线 和 光源 , 可以计算出表面的光滑程度, 角度越小越光滑.
specular 高光反射
[*]会受view angle影响
[*]会受观察射线和光线反弹射线的夹角影响 (上图V和R的夹角)
Shader &#34;MyShader/005_Lighting&#34;
{
Properties
{
ambientColor(&#34;Ambient Color&#34;,Color)=(0,0,0,0)
diffuseColor(&#34;Duffuse Color&#34;,Color)=(1,1,1,1)
specularColor(&#34;Specular Color&#34;,Color)=(1,1,1,1)
specularShininess(&#34;Specular Shininess&#34;,Range(1,128))=10
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
Pass
{
Cull Off
CGPROGRAM
#pragma vertex vs_main
#pragma fragment ps_main
#include &#34;UnityCG.cginc&#34;
#define M_PI 3.14
struct appdata
{
float4 pos : POSITION;
float4 color: COLOR;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
// vertex shader
struct v2f
{
float4 pos : SV_POSITION;
float3 wpos : POSTION;
float4 color: COLOR;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
v2f vs_main(appdata v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.pos);
o.wpos=mul(UNITY_MATRIX_M,v.pos).xyz;
o.color = v.color;
o.uv = v.uv;
o.normal = mul((float3x3)UNITY_MATRIX_M,v.normal);
return o;
}
float4 MyLightPos;
float4 ambientColor;
float4 diffuseColor;
float4 specularColor;
float specularShininess;
float4 basicLighting(float3 wpos,float3 normal)
{
float3 N = normalize(normal);
float3 L = normalize(MyLightPos - wpos);//计算入射光线, 光源位置减去物体表面位置, 然后归一到单位向量
float3 V = normalize(wpos - _WorldSpaceCameraPos);//眼睛的位置减去物体表面的位置
float3 R = reflect(L,N);
//Same as float3 R = L - dot(N,L) * 2 * N;
float4 ambient = ambientColor;
float4 diffuse = diffuseColor * max(0,dot(N,L));
//基础色乘以光照, 点积法线和光源可以计算出表面的光滑程度, 角度越小越光滑.
float specularAngle = max(0,dot(R,V));
float4 specular = specularColor * pow(specularAngle,specularShininess);//模拟高光部分
float4 color = 0;
color += ambient;
color += diffuse;
color += specular;
return color;
}
float4 ps_main (v2f i) : SV_Target
{
return basicLighting(i.wpos, i.normal);
}
ENDCG
}
}
}
unity 将不是光照的部分抽离出来让使用者自定义 : Writing Surface Shaders
Shader &#34;MyShader/005_Toon&#34;
{
Properties
{
toonAngle0(&#34;Toon Angle 0&#34;, Range(-1,1))=-0.6
toonColor0(&#34;Toon Color 0&#34;,Color)=(0,0,1,0)
toonAngle1(&#34;Toon Angle 1&#34;, Range(-1,1))=0.5
toonColor1(&#34;Toon Color 1&#34;,Color)=(0,1,0,0)
toonAngle2(&#34;Toon Angle 2&#34;, Range(-1,1))=0.9
toonColor2(&#34;Toon Color 2&#34;,Color)=(1,0,0,0)
toonColor3(&#34;Toon Color 3&#34;,Color)=(0.2,0.2,0.2,0.2)
toonOutlineWidth(&#34;Toon Outline Width&#34;,Range(0,1))=0.04
toonOutlineColor(&#34;Toon Outline Color0&#34;,Color)=(0,0,0,0)
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
//outline pass
Pass
{
Cull front//描边的重点就是这里,不画正面
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
//make fog work
#pragma multi_compile_fog
#include &#34;UnityCG.cginc&#34;
struct appdata
{
float4 pos : POSITION;
float3 normal : NORMAL;
};
// vertex shader
struct v2f
{
float4 pos : SV_POSITION;
};
float toonOutlineWidth;
float4 toonOutlineColor;
v2f vs_main(appdata v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.pos);//此处的pos已经是屏幕空间的了, 所以下面位移只用了XY
float3 N = mul ((float3x3)UNITY_MATRIX_MV,v.normal);
float2 offset =mul((float2x2)UNITY_MATRIX_P,N.xy);//描边重点, 位移
o.pos.xy += offset * toonOutlineWidth;
return o;
}
float4 frag (v2f i) : SV_Target
{
return toonOutlineColor;
}
ENDCG
}
//color pass
Pass
{
Cull Off
CGPROGRAM
#pragma vertex vs_main
#pragma fragment ps_main
#include &#34;UnityCG.cginc&#34;
#define M_PI 3.14
struct appdata
{
float4 pos : POSITION;
float4 color: COLOR;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
// vertex shader
struct v2f
{
float4 pos : SV_POSITION;
float3 wpos : POSTION;
float4 color: COLOR;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
v2f vs_main(appdata v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.pos);
o.wpos=mul(UNITY_MATRIX_M,v.pos).xyz;
o.color = v.color;
o.uv = v.uv;
o.normal = mul((float3x3)UNITY_MATRIX_M,v.normal);
return o;
}
float4 MyLightPos;
float toonAngle0;
float4 toonColor0;
float toonAngle1;
float4 toonColor1;
float toonAngle2;
float4 toonColor2;
float4 toonColor3;
float4 toonShading(float3 pos,float3 normal)
{
float3 N = normalize(normal);
float3 L = normalize(MyLightPos-pos);
float angle = max(0,dot(N,L));
if (angle > toonAngle2)
return toonColor2;
if (angle > toonAngle1)
return toonColor1;
if (angle > toonAngle0)
return toonColor0;
return toonColor3;
}
float4 ps_main(v2f i) : SV_Target
{
return toonShading(i.wpos,i.normal);
}
ENDCG
}
}
}
Shader &#34;MyShader/005_ToonTexture&#34;
{
Properties
{
toonOutlineWidth(&#34;Toon Outline Width&#34;,Range(0,1))=0.04
toonOutlineColor(&#34;Toon Outline Color0&#34;,Color)=(0,0,0,0)
toonTexture(&#34;Toon Texture&#34;,2D)=&#34;white&#34;
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
Pass
{
Cull front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include &#34;UnityCG.cginc&#34;
struct appdata
{
float4 pos : POSITION;
float3 normal : NORMAL;
};
// vertex shader
struct v2f
{
float4 pos : SV_POSITION;
};
float toonOutlineWidth;
float4 toonOutlineColor;
v2f vs_main(appdata v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.pos);
float3 N = mul ((float3x3)UNITY_MATRIX_MV,v.normal);
float2 offset=mul((float2x2)UNITY_MATRIX_P,N.xy);
o.pos.xy += offset * toonOutlineWidth;
return o;
}
float4 frag (v2f i) : SV_Target
{
return toonOutlineColor;
}
ENDCG
}
//color pass
pass
{
Cull Off
CGPROGRAM
#pragma vertex vs_main
#pragma fragment ps_main
#include &#34;UnityCG.cginc&#34;
#define M_PI 3.14
struct appdata
{
float4 pos : POSITION;
float4 color: COLOR;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
// vertex shader
struct v2f
{
float4 pos : SV_POSITION;
float3 wpos : POSTION;
float4 color: COLOR;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
v2f vs_main(appdata v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.pos);
o.wpos=mul(UNITY_MATRIX_M,v.pos).xyz;
o.color = v.color;
o.uv = v.uv;
o.normal = mul((float3x3)UNITY_MATRIX_M,v.normal);
return o;
}
float4 MyLightPos;
sampler2D toonTexture;
float4 toonShading(float3 pos,float3 normal)
{
float3 N = normalize(normal);
float3 L = normalize(MyLightPos-pos);
float angle = max(0,dot(N,L)*0.5+0.5);
return tex2D(toonTexture,float2(0.5,angle));
}
float4 ps_main(v2f i) : SV_Target
{
return toonShading(i.wpos,i.normal);
}
ENDCG
}
}
}
<hr/>#006 MatCap,Normal Map,Deferred Shading
MatCap : material capture
非常节省性能, 看背面物体的话会有些许问题( 适合相对固定视角的物件 )
Shader &#34;MyShader/006_MatCap&#34;
{
Properties
{
matCapTex(&#34;MatCap Texture&#34;, 2D)=&#34;black&#34;
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include &#34;UnityCG.cginc&#34;
//定义了 program / unity 送到 vertex shader 里面有什么
struct appdata
{
float4 pos : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
// 定义了 vertex shader 送到 pixel shader 有什么
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float3 viewDir : TEXCOORD4; //texture coordinate相较于color会更加精确
float3 viewNormal : TEXCOORD5;
};
sampler2D matCapTex;
v2f vert(appdata v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.pos);
o.uv = v.uv;
o.normal = v.normal;
o.viewDir = normalize(mul( UNITY_MATRIX_MV, v.pos));
o.viewNormal = mul((float3x3)UNITY_MATRIX_MV, v.normal);
return o;
}
float4 matCap(float3 viewNormal, float3 viewDir)
{
float3 N = normalize(viewNormal);
float3 V = normalize(viewDir);
N -= V * dot(V,N);
float2 uv = N.xy * 0.5 * 0.99 + 0.5;
// × 0.5 + 0.5 : 将法线的 -1 ~1的阈值变为 0~1
// ×0.99 是为了避免纹理的边缘是烂边, 避免得到不好的效果
return tex2D(matCapTex, uv);
}
// pixel shader
float4 frag (v2f i) : SV_Target
{
float4 o = 0;
o += matCap(i.viewNormal, i.viewDir);
return o;
}
ENDCG
}
}
}
Normal Map
[*]通过法线来制造假的深度和反射
[*]进阶版本是 Parallax Mapping
Shader &#34;MyShader/006_NormalMap&#34;
{
Properties
{
ambientColor(&#34;Ambient Color&#34;, Color) = (0,0,0,0)
diffuseColor(&#34;Diffus Color&#34;, Color) = (1,1,1,1)
specularColor(&#34;Specular Color&#34;, Color) = (1,1,1,1)
specularShininess(&#34;Specular Shininess&#34;, Range(0,128)) = 10
diffuseMap(&#34;Diffuse Map&#34;,2D)=&#34;white&#34;
specularMap(&#34;Specular Map&#34;,2D)=&#34;white&#34;
normalMap(&#34;Normal Map&#34;,2D)=&#34;white&#34;
uvRepeat(&#34;UV Repeat&#34;,Vector)=(1,1,1,1)
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fog
#include &#34;UnityCG.cginc&#34;
//定义了 program / unity 送到 vertex shader 里面有什么
struct appdata
{
float4 pos : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 uv : TEXCOORD0;
};
// 定义了 vertex shader 送到 pixel shader 有什么
struct v2f
{
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float4 pos : SV_POSITION;
float4 color : COLOR;
float3 worldSpaceNormal : TEXCOORD5;
float3 worldSpaceTangent : TEXCOORD6;
float3 worldSpacePos : TEXCOORD7;
};
float4 MyLightPos;
float4 ambientColor;
float4 diffuseColor;
float4 specularColor;
float specularShininess;
sampler2D diffuseMap;
sampler2D specularMap;
sampler2D normalMap;
float4 uvRepeat;
float4 basicLighting(float3 pos, float3 normal, float3 tangent, float2 uv)
{
float3 N = normalize(normal);
float3 L = normalize(MyLightPos-pos);//计算入射光线, 光源位置减去物体表面位置, 然后归一到单位向量
float3 V = normalize(pos - _WorldSpaceCameraPos);//眼睛的位置减去物体表面的位置
float3 T = normalize(tangent);//如果左右的法线都是有倾斜角度的, 中间计算出来的法线有可能会短一截, 所以需要归一到单位长度
float3x3 TBN = float3x3(T, cross(N,T),N);//通过 normal和 tangent叉积出来 binormal
//TBN = transpose(TBN);
//normal map
N = tex2D(normalMap, uv).xyz *2 - 1;// 0..1 -> -1..1
N.y = -N.y;
N = mul(TBN,N);//tangent space-> world space TBN是刚刚计算出来的切线空间的一个3×3的数组
float3 R = reflect(L,N);
float4 ambient = ambientColor;
float4 diffuse = diffuseColor * tex2D(diffuseMap,uv) * dot(N,L);
float specularAngle = max(0,dot(R,V));
float4 specular = specularColor * tex2D(specularMap,uv)* pow(specularAngle,specularShininess);
float4 color = 0;
color += ambient;
color += diffuse;
color += specular;
return color;
}
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.pos);
o.uv = v.uv * uvRepeat.xy;
o.normal = v.normal;
o.color = 1;
o.worldSpacePos = mul(UNITY_MATRIX_M, v.pos).xyz;
o.worldSpaceNormal = mul((float3x3)UNITY_MATRIX_M, v.normal);//normal has no translation,that&#39;s why
o.worldSpaceTangent = mul((float3x3)UNITY_MATRIX_M,v.tangent.xyz);
return o;
}
float4 frag(v2f i) : SV_Target
{
float4 o = i.color;
o = basicLighting(i.worldSpacePos, i.worldSpaceNormal, i.worldSpaceTangent, i.uv);
return o;
}
ENDCG
}
}
}
[*]NormalMap 的 alpha 通道可以用来储存 specular 信息
[*]NormalMap 的 B 通道可以通过 RG 通道算出, 所以B通道也可以用来存储别的信息
Defered Rendering
[*]G - Buffer : geometry buffer
[*]GPU 的 MRT ( multiple render target )
[*]简单来说是把光照放在后面做, 以节省每个物件都要做一次光照的计算
[*]但是无法处理透明物体, 透明物体需要用 forward rendering 来做
PBR
pixel(2D) 和 voxel(3D)
<hr/>#007 Post Processing, Blur, Bloom
Doc : MonoBehaviour.OnRenderImage(RenderTexture,RenderTexture)
可自定义每个脚本运行的次序
挂在相机上的脚本
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Ex007_GrayScale_Camera : MonoBehaviour
{
public Material grayScaleMat;
private void OnRenderImage(RenderTexture src, RenderTexture dst)
{
if (!grayScaleMat)
return;
Graphics.Blit(src, dst, grayScaleMat);
}
}
去色shader
Shader &#34;MyShader/Ex007_GrayScale&#34;
{
Properties
{
_MainTex(&#34;Base(RGB)&#34;,2D)=&#34;white&#34;{}
intensity(&#34;intensity&#34;, Range(0,1)) = 1
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
LOD 100
Cull off
ZTest Always
ZWrite Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include &#34;UnityCG.cginc&#34;
//定义了 program / unity 送到 vertex shader 里面有什么
struct appdata
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
// 定义了 vertex shader 送到 pixel shader 有什么
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
float intensity;
sampler2D _MainTex;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.pos);
o.uv = v.uv;
return o;
}
float4 grayScale(float4 v)
{
float g = v.r * 0.299 + v.g * 0.587 + v.b *0.114;
return float4(g,g,g,v.a);
}
float4 frag (v2f i) : SV_Target
{
float4 c = tex2D(_MainTex, i.uv);
return lerp(c,grayScale(c),intensity);
}
ENDCG
}
}
}
静态画面扰动shader
Shader &#34;MyShader/Ex007_Distortion&#34;
{
Properties
{
_MainTex (&#34;Base(RGB)&#34;,2D)=&#34;white&#34;{}
intensity(&#34;intensity&#34;, Range(0,1)) = 1
FlowMap(&#34;FlowMap&#34;, 2D) = &#34;black&#34;
}
SubShader
{
Tags { &#34;RenderType&#34;=&#34;Opaque&#34; }
LOD 100
Cull off
ZTest Always
ZWrite Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include &#34;UnityCG.cginc&#34;
//定义了 program / unity 送到 vertex shader 里面有什么
struct appdata
{
float4 pos : POSITION;
float2 uv : TEXCOORD0;
};
// 定义了 vertex shader 送到 pixel shader 有什么
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
};
float intensity;
sampler2D _MainTex;
sampler2D FlowMap;
float4 FlowMap_ST;
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.pos);
o.uv = v.uv;
return o;
}
float4 frag (v2f i) : SV_Target
{
float4 flow = tex2D(FlowMap, i.uv * FlowMap_ST.xy + FlowMap_ST.zw) * 2 -1;
float4 c = tex2D(_MainTex, i.uv + flow * intensity);
return c;
}
ENDCG
}
}
}
to be continue
0:10:20
页:
[1]