FX/Houdini_Joy of VEX

Joy of Vex - Day 11: if statements

Gamestonk118 2022. 7. 8. 17:19

중요했던 부분:

- If Statement (Conditional: 조건문)

지금까지 우리가 써왔던 식들은 굉장히 직선적이고 명령적이었다. 사용자인 우리가 후디니에게 직접적인 명령만을 하였다.

하지만 if Statement를 통해 후디니에게 판단의 기회를 내려준다.

eg)

if (A) { // 만약 A라면

       B; // B를 수행해주어라

}

@Cd = 0; // @Cd 색깔값으로 0으로 초기화
if (@P.x > 1) { // 만약 geometry의 @P.x의 수치가 1보다 크다면
	@Cd = {1, 0, 0}; // @Cd 즉 색깔값이 빨간색이 된다.
}

Grid상에서 @P.x를 기준으로 봤을때 수치가 1일때부터 나뉘어 흑과 적색으로 나뉘어지는 것을 알 수 있다.

/* 모든 점이 가진 @P에 대해 후디니는 판별을 할 것이다. 3보다 큰지 작은지. 만약 3보다 크다면 아래의 식을 수행한다*/

float d = length (@P); // 원점으로부터 모든 포인트의 거리길이를 float variable d에 저장
if (d > 3) { // 만약 원점으로부터 포인트까지의 거리길이가 3이상이라면
	@P.y = 1; // @P.y 높이값은 1이 된다
}

// 이는 아래와 같이 chf()을 사용하여 조건문을 파라미터로서 조절할 수 있게 할수 있다
float d = length (@P); // 원점으로부터 모든 포인트의 거리길이를 float variable d에 저장
if (d > chf ("D")) { // 만약 원점으로부터 포인트까지의 거리길이가 유저가 파라미터로 지정한 값보다 크다면
	@P.y = 1; // @P.y 높이값은 1이 된다
}

이렇게 원점으로부터 거리길이가 3부터 큰 모든 포인트의 높이값이 1로 올라간 것을 알 수 있다.

If statement는 평소에 쓰는 명령에서의 문장에서 등호를 하나만 썼던거와 다르게 같다라는 의미로 사용할 땐 등호를 두번 써넣어야 한다.

if (@ptnum == 1) { // @ptnum 포인트 넘버가 1이라면
	@P.y = 1; // @P.y 높이값을 1로 올려라
}

이와 같이 @ptnum수치가 1인 포인트에만 조건문안에 있는 식이 작동된것을 알 수 있다.

/* 만약 if statement에 등호를 하나만 붙이면 if statement자체가 성립이 되지 않아 무시당하게 되고
곧바로 아래 있는 식을 수행하여 방금전 이미지와 동일한 형태로 만들지 못하게 한다*/

if (@ptnum = 1) { // 등호가 하나밖에 없어서 성립안됨
	@P.y = 1; // 모든 포인트에 대해 @P.y 높이값을 1로 올려라
}

위와는 다르게 if statement가 제대로 작동되지 않아 모든 포인트가 1만큼 올라간 것이 보인다

다시한번 relpointbbox()를 상기시켜 보자면:

geometry를 감싸고 있는 bounding box를 기준으로 x/y/z가 가장 작은 곳은 0이 되고 x/y/z값을 최대 1로써 각각의 비율을 소수점의 형태로 반환해주는 function.

/* 0번 포인트로 들어온 geometry의 @P 포인트 위치값을 0부터 1까지의 Range로 조정하여 
vector variable bbox에 저장*/
@Cd = {1, 0, 0}; // 본래 geometry의 색깔값을 빨강으로 설정

if (bbox.y < 0.5) { // 만약 geometry의 y축으로 봤을때 0.5이하라면 
	@Cd = {0, 1, 0} // 그 부분은 초록색으로 바꾸어버려라
}

@P.y값의 0.5부근 아래로는 전부 초록색으로 Tommy가 칠해진 것을 알 수 있다.

else문은 if statement에 부합되지 않는 부분들에 대해 명령를 내려줄 수 있다. 

if (@P.x > chf ("WHERE")) { // 만약 @P.x값이 유저가 파라미터로 지정한 WHERE값보다 크다면
    @Cd = {1, 0, 0}; // 빨간색으로 변환하여라
} else { // 그렇지 않는 부분은
    @Cd = {0, 0, 1}; // 파란색으로 변환하여라
}

WHERE이 0.35로 지정되었을때 그 수치보다 높은 @P.x값을 가지는 포인트들은 빨간색 아닌부분은 파란색을 띄는 것을 확인 할 수 있다.

dot()은 빛이 얼마나 세게 맺힐지의 대한 내용을 정의해주는 function으로써 아래와 같은 식대로 작동되는 것을 볼 수 있다.

dot(A, B) = length (A)*length (B)*cos (θ)

cos()으로 생긴 파형. 각기 다른 각도 (X축)에 따라 빛의 세기 (Y축)이 결정난다.

// Geometry의 normal값과 vector {0, 1, 0}사이의 내적각도를 구하여 float variable d 에다 저장하여라
float d = dot (@N, {0, 1, 0});

if (d > chf ("CUTOFF")) { // 만약 내적각도 (cos(θ))가 유저가 지정한 파라미터의 값보다 크다면
    @Cd = {1, 1, 1}; // 그 부분은 흰색으로 바꾸어라
} else { // 아니면 그 나머지 부분은 
    @Cd = {1, 0, 0}; // 그 부분은 빨간색으로 바꾸어라
}

여기서 @N과 vector {0, 1, 0}의 내적각도는 아래 이미지와 같은 원리로 작동된다.

이때 만약 chf()의 파라미터 CUTOFF가 0.5 (파형상에서 Y값) 이라면 파형상에서 x값이 60도를 뜻하므로 60도보다 각도가 작은 값들에만 흰색이 적용된다.

@N과 {0, 1, 0}의 내적 각도가 커질 수 록 위의 파형에 따라 y값이 낮아져 빛의 밝기가 낮아지는 것을 알 수 있다.
@N과 {0, 1, 0}의 내적 각도가 작아질 수 록 위의 파형에 따라 y값이 커져 빛의 밝기가 늘어나는 것을 알 수 있다.
@N과 {0, 1, 0}의 내적 각도가 0이면 위의 파형에 따라 y값이 커져 빛의 밝기가 1로써 제일 큰 값을 가지는 것을 알 수 있다.
vector {0, 1, 0}과 비교했을때 Geometry의 normal과 내적 각도가 60도가 넘지 않는 면들은 모두 흰색으로 칠해진 것을 알 수 있다. 그렇지 않는 면들은 빨간색으로 칠해졌다.

Epsilon Test는 함수의 극한을 수학적으로 명확하게 정의하는 방법이다.

// Epsilon Test를 사용하지 않으면 이러한 현상이 발생한다

float a = 0; // float variable a 는 0임
// 원래는 0이지만 PI가 무한 소수인 컴퓨터는 어느 소수점 위치에서 반올림을 하게 되어 정확한 수치를 얻지 못한다
// 그러므로 0과 가까운 근사값을 float variable b는 가지게 된다
float b = sin ($PI);

if (a == b) { // 이는 수학적으로는 맞지만 컴퓨터가 인간의 생각으로 해석을 하지 못하기 때문에
 	A; // 이 조건이 제대로 작동을 하지 못함
}
// 그러므로 더욱이 정확하게 근사값인 수치에 조건문을 알맞게 작동시키기 위해

float a = 0.000001; 
float b = 0;

if (abs (a - b) < 0.001) { // a에서 b를 빼준값의 절댓값이 0.001보다 작다면 
	blah blah; // 조건문을 수행해주어라
}

/* 여기서 abs()를 붙인 이유는 a와 b의 차가 양수를 유지하게끔 만들기 위함이다. 그렇지 않으면
음수가 어느 양수보다 값이 작은것은 당연한 이치므로 조건문이 올바르게 작동되지 못하기 때문이다*/

if statement를 여러번 적음으로써 조건들을 여러게 수행시켜줄 수 있다.

if (@ptnum > 50) { // @ptnum 포인트 넘버가 50보다 크고
	if (@P.x > 2) { // @P.x 포인트 x위치가 2보다 크면
    		@Cd = {1, 0, 0}; // 그 부분은 빨간색으로 바꾸어라
   	}
}

