Last active
February 1, 2023 08:18
-
-
Save hecomi/1df7704b625d1d5e7639 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityStandardAssets.ImageEffects; | |
[ExecuteInEditMode] | |
public class SengaEffect : ImageEffectBase | |
{ | |
public float sampleDistance = 1; | |
public float normalEdge = 0.1f; | |
public float depthEdge = 1f; | |
public float colorEdge = 1f; | |
public float normalThreshold = 0.1f; | |
public float depthThreshold = 5; | |
public float colorThreshold = 1f; | |
public float edgeEnhanceLineLength = 2f; | |
public float edgeEnhanceThreshold = 0.8f; | |
public float edgeEnhancePower = 0.5f; | |
public Color shadowColor; | |
public Color skinColor; | |
public Color shadowIgnoreColor1, shadowIgnoreColor2; | |
public RenderTexture edgeRW; | |
void OnEnable() | |
{ | |
var camera = GetComponent<Camera>(); | |
camera.depthTextureMode |= DepthTextureMode.DepthNormals; | |
edgeRW = null; | |
} | |
protected override void OnDisable() | |
{ | |
base.OnDisable(); | |
if (edgeRW) { | |
RenderTexture.DestroyImmediate(edgeRW); | |
} | |
} | |
void OnRenderImage(RenderTexture source, RenderTexture destination) | |
{ | |
if (edgeRW == null || edgeRW.width != source.width || edgeRW.height != source.height) { | |
if (edgeRW) RenderTexture.DestroyImmediate(edgeRW); | |
edgeRW = new RenderTexture(source.width, source.height, 0, RenderTextureFormat.R8); | |
edgeRW.enableRandomWrite = true; | |
edgeRW.Create(); | |
} | |
var senga = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.ARGB32); | |
var temp = RenderTexture.GetTemporary(source.width, source.height, 0, RenderTextureFormat.ARGB32); | |
material.SetFloat("_SampleDistance", sampleDistance); | |
material.SetFloat("_LineLen", edgeEnhanceLineLength); | |
material.SetVector("_EdgePower", new Vector4(normalEdge, depthEdge, colorEdge, edgeEnhancePower)); | |
material.SetVector("_Threshold", new Vector4(normalThreshold, depthThreshold, colorThreshold, edgeEnhanceThreshold)); | |
Graphics.Blit(source, senga, material, 0); | |
material.SetTexture("_SengaTex", senga); | |
Graphics.SetRenderTarget(edgeRW); | |
GL.Clear(true, true, new Color(0, 0, 0, 0)); | |
Graphics.SetRandomWriteTarget(1, edgeRW); | |
Graphics.Blit(senga, temp, material, 1); | |
Graphics.ClearRandomWriteTargets(); | |
material.SetTexture("_EdgeRW", edgeRW); | |
material.SetColor("_ShadowColor", shadowColor); | |
material.SetColor("_SkinColor", skinColor); | |
material.SetColor("_ShadowIgnoreColor1", shadowIgnoreColor1); | |
material.SetColor("_ShadowIgnoreColor2", shadowIgnoreColor2); | |
material.SetMatrix("_InvCamera2WorldMatrix", GetComponent<Camera>().cameraToWorldMatrix.inverse); | |
Graphics.Blit(source, destination, material, 2); | |
RenderTexture.ReleaseTemporary(senga); | |
RenderTexture.ReleaseTemporary(temp); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Shader "Hidden/SengaEffect" | |
{ | |
Properties | |
{ | |
_MainTex("Main Texture", 2D) = "white" {} | |
} | |
SubShader | |
{ | |
Cull Off ZWrite Off ZTest Always | |
Pass | |
{ | |
CGPROGRAM | |
#pragma vertex vert_img | |
#pragma fragment frag | |
#include "UnityCG.cginc" | |
#pragma target 5.0 | |
uniform float4 _MainTex_TexelSize; | |
sampler2D _CameraDepthNormalsTexture; | |
sampler2D _MainTex; | |
half _SampleDistance; | |
half4 _Threshold; | |
half4 _EdgePower; | |
inline float depthToMeter(float dep) | |
{ | |
half nearClip = _ProjectionParams.y; | |
half farClip = _ProjectionParams.z; | |
return dep * (farClip - nearClip); | |
} | |
half4 frag(v2f_img i) : SV_Target | |
{ | |
float2 duvs[2] = { | |
float2(_MainTex_TexelSize.x, 0) * _SampleDistance, | |
float2(0, -_MainTex_TexelSize.y) * _SampleDistance | |
}; | |
half4 col1 = tex2D(_MainTex, i.uv); | |
half4 dn1 = tex2D(_CameraDepthNormalsTexture, i.uv); | |
half dep1; | |
half3 norm1; | |
DecodeDepthNormal(dn1, dep1, norm1); | |
dep1 = depthToMeter(dep1); | |
half edge = 0; | |
for (int j = 0; j < 2; j++) | |
{ | |
half2 uv2 = i.uv + duvs[j]; | |
half4 col2 = tex2D(_MainTex, uv2); | |
half4 dn2 = tex2D(_CameraDepthNormalsTexture, uv2); | |
half dep2; | |
half3 norm2; | |
DecodeDepthNormal(dn2, dep2, norm2); | |
dep2 = depthToMeter(dep2); | |
half2 dNorm = abs(norm1 - norm2); | |
if (length(dNorm) > _Threshold.x) | |
{ | |
edge += _EdgePower.x; | |
} | |
half dDep1 = abs(dep1 - dep2) / dep1 /* meter/texel -> meter */; | |
if (dDep1 * 100 /* cm */ > _Threshold.y && dep1 < 3) | |
{ | |
edge += _EdgePower.y; | |
} | |
half3 dCol = col1.rgb - col2.rgb; | |
if (length(dCol) > _Threshold.z) | |
{ | |
edge += _EdgePower.z; | |
} | |
} | |
return half4(0, 0, 0, edge); | |
} | |
ENDCG | |
} | |
Pass | |
{ | |
CGPROGRAM | |
#pragma vertex vert_img | |
#pragma fragment frag | |
#pragma target 5.0 | |
#include "UnityCG.cginc" | |
uniform float4 _MainTex_TexelSize; | |
sampler2D _MainTex; | |
RWTexture2D<float> _EdgeRW; | |
half _LineLen; | |
half4 _Threshold; | |
half4 _EdgePower; | |
fixed4 frag(v2f_img i) : SV_Target | |
{ | |
half PI = 3.14159265; | |
half PI8 = PI / 8; | |
half lineRot = 0; | |
for (int k = 0; k < 8; ++k) | |
{ | |
half2 lineDir = half2(cos(lineRot), sin(lineRot)) * _MainTex_TexelSize.xy; | |
half p1 = tex2D(_MainTex, i.uv + lineDir * _LineLen).a; | |
half p2 = tex2D(_MainTex, i.uv - lineDir * _LineLen).a; | |
if (p1 > _Threshold.w && p2 > _Threshold.w) | |
{ | |
for (int j = -_LineLen; j <= _LineLen; ++j) | |
{ | |
int2 coord; | |
half2 uv = i.uv + lineDir * j; | |
coord.x = (int)(uv.x * _ScreenParams.x); | |
coord.y = (int)(uv.y * _ScreenParams.y); | |
_EdgeRW[coord] = _EdgePower.w; | |
} | |
} | |
lineRot += PI8; | |
} | |
return 0; | |
} | |
ENDCG | |
} | |
Pass | |
{ | |
CGPROGRAM | |
#pragma vertex vert_img | |
#pragma fragment frag | |
#include "UnityCG.cginc" | |
#pragma target 5.0 | |
uniform float4 _MainTex_TexelSize; | |
half4x4 _InvCamera2WorldMatrix; | |
half4 _ShadowColor; | |
half4 _SkinColor; | |
half4 _ShadowIgnoreColor1; | |
half4 _ShadowIgnoreColor2; | |
sampler2D _CameraDepthNormalsTexture; | |
sampler2D _MainTex; | |
sampler2D _SengaTex; | |
sampler2D _EdgeRW; | |
inline half isSkin(half4 col) | |
{ | |
return length(col.rgb - _SkinColor.rgb) < 0.1; | |
} | |
inline half isIgnoreColor(half4 col) | |
{ | |
return | |
length(col.rgb - _ShadowIgnoreColor1.rgb) < 0.03 || | |
length(col.rgb - _ShadowIgnoreColor2.rgb) < 0.03; | |
} | |
inline float depthToMeter(float dep) | |
{ | |
half nearClip = _ProjectionParams.y; | |
half farClip = _ProjectionParams.z; | |
return dep * (farClip - nearClip); | |
} | |
half4 calcShadowedImg(v2f_img i) | |
{ | |
half2 uv1 = i.uv; | |
half4 col = tex2D(_MainTex, uv1); | |
half3 lightDir = mul(_InvCamera2WorldMatrix, normalize(_WorldSpaceLightPos0)); | |
// layer-based shadow | |
half dep1; | |
half3 norm1; | |
DecodeDepthNormal(tex2D(_CameraDepthNormalsTexture, uv1), dep1, norm1); | |
dep1 = depthToMeter(dep1); | |
half invDep = 1 / dep1; | |
half len = 50; | |
half dLen = 0.01; | |
half2 ray = _MainTex_TexelSize * (-lightDir.xy * half2(1, 0.5)) * invDep; | |
half2 uv2 = i.uv + ray * dLen; | |
float dep2 = depthToMeter(DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, uv2).zw)); | |
float grad12 = (dep2 - dep1); | |
half2 uv3 = i.uv + ray * len; | |
float dep3 = depthToMeter(DecodeFloatRG(tex2D(_CameraDepthNormalsTexture, uv3).zw)); | |
half4 col3 = tex2D(_MainTex, uv3); | |
half layerShadowThreshold = 0.03; /* meter */ | |
if (isSkin(col) && isSkin(col3)) | |
{ | |
layerShadowThreshold = 0.06; /* meter */ | |
} | |
float interpDep3 = dep1 + (grad12 * (len / dLen)); | |
if (dep1 - dep3 < 0.2 /* meter */ && | |
interpDep3 - dep3 > layerShadowThreshold && | |
!isIgnoreColor(col3) && | |
!isIgnoreColor(col)) | |
{ | |
col.rgb *= _ShadowColor.rgb; | |
return col; | |
} | |
// directional light shadow | |
half directionalShadowThreshold = -0.3; | |
if (isSkin(col)) | |
{ | |
directionalShadowThreshold = 0.5; | |
} | |
if (dep1 < 5 && dot(lightDir, norm1) > directionalShadowThreshold) | |
{ | |
col.rgb *= _ShadowColor.rgb; | |
return col; | |
} | |
return col; | |
} | |
half4 frag(v2f_img i) : SV_Target | |
{ | |
half4 col = calcShadowedImg(i); | |
half4 edge = tex2D(_SengaTex, i.uv); | |
edge.a = saturate(edge.a + tex2D(_EdgeRW, i.uv).r); | |
col.rgb = lerp(col.rgb, pow(col.rgb * 0.8, 1 + 2 * edge.a), edge.a); | |
return col; | |
} | |
ENDCG | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment