• Stars
    star
    358
  • Rank 118,272 (Top 3 %)
  • Language
    C#
  • License
    GNU General Publi...
  • Created over 8 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Implementation of the curve modifier from Blender to Unity3D

Update Feb 19, 2018 : Very simple curve modifier example

The curve modifier in a vertex shader

float current = fmod(position.y, 1.);
float next = fmod(current + .01, 1.);

float3 curvePosition = tex2Dlod(_CurveTexture, float4(current,0,0,0)).xyz;
float3 curvePositionNext = tex2Dlod(_CurveTexture, float4(next,0,0,0)).xyz;

float3 forward = normalize(curvePositionNext - curvePosition);
float3 up = normalize(cross(normalize(curvePositionNext), normalize(curvePosition)));
float3 right = normalize(cross(forward, up));

float angle = atan2(position.z, position.x);
float radius = length(position.xz);
position.xyz = curvePosition + (right * cos(angle) + up * sin(angle)) * radius;

SimpleCurveModifier.shader

Fish mesh by Shakiller - CC Attribution

Update Jun 8, 2016 : fixed curve issue

  • bezier curve edits can now be undo.
  • deformation is now smooth all along the path

What is happening in this gif ?

A 3D model of a dragon is being deformed in real-time along a curve with a vertex shader.
The curve is made by following a physicalized box.

The story behind a gif

I've discovered the curve modifier in Blender and really love it.
People on Twitter went crazy with the gif below, so I always wanted to share how to do it.

And ?

One night I couldn't sleep, because I had an intuition.
I thought I could make the maths for the curve modifier above.

Turns out it is always more complicated than excepted.
It took me time to figure out the geometry involved.
Struggling with maths is hard and frustrating.
But what a satisfaction when things work!

No interpolation

The gif below is the first tentative.
It looks cool, but it is not perfect.
It needs a smooth linear interpolation.

With interpolation

This is mainly because I used an array of float to pass positions to the shader.
And I've discovered that you could just send float within a texture.
It's great because texture gets a linear interpolation filter.

Warning

It is almost working like a charm, but my curve modifier still got weird glitches.
I guess I'm fine with it since I'm just doing it to mess with shaders.

Let's do it in Unity3D with a vertex shader !

In this repository, you will find the scripts and the shader.

The CurveModifier.cs is the base script used to simplify different cases of application.
His main function is to translate an array of vector into a texture.
An important information is that the texture format is RGBAFloat.
It allows the texture to store 32 bit floats, and so positions.

Simplified version of what CurveModifier.cs does

void Init () {
  texture = new Texture2D(resolution, 1, TextureFormat.RGBAFloat, false);
  colorArray = new Color[resolution];
  vectorArray = new Vector3[resolution];
}

// Get a curve point with a clamped ratio
public Vector3 GetCurvePoint (float ratio) {
  return vectorArray[ (int) Mathf.Floor ( vectorArray.Length * ratio ) ];
}

// Store points into a texture
public void CurveToTexture () {
	for (int i = 0; i < resolution; ++i) {
		float ratio = i / (float)resolution;
		Vector3 p = GetCurvePoint(ratio);
		colorArray[i] = new Color(p.x, p.y, p.z, 0.0);
	}
	texture.SetPixels(colorArray);
	texture.Apply();
}

So for example we can use a Bezier Curve like this one on the Asset Store.
And then just fill the vector array with the bezier points.
You can check BezierCurveModifier.cs that uses the bezier package.

Let's get scared at looking the vertex shader

// this function set the vertex position
v2f vert (appdata v)
{
	// unity stuff
	v2f o;

	// use transform component
	float4 vertex = mul(_Object2World, v.vertex);

	// axis setup (compress vectors into scalars)
	float vertexForward = vertex.x * _Forward.x + vertex.y * _Forward.y + vertex.z * _Forward.z;
	float vertexRight = vertex.x * _Right.x + vertex.y * _Right.y + vertex.z * _Right.z;
	float vertexUp = vertex.x * _Up.x + vertex.y * _Up.y + vertex.z * _Up.z;

	// the actual clamped ratio position on the curve
	float ratio = abs(vertexForward + _Time.x * _TimeSpeed);
	ratio = lerp(clamp(ratio, 0.0, 1.0), fmod(ratio, 1.0), _ShouldLoop);

	// used to distribute point on the plane that is perpendicular to the curve forward
	float angle = atan2(vertexUp, vertexRight);
	float radius = length(float2(vertexRight, vertexUp));

	// get current point through the texture
	float4 p = float4(ratio, 0.0, 0.0, 0.0);
	float3 bezierPoint = mul(_World2Object, tex2Dlod(_CurveTexture, p));

	// get neighbors of the current ratio
	float unit = 1.0 / _CurveResolution;
	float ratioNext = fmod(ratio + unit, 1.0);
	float ratioPrevious = fmod(ratio - unit + 1.0, 1.0);

	// make things loop or not
	ratioNext = lerp(clamp(abs(ratio + unit), 0.0, 1.0), ratioNext, _ShouldLoop);
	ratioPrevious = lerp(clamp(abs(ratio - unit), 0.0, 1.0), ratioPrevious, _ShouldLoop);

	// get next and previous point through the texture
	p.x = ratioNext;
	float3 bezierPointNext = mul(_World2Object, tex2Dlod(_CurveTexture, p));
	p.x = ratioPrevious;
	float3 bezierPointPrevious = mul(_World2Object, tex2Dlod(_CurveTexture, p));

	// find out vectors
	float3 forward = normalize(bezierPointNext - bezierPoint);
	float3 backward = normalize(bezierPointPrevious - bezierPoint);
	float3 up = normalize(cross(forward, backward));
	float3 right = normalize(cross(forward, up));

	// voila
	vertex.xyz = bezierPoint + right * cos(angle) * radius + up * sin(angle) * radius;

	// unity stuff
	o.vertex = mul(UNITY_MATRIX_MVP, vertex);
	o.uv = TRANSFORM_TEX(v.uv, _MainTex);
	return o;
}

