Skip to content

Commit

Permalink
Merge pull request #17 from Ikaroon/develop
Browse files Browse the repository at this point in the history
v0.1.0
  • Loading branch information
Ikaroon authored May 13, 2022
2 parents 4282675 + 2bec6ef commit 0428321
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 81 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# Changelog
All notable changes to this project will be documented in this file.

## [Unreleased]
## [v0.1.0] - 2022-05-13
### Added
- Support for bold text rendering
- Support for italic text rendering
- Custom Shader GUI for easier material editing
- Raymarching options for more control
- Support for more raymarching algorithms for the future
- Debug options to show used steps and the 3D uvs

### Changed
- Raymarching usage for more control and new algorithms in the future
- Boundaries structure for raymarching
- Outline rendering is now a shader feature and can be fully disabled

### Removed
- Unused shader uniforms
Expand Down
22 changes: 16 additions & 6 deletions Editor/Scripts/TMP3D_UnlitShaderGUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class TMP3D_UnlitShaderGUI : TMP_BaseShaderGUI
static TMP3D_ShaderFeature s_volumeModeFeature;
static TMP3D_ShaderFeature s_raymarchModeFeature;
static TMP3D_ShaderFeature s_maxStepsFeature;
static ShaderFeature s_debugFeature;

static bool s_general = true;
static bool s_outline = true;
Expand Down Expand Up @@ -44,6 +45,13 @@ static TMP3D_UnlitShaderGUI()
keywords = new[] { "_MAXSTEPS_32", "_MAXSTEPS_64", "_MAXSTEPS_96", "_MAXSTEPS_128" },
keywordLabels = new[] { new GUIContent("32"), new GUIContent("64"), new GUIContent("96"), new GUIContent("128") }
};
s_debugFeature = new ShaderFeature()
{
undoLabel = "Debug",
label = new GUIContent("Debug Mode"),
keywords = new[] { "DEBUG_STEPS", "DEBUG_MASK" },
keywordLabels = new[] { new GUIContent("None"), new GUIContent("Steps"), new GUIContent("Mask") }
};
}

protected override void DoGUI()
Expand All @@ -56,18 +64,18 @@ protected override void DoGUI()

EndPanel();

s_outline = BeginPanel("Outline", s_outlineFeature, s_outline);
if (s_outline)
s_3D = BeginPanel("3D", s_3D);
if (s_3D)
{
DoOutlinePanel();
Do3DPanel();
}

EndPanel();

s_3D = BeginPanel("3D", s_3D);
if (s_3D)
s_outline = BeginPanel("Outline", s_outlineFeature, s_outline);
if (s_outline)
{
Do3DPanel();
DoOutlinePanel();
}

EndPanel();
Expand Down Expand Up @@ -129,6 +137,8 @@ void DoDebugPanel()
DoFloat("_GradientScale", "Gradient Scale");
DoFloat("_TextureWidth", "Texture Width");
DoFloat("_TextureHeight", "Texture Height");
s_debugFeature.ReadState(m_Material);
s_debugFeature.DoPopup(m_Editor, m_Material);
EditorGUI.indentLevel -= 1;
EditorGUILayout.Space();
}
Expand Down
28 changes: 17 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<p align=center><img alt="TMP3D Logo" width="400px" src="https://user-images.githubusercontent.com/65419234/167305638-76138392-b394-4e1e-b391-d59677b61762.png"/></p>

<p align=center><a href="https://github.com/Ikaroon/com.ikaroon.tmp3d/blob/master/LICENSE"><img src="https://badgen.net/github/license/Naereen/Strapdown.js"/></a>
<a href="https://GitHub.com/Ikaroon/com.ikaroon.tmp3d/releases/"><img src="https://img.shields.io/badge/release-0.1.0--pre.1-yellow.svg"/></a>
<a href="https://GitHub.com/Ikaroon/com.ikaroon.tmp3d/releases/"><img src="https://img.shields.io/badge/release-0.1.0-green.svg"/></a>
<img alt="GitHub (Pre-)Release Date" src="https://img.shields.io/github/release-date-pre/Ikaroon/TMP3D"></p>

# Text Mesh Pro 3D
Expand All @@ -12,16 +12,20 @@ An extension for Text Mesh Pro that makes 3D text possible using shaders.
- [x] 3D character properties and animation
- [x] Outline support
- [x] Bold text support
- [ ] Italic text support
- [x] Italic text support
- [x] Debug rendering
- [ ] Solid, surface shader
- [ ] Solid, unlit shader (UI)
- [ ] Documentation

