Shader
Fixed shader: shader1.0 固定管线
Vertex/Fragment shader : shader 2.0 顶点/片段
Surface shader :Unity3d 特有的 表面
Shader1.0
开关式编程
最终呈现效果计算公式
环境光 * Unity环境光设置 + 漫反射 * 灯光的颜色 + 镜面反射 * 灯光的颜色 + 自发光
流水线
过程:
顶点着色器光栅化片段着色器alpha测试模板测试深度测试blendgbufferframebufferfrontbuffer显示器
shader1.0 开关式编程
shader2.0 能够编程的
顶点着色器
计算顶点的位置 MVP:以相机为坐标系,超框不显示;计算顶点的大小计算顶点的颜色 光栅化:
将顶点变成像素 (计算插值) 片段(像素)着色器
计算像素颜色结合灯光计算像素的颜色结合贴图计算像素的颜色 alpha测试
淘汰像素 RGBA:根据a值淘汰i像素 深度测试
根据深度值淘汰像素(离相机远近) 模板测试
程序自定义的值淘汰像素分辨率:1280720 float[1280720] RGBA:32bit,颜色缓存区(RGBA float[1280720]),深度缓存区(深度值8bit Byte[1280720] 或 short[1280720]) 模板缓存区(模板值8bit Byte[1280720]): blend (每个相机都会有 模板深度颜色的buffer):
将已经渲染到屏幕上的像素和即将渲染到屏幕上的像素做混合,存到gbuffer里 framebuffer、frontbuffer:
将颜色缓存区里面的东西写入framebuffer里交换到显示器上去
Unity3D编程
Fixed shader:固定管线编程 shader1.0Vertex/fragement shader:顶点 片段着色器编程shader2.0Surface shader :表面着色器编程 U3D特有
四种光
光照公式: 光照I = 自发光+环境光+漫反射+镜面反射 环境光=环境光颜色 * 环境光向量 漫反射=漫反射光颜色 * 漫反射强度 * (光源向量·法线向量) 镜面反射=镜面光颜色 * 镜面反射强度 * (反射光向量·视角方向)^n
漫反射 (Diffuse)
跟观察角无关跟入射角有关Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角) ) Tip:cosθ = 光方向· 法线方向
镜面反射 (Specular)
跟观察角有关跟入射角有关Specular=直射光 * pow( max(cosθ,0),10) θ:是反射光方向和视野方向的夹角
环境光 (Ambient)
周围环境光线
自发光 (Emission)
自己发光
Ambient + Lighting Window’s Ambient Intensity Setting + (Light Color * Diffuse + Light Color * Specular) + Emission
顶点着色器
//路径名
Shader "Custom/TestProperty"
{
//属性:定义属性
Properties
{
//1.数值,滑动条
//2.颜色,向量
//3.贴图
//变量名,显示名,类型, 默认值
_MainTex ("Texture", 2D) = "white" {}
_TestFloat("Test float", float) = 2.5
//GPU 只有浮点数
_TestInt("Test int", int) = 2
//滑动条
_TestRange("TestRange", Range(1,10)) = 5
//颜色
_TestColor("TestColor", Color) = (1,1,1,1)
//向量
//四维向量xyzw,三维矩阵变换,w表示平移
_TestVector("TestVector", Vector) = (1,1,1,1)
//贴图
_TestTexture("TestTexture", 2D) = "white" {}
_TestCube("TestCube", Cube) = "white" {}
_Test3D("Test3D", 3D) = "white" {}
}
//渲染流程:选择对应显卡,只用一个
SubShader
{
//渲染通道:每出现一次就渲染一次
//写代码
Pass
{
//sahder1.0顶点着色器编程(开关式编程):
//Color:表示顶点颜色的定义
// Color (1,0,0,1)
//使用变量
Color[_TestColor]
//Material:定义物体自身材质属性:
//设置物体自身四种光,漫反射,镜面反射,环境光,自发光
material{
}
//sahder1.0开关式编程
SeparateSpecular On
//灯光总开关
lighting On
}
}
//可以适配多个显卡
SubShader
{
Pass
{
}
}
//没找到适配显卡,选择默认shader
Fallback "Diffuse"
}
片段着色器
SetTexture指令
Previous是上一次SetTexture的结果
previous指的是先前的数据,将代码改成“combine texture * previous”,这里的意思就是用当前纹理的值去乘上当前settexture操作之前所有计算和采样过后的结果。 Primary是来自光照计算的颜色或是当它绑定时的顶点颜色
primary代表了前面所有计算材质和光照后的颜色值,将贴图和这个值相乘,就会得到一个混合的新的颜色值。 Texture是在SetTexture中被定义的纹理的颜色Constant是被ConstantColor定义的颜色
SubShader
{
Pass
{
//设置图片指令
SetTexture[_BlendTex]
{
//代码逻辑
//combine texture
//加减乘 当前贴图颜色
combine texture * Primary
}
SetTexture[_MainTex]
{
//加减乘 之前贴图颜色
combine texture * Previous
}
}
}
lerp插值
lerp(a,b,t) = a* t + b*(1-t)
lerp(RGBA1, RGBA3, RGBA2.A)
Constant 常量指令
SetTexture[_BlendTex]
{
ConstantColor(0,1,0,1)
combine texture * constant
}
alpha测试 深度测试
/*
*********************************************************************
*Copyright © 2019 Unity1903
*File Name: TestTexture.shader
*Author: lvzhijie
*CreateTime: 2019年10月29日 9:33:44
*Describe: Add a description here
*********************************************************************
*/
Shader "Shader02/TestTexture"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BlendTex("Blend", 2D) = "white"{}
_BlendTex2 ("Blend2", 2D) = "white" {}
_ConstantColor ("ConstantColor", Color) = (1,0,0,1)
}
SubShader
{
//消融效果
//Blend srcalpha oneminussrcalpha
Pass
{
//01 练习 Primary Previous Constant
//设置图片指令
/* SetTexture[_MainTex]
{
//加减乘 上当前贴图颜色
combine texture * Primary
}
SetTexture[_BlendTex]
{
//代码逻辑
combine texture + Previous
}
SetTexture[_BlendTex]
{
ConstantColor [_ConstantColor]
combine texture * constant
}*/
/*
02片段着色器
//顶点着色器写在最前面
Color[_ConstantColor]
//片段着色器
SetTexture[_BlendTex]
{
combine texture
}
SetTexture[_MainTex]
{
// combine texture * Primary + Primary
combine texture * Primary + Previous
}
SetTexture[_BlendTex2]
{
combine texture + Previous
}
*/
//03 lerp 插值 视频52分钟
/*Color(1,0,0,1)
SetTexture[_MainTex]
{
//调用Constant值设置变量
ConstantColor[_ConstantColor]
//根据变量设置lerp的alpha值
combine Primary lerp(constant) texture
}
*/
/*
三大测试
shader1.0 shader2.0通用
alpha测试, 模板测试, 深度测试
*/
/*
alpha测试:
AlphaTest comparison AlphaValue
对比方法:
Greater:大于
GEqual:大于等于
Less:小于
LEqual:钓鱼灯也
Equal:等于
NotEqual:不等于
Always:一直等于
Never:从不等于
*/
//三大测试写在pass里只应用与pass,pass外应用于所有pass
//取所有alpha大于0.9的值
//AlphaTest Greater 0.9
//Always 和 Never 后必须跟值,值无意义
//AlphaTest Always 0
/*
深度测试:
根据深度值判断淘汰像素
深度值:物体到相机的距离
深度缓冲区:跟颜色缓冲区匹配 Float[1024*768]
指令:
Cull Back|Front|Off:剔除,不渲染前或后,或都渲染
ZWrite Off : 是否替换
ZTest : 判断替换
Offset : 微调
*/
//前后都渲染,或两次渲染,从后往前,cull front 然后cull back
//cull Off
//一直显示
ZTest Always
//微调 (防止同一位置的两个物体重叠闪烁)
Offset -1,-1
SetTexture[_MainTex]
{
combine texture
}
}
//Cube渲染前后两个面
//从后往前渲染
/*Pass
{
cull front
SetTexture[_MainTex]
{
combine texture
}
}
pass
{
cull back
SetTexture[_MainTex]
{
combine texture
}
}
Pass{
Offset -1,-1
cull back
AlphaTest Greater [_TexTure01Float]
SetTexture [_TexTure02]{
ConstantColor [_ConstantColor]
combine Texture
}
}
*/
}
}
模板测试
所使用的代码
referenceValue:即将渲染的像素模板值:0-255默认0ReadMask:加密值,默认255WriteMask:加密值,默认255comparisonFunction:大于小于等于。。。stencilBufferValue:模板缓存区里的值
if(referenceValue & readMask comparisonFunction
stencilBufferValue & readMask
)
通过i像素
else
丢弃像素
关键字
Ref :设定讲讲渲染的像素模板值 referenceValue值ReadMask:设置读遮罩,做加密WriteMask:设置写遮罩,做加密Comp:设置对比表达式(大于小于等于) 取值范围Pass:深度测试通过,模板测试也通过的操作,后处理,对模板缓存区里面的值取一个操作Fail:两个测试都失败,取一个操作ZFail:表示当深度测试失败,模板测试通过,取一个操作
Keep:模板缓存区里的值保持不变Zero:模板缓存区里的值变成0Replace:模板缓存区里的值替换当前渲染的像素的模板值IncrSat:Increment,自增,到255,大于255为255DecrSat:自减,到0Invert:所有的位取反IncrWrap:循环自增,0-255-0-255DecrWrap:循环自减,255-0-255-0
使用方法
Pass
{
Stencil
{
Ref 2
Comp always 2
Pass replace
}
}
设定当前渲染的像素模板设定跟gbuffer里的值进行对比设定后处理
Blend
指令
Blend SrcFactor DstFactor: SrcFactor:即将渲染的像素RGBA DstFactor:已经渲染的RGBA
SrcColor:即将渲染的像素RGB SrcAlpha:即将渲染的A值
DstColor:已经渲染的RGB DstAlpha:已经渲染的A值
OneMinusSrcColor:1-即将渲染的RGB OneMinusDstColor:1-已经渲染的RGB
OneMinusSrcAlpha:1-即将渲染的A OneMinusDstAlpha:1-已经渲染的A
想改变+号
BlendOP Sub Add:加法 Sub:减法
Blend SrcAlpha OneMinusSrcAlpha 即将渲染的A值 * 即将渲染的像素RGBA + (1-即将渲染的像素A值) * 已经渲染的RGBA
Blend One OneMinusSrcAlpha 1 * 即将渲染的像素RGBA + (1-即将渲染的像素A值) * 已经渲染的RGBA
Blend One One 1 * 即将渲染的像素RGBA + 1 * 已经渲染的RGBA
Blend OneMinusDstColor One (1-已经渲染的像素RGB) * 即将渲染的像素RGBA + 1 * 已经渲染的RGBA
Blend DstColor One 已经渲染的像素RGB * 即将渲染的像素RGBA + 1 * 已经渲染的RGBA
Blend DstColor SrcColor 已经渲染的像素RGB * 即将渲染的像素RGBA + 即将渲染的像素RGB * 已经渲染的RGBA
Shader2.0
*** 1.0和2.0代码不能混用 *** 可以更改过程 不能直接跟灯光计算(可以间接计算),一般用来做屏幕后期特效
CG语言
CG宏指令
引申: Jit (just in time) :即时编译语言 例如Lua, python Aot (a head of time) : 预编译语言 例如C#,java
制作效果
制作UV贴图旋转
SubShader
{
/*
旋转贴图
UV动画,旋转需要用到矩阵转换,
1. 移动原点到中心
2. 旋转
3. 移动原点到原来位置
*/
//混合透明
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i) : SV_Target
{
fixed2 tmpUV = i.uv;
//移动中心到圆点
tmpUV -=fixed2(0.5,0.5);
//设置长度范围,防止出现多余的图片
if(length(tmpUV) > 0.5)
return fixed4(0,0,0,0);
float angle = _Time.y;
float xx = tmpUV.x * cos(angle) + tmpUV.y * sin(angle);
float yy = -tmpUV.x * sin(angle) + tmpUV.y * cos(angle);
tmpUV = fixed2(xx,yy);
//还原中心
tmpUV += fixed2(0.5,0.5);
fixed4 col = tex2D(_MainTex, tmpUV);
return col;
}
ENDCG
}
}
制作流水效果
SubShader
{
// No culling or depth
//Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
float _WaveW;
float _WaveA;
v2f vert (appdata v)
{
v2f o;
v.vertex.y += _WaveA * sin(_WaveW * v.vertex.x + _Time.y);
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
float _Speed;
fixed4 frag (v2f i) : SV_Target
{
fixed2 tmpUV = i.uv;
tmpUV.x += _Time.y * _Speed;
fixed4 col = tex2D(_MainTex, tmpUV);
//fixed4 col = tex2D(_MainTex, i.uv);
// just invert the colors
//col = 1 - col;
return col;
}
ENDCG
}
}
OutLine
需要两次渲染,关闭需要显示物体的ZWrite,关闭ZTest则会一直显示,影响视觉,
SubShader
{
//渲染边框
Pass
{
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
float _OutLineWidth;
v2f vert (appdata v)
{
v2f o;
//加边框
v.vertex.xyz *= _OutLineWidth;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
fixed4 _OutLineColor;
fixed4 frag (v2f i) : SV_Target
{
//fixed4 col = tex2D(_MainTex, i.uv);
// just invert the colors
//col = 1 - col;
return _OutLineColor;
//return fixed4(1,0,0,1);
}
ENDCG
}
//渲染贴图
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
//加边框
v.vertex.xyz *=1.2;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
// just invert the colors
//col = 1 - col;
return col;
}
ENDCG
}
}
高斯模糊
通过设置多次贴图偏移,达到模糊效果
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _MainTex;
float _OffSet;
fixed4 frag (v2f i) : SV_Target
{
//自己
fixed4 col = tex2D(_MainTex, i.uv);
//上
fixed4 col2 = tex2D(_MainTex, i.uv + float2(0, _OffSet));
//下
fixed4 col3 = tex2D(_MainTex, i.uv - float2(0, _OffSet));
//左
fixed4 col4 = tex2D(_MainTex, i.uv + float2(-_OffSet,0));
//右
fixed4 col5 = tex2D(_MainTex, i.uv + float2(_OffSet, 0));
//上
col = (col + col2+ col3+ col4+ col5) / 5;
// just invert the colors
//col = 1 - col;
return col;
}
ENDCG
}
抗锯齿(多重采样)
时域进行卷积 = 频域乘积(叉乘低频滤波器),得到一张模糊处理的图片,对图片进行采样; 卷积操作类似这里的高斯模糊,这里写的高斯模糊是上下左右进行平均,卷积操作可以简化为n*n(n为大于2的奇数)的矩阵与原始图片叉乘取平均;
摄像机后处理
Unity生命周期里的后处理过程
/// <summary>
///
/// </summary>
/// <param name="src">OnRender以前的画面</param>
/// <param name="dest">更改以后传递回去的画面</param>
private void OnRenderImage(RenderTexture src, RenderTexture dest)
{
//用myMat对src图片渲染,之后存入dest
//src, texture, dest renderTexture
Graphics.Blit(src, dest, myMat);
}
Surface Shader
Tag 标签
Queue 渲染队列
当场景中跟game窗口中不一样的时候要加Queue
Unity渲染整个场景 将场景中的所有物体分类
从小到大渲染(可以设置"Queue" = “Background+1”)
Background:最先渲染(1000)
Geometry :渲染不透明的物体,实体物体(2000)
AlphaTest:要经过alpha测试的队列(2450)
Transparent:透明的,半透明的物体(3000)
Overlay:最后渲染(如UI)(4000)
RenderPath 渲染通道
相机上的属性 Rendering Path:
Use Graphics SettingsForward:质量要求低的 (渲染2次,效率高)Deferred:延迟渲染,次时代游戏 (渲染2次,效率高。)Legacy Vertex Lit:渲染顶点 (渲染1次,最快,支持硬件最多,效率最高,效果最差)Legacy Deferred:Unity 5.0之前的算法 (渲染3次,效率低)
Project Setting-> Graphics: 可以设置渲染通道
Seuface Shader使用
SurfcaceOutputStandard
struct SurfcaceOutputStandard { fixed3 Albedo;像素颜色,RGB fixed3 Normal;法线 half Emission;自发光 half Metallic;金属光泽,0没有金属,1最强烈 half Smoothness;粗糙度,0粗糙,1光滑 half Occlusion;默认1 高光遮罩 fixed Alpha;透明度 }
高光颜色 struct SurfcaceOutputStandardSpecular { … fixed3 Specular;高光 … }
自定义surface shader
自定义灯光入口函数:
//unity5之前的 #pragma light surf Custom
void vert(inout appdata_full v, out Input o) { o.uv_MainTex = v.texcoord.xy; }
//自定义表面着色器入口 #pragma surface surf Custom vertex:vert finalcolor:myColor
void surf(inout appdata_full v, out Input o) {
}
//自定义灯光入口函数 half4 LightingCustom (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) { return half4 (1,0,0,1); }
//自定义雾效果 void myColor(Input IN, SurfaceOutput o, inout fixed4 color) {
}
计算公式:
冯氏着色: [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IHpotTtY-1599059227728)(en-resource://database/402:1)]
Blinn phone着色:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vrDymkNp-1599059227731)(en-resource://database/403:1)]
雾:
float pos = length(UnityObjectToViewPos(v.vertex.xyz).xyz);
//距离计算
//float dencity = (pos - _fogStart) / (pos - _fogEnd)
//指数计算雾的浓度
float dencity = exp(- _fogDensity * abs(pos));
o.FogDensity = 1-dencity;
//菲尼尔公式 float fresnel = _fresnelBase + _fresnelScale*pow(1 - dot(N, V), _fresnelIndensity);
法线
设置法线贴图
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
//设置法线贴图
fixed4 normalColor = tex2D (_NormalTex, IN.uv_NormalTex);
//从法线贴图到fixed3类型的法线
o.Normal = UnpackNormal(normalColor);
o.Albedo = c.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = c.a;
}
边缘检测
o.Emission = _EmissionColor.rgb
* (1-clamp(
dot(o.Normal, IN.viewDir)
, 0, 1))
* _Power;
立方体贴图
真假反射
真反射:RenderTexture
需要IO写操作,影响性能
假反射:采集环境做成立方体贴图
使用: 立方体贴图采样 反射
//设置反射天空盒颜色o.Emission = texCUBE(_CubeMap, IN.worldRefl);
o.Emission = texCUBE(_CubeMap, IN.worldRefl);
C#编写代码
使用GL
https://docs.unity3d.com/ScriptReference/index.html
复制GL代码
透视投影变正交投影GL.LoadOrtho();