Skip to content

Commit

Permalink
edge direction visualization via shader
Browse files Browse the repository at this point in the history
Implements a dedicated EdgeShader that handles color gradient.
A vertex deformation effect can be configured and toggled
that allows for a direction visualization. The effect depends
on a correct UV mapping and uses the direction as present
in our custom 3D edge meshes. The smoothness of the effect
relies on several configurable parameters and the vertex
density of the original mesh.
  • Loading branch information
tinxx committed Oct 17, 2024
1 parent b54aa37 commit 3aa6300
Show file tree
Hide file tree
Showing 10 changed files with 270 additions and 211 deletions.
1 change: 0 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
"**/*.asset": true,
"**/*.cubemap": true,
"**/*.flare": true,
"**/*.mat": true,
"**/*.meta": true,
"build/": true,
"Build/": true,
Expand Down
92 changes: 92 additions & 0 deletions Assets/Resources/Materials/TransparentEdgePortalMaterial.mat
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: TransparentEdgePortalMaterial
m_Shader: {fileID: 4800000, guid: dcc192bee1ca9aa03b1851f89b0beff2, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords: []
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AnimationFactor: 0.4
- _AnimationPause: 0.4
- _BumpScale: 1
- _ColorGradientEnabled: 0
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _DstBlend: 0
- _EdgeFlowEnabled: 0
- _EffectWidth: 0.03
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _GrowthAmount: 0.005
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _Parallax: 0.02
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _UVSec: 0
- _ZWrite: 1
m_Colors:
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _EndColor: {r: 0, g: 0, b: 1, a: 1}
- _PortalMax: {r: 10, g: 10, b: 0, a: 0}
- _PortalMin: {r: -10, g: -10, b: 0, a: 0}
m_BuildTextureStacks: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9575ff8f0b08cd787bb1993d37b2c438
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:
127 changes: 127 additions & 0 deletions Assets/Resources/Shaders/TransparentEdgePortalShader.shader
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
Shader "Unlit/SEE/TransparentEdgePortalShader"
{
Properties
{
// Color
_Color("(Start) Color", color) = (1,0,0,1)
_EndColor("End Color", color) = (0,0,1,1)
_ColorGradientEnabled("Enable Color Gradient?", Range(0, 1)) = 0 // 0 = false, 1 = true

// Data Flow
_EdgeFlowEnabled("Enable Data Flow Visualization?", Range(0, 1)) = 0 // 0 = false, 1 = true
_AnimationFactor("Animation Speed Factor", Range(0, 3)) = 0.4
_AnimationPause("Pause Between Animations", Range(0, 3)) = 0.4
_EffectWidth("Effect Width", Range(0, 1.0)) = 0.03
_GrowthAmount("Growth Amount", Range(0, 0.04)) = 0.005

// Clipping
_PortalMin("Portal Left Front Corner", vector) = (-10, -10, 0, 0)
_PortalMax("Portal Right Back Corner", vector) = (10, 10, 0, 0)
}
SubShader
{
Tags {
"Queue"="Transparent"
"RenderType"="Transparent"
"IgnoreProjector"="True"
"ForceNoShadowCasting" = "True"
"PreviewType" = "Plane"
}

// Alpha blending mode for transparency
Blend SrcAlpha OneMinusSrcAlpha
// Do not write to depth buffer to allow transparency effect
// Note: We will be able to see parts of the edge through other parts of the same edge, that should be occluded
// on full opacity. This is not a desired effect but not a big issue either.
ZWrite Off
// Makes the inside visible at clipping planes
Cull Off
// Unity's lighting will not be applied
Lighting Off

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

struct appdata
{
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};

struct v2f
{
float4 vertex : SV_POSITION;
float3 worldPos : TEXCOORD0;
float2 uv : TEXCOORD1;
};

// Color
fixed4 _Color;
fixed4 _EndColor;
float _ColorGradientEnabled;

// Data Flow
float _EdgeFlowEnabled;
float _AnimationFactor;
float _AnimationPause;
float _EffectWidth;
float _GrowthAmount;

// Clipping
float2 _PortalMin;
float2 _PortalMax;

v2f vert (appdata v)
{
v2f o;

if (_EdgeFlowEnabled > 0.5)
{
// The effect is supposed to move automatically based on the time and the animation factor.
// The position is calculated based on the assumption that the object has a uniform UV mapping (0.0 to 1.0 along the y axis).
// We stretch the effect scale by the effect width so that the effect fades in and out smoothly at both ends, respectively.
// Additionally, the effect scale is stretched to add a pause between the animations.
float effectPosition = frac(_Time.y * _AnimationFactor) * (1.0 + 2 * _EffectWidth + _AnimationPause) - _EffectWidth;

// Distance between the vertex and the effect position on the y axis in world-space
float distance = abs(v.uv.y - effectPosition);

if (distance < _EffectWidth)
{
// The effect strength is based on the distance to the effect position
float effectFactor = 1.0 - pow(distance / _EffectWidth, 3);
effectFactor = clamp(effectFactor, 0.0, 1.0);
// We use the direction of the normal to grow outward
float3 outwardDir = normalize(v.normal);
v.vertex.xyz += outwardDir * effectFactor * _GrowthAmount;
}
}

o.vertex = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
o.uv = v.uv;
return o;
}

fixed4 frag (v2f i) : SV_Target
{
// Clipping
// Note: We use a 2D portal (x, y) that spans over a (x, z) plane in the Unity 3D space.
if (i.worldPos.x < _PortalMin.x || i.worldPos.x > _PortalMax.x ||
i.worldPos.z < _PortalMin.y || i.worldPos.z > _PortalMax.y)
{
discard;
}

return _ColorGradientEnabled > 0.5 ? lerp(_Color, _EndColor, i.uv.y) : _Color;
}
ENDCG
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: dcc192bee1ca9aa03b1851f89b0beff2
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:
Loading

0 comments on commit 3aa6300

Please sign in to comment.