Shader Learing(ShaderLab syntax篇)
回顧:
回顧Unity中,數據的傳遞及Shader的工作原理:
從上圖我們可以看出,在渲染管線中,頂點着色器輸入的數據經過頂點着色器後,頂點着色器會把相關數據傳遞給片段着色器,上圖即表明了在Unity的頂點着色器和片段着色器中數據的傳遞類型和傳遞方向。
/*
齊次座標表示是計算機圖形學的重要手段之一,它既能夠用來明確區分向量和點
同時也更易用於進行仿射(線性)幾何變換。
*/
Shader "Custom/004_RGBCube" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert // vert function is the vertex shader
#pragma fragment frag // frag function is the fragment shader
// for multiple vertex output parameters an output structure
// is defined:
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : TEXCOORD0;
};
//POSITION語意用於頂點着色器,用來指定這些個位置座標值,是在變換前的頂點的object space座標。
//SV_POSITION語意則用於像素着色器,用來標識經過頂點着色器變換之後的頂點座標。
vertexOutput vert(float4 vertexPos : POSITION)
// vertex shader
{
vertexOutput output; // we don't need to type 'struct' here
output.pos = mul(UNITY_MATRIX_MVP, vertexPos);
output.col = vertexPos + float4(0.5, 0.5, 0.5, 0.0);
// Here the vertex shader writes output data
// to the output structure. We add 0.5 to the
// x, y, and z coordinates, because the
// coordinates of the cube are between -0.5 and
// 0.5 but we need them between 0.0 and 1.0.
return output;
}
float4 frag(vertexOutput input) : COLOR // fragment shader
{
return input.col;
// Here the fragment shader returns the "col" input
// parameter with semantic TEXCOORD0 as nameless
// output parameter with semantic COLOR.
}
ENDCG
}
}
}
ShaderBuilt-in Vertex Input:
我們知道Unity Shader編程中,可以使用結構體來把頂點輸入數據,如上面的示例,其實在Unity Shader編程中,有很多內建的結構體可以讓我們很方便的使用,下面使用Unity內建的結構體實現上面的示例:
Shader "Custom/005_Build_InShader" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct vertexOutput {
float4 pos : SV_POSITION;
float4 col : TEXCOORD0;
};
vertexOutput vert(appdata_full input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
output.col = input.texcoord;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
return input.col;
}
ENDCG
}
}
}
/*
struct vertexInput {
float4 vertex : POSITION; // position (in object coordinates,
// i.e. local or model coordinates)
float4 tangent : TANGENT;
// vector orthogonal to the surface normal
float3 normal : NORMAL; // surface normal vector (in object
// coordinates; usually normalized to unit length)
float4 texcoord : TEXCOORD0; // 0th set of texture
// coordinates (a.k.a. “UV”; between 0 and 1)
float4 texcoord1 : TEXCOORD1; // 1st set of tex. coors.
float4 texcoord2 : TEXCOORD2; // 2nd set of tex. coors.
float4 texcoord3 : TEXCOORD3; // 3rd set of tex. coors.
fixed4 color : COLOR; // color (usually constant)
};
預定義結構體:
pre-defined input structures appdata_base, appdata_tan, appdata_full
and appdata_img for the most common cases.
These are defined in the file UnityCG.cginc
(in the directory Unity > Editor > Data > CGIncludes):
struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct appdata_tan {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct appdata_full {
float4 vertex : POSITION;
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
float4 texcoord1 : TEXCOORD1;
float4 texcoord2 : TEXCOORD2;
float4 texcoord3 : TEXCOORD3;
fixed4 color : COLOR;
// and additional texture coordinates only on XBOX360
};
struct appdata_img {
float4 vertex : POSITION;
half2 texcoord : TEXCOORD0;
};
*/
uniform:
uniform變量一般用來表示:變換矩陣,材質,光照參數和顏色等信息。
Shader "Custom/006_Uniforms" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// uniform float4x4 _Object2World;
// automatic definition of a Unity-specific uniform parameter
struct vertexInput {
float4 vertex : POSITION;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 position_in_world_space : TEXCOORD0;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
output.position_in_world_space =
mul(_Object2World, input.vertex);
// transformation of input.vertex from object
// coordinates to world coordinates;
return output;
}
float4 frag(vertexOutput input) : COLOR
{
float dist = distance(input.position_in_world_space,
float4(0.0, 0.0, 0.0, 1.0));
// computes the distance between the fragment position
// and the origin (the 4th coordinate should always be
// 1 for points).
if (dist < 5.0)
{
return float4(0.0, 1.0, 0.0, 1.0);
// color near origin
}
else
{
return float4(0.1, 0.1, 0.1, 1.0);
// color far from origin
}
}
ENDCG
}
}
}
/*
uniform float4 _Time, _SinTime, _CosTime; // time values
uniform float4 _ProjectionParams;
// x = 1 or -1 (-1 if projection is flipped)
// y = near plane; z = far plane; w = 1/far plane
uniform float4 _ScreenParams;
// x = width; y = height; z = 1 + 1/width; w = 1 + 1/height
uniform float3 _WorldSpaceCameraPos;
uniform float4x4 _Object2World; // model matrix
uniform float4x4 _World2Object; // inverse model matrix
uniform float4 _WorldSpaceLightPos0;
// position or direction of light source for forward rendering
uniform float4x4 UNITY_MATRIX_MVP; // model view projection matrix
uniform float4x4 UNITY_MATRIX_MV; // model view matrix
uniform float4x4 UNITY_MATRIX_V; // view matrix
uniform float4x4 UNITY_MATRIX_P; // projection matrix
uniform float4x4 UNITY_MATRIX_VP; // view projection matrix
uniform float4x4 UNITY_MATRIX_T_MV;
// transpose of model view matrix
uniform float4x4 UNITY_MATRIX_IT_MV;
// transpose of the inverse model view matrix
uniform float4 UNITY_LIGHTMODEL_AMBIENT; // ambient color
*/
Uniform應用的第二個示例:
Shader "Custom/007_UserSpecifiedUniforms" {
Properties {
_Point ("a point in world space", Vector) = (0., 0., 0., 1.0)
_DistanceNear ("threshold distance", Float) = 5.0
_ColorNear ("color near to point", Color) = (0.0, 1.0, 0.0, 1.0)
_ColorFar ("color far from point", Color) = (0.3, 0.3, 0.3, 1.0)
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
// defines _Object2World and _World2Object
// uniforms corresponding to properties
uniform float4 _Point;
uniform float _DistanceNear;
uniform float4 _ColorNear;
uniform float4 _ColorFar;
struct vertexInput {
float4 vertex : POSITION;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 position_in_world_space : TEXCOORD0;
};
vertexOutput vert(vertexInput input)
{
vertexOutput output;
output.pos = mul(UNITY_MATRIX_MVP, input.vertex);
output.position_in_world_space =
mul(_Object2World, input.vertex);
return output;
}
float4 frag(vertexOutput input) : COLOR
{
float dist = distance(input.position_in_world_space,
_Point);
// computes the distance between the fragment position
// and the position _Point.
if (dist < _DistanceNear)
{
return _ColorNear;
}
else
{
return _ColorFar;
}
}
ENDCG
}
}
}
/*
Shader的屬性可以在腳本中被使用:
eg:
GetComponent(Renderer).sharedMaterial.SetVector("_Point",
Vector4(1.0, 0.0, 0.0, 1.0));
GetComponent(Renderer).sharedMaterial.SetFloat("_DistanceNear",
10.0);
GetComponent(Renderer).sharedMaterial.SetColor("_ColorNear",
Color(1.0, 0.0, 0.0));
GetComponent(Renderer).sharedMaterial.SetColor("_ColorFar",
Color(1.0, 1.0, 1.0));
*/
這樣,我們可以通過持有屬性來達到修改的目的。
Pass(通道):RenderSetup+固定功能着色器命令
Unity中一個SubShader(渲染方案)是由一個個Pass塊來執行的。每個Pass都會消耗對應的一個DrawCall。在滿足渲染效果的情況下儘可能地減少Pass的數量。
Pass(通道編程)
1個Pass塊可以使一個幾何物體被一次渲染。
Pass { [Name and Tags] [RenderSetup] [TextureSetup] }
最基礎的pass命令包含有1個可選的渲染設置命令列表,及1個可選的可用紋理列表。
一個Pass 可以定義它的名字和任意數量的標籤-name/value 字符串 將Pass的含義告訴給渲染引擎。
Render Setup 渲染設置
Pass設置了一系列的顯卡狀態,比如說alpha混合是否開啓,是否允許霧化等等。 這些命令有:
①Color color
Shader "Custom/NewShader" {
SubShader {
Pass { Color (1,0,0,0) } //將對象設置爲固體顏色
}
}
②Material {Material Block} 定義一個使用頂點光照管線的材質
Shader "Custom/NewShader" {
SubShader {
Pass {
Material { //材質塊用於定義對象的材質屬性
Diffuse (1,1,1,1)
Ambient (1,1,1,1)
}
Lighting On
}
}
}
③Lighting On|Off 開啓或關閉頂點光照
Shader "Custom/NewShader" {
Properties {
_Color ("Main Color", COLOR) = (1,1,1,1)
}
SubShader {
Pass {
Material {
Diffuse [_Color]
Ambient [_Color]
}
Lighting On
//對於在材質塊中定義的設置有任何影響,您必須啓用燈光與燈光的命令。如果燈光是關閉的,顏色是直接從顏色命令。
}
}
}
④SeparateSpecular On|Off 此命令使鏡面照明添加到着色通道的末端,所以鏡面照明不受紋理的影響。
⑤ColorMaterial AmbientAndDiffuse|Emission 使用每個頂點顏色代替材質中設置的顏色。
Shader "Custom/NewShader" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0)
_SpecColor ("Spec Color", Color) = (1,1,1,1)
_Emission ("Emmisive Color", Color) = (0,0,0,0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Pass {
Material {
Diffuse [_Color]//Diffuse color: 漫反射顏色分量。這是一個對象的基本顏色。
Ambient [_Color]//Ambient color: 環境顏色分量。這是對象的顏色,在照明窗口中被環境光照射
Shininess [_Shininess]//Shininess number: 突出的清晰度,在1和0之間
Specular [_SpecColor]//Specular color: 對象的高光的顏色
Emission [_Emission]//Emission color: 物體不被任何光線擊中時的顏色
}
Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
Combine texture * primary DOUBLE, texture * primary
}
}
}
}
⑥Cull Back|Front|Off 設置多邊形剔除模式,控制多邊形應該剔除(不繪製)的面
Shader "Custom/NewShader" { //對象只渲染對象的背面
SubShader {
Pass {
Material {
Diffuse (1,1,1,1)
}
Lighting On
Cull Front
}
}
}
⑦ZWrite On|Off 設置深度寫模式,控制像素從這個對象是否寫入深度緩衝
Shader "Custom/NewShader" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB) Trans (A)", 2D) = "white" {}
}
SubShader {
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
LOD 200
// extra pass that renders to depth buffer only
Pass {
ZWrite On // 開啓深度緩衝寫
ColorMask 0 //關閉所有渲染染色通道
}
// paste in forward rendering passes from Transparent/Diffuse
UsePass "Transparent/Diffuse/FORWARD"
}
//Fallback "Transparent/VertexLit"
}
⑧ZTest Less|Greater|LEqual|GEqual|Equal|NotEqual|Always 設置深度測試模式
⑨Offset Factor, Units 允許您指定兩個參數的深度偏移量
Shader "Custom/NewShader" {
Properties {
_Color ("Main Color", Color) = (1,1,1,0)
_SpecColor ("Spec Color", Color) = (1,1,1,1)
_Emission ("Emmisive Color", Color) = (0,0,0,0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
_MainTex ("Base (RGB)", 2D) = "white" { }
}
SubShader {
// We use the material in many passes by defining them in the subshader.
// Anything defined here becomes default values for all contained passes.
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]
}
Lighting On
SeparateSpecular On
// Set up alpha blending
Blend SrcAlpha OneMinusSrcAlpha
// Render the back facing parts of the object.
// If the object is convex, these will always be further away
// than the front-faces.
Pass {
Cull Front
SetTexture [_MainTex] {
Combine Primary * Texture
}
}
// Render the parts of the object facing us.
// If the object is convex, these will be closer than the
// back-faces.
Pass {
Cull Back
SetTexture [_MainTex] {
Combine Primary * Texture
}
}
}
}
⑩SetTexture [TextureName] {Texture Block}
Shader "Custom/NewShader" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" { }
}
SubShader {
// Render the front-facing parts of the object.
// We use a simple white material, and apply the main texture.
Pass {
Material {
Diffuse (1,1,1,1)
}
Lighting On
SetTexture [_MainTex] {
Combine Primary * Texture
}
}
// Now we render the back-facing triangles in the most
// irritating color in the world: BRIGHT PINK!
Pass {
Color (1,0,1,1)
Cull Front
}
}
}
//傳統的紋理合成
//Previous 是之前SetTexture的結果。
//Primary 是從照明計算或頂點顏色的顏色。
//Texture 紋理是在SetTexture TextureName指定紋理的顏色(見上圖)。
//Constant 是ConstantColor指定的顏色。
⑪SetTexture [_MainTex] { combine previous * texture, previous + texture }
⑫Fog {Fog Commands} 設置霧參數
⑬Mode Off|Global|Linear|Exp|Exp2
⑭Color ColorValue
⑮Density FloatValue
⑯Range FloatValue, FloatValue
⑰AlphaTest Off 開啓alpha測試
⑱AlphaTest comparison AlphaValue
Shader "Custom/NewShader" {
Properties {
_Color ("Main Color", Color) = (.5, .5, .5, .5)
_MainTex ("Base (RGB) Alpha (A)", 2D) = "white" {}
_Cutoff ("Base Alpha cutoff", Range (0,.9)) = .5
}
SubShader {
// Set up basic lighting
Material {
Diffuse [_Color]
Ambient [_Color]
}
Lighting On
// Render both front and back facing polygons.
Cull Off
// first pass:
// render any pixels that are more than [_Cutoff] opaque
Pass {
AlphaTest Greater [_Cutoff]
SetTexture [_MainTex] {
combine texture * primary, texture
}
}
// Second pass:
// render in the semitransparent details.
Pass {
// Dont write to the depth buffer
ZWrite off
// Don't write pixels we have already written.
ZTest Less
// Only render pixels less or equal to the value
AlphaTest LEqual [_Cutoff]
// Set up alpha blending
Blend SrcAlpha OneMinusSrcAlpha
SetTexture [_MainTex] {
combine texture * primary, texture
}
}
}
}
⑲Blend Off 設置α混合模式
Blend SrcFactor DstFactor
Blend SrcFactor DstFactor, SrcFactorA DstFactorA
BlendOp BlendOp
Shader "Custom/NewShader" {
Properties {
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB) Transparency (A)", 2D) = "white" {}
_Reflections ("Base (RGB) Gloss (A)", Cube) = "skybox" { TexGen CubeReflect }
}
SubShader {
Tags { "Queue" = "Transparent" }
Pass {
Blend SrcAlpha OneMinusSrcAlpha
Material {
Diffuse [_Color]
}
Lighting On
SetTexture [_MainTex] {
combine texture * primary double, texture * primary
}
}
Pass {
Blend One One
Material {
Diffuse [_Color]
}
Lighting On
SetTexture [_Reflections] {
combine texture
Matrix [_Reflection]
}
}
}
}
Per-pixel Lighting 逐像素光照
逐像素光照管道的工作依賴於渲染對象在多個pass中。Unity3D一旦獲得環境光和任何頂點的光照就開始渲染物體。然後,它在一個單獨添加的pass中影響渲染的物體每1個光照的像素。
Per-vertex Lighting 逐頂點光照
逐頂點光照是Direct3d/OpenGl的標準光照模式,它的計算依賴於每一個頂點。打開光照後才能啓動逐頂點光照。光照受Materialblock, ColorMaterial 和 SeparateSpecular命令影響。
有幾個特殊的pass可以重用公共的功能或實現一些高級效果。
UsePass 包含從另外的Shader中已被命名的pass。
GrabPass 抓取屏幕內容並轉換成紋理,可以給之後的pass使用,用來做特效最好了。