### In Evaluation
- [ ] Bevelled text
- [ ] Full fledged freeform text using textures
- [ ] Bevelled text[^1]
- [ ] Full fledged freeform text using textures[^1]
- [ ] Translucent, unlit shader
- [ ] VR optimizations
- [ ] VR optimizations[^2]

[^1]: Tempering with the SDF values in the distance makes raymarching more difficult, I need to find a good way of evaluating the shortest distance.
[^2]: I currently don't have access to any VR device and, therefore, cannot optimize for it right now.

## Compatibility

Expand All @@ -31,12 +35,12 @@ An extension for Text Mesh Pro that makes 3D text possible using shaders.
| DirectX 12 | :heavy_check_mark: Compatible | :heavy_check_mark: Compatible | :heavy_check_mark: Compatible |
| Vulkan | :heavy_check_mark: Compatible | :heavy_check_mark: Compatible | :heavy_check_mark: Compatible |
| OpenGL Core | :heavy_check_mark: Compatible | :warning: Invalid | :heavy_check_mark: Compatible |
| OpenGLES2[^1] | :x: Incompatible | :warning: Invalid | :x: Incompatible |
| OpenGLES2[^3] | :x: Incompatible | :warning: Invalid | :x: Incompatible |
| OpenGLES3 | :heavy_check_mark: Compatible | :warning: Invalid | :heavy_check_mark: Compatible |
| Metal[^2] | :wavy_dash: To Be Tested | :wavy_dash: To Be Tested | :wavy_dash: To Be Tested |
| Metal[^4] | :wavy_dash: To Be Tested | :wavy_dash: To Be Tested | :wavy_dash: To Be Tested |

[^1]: Support for OpenGLES2 is currently **NOT** planned.
[^2]: I currently don't have access to any Mac and, therefore, cannot test it for Metal right now.
[^3]: Support for OpenGLES2 is currently **NOT** planned.
[^4]: I currently don't have access to any Mac and, therefore, cannot test it for Metal right now.

If the shader doesn't work for a compatible combination try to reimport the shader file first!
When the issue persists contact me!
Expand All @@ -49,6 +53,8 @@ When the issue persists contact me!
3. Select `Add package from git URL...`
4. Enter `https://github.com/Ikaroon/TMP3D.git` as url and confirm

This method will always install the current state of the git. To get a released version head to the [release page](https://github.com/Ikaroon/TMP3D/releases)!

## How to use
To understand how to setup a TextMeshPro for 3D you can check out the sample in the package. For downloading that follow these steps:
1. Open the package manager in Unity from `Window>Package Manager`
Expand All @@ -61,10 +67,10 @@ If you still need help, here are some steps how you setup a TextMeshPro for 3D:
1. Create a new FontAsset by using the `Font Asset Creator` from `Window>Text Mesh Pro>Font Asset Creator`
2. Expand the created Asset and select the Material
3. Change the Material's shader to `TextMeshPro/3D/Unlit`
4. Create a TextMeshPro in a scene from `3D Object>Text - TextMeshPro` NOT from `UI>Text - TextMeshPro` this is not supported yet.
4. Create a TextMeshPro in a scene from `3D Object>Text - TextMeshPro` **NOT** from `UI>Text - TextMeshPro` this is not supported yet.
5. Add a TMP3D_Handler component from `Script>Ikaroon.TMP3D>TMP3D_Handler`
6. Apply the FontAsset to the TextMeshPro component
7. You have now acces to 3D text!
7. You have now access to 3D text!

## Notice
Work on this project happens in my freetime and, therefore, I cannot promise if and when certain features are added. I am considering to open this up for contribution but for now you can manipulate the code as you please. This project is MIT licensed and may be used freely. (Check the license file for more information)
Expand Down
24 changes: 24 additions & 0 deletions Runtime/Shaders/Lib/Raymarching/Common.cginc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,30 @@
float3 Temp_ViewDir;
float3 Temp_LocalStartPos;

float3 Temp_LocalPos;
float3 GetRaymarchLocalPos()
{
return Temp_LocalPos;
}

float Temp_Bound;
float GetRaymarchBound()
{
return Temp_Bound;
}

float Temp_Value;
float GetRaymarchValue()
{
return Temp_Value;
}

float3 Temp_Mask3D;
float3 GetRaymarchMask3D()
{
return Temp_Mask3D;
}

void PrepareTMP3DRaymarch(tmp3d_g2f input)
{
float3 viewDir = normalize(input.worldPos.xyz - _WorldSpaceCameraPos.xyz);
Expand Down
12 changes: 6 additions & 6 deletions Runtime/Shaders/Lib/Raymarching/PenaltyMarcher.cginc
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ void InitializeRaymarcher(tmp3d_g2f input)
temp_Input = input;
}

void NextRaymarch(out float3 localPos, out float bound, out float value, float offset)
void NextRaymarch(float offset)
{
localPos = GetRaymarchLocalPosition(temp_Progress);
float3 mask3D = PositionToMask(localPos, temp_Input);
bound = IsInBounds(mask3D);
value = 1 - SampleSDF3D(saturate(mask3D), temp_Input);
Temp_LocalPos = GetRaymarchLocalPosition(temp_Progress);
Temp_Mask3D = PositionToMask(Temp_LocalPos, temp_Input);
Temp_Bound = IsInBounds(Temp_Mask3D);
Temp_Value = 1 - SampleSDF3D(saturate(Temp_Mask3D), temp_Input);

float sdfDistance = max((value - offset) * GradientToLocalLength(temp_Input), _RaymarchMinStep);
float sdfDistance = max((Temp_Value - offset) * GradientToLocalLength(temp_Input), _RaymarchMinStep);
float3 viewDir = GetRaymarchLocalDirection();
float length1 = length(normalize(viewDir.xy) * sdfDistance);
float length2 = length(viewDir.xy);
Expand Down
12 changes: 6 additions & 6 deletions Runtime/Shaders/Lib/Raymarching/StandardMarcher.cginc
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ void InitializeRaymarcher(tmp3d_g2f input)
temp_Input = input;
}

void NextRaymarch(out float3 localPos, out float bound, out float value, float offset)
void NextRaymarch(float offset)
{
localPos = GetRaymarchLocalPosition(temp_Progress);
float3 mask3D = PositionToMask(localPos, temp_Input);
bound = IsInBounds(mask3D);
value = 1 - SampleSDF3D(saturate(mask3D), temp_Input);
Temp_LocalPos = GetRaymarchLocalPosition(temp_Progress);
Temp_Mask3D = PositionToMask(Temp_LocalPos, temp_Input);
Temp_Bound = IsInBounds(Temp_Mask3D);
Temp_Value = 1 - SampleSDF3D(saturate(Temp_Mask3D), temp_Input);

float sdfDistance = max((value - offset) * GradientToLocalLength(temp_Input), _RaymarchMinStep);
float sdfDistance = max((Temp_Value - offset) * GradientToLocalLength(temp_Input), _RaymarchMinStep);
float3 viewDir = GetRaymarchLocalDirection();
float length1 = length(normalize(viewDir.xy) * sdfDistance);
float length2 = length(viewDir.xy);
Expand Down
71 changes: 36 additions & 35 deletions Runtime/Shaders/Lib/TMP3D_Common.cginc
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ float InverseLerp(float a, float b, float x)
float SampleSDF3D(float3 mask3D, tmp3d_g2f input)
{
float2 maskUV = float2(0, 0);
maskUV.x = saturate(lerp(input.boundariesUV.x, input.boundariesUV.z, mask3D.x));
maskUV.y = saturate(lerp(input.boundariesUV.y, input.boundariesUV.w, mask3D.y));
maskUV.x = saturate(lerp(input.boundariesUV.x, input.boundariesUV.x + input.boundariesUV.z, mask3D.x));
maskUV.y = saturate(lerp(input.boundariesUV.y, input.boundariesUV.y + input.boundariesUV.w, mask3D.y));
return tex2D(_MainTex, maskUV).a;
}

Expand All @@ -34,16 +34,17 @@ void ClipBounds(float3 mask3D)

float3 PositionToMask(float3 localPos, tmp3d_g2f input)
{
float3 mask3D = float3(-1,-1,-1);
mask3D.x = InverseLerp(input.boundariesLocal.x, input.boundariesLocal.z, localPos.x);
mask3D.y = InverseLerp(input.boundariesLocal.y, input.boundariesLocal.w, localPos.y);
float3 mask3D = float3(-1, -1, -1);
mask3D.y = InverseLerp(input.boundariesLocal.y, input.boundariesLocal.y + input.boundariesLocal.w, localPos.y);
float xOffset = saturate(mask3D.y) * input.boundariesLocalZ.z;
mask3D.x = InverseLerp(input.boundariesLocal.x, input.boundariesLocal.x + input.boundariesLocal.z, localPos.x - xOffset);
mask3D.z = InverseLerp(input.boundariesLocalZ.x, input.boundariesLocalZ.y, localPos.z);
return mask3D;
}

float GradientToLocalLength(tmp3d_g2f input)
{
float l = abs(input.boundariesLocal.x - input.boundariesLocal.z);
float l = input.boundariesLocal.z;
return l * 0.01 * _GradientScale;
}

Expand All @@ -62,7 +63,7 @@ tmp3d_v2g TMP3D_VERT(tmp3d_a2v input)
}

// "Creates a vertex" with an offset and boundary values
tmp3d_g2f CreateVertex(tmp3d_v2g input, float3 positionOffset, float4 boundariesUV, float4 boundariesLocal, float2 boundariesLocalZ)
tmp3d_g2f CreateVertex(tmp3d_v2g input, float3 positionOffset, float4 boundariesUV, float4 boundariesLocal, float4 boundariesLocalZ)
{
tmp3d_g2f output;

Expand Down Expand Up @@ -92,7 +93,7 @@ tmp3d_g2f CreateVertex(tmp3d_v2g input, float3 positionOffset, float4 boundaries
return output;
}

void TMP3D_FILLGEOMETRY(triangle tmp3d_v2g input[3], inout TriangleStream<tmp3d_g2f> triStream, float3 def, float3 normal, float4 boundariesUV, float4 boundariesLocal, float2 boundariesLocalZ)
void TMP3D_FILLGEOMETRY(triangle tmp3d_v2g input[3], inout TriangleStream<tmp3d_g2f> triStream, float3 def, float3 normal, float4 boundariesUV, float4 boundariesLocal, float4 boundariesLocalZ)
{
// Top
triStream.RestartStrip();
Expand Down Expand Up @@ -131,7 +132,7 @@ void TMP3D_FILLGEOMETRY(triangle tmp3d_v2g input[3], inout TriangleStream<tmp3d_
// Side C is in between of the quad's triangles and not wanted
}

void TMP3D_FILLGEOMETRY_INVERTED(triangle tmp3d_v2g input[3], inout TriangleStream<tmp3d_g2f> triStream, float3 def, float3 normal, float4 boundariesUV, float4 boundariesLocal, float2 boundariesLocalZ)
void TMP3D_FILLGEOMETRY_INVERTED(triangle tmp3d_v2g input[3], inout TriangleStream<tmp3d_g2f> triStream, float3 def, float3 normal, float4 boundariesUV, float4 boundariesLocal, float4 boundariesLocalZ)
{
// Top
triStream.RestartStrip();
Expand Down Expand Up @@ -180,25 +181,25 @@ void TMP3D_GEOM(triangle tmp3d_v2g input[3], inout TriangleStream<tmp3d_g2f> tri
float depth = input[0].texcoord2.r;
float3 normal = input[0].normal * depth;

float minUVx = min(input[0].texcoord0.x, min(input[1].texcoord0.x, input[2].texcoord0.x));
float minUVy = min(input[0].texcoord0.y, min(input[1].texcoord0.y, input[2].texcoord0.y));
float maxUVx = max(input[0].texcoord0.x, max(input[1].texcoord0.x, input[2].texcoord0.x));
float maxUVy = max(input[0].texcoord0.y, max(input[1].texcoord0.y, input[2].texcoord0.y));
float4 boundariesUV = float4(minUVx, minUVy, maxUVx, maxUVy);
float skewUV = abs(input[1].texcoord0.x - input[0].texcoord0.x);
float widthUV = abs(input[2].texcoord0.x - input[1].texcoord0.x);
float heightUV = abs(input[1].texcoord0.y - input[0].texcoord0.y);
float xUV = min(input[0].texcoord0.x, input[2].texcoord0.x);
float yUV = min(input[0].texcoord0.y, input[1].texcoord0.y);
float4 boundariesUV = float4(xUV, yUV, widthUV, heightUV);

float3 v0local = mul(unity_WorldToObject, float4(input[0].position.xyz, 1)).xyz;
float3 v1local = mul(unity_WorldToObject, float4(input[1].position.xyz, 1)).xyz;
float3 v2local = mul(unity_WorldToObject, float4(input[2].position.xyz, 1)).xyz;

float minWorldx = min(v0local.x, min(v1local.x, v2local.x));
float minWorldy = min(v0local.y, min(v1local.y, v2local.y));
float maxWorldx = max(v0local.x, max(v1local.x, v2local.x));
float maxWorldy = max(v0local.y, max(v1local.y, v2local.y));
float4 boundariesLocal = float4(minWorldx, minWorldy, maxWorldx, maxWorldy);
float skewLocal = abs(v1local.x - v0local.x);
float widthLocal = abs(v2local.x - v1local.x);
float heightLocal = abs(v1local.y - v0local.y);
float xLocal = min(v0local.x, v2local.x);
float yLocal = min(v0local.y, v1local.y);
float4 boundariesLocal = float4(xLocal, yLocal, widthLocal, heightLocal);

float minWorldz = min(v0local.z, min(v1local.z, v2local.z));
float maxWorldz = max(v0local.z, max(v1local.z, v2local.z));
float2 boundariesLocalZ = float2(minWorldz - depth, maxWorldz);
float4 boundariesLocalZ = float4(-depth, 0, skewLocal, skewUV);

TMP3D_FILLGEOMETRY(input, triStream, def, normal, boundariesUV, boundariesLocal, boundariesLocalZ);
}
Expand All @@ -213,25 +214,25 @@ void TMP3D_GEOM_INVERTED(triangle tmp3d_v2g input[3], inout TriangleStream<tmp3d
float depth = input[0].texcoord2.r;
float3 normal = input[0].normal * depth;

float minUVx = min(input[0].texcoord0.x, min(input[1].texcoord0.x, input[2].texcoord0.x));
float minUVy = min(input[0].texcoord0.y, min(input[1].texcoord0.y, input[2].texcoord0.y));
float maxUVx = max(input[0].texcoord0.x, max(input[1].texcoord0.x, input[2].texcoord0.x));
float maxUVy = max(input[0].texcoord0.y, max(input[1].texcoord0.y, input[2].texcoord0.y));
float4 boundariesUV = float4(minUVx, minUVy, maxUVx, maxUVy);
float skewUV = abs(input[1].texcoord0.x - input[0].texcoord0.x);
float widthUV = abs(input[2].texcoord0.x - input[1].texcoord0.x);
float heightUV = abs(input[1].texcoord0.y - input[0].texcoord0.y);
float xUV = min(input[0].texcoord0.x, input[2].texcoord0.x);
float yUV = min(input[0].texcoord0.y, input[1].texcoord0.y);
float4 boundariesUV = float4(xUV, yUV, widthUV, heightUV);

float3 v0local = mul(unity_WorldToObject, float4(input[0].position.xyz, 1)).xyz;
float3 v1local = mul(unity_WorldToObject, float4(input[1].position.xyz, 1)).xyz;
float3 v2local = mul(unity_WorldToObject, float4(input[2].position.xyz, 1)).xyz;

float minWorldx = min(v0local.x, min(v1local.x, v2local.x));
float minWorldy = min(v0local.y, min(v1local.y, v2local.y));
float maxWorldx = max(v0local.x, max(v1local.x, v2local.x));
float maxWorldy = max(v0local.y, max(v1local.y, v2local.y));
float4 boundariesLocal = float4(minWorldx, minWorldy, maxWorldx, maxWorldy);
float skewLocal = abs(v1local.x - v0local.x);
float widthLocal = abs(v2local.x - v1local.x);
float heightLocal = abs(v1local.y - v0local.y);
float xLocal = min(v0local.x, v2local.x);
float yLocal = min(v0local.y, v1local.y);
float4 boundariesLocal = float4(xLocal, yLocal, widthLocal, heightLocal);

float minWorldz = min(v0local.z, min(v1local.z, v2local.z));
float maxWorldz = max(v0local.z, max(v1local.z, v2local.z));
float2 boundariesLocalZ = float2(minWorldz - depth, maxWorldz);
float4 boundariesLocalZ = float4(-depth, 0, skewLocal, skewUV);

TMP3D_FILLGEOMETRY_INVERTED(input, triStream, def, normal, boundariesUV, boundariesLocal, boundariesLocalZ);
}
Expand Down
2 changes: 1 addition & 1 deletion Runtime/Shaders/Lib/TMP3D_Structs.cginc
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ struct tmp3d_g2f {
float4 worldPos : TEXCOORD1;
float4 boundariesUV : TEXCOORD2;
float4 boundariesLocal : TEXCOORD3;
float2 boundariesLocalZ : TEXCOORD4;
float4 boundariesLocalZ : TEXCOORD4;
float4 tmp3d : TEXCOORD5;
float2 tmp : TEXCOORD6;
};
Expand Down
Loading

0 comments on commit 0428321

Please sign in to comment.