Skip to content

Instantly share code, notes, and snippets.

@kayk5654
Created February 13, 2022 02:31
Show Gist options
  • Save kayk5654/42df15a9c1ddec4b1134e1e2684eaed1 to your computer and use it in GitHub Desktop.
Save kayk5654/42df15a9c1ddec4b1134e1e2684eaed1 to your computer and use it in GitHub Desktop.
Shader "3DDrawing/WaterColorPainting"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Color", COLOR) = (1,1,1,1)
[Header(Graphic Pattern Settings)]
_PaintMap("Paint map", 2D) = "white" {}
_SecondaryTilingOffset("Secondary Tiling Offset", Vector) = (1,1,0,0)
_PatternPower("Pattern Power", Float) = 1
_ColorMap("Color map", 2D) = "white" {}
_AlphaMap("Alpha map", 2D) = "white" {}
_SecondaryAlphaTilingOffset("Secondary Alpha Tiling Offset", Vector) = (1,1,0,0)
_AlphaMul("Alpha Multiplier", Float) = 1
_AlphaPower("Alpha Poser", Float) = 1
[Header(FlowMap Settings)]
[Toggle] _UseFlowMap("Use FlowMap", Float) = 0
_FlowMap("Flowmap", 2D) = "white" {}
_FlowSpeed("Flow Speed", Float) = 1
[Header(Pseudo Color Bleeding)]
[Toggle] _UsePsuedoColorBleeding("Use Pseudo Color Bleeding", Float) = 0
_CubeMap("CubeMap", CUBE) = "white" {}
_BoxCenter("Center of Box Projection Boundary", Vector) = (0, 0, 0, 0)
_BoxMax("Max Box Projection Boundary", Vector) = (0.5, 0.5, 0.5, 0)
_BoxMin("Min Box Projection Boundary", Vector) = (-0.5, -0.5, -0.5, 0)
[Header(Lighting)]
_ShadowIntensity("Shadow Intensity", Range(0,1)) = 1
}
SubShader
{
Tags { "RenderType" = "Transparent"}
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
}
// prevent from drawing hidden front surface
Pass
{
ZWrite On
ColorMask 0
}
// main pass
Pass
{
Tags { "LightMode" = "ForwardBase" "Queue" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "ShaderCalculationHelper.cginc"
// shadow helper functions and macros
#include "AutoLight.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
float2 lightmapUv : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
float3 normal : NORMAL;
float2 lightmapUv : TEXCOORD1;
float3 worldPos : TEXCOORD2;
float3 viewDir : TEXCOORD4;
SHADOW_COORDS(3) // put shadows data into TEXCOORD3
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
sampler2D _PaintMap;
fixed4 _PaintMap_ST;
fixed4 _SecondaryTilingOffset;
half _PatternPower;
sampler2D _ColorMap;
sampler2D _AlphaMap;
fixed4 _AlphaMap_ST;
fixed4 _SecondaryAlphaTilingOffset;
half _AlphaMul;
half _AlphaPower;
fixed _UseVColorForAlpha;
UNITY_DECLARE_TEXCUBE(_CubeMap);
fixed _UseFlowMap;
sampler2D _FlowMap;
half _FlowSpeed;
fixed _UsePsuedoColorBleeding;
float3 _BoxCenter;
float3 _BoxMax;
float3 _BoxMin;
half _ShadowIntensity;
v2f vert (appdata v)
{
UNITY_SETUP_INSTANCE_ID(v);
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.lightmapUv = v.lightmapUv * unity_LightmapST.xy + unity_LightmapST.zw;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.normal = v.normal;
o.viewDir = normalize(UnityWorldSpaceViewDir(o.worldPos));
// compute shadows data
TRANSFER_SHADOW(o)
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
return o;
}
// sample 2d alpha texture using 3d position
float sample2DAlphaFromPosition(float3 pos)
{
float alphaX = tex2D(_AlphaMap, pos.yz * _AlphaMap_ST.xy + _AlphaMap_ST.zw).r;
float alphaY = tex2D(_AlphaMap, pos.xz * _AlphaMap_ST.xy + _AlphaMap_ST.zw).r;
float alphaZ = tex2D(_AlphaMap, pos.yx * _AlphaMap_ST.xy + _AlphaMap_ST.zw).r;
alphaX = lerp(alphaX, tex2D(_AlphaMap, pos.yz * _SecondaryAlphaTilingOffset.xy + _SecondaryAlphaTilingOffset.zw).r, 0.5);
alphaY = lerp(alphaY, tex2D(_AlphaMap, pos.xz * _SecondaryAlphaTilingOffset.xy + _SecondaryAlphaTilingOffset.zw).r, 0.5);
alphaZ = lerp(alphaZ, tex2D(_AlphaMap, pos.yx * _SecondaryAlphaTilingOffset.xy + _SecondaryAlphaTilingOffset.zw).r, 0.5);
return (alphaX + alphaY + alphaZ) / 3;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv) * _Color;
float3 worldNormal = UnityObjectToWorldNormal(i.normal);
/*** psuedo color bleeding ***/
fixed4 bleedColor = fixed4(1, 1, 1, 0);
if (_UsePsuedoColorBleeding == 1)
{
float3 boxProjSampleVector = boxProjection(i.worldPos - _BoxCenter, i.worldPos, _BoxCenter, _BoxMin, _BoxMax);
bleedColor = UNITY_SAMPLE_TEXCUBE(_CubeMap, boxProjSampleVector);
float bleedLerpFactor = length(i.worldPos - _BoxCenter) / max(length(_BoxMin - _BoxCenter), length(_BoxMax - _BoxCenter));
bleedColor = lerp(fixed4(bleedColor.rgb, 0), bleedColor, bleedLerpFactor);
}
/*** calculate lighting ***/
// test with simple lighting using dot product
float NdotL = dot(_WorldSpaceLightPos0.xyz, worldNormal);
// 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));
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), saturate(_ShadowIntensity));
lighting *= shadow;
/*** create painting pattern ***/
float normalViewDot = dot(worldNormal, i.viewDir);
float power = (1 - lighting) * _PatternPower;
float paintPattern = tex2D(_PaintMap, i.uv * _PaintMap_ST.xy + _PaintMap_ST.zw).r;
// blend paint maps using lerp
paintPattern = lerp(paintPattern, tex2D(_PaintMap, i.uv * _SecondaryTilingOffset.xy + _SecondaryTilingOffset.zw), 0.5);
paintPattern = saturate(pow(paintPattern, power));
// sample color ramp by lighting result
float NdotLLighting = fitRange(NdotL, -1, 1, 0, 1);
float otherLighting = fitRange(lightmap + indirectLighting, 0, 1, 0.5, 1);
float2 sampleCoord = float2((NdotLLighting + otherLighting) * 0.5 * shadow + (paintPattern - 0.5), 0.5);
fixed3 colorRamp = tex2D(_ColorMap, sampleCoord).rgb;
col.rgb *= colorRamp;
/* apply flow map on alpha */
float2 flowDir = float2(0, 0);
float progress1 = 0;
float progress2 = 0;
if (_UseFlowMap == 1)
{
// set uv range of flow map uv from -0.5 to 0.5
flowDir.xy = tex2D(_FlowMap, i.uv) - 0.5;
// loop time between 0 and 1
progress1 = frac(_Time.x * _FlowSpeed);
// offset time looping 0.5
progress2 = frac(_Time.x * _FlowSpeed + 0.5);
}
// create 2 uv layouts for each texture tiling offset
float2 uv1_PrimaryAlpha = i.uv * _AlphaMap_ST.xy + _AlphaMap_ST.zw + flowDir * progress1;
float2 uv2_PrimaryAlpha = i.uv * _AlphaMap_ST.xy + _AlphaMap_ST.zw + flowDir * progress2;
float2 uv1_SecondaryAlpha = i.uv * _SecondaryAlphaTilingOffset.xy + _SecondaryAlphaTilingOffset.zw + flowDir * progress1;
float2 uv2_SecondaryAlpha = i.uv * _SecondaryAlphaTilingOffset.xy + _SecondaryAlphaTilingOffset.zw + flowDir * progress2;
// interpolate loop end
float flowLerpRate = abs((0.5 - progress1) / 0.5);
float primaryAlpha = lerp(tex2D(_AlphaMap, uv1_PrimaryAlpha), tex2D(_AlphaMap, uv2_PrimaryAlpha), flowLerpRate).r;
float secondaryAlpha = lerp(tex2D(_AlphaMap, uv1_SecondaryAlpha), tex2D(_AlphaMap, uv2_SecondaryAlpha), flowLerpRate).r;
col.a = lerp(primaryAlpha, secondaryAlpha, 0.5);
// calculate alpha
col.a = saturate(pow(col.a * _AlphaMul, _AlphaPower));
// add highlight by alpha
fixed minimumAlpha = 0.7;
col.a *= max(1 - pow(clamp(NdotL, 0, 1), 5), minimumAlpha);
// add silhouette by alpha
fixed silhouetteAlpha = 3;
col.a = saturate(col.a + pow(1 - abs(normalViewDot), silhouetteAlpha));
//col.rgb = fixed3(0, 0, 0);
//col.rgb = lighting;
if (_UsePsuedoColorBleeding == 1)
{
col.rgb = lerp(col.rgb, bleedColor.rgb, bleedColor.a);
}
return col;
}
ENDCG
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment