'알고리즘 & 자료구조 > 3D알고리즘&자료구조' 카테고리의 다른 글
컴포넌트 기반 게임 설계 (0) | 2012.10.27 |
---|---|
water shader ! 물효과! - 수면반사방법 (0) | 2012.10.27 |
선형적 샘플링으로 효율적인 가우시안 블러 (0) | 2012.10.27 |
펄린노이즈 2탄 (Perlin noise) (0) | 2012.10.27 |
Hypercube Vertices (0) | 2012.10.27 |
컴포넌트 기반 게임 설계 (0) | 2012.10.27 |
---|---|
water shader ! 물효과! - 수면반사방법 (0) | 2012.10.27 |
선형적 샘플링으로 효율적인 가우시안 블러 (0) | 2012.10.27 |
펄린노이즈 2탄 (Perlin noise) (0) | 2012.10.27 |
Hypercube Vertices (0) | 2012.10.27 |
http://www.kocw.net/home/common/contents/document/04_ImageProcessing.pdf
위 주소에 있는 내용임
47 페이지를 보면 된다
x,y 중심으로 컨볼루션 필터배열과 매칭되는 이미지 픽셀을 곱해서 다 더 하면서 모든 이미지 영역을 처리한 것
그래서 가우시안 필터 연산시에 가우스 함수의 분포를 필터에 배치 시켜서 이것을 컨볼루션 하면 스무딩된 이미지가 나온다
힙(Heap) 트리와 정렬 (우선순위 큐) (0) | 2012.10.31 |
---|---|
정렬 알고리즘 - 힙 정렬(Heap Sort) (0) | 2012.10.31 |
해쉬, 해쉬테이블, 정적해쉬, 동적 해쉬 (0) | 2012.10.31 |
버튼입력 이벤트처리 (0) | 2012.10.31 |
3.5 AI 에이전트, 객체, 퀘스트를 위한 확장성있는 트리거 시스템 (0) | 2012.10.27 |
Gaussian blur is an image space effect that is used to create a softly blurred version of the original image. This image then can be used by more sophisticated algorithms to produce effects like bloom, depth-of-field, heat haze or fuzzy glass. In this article I will present how to take advantage of the various properties of the Gaussian filter to create an efficient implementation as well as a technique that can greatly improve the performance of a naive Gaussian blur filter implementation by taking advantage of bilinear texture filtering to reduce the number of necessary texture lookups. While the article focuses on the Gaussian blur filter, most of the principles presented are valid for most convolution filters used in real-time graphics.
Gaussian blur is a widely used technique in the domain of computer graphics and many rendering techniques rely on it in order to produce convincing photorealistic effects, no matter if we talk about an offline renderer or a game engine. Since the advent of configurable fragment processing through texture combiners and then using fragment shaders the use of Gaussian blur or some other blur filter is almost a must for every rendering engine. While the basic convolution filter algorithm is a rather expensive one, there are a lot of neat techniques that can drastically reduce the computational cost of it, making it available for real-time rendering even on pretty outdated hardware. This article will be most like a tutorial article that tries to present most of the available optimization techniques. Some of them may be familiar to all of you but maybe the linear sampling will bring you some surprise, but let’s not go that far but start with the basics.
In order to precede any possibility of confusion, I’ll start the article with the introduction of some terms and concepts that I will use in the post.
Convolution filter – An algorithm that combines the color value of a group of pixels.
NxN-tap filter – A filter that uses a square shaped footprint of pixels with the square’s side length being N pixels.
N-tap filter – A filter that uses an N-pixel footprint. Note that an N-tap filter does *not* necessarily mean that the filter has to sample N texels as we will see that an N-tap filter can be implemented using less than N texel fetches.
Filter kernel – A collection of relative coordinates and weights that are used to combine the pixel footprint of the filter.
Discrete sampling – Texture sampling method when we fetch the data of exactly one texel (aka GL_NEAREST filtering).
Linear sampling – Texture sampling method when we fetch a footprint of 2×2 texels and we apply a bilinear filter to aquire the final color information (aka GL_LINEAR filtering).
The image space Gaussian filter is an NxN-tap convolution filter that weights the pixels inside of its footprint based on the Gaussian function:
The pixels of the filter footprint are weighted using the values got from the Gaussian function thus providing a blur effect. The spacial representation of the Gaussian filter, sometimes referred to as the “bell surface”, demonstrates how much the individual pixels of the footprint contribute to the final pixel color.
Based on this some of you may already say “aha, so we simply need to do NxN texture fetches and weight them together and voilà”. While this is true, it is not that efficient as it looks like. In case of a 1024×1024 image, using a fragment shader that implements a 33×33-tap Gaussian filter based on this approach would need an enormous number of 1024*1024*33*33 ≈ 1.14 billion texture fetches in order to apply the blur filter for the whole image.
In order to get to a more efficient algorithm we have to analyze a bit some of the nice properties of the Gaussian function:
Both of these properties of the Gaussian function give us room for heavy optimization.
Based on the first property, we can separate our 2-dimensional Gaussian function into two 1-dimensional one. In case of the fragment shader implementation this means that we can separate our Gaussian filter into a horizontal blur filter and the vertical blur filter, still getting the accurate results after the rendering. This results in two N-tap filters and an additional rendering pass needed for the second filter. Getting back to our example, applying the two filters to a 1024×1024 image using two 33-tap Gaussian filters will get us to 1024*1024*33*2 ≈ 69 million texture fetches. That is already more than an order of magnitude less than the original approach made possible.
Using the second property of the Gaussian function, we can separate our 33×33-tap filter into three 9×9-tap filter (9+8=17, 17+16=33). Back to our example, for the 1024×1024 sized image this results in 1024*1024*9*9*3 ≈ 255 million texture fetches. As we can see, we also spared a large amount of the necessary texture fetches using this approach as well.
Of course, the combination of the two techniques is also possible. That means we both separate our filter to a vertical and horizontal filter as well as decompose our 33-tap filter into three 9-tap filter. This will get us to the almost optimal number of 1024*1024*9*3*2 ≈ 56 million texture fetches.
We’ve seen how to implement an efficient Gaussian blur filter for our application, at least in theory, but we haven’t talked about how we should calculate the weights for each pixel we combine using the filter in order to get the proper results. The most straightforward way to determine the kernel weights is by simply calculating the value of the Gaussian function for various distribution and coordinate values. While this is the most generic solution, there is a simpler way to get some weights by using the binomial coefficients. Why we can do that? Because the Gaussian function is actually the distribution function of the normal distribution and the normal distribution’s discrete equivalent is the binomial distribution which uses the binomial coefficients for weighting its samples.
For implementing our 9-tap horizontal and vertical Gaussian filter we will use the last row of the Pascal triangle illustrated above in order to calculate our weights. One may ask why we don’t use the row with index 8 as it has 9 coefficients. This is a justifiable question, but it is rather easy to answer it. This is because with a typical 32 bit color buffer the outermost coefficients don’t have any effect on the final image while the second outermost ones have little to no effect. We would like to minimize the number of texture fetches but provide the highest quality blur as possible with our 9-tap filter. Obviously, in case very high precision results are a must and a higher precision color buffer is available, preferably a floating point one, using the row with index 8 is better. But let’s stick to our original idea and use the last row…
By having the necessary coefficients, it is very easy to calculate the weights that will be used to linearly interpolate our pixels. We just have to divide the coefficient by the sum of the coefficients that is 4096 in this case. Of course, for correcting the elimination of the four outermost coefficients, we shall reduce the sum to 4070, otherwise if we apply the filter several times the image may get darker.
Now, as we have our weights it is very straightforward to implement our fragment shaders. Let’s see how the vertical file shader will look like in GLSL:
01 |
uniform sampler2D image;
|
02 |
|
03 |
out vec4 FragmentColor;
|
04 |
|
05 |
uniform float offset[5] =
float [](
0.0, 1.0, 2.0, 3.0, 4.0 ); |
06 |
uniform float weight[5] =
float [](
0.2270270270, 0.1945945946, 0.1216216216,
|
07 |
0.0540540541, 0.0162162162 );
|
08 |
|
09 |
void main( void ) |
10 |
{
|
11 |
FragmentColor = texture2D( image, vec2(gl_FragCoord)/1024.0 )
* weight[0]; |
12 |
for ( int i=1; i<5; i++) {
|
13 |
FragmentColor += |
14 |
texture2D( image, ( vec2(gl_FragCoord)+vec2(0.0, offset[i])
)/1024.0 ) |
15 |
* weight[i]; |
16 |
FragmentColor += |
17 |
texture2D( image, ( vec2(gl_FragCoord)-vec2(0.0, offset[i])
)/1024.0 ) |
18 |
* weight[i]; |
19 |
}
|
20 |
} |
Obviously the horizontal filter is no different just the offset value is applied to the X component rather than to the Y component of the fragment coordinate. Note that we hardcoded here the size of the image as we divide the resulting window space coordinate by 1024. In a real life scenario one may replace that with a uniform or simply use texture rectangles that don’t use normalized texture coordinates.
If you have to apply the filter several times in order to get a more strong blur effect, the only thing you have to do is ping-pong between two framebuffers and apply the shaders to the result of the previous step.
So far, we were able to see how to implement a separable Gaussian filter using two rendering pass in order to get a 9-tap Gaussian blur. We’ve also seen that we can run this filter three times over a 1024×1024 sized image in order to get a 33-tap Gaussian blur by using only 56 million texture fetches. While this is already quite efficient it does not really expose any possibilities of the GPUs as this form of the algorithm would work perfectly almost unmodified on a CPU as well.
Now, we will see that we can take advantage of the fixed function hardware available on the GPU that can even further reduce the number of required texture fetches. In order to get to this optimization let’s discuss one of the assumptions that we made from the beginning of the article:
So far, we assumed that in order to get information about a single pixel we have to make a texture fetch, that means for 9 pixels we need 9 texture fetches. While this is true in case of a CPU implementation, it is not necessarily true in case of a GPU implementation. This is because in the GPU case we have bilinear texture filtering at our disposal that comes with practically no cost. That means if we don’t fetch at texel center positions our texture then we can get information about multiple pixels. As we already use the separability property of the Gaussian function we actually working in 1D so for us bilinear filter will provide information about two pixels. The amount of how much each texel contribute to the final color value is based on the coordinate that we use.
By properly adjusting the texture coordinate offsets we can get the accurate information of two texels or pixels using a single texture fetch. That means for implementing a 9-tap horizontal/vertical Gaussian filter we need only 5 texture fetches. In general, for an N-tap filter we need [N/2] texture fetches.
What this will mean for our weight values previously used for the discrete sampled Gaussian filter? It means that each case we use a single texture fetch to get information about two texels we have to weight the color value retrieved by the sum of the weights corresponding to the two texels. Now that we know what are our weights, we just have to calculate the texture coordinate offsets properly.
For texture coordinates, we can simply use the middle coordinate between the two texel centers. While this is a good approximation, we won’t accept it as we can calculate much better coordinates that will result us exactly the same values as when we used discrete sampling.
In case of such a merge of two texels we have to adjust the coordinates that the distance of the determined coordinate from the texel #1 center should be equal to the weight of texel #2 divided by the sum of the two weights. In the same style, the distance of the determined coordinate from the texel #2 center should be equal to the weight of texel #1 divided by the sum of the two weights.
As a result, we get the following formulas to determine the weights and offsets for our linear sampled Gaussian blur filter:
By using this information we just have to replace our uniform constants and decrease the number of iterations in our vertical filter shader and we get the following:
01 |
uniform sampler2D image;
|
02 |
|
03 |
out vec4 FragmentColor;
|
04 |
|
05 |
uniform float offset[3] =
float [](
0.0, 1.3846153846, 3.2307692308 ); |
06 |
uniform float weight[3] =
float [](
0.2270270270, 0.3162162162, 0.0702702703 );
|
07 |
|
08 |
void main( void ) |
09 |
{
|
10 |
FragmentColor = texture2D( image, vec2(gl_FragCoord)/1024.0 )
* weight[0]; |
11 |
for ( int i=1; i<3; i++) {
|
12 |
FragmentColor += |
13 |
texture2D( image, ( vec2(gl_FragCoord)+vec2(0.0, offset[i])
)/1024.0 ) |
14 |
* weight[i]; |
15 |
FragmentColor += |
16 |
texture2D( image, ( vec2(gl_FragCoord)-vec2(0.0, offset[i])
)/1024.0 ) |
17 |
* weight[i]; |
18 |
}
|
19 |
} |
This simplification of the algorithm is mathematically correct and if we don’t consider possible rounding errors resulting from the hardware implementation of the bilinear filter we should get the exact same result with our linear sampling shader like in case of the discrete sampling one.
While the implementation of the linear sampling is pretty straightforward, it has a quite visible effect on the performance of the Gaussian blur filter. Taking into consideration that we managed to implement a 9-tap filter using just five texture fetches instead of nine, back to our example, blurring a 1024×1024 image with a 33-tap filter takes only 1024*1024*5*3*2 ≈ 31 million texture fetches instead of the 56 million required by discrete sampling. This is a quite reasonable difference and in order to better present how much that matters I’ve done some experiment to measure the difference between the two techniques. The result speaks for itself:
As we can see, the performance of the Gaussian filter implemented with linear sampling is about 60% faster than the one implemented with discrete sampling indifferent from the number of blur steps applied to the image. This roughly proportional to the number of texture fetches spared by using linear filtering.
We’ve seen that implementing an efficient Gaussian blur filter is quite straightforward and the result is a very fast real-time algorithm, especially using the linear sampling, that can be used as the basis of more advanced rendering techniques.
Even though we concentrated on Gaussian blur in this article, many of the discussed principles apply to most convolution filter types. Also, most of the theory applies in case we need a blurred image of reduced size like it is usually needed by the bloom effect, even the linear sampling. The only thing that is really different in case of a reduced size blurred image is that our center pixel is also a “double-pixel”. This means that we have to use a row from our Pascal triangle that has even number of coefficients as we would like to linear sample the middle texels as well.
We’ve also had a brief insight into the computational complexity of the various techniques and how the filter can be efficiently implemented on the GPU.
The demo application used for the measurements performed to compare the discrete and linear sampling method can be downloaded here:
Platform: Windows
Dependency: OpenGL 3.3
capable graphics driver
Download link: gaussian_win32.zip (2.96MB)
Language: C++
Platform:
cross-platform
Dependency: GLEW, SFML,
GLM
Download link: gaussian_src.zip
(5.37KB)
P.S.: Sorry for the high minimum requirements of the application just I would really like to stick to strict OpenGL 3+ demos.
Print article | This entry was posted by Daniel Rákos on September 7, 2010 at 8:48 pm, and is filed under Graphics, Programming, Samples. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |
water shader ! 물효과! - 수면반사방법 (0) | 2012.10.27 |
---|---|
3차 곡선을 이용한 다 구간 경로 기반의 컷씬 효과 (0) | 2012.10.27 |
펄린노이즈 2탄 (Perlin noise) (0) | 2012.10.27 |
Hypercube Vertices (0) | 2012.10.27 |
플랙탈 - Apophysis, ultra fractal 5 (0) | 2012.10.27 |
구글링하다 찾은 펄린노이즈
알려진 펄린노이즈의 방법은 전체적인 대강의 윤곽은 비슷 할 수 있찌만 내부적으로 세세히 조금씩 다른
방법들이 있는데 아래 필자가 쓴 방법이 좀더 빠른 연산을 보인다고 한다
tip : 일부 내적에 대한 부족한 설명을 '편집' 이라는 부분에 보충 설명을 해놓음
1차로 예를 들면..
펄린노이즈는 각 정수 단위 사이에들어오는 성분(x) 에 대해서 -1 ~1 사이의 난수 값을 생성해 낸다
각 구간마다 난수벡터를 만들어 놓고 성분(x) x 가 어느 정수 구간 1 거리 사이안에 들어 간다면
ex( 244 ~255) 이 구간 244 와 255 에 설정되어 있는 난수값과 244 와 255 에서 x 값을 향하는 벡터와의 각 내적 값들을
3차 방정식이 x 축이 되는 x ----- > 3차 --------> 선형 보간----------- > v0 ~ v1 의 값으로 보간 되는
즉 일반적으로 선형 보간 된 값이 v0 또는 v1 쪽으로 더 쏠리는 보간값의 형태가 되어
최종 결과 값은 v0 와 v1 의 중간 값보다는 v0 쪽이나 v1 쪽에 더 영향을 받는 난수 값을 얻게 된다
2차는 1차 사이의 보간을 통해
3차는 2차 사이의 보간을 통해 구한다
v. 1.0, last updated February 2001
So far, I have found two really great sources for information about Perlin noise. They are Ken Perlin's Making Noise web site, which has a comprehensive introduction to the topic, and Hugo Elias's page, which features some algorithms and a few more detailed examples of applications. This document is intended to complement those two valuable resources by explaining in more depth, hopefully in plain English, some of the math involved in Ken Perlin's original implementation of Perlin noise.
I wrote a demo program in C++ that includes a Perlin noise texture class, which I hope provides a decent example of C++ code for Perlin noise. You can find the demo at http://www.robo-murito.net/code.html.
If I've made any grave errors or you just want to comment, feel free to email me.
Perlin noise is function for generating coherent noise over a space. Coherent noise means that for any two points in the space, the value of the noise function changes smoothly as you move from one point to the other -- that is, there are no discontinuities.
When I talk about a noise function, I mean a function that takes a coordinate in some space and maps it to a real number between -1 and 1. Note that you can make noise functions for any arbitrary dimension. The noise functions above are 2-dimensional, but you can graph a 1-dimensional noise function just like you would graph any old function of one variable, or consider noise functions returning a real number for every point in a 3D space.
These images were all created using nothing but Perlin noise:
Besides these trivial images, there are countless interesting applications for noise in computer graphics. Look at Hugo Elias' page or Ken Perlin's pages for some pretty examples, in-depth explanations and nifty ideas.
I've seen implementations on the internet that take non-coherent noise, as shown above, and smooth it (with something like a blur function) so it becomes coherent noise, but that can end up being more computationally expensive than the function I'll present here, which is more or less theoriginal implementation that Ken Perlin came up with. The problem with Perlin's implementation on its own was that that reading the descriptions of the math published on Perlin's website was a bit like reading Greek -- it took me about a week of reading his code and notes to figure out the actual geometric interpretations of the math. So hopefully this document can trim down that learning curve.
Let's look at the 2D case, where we have the function
noise2d(x, y) = z,
with x, y, and z all real (floating-point) numbers. We'll define the noise function on a regular grid, where grid points are defined for each whole number, and any number with a fractional part (i.e. 3.14) lies between grid points. Consider finding the value of noise2d(x, y), with x and y both notwhole numbers -- that is, the point (x, y) lies somewhere in the middle of a square in our grid. The first thing we do is look at the gridpointssurrounding (x, y). In the 2D plane, we have 4 of them, which we will call (x0, y0), (x0, y1), (x1, y0), and (x1, y1).
Now we need a function
g(xgrid, ygrid) = (gx, gy)
which takes each grid point and assigns it a pseudorandom gradient of length 1 in R2 ('gradient' is just the name for an ordered pair or vector that we think of as pointing in a particular direction). By pseudorandom, we mean that g has the appearance of randomness, but with the important consideration that it always returns the same gradient for the same grid point, every time it's calculated. It's also important that every direction hasan equal chance of being picked.
Also, for each grid point, we generate a vector going from the grid point to (x, y), which is easily calculated by subtracting the grid point from (x, y).
Now we have enough to start calculating the value of the noise function at (x, y). What we'll do is calculate the influence of each pseudorandom gradient on the final output, and generate our output as a weighted average of those influences.
First of all, the influence of each gradient can be calculated by performing a dot product of the gradient and the vector going from its associated grid point to (x, y). A refresher on the dot product -- remember, it's just the sum of the products of all the components of two vectors, as in
(a, b) · (c, d) = ac + bd .
Since I'm really tired of writing subscripts, we'll just refer to these 4 values as s, t, u, and v, where
s = g(x0, y0) · ((x, y) - (x0, y0)) ,
t = g(x1, y0) · ((x, y) - (x1, y0)) ,
u = g(x0, y1) · ((x, y) - (x0, y1)) ,
v = g(x1, y1) · ((x, y) - (x1, y1)) .
So here's a 3D-ish picture of some influences s, t, u, and v coming out of the R2 plane (note that these probably wouldn't be the actual values generated by the dot products of the vectors pictured above).
Geometrically, the dot product is proportional to the cosine of the angle between two vectors
, though its unclear to me
------------------------------------------------------------------------------------------
편집 : 다른 곳에서 보니....
내적이 의미 하는게 사각 셀(사각형 안의 x,y 에 해당하는) 것의 4 귀퉁위의 각 높이값을
말하는 듯..
------------------------------------------------------------------------------------------
if that geometrical interpretation helps one visualize what's going on. The important thing to know at this point is that we have four numbers, s, t, u, and v which are uniquely determined by (x, y) and the pseudorandom gradient function g. Now we need a way to combine them to get noise2d(x, y), and as I suggested before, some sort of average will do the trick.
I'm going to state the obvious here and say that if we want to take an average of four numbers, what we can actually do is this:
-- and that's the strategy we'll take here. We'll average the "front" values s and t first, then average the "rear" values u and v. This works to our advantage because the points below s and t, and similarly below u and v vary only in the x dimension -- so we only have to worry about dealing with one dimension at a time.
We're not taking a plain vanilla-flavored average here, but instead, a weighted average. That is, the value of the noise function should be influenced more by s than t when x is closer to x0 than x1 (as is the case in the pictures above). In fact, it ends up being nice to arrange things so that x and y values behave as if they are slightly closer to one grid point or the other than they actually are because that has a smoothing effect on the final output. I know that sounds really silly, but noise without this property looks really stupid. In fact, it sounds so silly that I'm going to write it again: we're going to want the input point to behave as if its closer to one grid point or another than it actually is.
This is where we introduce the concept of the ease curve. The function 3p² - 2p³ generates a nice S-shaped curve that has a few characteristics that are good for our purposes.
What the ease curve does is to exaggerate the proximity its input to zero or one. For inputs that are sort of close to zero, it outputs a numberreally close to zero. For inputs close to one, it outputs a number really close to one. And when the input is exactly one half, it outputs exactly one half. Also, it's symmetrical in the sense that it exaggerates to the same degree on both sides of p = ½. So if we can treat one grid point as the zero value, and the other as the one value, the ease curve has the exact smoothing effect that just we said we wanted.
Ok, I've droned on long enough, it's calculation time: first, we take the value of the ease curve at x - x0 to get a weight Sx. Then we can find the weighted average of s and t by constructing a linear function that maps 0 to s and 1 to t, and evaluating it at our x dimension weight Sx. We'll call this average a. We'll do the same for u and v, and call the result b.
The above figures should satisfy you that no matter where x happens to fall in the grid, the final averages will always be between s and t, and u andv, respectively. Mathematically, this is all simple to calculate:
Sx = 3(x - x0)² - 2(x - x0)³
a = s + Sx(t - s)
b = u + Sx(v - u)
Now we find our y dimension weight, Sy, by evaluating the ease curve at y - y0, and finally we take a weighted sum of a and b to get our final output value z.
And there you have it. So all you have to do is call the noise function for every pixel you want to color, and you're done. Probably.
An interesting consequence of all of this is that the noise function becomes zero when x and y are both whole numbers. This is because the vector (x, y) - (x0, y0) will be the zero vector. Then the dot product of that vector and g(x0, y0)will evaluate to zero, and the weights for the averages will also always be zero, so that the zero dot product will influence the final answer 100%. The funny thing is that looking at the noise function, you would never guess that it ends up being zero at regular intervals.
If you want to extend the noise function to n dimensions, you'll need to consider 2n grid points, and perform 2(n - 1) weighted sums. The implementation presented here has a computational complexity of O(2(n - 1)), which means that you'll really start to drag if you want to look at 5-, 6-, or greater dimensional noises.
We could increase the speed of the noise function if we didn't have to compute the pseudorandom gradient every time we called the noise function (because for every x between some x0 and x1 and y between some y0 and y1, we have to recalculate the same four gradients every time the noise function is called!) The nice thing to do would be to precalculate the gradient for every possible (xgrid, ygrid), but xgrid and ygrid are allowed to vary all over the real plane! How could we possible precalculate every value?
The neat thing about noise is that it's locally variable, but globally flat -- so if we zoom out to a large degree, it will just look like a uniform value (zero in fact). So, we don't care if the noise function starts to repeat after large intervals, because once you're zoomed out far enough to see the repeat, it all looks totally flat anyways.
So now we should feel justified in doing a little trick with modulus. Say we're doing noise in 3D, and we want to find the gradient for some grid point (i, j, k). Let's precompute a permutation table P, that maps every integer from 0 to 255 to another integer in the same range (possibly even the same number). Also, let's precompute a table of 256 pseudorandom gradients and put it in G, so that G[n] is some 3-element gradient.
Now,
g(i, j, k) = G[ ( i + P[ (j + P[k]) mod 256 ] ) mod 256 ]
But on a computer, modulus with 256 is the same thing as a bitwise and with 255, which is a very fast operation to perform. Now we've reduced the problem of a possibly involved function call into nothing but some table lookups and bitwise operations. Granted, the gradients repeat every 256 units in each dimenstion, but as demonstrated, it doesn't matter.
This speedup is also mentioned on Perlin's web page, and is actually used in his original implementation.
What if you want to use procedural noise to calculate a repeating animation of a licking flame or rolling clouds? It is true that noise on it's own doesn't tend to repeat discernably, and that's great for generating ever-changing images; however, if you want to prerender an animation, you'll probably want it to repeat. Given that F is your function that generates a real number from a point in noise-space, and you want your animation to repeat every t units, you define a new function that loops when z is between 0 and t:
Floop(x, y, z) = ( (t - z) * F(x, y, z) + (z) * F(x, y, z - t) ) / (t)
Now each function call takes twice as long to calculate, but since you're probably not going to use this cycling technique in real time, it probably doesn't matter.
The explanation
Consider the graph of a noise function F(x, y, z) for some fixed x and y. The problem is, you want F(0) = F(t) and there is no guarantee that will be the case with any old x, y, and t. Actually, since any given x and y should have no influence on the repeating aspect of the animation, we can for all practical purposes throw them out while we're developing our repeating function, and consider the simplified case of a one-dimensional noise function F(z).
We need to take this function and change it into one where it is guaranteed that F(0) = F(t). Here's an expanded view of our noise function showing its behavior from -t to t:
Now we define a new function F'(z) that behaves like F(z) when z is near zero, but behaves like F(z - t) when z is near t. If you think about it, you see that F'(0) = F(0), and that F'(t) = F(t - t) = F(0). We can do this with a simple linear interpolation by considering a linear function which is equal to F(z) at 0, and equal to F(z - t) at t.
High school algebra tells us that this linear function has a slope of
(rise) / (run) = (F(z - t) - F(z)) / (t - 0) = (F(z - t) - F(z)) / t
and an intercept of F(z). Now we set up F' as this linear interpolation operating on z:
F'(z) = slope(z) + intercept
= ( (F(z - t) - F(z)) / t )(z) + F(z)
= ( ( (z) * F(z - t) - (z) * F(z) ) / t ) + F(z)
= ( ( (z) * F(z - t) - (z) * F(z) ) / t ) + (t / t) * F(z)
= ( (z) * F(z - t) - (z) * F(z) + (t) * F(z)) / (t)
= ( (z) * F(z - t) + (t-z) * F(z) ) / (t)
= ( (t-z) * F(z) + (z) * F(z - t) ) / (t)
Which is basically the same as the function given above. If you look at F' next to F, you can see that the function does indeed repeat in that F'(0) = F'(t). Also notice that the peaks just to the right of F'(0) are the same as the peaks just to the right of F'(0), and that the peaks just to the left ofF'(t) are virtually identical to the peaks just to the left of F(0).
If you had a little calculus and too much free time, you could prove that this looping (and the tiling in the next section) is truly seamless: not only does F(0) = F'(t), but all their derivatives should match up as well. Which means that no one should be able to tell where the repeat actually happens.
Say you're using a noise function to generate textures for a polygon-based engine. Chances are, you might want the textures to be tileable. You can extend the above example to generate tileable 2-dimensional noise that repeats every w units in the x dimension and every h units in the y dimension. This will require a weighted sum of four calls to the original function (which again, for lack of imagination, we will call F).
Ftileable(x, y) = (
F(x, y) * (w - x) * (h - y) +
F(x - w, y) * (x) * (h - y) +
F(x - w, y - h) * (x) * (y) +
F(x, y - h) * (w - x) * (y)
) / (wh)
A pattern should be emerging here, so it shouldn't surprise you that you'll need evaluate a weighted sum of 8 calls to the noise function if you want to have a repeating animation that tiles in the x and y dimenstions. I'm not going to write it out here out of space considerations, and also because it should be evident what the sum will be.
3차 곡선을 이용한 다 구간 경로 기반의 컷씬 효과 (0) | 2012.10.27 |
---|---|
선형적 샘플링으로 효율적인 가우시안 블러 (0) | 2012.10.27 |
Hypercube Vertices (0) | 2012.10.27 |
플랙탈 - Apophysis, ultra fractal 5 (0) | 2012.10.27 |
프렉탈의 프로그래밍 (0) | 2012.10.27 |
http://www.math.union.edu/~dpvc/talks/2001-04-28.HRUMC/vertices-2.html
Hypercube Vertices |
1 | 2 | 4 | 8 | 16 | |||
0D | 1D | 2D | 3D | 4D |
The hypercube has 16 = 24 vertices
선형적 샘플링으로 효율적인 가우시안 블러 (0) | 2012.10.27 |
---|---|
펄린노이즈 2탄 (Perlin noise) (0) | 2012.10.27 |
플랙탈 - Apophysis, ultra fractal 5 (0) | 2012.10.27 |
프렉탈의 프로그래밍 (0) | 2012.10.27 |
펄린노이즈 1탄 (Perlin Noise) (2) | 2012.10.27 |
http://wibler.egloos.com/3407369
제목 그대로 그림에서처럼 두 벡터 사이의 각도를 구해보죠.
이건 원운동을 하기
위해 필요한 작업이에요.
사실 이건 고등학교 수학책에도 나올만한 문제이지요. 백터의 내적을 이용하면 되거든요.
a점의
x1, y1이고 b점이 x2, y2면 아시다시피 두 백터의 내적은 다음과 같아요.
a*b = |a||b|*cos(c) -- 앞의 *
는 내적을 표해요. 표현하기가 마땅치 않군요.
이걸 풀어쓰고 정리하면(루트는 sqrt()로 쓸게요)
cos(c) = {
x1 * y1 + x2 * y2 } / { sqrt( x1^2 + y1^2 ) * sqrt( x2^2 + y2^2 ) }
이제
cos의 역함수인 acos()함수를 쓰면 c를 구할 수 있어요. 보통은.
그런데 여기서 math.h 에 들어있는 acos의 한계가
나와요. 이놈은 리턴을 0~180까지의 범위만으로 하거든요.
다시 설명하자면 다음과 같이 점이 있을때.
나는 a에서 b까지 시계방향으로 측정한 각도를 구하고 싶은데 위 방법대로
하면
반시계방향의 각도가 나와 버린다는 소리죠.
그래서 내적을 이용해 구하기전에 약간의 사전작업을 해야 해요. 다시
그림을 보자면.
일단 a를 90도 회전시킨 a' 을 구해요. 이건 매우 쉬워요.
주의해야할껀
좌표계의 방향이에요. 보통 윈도우를 만들면 아래쪽으로 갈수록
y값이 커지는건 알고 있죠. 그걸 기준으로 설명할게요.
a
가 x1, y1 이고 a' 이 x1', y1' 일때 시계방향으로 회전시키고 싶으면
x1' = -y1, y1' = x1이
되요.
반시계방향이라면. x1' = y1, y1' = -x1이 되죠. 조금만 생각해보면 답나와요.
이제 a'점과
b점사이의 각도를 위의 방식으로 내적을 이용해서 구해요.
이제 거의 다 됬어요. 방금구한 각도를 c' 라고
할때.
방금구한 각도가 90 보다 크면 a에서 b까지의 각도는
180도 보다 크다는 이야기에요.
그렇다면 간단하죠.
c'이 90보다 작을때에는 a와 b사이의 각도는
내적을 이용해서 바로 구할 수 있고.
c'이 90보다 클 때에는 360에서 내적을 이용해 구한 a와 b사이의 각도를 빼면
되지요.
아까 설명했다시피 c'이 90보다 클때는 a와 b사이의 각도가 반시계방향의 각도로 나오잖아요.
0~180의
범위로.
이런식으로 시계방향 혹은 반시계방향으로 0~360범위의 각도를 구할 수 있답니다.
아래 코드는 그걸
짜본거에요.
세번째 인자인 isClockWise는 true로 하면 시계방향, false로 하면 반시계방향의 각도를
구합니다.
fPOINT 는 전에 말했다시피 float x, y로 구성된
구조체입니다.
----------------------------------------------------------------------------------------
float
CPath::CalAngleBetweenTwoPoint(fPOINT ptPoint1, fPOINT ptPoint2, BOOL
isClockWise)
{
//acos()나 asin()함수로 구하는 각도를 degree로 나타냈을때
//그 범위는
0~180이다.
//각도를 0~360으로 나타내기 위한 사전작업으로
//기준점(ptPoint1)을 90도 회전시킨 점을
구한다.
fPOINT ptRotated90Point1 = {0,0};
if(isClockWise)
{
ptRotated90Point1.x=
-ptPoint1.y;
ptRotated90Point1.y =
ptPoint1.x;
}
else
{
ptRotated90Point1.x =
ptPoint1.y;
ptRotated90Point1.y = -ptPoint1.x;
}
//앞서 계산한 점과 두번째 인자로 받은 점간의 각도를 구함.
//이 각도가 90도보다 크다면 첫번째인자로 받은 기준점과 두번째
점간의 각도가
//180도보다 크다는 것을 의미한다.
float fAng = acos( (ptRotated90Point1.x * ptPoint2.x + ptRotated90Point1.y *
ptPoint2.y) /
(sqrt(
ptRotated90Point1.x * ptRotated90Point1.x + ptRotated90Point1.y *
ptRotated90Point1.y ) *
sqrt(ptPoint2.x * ptPoint2.x +
ptPoint2.y * ptPoint2.y) ) )
* 360 / TWOPI;
//fAng의 크기에 따라 두점사이의 각도를 다른 방식으로 구함.
if(fAng > 90)
return 360 -
acos( (ptPoint1.x * ptPoint2.x + ptPoint1.y * ptPoint2.y) /
(sqrt( ptPoint1.x * ptPoint1.x +
ptPoint1.y * ptPoint1.y ) *
sqrt(ptPoint2.x * ptPoint2.x +
ptPoint2.y * ptPoint2.y) ) )
* 360 / TWOPI;
else
return acos( (ptPoint1.x * ptPoint2.x + ptPoint1.y * ptPoint2.y)
/
(sqrt( ptPoint1.x * ptPoint1.x
+ ptPoint1.y * ptPoint1.y ) *
sqrt(ptPoint2.x * ptPoint2.x +
ptPoint2.y * ptPoint2.y) ) )
* 360 / TWOPI;
}
언프로젝션과 픽킹 (0) | 2012.11.02 |
---|---|
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
D3DXVec3TransformCoord & D3DXVec3Transform 둘의 차이는 w 로 나누었는가의 차이 (0) | 2012.11.02 |
D3DXVec3TransformNormal 과 D3DXVec3TransformCoord 의 차이 (0) | 2012.10.31 |
convex polygon 이든 concave polygon 이든 모든 다각형의 내부에서 점의 존재여부 판단 (0) | 2012.10.27 |
http://lsujang.egloos.com/845586
벡터의
외적을 이용하면 3개의 점(A,
B, C)이
이루는 삼각형 안에 또다른 점 D가
포함되어 있는지의 여부를 손쉽게 판단할 수 있다. 바로 곱하여지는 두 벡터의 위치에 따라 법선 벡터의 방향이 달라진다는 성질을 이용한 것이다.
여기선 오른손 좌표계를 이용한다고 정하자.(머 왼손 좌표계라도 상관은 없다.)
언프로젝션과 픽킹 (0) | 2012.11.02 |
---|---|
D3DXVec3TransformNormal & D3DXVec3TransformCoord (0) | 2012.11.02 |
D3DXVec3TransformCoord & D3DXVec3Transform 둘의 차이는 w 로 나누었는가의 차이 (0) | 2012.11.02 |
D3DXVec3TransformNormal 과 D3DXVec3TransformCoord 의 차이 (0) | 2012.10.31 |
원점에서 두 점을 잇는 선사이의 각도, 즉 두 벡터 사이의 각도 0~360범위로 구하기. (0) | 2012.10.27 |
http://apophysis.org/
ApophysisFreeware fractal flame editor for Windows
|
http://www.ultrafractal.com/download/index.php
Download your copy of Ultra Fractal 5 here:
After installing, Ultra Fractal will run as a free trial version for 30 days. If you already have purchased a license key, simply enter it when Ultra Fractal starts to unlock full functionality.
Installation (Windows)
After downloading, double-click on the downloaded file to install Ultra Fractal. When you first start Ultra Fractal, a welcome screen will appear that provides access to tutorials and online help to help you to get started quickly.
Evaluation
The downloaded evaluation version is fully functional, except that exported and rendered images are marked as written by an evaluation copy. If you have a license for Ultra Fractal 5, enter it when starting the program to turn the trial version into a full version.
If you want to keep using Ultra Fractal after the 30-day trial period, you must purchase a copy in the Ultra Fractal shop. You can also purchase a site license if you need more than one copy.
Released August 11, 2010, version 5.04 contains bug fixes and improvements.
The included help file is also available in PDF format for easy printing.
펄린노이즈 2탄 (Perlin noise) (0) | 2012.10.27 |
---|---|
Hypercube Vertices (0) | 2012.10.27 |
프렉탈의 프로그래밍 (0) | 2012.10.27 |
펄린노이즈 1탄 (Perlin Noise) (2) | 2012.10.27 |
클럭 : 게임의 심장박동 (0) | 2012.10.27 |
펄린노이즈 2탄 (Perlin noise) (0) | 2012.10.27 |
---|---|
Hypercube Vertices (0) | 2012.10.27 |
플랙탈 - Apophysis, ultra fractal 5 (0) | 2012.10.27 |
펄린노이즈 1탄 (Perlin Noise) (2) | 2012.10.27 |
클럭 : 게임의 심장박동 (0) | 2012.10.27 |
펄린노이즈 2탄 (Perlin noise) (0) | 2012.10.27 |
---|---|
Hypercube Vertices (0) | 2012.10.27 |
플랙탈 - Apophysis, ultra fractal 5 (0) | 2012.10.27 |
프렉탈의 프로그래밍 (0) | 2012.10.27 |
클럭 : 게임의 심장박동 (0) | 2012.10.27 |
2시간 삽질하면서 찾은 cuda 4.1 설정 방법입니다.
간단하게 설정 할 수 있는데 역시 모르니 완전 헤멥니다. 아는게 힘이라능
다운 받는건 http://blog.naver.com/toses3/40140926000 요기로 가셔서 보시고...
4.0버전으로 설명되어있는데, bug fix는 현재 사라졌으니 무시하시고
1. CUDA Tool kit 4.1 2. GPU Computing SDK 3. 개발자용 그래픽 드라이버
이렇게 3개가 필요합니다.
각각 설치하시고 난뒤
visual studio 2010을 실행시키고 프로젝트를 하나 만듭니다. 이 과정도 위 링크에 다 있습니다.
프로젝트 속성에서 라이브러리 폴더와 include 폴더, 라이브러리를 추가해주어야 합니다.
아래 그림과 같이 VC++ 디렉터리에서 설정하시면 됩니다.
포함 디렉터리는 헤더파일용입니다. 그자리에 다음과 같이 넣으시면 됩니다.
C:\ProgramData\NVIDIA Corporation\NVIDIA GPU Computing SDK 4.1\C\common\inc C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v4.1\include
SDK의 include 폴더와 GPU Tool kit의 include 폴더 이렇게 2개가 한 쌍으로 있어야합니다. 하나라도 빠지면 링크 에러 납니당..;;
라이브러리 디렉터리에는 다음과 같이 넣습니다.
C:\ProgramData\NVIDIA Corporation\NVIDIA GPU Computing SDK 4.1\C\common\lib\Win32 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v4.1\lib\Win32
저는 32비트로 설치했으므로 위와 같이 하였고, 64비트로 설치하신분은 끝에 Win32를 x64로 하시면 될겁니다.
그리고 *.cu로 생성한 파일의 속성에서 cuda 컴파일러를 설정해줍니다.
파일의 속성입니다. 프로젝트 속성이 아닙니다. 헷갈리지 마시구요. 처음에는 빌드에 참여안함 이라고 되어있는데 그것을 위의 그림처럼 설정 해줍니다.
이제 설정이 다되었구요. CUDA의 세계로 빠지시면 됩니다. [출처] CUDA 4.1 visual studio 2010 설정 (윈도우 7)|작성자 쏭섭 |
cudaThreadSynchronize (0) | 2012.10.27 |
---|---|
그래픽카드 인터페이스 - PCI 익스프레스 (0) | 2012.10.27 |
atomicAdd vs2010 에서 정의도지 않았다고 할때.. (0) | 2012.10.27 |
쿠다 변수 특성 (0) | 2012.10.27 |
CUDA Event로 시간측정 (1) | 2012.10.27 |
CUDA 4.1 visual studio 2010 설정 (윈도우 7) (1) | 2012.10.27 |
---|---|
그래픽카드 인터페이스 - PCI 익스프레스 (0) | 2012.10.27 |
atomicAdd vs2010 에서 정의도지 않았다고 할때.. (0) | 2012.10.27 |
쿠다 변수 특성 (0) | 2012.10.27 |
CUDA Event로 시간측정 (1) | 2012.10.27 |
PCI 32Q비트 66MHz 266MB/s
PCI ExpressX1 2.5GHz 512MB/s
PCI ExpressX4 2.5GHz 2GB/s (Duplex)
PCI ExpressX8 2.5GHz 4GB/s (Duplex)
PCI ExpressX16 2.5GHz 8GB/s (Duplex)
CUDA 4.1 visual studio 2010 설정 (윈도우 7) (1) | 2012.10.27 |
---|---|
cudaThreadSynchronize (0) | 2012.10.27 |
atomicAdd vs2010 에서 정의도지 않았다고 할때.. (0) | 2012.10.27 |
쿠다 변수 특성 (0) | 2012.10.27 |
CUDA Event로 시간측정 (1) | 2012.10.27 |
http://forums.nvidia.com/index.php?showtopic=198586
EDIT
: Okay, on VS2010 :
Quote
cudaThreadSynchronize (0) | 2012.10.27 |
---|---|
그래픽카드 인터페이스 - PCI 익스프레스 (0) | 2012.10.27 |
쿠다 변수 특성 (0) | 2012.10.27 |
CUDA Event로 시간측정 (1) | 2012.10.27 |
쿠다 비주얼어시스트 목록 뜨게하기 (0) | 2012.10.27 |
쿠다 변수 특성
http://blog.naver.com/lithium81/80145123334
이 포스트는 "CUDA By Example", "대규모 병렬 프로세서 프로그래밍", "CUDA 병렬 프로그래밍"을 스터디하면서 익힌 내용을 까먹지 않게 정리한 것임.
※ 로그인 안 하고도 내가 언제든지 볼 수 있도록 공개한 것일 뿐, 맘대로 퍼가고 수정하라고 만든 자료 아님. 불펌 금지
2012/01/27 15:02 |
//---------------------------------------------------
아~ for문으로 10번 돌것을 하나의 block 이 하나의 배열을
맡아서 계산해서 넘겨주는 거구나
즉 커널을 통해 "나(CPU) 10번 돌거 GPU 니내 10명이서 나눠서
한개씩 맡고 나에게 주렴" 이라는 거구나... 어렵다...
//--------------------------------------------------
#include <stdio.h>
#include <malloc.h>
#include <cuda_runtime.h>
#include <cutil.h>
#define blksize 8
#define grdsize 8
#define N 10
__global__ void test(int *a, int *b, int *c)
{
int tid = blockIdx.x;
c[tid] = a[tid] + b[tid];
}
int main()
{
//cudaSetDevice (int device );
//property출력을 위한 구조체 생성
cudaDeviceProp prop;
//디바이스 장치의 숫자를 획득
int count;
cudaGetDeviceCount(&count);
//각 디바이스 장치의 property들을 출력
for(int i=0;i<count;i++){
cudaGetDeviceProperties(&prop,i);
printf("--%d번째 디바이스의 정보를 출력합니다--\n",i+1);
printf(" (1) 장치 이름: %s\n", prop.name);
printf(" (2) Clock Rate: %d\n", prop.clockRate);
printf(" (3) 전역 메모리 용량: %ld\n",prop.totalGlobalMem);
printf(" (4) 상수 메모리 용량: %ld\n",prop.totalConstMem);
printf(" (5) 통합형인가??: %ld\n",prop.integrated);
}
int a[N], b[N], c[N];
int *dev_a, *dev_b, *dev_c;
cudaMalloc( (void**) &dev_a, sizeof(int) * N);
cudaMalloc( (void**) &dev_b, sizeof(int) * N);
cudaMalloc( (void**) &dev_c, sizeof(int) * N);
for(int i=0; i<N; i++) {
a[i] = i;
b[i] = i;
}
cudaMemcpy( dev_a, a, sizeof(int) * N, cudaMemcpyHostToDevice );
cudaMemcpy( dev_b, b, sizeof(int) * N, cudaMemcpyHostToDevice );
test<<<N, 1>>>(dev_a, dev_b, dev_c);
cudaMemcpy( c, dev_c, sizeof(int) * N, cudaMemcpyDeviceToHost );
for(int i=0; i<N; i++) {
printf("%d + %d = %d\n", a[i], b[i], c[i]);
}
cudaFree(dev_a);
cudaFree(dev_b);
cudaFree(dev_c);
return 0;
}
[출처] CUDA 병렬프로그래밍에 대한 이해...|작성자 김연슬
그래픽카드 인터페이스 - PCI 익스프레스 (0) | 2012.10.27 |
---|---|
atomicAdd vs2010 에서 정의도지 않았다고 할때.. (0) | 2012.10.27 |
CUDA Event로 시간측정 (1) | 2012.10.27 |
쿠다 비주얼어시스트 목록 뜨게하기 (0) | 2012.10.27 |
CUDA 4.1 VS2010 Configuration (0) | 2012.10.27 |
그래픽카드 인터페이스 - PCI 익스프레스 (0) | 2012.10.27 |
---|---|
atomicAdd vs2010 에서 정의도지 않았다고 할때.. (0) | 2012.10.27 |
쿠다 변수 특성 (0) | 2012.10.27 |
쿠다 비주얼어시스트 목록 뜨게하기 (0) | 2012.10.27 |
CUDA 4.1 VS2010 Configuration (0) | 2012.10.27 |
지금부터는
.cu파일을
열어보면 int float #include 등에 상관없이 모두 검정색 글씨로 되어있는데
이걸
일반C++파일을 열었을 때처럼 파란색/갈색으로 하이라이팅 하는 법입니다.
아래
사진처럼 SDK-C--doc-syntax_highlighting-visual_studio_8 안쪽에 있는 usertype.dat를
비주얼스튜디오가
설치된 폴더의 Common7-IDE에 복사합니다. (비주얼스튜디오버전에 맞는 폴더를 찾아가시면됩니다.)
설치후에
보면~~ 푸른색과 갈색?으로 하이라이팅 된 것을 확인하실 수 있습니다~
이번에는
Visual Assist X (비주얼 어시스트 X)설정 법입니다.
Visual
Assist X\AutoText\Latest\Cpp.tpl을 하나 복사해서 이름을 Cu.tpl로 바꿉니다.
시작-실행-regedit
하신다음에 HKEY_CURRENT_USER\Software\Whole Tomato\Visual Assist X\VANet7.0
(자신의
VS버전폴더를찾으시면됩니다.) 에 들어가셔서 ExtHeader와 ExtSource에 각각 .cuh와 .cu를 추가해줍니다.
비주얼
스튜디오를 다시 실행시켜보시면 우왕ㅋ굳ㅋ
http://sigttou.com/category/adventures-in-graphicsland
To get full syntax highlighting and include support, we need to tell VAX about our CUDA libraries as well as the fact that it should treat CUDA files as C/C++ files.
Launch VS 2010 and open the Options screen (Tools > Options). Under Projects and Solutions > VC++ Project Settings add the following entries to the Extensions to Include item:
It should look something like this:
Now close VS 2010 and open up the registry editor (Start > regedit.exe). Browse to the following folder:
Now look for the ExtHeader key and add .cu.h to the list. Make sure the whole line ends with a semicolon. It should look something like this:
Look a little further down and you should see the ExtSource key. Add .cu to the list in the same way. Again, make sure the line ends with a semicolon.
Now relaunch VS 2010 and open the VAX options (VAssistX > Visual Assist X Options). Open the Projects group on the left and select C/C++ Directories. Under Platform select Custom. Then, select Stable Include Files from the drop down on the right and add the paths to your CUDA toolkit includes and GPU Computing SDK includes. If you used the default installation directories, these are:
In other words, your screen should look something like this:
Now switch the drop down to Source files and add the following paths:
Lastly, select the Performance item from the left and click the Rebuild Symbol Databases button.
You should be good to go now with Visual Assist X and CUDA.
그래픽카드 인터페이스 - PCI 익스프레스 (0) | 2012.10.27 |
---|---|
atomicAdd vs2010 에서 정의도지 않았다고 할때.. (0) | 2012.10.27 |
쿠다 변수 특성 (0) | 2012.10.27 |
CUDA Event로 시간측정 (1) | 2012.10.27 |
CUDA 4.1 VS2010 Configuration (0) | 2012.10.27 |
순서대로 따라하면 됨.
http://blog.naver.com/PostView.nhn?blogId=toses3&logNo=40140926000
-개발자버전 드라이버를 설치해야 한다, 그냥 드라이버가 아닌..
-쿠다툴킷 설치
-비주얼스튜디오 설치
요약
1. win32 Console application 으로 생성
2. 사용자 지정빌드(프로젝트에 마우스 대로오른쪽 버튼 누르면 나옴) 에서 CUDA 4.1(Targets, .props) 를 선택
하면 빌드할 것들을 알아서 목록포함
(4.1 툴킷 부터는 프로퍼티가 안뜨는 버그는 안보이는듯)
옵션에서 텍스트 에디터 -> 파일 확장자 에서 cu. cuh 확장자를 Visual C++ 에티터로 등록
.cu 코드 작성후 속성에서
.cu 의 속성->일반->아이템 타입 이 CUDA C/C++ 이 되도록 변경
프로젝트속성 -> 입력 에서 추가 종속성에 cudart.lib 추가
그래픽카드 인터페이스 - PCI 익스프레스 (0) | 2012.10.27 |
---|---|
atomicAdd vs2010 에서 정의도지 않았다고 할때.. (0) | 2012.10.27 |
쿠다 변수 특성 (0) | 2012.10.27 |
CUDA Event로 시간측정 (1) | 2012.10.27 |
쿠다 비주얼어시스트 목록 뜨게하기 (0) | 2012.10.27 |
펄린노이즈 2탄 (Perlin noise) (0) | 2012.10.27 |
---|---|
Hypercube Vertices (0) | 2012.10.27 |
플랙탈 - Apophysis, ultra fractal 5 (0) | 2012.10.27 |
프렉탈의 프로그래밍 (0) | 2012.10.27 |
펄린노이즈 1탄 (Perlin Noise) (2) | 2012.10.27 |
http://www.gpgstudy.com/gpg3/gpg3s3-5draft.html
이 글은 조만간 출판될 Game Programming Gems 3 한국어판(류광 역, 정보문화사)의 일부입니다. 이 글에 대한 모든 권리는 정보문화사가 가지고 있으며, 사전 허락 없이는 웹 사이트 게시를 비롯한 어떠한 형태의 재배포도 금지되어 있습니다. 이 글은 최종 교정 전의 상태이므로 오타나 오역이 있을 수 있습니다. 또한 웹 페이지의 한계 상, 실제로 종이에 인쇄된 형태와는 다를 수 있습니다. 실제 책에서는 표나 수식이 좀 더 정확한 형태로 표시될 것이며 그림/도표 안의 영문도 적절히 한글화될 것입니다. Game Programming Gem 3 한국어판에 대한 정보는 GPG 스터디(http://www.gpgstudy.com/)에서 얻을 수 있습니다. |
게임의 1인용 버전에 식상해버린 플레이어는 웹에서 더 많은 레벨들이나 레벨 편집기를 찾게 된다. 그리고 게임이 성공하려면 그런 기대에도 부응할 수 있어야 한다. 확장성 있는 레벨들과 퀘스트들은 잘 설계된 게임에 대한 하나의 품질 보증서로 간주될 수 있으며, 게임의 일반적인 수명을 상당히 늘려줄 수 있다. 게임은 그것이 RTS이든 아니면 RPG나 액션이든, 플레이어가 어떠한 형태로든 레벨들을 커스텀화하거나 새로운 영역들을 추가하는 것이 가능해야 한다.
Baldur’s Gate, StarCraft, Dungeon Siege가 훌륭한 게임으로 평가되는 이유에는 플레이어가 새로운 퀘스트들을 만들거나 심지어는 AI를 수정하고 확장하는 것이 가능하다는 측면도 포함된다. 그러나 플레이어는 프로그래머가 아니므로 플레이어들에게 가상의 프로그래밍 언어를 배우도록 하거나 디버깅을 강제하는 것은 불가능하다. 평범한 플레이어도 스스로 레벨이나 퀘스트를 만들 수 있으려면 무엇보다도 단순함이 보장되어야 하는데, 그러한 단순함은 확장성있는 트리거 시스템을 통해서 제공할 수 있다.
트리거 시스템은 ‘조건을 평가하고 반응을 수행한다’라는 한 가지 목적을 가진 중앙 집중화된 코드이다. 일련의 조건들이 만족되면, 일련의 반응들이 수행된다. 이러한 간단한 시스템은 우아하고, 구현하기 쉬우며, 데이터 주도적[Rabin0]으로 만들기도 쉽다. 트리거 시스템은 다양한 범위의 문제들을 해결할 수 있으며, 특히 디자이너와 플레이어에 의해 수정될 수 있다는 점에서 바람직하다. 트리거 시스템의 매력은 무엇보다도 플레이어가 탐험할 흥미롭고 상호작용적이며 새로운 환경을 쉽게 만들 수 있도록 한다는 데 있다.
일단의 모험가들이 던전을 탐험하는 게임을 생각해보자. 모험대는 지하 묘지를 통과하는 도중 무너져 내린 기둥에 대장을 잃게 된다. 그리고 인상적인 석문에 도달했을 때에는 찬 바람이 불어 와 횃불도 꺼뜨린다. 마지막 횃불에 불을 붙인 후 석문을 보니 “무거운 심장이여 이 문을 통과하리니”라고 새겨진 문구가 눈에 띈다. 잠시 생각해본 후, 체중이 무거운 대원 하나를 심장 모양의 받침대에 올려놓았더니 문이 서서히 열린다.
간단한 조건-반응 패러다임을 이용하는 트리거 시스템으로 이러한 사건들을 지정하는 것이 가능하다. 대원이 특정 기둥 객체에 1미터 이내로 접근하면 바람 소리에 해당하는 음향 효과를 재생하고 그 주변의 횃불들을 꺼뜨린다던가, 플레이어의 파티 대원들 중 하나가 심장 모양의 받침대 위로 올라가면 문을 열고 돌이 갈리는 듯한 음향 효과를 재생하는 등은 모두 조건-반응 패러다임에 속한다.
트리거 시스템을 잘 구현한다면, 디자이너와 플레이어 모두가 독창적인 시나리오 및 퀘스트의 작성을 위해 여러 시간 동안 몰두하도록 만들 수 있는 장치들을 갖출 수 있다. StarCraft 맵 편집기는 참고할 만한 좋은 트리거 시스템의 예이다. 기존의 시스템을 연구하고 새로운 아이디어를 만들어 내는 것은 개발자의 기본적인 자세이다.
트리거 시스템을 처음 만들 때에는 하나의 마스터 트리거 시스템([Orkin02]에 나온 것 같은)을 떠올리게 될 것이다. 그러나 좀 더 강력한 구조를 원한다면 어떠한 에이전트나 객체, 퀘스트도 소유할 수 있는 트리거 시스템 클래스를 생각해 볼 수 있다. 모든 객체가 트리거 시스템을 가져야 하는 것은 아니지만, 객체마다 자신의 트리거 시스템 인스턴스를 가질 수 있다면 객체 안에 데이터를 캡슐화하는 데에도 도움이 되며 시스템이 좀 더 유연하고 객체지향적이 될 수 있다. 또한 트리거라는 것 자체가 특정 객체에 대해 작동하게 되므로, 플레이어가 개념을 잡는 데에도 도움이 된다.
누군가가 다가오면 무너져 내리는 기둥을 생각해보자. 그러한 기둥을 레벨 편집기 안에서 정의하고 거기에 무너져 내리는 행동을 강조하는 하나의 트리거 정의를 부여한다. 그리고 디자이너나 플레이어가 게임의 여러 곳에 그러한 기둥들을 배치하면, 신통하게도 의도했던 대로의 모습을 볼 수 있게 된다. 이는 트리거 행동이 객체에 직접 부착되어 있기 때문에 가능한 것이다. 이런 방식에서는 어떠한 에이전트나 객체, 퀘스트도 전적으로 해당 개체만을 위해 존재하는 자신만의 고유한 트리거 시스템을 가질 수 있다.
게임 안에서 수량화할 수 있는 이벤트나 상태라면 어떠한 것이라도 조건이 될 수 있다. 조건은 실행 파일 안에 고정되지만 인수들이나 레벨 편집기를 통해서 조절할 수 있는 여지도 매우 크다. 다음은 가능한 몇 가지 조건들이다.
조건들을 AND, OR, NOT, XOR 같은 부울 연산자들로 결합할 수 있다면 조건들이 좀 더 유연해질 수 있다. 예를 들어 플레이어가 얼음검, 얼음방패, 얼음갑옷을 장착해야만 어떤 문이 열리게 되어 있다면 조건들은 AND로 결합되어야 한다. 또 어떤 문은 플레이어가 은열쇠나 해골열쇠 중 하나만 가지고 있어도 열릴 수 있다면 조건들이 OR로 결합되어야 한다. 그림 3.5.1과 3.5.2는 그러한 조건들을 트리 구조로 표현한 것이다.
AND 참이면 트리거 발동 문 열림 얼음검 장착 얼음방패 장착 얼음갑옷 장착
그림 3.5.1 세 조건 모두 “참”이면 문이 열린다.
OR 참이면 트리거 발동 문 열림 은열쇠를 가지고 있음 해골열쇠를 가지고 있음
그림 3.5.2 둘 중 하나라도 “참”이면 문이 열린다.
좀 더 복잡한 경우로, 플레이어가 얼음검, 얼음방패, 얼음갑옷을 장착하고 있으며 은열쇠 또는 해골열쇠를 가지고 있어야 문이 열리게 된다면 어떻게 될까? 그림 3.5.3이 그러한 조건들을 표현한 것이다.
이와 같은 그림들을 통한 시각화는 코드를 구조화하는 좋은 방법을 제시한다는 점에서 중요한 의의를 갖는다. 각 요소가 하나의 클래스인 경우 두 종류의 클래스가 필요한데, 하나는 Operator 클래스이고 또 하나는 Condition 클래스이다. Operator 클래스는 임의의 부울 연산자처럼 작동하도록 만들어야 할 것이다. 또한 여러 연산자, 피연산자들이 포함된 표현식을 나타내기 위해서는 다른 Operator 인스턴스들이나 Condition 인스턴스들로의 포인터들의 목록도 담아야 한다. Condition 클래스는 임의의 판정 가능한 조건들을 평가할 수 있어야 하며, 조건의 커스텀화를 위한 인수들을 담아야 한다.
AND 참이면 트리거 발동 문 열림 얼음검 장착 얼음방패 장착 얼음갑옷 장착 OR 은열쇠를 가지고 있음 해골열쇠를 가지고 있음
그림 3.5.3 문을 좀 더 복잡한 조건들
게임 안에서 변경하고자 하는 상태나 행동이라면 어떠한 것도 반응이 될 수 있다. 조건과 마찬가지로, 반응 자체는 실행 파일 안에 고정되지만 인수들 또는 레벨 편집기를 통해서 조절될 수 있다. 다음은 가능한 반응들의 목록이다.
특정 조건들의 집합이 만족되면 그에 해당하는 반응이 수행된다. 이를 단 하나의 반응이 아니라 여러 개의 반응들의 집합으로 확장할 수도 있다. 그렇게 하면 하나의 트리거 발동이 여러 가지 것들에 동시에 영향을 미치게 할 수도 있고, 여러 반응들 중 임의의 것이 수행되도록 할 수도 있다.
트리거에 조건들이 정의되었다고 할 때, 다음으로 필요한 것은 한 트리거의 조건들을 평가해서 발동 여부를 판단하는 구조이다. 우선 고려할 것은, 특정 조건이 이벤트 주도적인지(트리거 시스템에 이벤트가 통지되기를 기다리는 것) 아니면 게임 세계를 주기적으로 점검해야 하는지를 결정하는 것이다. 두 방식 모두 지원하는 것이 유연성에 도움이 된다.
이벤트 주도적인 조건의 경우, 이벤트가 트리거 시스템에 통지될 수 있도록 하는 인터페이스가 필요하다. 가장 간단한 방식은 이벤트 메시지를 사용하는 것이다. 이벤트 메시지는 발생한 이벤트의 종류와 이벤트에 관련된 기타 데이터 등으로 구성된다. 이벤트 메시지에 대한 좀 더 자세한 내용은 [Rabin02]를 참고하기 바란다.
주기적 점검 방식의 조건이라면, 트리거 시스템 안에서 각각의 조건들을 점검하는 어떠한 갱신 함수를 호출하는 방식에 대해 생각해볼 수 있다. 그런 함수에서 이벤트 주도적 조건들에 대한 점검은 일어나지 않아야 한다.
이벤트 메시지나 주기적 점검 갱신이 트리거 시스템에 들어왔다면, 그것을 조건들에게 전파해야 한다. 그림 3.5.4는 이벤트 메시지와 주기적 점검 모두를 요구하는 하나의 조건 집합의 예이다. 왼쪽 조건은 충돌 이벤트를 기다리는 반면, 반면 오른쪽 조건은 주기적 점검 갱신이 들어왔을 때 조건을 점검한다.
이벤트 메시지나 주기적 점검 갱신이 트리거 시스템에 들어오면, 그것을 각 트리거의 루트 Operator 인스턴스에 전달한다. Operator는 그것을 자신의 자식들에게 넘겨주며, 자식들은 그에 대한 판단 결과를 “참” 또는 “거짓”으로 돌려준다. 각 자식 역시 그러한 요청을 자신의 자식들에 넘겨준다. 그러한 요청이 실제 조건에 도달하면 참/거짓 판정이 일어나게 되고, 그 결과들이 상위 Operator 인스턴스에 올라가서 부울 연산자에 의한 참/거짓 판정이 일어난다. 그런 식으로 참/거짓이 루트에게까지 올라가면 최종적인 결과가 만들어진다.
이벤트 메시지 및 주기적 점검 갱신 AND 참이면 트리거 발동 쥐 10 마리 생성 플레이어가 10 미터 이내 플레이어의 건강이 50% 이상
그림 3.5.4 하나의 트리거가 이벤트 주도적 조건(아래 왼쪽)과 주기적 점검 기반의 조건(아래 오른쪽)을 모두 가진 예
Operator 클래스는 자식들을 처리할 때 늦은 평가(lazy evaluation)를 사용해야 한다는 점을 명심하기 바란다. 어떠한 조건이 해당 연산자에 대해 만족되지 않은 경우, 트리거의 판정은 그 시점에서 끝난다. 예를 들어 그림 3.5.4에서 어떠한 이벤트 메시지가 왼쪽 조건에 전달되고 그것이 “거짓”으로 판정되었다면 오른쪽 조건으로는 그 이벤트 메시지가 전달되지 않도록 하는 것이다. 이렇게 하면 처리 시간을 줄이는 데 도움이 된다.
또 다른 중요한 점 하나는, 이벤트 주도적 조건의 경우 조건이 명시적으로 재설정되기 전까지는 이벤트들을 기억하고 있어야 한다는 점이다. 그림 3.5.4의 예에서는 플레이어가 10미터 이내로 접근하면 충돌 이벤트가 트리거에 전달되어야 한다. 왼쪽 조건은 그 이벤트를 기억해 두고(플레이어가 10 미터 바깥으로 나가기 전까지는), 이후의 이벤트 메시지나 주기적 점검 갱신에 대해 항상 “참”을 돌려줘야 한다.
어떠한 시점에서, 특정 트리거의 조건들이 모두 참을 돌려준다면 트리거가 발동된다. 트리거가 발동되면 발동되었다는 사실을 기억해서 연달아 다시 발동되는 일이 없도록 해야 한다.
모든 트리거들에는 추가적으로 두 개의 속성들이 더 필요하며, 이 속성들은 디자이너가 정의하게 된다.
bool SingleShot; // 트리거가 한 번만 발동되어야 하는지의 여부 float ReloadTime; // 트리거가 여러 번 발동되어야 하는 경우, // 재설정되기까지의 시간
이 두 속성들은 트리거가 한 번 이상 발동될 수 있도록 한다. SingleShot 속성은 트리거가 한 번만 발동되어야 하는지 아니면 여러 번 발동될 수 있는지의 여부를 가리킨다. SingleShot이 “거짓”일 때, ReloadTime은 트리거가 한 번 발동된 후 다시 발동될 수 있을 때까지, 즉 조건들을 초기화하고 다시 이벤트를 받게 될 때까지 걸리는 시간을 결정한다.
트리거들이 함께 결합될 수 있으려면, 시스템 안의 모든 트리거들이 접근할 수 있으며 즉시 설정할 수 있는 상태들이 존재해야 한다. 따라서 트리거 시스템은 발동된 트리거들의 상태를 추적할 수 있는 일련의 플래그들과 카운터들을 갖출 필요가 있다. 이를 최대한 일반화하기 위해, 각각의 트리거가 문자열 이름으로 접근할 수 있는 임의의 플래그들을 만들 수 있도록 하자. 트리거 시스템은 플래그 이름이 참조될 때 플래그를 생성하거나, 생성되어 있다면 아니면 플래그를 설정한다. 생성된 플래그는 시스템이 종료될 때까지 유지된다.
다음과 같은 새로운 조건들을 생각해보자.
이런 조건들이 추가된다면, 다음과 같은 새로운 반응들이 필요할 것이다.
이러한 플래그들과 카운터들이 있으면 트리거 시스템은 특정 이벤트에 표시를 하거나 발동 횟수를 셀 수 있으며, 이를 통해서 예를 들면 플레이어가 특정 지역에 들린 횟수 등을 알 수 있다. 또한 이벤트들이 특정한 순서로 발생했을 때에만(예를 들면 세 개의 타일들을 특정한 순서로 밟았을 때 등) 트리거가 발동되도록 할 수도 있다. 이러한 플래그들과 카운터들은 상태 정보를 담으므로, 좀 더 많은 종류의 트리거들이 가능해진다.
그림 3.5.5는 플레이어가 어떤 문을 열지 못해서 특정 지역을 반복적으로 찾아오는 경우에 문 열기에 대한 단서를 제공하는 예이다. 세 개의 개별적인 트리거들이 “Visited”라는 이름의 카운터를 통해서 서로 협동적으로 작동한다는 점을 주목하기 바란다.
AND "Visited"를 증가 AND "Visited"를 증가 플레이어가 "Visited"가 플레이어가 "Visited"가 지역 A 안에 있음 짝수 지역 C 안에 있음 홀수 AND 문 D에 대한 단서를 떨어뜨림 "Visited"가 문 D가 8과 같음 잠겼음
_그림 3.5.5 세 개의 트리거들이 “Visited"라는 이름의 카운터를 통해서 함께 작동하는 예. 플레이어가 문 D를 열지 못한 채로 영역 A와 C를 번갈아 8번 방문하면 단서가 제시된다.
플래그와 카운터가 추가되면, 트리거 시스템의 형태는 흑판 아키텍쳐[Isla02]와 매우 비슷해진다. 플래그들과 카운터들은 흑판이 되고 트리거들은 흑판의 내용을 조작하는 지식 원천(knowledge source)의 역할을 하게 되는 것이다. 그러나 트리거들은 대부분 흑판의 외부로부터 비롯된 데이터에 대해 작용하므로, 엄밀히 말해서 트리거 시스템이 곧 흑판 아키텍쳐인 것은 아니다.
트리거 시스템의 기능성이 스크립팅 언어의 기능성과 비슷하다는 점을 눈치 챈 독자도 있을 것이다. 특히 상태 정보가 추가되면 더욱 비슷해진다. 양 쪽의 기능성이 겹치는 부분도 존재하나, 완전한 기능을 갖춘 스크립팅 언어에 비해 트리거 시스템은 다음과 같은 장점들을 가진다.
이 글에서 살펴 본 시스템의 주된 한계는 규모가변성(scalability)이 좋지 않다는 점이다. 그러나 이러한 문제는 무관한 트리거들을 걸러내는 추가적인 코드를 통해서 해결할 수 있다. 근접성을 통한 제외는 이미 그 효과가 상당함이 입증되었다.
또 다른 한계는 조건들과 반응들을 정의하기 위한 어휘가 실행파일 안에 고정되어 있다는 점이다. 사용자의 설정을 코드와 연결하는 부분은 프로그래머의 손을 거쳐야 한다. 이는 게임을 임의의 또는 불순한 의도의 조작으로부터 보호할 수 있다는 장점이 되기도 하다.
부족한 개발 일정에 시달리는 게임 개발자들에게 있어 확장성 있는 트리거 시스템은 어찌 보면 사치스럽게 느껴질 수도 있을 것이다. 그러나 투자한 만큼의 가치와 깊이를 게임에 부여할 수 있다는 점은 잊지 말아야 할 것이다. 확장성있는 트리거 시스템은 또한 프로그래밍 능력을 갖추지 못한 레벨 디자이너도 로직을 구축할 수 있게 하는 효과적인 수단이다. 언뜻 보면 트리거 시스템이 너무 간단한 해결책으로 느껴질 수도 있겠지만, 목표는 디자이너와 플레이어에게 더 많은 능력을 부여하는 데 있음을 잊어서는 안 된다. 내용과 레벨 관련 로직을 정의하는 방식이 쉬우면 쉬울수록 게임은 더욱 확장되고 플레이어는 더 많은 재미를 얻을 수 있다.
힙(Heap) 트리와 정렬 (우선순위 큐) (0) | 2012.10.31 |
---|---|
정렬 알고리즘 - 힙 정렬(Heap Sort) (0) | 2012.10.31 |
해쉬, 해쉬테이블, 정적해쉬, 동적 해쉬 (0) | 2012.10.31 |
버튼입력 이벤트처리 (0) | 2012.10.31 |
컨볼루션 Convolution (0) | 2012.10.27 |
UE3를 활용하여 자신의 프로젝트에 맞는 캐릭터를 구현하기에 앞서 UE3에 이미 구현되어 있는 캐릭터 작동 방식을 분석하기로 한다. 그리하여 캐릭터 구현에 필요한 기능이 중복구현됨을 막고 효율적으로 UE3에 통합될 수 있도록 한다.
여기서 살펴볼 내용은 Core기능에 해당하는 AnimTree, AnimSet, Morph 등의 기능이 아니라, 로직에 해당하는 Pawn, PlayerController, PlayerReplicationInfo 등의 오버뷰와 이들이 엔진 내 어떤 포지션에 해당하는지를 중점으로 살펴본다.
UE3에서 캐릭터를 구현하는데 필요한 요소들을 살펴본다.
// APawn::performPhysics 로부터 호출 void APawn::Crouch(INT bClientSimulation) { ... } void APawn::UnCrouch(INT bClientSimulation) { ... }
// Pawn.uc defaultproperties { ... DesiredSpeed=+00001.00000 ... } // UnController.cpp void AController::MoveTo( ... ) { ... Pawn->DesiredSpeed = Pawn->MaxDesiredSpeed; ... } void AController::MoveToward( ... ) { ... Pawn->DesiredSpeed = Pawn->MaxDesiredSpeed; ... } // UnPhysic.cpp FLOAT APawn::MaxSpeedModifier() { ... if ( !IsHumanControlled() ) { Result *= DesiredSpeed; // 사람이 조종하지 않는 녀석에 한해서만 적용 } ... }
// Pawn.uc function CrushedBy( Pawn OtherPawn ) { TakeDamage( ( 1 - OtherPawn.Velocity.Z / 400 ) * OtherPawn.Mass / Mass, // 데미지, 위에서 내리누를 때 상대방과의 질량에 대비하여 데미지를 가감하는 용도. OtherPawn.Controller, // 유발자 Controller Location, // HitLocation vect( 0, 0, 0 ), // Momentum class'DmgType_Crushed' ); // 데미지 타입 } event TakeDamage( ... ) { ... momentum = momentum / Mass; // 데미지가 가해질 때 전해진 충격량으로부터 움직일 속도를 구함. F=ma -> a=F/m ... }
// UnPhysic.cpp void APawn::CalcVelocity( ... ) { ... if ( bBuoyant ) { Velocity.Z += GetGravityZ() * DeltaTime * ( 1.f - Buoyancy ); // Buoyancy 가 0 이면 중력을 그대로 적용한다. } ... }
// Pawn.uc function JumpOutOfWater( vector jumpDir ) { ... velocity.Z = OutofWaterZ; // set here so physics uses this for remainder of tick ... }
// UnPhysic.cpp void APawn::physFalling( FLOAT deltaTime, INT Iterations ) { ... if ( !bDoRootMotion && TickAirControl > 0.05f ) { // 현재 velocity 에 TickAirControl 시간만큼 경과 후 delta velocity 까지 더한 후 이동거리를 체크한다. FVector TestWalk = ( TickAirControl * AccelRate * Acceleration.SafeNormal() + Velocity ) * deltaTime; TestWalk.Z = 0.f; ... // 이후는 현재 Location 으로부터 TestWalk 만큼 이동한 곳에 특정 world 오브젝트가 있는지 (지형 포함) 체크한다. } ... }
// UnPhysic.cpp FLOAT APawn::MaxSpeedModifier() { ... if ( bIsCrouched ) { Result *= CrouchedPct; } else if ( bIsWalking ) { Result *= WalkingPct; // 바로 위에서 Pawn 의 무브먼트 속도를 Result 에 누적하여 구하고 그것을 Walking 상태여부에 따라 곱하여 현재 무브먼트 속도를 구한다. } ... }
// UnPawn.cpp FVector APawn::GetPawnViewLocation() { return Location + FVector( 0.f, 0.f, 1.f ) * BaseEyeHeight; }
// UnLevTic.cpp void APawn::TickSpecial( FLOAT DeltaSeconds ) { // Authority 이고 BreathTime 중이라면 if ( Role == ROLE_Authority && BreathTime > 0.f ) { BreathTime -= DeltaSeconds; if ( BreathTime < 0.001f ) { // 때가 됐다면 BreathTimer 호출 (바로 아래) BreathTime = 0.0f; eventBreathTimer(); } } ... } // Pawn.uc event BreathTimer() { if ( HeadVolume.bWaterVolume ) { if ( Health < 0 || WorldInfo.NetMode == NM_Client || DrivenVehicle != None ) return; // 죽었거나 클라이언트거나 무엇인가를 타고있다면 무시 TakeDrowningDamage(); // 익사 피해 if ( Health > 0 ) BreathTime = 2.0; // 2초 후 다시 BreathTimer 호출 } else { BreathTime = 0.0; // 더 이상 피해는 없음 } }
// UTPawn.uc event HeadVolumeChange( PhysicsVolume newHeadVolume ) { ... else if ( ... ) { BreathTime = UnderWaterTime; // 입수 시 숨쉬기 타이머 발동 } } defaultproperties { ... UnderWaterTime=+00020.000000 // 20초 ... }
// Pawn.uc function PlayHit( ... ) { ... LastPainTime = WorldInfo.TimeSeconds; }
// UnPhysic.cpp void APawn::CalcVelocity( ... ) { ... if ( bForceRMVelocity ) { Velocity = RMVelocity; return; } ... }
샘플로 제공되는 언리얼토너먼트3 (이하 UTGame) 의 캐릭터를 구현하는데 활용된 여러 요소들을 추가적으로 살펴본다.
엔진에서 목표지점destination까지 정확하게 이동시켜주는 로직이 존재한다.
대략적인 방법은 이렇다.
관련 변수/함수는 아래와 같다.
// Pawn.uc var float DestinationOffset; // 목표지점으로부터의 허용 Offset // Controller.uc var bool bPreciseDestination; // 목표지점에 맞는 velocity 를 강제할 것인지의 여부, 정확한 이동을 수행할 것인지 여부와 상통 var BasedPosition DestinationPosition; // 목표지점
// UnPhysic.cpp void Pawn::CalcVelocity( ... ) { ... // RooMotion 일 경우를 제외하고 '정확한 이동' 처리를 수행한다. if ( !bDoRootMotionAccel && Controller && Controller->bPreciseDestination ) { FVector Dest = controller->GetDestinationPosition(); // Controller.DestinationPosition 을 Vector 로 형변환하여 리턴 if ( ReachedDestination( Location, Dest, NULL ) ) { Controller->bPreciseDestination = FALSE; // '정확한 이동'을 종료 Controller->eventReachedPreciseDestination(); // 종료 이벤트 호출 Velocity = FVector( 0.f ); Acceleration = FVector( 0.f ); } else if ( bForceMaxAccel ) { const FVector Dir = (Dest - Location).SafeNormal(); Acceleration = Dir * MaxAccel; Velocity = Dir * MaxSpeed; } else { Velocity = (Dest - Location) / DeltaTime; } ... } ... } // UnPawn.cpp UBOOL APawn::ReachedDestination( ... ) { ... return ReachThresholdTest( ... ); } UBOOL APawn::ReachThresholdTest( ... ) { ... FLOAT Threshold = ThresholdAdjust + CylinerComponent->CollisionRadius + DestinationOffset; // 도착으로 인정할 유효 반지름을 계산 ... if ( Dir.SizeSquared() > Threshold * Threshold ) return FALSE; ... // 적절하게 테스트하고 return TRUE; // 도착했다고 판정 }
상기 APawn::ReachThresholdTest 함수의 동입부에 도착지점으로부터의 허용 반지름을 계산하는 부분이 있는데, 이 값에 음수를 주어 좀 더 정확한 목표지점에 도달하게 할 수 있다.
언리얼엔진3에 기본적으로 제공되는 UTGame 을 기반으로 한 분석내용이다.
// DefaultInput.ini .Bindings=(Name="GBA_Jump",Command="Jump | Axis aUp Speed=+1.0 AbsoluteAxis=100")
// UTPlayerInput.uc exec function Jump() { ... Super.Jump(); ... } // PlayerInput.uc exec function Jump() { ... bPressedJump = true; }
// PlayerController.uc state PlayerWalking { ... function PlayerMove( float DeltaTime ) { ... // 지금 점프할 수 없는 상황이면 지연시킨다. if ( bPressedJump && Pawn.CannotJumpNow() ) { bSaveJump = true; bPressedJump = false; } ... } ... }
// UTPlayerController.uc function CheckJumpOrDuck() { ... else if ( bPressedJump ) { Pawn.DoJump( bUpdateing ); } ... } // PlayerController.uc function CheckJumpOrDuck() { if ( bPressedJump && (Pawn != None) ) { Pawn.DoJump( bUpdating ); } }
// UTPawn.uc function bool DoJump( bool bUpdating ) { ... if ( Physics ==PHYS_Spider ) Velocity = JumpZ * Floor; else if ( Physics == PHYS_Ladder ) Velocity.Z = 0 else if ( bIsWalking ) Velocity.Z = Default.JumpZ; else // 보통 이부분에 걸린다. Velocity.Z = JumpZ; ... }
Pawn 의 현재 Rotation 을 기준으로 좌우 일정 반경을 회전할 동안 발이 땅에 접지한 상태로 있는 기능을 지원한다. UnrealEngine3 에서는 이 기능을 Turn-In-Place 라고 지칭한다. 이를 수행하는 레이어는 UDKPawn 이다.
// UTPawn.uc simulated event PostInitAnimTree(SkeletalMeshComponent SkelComp) { ... // 허리쪽 Bone 을 제어하는 컨트롤 캐싱 (RootRot 이란 이름을 가진 SkelControlSingleBone 컨트롤) RootRotControl = SkelControlSingleBone( mesh.FindSkelControl( 'RootRot' ) ); ... // 조준 노드 캐싱 AimNode = AnimNodeAimOffset( mesh.FindAnimNode( 'AimNode' ) ); ... }
// UDKPawn.cpp void AUDKPawn::TickSpecial( FLOAT DeltaSeconds ) { ... // 현재 Aim pitch 와 yaw 를 얻는다. INT PawnAimPitch; if ( Controller ) { // 컨트롤러의 Pitch를 얻는다. PawnAimPitch = Controller->Rotation.Pitch; } else { // Pawn 의 Pitch를 얻는다. PawnAimPitch = Rotation.Pitch; if ( PawnAimPitch == 0 ) { PawnAimPitch = RemoteViewPitch << 8; } } // Pawn 의 최종 조준 Pitch PawnAimPitch = UnwindRot( PawnAimPitch ); INT PawnAimYaw = UnwindRot( Rotation.Yaw ); // Pawn 의 최종 조준 Yaw (가 될 값) INT AimYaw = 0; if ( Physics == PHYS_Walking && Velocity.Size() < KINDA_SMALL_NUMBER ) { // PawnAimYaw 는 손이 향하는 방향, RootYaw 는 발이 향하는 방향이라 생각하면 이해가 쉽다. INT CurrentAimYaw = UnwindRot( PawnAimYaw - RootYaw ); INT RootRot = 0; if ( CurrentAimYaw > MaxYawAim ) { RootRot = ( CurrentAimYaw - MaxYawAim ); } else if ( CurrentAimYaw < -MaxYawAim ) { RootRot = ( CurrentAimYaw - (-MaxYawAim) ); } RootYaw += RootRot; RootYawSpeed += ( (FLOAT)RootRot ) / DeltaSeconds; // 최종 손과 발의 offset. 이것이 곧 Aim 노드에 적용될 Yaw AimYaw = UnwindRot( PawnAimYaw - RootYaw ); } else { RootYaw = Rotation.Yaw; RootYawSpeed = 0.f; AimYaw = 0; } // 좌우 90 도 회전을 각각 -1, 1 로 매핑시킨 값으로 변환 if ( !bNoWeaponFiring ) { CurrentSkelAim.X = Clamp<FLOAT>( ( (FLOAT)AimYaw / 16384.f ), -1.f, 1.f ); CurrentSkelAim.Y = Clamp<FLOAT>( ( (FLOAT)PawnAimPitch / 16384.f ), -1.f, 1.f ); } // 허리를 Aim 의 반대쪽으로 회전. 즉, 손의 방향이 Pawn Rotation 과 일치하고 발의 방향을 보정하는 방식 if ( RootRotControl ) { RootRotControl->BoneRotation.Yaw = -AimYaw; } // Aim 업데이트 if ( AimNode ) { AimNode->Aim = CurrentSkelAim; } ... }
AnimTree 이해영상 (0) | 2016.02.29 |
---|---|
웹html5 에서 언리얼이 나왔네요 (0) | 2013.05.03 |
글루 코드 (=C++ 과 언리얼 스크립트 연결하기위한 작업코드) (0) | 2012.10.27 |
네이티브 패키지 목록에 패키지 추가하기 (0) | 2012.10.27 |
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 (0) | 2012.10.27 |
UnrealScript 를 사용하다 보면 Native 레이어와 상호 통신을 해야할 필요가 생긴다. 이 때 Script 의 내용을 Native 가 인식할 수 있도록 글루코드를 생성할 필요가 있는데 여기서는 이것에 대해 다뤄보도록 한다.
이어지는 설명을 이해하려면 스크립트 컴파일하는 법을 먼저 알아야 한다. 컴파일은 게임 실행파일을 이용한1) 콘솔명령어로 수행되며 거두절미하고 바로 명령어 때려본다.
// 이하 콘솔명령어이며, 실행파일은 UTGame.exe 라고 가정한다. UTGame.exe make // 릴리즈로 빌드 UTGame.exe make -full // 릴리즈로 풀 빌드 UTGame.exe make -debug // 디버그로 빌드 (스크립트를 디버깅하기 위해) UTGame.exe make -debug -full // 디버그로 풀 빌드
UnrealScript 에서 Native 코드를 생성하는 키워드는 아래와 같다.
// UTGame/Classes/UTWeapon.uc class UTWeapon extends UDKWeapon native // 이 키워드가 붙으면 Native 글루코드가 생성된다. ...
글루코드가 생성되는 파일명도 지정할 수 있다.
위와 같이 native 키워드 뒤에 아무것도 없다면 [패키지명]+Classes.h 에 글루코드가 생성된다. (위 예제와 같이) UTGame 의 경우 UTGameClasses.h 파일에 AUTWeapon 클래스에 대한 선언이 되어있는 것을 확인할 수 있다.
만약 native( MyWeapon ) 와 같이 괄호 안에 특정 명칭을 넣는다면 [패키지명]+[괄호內명]+Classes.h 에 글루코드가 생성된다. 위의 경우 UTGameMyWeaponClasses.h 에 해당된다.2)
위와같이 글루코드의 헤더가 생성되면 이제 정의(.cpp)를 하고 등록을 할 차례다.
스크립트 내에서 네이티브 함수를 지정하는 방법은 여럿 있지만 여기선 그 설명은 생략하고 클래스와 네이티브 함수를 정의하는 방법에 대해 설명하겠다. (위의 UTWeapon 을 예로들어 계속 설명)
먼저 적당한 .cpp 파일을 만든다. UTWeapon.cpp 라고 파일을 만든 후 그 안의 내용은 아래와 같아야 한다.
// UTWeapon.cpp #include "UTGame.h" #include "UTGameMyWeaponClasses.h" // UTWeapon이 선언된 헤더 IMPLEMENT_CLASS(AUTWeapon); // 클래스의 기본적인 기능들을 정의 UBOOL AUTWeapon::Tick( FLOAT DeltaTime, ELevelTick TickType ) { ... }
여기서 핵심은 IMPLEMENT_CLASS 이다. 이것은 엔진 내에서 필요한 기본기능 및 글루코드에 필요한 기능들을 자동으로 정의해주는 매크로이다.
나머지 함수들은 알아서 정의한다.
이제 엔진이 알 수 있도록 글루코드를 등록할 차례이다. 복잡한 설명은 생략하고 바로 코드 들어간다.
// UTGame.cpp ... #include "UTGameMyWeaponClasses.h" // 다른 헤더들을 보고 "여기다 싶은 곳" 에 모두 포함시킨다. ... void AutoInitializeRegistrantsUTGame( INT& Lookup ) { AUTO_INITIALIZE_REGISTRANTS_UTGAME; AUTO_INITIALIZE_REGISTRANTS_UTGAME_MYWEAPON; // 여기서 등록 ... } void AutoGenerateNamesUTGame() { ... #include "UTGameMyWeaponClasses.h" // 여기서 이름 (Name) 등록 ... } ...
웹html5 에서 언리얼이 나왔네요 (0) | 2013.05.03 |
---|---|
언리얼3 캐릭터 구조 (0) | 2012.10.27 |
네이티브 패키지 목록에 패키지 추가하기 (0) | 2012.10.27 |
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 (0) | 2012.10.27 |
언리얼 디렉토리 / 프로젝트 구조 (0) | 2012.10.27 |
네이티브 패키지 목록에 패키지 추가하기
void appGetGameNativeScriptPackageNames(TArray<FString>& PackageNames, UBOOL bCanIncludeEditorOnlyPackages) { ... PackageNames.AddItem(TEXT("AutoGenExample")); }
언리얼3 캐릭터 구조 (0) | 2012.10.27 |
---|---|
글루 코드 (=C++ 과 언리얼 스크립트 연결하기위한 작업코드) (0) | 2012.10.27 |
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 (0) | 2012.10.27 |
언리얼 디렉토리 / 프로젝트 구조 (0) | 2012.10.27 |
머티리얼 에디터 사용 안내서 (0) | 2012.10.27 |
http://donggas90.blog.me/100141378528
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 [출처] [UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선|작성자 나가놀자
이번 포스트는 프로그래밍을 UDK로 처음 접하시는 분을 위해 적어 볼까 합니다.
먼저 UDK(언리얼 엔진)는 언리얼 스크립트라는 자신들만의 언어를 사용합니다.
언리얼 스크립트를 위한 비쥬얼 스튜디오 플러그인 엔프린지와
스크립팅을 시작하는 방법에 대해서는 아래 포스트에서 확인하세요.
http://donggas90.blog.me/100127258591
그리고 짠 언리얼 스크립트를 어떻게 게임과 에디터에 적용하는 지에 대한 정보는
아래 포스트를 확인하세요.
http://donggas90.blog.me/100134298424
위 두 포스트의 내용을 숙지하셨다는 가정 하에 시작하겠습니다.
1. 클래스 오버라이드 - extends
class HUD extends Actor;
HUD.uc의 일부입니다.
class는 앞으로 클래스를 선언할 것임을 나타 냅니다.
HUD는 이 클래스의 이름을 나타내며, uc파일과 이름이 같아야 합니다.
extends는 이 클래스가 어느 클래스를 오버라이드하고 있는지 가르킵니다.
Actor는 extends가 가르키고 있는 클래스 입니다.
;는 여기까지 클래스 선언이 끝났음을 알리고 있습니다.
오버라이드란 extends가 가르키는 클래스의 모든 내용을 전부 그대로 복사한다는 뜻입니다.
복사'당한' 클래스는 부모 클래스가 되고, 복사'받은' 클래스는 자식 클래스가 됩니다.
비록 그 내용이 여기 일일이 적혀 있지는 않지만 적혀져 있는 것으로 간주하는 것입니다.
그리고 또 다른 의미로 '덮어쓴다'는 의미가 있습니다.
부모 클래스에 있는 것을 다시 새로 쓸 때, 오버라이드했다고 합니다.
언리얼 스크립트 파일들을 하나하나 열어 보시면
모든 클래스들이 계속해서 다른 클래스를 오버라이드하고 있음을 알 수 있습니다.
그리고 그 가장 위에는 Object클래스가 있고 그 바로 밑에는 Actor가 있습니다.
즉, 모든 언리얼 스크립트의 시조는 Object입니다.
2. 변수 선언 - var
var string A;
var int B;
var(Variable) float C;
var bool D;
var는 앞으로 변수를 선언할 것임을 나타냅니다.
이것들은 이 변수가 어떤 타입인지 나타 냅니다.
string이란 문자열을 말합니다.
int란 정수를 말합니다.
float이란 실수 말합니다.
bool이란 참, 거짓 논리자입니다.
A는 이 변수의 이름이 A임을 나타냅니다.
;는 여기까지 변수의 선언이 끝났음을 나타냅니다.
(Variable)란 언리얼 에디터에서 클래스 프로퍼티 창에
Variable이라는 카테고리를 만들고 이 변수를 표시하라는 의미이며
반드시 필요하지는 않습니다.
이런 방법으로 변수에 이름을 붙여 줄 수 있습니다.
변수의 종류는 더 많지만 일단 이 정도만 알아도 대부분의 데이터 주무르기가 가능합니다.
변수에 값을 넣는 방법은 아래와 같습니다.
A = "string";
B = 123;
C = 1.f;
이것들은 값을 넣어줄 대상입니다.
값을 받을 대상은 항상 왼쪽에 위치합니다.
=는 왼쪽에 있는 것에 오른쪽 것을 넣는다는 뜻입니다.
일반적으로 사용하는 의미와는 다릅니다.
넣을 대상과 넣어 줄것의 타입이 일치해야 함을 유의하세요.
"string"은 문자열 string을 나타냅니다. " 가 양쪽으로 사용됐음에 유의하세요.
123는 정수 123(백이십삼)을 나타냅니다.
별다른 기호를 사용하지 않아도 됩니다.
1.f는 실수 1(일)을 나타냅니다.
언리얼 스크립트에서는 정수와 실수를 구분하기 위해
실수에는 특수한 표기 방법을 사용합니다.
값의 끝에 f를 붙이는 것이 그것입니다.
만약 소수점 자리가 없다면 .(온점)과 함께 f를 붙입니다.
;는 여기까지 명령이 끝났음을 알립니다.
* 심화
var array<int> A;
A[0] = 1;
A[100] = 2;
배열은 이름은 같은데 다른 번호를 가진 여러 값의 무리입니다.
3. 기본값 - DefaultProperties
DefaultProperties
{A = 123
}
DefaultProperties{ 는 앞으로 기본값 영역이 시작될 것임을 의미합니다.
A의 기본값이 123임을 의미합니다. 여기서 세미콜론(;)은 선택 사항입니다.
} 는 기본값 영역이 끝났음을 의미합니다.
이렇게 하면 A는 UDK가 시작되면 항상 123일 것입니다.
이런식으로 기본값이 필요할 경우 정의해 줄 수 있습니다.
* 심화
런타임이 초기화될 때마다 이 영역의 기본값이 로드됩니다.
4. 함수 - function
함수를 선언하는 방법은 아래와 같습니다.
function EXAMPLE()
{
A = 123;
}
function{}는 함수 영역을 나타냅니다.
EXAMPLE는 함수의 이름을 나타냅니다.
()는 함수에 사용할 인수(파라미터)의 목록입니다.
자세한 내용은 아래에...
A = 123;는 함수에서 할 것입니다.
이 함수가 집행되면 A = 123;을 할 것입니다.
하지만 재밌는 점은 이렇게 만들더라도 실질적으로 작동은 되지 않습니다.
왜냐면 언리얼 엔진이 이 함수를 호출하지 않기 때문입니다.
따라서 이 함수를 작동하게 하려면
언리얼 엔진이 호출하는 함수 안에서 이 함수를 호출해야 합니다.
simulated event Tick(float DeltaTime)
{EXAMPLE();
}
대표적인 예로 Tick이라는 이벤트가 있습니다.
이 이벤트는 일정 시간 간격으로 계속 이 영역을 집행합니다.
그리고 그 시간 간격은 DeltaTime에 실수 타입으로 저장합니다.
이벤트는 함수와는 성격이 다른데, 이벤트는 모두 언리얼 엔진이 특정 상황이나 조건에서 호출하는 것입니다.
임의로 프로그래머가 호출할 수 없습니다.
function EXAMPLE( int B )
{
A = 25;
A = B;
}
EXAMPLE( 12 );
A는 12
위 스크립트는 인수 사용의 대표적인 예입니다.
인수는 함수를 선언할 당시에는 타입만 지정돼 있습니다.
뭔가 값은 없습니다.
하지만 다른 곳에서 사용되었을 때 그 값을 지정해 주고
함수 내용을 집행하게 됩니다.
5. 함수의 변수화 - return
앞에서 함수에 대해 알아 봤습니다.
그런데 함수에서 뭔가 계산한 뒤 바로 무엇인가 얻고 싶을 경우가 생길 겁니다.
따로 변수의 도움 없이 말입니다.
function int EXAMPLE()
{
A = 123;
return A;
}
B = EXAMPLE();
B는 123
int로 이 함수는 정수 결과를 출력할 것임을 나타냅니다.
return A; 로 이 함수의 출력 값은 A값임을 알립니다.
이렇게 하면 함수 내에서 계산한 값을 또 다른 변수에 넣어주고
다시 받아서 B에 할당하는 번거로운 과정이 생략됩니다.
* 심화
function Ex ( out int A )
{
A = B + C ;
return;
}
값을 직접 받지 않고 간접으로 받을 수도 있습니다.
6. 부모 함수 집행 - super
오버라이드는 덮어쓴다는 개념이 있다고 말씀 드렸습니다.
덮어쓰게 되면 이전의 것은 없어지고 새로운 것만 작성되게 됩니다.
함수 역시 오버라이드할 수 있습니다.
그런데 함수를 통째로 다시 쓰지 않고 나만의 새로운 코드만 추가하고 싶을 때가 있습니다.
이럴 때 쓰는 것이 super코드입니다.
function EXAMPLE()
{
super(Actor).Example();
A = 123;
}
super.Example();로 가장 가까운 부모에 있는 Example()함수를 집행하도록 합니다.
(Actor)는 선택 사항인데 어느 클래스에 있는 부모 함수를 집행할지 지정합니다.
부모 엑터 클래스의 함수가 집행된 뒤 A=123; 집행되게 됩니다.
게임의 기본 틀을 구성하는 함수여서 마음데로 수정이 어렵거나
언리얼 엔진이 호출하는 이벤트에 내용을 덧붙이고 싶을 때 아주 유용합니다.
7. 조건부 - if
뭔가 조건을 제시하고 그게 맞다면 실행되게 하고 싶을 때가 많을 겁니다.
이럴 때 쓸 수 있는 것이 if 코드 입니다.
if ( A == 1 )
{
B = 1;
}
else if ( A == 2 )
{
B = 2;
}
else
{
B = 3;
}
이것들은 한 묶음입니다. if와 else는 처음과 끝에 각각 하나만 올 수 있고
else if 는 원하는 만큼 쓸 수 있습니다.
이것이 참이면 그 아래 영역을 집행합니다.
즉, A가 1이면 B는 1이될 겁니다.
여기에 사용되는 논리 판단 기호에 유의하세요.
==는 서로 같은지 비교합니다.
!=는 서로 다른지 비교합니다.
이외에 <, >, <=, >=는 일반적인 수학에서 쓰는 그데로입니다.
if는 조건 부분을 무조건 참인지 거짓인지 판단합니다.
else if 는 if의 조건이 거짓일 경우 참인지 거짓인지 판단합니다.
else 는 if 와 else if 의 조건 모두가 거짓이면 집행합니다.
위에서 적어드린데로 프로그램이 조건을 검사하게 됩니다.
조건을 검사하려면 CPU가 계산을 해야하고
그러면 리소스가 소모 됩니다.
게임을 최적화하려면 if를 남발하지 말고 적절히 else를 이용해야 합니다.
조건을 검사할 때, 꼭 한 가지가 아니라 여러 가지의 조건을 쓰는 경우가 있습니다.
이럴 때 수학에서는 AND, OR라는 표현을 쓰는데
프로그래밍에도 이런 개념이 존재합니다.
if ( A == 1 && B == 3 )
{
B = 1;
}
else if ( A == 2 || B == 1 )
{
B = 2;
}
&&(쉬프트 + 숫자 7)는 AND라는 의미입니다.
||(쉬프트 + \)는 OR라는 의미입니다.
덧붙여 이런 것도 가능합니다.
var bool A;
A = true;
B = 0;
if ( A )
{
B = 1;
}
B는 1
조건 부분이 참(true)인지 거짓(false)인지 검사하는 것이기 때문에
조건 그 자체가 참인 경우도 집행됩니다.
* 심화
switch(A)
{
case(1) :
//A가 1이면 집행합니다.
break;//으로 집행의 끝을 알립니다.
default:
//A와 관계 없이 집행합니다.
break;
}
8. 변수의 무리 - struct
변수들을 여럿 선언하고 보면
이따금씩 서로 관련이 있는 변수인 경우가 있습니다.
예를 들면 게임 케릭터의 능력치입니다.
힘 변수를 선언하고 지능 변수를 각각 선언한다면
관리하기가 쉽지 않겠죠.
왜냐면 케릭터가 하나가 아닐테니까요.
그리고 새로운 능력치를 추가하려면 대부분의 코드들을 뜯어 고쳐야할 것입니다.
이럴 때 필요한 것이 바로 구조체, struct 코드입니다.
struct CharacterInfo
{
var int Strength;
var int Intelligence;
};
struct로 여기부터 구조체를 선언하겠다고 알립니다.
CharacterInfo는 이 구조체의 이름입니다.
var int Strength; 구조체에 포함된 변수입니다.
; 구조체를 선언한 뒤 붙이는 것을 잊지 마세요.
그런데 이걸 어떻게 사용하는 걸까요.
<strong></strong>var CharacterInfo A;
구조체를 선언하면 마치 변수 타입인 것처럼 사용할 수 있습니다.
A라는 이름을 가진 CharacterInfo 구조체를 만들었습니다.
그렇다면 구조체에 포함된 변수는 어떻게 쓰는 걸까요.
A.Strength = 123;
A.Intelligence = 321;
구조체의 이름 뒤에 온점을 붙여 그 속에 포함된 변수를 부를 수 있습니다.
다른 이름의 같은 구조체가 있습니다.
하나의 값을 다른 하나에 넣어 주기 위해
포함된 변수를 하나하나씩 불러 할당하고 있습니다.
var CharacterInfo A;
var CharacterInfo B;
A.Strength = B.Strength;
A.Intelligence = B.Intelligence;
물론 이런 방법을 쓸 수도 있지만
굳이 일일이 값들을 불러오지 않아도 됩니다.
var CharacterInfo A;
var CharacterInfo B;
A = B;
9. 메모 - /* **/
프로그래밍 언어는 우리가 쓰는 말과는 전혀 다릅니다.
그리고 복잡한 함수나 추상적인 이름의 변수를 사용해야 하는 경우가 자주 발생합니다.
이를 설명하기 위해 메모, 주석을 달게 됩니다.
/** 수 두 개를 더하는 함수 */
function int Add( int A, int B )
{
C = A + B;
return C;
}
/** */이 부호가 사용된 문자들은 모두 무시됩니다.
비쥬얼 스튜디오와 엔프린지를 사용하는 경우
Add에 마우스를 올리면 툴팁이 나오는데
두번째 줄에 주석의 내용이 표시됩니다.
* 심화
/** 수 두 개를 더하는 함수
* @param A 더할 첫번째 값
* @param B 더할 두번째 값 */
<strong></strong>function int Add( int A, int B )
{
C = A + B;
return C;
}
파라미터마다 개별적인 주석을 달 수도 있습니다.
10. 디버깅 - `log
버그는 예상치 못한 곳에서 주로 발생합니다.
버그를 해결하는 작업을 디버깅이라 하는데
디버깅을 위해서 사용되는 코드가 있습니다.
바로 `log코드입니다.
`는 1의 왼쪽에 있는 키입니다.
쉬프트를 누르면 ~이 되는 키죠.
`log("test");
이런 식으로 사용합니다.
괄호안의 내용은 문자열이므로 쌍따옴표를 사용해야 합니다.
그런데 이 글자는 어디서 확인하는 것일까요.
UDK에서 게임 태스트를 시작합니다.
그리고 커멘드라인에 SHOWLOG를 입력하면
윈도우 cmd같은 검은 창이 표시됩니다.
만약 로그가 정상적으로 집행됐다면
그곳에 하얀색 글씨로 test란 글자가 나타날 겁니다.
이런 단순 문자열 로그는 특정 함수나 조건부가 제대로 동작하는지
파악할 때 유용합니다.
만약 변수 값이 궁금할 때는 어떻게 할까요.
그럴 때는 이렇게 합니다.
var int A;
`log("variable is"@A);
@는 문자열을 공백 하나를 넣고 합칠 때 사용합니다.
만약 A가 123이면
로그창에는 이렇게 나타날 것입니다.
variable is 123
로그 코드는 디버깅에 효과적이기는 하지만
로그 또한 집행하려면 CPU가 계산을 해야 합니다.
따라서 디버깅이 끝나면 로그 코드는 지워 주세요.
[출처] [UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선|작성자 나가놀자
글루 코드 (=C++ 과 언리얼 스크립트 연결하기위한 작업코드) (0) | 2012.10.27 |
---|---|
네이티브 패키지 목록에 패키지 추가하기 (0) | 2012.10.27 |
언리얼 디렉토리 / 프로젝트 구조 (0) | 2012.10.27 |
머티리얼 에디터 사용 안내서 (0) | 2012.10.27 |
언리얼 스크립트 (0) | 2012.10.27 |
퍼포스에서 UE3 코드 베이스 싱크를 마치고나면 퍼포스 디포 루트 폴더에 UnrealEngine3 라는 디렉토리가 생길 것입니다.
"UnrealEngine3" 폴더 안에는 다음 디렉토리가 있습니다:
UE3 는 여러 프로젝트로 나뉘는데, 시스템을 분리하고 크로스-플랫폼 호환성을 쉽게 유지할 수 있도록 하기 위해서입니다.
기본 구조는 (최하위 레벨 코드에서 최상위 레벨까지) 다음과 같습니다:
참고로 이 프로젝트 모두는 서로의 위에 빌드됩니다. 즉 Engine 은 Core 에 의존, Editor 는 Engine 에 의존, UnrealED 는 Editor 에 의존합니다.
다른 플랫폼 전용 프로젝트도 여럿 있는데, D3DDrv (렌더링의 Direct 3D 구현), WinDrv (뷰포트 생성, 이벤트 처리 등의 윈도우 구현) 등입니다. 한 프로젝트가 크로스-플랫폼 인터페이스의 특정 플랫폼 전용 구현이라면 보통 Drv 같은 접미사가 붙게 마련입니다.
네이티브 패키지 목록에 패키지 추가하기 (0) | 2012.10.27 |
---|---|
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 (0) | 2012.10.27 |
머티리얼 에디터 사용 안내서 (0) | 2012.10.27 |
언리얼 스크립트 (0) | 2012.10.27 |
스크립트 세팅 (0) | 2012.10.27 |
Red links require licensee log in.
언리얼 에디터의 머티리얼 에디터 사용법에 대한 설명서입니다. 다양한 머티리얼 표현식의 역할에 대한 설명은 Materials Compendium KR (머티리얼 개론) 페이지를, 새 머티리얼 표현식 제작법에 대해서는 Creating Material Expressions KR 페이지를 참고해 주시기 바랍니다.
모바일 플랫폼용 머티리얼 제작에 대한 정보는 Mobile Material Reference KR 페이지를 참고해 주시기 바랍니다.
머티리얼 에디터는 머티리얼 애셋을 더블클릭하거나 콘텐츠 브라우저의 머티리얼 애셋의 우클릭 맥락 메뉴를 통해 열 수 있습니다. 어느 방법으로나 해당 머티리얼이 머티리얼 에디터에서 편집 가능한 상태로 열리게 됩니다.
머티리얼 에디터는 여섯 구역으로 구성되어 있습니다:
다음은 각 툴바 버튼에 대한 설명으로, 툴바 표시상 왼쪽에서 오른쪽 순입니다.
아이콘 | 설명 |
---|---|
메인 패널의 좌상단 코너에 베이스 머티리얼 노드가 오도록 머티리얼 표현식 그래프를 옮깁니다. | |
머티리얼 미리보기 패널의 배경 그리드를 토글합니다. | |
머티리얼 미리보기에 사용할 표준 모양을 선택합니다. | |
콘텐츠 브라우저를 열고 머티리얼을 선택합니다. | |
콘텐츠 브라우저에서 스태틱 메시를 선택하고 이 버튼을 누르면 선택된 메시를 미리보기 메시로 지정합니다. | |
머티리얼에 연결되지 않은 머티리얼 표현식 노드를 지웁니다. | |
아무것에도 연결되지 않은 머티리얼 표현식 단자를 표시/숨깁니다. | |
켜면 미리보기 메시의 머티리얼을 실시간으로 업데이트합니다. 에디터 성능을 위해서라면 이 옵션은 끄십시오. | |
켜면 각 머티리얼 표션식 노드의 머티리얼을 실시간으로 업데이트합니다. 에디터 성능을 위해서라면 이 옵션은 끄십시오. | |
이 버튼은 머티리얼 표현식의 bRealtimePreview (실시간 미리보기) 옵션에 대한 글로벌 토글입니다. 켜면 모든 하위표현식의 셰이더는 노드가 추가, 삭제, 연결, 연결 해제, 프로퍼티 값 변경시마다 컴파일됩니다. 에디터 성능을 위해서라면 이 옵션은 끄십시오. 표현식 미리보기 부분을 참고하십시오. | |
머티리얼 에디터에서 변경한 사항을 원본 머티리얼과 월드에 해당 머티리얼을 사용한 곳에 적용합니다. | |
표현식 그래프 패널에 머티리얼 통계를 표시/숨깁니다. | |
현재 선택된 표현식에 대한 HLSL 소스 표시를 토글합니다. | |
텍스트 일부가 일치하는 표현식을 검색할 수 있습니다. 자세한 것은 머티리얼 표현식 검색 부분을 참고하십시오. |
머티리얼 미리보기 패널은 편집중인 머티리얼을 메시에 적용하여 보여줍니다. 마우스 왼쪽 버튼으로 끌면 메시의 회전, 가운데 버튼으로는 패닝, 오른쪽 버튼으로는 줌입니다. 라이트의 방향은 L키를 누르고 좌클릭 드래깅으로 회전시킬 수 있습니다.
관련 툴바 콘트롤(모양 버튼, "미리보기 메시 선택" 콤보 박스, "선택된 스태틱 메시 사용" 버튼 등)을 사용하여 미리보기 메시를 바꿀 수 있스니다. 미리보기 메시는 머티리얼에 저장되므로 다음 번에 머티리얼 에디터에서 열 때도 같은 메시에서 미리볼 수 있습니다.
이 패널에는 선택된 머티리얼 표현식에 대한 프로퍼티창이 담깁니다. 선택된 표현식이 없으면 편집중인 머티리얼 자체의 프로퍼티가 표시됩니다.
모든 머티리얼 프로퍼티에 대한 설명은 Materials Overview KR 페이지를 확인하시기 바랍니다.
이 패널에는 "드래그 앤 드롭" 방식으로 머티리얼에 놓을 수 있는 머티리얼 표현식이 나열됩니다. 머티리얼 표현식 노드를 새로 놓으려면, 표현식에 좌클릭 > 그래프 패널로 드래그 > 원하는 위치에 드롭 하면 됩니다.
이 패널에는 "드래그 앤 드롭" 방식으로 머티리얼에 놓을 수 있는 머티리얼 함수 목록이 표시됩니다. 새로운 머티리얼 함수를 놓으려면 놓으려는 함수 종류에 왼클릭한 후, 커서를 그래프 패널 위로 끌어 놓습니다. 적합한 머티리얼 함수가 할당된 MaterialFunctionCall 노드가 새로이 놓입니다.
이 패널에는 이 머티리얼에 속하는 모든 머티리얼 표현식의 그래프가 포함되어 있습니다. 머티리얼에 사용된 셰이더 인스트럭션 수와 함께 컴파일러 에러 도 좌상단 구석에 표시됩니다. 인스트럭션 수가 적을 수록 머티리얼 비용도 싸집니다. 기본 머티리얼 노드에 연결되지 않은 머티리얼 표현식 노드는 머티리얼의 인스트럭션 수(비용)에 계산되지 않습니다.
머티리얼에는 디폴트로 하나의 머티리얼 노드가 포함되어 있습니다. 이 노드에는 여러가지 입력이 달려 있는데, 각각은 머티리얼의 다양한 면에 관련되어 있으며, 여기에 다른 표현식이나 표현식 망이 연결될 수 있습니다.
머티리얼 노드의 여러가지 입력에 대한 설명은 Materials Overview KR 페이지를 확인해 주시기 바랍니다.
머티리얼 에디터의 콘트롤은 언리얼 에디터 내의 다른 툴 콘트롤과 일반적으로는 비슷합니다. 머티리얼 표현식 그래프는 여타 오브젝트 에디터들과 같은 식으로 조작할 수 있으며, 머티리얼 미리보기 메시도 다른 메시 툴과 마찬가지로 방향을 맞출 수 있습니다.
콘트롤 | 액션 |
---|---|
배경에 좌/우클릭-드래그 | 머티리얼 표현식 그래프 패닝 |
마우스 휠 스크롤 | 줌 인/아웃 |
좌+우클릭 드래그 | 줌 인/아웃 |
오브젝트에 좌클릭 | 표현식/코멘트 선택 |
오브젝트에 Ctrl + 좌클릭 | 표현식/코멘트 선택 토글 |
Ctrl + 좌클릭 + 드래그 | 현재 선택/코멘트 옮기기 |
Ctrl + Alt + 좌클릭 + 드래그 | 박스 선택 |
Ctrl + Alt + Shift + 드래그 | 박스 선택(하여 현재 선택에 추가) |
단자에 좌클릭 + 드래그 | (하다가 단자나 변수에서 떼면) 연결 생성 |
연결에 좌클릭 + 드래그 | (하다가 같은 종류의 단자나 변수에서 떼면) 연결 이동 |
단자에 Shift + 좌클릭 | 단자를 마크합니다. 단자가 마킹된 상태로 다른 단자에 Shift + 클릭하면 두 단자를 연결합니다. 원거리 연결이 매우 빨라집니다. |
배경에 우클릭 | 새 표현식 메뉴를 띄웁니다. |
오브젝트에 우클릭 | 오브젝트 메뉴를 띄웁니다. |
단자에 우클릭 | 오브젝트 메뉴를 띄웁니다. |
단자에 Alt + 좌클릭 | 단자의 무든 연결을 끊습니다. |
(미리보기 패널에서) L 키 + 드래그 | 미리보기 라이트 방향을 돌립니다. |
콘트롤 | 액션 |
---|---|
Ctrl + C | 선택된 표현식 복사 |
Ctrl + V | 붙여넣기 |
Ctrl + W | 선택된 오브젝트 복제 |
Ctrl + Y | 다시하기 |
Ctrl + Z | 되돌리기 |
Delete | 선택된 오브젝트 삭제 |
스페이스바 | 모든 머티리얼 표현식 미리보기 강제 업데이트 |
Enter | (적용 클릭과 동일) |
자주 사용되는 머티리얼 표현식 유형은 핫키로 놓을 수 있습니다. 단축키를 누르고 노드의 놓을 곳에 좌클릭하면 됩니다. 핫키는 다음과 같습니다:
핫키 | 표현식 |
---|---|
A | Add 더하기 |
B | BumpOffset 범프 오프셋 |
C | ComponentMask 컴포넌트 마스크 |
D | Divide 나누기 |
E | Power 파워(승) |
F | MaterialFunctionCall 머티리얼 함수 호출 |
I | If 만약 |
L | LinearInterpolate 선형 보간 |
M | Multiply 곱하기 |
N | Normalize 정규화 |
O | OneMinus 1빼기 |
P | Panner 패너 |
R | ReflectionVector 리플렉션 벡터 |
S | ScalarParameter 스칼라 파라미터 |
T | TextureSample 텍스처 샘플 |
U | TexCoord 텍스처 좌표 |
V | VectorParameter 벡터 파라미터 |
1 | Constant 상수 |
2 | Constant2Vector 상수 2벡터 |
3 | Constant3Vector 상수 3벡터 |
4 | Constant4Vector 상수 4벡터 |
Shift + C | Comment 코멘트 |
코멘트는 머티리얼의 역할을 설명하기에 좋으며, 타인은 물론 자신에게도 복잡한 머티리얼 그래프를 이해하는 데 도움이 됩니다. 코멘트는 관련된 노드 위에 나타나는 파랑 텍스트로 표시됩니다. 코멘트는 줌과 별도로 표시되므로 복잡한 머티리얼 그래프를 쉽게 옮겨다닐 수 있습니다.
머티리얼 표현식 노드는 해당 노드의 "Desc" 프로퍼티에 텍스트를 입력하여 개별적으로 코멘트를 달 수 있습니다.
여러 노드를 선택하고 'Shift + C'를 쳐서 노드 그룹에 그룹 코멘트를 할당할 수도 있습니다. "새 코멘트" 대화창에 코멘트 텍스트를 입력하고 OK를 칩니다. 선택된 노드가 코멘트 프레임에 묶이게 됩니다. 그룹 코멘트의 노드는 그룹 코멘트 텍스트를 드래그하여 움직일 수 있습니다. 코멘트 프레임의 우하단 코너에 있는 검정 삼각형을 드래그하여 프레임 크기도 조절할 수 있습니다. 그룹 코멘트 내의 노드는 전부 프레임과 함께 움직이므로, 기존 프레임 크기를 조절하여 새 노드를 포함시킬 수도 있습니다.
코멘트를 선택하고 프로퍼티창을 통해 "Text" 프로퍼티를 변경하면 코멘트의 이름을 바꿀 수 있습니다.
머티리얼 에디터의 노드에는 좌상단 구석에 작은 박스가 있습니다. 이 박스는 노드의 bRealtimePreview (실시간 미리보기) 프로퍼티를 나타내며: 노랑은 켜짐, 검정은 꺼짐 입니다.
머티리얼이 어떤 식으로든 (생성, 삭제, 연결, 프로퍼티 변경 등등) 바뀔 때마다, bRealtimePreview (실시간 미리보기) 프로퍼티가 켜진 노드는 전부 그 셰이더를 다시 컴파일합니다. 머티리얼 미리보기를 해당 노드에서 최신으로 유지하려면 이런 리컴파일이 필요합니다. 그러나 이런 중간 셰이더 리컴파일은 시간을 잡아먹을 수 있는데, 특히나 머티리얼에 노드가 많을 경우에는 더합니다. 그래서 작업 방해를 피하기 위해서, TextureSample 을 제외한 모든 노드 유형에 대해서는 기본적으로 bRealtimePreview (실시간 미리보기)가 꺼져 있습니다.
스페이스바를 쳐서 강제로 모든 미리보기를 업데이트시킬 수도 있습니다. 아무튼 가급적 많은 노드의 bRealtimePreview (실시간 미리보기)를 끄고, 변경 확인시마다 스페이스를 치는 방식을 통해 반복처리 속도를 높일 수 있습니다.
노드의 좌상단 구석 박스를 클릭하거나, 노드를 선택한 상태에서 프로퍼티 창을 통해 노드의 bRealtimePreview (실시간 미리보기)를 토글할 수 있습니다.
"Toggle Expression Realtime Preview" (표현식 실시간 미리보기 토글) 버튼으로 모든 노드 글로벌 토글이 가능합니다.
머티리얼 망에 무언가 변화가 있을 때마다 머티리얼을 컴파일해 줘야 합니다. 망 내 어느 표현식에 필요한 입력에 연결된 것이 없거나 잘못된 데이터형이 주어지는 경우, 컴파일 에러가 납니다. 이 에러는 그래프 패널 에 표시됩니다.
컴파일러 에러에 제공되는 문제 발생 표현식의 종류와 에러에 대한 설명을 통해, 그 문제가 무엇인지 알 수 있습니다. 게다가 에러가 있는 노드는 그래프 패널에 빨갛게 강조되어 보이므로, 쉽게 찾아 필요한 연결을 메꿀 수 있습니다.
머티리얼 에디터의 검색 기능을 활용하면 머티리얼 망 내 (코멘트 포함) 어느 노드든, 그 설명이나 각 표현식 종류에 한정된 여러 프로퍼티에 지정한 텍스트가 포함되는 것을 빠르게 찾아 줍니다. 노드에 식별 키워드를 추가해 놓고 나중에 찾아가거나 하기가 쉬워지니, 정처없이 표현식 바다를 허우적대지 않아도 됩니다.
검색 박스에 키워드 전체나 부분을 입력하면 그래프 패널에 있는 표현식의 프로퍼티를 대상으로 검색합니다. 일치되는 것이 현재 활성 결과라면 녹색으로 강조됩니다.
주: 검색은 대소문자를 구별합니다.
검색 대상 프로퍼티 값은 다음과 같습니다:
표현식 종류 | 검색되는 프로퍼티 |
---|---|
All | Desc |
Texture Sample | Texture |
Parameters | ParamName |
Comment | Text |
FontSample | Font |
MaterialFunctionCall | MaterialFunction |
검색에 NAME=
스위치를 사용하면 특정 표현식 종류만을 대상으로 검색할 수도 있습니다. 예를 들어
모든 텍스처 샘플러를 찾으려면:
NAME=texture
와 버튼을 사용하여 검색 결과 표현식 전부를 돌아가며 볼 수 있습니다.
애초부터든, 나 버튼으로든, 선택된 일치 결과가 보이지 않는 경우, 그래프 패널의 뷰에 나타납니다.
검색을 비우려면 버튼을 누르면 됩니다.
네이티브 패키지 목록에 패키지 추가하기 (0) | 2012.10.27 |
---|---|
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 (0) | 2012.10.27 |
언리얼 디렉토리 / 프로젝트 구조 (0) | 2012.10.27 |
언리얼 스크립트 (0) | 2012.10.27 |
스크립트 세팅 (0) | 2012.10.27 |
var
대신 var()
구문을 사용해서 지정됩니다. 자세한 설명.
for
, while
, break
,
switch
, if
등 C 와 Java 의 표준 키워드가 모두 지원됩니다. UnrealScript
에서도 괄호와 세미콜론이 C, C++ 및 Java 에서처럼 사용됩니다.
Demon
, demON
과 demon
이
모두 같은 것을 가리킵니다.
객체의 계층구조
Object 는 Unreal 내 모든 객체의 부모 클래스입니다. 모든 것들이 Object 에서 파생되기 때문에, 어디에서나 Object 클래스 내에 있는 모든 함수들에 접근할 수 있습니다. Object 는 abstract 기초 클래스이므로, 그 자체는 전혀 유용한 일을 하지 않습니다. 모든 기능성은 Texture (텍스처 맵), TextBuffer (텍스트의 덩어리), 그리고 Class (다른 객체의 클래스를 설명하는) 등의 하위클래스에 의해 제공됩니다.
Actor (extends Object) 는 Unreal 내 모든 자립형 게임 객체들의 부모 클래스입니다. Actor 클래스는 액터가 돌아다니고, 다른 액터들과 상호작용하고, 환경에 영향을 주고, 게임에 관련된 다른 유용한 일들을 하는데 필요한 모든 기능성을 함유하고 있습니다.
Pawn (extends Actor) 은 고급 수준의 AI 및 플레이어 콘트롤을 감당하는 Unreal 내 모든 창조물 및 플레이어들의 부모 클래스입니다.
Class (extends Object) 는 객체의 클래스를 설명하는 특별한 종류의 객체입니다. 클래스가 객체이고, 객체가 어떤 객체를 설명한다는 것이 처음에는 혼동스러워 보일지 모릅니다. 그러나 이 개념은 논리적인 것이며 여러분이 Class 객체를 다루어야 할 경우가 많습니다. 예를 들어 UnrealScript 에서 새 액터를 스폰할 때, 그 새 액터의 클래스를 Class 객체로 지정할 수 있습니다.
UnrealScript 를 사용해서 어떠한 Object 클래스에 대한 코드라도 작성할 수 있지만, 99% 의 경우 Actor 에서 파생된 클래스에 대한 코드를 쓰게 될 것입니다. 유용한 UnrealScript 기능성의 대부분은 게임과 관련된 것이며 액터들을 취급하는 것입니다.
각 스크립트는 정확히 하나의 클래스에 해당되며, 스크립트는 클래스, 클래스의 부모 그리고 그 클래스와 관계있는 모든 추가 정보를 선언하는 것으로 시작됩니다. 가장 간단한 클래스의 형태는 다음과 같습니다:
class MyClass extends MyParentClass;
여기서 저는 "MyParentClass" 의 기능성을 물려받는, "MyClass" 라는 이름의 새 클래스를 선언하고 있습니다. 또, 이 클래스는 "MyPackage" 라는 이름의 패키지 안에 들어 있습니다.
각 클래스는 그 부모 클래스의 변수, 함수 그리고 상태를 모두 물려 받습니다. 클래스는 그 다음에 새 변수의 선언 추가, 새 함수 추가 (또는 기존 함수 오버라이드) 그리고 새 상태를 추가 (또는 기존의 상태에 기능 추가) 할 수 있습니다.
UnrealScript 에서 클래스 디자인의 전형적인 접근방식은, 필요한 모든 기능성을 가지고 있는 기존의 클래스 (예: 괴물들의 베이스 클래스인 Pawn 클래스) 를 확장하는 새 클래스(예: Minotaur 괴물) 을 만드는 것입니다. 이 방식을 따르면 절대로 시간과 노력을 낭비할 필요가 없습니다 – 커스터마이즈 할 필요가 없는 기존의 기능을 모두 유지하면서 커스터마이즈 하고 싶은 새 기능을 간단히 추가할 수 있습니다. 이 접근방식은 Unreal 에서 AI를 구현하는데 특히 효과적입니다. 내장된 AI 시스템이 여러분 고유의 창조물에 대해 구성 요소로서 사용할 수 있는, 놀라운 양의 기본적인 기능성을 제공하기 때문입니다.
클래스 선언은 그 클래스에 영향을 미치는 다수의 선택적 지정자를 취할 수 있습니다:
DependsOn
줄에서 콤마로 분리하거나, 각 클래스마다
별도의 DependsOn
줄을 사용하여 여러 개의 종속 클래스를 지정할 수 있습니다.
NotTransient
키워드를 사용하여 이 플래그를 오버라이드 할 수 있습니다.
Transient
키워드를 무효로 합니다.
Config
NotEditInlineNew
키워드를 사용하여 이 플래그를 오버라이드 할 수
EditInlineNew
키워드를 무효로 합니다.
EditInlineNew
를 사용하는 부모 클래스가 없는 경우에는 효과가 없습니다.
NotPlaceable
키워드를 사용하여 이 플래그를 오버라이드 할 수 있습니다.
Placeable
키워드를 무효로 합니다. 이 클래스가 UnrealEd 에서
HideCategories
키워드를 무효로 합니다.
DontCollapseCategories
키워드를 사용하여
CollapseCatogories
키워드를 무효로 합니다.
Outer
객체로서 지정해야
합니다.
Inherits
줄에서
콤마로
nherits
줄을 사용하여 복수의 기본 클래스를 지정할
Implements
줄에서 콤마로 분리하거나, 각 인터페이스 클래스마다 별도의
Implements
줄을 사용하여
네이티브 패키지 목록에 패키지 추가하기 (0) | 2012.10.27 |
---|---|
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 (0) | 2012.10.27 |
언리얼 디렉토리 / 프로젝트 구조 (0) | 2012.10.27 |
머티리얼 에디터 사용 안내서 (0) | 2012.10.27 |
스크립트 세팅 (0) | 2012.10.27 |
udk.exe 설정시 win32/UDK.exe 로 설정
DefaultEngine.ini
ModEditPackages=MyMod 를
ModEditPackages=MyGame 로 변경
.uc 페이지 유니코드 949 로 변경
프로젝트 만들때 솔루션용 디렉터리 만들기 체크 해제
http://blog.naver.com/kiru81?Redirect=Log&logNo=60106380729
nFringe 라는 프로그램을 다운받는디.
http://wiki.pixelminegames.com/index.php?title=Tools:nFringe#First_time_installation
nFringe 를 설치한 후, VC++ 2005 나 2008 을 실행하고 새 프로젝트를 만들게 되면, 프로젝트 형식에 UnrealScript 라는 항목을 볼 수 있다.
VC++ 2005 에서 할 경우에는 한 가지를 더 설치해주어야 한다. 바로 ProjectAggregator2 이다. 위의 링크된 위키사이트에서 다운받을 수 있다. VC++ 2008 로는 테스트해보지 않았지만, 아마 필요없는 것 같다.
프로그램 셋팅 및 코드 작성은 아래 동영상을 참고
http://dl.deathtouchstudios.com/videotutorials/UnrealScriptSeries/
1. UnrealEngine 3 License Project 템플릿으로 프로젝트 생성
2. 프로젝트 속성 > General 탭에 Game > Target Game 을 UnrealEngine 3 Mod 로 바꿈.
Script Compiler > UCC Path 를 Binaries\UDK.exe 로 설정
Script Compiler > Reference Source Path 를 Development\Src 로 설정
3. Build 탭에 Script Outputs > Manually set UCC output directory 를 체크하고 UDKGame\Script 를 경로로 잡아준다.
4. Debug 탭에 StartAction
프로젝트 셋팅 시 Debug 탭에서 Load map at startup 이라는 항목이 있는데, 여기에는 자신이 만들어 저장한 맵의 이름을 저장해야 한다.
아래는 test.udk 로 3인칭 시점의 카메라를 테스트 했던 맵을 지정하고, 실행한 결과이다.
근데 로그를 출력해보는 중에 warning 이 발생.. MyGame.MyGameInfo 를 찾지 못한다는 거였다..
\UDK\UDK-2010-04\UDKGame\Script 에 MyGame.u 파일이 생성되지 않는 것과 관계가 있는걸까?
[출처] [UDK] 스크립트 프로그래밍
네이티브 패키지 목록에 패키지 추가하기 (0) | 2012.10.27 |
---|---|
[UDK] 프로그래밍 초보를 위한 언리얼 스크립트 코드 10선 (0) | 2012.10.27 |
언리얼 디렉토리 / 프로젝트 구조 (0) | 2012.10.27 |
머티리얼 에디터 사용 안내서 (0) | 2012.10.27 |
언리얼 스크립트 (0) | 2012.10.27 |
오우거 vc 2010 로 빌드시 ' _ITERATOR_DEBUG_LEVEL'에 대해 불일치가 검색되었습니다. ' 와 같은 릴리즈에서 에러날경우
C:/OGRE3D/ogre_src_v1-7-4/Dependencies 에 들어가 버전에 맞는 .sin 파일의 릴리즈 디버그를 빌드 한 후
CMake 에서
OGRE_DEPENDENCIES_DIR C:/OGRE3D/ogre_src_v1-7-4/Dependencies
를 추가 해주고
원래 있던 소스파일에다 덮어 configure, generate 한 후
ALL_BUILD에서 일괄빌드가 아닌
개별적으로 디버깅, 릴리즈모드로 각각 빌드 하면 된다
[출처] 오우거(OGRE3D) 1.7.1 소스 설치 (VS2005 버전)|작성자 리치왕
http://blog.naver.com/codeknight2/110094046557
오우거(OGRE3D) 1.7.1 소스 설치 (VS2005 버전) OGRE3D 2010/09/17 04:16 |
현재의 내 컴퓨터 작업환경이 WindowsXP (서비스팩3), 비주얼스튜디오 2005 이므로, 이 환경을 기준으로 작성하였습니다. 혹 비주얼 스튜디오의 버전이나 윈도우를 업글하게 되면 해당 버전에 대해서도 새로 포스트 하도록 하겠습니다.
1. 설치전 준비사항
오우거를 설치하기 전에 먼저 컴퓨터에 DirectX SDK가 설치되어야 합니다.
DirectX SDK 최신버전인 June 2010 버전은 여기에서 다운 받을 수 있습니다.
그런데, 이 다운로드 페이지에 다음과 같은 내용이 있군요.
The June 2010 DirectX SDK includes support for Visual Studio 2010. The DirectX SDK will continue to support Visual Studio 2008 as well. However, Visual Studio 2005 will no longer be supported.
즉, June 2010 버전은 비주얼 스튜디오 2010을 지원하고 2008도 여전히 지원하지만, 비주얼 스튜디오 2005는 더이상 지원하지 않는다는 것입니다. 비주얼 스튜디오 2005를 사용하는 환경에서 DirectX June 2010을 링크하고 컴파일하려고 하면 에러가 발생할 것입니다. 깡패같은 놈들이 아닐 수 없습니다. 다이렉트 엑스 최신버전 사용하고 싶으면 새 컴파일러를 사라, 뭐 이런 소리이군요.
비주얼 스튜디오를 상위버전으로 새로 설치할 생각이 없고 계속해서 2005 버전을 사용할 계획이라면, 다음의 링크에서 제가 알고있는 DirectX의 2009년 마지막 릴리즈인 August 2009 버전을 받아서 설치하시면 되겠습니다.
만일, 컴퓨터에 이미 다른 버전의 DirectX 9.0c SDK가 설치되어 있고 이걸 최근의 버전으로 패치할 생각이 없다면, DirectX SDK 설치는 그냥 건너뛰어도 상관없겠습니다.
다음으로, 비주얼 스튜디오 2005를 사용하고 계시는 경우, 비주얼 스튜디오 2005 서비스팩1을 설치해야 합니다.
그 이유는 오우거 엔진의 비주얼 스튜디오 2005 버전의 소스가 서비스팩1 버전을 기준으로 작성되어 있기 때문에 서비스팩 없이 컴파일을 시도하면 여러가지 에러를 만나게 되고, 서비스팩을 설치하지 않으면 해결이 불가능하기 때문이지요.
비주얼 스튜디오 2005 서비스팩1은 여기에서 다운로드 받을 수 있습니다.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=bb4a75ab-e2d4-4c96-b39d-37baf6b5b1dc
만일 사용중인 운영체제가 Windows Vista, Windows Server 2008, Windows7 이라면 다음의 서비스팩을 설치하는 것이 좋습니다.
http://www.microsoft.com/downloads/en/details.aspx?FamilyID=90E2942D-3AD1-4873-A2EE-4ACC0AACE5B6
비주얼 스튜디오 2005가 아닌 다른 컴파일러를 사용하고 있는 경우라면, 혹시 다른 요구사항이 있는지 확인하기 위해 오우거 홈페이지에 가서 인스톨 가이드를 읽어 보시길 바랍니다.
위에 제시한 다운로드 URL들은 2010년 9월 16일 현재 유효한 경로로써, 구버전 SDK나 서비스팩의 다운로드 경로를 마이크로소프트가 수시로 변경하기 때문에 향후 어느 시점에서 더이상 유효한 경로가 아닐 수도 있습니다. 이런 경우에는 마이크로소프트 다운로드 페이지에서 검색하여 새 경로를 찾거나, 운영체제 또는 컴파일러의 업그레이드를 추천합니다.
2. 오우거의 다운로드 및 설치
위의 과정을 모두 마쳤다면 이제 오우거 소스를 다운로드해야 겠지요.
오우거 1.7.1 버전의 자동압축 파일을 여기에서 다운로드할 수 있습니다.
http://www.ogre3d.org/download/source
다운로드 받은 파일을 실행하면 다음과 같이 압축을 해제할 경로를 묻습니다.
제 경우는 압축을 해제할 경로를 단순히 C드라이브로 지정했는데, 그 이유는 압축을 해제하면 지정된 경로에 ogre_src_v1-7-1 이라는 서브 폴더가 생성되고, 소스들이 그 폴더안에 존재하게 되기 때문입니다.
압축을 해제할 경로로 다른 폴더 이름을 지정해주게 되면 나중에 컴파일하고 나서 생성된 라이브러리들은 상당히 여러 단계 아래의 폴더에 존재하게되고, 나중에 응용프로그램(즉, 게임과 같이 오우거를 사용하는 프로그램)을 만들 때 비주얼 스튜디오에 오우거 라이브러리 경로를 지정해야 하는데, 이 경로 이름의 길이가 매우 길어질 것이기 때문입니다.
저는 개인적으로 긴 경로이름을 싫어하므로 이렇게 한 것인데, 머 임시 폴더를 지정해주고 압축을 해제한 후에 서브폴더들을 상위 경로로 옮기거나, 혹은 긴 경로를 그대로 사용하거나, 또는 오우거 컴파일을 완료한 후에 출력된 라이브러리들과 헤더들 만을 별도의 경로로 옮겨 사용해도 됩니다.
이건 각자의 취향에 따라 선택하면 되고 꼭 제가 한 그대로 하실 필요는 없습니다.
압축을 해제할 경로를 지정했다면, Extract를 클릭하여 압축 해제를 시작합니다.
압축 해제가 완료된 후에 지정했던 경로에 가보면 위에 보시는 것과 같이 ogre_src_v1-7-1 이라는 폴더가 새로 생성된 것이 보입니다. 그 폴더 안에는 오우거 엔진, 데모들, 도구들의 소스와 도큐먼트들이 들어 있습니다.
BuildingOgre.txt 라는 텍스트 파일이 보이는 군요. 사실 이 파일에 설치와 관련된 모든 내용이 설명되어 있습니다. 단지 (영국식)영어로 되어 있다는 것이 단점이지요.
위에 압축 해제 이미지를 보시면 제 컴퓨터에 이미 오우거 1.6.3 버전이 설치되어 있는 것이 보이실 것입니다. 제 경우와 같이 이전 버전의 오우거가 이미 설치되어 있다고 하더라도 아무 상관없이 1.7.1 버전을 새로 설치하실 수 있습니다. 다만, 폴더 이름만 다른 이름을 사용하시면 되는 것이지요.
이제, Boost 라이브러리를 설치해야 합니다.
Boost는 오우거를 컴파일하기 위해 반드시 필요한 것은 아닙니다. 그러나 오우거의 지형(Terrain) 라이브러리의 페이징 기술에서 리소스의 백그라운드 로딩에 Boost 라이브러리를 사용할 경우 좀 더 빠른 성능을 보여주기 때문에 설치하는 것이 좋겠죠.
물론, 이 라이브러리가 없어도 동작합니다.
다음의 경로에서 Boost 라이브러리 인스톨러를 다운로드 할 수 있습니다.
이 포스트를 쓰는 현재 최신버전은 1.44이지만, 오우거 1.7.1은 1.42버전을 사용하여 작성되었으므로, 1.42 버전을 다운 받도록 합니다. (1.44 버전은 오우거를 컴파일할 때 에러가 발생합니다)
http://www.boostpro.com/download/
다운받은 인스톨러를 실행하면 위와 같이 Boost라이브러리를 어디에서 다운 받을 것인가를 묻습니다. 적당한 다운로드 서버를 지정해주고 Next를 클릭합니다.
Boost 라이브러리는 몇 가지 형태로 미리 컴파일된 라이브러리인데 설치하길 원하는 형식을 선택하라는 창이 뜹니다.
좌측 컴파일러 항목에서는 오우거 컴파일에 사용할 컴파일러의 버전을 선택해 줍니다.
우측 라이브러리 형식에서는 원하는 형식의 라이브러리를 선택합니다. 제 경우에는 최소 설치를 위해 멀티스레드 DLL만을 선택해 주었습니다.
다음으로 설치할 라이브러리 항목들을 선택하는 창이 뜹니다.
오우거의 지형엔진이 사용하는 라이브러리는 Boost DateTime과 Boost Thread 입니다. 이 두 항목은 반드시 체크해 주어야 합니다.
선택한 항목의 서브 항목에서 필요하지 않은 버전의 컴파일러는 체크를 해제해 줍니다.
선택을 완료하고 Next를 클릭하면 다운로드 및 설치가 시작됩니다.
Boost는 상당히 쓸만한 라이브러리이므로, 꼭 오우거가 아니라도 다른 프로그램을 만드는 작업에서 이 라이브러리를 사용할 수도 있으므로 죄다 설치해 주는 것이 좋지만 많은 항목을 선택할 수록 다운로드 시간이 더 오래 걸린다는 것도 염두에 두시기 바랍니다.
저는 중국에 거주하고 있는데 인터넷 속도가 엉망이라 다운 받는데 돌아버릴거 같군요.
Boost의 인스톨이 완료되었다면 나중에 cmake가 Boost를 제대로 찾을 수 있도록 환경변수를 등록해 주어야 합니다.
[내 컴퓨터 -> 속성 -> 고급 -> 환경 변수 -> 시스템 변수 -> 새로 만들기] 를 선택해서 다음과 같이 BOOST_ROOT 시스템 환경 변수를 추가해 줍니다.
변수 값에는 여러분이 설치한 경로를 입력해 주어야 합니다. 인스톨 과정에서 특별히 설치경로를 변경하지 않았다면 위의 이미지와 같은 경로일 것입니다.
같은 방법으로 아래 두 개의 변수를 더 등록해 줍니다.
BOOST_INCLUDEDIR -> C:\Program Files\boost\boost_1_42
BOOST_LIBRARYDIR -> C:\Program Files\boost\boost_1_42\lib
이제 의존성 패키지를 설치해야 합니다.
앞서 오우거의 소스코드를 다운로드 했던 페이지(http://www.ogre3d.org/download/source)에서 Microsoft Visual C++ Dependencies Package를 다운로드 합니다.
OgreDependencies_MSVC_20100501.zip이라는 파일을 다운로드하게 될 것입니다.
이 파일을 앞서 오우거 소스를 압축 해제해서 생성된 폴더 안에 압축을 해제합니다. 이 파일의 압축을 해제하면 Dependencies라는 폴더가 생성되는데, 이 폴더는 오우거 소스 폴더의 OgreMain 폴더와 같은 위치에 존재해야 합니다.
즉, 저의 경우에는 C:\ogre_src_v1-7-1 라는 폴더에 압축을 해제하게 되는 거죠.
압축을 해제하고 난 후 오우거 소스 폴더의 모습입니다. 보시는 바와 같이 C:\ogre_src_v1-7-1 폴더 안에 Dependencies라는 폴더가 새로이 생성된 것을 알 수 있습니다.
Dependencies 폴더 안에 src라는 폴더가 있을 것인데 그 폴더 안에 들어가 보면 지원되는 비주얼 스튜디오 각 버전에 따른 솔루션 파일 (*.sln)이 있을 것입니다.
자신이 사용하는 버전에 맞는 이름을 가진 솔루션 파일을 비주얼 스튜디오를 사용해서 엽니다.
이 솔루션에는 14개의 프로젝트가 포함되어 있을 것입니다. 이 솔루션을 디버그 모드와 릴리즈 모드에서 각각 컴파일합니다.
디버그 모드와 릴리즈 모드를 따로 한번씩 컴파일하는 것 보다는 일괄 빌드 기능을 사용하는 것이 편리합니다.
비주얼 스튜디오에서 [빌드 -> 일괄 빌드]를 선택합니다.
일괄 빌드 대화상자에서 솔루션에 포함된 모든 프로젝트에 대해서 Win32 디버그와 Win32 릴리즈를 선택해주고 빌드를 클릭합니다.
컴파일이 시작되고, 약간의 시간이 소요될 것입니다.
컴파일이 완료되고 난 후에 위와 같이 실패가 없으면 성공입니다.
이제 cmake를 이용하여 소스로부터 오우거 프로젝트들을 생성해야 합니다.
cmake는 크로스 플랫폼 빌드 시스템인데, 정확히 말하면 빌드 환경 설정 프로그램이라고 할 수 있습니다. 소스 코드들과 개발 도구 등의 시스템 환경들에 맞춰 가장 적합한 빌드 환경을 만들어 주는 프로그램이라고 할 수 있습니다.
마치 리눅스에서 make 명령을 실행하기 전에 configure를 사용하여 컴파일 환경을 수집하는 것과 같은 역할을 한다고 보면 되겠습니다.
우리는 윈도우즈 환경에서 작업하고 있으므로, cmake-gui (윈도우즈 환경에 맞춰진 GUI를 지원하는 버전)를 사용하면 됩니다.
cmake-gui는 여기에서 다운로드 할 수 있습니다.
특별히 cmake의 소스를 살펴보고 싶은게 아니라면 RESOURCES->Download 에서 Binary distributions -> Windows (Win32 Installer)라고 되어 있는 인스톨러를 다운로드 하도록 합니다.
cmake는 최신버전을 다운로드 하도록 합니다.
다운로드가 완료되면 인스톨러를 실행하여 원하는 위치에 설치하고, 설치가 완료되면 cmake-gui를 실행합니다.
실행하면 위와 같은 창이 열리는데, Where is the source code: 항목에 우리가 앞서 오우거 소스를 압축해제한 폴더 (즉, OgreMain 폴더가 존재하는 폴더)를 지정해주고, Where to build binaries: 항목에 cmake가 빌드 시스템을 출력하기를 원하는 경로를 지정해 줍니다.
cmake가 만드는 결과물은 컴파일이 완료된 오우거 엔진이 아니라, 비주얼 스튜디오로 컴파일할 수 있는 오우거 엔진의 소스 프로젝트라는 것을 기억하시기 바랍니다. cmake로 빌드 환경을 만들고 나서 최종적으로 비주얼 스튜디오를 이용해서 다시 한번 빌드해주어야 비로서 오우거 엔진이 만들어진다는 것이지요.
제 경우는 앞서 오우거 소스를 압축해제한 폴더 안에 VS2005_Build라는 새 폴더를 만들어서 지정해 주었습니다.
경로 설정이 완료되었으면, 아래쪽에 있는 Configure 버튼을 클릭합니다.
사용하려는 컴파일러 버전을 묻는 대화상자가 나타나는데, 위의 콤보 박스에서 자신이 사용하는 버전의 컴파일러를 선택해 준 후에 Finish를 클릭합니다.
그러면 cmake는 시스템 정보와 소스의 정보를 수집하기 시작할 것입니다.
정보 수집이 완료되면 아래쪽 안내 창에 Configuring done 이라는 메시지가 나타나면서 프로젝트의 설정 옵션들이 보여집니다.
이 설정들은 대부분 디폴트 상태로 그대로 사용해도 상관이 없습니다. 각 설정 옵션들의 의미를 알고 싶다면 오우거 홈페이지 wiki에서 인스톨 관련 문서를 참조하시기 바랍니다.
제 경우에는 디폴트 설정에서 맨 아래에 OGRE_INSTALL_DOCS, OGRE_INSTALL_PDB, OGRE_INSTALL_SAMPLES를 추가로 체크해 주었습니다.
설정이 완료되었다면 Configure 버튼을 한번 더 클릭하여 설정을 저장합니다. 저장이 완료되고 나면 옆에 있는 Generate 버튼이 활성화 될 것인데, 이를 클릭합니다.
이제 cmake는 우리를 위해서 비주얼 스튜디오 2005로 컴파일할 수 있는 오우거 엔진의 프로젝트들과 솔루션을 만들어 줄 것입니다.
아무런 오류없이 Generating done 이라는 메시지가 나오면 완료된 것입니다.
cmake가 빌드 환경 만들기를 완료하고 난 후, 우리가 cmake에게 지정해 주었던 출력 폴더로 가보면 빌드에 필요한 모든 소스들과 솔루션 파일이 생성된 것을 볼 수 있을 것입니다.
이 폴더 안에 OGRE.sln 솔루션 파일이 있을 것인데, 이것을 비주얼 스튜디오로 엽니다.
솔루션을 열면, 51개의 프로젝트가 포함되어 있을 것인데, 이 중에 ALL_BUILD라는 프로젝트가 보일 것입니다. 이것은 cmake가 우리를 위해 만들어준 프로젝트로, 이 프로젝트만 빌드하면 솔루션의 모든 프로젝트를 빌드해주게 됩니다.
이 역시 디버그 모드, 릴리즈 모드로 각각 컴파일 해야 하므로, 일괄 빌드 기능을 이용하도록 합니다.
[빌드 -> 일괄 빌드]를 선택하고, 일괄 빌드 대화상자에서 ALL_BUILD 프로젝트에 대해서만 Debug|Win32, Release|Win32 항목을 체크해주고 빌드를 클릭하여 빌드를 시작합니다.
이 작업은 시간이 꽤 걸릴 것입니다. 커피한잔 마시면서 담배나 한대 피우며 빌드가 완료되기를 기다립시다.
아마도 문제없이 컴파일이 완료되었을 것입니다. 혹시 실패가 하나라도 발생한 다면 빌드 과정을 다시 한번 수행해 줍니다.
컴파일이 완료되고 나면 솔루션이 있는 경로에 lib 라는 폴더가 새로이 생성된 것이 보일 것입니다. 이 폴더에는 오우거 엔진의 lib 파일들(여러분이 오우거를 사용하는 응용프로그램을 만들때 링크해주어야 하는 파일들)이 추가되었을 것이며, bin 폴더에는 오우거 엔진의 각종 dll 들이 추가되었을 것입니다.
이제, 오우거 엔진이 제대로 컴파일 되었는지 확인 해 봅시다.
솔루션에 포함된 프로젝트들 중에서 SampleBrowser 프로젝트를 시작 프로젝트로 설정합니다.
솔루션 탐색기에서 SampleBrowser 프로젝트를 우클릭하고 시작 프로젝트로 설정을 클릭하여 시작 프로젝트로 지정해준 뒤, [디버그 -> 디버그 시작] 또는 툴바의 플레이 버튼을 클릭하여 실행시킵니다.
오우거의 설정 대화상자가 나타날 것입니다. Rendering Subsystem: 은 Direct3D9 Rendering Subsystem으로 지정해주고 Full Screen: 을 No로 설정한 뒤 OK를 클릭합니다.
오, 마이 갇 !
오우거 1.7.1 엔진의 중대한 버그가 발견되는 순간입니다!
오우거가 설정 내용을 저장하기를 시도하려고 할 때 위와 같은 예외가 발생하는 경우가 있습니다.
이 예외의 내용으로 검색을 해보니, 다양한 운영체제에서 동일한 예외를 만나는 사람들이 꽤 있는 것 같습니다.
포럼에서 사람들이 하는 이야기로는, 오우거 응용프로그램을 처음 시작할 때 나타난 설정 대화상자에서 선택한 (우리가 조금 전 설정 대화상자에서 선택한 옵션들) 내용을 오우거는 사용자의 홈 디렉토리, 즉, 제 경우에는 "C:\Documents and Settings\리치왕\My Documents\Ogre\Cthugha\ogre.cfg" 이런 경로에 저장하려고 시도하는데, 이 경로에 영문이 아닌 문자가 들어갈 때에 문제가 발생한다고 합니다.
그러나, 제가 디버깅해 본 바에 의하면 위의 경로가 정확하게 Root 객체에게 전달되는 것을 확인했습니다. 더우기, 회사에 있는 제 컴퓨터에서는 실행에 아무런 문제가 없었습니다. 또 집에서 사용중인 이 컴퓨터는 1.6.3 버전을 사용할 때에는 아무런 문제가 없었습니다!
오우거의 Root 객체가 설정 파일의 경로를 어떻게 결정하는지 디버깅을 해보니, SampleBrowser의 createRoot라는 함수에서 Root 객체의 인스턴스를 생성하면서 생성자에게 경로명을 넘겨주고 있었습니다.
위의 코드는 SampleBrowser의 SampleContext.h 파일의 474번째 라인입니다. 위의 이미지에서는 주석처리된 부분이 원래 코드이고, 바로 아래는 제가 테스트를 위해 경로명을 변경하여 하드코딩으로 대체한 코드입니다.
일단 이렇게 처리한 후에 SampleBrowser를 다시 컴파일하고 실행하니 정상적으로 실행이 되는 군요.
SampleBrowser가 실행된 모습입니다.
일단 오우거 엔진의 컴파일은 문제없이 정상적으로 이루어졌다는 것을 확인 할 수 있습니다.
위의 예외 문제의 정확한 원인을 진단하기 위해서는 좀 더 디버깅을 해 봐야 할 것 같습니다.
이제, 다 되었습니다. 오우거 설치를 마무리 지을 시간입니다.
다시 비주얼 스튜디오의 솔루션 탐색기를 보면 INSTALL 이라는 이름의 프로젝트가 보일 것입니다. 이 프로젝트를 빌드해주면 우리가 cmake에서 인스톨하기로 선택한 모든 항목들이 sdk라는 새 폴더가 생성되면서 이 폴더로 인스톨이 진행됩니다.
일괄 빌드 대화상자를 열고, INSTALL 프로젝트에 대해서만 Debug|Win32, Release|Win32 항목을 체크해주고 빌드합니다.
INSTALL 프로젝트를 모두 빌드하고 나면, 솔루션 폴더 아래에 sdk라는 폴더가 새로이 생성된 것이 보일 것입니다. 이 폴더 안의 bin 폴더에는 실제의 오우거 엔진 파일들 즉, DLL 파일들과 CFG 파일들이 인스톨되어 있고, lib 폴더에는 우리가 앞으로 만들게 될 프로그램이 링크할 수 있는 lib 파일들이, 그리고 include 폴더에는 프로그램이 포함할 수 있는 헤더 파일들이 인스톨되어 있습니다.
즉, 우리가 오우거를 이용하여 프로그램을 작성할 때, 프로젝트에 추가 포함 디렉터리로 이 include 폴더를, 추가 라이브러리 디렉터리로 이 lib 폴더를 지정해 주어야 한다는 것이지요.
media 폴더에는 데모 프로그램들이 사용하는 각종 리소스들(메시 파일들, 텍스처 파일들 등)이 들어 있습니다.
Docs 폴더에는 오우거 매뉴얼과 API 레퍼런스와 같은 문서가 들어 있습니다. 오우거를 공부하면서 참조하면 좋을 것입니다.
이 sdk 폴더는 현재 이 경로 그대로 두고 사용해도 되고, sdk 폴더를 따로 다른 경로로 이동시키고 폴더 이름을 자신이 알기 쉽도록 변경해서 사용해도 됩니다. 각자의 취향에 따라 선택하시면 될 것이나, 중요한 것은 여러분이 응용 프로그램 프로젝트를 만들 때 인클루드 디렉토리와 라이브러리 디렉토리의 경로를 지정해 주어야 그 프로젝트가 제대로 컴파일 될 것이란 점이지요.
자, 이제 오우거 1.7.1의 설치가 완료되었습니다. 수고하셨습니다 ^^
게임브리오 세팅법 (0) | 2013.11.08 |
---|---|
C - 콘솔에서 Box2D(물리엔진)를 쉽게 사용하는 방법! (VS2010 기준) (0) | 2012.12.25 |
OGRE 캐릭터 신밧드 데모영상 (0) | 2012.10.27 |
OGRE with CrystaX , android(안드로이드) (0) | 2012.10.27 |
어딘가에서 오우거 조사한 pdf 파일 (0) | 2012.10.27 |
This is an early draft of the new Character sample for the OGRE SDK. It features Sinbad the OGRE with third-person game-style controls. It's not done yet, and there are still a few things to add. The ribbon trails are kinda glitchy, but they are just there for fun right now. Probably won't keep them. :)
C - 콘솔에서 Box2D(물리엔진)를 쉽게 사용하는 방법! (VS2010 기준) (0) | 2012.12.25 |
---|---|
오우거 빌드 자세한 설명 (0) | 2012.10.27 |
OGRE with CrystaX , android(안드로이드) (0) | 2012.10.27 |
어딘가에서 오우거 조사한 pdf 파일 (0) | 2012.10.27 |
gamekit 아이폰으로 개발&오우거렌더링엔진 (0) | 2012.10.27 |
about 7 months ago
You are looking up 10 and not 9 pixels! Coordinate 0.0 intersects two pixels and so you take full advantage of linear interpolation! Else you need to offset by half a pixel!
Where is the benefit of the usual easier way? Lookup at coordinates [-4, -2, 0, 2, 4] to get 10 pixel range and then apply the weighting of each two texels and calculate final value?
about 6 months ago
Hi Daniel,
This article was incredibly clear and informative. Thank you for taking the time to put it together. I’m still learning this stuff, so this was very helpful.
I wrote up a small Python script based on this post to generate the gaussian texture lookups, optimized for linear sampling. Maybe it can be useful to someone else too:
https://gist.github.com/2332010
Thanks again
about 1 month ago
Hey Daniel,
nice article, the connection with Pascal’s triangle is indeed very interesting.
I would like to add two things. First, maybe it would be good to add that Gaussian has a unit integral, since it’s probably one of the reasons the filtering works (in the continuous case at least). The 3D plot should also reflect this (meaning that such a wide Gaussian certainly wouldn’t reach a value of 1.6). This is of course just minor.
The second thing that struck me was:
“A Gaussian function with a distribution of 2σ is equivalent with the product of two Gaussian functions with a distribution of σ.”
This is just my opinion, but I think this doesn’t hold. First, I think such product won’t be a 2σ-Gaussian (properly normalized and all). Second, a repeated application of any filter corresponds to applying their *convolution*, which differs from product. And, convolution of two Gaussians with StD=σ produces a Gaussian with StD=Sqrt(2*σ^2)=σ*Sqrt(2).
I think what you meant was that product of 2 polynoms with order n (n-th row of Pascal triangle) gives you a polynom of order 2n? That should be true.
What do you think?
Oskar
about 1 month ago
You can do a 9-tap gaussian using only 2 texture fetches by sampling at one of corner of the center pixel in the first pass (f.x with an xy-offset of -0.5,0.5 ) and then sample the opposite corner in the next pass ( xy-offset of 0.5,-0.5 )
This results in the standard
121
242
121
9-tap gaussian.
You can do the same gaussian in a single pass if you sample at all 4 corners (4 texture fetches)
You can do a nice and very fast 7-tap blur in a single pass if you sample two opposite corners with an offset of 1/3 away from the center.
This results in a
121
282
121
convolution kernel.
If you calculate the strength of the blur by subtracting from the center pixel you can then scale the result so that it closely matches the regular 9-tap gaussian.
I also do a 17-tap blur using only 5 fetches in a single pass by sampling the center pixel and then offsets (0.4,-1.2) , (-1.2,-0.4) , (1.2,0.4) and (-0.4,1.2).
I then adjust the strength of it to more closely match the 9-tap gaussian.