[ 수학적인 평면 만들기 ]
Support multiple function methods.
Use a delegate and enumeration.
Display 2D functions with a grid.
Define surfaces in 3D space.
전 단계에서 진행한 라인 그래프를 확장한 것으로 복잡한 수식을 이용하여 표현한 평면 그래프(라인 그래프의 집합) 를 만들자
[ 1 Switching Between Functions ]
다양한 그래프를 표현할 수 있도록 각각의 그래프들을 함수로 만들 수 있도록 하자.
[ 1.1 Function Method ]
public class Graph02 : MonoBehaviour {
public Transform pointPrefab;
[ Range( 10, 100) ] public int resolution = 50;
Transform[] points;
void Awake() {
float step = 2f / resolution;
Vector3 scale = Vector3.one * step;
Vector3 position;
position.y = position.z = 0f;
points = new Transform[resolution];
for (int i = 0; i < resolution ; i++) {
Transform point = Instantiate (pointPrefab);
//point.localPosition = Vector3.right * ((i+0.5f) / 5 - 1f);
position.x = (i + 0.5f) * step - 1f;
//position.y = position.x * position.x * position.x;
point.localPosition = position;
point.localScale = scale;
point.SetParent (transform,false);
points [i] = point;
}
}
float SineFunction(float x, float t) {
return Mathf.Sin (Mathf.PI * (x + t));
}
void Update() {
float t = Time.time;
for (int i = 0; i < points.Length; i++) {
Transform point = points [i];
Vector3 position = point.localPosition;
//position.y = Mathf.Sin(Mathf.PI * (position.x + Time.time));
position.y = SineFunction(position.x, t);
point.localPosition = position;
}
}
}
SineFunction(float x, float t)
x좌표 값에 따라 상응하는 y 좌표값을 얻어내는 부분을 함수로 구현
t (시간) 값에 따라 다른 y값을 얻을 수 있음
[ 1.2 A Second Function ]
// 1)
float MultiSineFunction1(float x, float t) {
float y = Mathf.Sin(Mathf.PI * (x + t)); // y 값이 (-1 ~ 1)의 범위를 가지게 된다.
y += Mathf.Sin(2f * Mathf.PI * (x + t)) / 2f; // y 값이 (-1.5 ~ 1.5)의 범위를 가지게 된다. sin(x) + sin(2x)/2
return y;
}
// 2)
float MultiSineFunction2(float x, float t) {
float y = Mathf.Sin(Mathf.PI * (x + t)); // y 값이 (-1 ~ 1)의 범위를 가지게 된다.
y += Mathf.Sin(2f * Mathf.PI *(x + 2f * t)) / 2f;
y *= 2f / 3f; // y 값이 다시 (-1 ~ 1)의 범위를 가지게 된다. 시간 간격을 2배로 하고 있어 급격하게 값이 변하게 하고 있음
return y;
}
void Update() {
float t = Time.time;
for (int i = 0; i < points.Length; i++) {
Transform point = points [i];
Vector3 position = point.localPosition;
//position.y = Mathf.Sin(Mathf.PI * (position.x + Time.time));
position.y = MultiSineFunction1(position.x, t);
point.localPosition = position;
}
}
참고 사이트 : 그래프 그려주는 사이트
[ 1.3 Selecing Functions in the Editor ]
Graph : Inspector 에서 실행 시간에 Function 선택이 가능해짐
[Range(0, 2)]
public int function;
void Update() {
float t = Time.time;
for (int i = 0; i < points.Length; i++) {
Transform point = points [i];
Vector3 position = point.localPosition;
//position.y = Mathf.Sin(Mathf.PI * (position.x + Time.time));
//position.y = TestFucion2(position.x, t); //
//position.y = MultiSineFunction2(position.x, t);
if (function == 0) {
position.y = SineFunction (position.x, t);
} else if (function == 1) {
position.y = MultiSineFunction1 (position.x, t);
} else {
position.y = MultiSineFunction2 (position.x, t);
}
point.localPosition = position;
}
}
[ 1.4 Static Methods ]
static float SineFunction(float x, float t) {
return Mathf.Sin (Mathf.PI * (x + t));
}
static float MultiSineFunction(float x, float t) {
float y = Mathf.Sin(Mathf.PI * (x + t));
y += Mathf.Sin(2f * Mathf.PI * (x + t)) / 2f;
//y += Mathf.Sin(2f * Mathf.PI *(x + 3f * t)) / 2f;
y *= 2f / 3f;
return y;
}
static 멤버 변수, 메소드
클래스 이름공간의 영향을 받는 전역 객체 (클래스 이름공간으로 접근이 가능함)
객체의 멤버 변수나 멤버 함수를 직접 참조/호출할 수 없음(즉, static 멤버 함수는 매개변수를 이용하여 처리하는 형태로 독립적인 처리가 가능해야 함)
[ 1.5 Delegates ]
if-else 구문을 없애기 위해서 delegate를 사용 (C언어의 함수포인터)
참고 사이트 - delegate
Project Window : Create >> New Script
Name : GraphFunction
public delegate float GraphFunction(float x, float t);
Graph 스크립트
void Update() {
float t = Time.time;
GraphFunction f;
if (function == 0) {
f = SineFunction;
} else {
f = MultiSineFunction;
}
for (int i = 0; i < points.Length; i++) {
Transform point = points [i];
Vector3 position = point.localPosition;
//if (function == 0) {
// position.y = SineFunction (position.x, t);
//} else {
// position.y = MultiSineFunction (position.x, t);
//}
position.y = f(position.x, t);
point.localPosition = position;
}
}
[ 1.6 An Array of Delegates ]
static GraphFunction[] function ...
static 변수로 다른 클래스에서도 사용 가능
[ Range(0,1) ] public int function;
static GraphFunction[] functions = {
SineFunction, MultiSineFunction
};
void Update() {
float t = Time.time;
GraphFunction f = functions [function];
//if (function == 0) {
// f = SineFunction;
//}
//else {
// f = MultiSineFunction;
//}
for (int i = 0; i < points.Length; i++) {
Transform point = points [i];
Vector3 position = point.localPosition;
position.y = f(position.x, t);
point.localPosition = position;
}
}
[ 1.7 Enumerations ]
Enumeration (열거 자료형)
0, 1, 2 숫자 형태로 지칭하는 것보다는 문자열로 표현될 수 있도록 하는 자료형
예제에서 0이면 SineFunction, 1이면 MultiSineFunction으로 표현하는 것 보다는 이름으로 지칭할 수 있도록 지정하는 것이 효율적
Project Window : Create >> C# Scipt
Name : GraphFunctionName
기존의 GraphFunction.cs을 사용해도 무방
public enum GraphFunctionName {
Sine,
MultiSine
}
Graph 스크립트
// [ Range (0, 1) ] public int function;
public GraphFunctionName function;
static GraphFunction[] functions = {
SineFunction, MultiSineFunction
};
void Update() {
float t = Time.time;
GraphFunction f = functions [(int)function];
for (int i = 0; i < points.Length; i++) {
Transform point = points [i];
Vector3 position = point.localPosition;
position.y = f(position.x, t);
point.localPosition = position;
}
}
실행 테스트
[ 2 Adding Another Dimension ]
3 차원 공간 그래프 만들기
지금까지 2차원적인 그래프를 그렸던 것을 3차원 공간 그래프로 만들기 위한 작업들을 우선적으로 수행하자.
Shader Code(ColoredPoint) 수정
o.Albedo.rgb = IN.worldPos.xyz * 0.5 + 0.5;
z 좌표 값을 포함한 공간적인 그래프를 만들기 위해서 우선 shader에서 z 좌표 값에 따른 색상을 조절 할 수 있도록 함
[ 2.1 Adjusting the Functions ]
public delegate float GraphFunction ( float x, float z, float t);
Graph02.cs 수정
static float SineFunction(float x, float z, float t) {
return Mathf.Sin (Mathf.PI * (x + t));
}
static float MultiSineFunction(float x, float z, float t) {
float y = Mathf.Sin(Mathf.PI * (x + t));
y += Mathf.Sin(2f * Mathf.PI * (x + t)) / 2f;
//y += Mathf.Sin(2f * Mathf.PI *(x + 3f * t)) / 2f;
y *= 2f / 3f;
return y;
}
void Update() {
float t = Time.time;
GraphFunction f = functions [(int)function];
for (int i = 0; i < points.Length; i++) {
Transform point = points [i];
Vector3 position = point.localPosition;
position.y = f(position.x, position.z, t);
point.localPosition = position;
}
}
[ 2.2 Creating a Grid of Points ]
우선, 한개의 라인을 만들었던 것을 여러 개의 라인(Grid 처럼)으로 표현하기 위해서 z좌표값을 따라 생성
정점의 개수를 resolution * resolution만큼 늘려서 표현하기
void Awake() {
float step = 2f / resolution;
Vector3 scale = Vector3.one * step;
Vector3 position;
position.y = position.z = 0f;
points = new Transform[resolution * resolution];
for (int i = 0; i < points.Length ; i++) {
Transform point = Instantiate (pointPrefab);
//point.localPosition = Vector3.right * ((i+0.5f) / 5 - 1f);
position.x = (i + 0.5f) * step - 1f;
//position.y = position.x * position.x * position.x;
point.localPosition = position;
point.localScale = scale;
point.SetParent (transform,false);
points [i] = point;
}
}
긴 라인이 생성
z 값에 변화를 줘서 평면 형태로 만들 수 있도록 하자.
void Awake() {
float step = 2f / resolution;
Vector3 scale = Vector3.one * step;
Vector3 position;
position.y = position.z = 0f;
points = new Transform[resolution * resolution];
for (int i = 0, x = 0, z = 0; i < points.Length; i++, x++) {
if (x == resolution) { // 사인 곡선의 한 싸이클이 돌면, z값을 1증가 시키고 있음
x = 0;
z += 1;
}
Transform point = Instantiate (pointPrefab);
//point.localPosition = Vector3.right * ((i+0.5f) / 5 - 1f);
position.x = (x + 0.5f) * step - 1f;
position.z = (z + 0.5f) * step - 1f;
//position.y = position.x * position.x * position.x;
point.localPosition = position;
point.localScale = scale;
point.SetParent (transform,false);
points [i] = point;
}
}
실행 테스트
Directional Light
Transform : rotation ( 50, 30, 0)
Light : Shadow Type 해보기
Frame rate 확인
[ 2.3 Double Looping ]
//for (int i = 0, x = 0, z = 0; i < points.Length; i++, x++) {
// if (x == resolution) {
// x = 0;
// z += 1;
// }
for(int i = 0, z = 0; z < resolution; z++) {
position.z = (z + 0.5f) * step - 1f;
for (int x = 0; x < resolution; x++, i++) {
Transform point = Instantiate (pointPrefab);
//point.localPosition = Vector3.right * ((i+0.5f) / 5 - 1f);
position.x = (x + 0.5f) * step - 1f;
//position.z = (z + 0.5f) * step - 1f;
point.localPosition = position;
point.localScale = scale;
point.SetParent (transform, false);
points [i] = point;
}
}
내부적으로는 x 좌표를 기준으로 루프를 만들어 y좌표값을 생성하고,외부적으로는 z좌표를 기준으로 이를 다시 감싸서 수행하고 있음
[ 2.4 Incorporating Z ]
y 값의 변화를 x 값에 의해서만 영향을 받게 했던 것을 z 값에 따라서도 영향을 받을 수 있도록 그래프를 만들어 보자.
편의성을 위해서 pi 변수를 const로 만들어서 사용
const float pi = Mathf.PI;
static float SineFunction(float x, float z, float t) {
return Mathf.Sin (pi * (x + t));
}
static float MultiSineFunction(float x, float z, float t) {
float y = Mathf.Sin(pi * (x + t));
y += Mathf.Sin(2f * pi * (x + t)) / 2f;
y *= 2f / 3f;
return y;
}
기존의 두 함수(SineFunction, MultiSineFunction) 이외에 다른 하나를 추가
static float Sine2DFunction (float x, float z, float t) {
return Mathf.Sin(pi * (x + z + t)); // 1) x값과 z값의 영향을 같이 받음
/* // 2)
float y = Mathf.Sin(pi * (x + t));
y += Mathf.Sin(pi * (z + t));
y *= 0.5f;
return y;
*/
}
static GraphFunction[] functions = {
SineFunction, Sine2DFunction, MultiSineFunction
};
public enum GraphFunctionName {
Sine, Sine2D, MultiSine
}
1) 실행 테스트
x와 z를 더한 값 자체가 y값에 영향을 줌
2) 실행 테스트
x축과 z축에 따른 y값 (z = 0 일때 xy평면은 Sine 곡선을 그리고 있고, z=0 일때 zy평면도 Sine 곡선을 그리고 있음)
"/= 2f" "*= 0.5f" 를 사용하는 이유 : 곱하기보다 나누기의 연산속도가 현저하게 느림
다른 함수(MultiSine2DFunction) 추가
Main Wave : 시간을 2로 나누고 있다. wave의 속도를 2배 감소 시킴
Second Wave : x축에 형성되는 Wave 파형
Third Wave : z축에 형성되는 wave의 속도를 2배로 올림
최종 f(x,z,t)가 -1 ~ 1을 벗어나지 않도록 5.5(M 최대값 4, Second 최대값 1, Third / 2의 최대값 0.5)로 나눔
static float MultiSine2DFunction (float x, float z, float t) {
float y = 4f * Mathf.Sin(pi * (x + z + t * 0.5f));
y += Mathf.Sin(pi * (x + t));
y += Mathf.Sin(2f * pi * (z + 2f * t)) * 0.5f;
y *= 1f / 5.5f;
return y;
}
static GraphFunction[] functions = {
SineFunction, Sine2DFunction, MultiSineFunction, MultiSine2DFunction
};
public enum GraphFunctionName {
Sine, Sine2D, MultiSine, MultiSine2D
}
[ 2.5 Creating a Ripple(물결) ]
구심점을 가지는 물결 패턴을 만들자.
위의 그림처럼 표현되기 위해서 필요한 공식 (xy평면의 곡선과 zy평면의 곡선 공식이 같음)
y값의 경우 x,z가 0인경우(x,z 평면의 정중앙 위치) 0값을 가지고, x와z 가 1(or -1)이면 최대값 [루트 2]를 가지게 됨
static float Ripple (float x, float z, float t) {
float d = Mathf.Sqrt(x * x + z * z);
float y = d;
return y;
}
static GraphFunction[] functions = {
SineFunction, Sine2DFunction, MultiSineFunction, MultiSine2DFunction, Ripple
};
public enum GraphFunctionName {
Sine, Sine2D, MultiSine, MultiSine2D, Ripple
}
파형 만들기
static float Ripple (float x, float z, float t) {
float d = Mathf.Sqrt(x * x + z * z);
//float y = d;
float y = Mathf.Sin(pi * d); // 1)
//float y = Mathf.Sin(4f * pi * d); // 2)
//float y = Mathf.Sin(4f * pi * d - t); // 4)
//y /= 1f + 10f * d; // 3)
return y;
}
1) d값의 범위에 pi를 곱한 sine 의 최대값은 -1 ~ 1을 넘지 않음
2) 4를 더 곱해주면 Wave 파형이 4번 정도가 더 그려짐
3) 멀어질 수록 1/(10 *d)는 파도의 진폭을 줄임, 1/(1+10*d)에서 기본 값 1 더하는 이유는 원점에 가까운 지폭의 폭을 줄여줌
4) 시간의 흐름에 따른 애니메이션을 표현 (물결이 퍼지는 표현을 위해 -t를 적용)
[ 3 Leaving the Grid ]
지금까지 x와 z를 사용해 y값을 추출해 주는 방식은 항상 XZ 평면에 국한적이다. ( 모든 XZ평면의 정점은 오직 한개만의 Y정점을 가지고 있다)
[ 3.1 Three-dimensional Function ]
u, v : 기존의 x, z 값을 대체 (xz 평면의 값에 따른 y 값만을 생성하는 형태가 아니라 Vector3를 설정할 수 있도록 함)
기존의 SineFunction을 표현한 형태
//public delegate float GraphFunction(float x, float y, float t);
public delegate Vector3 GraphFunction(float u, float v, float t);
Vector3를 사용하기 위해서 using UnityEngine; 을 추가해 주어야 함
float형의 y 값을 얻어내던 함수를 x, y, z 값을 얻어 낼 수 있도록 Vector3 자료형으로 기좀 함수를 바꿈
//static float SineFunction(float x, float z, float t) {
static Vector3 SineFunction(float x, float z, float t) {
//return Mathf.Sin (pi * (x + t));
Vector3 p;
p.x = x;
p.y = Mathf.Sin (pi * (x + t));
p.z = z;
return p;
}
//static float MultiSineFunction(float x, float z, float t) {
static Vector3 MultiSineFunction(float x, float z, float t) {
Vector3 p;
p.x = x;
p.y = Mathf.Sin (pi * (x + t));
p.y += Mathf.Sin (2f * pi * (x + 2f * t)) / 2f;
p.y *= 2f / 3f;
p.z = z;
return p;
//float y = Mathf.Sin(pi * (x + t));
//y += Mathf.Sin(2f * pi * (x + t)) / 2f;
////y += Mathf.Sin(2f * Mathf.PI *(x + 3f * t)) / 2f;
//y *= 2f / 3f;
//return y;
}
//static float Sine2DFunction (float x, float z, float t) {
static Vector3 Sine2DFunction (float x, float z, float t) {
Vector3 p;
p.x = x;
p.y = Mathf.Sin (pi * (x + t));
p.y += Mathf.Sin (pi * (z + t));
p.y *= 0.5f;
p.z = z;
return p;
//return Mathf.Sin(pi * (x + z + t)); // 1)
// 2)
//float y = Mathf.Sin(pi * (x + t));
//y += Mathf.Sin(pi * (z + t));
//y *= 0.5f;
//return y;
}
//static float MultiSine2DFunction (float x, float z, float t) {
static Vector3 MultiSine2DFunction (float x, float z, float t) {
Vector3 p;
p.x = x;
p.y = 4f * Mathf.Sin (pi * (x + z + t / 2f));
p.y += Mathf.Sin (pi * (x + t));
p.y += Mathf.Sin (2f * pi * (z + 2f * t)) * 0.5f;
p.y *= 1f / 5.5f;
p.z = z;
return p;
//float y = 4f * Mathf.Sin(pi * (x + z + t * 0.5f));
//y += Mathf.Sin(pi * (x + t));
//y += Mathf.Sin(2f * pi * (z + 2f * t)) * 0.5f;
//y *= 1f / 5.5f;
//return y;
}
//static float Ripple (float x, float z, float t) {
static Vector3 Ripple (float x, float z, float t) {
Vector3 p;
float d = Mathf.Sqrt (x * x + z * z);
p.x = x;
p.y = Mathf.Sin (pi * (4f * d - t));
p.y /= 1f + 10f * d;
p.z = z;
return p;
//float d = Mathf.Sqrt(x * x + z * z);
////float y = d;
////float y = Mathf.Sin(pi * d); // 1)
////float y = Mathf.Sin(4f * pi * d); // 2)
//float y = Mathf.Sin(5f * pi * d - t); // 4)
//y /= 1f + 10f * d; // 3)
//return y;
}
그에 따른 Update, Awake 함수 조정
void Awake() {
float step = 2f / resolution;
Vector3 scale = Vector3.one * step;
Vector3 position;
position.y = position.z = 0f;
points = new Transform[resolution * resolution];
// for(int i = 0, z = 0; z < resolution; z++) {
// position.z = (z + 0.5f) * step - 1f;
// for (int x = 0; x < resolution; x++, i++) {
// Transform point = Instantiate (pointPrefab);
// //point.localPosition = Vector3.right * ((i+0.5f) / 5 - 1f);
// position.x = (x + 0.5f) * step - 1f;
// //position.z = (z + 0.5f) * step - 1f;
// //position.y = position.x * position.x * position.x;
// point.localPosition = position;
// point.localScale = scale;
// point.SetParent (transform, false);
// points [i] = point;
// }
// }
for (int i = 0; i < points.Length; i++) {
Transform point = Instantiate (pointPrefab);
point.localScale = scale;
point.SetParent (transform, false);
points [i] = point;
}
}
void Update() {
float t = Time.time;
GraphFunction f = functions [(int)function];
// for (int i = 0; i < points.Length; i++) {
// Transform point = points [i];
// Vector3 position = point.localPosition;
// position.y = f(position.x, position.z, t);
// point.localPosition = position;
// }
float step = 2f /resolution;
for (int i = 0, z = 0; z < resolution; z++) {
float v = (z + 0.5f) * step - 1f;
for (int x = 0; x < resolution; x++, i++) {
float u = (x + 0.5f) * step - 1f;
points [i].localPosition = f (u, v, t);
}
}
}
[ 3.2 Creating a Cylinder ]
우선 xz 평면에 Circle을 만들어 보자
static Vector3 Cylinder (float u, float v, float t) {
Vector3 p;
p.x = Mathf.Sin (pi * u);
p.y = 0f;
//p.y = u;
p.z = Mathf.Cos (pi * u);
return p;
}
GraphFunctionName 에 "Cylinder" , GraphFuncion[] funcstions에 "Cylinder" 추가해야 함
xz 평면에서,
p.y = u;
u의 변위에 따라 y값을 세팅하는 경우
Cylinder
static Vector3 Cylinder (float u, float v, float t) {
Vector3 p;
float r = 1f;
p.x = r * Mathf.Sin (pi * u);
p.y = v;
p.z = r * Mathf.Cos (pi * u);
return p;
}
다양한 원주율에 따른 모형 변형
float r = 1f + Mathf.Sin(6f * pi * u) * 0.2f;
3번의 주기율(6번의 pi),
float r = 1f + Mathf.Sin(2f * pi * v) * 0.2f;
v의 변위 값에 따라 r의 값을 정하게 되면, r값이 y축으로 sin 곡선을 그리게 됨
r의 값에 t 값을 적용
float r = 0.8f + Mathf.Sin(pi * (6f * u + 2f * v + t)) * 0.2f;
[ 3.3 Creating a Sphere ]
float r = Mathf.Cos(pi * 0.5f * v);
pi에 0.5f를 곱해서 원주기 반을 만듬
p.y = Mathf.Sin(pi * 0.5f * v);
u값에 따라 xz평면에 Circle을 만들었던 것 처럼, v값에 따라 xy 축에 Circle이 생성될 수 있도록 생성
static Vector3 Sphere (float u, float v, float t) {
Vector3 p;
float r = Mathf.Cos(pi * 0.5f * v) ;
p.x = r * Mathf.Sin (pi * u);
p.y = v;
//p.y = Mathf.Sin (pi * 0.5f * v);
p.z = r * Mathf.Cos (pi * u);
return p;
}
정리하면,
x, z 좌표는 S 배 크기만 큼의 Circle을 그림
y 좌표는 R 크기의 반원 크기를 가짐
ㅇ
R <= 1이고, u와 v에 따라 R 값의 크기가 바뀌게 됨
static Vector3 Sphere2 (float u, float v, float t) {
Vector3 p;
float r = 0.8f + Mathf.Sin(pi * (6f * u + t)) * 0.1f ;
r += Mathf.Sin (pi * (4f * v + t)) * 0.1f;
float s = r * Mathf.Cos (pi * 0.5f * v);
p.x = s * Mathf.Sin (pi * u);
p.y = r * Mathf.Sin (pi * 0.5f * v);
p.z = s * Mathf.Cos (pi * u);
return p;
}
[ 3.4 Creating a Torus ]
float s = Mathf.Cos (pi * v) + 0.5f;
기존 Sphere를 만드는 코드에서 s 부분에 0.5f를 추가
static Vector3 Torus (float u, float v, float t) {
Vector3 p;
float s = Mathf.Cos (pi * 0.5f * v) + 0.5f;
p.x = s * Mathf.Sin (pi * u);
p.y = Mathf.Sin (pi * 0.5f * v);
p.z = s * Mathf.Cos (pi * u);
return p;
}
pi * 0.5 >> pi 로 변경
반 원주에서 원주기를 가짐
static Vector3 Torus (float u, float v, float t) {
Vector3 p;
float s = Mathf.Cos (pi * v) + 0.5f;
p.x = s * Mathf.Sin (pi * u);
p.y = Mathf.Sin (pi * v);
p.z = s * Mathf.Cos (pi * u);
return p;
}
float r1 = 1f; float s = Mathf.Cos(pi * v) + r1;
S와 y좌표값과 대응되는 부분을 0.5 * pi에서 pi로 바꿔 줌
R1 > 1 일때 마다 중앙에 홀이 생김
static Vector3 Torus (float u, float v, float t) {
Vector3 p;
float r1 = 3f;
float s = Mathf.Cos (pi * v) + r1;
p.x = s * Mathf.Sin (pi * u);
p.y = Mathf.Sin (pi * v);
p.z = s * Mathf.Cos (pi * u);
return p;
}
A ring torus 와 그밖의 것
static Vector3 Torus (float u, float v, float t) {
Vector3 p;
float r1 = 1f;
float r2 = 0.5f;
float s = r2 * Mathf.Cos (pi * v) + r1;
p.x = s * Mathf.Sin (pi * u);
p.y = r2 * Mathf.Sin (pi * v);
p.z = s * Mathf.Cos (pi * u);
return p;
}
r1 : 1보다 크면 torus의 중앙에 Hall이 생성
r2 : torus의 반지름
static Vector3 Torus (float u, float v, float t) {
Vector3 p;
float r1 = 0.65f + Mathf.Sin(pi * (6f * u + t)) * 0.1f;
float r2 = 0.2f + Mathf.Sin (pi * (4f * v + t)) * 0.05f;
float s = r2 * Mathf.Cos (pi * v) + r1;
p.x = s * Mathf.Sin (pi * u);
p.y = r2 * Mathf.Sin (pi * v);
p.z = s * Mathf.Cos (pi * u);
return p;
}
6f : 6개의 반주기, 4f : 4개의 반주기