Looks like hieroglyphs, right ? I still can't believe it works.
My methodology is to change a + into a * and watch if things get better.
And with luck, sometimes you find the right combination.

Let's play with the examples

Things to know about the curve modifier

You have to find the right curve axis.

Changing position and rotation will have consequences

You can change the scale to adjust the curve distribution

You can adjust the curve distribution with these two parameters

There is chance that you will need a bigger culling bounds

Because the shader won't change the mesh bounds.
So the camera can be out of range.
And the model can disappear from the frustum culling.

Culling bounds is now auto calculated

Things to know about the Bezier Curve modifier

You can modify the spline to change the curve.

You can change the cycle offset speed.

Things to know about the Follow Curve modifier

You can change the how quick you follow the target.

Alright folks

Of course there is lot of things left to do. Like bugs fix, solid curve system and better user experience.
But I am just having fun with vertex shaders on my free time and feel satisfied with the actual prototype.
Feels free to contact me if you want to improve the project.

I would love to hear about your project if you are using my shader.
You can contact me on Twitter : @leondenise
And please share your code like everyone else in this open source community <3

More Repositories

1

PointCloudExporter

C#
183
star
2

ShaderGum

Unity3D GPU Sculpt & Morph
C#
143
star
3

SIGExam

Correction of a Shader Exam
ShaderLab
106
star
4

Workshop

GLSL
64
star
5

OpticalFlowExample

Processing
41
star
6

CookieEngine

WebGL Demotool
JavaScript
29
star
7

CoursGlitchArt

C#
19
star
8

DragAndGlitch

JavaScript
16
star
9

GLASSSchool

Unity3D shader workshop for GLASS summer school
C#
15
star
10

Voxel

WebGL
JavaScript
14
star
11

GPU

JavaScript
13
star
12

TimeDisplacement

Time Displacement effect with Unity3D
JavaScript
9
star
13

godot-particles

GPU particles example in Godot engine
8
star
14

NaturallyUndead

First demo made with love for Evoke 2016
JavaScript
8
star
15

Hatch

WebGL demo for Revision 2019
JavaScript
7
star
16

CoursSIG2017

Class Resources
HLSL
5
star
17

DingDong

C#
4
star
18

WJ

Tool for VJing in WebGL
C#
4
star
19

Choupichoup

JavaScript
3
star
20

TalkingHeads

Game for Ludum Dare 31
JavaScript
3
star
21

RaymarchingSketches

Raymarching shader sketches in GLSL
GLSL
3
star
22

UnityVJ

GLSL
3
star
23

WinterJamBisous

C#
3
star
24

LightPath

C#
3
star
25

leon196.github.io

My website
HTML
2
star
26

Revision-2020-WebGL

2
star
27

Smoothie

4k demo for Evoke 2018
JavaScript
2
star
28

Shapeshift

JavaScript
2
star
29

TouchDesignerSketches

2
star
30

CellularAutomatown

Project for the game jam bio and the city : https://itch.io/jam/biodesign-and-the-city
GLSL
2
star
31

RaymarchingUnityBase

HLSL
2
star
32

CookiePartyInvitro

2
star
33

ThreeSeconds

A game inspired by the comic from Marc Antoine Matthieu
GLSL
2
star
34

TheSaltPlease

JavaScript
2
star
35

WorkshopSIG

WorkshopSIG
ShaderLab
2
star
36

MystJamGame

Game for Myst Jam (http://itch.io/jam/myst-jam)
C#
1
star
37

MeliMelo

JavaScript
1
star
38

demo-regl

JavaScript
1
star
39

Mortel

WebGL demo
JavaScript
1
star
40

Geometry

JavaScript
1
star
41

PuzzleProtein

A game
JavaScript
1
star
42

GIFJamMapping

C#
1
star
43

workshop-esgi-2020-06-30

C#
1
star
44

Parmesan

4k demo for Revision 2019
JavaScript
1
star
45

workshop-esgi-2020-03-24

ShaderLab
1
star
46

ZooMachines2016

C#
1
star
47

OhoShaders

C
1
star
48

WebGLWorkshop

JavaScript
1
star
49

workshop-esgi-2020-05-25

C#
1
star
50

Boupiboup

Boupiboup
C#
1
star