// 이때의 코드는 조건이 두 개이다
// 1. @ptnum > 50
// 2. @P.x < 2

 

이때 여러개의 조건들을 한 if statement문장에다 적는 방법이 있다. 이때 두가지 방법이 있는데 "&&"은 AND의 의미로써 if statement안에 있는 조건들을 전부다 충족했을때만 그 다음 문장이 구현된다. 

if (@ptnum > 50 && @P.y < 2) { // 조건을 두 개다 충족한다면
	@Cd = {1, 0, 0}; // 이 식을 수행하여라
}

Grid상에서 위의 조건들만 성립한 부분들만 빨간색으로 칠해진것을 알 수 있다.

이와는 다르게 "||"은 OR의 의미로써 if statement안에 있는 조건들을 한가지만이라도 충족했을때만 그 다음 문장이 구현된다.

if (@ptnum > 50 || @P.y < 2) { // 조건을 하나만이라도 충족한다면
	@Cd = {1, 0, 0}; // 이 식을 수행하여라
}

Grid상에서 위의 조건들중 하나라도 성립한 부분들은 전부 빨간색으로 칠해진것을 알 수 있다.

그리고 등호나 부등호을 응용한 다른 기호들을 통해 조건문을 더욱 풍부하게 만들 수 있다.

if (@ptnum != 50) { // 만약 @ptnum 포인트 넘버가 50이 아니라면 
        @Cd = {1, 0, 0}; // 그 부분은 전부 빨간색으로 칠하여라
}

if (@ptnum >= 50) { // 만약 @ptnum 포인트 넘버가 50이 크거나 같다면
        @Cd = {1, 0, 0}; // 그 부분은 전부 빨간색으로 칠하여라
}

if (@ptnum <= 50) { // 만약 @ptnum 포인트 넘버가 50이 작거나 같다면
        @Cd = {1, 0, 0}; // 그 부분은 전부 빨간색으로 칠하여라
}

또한 Modulo와 같은 식들을 If statement안에 적용함으로써 특정한 곳에서만 식이 적용되게끔 할 수 있다.

if (@ptnum%5 == 0) { // @ptnum 포인트 넘버가 5에 배수일 경우에만 
	@Cd = {1, 0, 0}; // 빨간색으로 나타내어라
}

지금까지 배웠던것을 조금 응용하자면 이러한 코드를 쓸 수 있다.

/*원점으로부터 @P 모든 포인트까지의 거리길이를 두 배로 곱해주고 각 @ptnum 포인트 넘버에서 5를 나눈값의 나머지를
더하여 float variable a에 저장하여라*/
float a = length (@P)*2 + @ptnum%5;
/* 광원에서 수직이 되는 vector값과 Normal값을 가진 Vector값의 내적 각도를 구하여 @Time을 곱해 움직이는 결과를
생성하고 Float variable b에 저장하여라*/
float b = dot (@N, {0, 1, 0})*@Time;

if (a > b) { // 만약 a값의 수치가 b값의 수치보다 크다면
    @Cd = {1, 0, 0}; // Geometry의 그 부분은 빨간색으로 칠하여라
}
/*원점으로부터 @P 모든 포인트까지의 거리길이를 두 배로 곱해주고 각 @ptnum 포인트 넘버에서 5를 나눈값의 나머지를
더한 값이 0과 같다면*/
if (length (@P)*2 + @ptnum%5 == 0) {
	@Cd = {1, 0, 0}; // 그 부분은 빨간색으로 칠하여라
}

 

if statement에서 해당되는 포인트들만 빨간색으로 칠해지는 명령문을 수행하여 마치 눈이 쌓여가다가 녹아내리는 모양의 애니메이션을 만들 수 있다. 

Exercises:

1. Waves that only start after @Time being greater than 2 seconds

If statement 조건문을 사용하여 정확히 2초후에 geometry의 wave가 생성되게끔 세팅해주었다. 후디니에서 1초는 24프레임이다. 그러므로 2초는 48프레임으로써 48프레임을 지나면 웨이브가 딱 생성되게끔 조건문을 맞추어주었다.

/* * 변수 생성 & 효과 생성 * */

// 여기 float variable fx는 전체적인 effect를 추가한 값임. 모든 효과에 대해 정의를 내려준다.
float fx = 0;
// 원점으로부터 모든 포인트까지의 거리 길이를 float variable d에 저장하여라
float d = length (@P);
d *= chf ("FREQ"); // d에 float 상수를 곱해줌으로써 웨이브의 frequency조절
d -= @Time*chf ("SPEED"); //d에 @Time을 곱해줌으로써 모션을 생성하고 float상수를 곱해줌으로써 speed조절

// 위의 만든 식들을 sin()에 적용하여 float variable fxA에 적용하여 effect를 생성해준다
float fxA = sin (d);

/* * 효과를 택 * */

/*조건문을 사용하여 @Time기준으로 2초가 넘어갈 48번째 프레임때부터 웨이브가 생성되도록 하였다*/
if (@Time >= 2) { // @Time이 2보다 크다면 
	/* 맨 위에 선언한 float variable fx에 모든 effect variable을  추가하였다.*/
    fx += fxA*chf ("HEIGHT"); // effect가 생성되게끔 하여라
}

/* * 원본에 저장 * */

/* 방금 전 추가해준 모든 ramp안에 지정된 수치값에 따라 반영된 effect들을 포함하는 float variable fx를
@P.y 높이값으로 보여주어라*/
@P.y = fx;
// @Cd 색깔값으로 보여주어라
@Cd = 0; // @Cd 색깔값을 0으로 초기화
@Cd.b = fx; // fx가 적용된 부분만 파란색으로 바꿈

 

후디니 상에서의 정확히 2초가 지난후에 grid상에서 sin()파형이 높이값과 색깔값으로 일렁이는 모션을 if statement를 통해 주어줄수 있다.

2. Colour points that have their normals facing towards a specific point/normal

조건문을 사용하여 우선적으로 @ptnum이 짝수로 입력된 점들에게 색깔값을 특정지어준 다음 if statement를 활용하여 그 점들의 normal값들만 특정 포인트만을 향하도록 세팅해주었다. 

int pt = @ptnum; // @ptnum (point number)를 integer variable pt에 저장하여라

// 0번 인풋으로 연결된 geometry의 각기 포인트 넘버값의 @P (포인트 위치)를 불러와 vector variable a에 저장
vector a = point (0, "P", pt);
// 0번 인풋으로 연결된 geometry의 0번 포인트의 @P (포인트 위치)를 불러와 vector variable b에 저장
vector b = point (1, "P", 0);

if (@ptnum%2 == 0) { // 만약 포인트의 @ptnum즉 포인트 넘버가 짝수라면
    @Cd = {1, 0, 0}; // 포인트의 @Cd 색깔을 빨간색으로 바꾸어라
}
if (@Cd == {1, 0, 0}) { // 만약 포인트의 @Cd값 색깔이 빨간색이라면
     /* a vector (모든 포인트 넘버값의 포인트 위치) 로부터 b vector ((normal)이 향하는
     포인트의 위치)까지의 방향을 @N으로 지정하여라*/
    @N = b - a;
}

@Cd값이 빨간색으로 지정된 점들의 Normal값들이 공통적으로 모두 한 포인트를 향하고 있는 것을 볼 수 있다.

3. Highlight red all the points where their ptnum can be cleanly divided by 10

이는 간단하게 if statement안에 Modulo를 적용하여 @ptnum을 10으로 나누었을때 나머지가 0으로써 딱 떨어진 포인트들만 아래 식이 적용되게끔 세팅하였다. Grid로 하면 재미가 없어서 조금 입체적인 geometry에 코드를 적용하였다.

if (@ptnum%10 == 0) { // 만약 @ptnum 포인트 넘버가 10으로 나누었을때 나머지가 0이라면
    @Cd = {1, 0, 0}; // 그 @ptnum을 가진 포인트의 @Cd 색깔값을 빨간색으로 바꾸어라
}

pighead안의 @ptnum이 10의 배수인 포인트들이 모두 빨간색으로 칠해져 있는 것이 보인다. 돼지가 많이 아파보인다. 좀 안타까운 맘이 살짝 든다.
내가 제일 좋아하는 토미로도 해보았다. 갑자기 분위기가 스릴러가 되었다. 토미가 오늘따라 이렇게 무서워 보이기는 처음인것 같다. 토미는 착해보이던 아저씨가 아니었던 것 같다.

이해가 안되었던 부분:

- 처음에 쉬웠다가 Joy of Vex 11일차 마지막의 rubbertoy를 사용한 예제에서 if statement안에 들어가는 식이 복잡해지면서 뇌정지가 오기 시작하였던것 같다. dot()을 이용해 내적각도를 구하든가든지 특정 포인트들에만 effect를 준다는지의 식은 쉬웠지만 그런 식들이 전부 짬뽕이 되기 시작하면서 왜 rubbertoy의 색깔이 시간이 지나며 저리 변하였는지 확실히 이해가 되지 않았다. if statement안에 식이 여러개 섞였을때 어떠한 포인트들이 정확히 효과를 행하기 시작할 것인지 예상하는 실력을 더욱 키워야 할 것 같다.

 

- Exercise 1에서 동일한식에 @Time만 바꾼체 @Frame에서 2*24를 하여 48프레임으로 맞추어 실행해보면 eg) @Frame >= 2*24 이렇게 적용하면 @Time때와는 다르게 (@Time은 49프레임부터 시작함) 48프레임때부터 웨이브가 작동되는것을 알 수 있었다. 식은 같고 내가 알기론 @Time에서 1이 24프레임으로 알고있는데 그렇다면 방금전 처럼 @Frame만 동일하게 바꿔준다면 똑같은 프레임부터 작동되어야 하는거 아닌가라는 의구심이 들었다. 1프레임 차이지만 무척 궁금하였다. 설명이 어려워 동영상을 첨부해야겠다.

if (@Time >= 2) { // @Time이 2보다 크다면 
    fx += fxA*chf ("HEIGHT"); // effect가 생성되게끔 하여라
}

if (@Frame >= 24*2) { // @Time이 2보다 크다면 
    fx += fxA*chf ("HEIGHT"); // effect가 생성되게끔 하여라
}

// 이 두 식 모두 웨이브가 48프레임부터 effect가 작동되어야 하는 것 아닌가? 
// 왜 @Time >= 2로 하면 48프레임이 아닌 49프레임부터 effect가 작동되어지는 것이 궁금하였다

 

직접 Attribute를 @Time에서 @Frame으로 바꿔보고 알맞게 조건문도 수정해 보았지만 두 결과의 차이가 1프레임씩 차이가나는지 궁금하였다.

공부하면서 들었던 생각:

드디어 If statement조건문을 후디니에 쓰니까 감회가 새로웠던 것 같다. 학교에서 자바를 배울때 조건문을 확실히 배운것이 많이 도움이 됬다고 생각되는 강의였다. 조건문을 쓰니 진짜 무언가 코딩을 하는 기분이 들고 진정으로 구현 할 수 있는 것이 많아진 다는 생각에 정말 설랬던것 같다. Joy of Vex커리큘럼이 정말 차근차근 잘 짜여졌다는 생각도 들고 있다. 지금까지 배워왔던 relpointbbox()나 dot()같은 조금은 심화적이었던 function들이 if statement를 만나 이러한 원리로 작동되는지 새롭게 알게되어 후디니로 더욱 할 수 있는 것들이 많아진다는 생각이 든다. 이제 벌써 커리큘럼의 반을 지났다. 벌써 반을 지나니 감회가 새롭고 이제 다음에 무엇을 배울지 궁금해 지기도 한다. 다음 파트 Day12로 가면 또 어려운 과정을 겪게 된다고 들었다. 마의 구간인 것 같다. 아마 더 열심히 해야 될 것 같다. 항상 느끼지만 후디니는 정말 대단한 소프트웨어 같다. 뭐 이걸로 못하는게 없는 것 같다. 반 지났다고 안심해지지 말고 초심과 함께 나머지 코스들을 완주해야 겠다는 생각이 더욱이 든다.