Shader intro
- Rendering Pipeline (Geometry and Rasterization process)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| Shader "Holistic/HelloShader" {
Properties {
_myColour ("Example Colour", Color) = (1,1,1,1)
_myEmission ("Example Emission", Color) = (1,1,1,1)
_myNormal ("Example Normal", Color) = (1,1,1,1)
}
SubShader {
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uvMainTex;
};
fixed4 _myColour;
fixed4 _myEmission;
fixed4 _myNormal;
void surf (Input IN, inout SurfaceOutput o){
o.Albedo = _myColour.rgb;
o.Emission = _myEmission.rgb;
o.Normal = _myNormal.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
|
Vector Recap
- v · w = vxwx + vywy
- [cross product] v x w = (vywz - vz wy )(1, 0, 0) + (vzwx - vxwz )(0,1,0) + (vxwy - vywx)(0,0,1)
Shader Essentials & Surface Shader
Surface Shaders
variables and packed arrays
- the code you write, you write as though you are only writing for one pixel. You don’t need to write loops that process all of the pixels that need to appear on the screen. GPU do the rest.
variables
fixed4 -> r,g,b,a or x,y,z,w
1
2
| colour1.a = 1;
colour2.x = 0;
|
Convention:
- position or vertices -> xyzw
- color -> rgba
Easy to copy
copy with different length
1
2
3
| fixed4 colour1 =(0,1,1,0);
fixed3 colour3;
colour3 = colour1.rgb;
|
Packed Arrays
1
2
3
| // they are same
fixed3 colour3 = 1;
colour3 = (1, 1, 1);
|
Masking
1
| colour1.rg = colour2.gr;
|
Packed Matrices
1
2
| fixed4 colour = matrix._m00_m01_m02_m03
fixed4 colour = matrix[0];
|
Mesh
1
2
3
4
| // The input struct in the shader code is where you declare any values from the mesh that you will need to manipulate in the shader function.
struct Input {
float2 uv_MainTex;
};
|
Shader Properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| Properties{
_myColor ("Example Color", Color) = (1,1,1,1)
_myRange("Example Range", Range(0,5))=1
_myTex(“Example Texture",2D)="white"{}
_myCube(“Example Cube",CUBE)=""{}
_myFloat(“Example Float", Float) = 0.5
_myVector("Example Vector", Vector) = (0.5,1,1,1)
}
fixed4 _myColor;
half _myRange;
sampler2D _myTex;
samplerCUBE _myCube;
float _myFloat;
float4 _myVector;
|
- How the color of texture -> albedo
1
| tex2D(_myTex, IN.uv_myTex)
|
- How to do refection skybox cube map
float3 worldRefl
- contains world reflection vector if surface shader does not write to o.Normal. See Reflect-Diffuse shader for example.
1
2
3
4
| void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = (tex2D(_myTex, IN.uv_myTex) * _myRange).rgb;
o.Emission = texCUBE (_myCube, IN.worldRefl).rgb;
}
|
Illuminating Surface
Normal Mapping
- z value is used to control brightness
1
2
3
4
5
6
| void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = tex2D(_myDiffuse, IN.uv_myDiffuse * _myScale).rgb;
o.Normal = UnpackNormal(tex2D(_myBump, IN.uv_myBump));
o.Normal *= float3 (_mySlider, _mySlider, 1);
}
|
Illumination Models
- not geometric normal
- pixel basis -> illumination model
- Flat
- only shade each polygon -> That gives the entire surface of a polygon the same color
- Gouraud
- works ok until you add highly localized light in one polygon -> not transfer to others
- Phong: Taking the actual normals at each vertex, the ones across the surface are calculated as an interpolation of one to another.
Switch From Phong -> Flat
- bumpMap + cubeMap -> world refection
1
2
3
4
5
6
7
8
9
10
11
12
| struct Input {
float2 uv_myDiffuse;
float2 uv_myBump;
float3 worldRefl; INTERNAL_DATA
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D(_myDiffuse, IN.uv_myDiffuse).rgb;
o.Normal = UnpackNormal(tex2D(_myBump, IN.uv_myBump)) * _myBright;
o.Normal *= float3(_mySlider,_mySlider,1);
o.Emission = texCUBE (_myCube, WorldReflectionVector (IN, o.Normal)).rgb;
}
|
1
2
3
4
| void surf (Input IN, inout SurfaceOutput o) {
o.Normal = UnpackNormal(tex2D(_myBump, IN.uv_myBump)) * 0.3;
o.Albedo = texCUBE (_myCube, WorldReflectionVector (IN, o.Normal)).rgb;
}
|
Buffer
Frame Buffer: Computer memory structure that holds the color information about every pixel that appears on the screen.
Z Buffer: the same dimensions as the frame buffer, but holds depth information for each pixel.
Anything behind will be ignored & Render From Front-to-Back: If the pixel trying to be added has a smaller depth value than the one already in the Z buffer, it means that it must be closer to the camera and therefore its color should replace the one already in the pixel buffer and then its depth is added to the Z buffer.
G buffer
Deferred Rendering
good when there is a lot of light
cannot display transparent object (because transparent objects are see-through, they need to display any lighting effects behind them and because lights are calculated at the end)
Forward Rendering [Default]
## Dot Product
1
2
3
4
5
6
7
8
9
10
| struct Input{
float3 viewDir;
};
void surf (Input IN, inout SurfaceOutput o){
half dotp = dot(IN.viewDir, o.Normal);
o.Albedo = float3(dotp, 1, 1);
// shows blue on the round (0, 1, 1)
// white on the edge (1, 1, 1)
}
|
Rim Lighting
- Normalize
- Saturate -> -1 to 1 map to 0 to 1
- pow
1
2
3
4
5
| void surf (Input IN, inout SurfaceOutput o)
{
half rim = 1 - saturate(dot(normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor * pow(rim, _RimPower);
}
|
Logical Cutoffs
1
2
3
4
5
6
7
8
9
10
11
12
13
| struct Input
{
float3 viewDir;
};
float4 _RimColor;
float _RimPower;
void surf (Input IN, inout SurfaceOutput o)
{
half rim = 1 - saturate(dot(normalize(IN.viewDir), o.Normal));
o.Emission = rim > 0.5 ? float3(1, 0, 0): rim > 0.3 ? float3(0, 1, 0): 0;
}
|
- WorldPos Cutline
- frac: give the fractional part = remainder
- x / 2 -> x * 0.5
1
2
3
4
5
6
7
| void surf (Input IN, inout SurfaceOutput o)
{
o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
half rim = 1 - saturate(dot(normalize(IN.viewDir), o.Normal));
o.Emission = frac(IN.worldPos.y * (20 - _StripeWidth) * 0.5) > 0.4 ?
float3(0, 1, 0) * rim : float3(1, 0, 0) * rim;
}
|
Lighting
[Specular Reflection] Blinn-Phong
PBR
- vertex lighting [Gouraud]: calculated at each vertex -> average across the surface
- pixel lighting [Phong]: more detailed high light, but more processing
Blinn-Phong
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| CGPROGRAM
#pragma surface surf BlinnPhong
// _Spec is predefined in the Unity
float4 _Colour;
half _Spec;
fixed _Gloss;
struct Input {
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o) {
o.Albedo = _Colour.rgb;
o.Specular = _Spec;
o.Gloss = _Gloss;
}
ENDCG
|
Physically Based Rendering
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| SubShader{
Tags{
"Queue" = "Geometry"
}
CGPROGRAM
#pragma surface surf StandardSpecular
sampler2D _MetallicTex;
fixed4 _Color;
struct Input {
float2 uv_MetallicTex;
};
void surf(Input IN, inout SurfaceOutputStandardSpecular o) {
o.Albedo = _Color.rgb;
o.Smoothness = tex2D (_MetallicTex, IN.uv_MetallicTex).r;
o.Specular = _SpecColor.rgb;
}
ENDCG
}
|
Custom Lighting Model
CustomLambert
1
2
3
4
5
6
7
| half4 LightingBasicLambert (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
c.a = s.Alpha;
return c;
}
|
CustomBlinn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #pragma surface surf BasicBlinn
// has the same heading "Lighting"
half4 LightingBasicBlinn (SurfaceOutput s, half3 lightDir, half3 viewDir, half atten) {
// format for the parameter in this function
half3 h = normalize (lightDir + viewDir);
half diff = max (0, dot (s.Normal, lightDir));
float nh = max (0, dot (s.Normal, h));
float spec = pow (nh, 48.0);
half4 c;
c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * atten;
c.a = s.Alpha;
return c;
}
|
ToonRamp - Ramp Texture
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| #pragma surface surf ToonRamp
float4 _Color;
sampler2D _RampTex;
float4 LightingToonRamp (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
float diff = dot (s.Normal, lightDir);
float h = diff * 0.5 + 0.5;
float2 rh = h;
float3 ramp = tex2D(_RampTex, rh).rgb;
float4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (ramp);
c.a = s.Alpha;
return c;
}
|
Pass & Blend
Alpha - Transparent
“Queue” = “Transparent” -> Z-Buffer -> Transparent
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| SubShader{
Tags{
"Queue" = "Transparent"
}
// since the z buffer is 0, make sure set the render queue
CGPROGRAM
//setting the "alpha:fade"
#pragma surface surf Lambert alpha:fade
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
|
Holograms - Pass
Pass Definition: Pass { . }
defines a block of code that represents a rendering pass within a shader. A pass encapsulates a set of rendering operations that should be performed together during a single rendering cycle.
Multiple Passes: Shader programs can have multiple passes, and each pass can have its own unique set of rendering operations. For example, you might have one pass for rendering the basic color of an object and another pass for applying a reflection effect.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| SubShader
{
Tags{"Queue" = "Transparent"}
Pass{
//Zbuffer on, but do not write color info
ZWrite On
ColorMask 0
}
CGPROGRAM
#pragma surface surf Lambert alpha:fade
struct Input
{
float3 viewDir;
};
float4 _RimColor;
float _RimPower;
void surf (Input IN, inout SurfaceOutput o)
{
half rim = 1 - saturate(dot(normalize(IN.viewDir), o.Normal));
o.Emission = _RimColor.rgb * pow(rim, _RimPower) * 10;
// same as RimLight
o.Alpha = pow(rim, _RimPower);
}
ENDCG
}
|
Blend
SrcFactor: taking the color that’s on the texture and multiplying it by 1
DestFactor: and then it’s also getting the color already in the frame buffer (whatever is behind this quad)
add -> brighter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| Shader "Custom/BlendTest"
{
Properties
{
_MainTex ("Texture", 2D) = "black" {}
}
SubShader
{
Tags { "RenderType"="Transparent" }
Blend One One
Pass{
SetTexture [_MainTex] {combine texture}
}
}
FallBack "Diffuse"
}
|
1
| Blend SrcAlpha OneMinusSrcAlpha
|
Cull - back face
1
2
3
4
5
6
7
8
9
10
| SubShader
{
Tags { "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
// Default "Cull back"
Cull Off
Pass{
SetTexture [_MainTex] {combine texture}
}
}
|
Blender two image
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| Shader "Holistic/BasicTextureBlend" {
Properties{
_MainTex ("MainTex", 2D) = "white" {}
_DecalTex ("Decal", 2D) = "white" {}
[Toggle] _ShowDecal("Show Decal?", Float) = 0
}
SubShader{
Tags{
"Queue" = "Geometry"
}
CGPROGRAM
#pragma surface surf Lambert
sampler2D _MainTex;
sampler2D _DecalTex;
float _ShowDecal;
struct Input {
float2 uv_MainTex;
};
void surf(Input IN, inout SurfaceOutput o) {
fixed4 a = tex2D(_MainTex, IN.uv_MainTex);
fixed4 b = tex2D(_DecalTex, IN.uv_MainTex) * _ShowDecal;
o.Albedo = b.r > 0.9 ? b.rgb: a.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
|
Stencil Buffer
Wall (be seen through)
1
2
3
4
5
6
| Stencil
{
Ref 1
Comp notequal
Pass keep
}
|
Hole (see-through glass)
1
2
3
4
5
6
7
8
9
10
| Tags { "Queue"="Geometry-1" }
ColorMask 0
ZWrite off
Stencil
{
Ref 1
Comp always
Pass replace
}
|
Select Function in Inspector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| Properties
{
_Color("Main Color", Color) = (1,1,1,1)
_SRef("Stencil Ref", Float) = 1
[Enum(UnityEngine.Rendering.CompareFunction)] _SComp("Stencil Comp", Float) = 8
[Enum(UnityEngine.Rendering.StencilOp)] _SOp("Stencil Op", Float) = 2
}
Stencil
{
Ref[_SRef]
Comp[_SComp]
Pass[_SOp]
}
|
Vertex & Fragment
Basic Structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
| Shader "Unlit/ColorVF"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
// vertex value from world space -> clipping space
float4 vertex : SV_POSITION;
float4 color : COLOR;
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
// vert -> return 0
// o -> i > frag (can't see)
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = fixed4(0, 1, 0, 1);
return col;
}
ENDCG
}
}
}
|
Material
- Ripple UV
- GrabPass [Mirror effect] [capture the screen]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
| SubShader
{
Tags{ "Queue" = "Transparent"}
// draw last -> avoid mirror inside the mirror
GrabPass{}
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;
};
sampler2D _GrabTexture;
sampler2D _MainTex;
float4 _MainTex_ST;
float _ScaleUVX;
float _ScaleUVY;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv.x = sin(o.uv.x * _ScaleUVX);
o.uv.y = sin(o.uv.y * _ScaleUVY);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_GrabTexture, i.uv);
return col;
}
ENDCG
}
|
Lighting
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
| SubShader
{
Pass
{
Tags {"LightMode"="ForwardBase"}
// calculate lighting at the beginning
// not deferred Lighting
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
fixed4 diff : COLOR0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
// convert normal in the mesh(local space) to world space
// To compare the noraml with lighting(in world space)
half3 worldNormal = UnityObjectToWorldNormal(v.normal);
// Lambert -> dot product
half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
o.diff = nl * _LightColor0;
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
col *= i.diff;
return col;
}
ENDCG
}
|
Shadow
one more draw call
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| Pass
{
Tags {"LightMode"="ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f {
// take vertex position and normal
V2F_SHADOW_CASTER;
};
v2f vert(appdata v)
{
v2f o;
// create the shadow data
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
float4 frag(v2f i) : SV_Target
{
// spit out the shadow color
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
|
accept shadow
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
| Pass
{
Tags {"LightMode"="ForwardBase"}
// calculate lighting at the beginning
// not deferred Lighting
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// ignore stuff to show shadow
#pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap novertexlight
#include "UnityCG.cginc"
#include "UnityLightingCommon.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct appdata {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
fixed4 diff : COLOR0;
// TRANSFER_SHADOW is looking for "pos"
float4 pos : SV_POSITION;
SHADOW_COORDS(1)
};
v2f vert (appdata v)
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
// convert normal in the mesh(local space) to world space
// To compare the noraml with lighting(in world space)
half3 worldNormal = UnityObjectToWorldNormal(v.normal);
// Lambert -> dot product
half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
o.diff = nl * _LightColor0;
// shadow(world space) -> v2f
TRANSFER_SHADOW(o)
return o;
}
sampler2D _MainTex;
fixed4 frag (v2f i) : SV_Target
{
fixed4 col = tex2D(_MainTex, i.uv);
// calculate the shadow
fixed shadow = SHADOW_ATTENUATION(i);
col.rgb *= i.diff * shadow;
return col;
}
ENDCG
}
|
Effect Examples
Vertex Extruding
use the vertex shader inside the code with a surface shader –> no need to write the lighting / shadow from vertex shader
inout: indicates that the parameter can be both an input (read-only) and an output (writable) parameter. In this case, appdata v
is both provided as input data for the function, and the function is allowed to modify it to change the vertex position.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| SubShader {
CGPROGRAM
// declear using vertex in the surface shader
#pragma surface surf Lambert vertex:vert
// --------- vertex part -----------
struct appdata {
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 texcoord: TEXCOORD0;
};
float _Amount;
// inout: the function is allowed to modify it to change the vertex position
void vert (inout appdata v) {
v.vertex.xyz += v.normal * _Amount;
}
sampler2D _MainTex;
// ---------- surface part--------
struct Input {
float2 uv_MainTex;
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
|
Outlining
Simple Outline
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| SubShader {
Tags { "Queue"="Transparent" } // make it on top of everything
ZWrite off // to overlay the orginal vertex
CGPROGRAM
#pragma surface surf Lambert vertex:vert
struct Input {
float2 uv_MainTex;
};
float _Outline;
float4 _OutlineColor;
void vert (inout appdata_full v) {
v.vertex.xyz += v.normal * _Outline;
}
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o)
{
o.Emission = _OutlineColor.rgb;
}
ENDCG
// --------- draw the mainTex ------------
ZWrite on
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float2 uv_MainTex;
};
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb;
}
ENDCG
}
|
Advanced Outline - Geometry
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| v2f vert(appdata v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
/* the normal vector (v.normal) of the vertex is transformed from object space to view space
by multiplying it with the inverse transpose of the Model-View matrix */
float3 norm = normalize(mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal));
// 2D offset -> transforms the x and y components of the normalized normal vector into screen space
float2 offset = TransformViewToProjection(norm.xy);
// on Geometry, not vertex position
o.pos.xy += offset * o.pos.z * _Outline;
o.color = _OutlineColor;
return o;
}
|
Glass - GrabPass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
| Shader "Holistic/Glass"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BumpMap ("Normalmap", 2D) = "bump" {}
_ScaleUV ("Scale", Range(1,5000)) = 1
}
SubShader
{
Tags{ "Queue" = "Transparent"}
GrabPass{}
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float4 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 uvgrab : TEXCOORD1;
float2 uvbump : TEXCOORD2;
float4 vertex : SV_POSITION;
};
sampler2D _GrabTexture; // captured using the GrabPass{} block
float4 _GrabTexture_TexelSize;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
float _ScaleUV;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
//add this to check if the image needs flipping
# if UNITY_UV_STARTS_AT_TOP
float scale = -1.0;
# else
float scale = 1.0f;
# endif
/*basing on where it positions itself within the screen space,
it calculates what the UVs would be
(or the equivalent UVs required) for our grab texture */
/* 0.5 texture coordinates -> normalized texture space:
The entire expression is multiplied by 0.5 at the end.
This is used to map the calculated coordinates to a normalized texture space,
where both the horizontal and vertical components range from 0 to 1. */
//include scale in this formulae as below
o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y * scale) + o.vertex.w) * 0.5;
o.uvgrab.zw = o.vertex.zw;
o.uv = TRANSFORM_TEX( v.uv, _MainTex );
o.uvbump = TRANSFORM_TEX( v.uv, _BumpMap );
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// add distortion
half2 bump = UnpackNormal(tex2D( _BumpMap, i.uvbump )).rg;
float2 offset = bump * _ScaleUV * _GrabTexture_TexelSize.xy;
i.uvgrab.xy = offset * i.uvgrab.z + i.uvgrab.xy;
// show mirror texture
fixed4 col = tex2Dproj( _GrabTexture, UNITY_PROJ_COORD(i.uvgrab));
fixed4 tint = tex2D(_MainTex, i.uv);
col *= tint;
return col;
}
ENDCG
}
}
}
|
Wave
void vert (inout appdata v, out Input o)
_Time
Surface shader struct Input
: ` float3 vertColor; &
float2 uv_MainTex;`
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
| Shader "Holistic/Waves" {
Properties {
_MainTex("Diffuse", 2D) = "white" {}
_Tint("Colour Tint", Color) = (1,1,1,1)
_Freq("Frequency", Range(0,5)) = 3
_Speed("Speed",Range(0,100)) = 10
_Amp("Amplitude",Range(0,1)) = 0.5
}
SubShader {
CGPROGRAM
#pragma surface surf Lambert vertex:vert
struct Input {
float2 uv_MainTex;
float3 vertColor;
};
float4 _Tint;
float _Freq;
float _Speed;
float _Amp;
struct appdata {
float4 vertex: POSITION;
float3 normal: NORMAL;
float4 texcoord: TEXCOORD0;
float4 texcoord1: TEXCOORD1;
float4 texcoord2: TEXCOORD2;
};
// changes appdata and Input value (modify vertex color)
void vert (inout appdata v, out Input o) {
// HLSL compiler
UNITY_INITIALIZE_OUTPUT(Input,o);
float t = _Time * _Speed;
// height value for vertex
float waveHeight = sin(t + v.vertex.x * _Freq) * _Amp +
sin(t*2 + v.vertex.x * _Freq*2) * _Amp;
v.vertex.y = v.vertex.y + waveHeight;
v.normal = normalize(float3(v.normal.x + waveHeight, v.normal.y, v.normal.z));
// trough darker in the bottom
// peak lighter in the top
o.vertColor = waveHeight + 2;
}
sampler2D _MainTex;
void surf (Input IN, inout SurfaceOutput o) {
float4 c = tex2D(_MainTex, IN.uv_MainTex);
o.Albedo = c * IN.vertColor.rgb; // add vertex color
}
ENDCG
}
Fallback "Diffuse"
}
|
Scrolling Texture
1
2
3
4
5
6
7
8
| void surf (Input IN, inout SurfaceOutput o) {
_ScrollX *= _Time;
_ScrollY *= _Time;
float3 water = (tex2D (_MainTex, IN.uv_MainTex + float2(_ScrollX, _ScrollY))).rgb;
float3 foam = (tex2D (_FoamTex, IN.uv_MainTex + float2(_ScrollX/2.0, _ScrollY/2.0))).rgb;
o.Albedo = (water + foam)/2.0;
}
|
Plasma
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| void surf (Input IN, inout SurfaceOutput o) {
const float PI = 3.14159265;
float t = _Time.x * _Speed;
//vertical
float c = sin(IN.worldPos.x * _Scale1 + t);
//horizontal
c += sin(IN.worldPos.z * _Scale2 + t);
//diagonal
c += sin(_Scale3*(IN.worldPos.x*sin(t/2.0) + IN.worldPos.z*cos(t/3))+t);
//circular
float c1 = pow(IN.worldPos.x + 0.5 * sin(t/5),2);
float c2 = pow(IN.worldPos.z + 0.5 * cos(t/3),2);
c += sin(sqrt(_Scale4*(c1 + c2)+1+t));
o.Albedo.r = sin(c/4.0*PI);
o.Albedo.g = sin(c/4.0*PI + 2*PI/4);
o.Albedo.b = sin(c/4.0*PI + 4*PI/4);
o.Albedo *= _Tint;
}
|
Misc
_SinTime
Built-in shader variables
In this case the _SinTime returns 4 values that change overtime
1
| c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * atten * _SinTime;
|
Shader Cheat Sheet
Lambert and BlinnPhong
1
2
3
4
5
6
7
8
9
| struct SurfaceOutput
{
fixed3 Albedo; // diffuse color
fixed3 Normal; // tangent space normal, if written
fixed3 Emission;
half Specular; // specular power in 01 range
fixed Gloss; // specular intensity
fixed Alpha; // alpha for transparencies
};
|
Standard
1
2
3
4
5
6
7
8
9
10
| struct SurfaceOutputStandard
{
fixed3 Albedo; // base (diffuse or specular) color
fixed3 Normal; // tangent space normal, if written
half3 Emission;
half Metallic; // 0=non-metal, 1=metal
half Smoothness; // 0=rough, 1=smooth
half Occlusion; // occlusion (default 1)
fixed Alpha; // alpha for transparencies
};
|
Standard Specular
1
2
3
4
5
6
7
8
9
10
| struct SurfaceOutputStandardSpecular
{
fixed3 Albedo; // diffuse color
fixed3 Specular; // specular color
fixed3 Normal; // tangent space normal, if written
half3 Emission;
half Smoothness; // 0=rough, 1=smooth
half Occlusion; // occlusion (default 1)
fixed Alpha; // alpha for transparencies
};
|
Vertex/Fragment Structures
AppData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| struct appdata_full {
float4 vertex : POSITION; //vertex xyz position
float4 tangent : TANGENT;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0; //uv coordinate for first set of UVs
float4 texcoord1 : TEXCOORD1; //uv coordinate for second set of UVs
float4 texcoord2 : TEXCOORD2; //uv coordinate for third set of UVs
float4 texcoord3 : TEXCOORD3; //uv coordinate for fourth set of UVs
fixed4 color : COLOR; //per-vertex colour
};
struct v2f
{
float4 pos : SV_POSITION; //The position of the vertex in clipping space.
float3 normal : NORMAL; //The normal of the vertex in clipping space.
float4 uv : TEXCOORD0; //UV from first UV set.
float4 textcoord1 : TEXCOORD1; //UV from second UV set.
float4 tangent : TANGENT; //A vector that runs at right angles to a normal.
float4 diff : COLOR0; //Diffuse vertex colour.
float4 spec : COLOR1; //Specular vertex colour.
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
| Shader "MultipassShader"
{
Properties //PROPERTIES BLOCK
{
_Color ("Main Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader //ENCLOSING SHADER BLOCK
{
//FIRST PASS - SURFACE SHADER DOES NOT REQUIRE PASS BLOCK
Tags { "Queue" = "Geometry+1" }
CGPROGRAM
#pragma surface surf BlinnPhong
float4 _Color;
struct Input
{
};
void surf (Input IN, inout SurfaceOutput o)
{
}
ENDCG
//SECOND PASS - ANOTHER SURFACE SHADER, NO PASS BLOCK REQUIRED
ZWrite Off
Blend DstColor Zero
CGPROGRAM
#pragma surface surf BlinnPhong
float4 _Color;
struct Input
{
};
void surf (Input IN, inout SurfaceOutput o)
{
}
ENDCG
//THIRD PASS - VERT/FRAG NEEDS TO BE ENCLOSED IN PASS
Pass
{
Tags { "LightMode" = "Always" }
ZWrite Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
struct v2f
{
};
v2f vert (appdata_full v)
{
}
half4 frag( v2f i ) : COLOR
{
}
ENDCG
}
//FOURTH PASS - SIMPLE SHADER LAB FUNCTIONS
Pass
{
Tags { "LightMode" = "Always" }
ZWrite Off
SetTexture [_MainTex]
{
combine constant* texture
}
}
}
Fallback "Diffuse
}
|