Simple Pattern Examples
우리는 noise function을 사용하여 만들어진 몇 가지 패턴들의 예제를 너에게 제공하여 이 강의를 마칠 것이다. Ken Perlin이 원래 그의 noise function을 개발할 때, 그는 또안 이 function을 building block으로 사용하여 흥미로운 solid texture를 생성하는 몇 가지 간단한 알고리즘을 제안했었다. turbulence같은 이러한 함수들 중 몇몇은 여전히 널리 사용된다. 그는 대부분의 이러한 패턴들과 좀 더 많은 것들을 "An Image Synthesizer"라고 이름 붙은 seminar paper에서 설명했었다. 그는 이것을 1985년 Siggraph에서 발표했다. 이 논문은 인터넷에서 쉽게 발견될 수 있으며, 우리는 너가 그것을 읽어보라고 매우 추천한다.
우리가 이전에 말했듯이, 그 noise function의 우리의 버전은 가장 흥미롭게 보이는 noise를 만들지 않는다 (지금 꽤 blocky하게 보인다). 이 강의의 목적은 좋게 보이는 noise를 만드는게 아니라 그 기법이 어떻게 작동하고, 그 함수의 특성을 이해하는 것이다. noise function의 더 좋게 보이는 버전에 대해서 배우기 위해서 두 번째 Noise Part 2를 확인해라 (1985년 Ken Perlin에 의해 제안된 원래 기법인 gradient noise라고 불려진다). 그러나, 그것은 우리가 너에게 몇 가지 예쩨를 보여주는 것을 막지 않을 것이다. 특히 2D noise인데, 우리는 몇 가지 좀 더 복잡한 이미지를 만드는 것을 시작하기 위해 사용할 수 있다. 우리는 너가 결과에 대해 그것들이 갖는 효과를 이해하기위해 코드에서 파라미터들을 바꾸는 것을 추천한다. 다음의 챕터 각각에서, 너는 그 알고리즘의 설명, 최종 패턴, 그리고 이 이미지를 생성하는데 사용되는 코드들을 볼 것이다.
1D Noise Examples
우리가 설명할 첫 번째 예제들 중 하나는 모든 차원에서 잘 작동한다. 그러나, 우리는 1D case로 시작할 것이고, 2D noise에 대한 예쩨를 나중에 줄 것이다. 이 기법의 3D noise example에 대해서는 두 번째 lesson을 보아라. 그것 뒤에 있는 idea는 몇 가지 noises들의 contribution을 합하는 것이다 (각 layer는 종종 CG literature에서 octave라고 불리지만, 가능하다면 이 용어를 피하자). 우리는 이러한 다양한 noise layers들의 (octaves) 파라미터들을 다양하게 할 수 있다 - 예를들어, 그것들의 frequency와 amplitude를 바꿔보자 - coherent(일관성있는) manner(방식)로. 다시 말해서, 우리는 frequency의 변화와 layer마다의 amplitude사이의 연결을 구성할 수 있다.
- 우리는 base frequency와 unit amplitude (1)로 시작할 것이고, 이러한 값들로 noise의 첫 번째 layer를 연산할 것이다.
- 그러고나서 우리는 noise의 second layer를 더할 것이지만, 우리는 this layer의 frequency와 amplitude에 각각 2와 0.5를 곱할 것이다 (그리고 이것은 amplitude 를 2로 나누는 것과 같다).
- the third layer에 대해, 우리는 frequency에 4를 곱하고 amplitude를 4로 나눌 것이다. Layer 4와 5는 그것들의 frequency와 amplitude에 8과 16을 곱하고, 8과 16으로 각각 나눌 것이다.
만약 너가 아직 관계를 찾지 못했다면, 각 layer는 이전 layer로부터 frequency가 두 배가 되고, amplitude는 절반이 된다. 만약 너가 그 방식으로 noise의 연속을 구성하고, 함께 합친다면, 너는 그림 1과 같은 결과를 얻을 것이다 (bottom image). 너가 볼 수 있듯이, 이 curve는 single noise보다 더 풍분하 profile을 가지고 있다. 시각적으로 그것은 mountain chain의 profile처럼 보인다 (너는 octaves or layers의 개념을 더 작은 frequency의 디테일들을 terrain에 더하는 것으로 볼 수 있다 - 산의 높이가 배열의 미세한 세부사항을 구성하는 돌의 높이보다 더 크기 때문에, frequency가 증가할 때 noise layers들의 amplitude를 줄이는 것은 합리적인 것처럼 보인다). 그리고 우리는 이것을 실수로 언급하지 않았다. 우리가 이 기법의 2D 대응물로서 보여주게 되듯이, 우리는 이 결과를 mesh를 displace시키는데 사용할 수 있고, 산 처럼 생긴 어떤 것을 만들 수 있따. 이 강의 첫 챕터에서 noise에 대해 말했던 것을 기억해라. 산 또는 바다의 파도 같은 자연에서 발견되는 많은 자연스러운 모양들을 재현하는데 그것이 사용될 수 있다. 이것은 한 예제이다.
우리가 용어를 좀 더 보기 전에, 이 예제에서, 특정한 layer의 frequency와 amplitude가 2의 제곱꼴에 의해 주도된다고 말하자. 만약 우리가 그 아이디어를 간단히 한다면, 우리는 슈도 코드에서 설명한 아이디어를 formalize한다면, 우리는 다음을 쓸 수 있다:
{
graph 형식으로 나타내기 위해, index 접근을 위해 numLayers가 5일 때 noiseSum의 최대값은 1.9~~ 이기 때문에 2.f으로 나누어서 0 ~ 1로 다시 스케일 다운 해주었다.
그리고 결과물들
그래프의 곡선이 잘 보이지 않을 수도 있기 때문에 noise array number를 16에서 256까지 늘려나갔다
기본 width : 2048, height : 512
- noise array number = 16
- noise array number = 64
- noise array number = 256
}
너가 질문할지도 모르는 질문들 중 하나는 내가 이 패턴을 만들기 위해 얼마나 많은 layers들을 사용해야 하는지이다. 우리가 간단히 들여다 볼 기술적 답이 있다. 입력 값의 frequency가 정말 매우 높을 때, noise function의 결과가 또 다시 우리가 피하고 싶은 white noise가 될 가능성이 높다.
너는 너가 input value의 frequency가 noise function의 결과를 white noise로 바꾸는 지점까지 너가 원하는 만큼 많은 layers들을 합칠 수 있다. 그리고 이것은 또 다른 질문을 만들어낸다: 그것이 언제 발생하는지를 아는가? 이론적으로, 알 수 있다. 우리는 이것이 언제 발생하는지를 발견할 수 있다. 이것은 복잡하고 큰 topic인 aliasing의 규칙에 의해 지배받는다. 너는 aliasing과 filtering (Basic section)의 강의에서 그리고 Noise Part 2에서 filtering noise에 대한 기본적인 개론에서 이것에 대한 설명을 볼 것이다.
그것은 이론이다. 실제로 (우리가 영화를 만들기 위해 이 기법을 사용할 때), 우리는 결코 3 or 5 이상의 layers를 사용하지 않는다 (가끔씩 7까지). 그것에 대한 여러 이유들이 있다. 첫 번째로, noise를 연산하는 것은 비싸다. 그래서 너가 더 많은 layers를 쓸 수록, 연산하는데 더 시간이 들게 된다. 다른 주된 이유는 7개의 layers들 중 5개 이상을 더 한 후의 layers들은 크게 그 결과에 기여하지 않을지도 모른다. 왜냐하면 이러한 layers들의 amplitude는 그 단계에서 매우 작다. 왜 우리가 5 layers 이상을 써야 할까? 만약 더 많은 layers가 어떤 큰 차이를 만들지 않는다면? 그리고 여전히 연산하기 비싸면서 (만약 너가 실제로 noise pattern에 zoom하지 않는다면)?
이상적으로 너는 이론과 실제 사이의 타협을 발견하고 싶다. 완벽한 코드는 layers를 합하고, 그것이 next layer를 위한 input value의 frequency가 white noise를 발견할 때 자동으로 멈추는 것이다 (white noise로 넘어가기전에 layers들의 숫자를 cut off하는 function의 예제에 대해 aliasing 강의를 보아라). 한 automatic system은 여전히 매우 많은 수의 layers를 이끌 수 있고, 그것은 매우 비싸다. 사용자가 기본적으로 3, 5 or 7로 기본적으로 설정된 input parameter로 사용될 layers의 maximum number를 조절하게 하는 것이 좋을 것이다.
Terminology
이제 몇 가지 용어들을 봐보자. frequency와 amplitude가 서로에게 관계되어있는 noise의 layers들을 합하는 기법은 fractal sum이라고 불릴 수 있다. 사실 이 기법을 사용하여 fractal curves (or surfaces)를 만드는 것은 CG 분야에 제한되지 않는다. 너는 그것이 어떤 주기 동안 stock market의 evolution을 보여주는 곡선과 유사하게 보인다는 것을 눈치챘을지도 모른다. 이러한 곡선들과 그것들의 수학적 표현은 Benoit Mandelbrot에 의해 연구되었다. 그는 fractal patterns에 대한 그의 연구로 잘 알려진 수학자이다 (특히 finance 분야에 적용되는). 우리는 우리 자신을 여기에 몇 가지 기술적인 용어의 개론에 제한할 것것이지만, 이 주제에 관심이 있는 독자들은 fractal 강의를 읽을 수 있따 (그것은 그 자체로 꽤 크고 매력적인 주제이다). 실제 세계, 배경, seascapes, 구름, 식물들, 주식 시장의 evolution을 포함한 그런 것들로부터 많은 것은 fractal form을 가진다.
fractal noise의 연속적인 layers들이 그것들의 frequency에 역으로 비례하는 amplitude를 가질 때, 그 결과는 설명하는데 사용되는 용어는 pink noise이다. 만약 우리가 이것을 한 공식으로 formalize한다면, 우리는 다음을 쓸 수 있다.
{
int main() { const int width = 2048; const int height = 512; float* noiseMap = new float[width * height]; /*** 2D example ***/ /* // generate value noise ValueNoise2D myNoise; float frequency = 0.05f; for (unsigned j = 0; j < height; ++j) for (unsigned i = 0; i < width; ++i) //generate a float in the range [0:1] noiseMap[j * width + i] = myNoise.eval(glm::vec2(i, j) * frequency); savePNGfile(noiseMap, width, height, "cosineNoise.png"); */ /*** 2D example ***/ /*** 1D example ***/ ValueNoise1D myNoise; memset(noiseMap, 0, sizeof(float) * width * height); static const unsigned numLayers = 5; float amplitude; float frequency; float noiseSum; static const int numSteps = 512; amplitude = 1.f; float limitMax = 0.f; for (int i = 0; i < numLayers; ++i) { limitMax += amplitude; amplitude *= 0.5f; } for(int i = 0; i < numSteps; ++i) { amplitude = 1; frequency = 1; noiseSum = 0; for (unsigned k = 0; k < numLayers; ++k) { float x = i / float(numSteps - 1) * myNoise.kMaxVertices; noiseSum += myNoise.eval(x * frequency) * amplitude; amplitude *= 0.5f; frequency *= 2.f; } // The max value of noiseSum is less than 2, 1 + 0.5 + 0.25 + .. = 1.923.. // For the purpose of representing the graph, all of the values are scaled down to the range [0:1] again noiseSum /= limitMax; int widthIndex = width / numSteps * i; int heightIndex = noiseSum * height; noiseMap[widthIndex + (height - heightIndex) * width] = 1.0; } savePNGfile(noiseMap, width, height, "64Array.png"); /*** 1D example ***/ delete[] noiseMap; return 0; }
그리고 결과물들
그래프의 곡선이 잘 보이지 않을 수도 있기 때문에 noise array number를 16에서 256까지 늘려나갔다
기본 width : 2048, height : 512
- noise array number = 16
- noise array number = 64
- noise array number = 256
}
너가 질문할지도 모르는 질문들 중 하나는 내가 이 패턴을 만들기 위해 얼마나 많은 layers들을 사용해야 하는지이다. 우리가 간단히 들여다 볼 기술적 답이 있다. 입력 값의 frequency가 정말 매우 높을 때, noise function의 결과가 또 다시 우리가 피하고 싶은 white noise가 될 가능성이 높다.
너는 너가 input value의 frequency가 noise function의 결과를 white noise로 바꾸는 지점까지 너가 원하는 만큼 많은 layers들을 합칠 수 있다. 그리고 이것은 또 다른 질문을 만들어낸다: 그것이 언제 발생하는지를 아는가? 이론적으로, 알 수 있다. 우리는 이것이 언제 발생하는지를 발견할 수 있다. 이것은 복잡하고 큰 topic인 aliasing의 규칙에 의해 지배받는다. 너는 aliasing과 filtering (Basic section)의 강의에서 그리고 Noise Part 2에서 filtering noise에 대한 기본적인 개론에서 이것에 대한 설명을 볼 것이다.
그것은 이론이다. 실제로 (우리가 영화를 만들기 위해 이 기법을 사용할 때), 우리는 결코 3 or 5 이상의 layers를 사용하지 않는다 (가끔씩 7까지). 그것에 대한 여러 이유들이 있다. 첫 번째로, noise를 연산하는 것은 비싸다. 그래서 너가 더 많은 layers를 쓸 수록, 연산하는데 더 시간이 들게 된다. 다른 주된 이유는 7개의 layers들 중 5개 이상을 더 한 후의 layers들은 크게 그 결과에 기여하지 않을지도 모른다. 왜냐하면 이러한 layers들의 amplitude는 그 단계에서 매우 작다. 왜 우리가 5 layers 이상을 써야 할까? 만약 더 많은 layers가 어떤 큰 차이를 만들지 않는다면? 그리고 여전히 연산하기 비싸면서 (만약 너가 실제로 noise pattern에 zoom하지 않는다면)?
이상적으로 너는 이론과 실제 사이의 타협을 발견하고 싶다. 완벽한 코드는 layers를 합하고, 그것이 next layer를 위한 input value의 frequency가 white noise를 발견할 때 자동으로 멈추는 것이다 (white noise로 넘어가기전에 layers들의 숫자를 cut off하는 function의 예제에 대해 aliasing 강의를 보아라). 한 automatic system은 여전히 매우 많은 수의 layers를 이끌 수 있고, 그것은 매우 비싸다. 사용자가 기본적으로 3, 5 or 7로 기본적으로 설정된 input parameter로 사용될 layers의 maximum number를 조절하게 하는 것이 좋을 것이다.
Terminology
이제 몇 가지 용어들을 봐보자. frequency와 amplitude가 서로에게 관계되어있는 noise의 layers들을 합하는 기법은 fractal sum이라고 불릴 수 있다. 사실 이 기법을 사용하여 fractal curves (or surfaces)를 만드는 것은 CG 분야에 제한되지 않는다. 너는 그것이 어떤 주기 동안 stock market의 evolution을 보여주는 곡선과 유사하게 보인다는 것을 눈치챘을지도 모른다. 이러한 곡선들과 그것들의 수학적 표현은 Benoit Mandelbrot에 의해 연구되었다. 그는 fractal patterns에 대한 그의 연구로 잘 알려진 수학자이다 (특히 finance 분야에 적용되는). 우리는 우리 자신을 여기에 몇 가지 기술적인 용어의 개론에 제한할 것것이지만, 이 주제에 관심이 있는 독자들은 fractal 강의를 읽을 수 있따 (그것은 그 자체로 꽤 크고 매력적인 주제이다). 실제 세계, 배경, seascapes, 구름, 식물들, 주식 시장의 evolution을 포함한 그런 것들로부터 많은 것은 fractal form을 가진다.
fractal noise의 연속적인 layers들이 그것들의 frequency에 역으로 비례하는 amplitude를 가질 때, 그 결과는 설명하는데 사용되는 용어는 pink noise이다. 만약 우리가 이것을 한 공식으로 formalize한다면, 우리는 다음을 쓸 수 있다.
float pinkNoise = 0; static const unsigned numLayer = 5; float rateOfChange = 2.0f; for(unsigned i = 0; i < numLayers; ++i) { // change in frequency and amplitude pinkNoise += noise(P * pow(rateOfChange, i)) / pow(rateOfChange, i); }
좀 더 구체적으로 해서, 연속적인 layers 사이의 frequency와 amplitude의 변화는 result noise curve의 signature(특징)를 형성한다. 그것은 그것의 spectral properties를 정의한다. 우리는 spectral densities 용어를 최종 noise가 구성되는 다양한 frequencies (layers)를 정의하기 위해 사용한다. 그리고 이러한 layers들의 각각은 우리가 power spectra라고 부르는 특정한 amplitude를 갖는다. Amplitude와 frequency는 pink noise의 케이스처럼 서로와 연관이 있을 수도 아닐 수도 있다. 너는 또한 연속적인 layers들 사이의 frequency의 변화에서 관계를 가질 수 있다. 이러한 관찰들은 컴퓨터 그래픽스에서 많은 패턴을 만드는데 중요하고, 우리는 pattern creation에 대한 다른 강의를 읽기전에 이것을 잘 이해하기를 추천한다.
우리는 frequencies가 서로 연관되어 있는 연속된 basic noises를 합하는 것으로 부터 복잡한 noise function (fractal sum)을 만들 수 있다. 한 layer의 frequency와 그것의 amplitude는 또한 연결된다. 만약 우리가 그것을 할 수 있따면, 그것은 reverse process가 또한 가능할지도 모른다는 것을 의미한다. 이것은 두 연속되는 layers 사이의 frequency ratio가 무엇인지 알기 위해 우리가 fractal인 것 처럼 보이는 frequency를 받아서, 그것을 더 간단한 noise functions으로 분해할 수 있다는 것이다 (즉, 그 신호가 구성되어 있는 다른 frequencies가 무엇이고, 그리고 존재한다면 이러한 frequencies 사이의 관계가 무엇인지). 또한 각 layer에 대해 frequency-amplitude 비율을 알아내기 위해서. 그렇게 하는 것은 그 신호의 spectral properties를 만드는 것을 가는ㅇ하게 한다. 이 관찰은 특히 ocean surfaces를 재현하는데 사용되는 알고리즘 개발에서 중요하다. 만약 우리가 자연에서 어떤 패턴들의 spectral densities를 안다면, 그러면 우리는 그럴듯하게 컴퓨터로 그것들을 다시 만들 수 있고 비슷한 패턴을 얻을 수 있다 (이 프로세스는 acoustic(소리)에서 spectral modelling synthesis의 개념과 유사하다). 이것은 적어도 80년대 중반 이후부터 CG에서 널리 사용되온 멋진 관찰이다.
이름상 noise라는 단어의 사용 (pink noise)는 조금 잘못된 거일지도 모른다. 왜냐하면 그것은 상호 연관된 frequencies와 amplitudes와 함께 noise functions의 합계를 말하기 때문이다. octave라는 용어는 가끔씩 layer 단어 부분에서 (잘못) 사용된다. layer라는 용어는 octave보다 좀 더 generic한데, 이것은 음악에서 또한 사용된다. octave는 doubling or 한 frequency를 갖는 것이다. 만약 한 프로그램에서 (또는 책에서) 그것이 사용된다면, fractal sum의 연산에서 그것은 각 연속되는 layer가 이전 layer의 frequency가 두 배라는 것을 의미한다. 그것은 우리의 방젖ㅇ식에서 rate의 변화가 2를 취하는 것을 의미한다. 만약 연속되는 layers사이의 frequency가 2와 다르다면, 이 term의 사용은 부정확하고, layer가 대신 사용되어야 한다. 우리가 layers사이에 frequency를 두 배 하고, 이러한 layers들의 amplitude가 그것들의 freqeuncy와 반비례 할 때, 우리는 Brownian noise라고 부르는 특별한 종류의 pink noise를 얻는다 (수학자 Robert Brown의 이름을 딴).
컴퓨터 그래픽스에서, 너는 종종 fractal functions들이 fBm 이라고 불려지는 것을 볼 것이다 (그리고 이것은 fractional brownian motion을 나타낸다). CG 커뮤니티는 이러한 용어들을 수학 분야로 부터 빌려왔다, 주로 일반적으로 간단화된/더욱 간단한 형태로 이러한 기법들을 사용할 함수들의 일므을 붙이는 편리한 방식으로서. fBm 함수의 generic form에서, 한 layer의 amplitude는 그것의 frequency와 반비례할 필요는 없다. 너는 frequency와 amplitude가 layers사이에 어떻게 변하는지를 조절하기 위해 두 개의 다른 값을 쓸 수 있다. lacunarity라는 단어는 그 비율을 조절하는데 사용되는데, 그 비율에서, frequency는 layer마다 변한다. Lacunarity는 fractal 분야에서 특별한 의미를 갖는다 (더 많은 정보를 위해서 fractals 강의를 보아라). layer마다 amplitude에서 변화율에 대한 특별한 단어는 없다. 우리는 fBm function의 다음의 슈도 코드에서 gain을 사용할 것이다:
float fBm(Vec3f P, float lacunarity = 2, float gain = 0.5, int numLayers 5) { float noiseSum = 0; Vec3f Pnoise = P; float amplitude = 1; for(unsigned i = 0; i < numLayers; ++i) { // change in frequency and amplitude noiseSum += noise(Pnoise) * amplitude; Pnoise *= lacunarity; amplitude *= gain; } return noiseSum; }
우리는 1D noise에 대한 이러한 기본 개념들을 만들었으니, 같은 기법을 2D noise에 적용할 때 무슨일이 일어나는지 봐보자.
2D Noise Examples
우리의 프로그램이 사용하기에 더 쉽도록 하기 위해서, 우리는 처음에 한 이미지의 모든 픽셀들을 loop하고 그것으로 부터 2D position을 연산하는 간단한 generic function을 만들 것이다. 그 픽셀 컬러는 그 점에 대한 우리의 noise function의 결과로 정해진다. 이 함수는 template인데, noise function을 인자로 받는다. 그 noise function은 우리가 다양한 패턴을 연산할 코드를 구현할 곳이다 (완전한 예제를 위해 source code를 다운받아라).
Simple 2D Noise
우리의 첫 예제는 간단하다. 우리의 프로그램의 사용을 보여주고 우리의 noise function을 테스트 하기 위해, 우리는 처음에 간단한 noise image를 만들어낸다.
{
int main() { const int width = 512; const int height = 512; float* noiseMap = new float[width * height]; /*** 2D example ***/ ValueNoise2D myNoise; /*** 2D example ***/ unsigned numLayers = 5; float noiseFrequency; float noiseAmplitude; float noiseSum; float maxNoiseValue = 0.f; for (unsigned j = 0; j < height; ++j) { for (unsigned i = 0; i < width; ++i) { noiseSum = 0.f; noiseFrequency = 0.05f; noiseAmplitude = 1.f; for (unsigned k = 0; k < numLayers; ++k) { noiseSum += myNoise.eval(glm::vec2(i, j) * noiseFrequency) * noiseAmplitude; noiseFrequency *= 2.f; noiseAmplitude *= 0.5f; } noiseMap[i + j * width] = noiseSum; if (noiseSum > maxNoiseValue) maxNoiseValue = noiseSum; } } for (unsigned i = 0; i < width * height; ++i) noiseMap[i] /= maxNoiseValue; savePNGfile(noiseMap, width, height, "simple2DLayerTest.png"); delete[] noiseMap; return 0; }
위와 같이 설정했고, 다음의 이미지를 얻었다.
확실히 Layer가 들어가니 더 blur가 되었다.
}
Fractal Sum
우리의 두 번째 예제는 우리가 1D case에서 이미 설명했던 fractal sum의 데모이다. 우리는 noise의 5개 layers들의 contribution을 축적한다. 각 연속적인 layer 사이에, 우리는 그 점의 frequency를 이전 layer로 부터 2 곱하고, amplitude를 이전 layer의 2로 나눈다.
여기에 그 결과 이미지와 이 결과를 연산하는데 사용된 코드가 있다 (그 코드는 이미 조금 최적화되어 있따. 우리는 noise function의 frequency와 amplitude를 바꾸기 위해 pow(2,k) 함수를 사용할 수 있다. 그러나 이 함수는 꽤 느리고, 우리는 그것을 frequency (2)와 amplitude (0.5) 파라미터의 재귀 곱으로 대체할 수 있다.
{
사실 위의 코드와 이미지가 여기 섹션의 코드와 이미지이다.
}
우리가 noise의 몇 개의 layers들을 합쳤기 때문에, 그 결과가 1보다 클 수 있고, 이것은 우리가 pixel color로 변환할 때 문제가 될 것이다. 우리는 그 값을 pixel color로 변환할 때 clamp할 수 있지만, 더 좋은 솔루션은 noise values의 array를 array에서 모든 값을 maximum computed value로 나누어서 표준화 하는 것이다. 그렇게 하기 위해서, 우리는 maximum value를 저장한다. 우리가 noise map에서 모든 entry를 연산하기 떄문이다. 그러고나서 모든 값이 연산되면, 우리는 그것들을 다시 maxNoiseVal로 나눈다 (line 12).
이전에 언급되었듯이, fractal sum은 그럴듯한 테레인과 많은 다른 natural patterns들을 만드는데 사용될 수 있다 (seascapes, landscapes, 등등). 우리는 쉽게 2D texture를 만들 수 있고, mesh를 displace하기 위해 그것을 사용할 수 있다 (그림 3). 더 세부사항은 texture synthesis, terrain generation and ocean surfaces의 모델링 강의에서 찾아질 수 있다.
코드에서, 너는 frequency와 amplitude에 대한 multiplier를 바꾸어 실험할 수 있고, 이것은 너의 fractal noise function을 좀 더 이전에 설명한 generic fBm function으로 바꾸게 한다.
많은 다른 looks는 lacunarity와 gain에 대한 그 값을 다르게 하여 성취될 수 있다. 그림 4에 보여지듯이
{
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | int main() { const int width = 512; const int height = 512; float* noiseMap = new float[width * height]; /*** 2D example ***/ ValueNoise2D myNoise(1234); /*** 2D example ***/ unsigned numLayers = 5; float noiseFrequency; float frequencyMult = 1.8; float amplitudeMult = 0.35; float noiseAmplitude; float noiseSum; float maxNoiseValue = 0.f; for (unsigned j = 0; j < height; ++j) { for (unsigned i = 0; i < width; ++i) { noiseSum = 0.f; noiseFrequency = 0.02f; noiseAmplitude = 1.f; for (unsigned k = 0; k < numLayers; ++k) { noiseSum += myNoise.eval(glm::vec2(i, j) * noiseFrequency) * noiseAmplitude; noiseFrequency *= frequencyMult; noiseAmplitude *= amplitudeMult; } noiseMap[i + j * width] = noiseSum; if (noiseSum > maxNoiseValue) maxNoiseValue = noiseSum; } } for (unsigned i = 0; i < width * height; ++i) noiseMap[i] /= maxNoiseValue; savePNGfile(noiseMap, width, height, "simple2DLayerTest.png"); delete[] noiseMap; return 0; } |
}
Turbulence
Turbulence는 fractal sum과 같은 원리로 만들어지는 함수이다. 그러나, 각 layer에 대한 noise function을 직접적으로 사용하는 대신에, 우리는 signed noise의 절대값을 사용할 것이다. 우리는 처음에 noise의 결과를 signed noise로 변환할 것이고, 그러고나서 이 결과의 절대값을 취할 것이다. 너가 다음의 그림에서 볼 수 있듯이, noise function을 그러한 방식으로 처리하는 것은 bumps로 만들어진 것 같은 profile을 만든다. 그 곡선이 음수인 곳에서 마다 (black line), 우리는 곡선을 x 축을 따라 이러한 지역에 반사시킬 것이다. 그 red line 곡선은 그 결과이다. 2D noise로 이 기법을 사용하는 것은 fire, smoke or clouds를 재현하는데 적절한 패턴을 만들 수 있다.
{
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | for (unsigned j = 0; j < height; ++j) { for (unsigned i = 0; i < width; ++i) { noiseSum = 0.f; noiseFrequency = 0.02f; noiseAmplitude = 1.f; for (unsigned k = 0; k < numLayers; ++k) { noiseSum += std::fabs((myNoise.eval(glm::vec2(i, j) * noiseFrequency)) * 2 - 1) * noiseAmplitude; noiseFrequency *= frequencyMult; noiseAmplitude *= amplitudeMult; } noiseMap[i + j * width] = noiseSum; if (noiseSum > maxNoiseValue) maxNoiseValue = noiseSum; } } |
}
Marble Texture
marble texture는 fractal sum의 noise function으로 sine pattern의 phase를 조절하여 만들어질 수 있다. 여기에서 그 아이디어는 그 pattern을 만들기 위해 noise funciton을 직접적으로 사용하는 것이 아니라, 패턴을 만들기 위해서 그 함수를 perturb(동요)시키는 것이다. 그 경우에, 우리는 fractal sum으로 sine function의 phase를 perturb or shift시킨다. 이 아이디어는 어떤 주기적인 또는 규칙적인 함수에서 randomness를 도입하는데 사용될 수 있다. 우리의 예제는 꽤 간단하고, 오직 black and white marble texture를 그리는 것이다. 그러나, 혼합해서 몇 가지 컬러를 도입하여, 좀 더 현실적인 패턴을 만드는 것이 가능하다. texture synthesis 강의는 좀 더 정교한 예제들을 포함한다.
{
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | int main() { const int width = 512; const int height = 512; float* noiseMap = new float[width * height]; /*** 2D example ***/ ValueNoise2D myNoise; /*** 2D example ***/ unsigned numLayers = 5; float noiseFrequency; float frequencyMult = 1.8; float amplitudeMult = 0.35; float noiseAmplitude; float noiseSum; float maxNoiseValue = 0.f; for (unsigned j = 0; j < height; ++j) { for (unsigned i = 0; i < width; ++i) { noiseSum = 0.f; noiseFrequency = 0.02f; noiseAmplitude = 1.f; for (unsigned k = 0; k < numLayers; ++k) { noiseSum += myNoise.eval(glm::vec2(i, j) * noiseFrequency) * noiseAmplitude; noiseFrequency *= frequencyMult; noiseAmplitude *= amplitudeMult; } noiseSum = (sin((i + noiseSum * 100) * 2 * PI / 200.f) + 1) / 2.f; noiseMap[i + j * width] = noiseSum; // if (noiseSum > maxNoiseValue) maxNoiseValue = noiseSum; } } // for (unsigned i = 0; i < width * height; ++i) noiseMap[i] /= maxNoiseValue; savePNGfile(noiseMap, width, height, "simple2DLayerTest.png"); delete[] noiseMap; return 0; } |
졸라 신기하다...
}
Wood Texture
marble texture처럼, wood texture는 매우 간단한 trick에 의존한다. 이 아이디어는 noise function에 1보다 더 큰 값을 곱하는 것이다. 이 곱의 결과를 g라고 부르자 (역사적으로 wood grain에 대한 reference에서 그것을 g라고 부른다). 그 텍스쳐는 g를 그것의 정수 부분으로 뺄셈해서 얻어진다. positive float를 integer로 캐스팅하는 것은 g와 같거나 더 작은 정수를 만들 것이다. 그 뺄셈의 결과는 그러므로 [0:1]의 범위에 있다. 그림 6은 그 프로세스를 보여준다. 이 예제에서, 우리는 noise function에 4를 곱해준다. 그 blue curve는 value g를 나타내고, 반면에 red curve는 g를 그것의 정수 부분으로 뺀 결과를 나타낸다. 그 noise function 을 4보다 큰 값으로 곱하는 것은 red curve에서 좀 더 큰 breakups를 만들어 낼 것이다. 2D에서, 이러한 breakups는 더 밝고 어두운 컬러 영역 사이의 경계를 나타낸다 (아래의 이미지를 보아라).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | for (unsigned j = 0; j < height; ++j) { for (unsigned i = 0; i < width; ++i) { noiseSum = 0.f; noiseFrequency = 0.02f; noiseSum += myNoise.eval(glm::vec2(i, j) * noiseFrequency) * 10; noiseMap[i + j * width] = noiseSum - (int)noiseSum; // if (noiseSum > maxNoiseValue) maxNoiseValue = noiseSum; } } |
댓글 없음:
댓글 쓰기