Skip to content

Instantly share code, notes, and snippets.

@kayk5654
Created February 13, 2022 02:36
Show Gist options
  • Save kayk5654/75ed9a3289a44aea41698a573f306bb2 to your computer and use it in GitHub Desktop.
Save kayk5654/75ed9a3289a44aea41698a573f306bb2 to your computer and use it in GitHub Desktop.
Shader "3DDrawing/InkSplash"
{
Properties
{
_SplatMap("Splat Map", 2D) = "white" {}
_ShadePower("Shade Power", Float) = 0.3
_OutlineWidth("Outline Width", Float) = 0.03
_MaxOutlineWidth("Max Outline Width", Float) = 0.05
[Toggle] _UseVColorForOutline("Use Vertex Color for Outline", Float) = 0
_OcclusionMap("Ambient Occlusion", 2D) = "white" {}
_NormalMap("Normal Map", 2D) = "bump"{}
_ShadowIntensity("Shadow Intensity", Float) = 1
_RubbedAreaMap("RubbedAreaMap", 2D) = "black" {}
_Splash("Splash Texture", 2D) = "black" {}
_SpriteSize("Sprite Size", Float) = 0.01
_SpriteDistance("Sprite Distance", Float) = 0.1
}
SubShader
{
Tags { "RenderType"="Opaque"}
LOD 100
// shadowcaster pass
Pass
{
Name "ShadowCast"
Tags {"LightMode" = "ShadowCaster"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
};
v2f vert(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
return o;
}
float4 frag(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
// main pass
Pass
{
Tags{ "LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
// shadow helper functions and macros
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float2 lightmapUv : TEXCOORD1;
float4 tangent : TANGENT;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normal : NORMAL;
float2 lightmapUv : TEXCOORD1;
float3 worldPos : TEXCOORD2;
float4 tangent : TANGENT;
float3 binormal : TEXCOORD4;
SHADOW_COORDS(3) // put shadows data into TEXCOORD3
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _SplatMap;
float4 _SplatMap_ST;
fixed _ShadePower;
sampler2D _OcclusionMap;
sampler2D _NormalMap;
half _ShadowIntensity;
sampler2D _RubbedAreaMap;
v2f vert (appdata v)
{
UNITY_SETUP_INSTANCE_ID(v);
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
o.normal = v.normal;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.lightmapUv = v.lightmapUv * unity_LightmapST.xy + unity_LightmapST.zw;
o.tangent = mul(unity_ObjectToWorld, v.tangent);
o.binormal = normalize(cross(v.normal, v.tangent.xyz) * v.tangent.w * unity_WorldTransformParams.w);
o.binormal = mul(unity_ObjectToWorld, o.binormal);
// compute shadows data
TRANSFER_SHADOW(o)
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// sample the texture
fixed4 col = fixed4(1, 1, 1, 1);
// normal map
float3 normalMap = UnpackNormal(tex2D(_NormalMap, i.uv));
// convert normal map from tangent space to world space
normalMap = (i.tangent * normalMap.x) + (i.binormal * normalMap.y) + (i.normal * normalMap.z);
// convert normal local to world space
float3 worldNormal = UnityObjectToWorldNormal(i.normal);
// test with simple lighting using dot product
float NdotL = dot(_WorldSpaceLightPos0.xyz, normalize(worldNormal + normalMap));
// test with lightmap
half3 lightmap = DecodeLightmap(UNITY_SAMPLE_TEX2D(unity_Lightmap, i.lightmapUv));
// test environmental lighting using a shading function on UnityCG.cginc
half3 indirectLighting = ShadeSH9(half4(i.normal, 1));
// apply splat map on the result of lighting
float splatPattern = tex2D(_SplatMap, i.uv * _SplatMap_ST.xy + _SplatMap_ST.zw).r;
// make tiling pattern difficult to recognize
splatPattern *= tex2D(_SplatMap, i.uv * (_SplatMap_ST.xy * 2) + _SplatMap_ST.xy * 0.3).r;
splatPattern *= tex2D(_SplatMap, i.uv * (_SplatMap_ST.xy * 0.5) + _SplatMap_ST.xy * 0.7).r;
// cross hatching
//splatPattern = lerp(splatPattern, tex2D(_SplatMap, i.uv.yx * _SplatMap_ST.yx + _SplatMap_ST.yx).r, 0.5);
// adjust weight of splat pattern
splatPattern = pow(splatPattern, _ShadePower);
half3 lighting = lerp(1, clamp(NdotL, 0, 1), _ShadowIntensity) * 0.5 + lightmap + indirectLighting;
// compute shadow attenuation (1.0 = fully lit, 0.0 = fully shadowed)
fixed shadow = lerp(1, SHADOW_ATTENUATION(i), _ShadowIntensity);
lighting *= shadow;
lighting *= tex2D(_OcclusionMap, i.uv);
half grayscaleLighting = (lighting.r + lighting.g + lighting.b) / 3;
col.rgb = grayscaleLighting > splatPattern ? 1 : 0;
// rubbing indirect light area
//half rubbedArea = tex2D(_RubbedAreaMap, float2(0, grayscaleLighting)).r;
//col.rgb = lerp(col.rgb, tex2D(_SplatMap, i.uv * _SplatMap_ST.xy * 0.1 + _SplatMap_ST.zw + i.tangent.xy * abs(NdotL)).r * 0.1, rubbedArea);
//col.rgb = lighting;
return col;
}
ENDCG
}
// outline pass
Pass
{
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float3 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
float3 color : COLOR;
UNITY_VERTEX_OUTPUT_STEREO
};
half _OutlineWidth;
fixed _UseVColorForOutline;
half _MaxOutlineWidth;
v2f vert(appdata v)
{
UNITY_SETUP_INSTANCE_ID(v);
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
// convert normal local to world space
float3 normal = normalize(mul((float3x3)UNITY_MATRIX_IT_MV, v.normal));
//normal = UnityObjectToWorldNormal(v.normal); // another way to convert normal from local to world
float2 offset = TransformViewToProjection(normal.xy);
if (_UseVColorForOutline == 0)
{
o.vertex.xy += offset * min(o.vertex.z * _OutlineWidth, _MaxOutlineWidth);
}
else
{
// if the model contains curvature as vertex color, use this process
o.vertex.xy += offset * min(o.vertex.z * _OutlineWidth * (v.color.r + v.color.g) * 0.5, _MaxOutlineWidth);
}
o.color = v.color;
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
// set outline color
fixed4 col = fixed4(0, 0, 0, 1);
//fixed4 col = fixed4(i.color, 1);
return col;
}
ENDCG
}
// splash sprites pass
Pass
{
Tags{ "RenderType" = "TransparentCutout" "Queue" = "Transparent+1" }
Blend DstColor Zero // multiply
CGPROGRAM
#pragma target 5.0
#pragma vertex vert
#pragma geometry geom
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float3 color : COLOR;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2g {
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normal : NORMAL;
float3 color : COLOR;
UNITY_VERTEX_OUTPUT_STEREO
};
struct g2f {
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _Splash;
float4 _Splash_ST;
half _SpriteSize;
half _SpriteDistance;
v2g vert(appdata v)
{
UNITY_SETUP_INSTANCE_ID(v);
v2g o;
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.vertex = float4(worldPos, 1);
o.uv = TRANSFORM_TEX(v.uv, _Splash);
o.normal = UnityObjectToWorldNormal(v.normal);
o.color = v.color;
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
return o;
}
[maxvertexcount(4)]
void geom(point v2g input[1], inout TriangleStream<g2f> outStream)
{
// test with simple lighting using dot product
float NdotL = dot(_WorldSpaceLightPos0.xyz, normalize(input[0].normal));
if(input[0].color.r > 0.001 && abs(NdotL) < 0.2)
{
// define a base value
float4 pos = input[0].vertex + float4(input[0].normal, 1) * _SpriteDistance;
// prepare a matrix for billboards
float4x4 billboardMatrix = UNITY_MATRIX_V;
billboardMatrix._m03 = 0;
billboardMatrix._m13 = 0;
billboardMatrix._m23 = 0;
billboardMatrix._m33 = 0;
float spriteSize = _SpriteSize * input[0].color.r * saturate(abs(NdotL));
// calculate position of each vertex of a sprite
g2f o0;
o0.uv = float2(0, 0);
o0.vertex = pos + mul(float4((o0.uv * 2 - float2(1, 1)) * spriteSize, 0, 1), billboardMatrix);
o0.vertex = mul(UNITY_MATRIX_VP, o0.vertex);
g2f o1;
o1.uv = float2(0, 1);
o1.vertex = pos + mul(float4((o1.uv * 2 - float2(1, 1)) * spriteSize, 0, 1), billboardMatrix);
o1.vertex = mul(UNITY_MATRIX_VP, o1.vertex);
g2f o2;
o2.uv = float2(1, 0);
o2.vertex = pos + mul(float4((o2.uv * 2 - float2(1, 1)) * spriteSize, 0, 1), billboardMatrix);
o2.vertex = mul(UNITY_MATRIX_VP, o2.vertex);
g2f o3;
o3.uv = float2(1, 1);
o3.vertex = pos + mul(float4((o3.uv * 2 - float2(1, 1)) * spriteSize, 0, 1), billboardMatrix);
o3.vertex = mul(UNITY_MATRIX_VP, o3.vertex);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o0);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o1);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o2);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o3);
outStream.Append(o0);
outStream.Append(o1);
outStream.Append(o2);
outStream.Append(o3);
}
}
fixed4 frag(g2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_Splash, i.uv) * fixed4(0, 0, 0, 1);
fixed cutoff = 0.5;
clip(col.a - cutoff);
return col;
}
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment