반응형


원본 : http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm


선 클리핑 알고리즘인 Cohen–Sutherland algorithm.


선을 그릴때 화면을 벗어났는지 체크하고 벗어났으면 화면 끝으로 점의 위치를 맞춰주는 알고리즘.


소프트웨어 렌더링을 할때 자주 사용된다.


Wiki에 정리가 잘 되어 있어서 올린다.





Cohen–Sutherland algorithm

From Wikipedia, the free encyclopedia

In computer graphics, the Cohen–Sutherland algorithm (named after Danny Cohen and Ivan Sutherland) is a line clipping algorithm. The algorithm divides a 2D space into 9 regions, of which only the middle part (viewport) is visible.

In 1967, flight simulation work by Danny Cohen led to the development of the Cohen–Sutherland computer graphics two and three dimensional line clipping algorithms, created with Ivan Sutherland.[1]

Contents

  [hide

[edit]The algorithm

The algorithm includes, excludes or partially includes the line based on where:

  • Both endpoints are in the viewport region (bitwise OR of endpoints == 0): trivial accept.
  • Both endpoints are on the same non-visible region (bitwise AND of endpoints != 0): trivial reject.
  • Both endpoints are in different regions: In case of this non trivial situation the algorithm finds one of the two points that is outside the viewport region (there will be at least one point outside). The intersection of the outpoint and extended viewport border is then calculated (i.e. with the parametric equation for the line) and this new point replaces the outpoint. The algorithm repeats until a trivial accept or reject occurs.

The numbers in the figure below are called outcodes. An outcode is computed for each of the two points in the line. The first bit is set to 1 if the point is above the viewport. The bits in the outcode represent: Top, Bottom, Right, Left. For example the outcode 1010 represents a point that is top-right of the viewport. Note that the outcodes for endpoints must be recalculated on each iteration after the clipping occurs.


100110001010
000100000010
010101000110

The Cohen–Sutherland Algorithm can be used only on a rectangular clipping area . For other convex polygon clipping windows, use Cyrus–Beck Algorithm.

[edit]Example C/C++ implementation

typedef int OutCode;
 
const int INSIDE = 0; // 0000
const int LEFT = 1;   // 0001
const int RIGHT = 2;  // 0010
const int BOTTOM = 4; // 0100
const int TOP = 8;    // 1000
 
// Compute the bit code for a point (x, y) using the clip rectangle
// bounded diagonally by (xmin, ymin), and (xmax, ymax)
 
// ASSUME THAT xmax , xmin , ymax and ymin are global constants.
 
OutCode ComputeOutCode(double x, double y)
{
        OutCode code;
 
        code = INSIDE;          // initialised as being inside of clip window
 
        if (x < xmin)           // to the left of clip window
                code |= LEFT;
        else if (x > xmax)      // to the right of clip window
                code |= RIGHT;
        if (y < ymin)           // below the clip window
                code |= BOTTOM;
        else if (y > ymax)      // above the clip window
                code |= TOP;
 
        return code;
}
 
// Cohen–Sutherland clipping algorithm clips a line from
// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with 
// diagonal from (xmin, ymin) to (xmax, ymax).
void CohenSutherlandLineClipAndDraw(double x0, double y0, double x1, double y1)
{
        // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
        OutCode outcode0 = ComputeOutCode(x0, y0);
        OutCode outcode1 = ComputeOutCode(x1, y1);
        bool accept = false;
 
        while (true) {
                if (!(outcode0 | outcode1)) { // Bitwise OR is 0. Trivially accept and get out of loop
                        accept = true;
                        break;
                } else if (outcode0 & outcode1) { // Bitwise AND is not 0. Trivially reject and get out of loop
                        break;
                } else {
                        // failed both tests, so calculate the line segment to clip
                        // from an outside point to an intersection with clip edge
                        double x, y;
 
                        // At least one endpoint is outside the clip rectangle; pick it.
                        OutCode outcodeOut = outcode0? outcode0 : outcode1;
 
                        // Now find the intersection point;
                        // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
                        if (outcodeOut & TOP) {           // point is above the clip rectangle
                                x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
                                y = ymax;
                        } else if (outcodeOut & BOTTOM) { // point is below the clip rectangle
                                x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
                                y = ymin;
                        } else if (outcodeOut & RIGHT) {  // point is to the right of clip rectangle
                                y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
                                x = xmax;
                        } else if (outcodeOut & LEFT) {   // point is to the left of clip rectangle
                                y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
                                x = xmin;
                        }
 
                        //NOTE:*****************************************************************************************
 
                        /* if you follow this algorithm exactly(at least for c#), then you will fall into an infinite loop 
                        in case a line crosses more than two segments. to avoid that problem, leave out the last else
                        if(outcodeOut & LEFT) and just make it else*/
 
                        //**********************************************************************************************
 
                        // Now we move outside point to intersection point to clip
                        // and get ready for next pass.
                        if (outcodeOut == outcode0) {
                                x0 = x;
                                y0 = y;
                                outcode0 = ComputeOutCode(x0, y0);
                        } else {
                                x1 = x;
                                y1 = y;
                                outcode1 = ComputeOutCode(x1, y1);
                        }
                }
        }
        if (accept) {
               // Following functions are left for implementation by user based on his platform(OpenGL/graphics.h etc.)
               DrawRectangle(xmin, ymin, xmax, ymax);
               LineSegment(x0, y0, x1, y1);
        }
}

[edit]Notes

  1. ^ Principles of Interactive Computer Graphics p.124 and p.252, by Bob Sproull and William M. Newman, 1973, McGraw–Hill Education, International edition, ISBN 0-07-085535-8

[edit]See also

Algorithms used for the same purpose:

[edit]References

[edit]External links


반응형
Posted by msparkms
,
반응형

원본 :  http://content.gpwiki.org/index.php/D3DBook:Screen_Space_Ambient_Occlusion 

Screen Space Ambient Occlusion

Introduction

There are many ways in which to increase the realism of a computer generated image. One such method is to calculate the effects of shadowing on an object when evaluating its lighting equations. There is a very wide variety of shadowing techniques available for use in real-time computer graphics, with each technique exhibiting both advantages and disadvantages. In general, shadowing techniques try to strike some balance between shadow quality and runtime performance.

One such technique has been termed Screen Space Ambient Occlusion (SSAO). This technique was originally discussed by Martin Mittring when presenting the paper “Finding Next Gen” at the 2007 SIGGRAPH conference. The basic concept behind the algorithm is to modify the ambient lighting term [see the lighting section of the book for details about the lighting equation :Foundation and theory] based on how occluded a particular point in a scene is. This was not the first technique to utilize the concept of ambient occlusion, but as you will see later in this chapter Screen Space Ambient Occlusion makes some clever assumptions and simplifications to give very convincing results while maintaining a high level of performance.

컴퓨터에서 생성되는 이미지의 현실성을 높이기 위한 많은 방법들이 있다. 이러한 방법중 하나는 조명 방정식을 평가할 때 오브젝트에 그림자 효과를 계산하는 것이다. 실시간 컴퓨터 그래픽스에서 사용할 수 있는 매우 널리 알려진 다양한 그림자 기법들이 있다. 각각의 기법들은 장단점을 가지고 있다. 보통 그림자 기법들은 그림자 퀄리티와 실시간 퍼포먼스 사이에서 약간의 밸런스를 취한다.

이러한 테크닉중 하나는 SSAO라고 한다. 이 기법은 2007 SIGGRAPH 컨퍼런스에서 "Finding Next Gen" 발표때 Martin Mittring에 의해 처음으로 논의되었다. 이 알고리즘의 기본 개념의 배경은 씬에서 특정 점이 얼마나 차폐되었는지를 기반으로 환경광을 수정하는 것이다. [Foundation and Theory에 조명 방정식에 대해서 자세하게 알고 싶으면 책의 조명 부분을 봐라.] 이것은 Ambient Occlusion의 개념을 이용하는 첫번째 테크닉은 아니지만, 이 챕터 나중에 SSAO가 매우 확실한 결과를 주는 반면에 높은 레벨의 퍼포먼스를 내기 위해 얼마나 영리한 가정과 단순화를 했는지 알게 될 것이다.

Figure 1 SSAO Screen Shot.png
Figure 1: Sample rendering utilizing screen space ambient occlusion.

In this chapter, we will explore the theory behind the Screen Space Ambient Occlusion technique, provide an implementation of the technique that utilizes the Direct3D 10 pipeline, and discuss what parameters in the implementation can be used to provide scalable performance and quality. Finally, we will discuss an interactive demo which uses the given implementation and use it to give some indication of the performance of the algorithm.

이 챕터에서 우리는 Direct3D 10 파이프라인을 사용하는 구현을 제공하여 SSAO 기법의 뒤에 깔린 이론을 알아볼 것이고 구현에서 어떤 인자들이 재볼 수 있는 퍼포먼스와 퀄리티를 제공하는데 사용되는지 의논할 것이다. 마지막으로 우리는 주어진 구현을 사용하는 데모에 대해 의논하고 알고리즘의 퍼포먼스의 징후를 보는데 사용할 것이다.

Algorithm Theory

Now that we have a background on where screen space ambient occlusion has come from, we can begin to explore the technique in more detail. SSAO differs from its predecessors by making one major simplification: it uses only a depth buffer to determine the amount of occlusion at any given point in the current scene view instead of using the scene information before it is rasterized. This is accomplished by either directly using the depth buffer or creating a special render target to render the depth information into. The depth information immediately surrounding a given point is then scrutinized and used to calculate how occluded that point is. The occlusion factor is then used to modify the amount of ambient light applied to that point.

The generation of an occlusion factor using the SSAO algorithm is logically a two step process. First we must generate a depth texture representation of the current view of the scene, and then use the depth texture to determine the level of occlusion for each pixel in the final view of the current scene. This process is shown below in Figure 2.

이제 우리는 SSAO가 어디로 부터 왔는지 그 배경을 알게되었다. 이제 더 자세히 이 기법에 대해 탐험을 시작해 볼 수 있다. SSAO는 주요한 단순화를 함으로써 전에 있던 것들과는 다르다. : 이 기법은 레스터라이징 전에 화면 정보를 사용하는 대신에 현재 씬 뷰에 주어진 점에서 차폐된 정도를 계산하기 위해 오직 깊이버퍼만 사용한다. 이것은 직접적으로 사용하는 깊이 버퍼나 깊이 정보를 렌더링하기 위한 특정 렌더타겟의 생성에 의해서 수행된다. 주어진 한 점의 주변을 감싸는 깊이 정보는 조사되고 그 점이 얼마나 차폐되었는지 계산하는데 사용된다. 차폐 요소는 그점에 적용되는 환경 광의 양을 수정하는데 사용되어진다.

SSAO 알고리즘에 사용되는 차폐 요소의 발생은 논리적으로 두 스텝으로 진행된다. 먼저 우리는 현재 씬의 뷰의 화면을 렌더링하는 깊이 텍스쳐를 생성해야만 하고, 그리고 나서 현재 씬의 마지막 뷰에 각각의 픽셀에 차폐정도를 결정하기 위해 깊이 텍스쳐를 사용한다. 이 과정은 뒤의 Figure 2를 보자.



Figure 2 SSAO Algorithm.png
Figure 2: Overview of the screen space ambient occlusion technique.

The simplification of using only a depth buffer has several effects with respect to performance and quality. Since the scene information is rasterized into a render target, the information used to represent the scene is reduced from three dimensional to two dimensional. The data reduction is performed with the standard GPU pipeline hardware which allows for a very efficient operation. In addition to reducing the scene by an entire dimension, we are also limiting the 2D scene representation to the visible portions of the scene as well which eliminates any unnecessary processing on objects that will not be seen in this frame.

The data reduction provides a significant performance gain when calculating the occlusion factor, but at the same time removes information from our calculation. This means that the occlusion factor that is calculated may not be an exactly correct factor, but as you will see later in this chapter the ambient lighting term can be an approximation and still add a great deal of realism to the scene.

Once we have the scene depth information in a texture, we must determine how occluded each point is based on it's immediate neighborhood of depth values. This calculation is actually quite similar to other ambient occlusion techniques. Consider the depth buffer location shown in Figure 3:

오직 하나의 깊이 버퍼를 사용하는 단순화는 퍼포먼스와 퀄리티 면에서 여러모로 효과적이다. 씬 정보를 렌더 타겟에 레스터라이징한 이후로, 씬을 표현하기 위해 사용되는 정보는 3차원에서 2차원으로 줄어든다. 이 데이터 축소는 매우 효과적인 연산을 가능하게 하는 표준 GPU 파이프라인 하드웨어로 행해진다. 전체 씬의 차원이 감소한 것 뿐만아니라 추가로 2D 씬의 표현을 씬의 보여진 부분만으로 제한함으로써 이번 프레임에 보이지 않을 오브젝트에 대해 어느 불필요한 처리를 제거할 수 있다. 

데이터 축소는 차폐 요소를 계산할 때 분명한 퍼포먼스 이익을 제공하지만 동시에 계산으로부터 정보를 제거해야 한다. 계산된 차폐 정보가 정확하게 올바른 값이 아닐지도 모른다는 것을 의미하지만 환경광의 개념은 근사치로 할 수 있고 씬에 사실성을 충분한 정도로 추가해준다는 것을 이번 챕터 후반부에 볼 수 있을 것이다.

일단 텍스쳐에 씬의 깊이 정보를 가지고, 깊의 값들의 이웃된 정보를 기반으로 각각의 점이 얼마나 차폐되었는지를 결정해야만 한다. 이 계산은 사실상 다른 Ambient Occlusion 기법과 꽤 유사하다. 깊이 버퍼 위치에 대한 고려는 Figure 3을 보자.


Figure 3 Scene Depth Buffer.png
Figure 3: A sample depth buffer shown with the view direction facing downward.

Ideally we would create a sphere surrounding that point at a given radius r, and then integrate around that sphere to calculate the volume of the sphere that is intersected by some object in the depth buffer. This concept is shown here in Figure 4.

이상적으로 우리는 주어진 반지름 r에서 그 점을 감싸는 하나의 구를 생성하고 깊이버퍼에서 어떠한 오브젝트에 의해 교차되는 구의 볼륨을 계산하는데 그 구의 주변값을 모두 통합한다.(모두 더한다.) 이 개념은 Figure 4를 보자.


Figure 4 Scene Depth Buffer Sphere.png
Figure 4: The ideal sampling sphere for determining occlusion.

Of course, performing a complex 3D integration at every pixel of a rendered image is completely impractical. Instead, we will approximate this sphere by using a 3D sampling kernel that resides within the sphere's volume. The sampling kernel is then used to look into the depth buffer at the specified locations and determine if each of it's virtual points are obscured by the depth buffer surface. An example sampling kernel is shown in Figure 5, as well as how it would be applied to our surface point.

물론 렌더링된 이미지의 모든 픽셀에 대해 복잡한 3D 총합을 구하는 것은 완전히 실용적이지 못하다. 대신에 우리는 구의 볼륨이내에 상주하는 3D 샘플링 커널에 의해 이 구를 간략화 할 것이다. 샘플링 커널은 특정 위치에 있는 깊이 버퍼를 조사하고 각각의 가상의 점들이 깊이 버퍼 표면에 의해 가려졌는지 아닌지를 결정하는데 사용된다. 샘플링 커널의 한가지 예는 Figure5를 보고 우리 표면의 점에 어떻게 적용되는지를 보자.

Figure 5 Scene Depth Buffer Kernel.png
Figure 5: Simplified sampling kernel and its position on the depth buffer.

The occlusion factor can then be calculated as the sum of the sampling kernel point's individual occlusions. As you will see in the implementation section, these individual calculations can be made more or less sensitive based on several different factors, such as distance from the camera, the distance between our point and the occluding point, and artist specified scaling factors.

차폐 요소는 샘플링 커널 점들의 각각의 차폐된 정도의 함으로 계산할 수 있다. 구현 섹션을 보면 이 개별적인 계산은 카메라로 부터의 거리, 점과 차폐점사이의 거리, 아티스트들이 잡아줄 크기 요소등 몇가지 다른 요소들을 기반으로 감각적으로 만들 수 있다.

Implementation

With a clear understanding of how the algorithm functions, we can now discuss a working implementation. As discussed in the algorithm theory section, we will need a source of scene depth information. For simplicity's sake, we will use a separate floating point buffer to store the depth data. A more efficient technique, although slightly more complex, would utilize the z-buffer to acquire the scene depth. The following code snippet shows how to create the floating point buffer, as well as the render target and shader resource views that we will be binding it to the pipeline with:

알고리즘 함수의 확실한 이해를 통해 우리는 지금부터 구현에 대해 이야기해 볼 수 있다. 알고리즘 이론 섹션에서 이야기 했듯이, 우리는 씬의 깊이 정보의 소스를 필요로한다. 간단하게 깊이 정보를 저장하기 위해 별개의 부동소스점 버퍼를 사용할 것이다. 비록 약간 더 복잡할 수는 있겠지만 더 효율적인 기법은 씬의 깊이를 얻어오는데 z-buffer를 이용하는 것이다. 뒤의 코드는 어떻게 부동소수점 버퍼를 어떻게 생성하는지 보여주고 또한 우리 파이프라인에 연결시킬 렌더타겟과 쉐이더 리소스 뷰의 생성도 보여준다.

// Create the depth buffer
D3D10_TEXTURE2D_DESC desc;
ZeroMemory( &desc, sizeof( desc ) );
 
desc.Width = pBufferSurfaceDesc->Width;
desc.Height = pBufferSurfaceDesc->Height;
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.Format = DXGI_FORMAT_R32_FLOAT;
desc.SampleDesc.Count = 1;
desc.Usage = D3D10_USAGE_DEFAULT;
desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
 
pd3dDevice->CreateTexture2D( &desc, NULL, &g_pDepthTex2D );
 
// Create the render target resource view
D3D10_RENDER_TARGET_VIEW_DESC rtDesc;
rtDesc.Format = desc.Format;
rtDesc.ViewDimension = D3D10_RTV_DIMENSION_TEXTURE2D;
rtDesc.Texture2D.MipSlice = 0;
 
pd3dDevice->CreateRenderTargetView( g_pDepthTex2D, &rtDesc, &g_pDepthRTV );
 
// Create the shader-resource view
D3D10_SHADER_RESOURCE_VIEW_DESC srDesc;
srDesc.Format = desc.Format;
srDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
srDesc.Texture2D.MostDetailedMip = 0;
srDesc.Texture2D.MipLevels = 1;
 
pd3dDevice->CreateShaderResourceView( g_pDepthTex2D, &srDesc, &g_pDepthSRV );

Once the depth information has been generated, the next step is to calculate the occlusion factor at each pixel in the final scene. This information will be stored in a second single component floating point buffer. The code to create this buffer is very similar to the code used to create the floating point depth buffer, so it is omitted for brevity.

Now that both buffers have been created, we can describe the details of generating the information that will be stored in each of them. The depth buffer will essentially store the view space depth value. The linear view space depth is used instead of clip space depth to prevent any depth range distortions caused by the perspective projection. The view space depth is calculated by multiplying the incoming vertices by the worldview matrix. The view space depth is then passed to the pixel shader as a single component vertex attribute.

깊이 정보를 생성하고, 다음 스텝은 마지막 씬에 각각의 픽셀에 대해 차폐 요소를 계산하는 것이다. 이 정보는 부동소수점 버퍼에 두번째 단일 성분에 저장될 것이다. 이 버퍼를 생성하는 코드는 부동소스점 깊이 버퍼를 생성하는데 사용된 코드와 매우 유사하다. 그래서 글을 줄이기 위해 제외하였다.

이제 두 버퍼들의 생성되었고 우리는 각각에 저장될 정보를 생성하는 것에 대해 자세한 부분을 설명할 수 있다. 깊이 버퍼는 필수적으로 뷰 공간의 깊이값을 저장할 것이다. 원근 투영에 의해 발생될 수 있는 깊이 범위의 왜곡을 예방하기 위해 클립 공간의 깊이 대신에 선형 뷰 공간 깊이를 사용한다. 뷰  공간 깊이는 넘어온 버텍스에 월드-뷰 행렬을 곱해줌으로써 계산된다. 뷰 공간 깊이는 버텍스 속성의 단일 성분으로 픽셀 쉐이더로 넘어간다.

fragment VS( vertex IN )
{
    fragment OUT;
 
    // Output the clip space position
    OUT.position = mul( float4(IN.position, 1), WVP );
 
    // Calculate the view space position
    float3 viewpos = mul( float4( IN.position, 1 ), WV ).xyz;
 
    // Save the view space depth value
    OUT.viewdepth = viewpos.z;
 
    return OUT;
}

To store the depth values in the floating point buffer, we then scale the value by the distance from the near to the far clipping planes in the pixel shader. This produces a linear, normalized depth value in the range [0,1].

부동소수점 버퍼에 깊이 값을 저장하기 위해 우리는 픽셀 쉐이더에서 근단면에서 부터 원단면까지의 거리에 대해 값의 크기를 조정한다. 이것은 선형적이고 [0, 1]사이의 범위를 가지는 정규화된 깊이값을 생성한다.

pixel PS( fragment IN )
{
    pixel OUT;
 
    // Scale depth by the view space range
    float normDepth = IN.viewdepth / 100.0f;
 
    // Output the scaled depth 
    OUT.color = float4( normDepth, normDepth, normDepth, normDepth );
 
    return OUT;
}

With the normalized depth information stored, we then bind the depth buffer as a shader resource to generate the occlusion factor for each pixel and store it in the occlusion buffer. The occlusion buffer generation is initiated by rendering a single full screen quad. The vertices of the full screen quad only have a single two-component attribute which specifies the texture coordinates at each of its four corners corresponding to their locations in the depth buffer. The vertex shader trivially passes these parameters through to the pixel shader.

정규화된 깊이 정보를 저장하고, 각각의 픽셀의 차폐 요소를 생성하고 차폐 버퍼에 저장하기 위해 쉐이더 리소스로 깊이 버퍼를 연결한다. 이 차폐 버퍼 생성은 하나의 풀 스크린을 덮는 사각형 하나를 렌더링 하는것에 의해 시작된다. 풀 스크린 사각형의 버텍스들은 깊이 버퍼에 위치에 대응되는 4개의 코너 각각의 텍스쳐 좌표를 명시하는 단일 2차원 성분만 가진다. 버텍스 쉐이더는 평범하게 이 파라미터들을 픽셀 쉐이더로 넘겨준다.

fragment VS( vertex IN )
{
    fragment OUT;
 
    OUT.position = float4( IN.position.x, IN.position.y, 0.0f, 1.0f );
    OUT.tex = IN.tex;
 
    return OUT;
}

The pixel shader starts out by defining the shape of the 3D sampling kernel in an array of three component vectors. The lengths of the vectors are varied in the range [0,1] to provide a small amount of variation in each of the occlusion tests.

픽셀 쉐이더는 3차원 벡터의 배열에 3D 샘플링 커널의 모양을 정의함으로써 시작한다. 벡터의 길이는 [0, 1] 범위에서 차폐 테스트의 각각 변화의 작은 값들을 생성하기 위해 다양하게 만든다.

const float3 avKernel[8] =
{
    normalize( float3( 1, 1, 1 ) ) * 0.125f,
    normalize( float3( -1,-1,-1 ) ) * 0.250f,
    normalize( float3( -1,-1, 1 ) ) * 0.375f,
    normalize( float3( -1, 1,-1 ) ) * 0.500f,
    normalize( float3( -1, 1 ,1 ) ) * 0.625f,
    normalize( float3( 1,-1,-1 ) ) * 0.750f,
    normalize( float3( 1,-1, 1 ) ) * 0.875f,
    normalize( float3( 1, 1,-1 ) ) * 1.000f
};

Next, the pixel shader looks up a random vector to reflect the sampling kernel around from a texture lookup. This provides a high degree of variation in the sampling kernel used, which will allow us to use a smaller number of occlusion tests to produce a high quality result. This effectively "jitters" the depths used to calculate the occlusion, which hides the fact that we are under-sampling the area around the current pixel.

다음으로, 픽셀 쉐이더는 텍스쳐 룩업으로 부터 샘플링 커널을 반사시키기 위한 랜덤 벡터를 얻어온다. 이것은 샘플링 커널 사용에 대단한 변화를 제공하여 높은 품질의 결과를 생성하기 위한 차폐 테스트의 횟수를 더 줄여줄 것이다. 깊이의 효과적인 "jitters"는 현재 픽셀 주변에 영역에 더 낮게 샘플링되는 요소를 숨기면서 차폐를 계산하는데 사용된다.

float3 random = VectorTexture.Sample( VectorSampler, IN.tex.xy * 10.0f ).xyz;
 
random = random * 2.0f - 1.0f;

Now the pixel shader will calculate a scaling to apply to the sampling kernel based on the depth of the current pixel. The pixel's depth value is read from the depth buffer and expanded back into view space by multiplying by the near to far clip plane distance. Then the scaling for the x and y component of the sampling kernel are calculated as the desired radius of the sampling kernel (in meters) divided by the pixel's depth (in meters). This will scale the texture coordinates used to look up individual samples. The z component scale is calculated by dividing the desired kernel radius by the near to far plane distance. This allows all depth comparisons to be performed in the normalized depth space that our depth buffer is stored in.

이제 픽셀 쉐이더는 현재 픽셀의 깊이에 기반하여 샘플링 커널을 적용하기 위해 크기를 계산할 것이다. 이 픽셀들의 깊이 값은 깊이 버퍼에서 읽어오고 근단면, 원단면의 거리에 의해 곱해짐으로써 뷰 공간으로 확장된다. 샘플링 커널의 x, y 성분의 크기는 샘플링 커널의 원하는 반지름 크기(미터단위)를 픽셀의 깊이(미터단위)로 나눈 값으로 계산된다. 이것은 개별적인 샘플들을 찾는데 사용될 텍스쳐 좌표의 크기를 변경시킬 것이다. z 성분의 크기는 희망하는 커널 반지름을 근단면, 원단면 거리차로 나는 값으로 계산된다. 이것은 우리의 깊이 버퍼를 저장한 정규화된 깊이 공간에 행해지는 모든 깊이 비교를 가능하게한다.

float fRadius = vSSAOParams.y; 
float fPixelDepth = DepthTexture.Sample( DepthSampler, IN.tex.xy ).r;
float fDepth = fPixelDepth * vViewDimensions.z;
float3 vKernelScale = float3( fRadius / fDepth, fRadius / fDepth, fRadius / vViewDimensions.z ) ;

With the kernel scaling calculated, the individual occlusion tests can now be carried out. This is performed in a for loop which iterates over each of the points in the sampling kernel. The current pixel's texture coordinates are offset by the randomly reflected kernel vector's x and y components and used to look up the depth at the new location. This depth value is then offset by the kernel vector's z-component and compared to the current pixel depth.

커널 샘플링을 계산하면서 개별적인 차폐 테스트는 바로 수행될 수 있다. 이것은 샘플링 커널의 각 점들을 도는 루프에서 행해진다. 현재 픽셀의 텍스쳐 좌표는 커널 벡터의 x, y 성분을 랜덤하게 반사시킨 만큼 옮겨지고 새로운 위치의 깊이값을 찾는데 사용된다. 이 깊이 값은 커널 벡터의 z 성분만큼 옮겨지고 현재 픽셀 깊이와 비교된다.

float fOcclusion = 0.0f;
 
for ( int j = 1; j < 3; j++ )
{
    float3 random = VectorTexture.Sample( VectorSampler, IN.tex.xy * ( 7.0f + (float)j ) ).xyz;
    random = random * 2.0f - 1.0f;
 
    for ( int i = 0; i < 8; i++ )
    {
        float3 vRotatedKernel = reflect( avKernel[i], random ) * vKernelScale;
        float fSampleDepth = DepthTexture.Sample( DepthSampler, vRotatedKernel.xy + IN.tex.xy ).r;
        float fDelta = max( fSampleDepth - fPixelDepth + vRotatedKernel.z, 0 );
        float fRange = abs( fDelta ) / ( vKernelScale.z * vSSAOParams.z );
 
        fOcclusion += lerp( fDelta * vSSAOParams.w, vSSAOParams.x, saturate( fRange ) );
    }
}

The delta depth value is then normalized by an application selectable range value. This is intended to produce a factor to determine the relative magnitude of the depth difference. This information can, in turn, be used to adjust the influence of this particular occlusion test. For example, if an object is in the foreground of a scene, and another object is partially obscured behind it then you don't want to count a failed occlusion test for the back object if the foreground object is the only occluder. Otherwise the background object will show a shadow-like halo around the edges of the foreground object. The implementation of this scaling is to linearly interpolate between a scaled version of the delta value and a default value, with the interpolation amount based on the range value.

델타 깊이 값은 어플리케이션에서 선택한 범위 값에 의해 정규화된다. 이것은 깊이 차이의 상대적인 크기를 결정하는 요소를 생산하기 위한   의도이다. 결국 이 정보는 이 특정한 차폐 테스트의 정도(영향)을 조절하는데 사용될 수 있다. 예를 들어 만약 하나의 오브젝트가 씬의 앞부분에 있고 다른 한 물체는 이 물체의 뒤에서 부분적으로 가려졌다면 앞에 있는 오브젝트가 유일한 차폐물이라면 뒤에 있는 오브젝트에 대한 실패할 차폐 테스트를 하는걸 원하진 않을 것이다. 반면에 뒤에 있는 오브젝트는 앞의 물체의 가장자리 주변에 후광과 같은 그림자가 보일것이다. 이 크기의 구현은 델타 값의 크기가 변경된 것과 기본 값 사이의 범위 값을 기반으로한 선형 보간으로 구현된다.

fOcclusion += lerp( ( fDelta * vSSAOParams.w ), vSSAOParams.x, saturate( fRange ) );

The final step before emitting the final pixel color is to calculate an average occlusion value from all of the kernel samples, and then use that value to interpolate between a maximum and minimum occlusion value. This final interpolation compresses the dynamic range of the occlusion and provides a somewhat smoother output. It should also be noted that in this chapter’s demo program, only sixteen samples are used to calculate the current occlusion. If a larger number of samples are used, then the occlusion buffer output can be made smoother. This is a good place to scale the performance and quality of the algorithm for different hardware levels.

최종 픽셀 색을 내보내기 전에 마지막 단계는 모든 커널 샘플들의 평균 차폐 값을 계산하고 최대, 최소 차폐 값 사이로 보간하기 위한 값으로 사용하는 것이다. 이 마지막 보간은 차폐의 동적인 범위를 압축하고 더 부드러운 결과물을 제공한다. 또한 이 챕터의 데모 프로그램안에서 적혀있듯이 오직 16개 샘플들이 현재 차폐 계산에 사용되었다. 만약 더 큰 숫자의 샘플들을 사용한다면, 차폐 버퍼 결과물은 더 부드럽게 될 것이다. 이것은 다른 하드웨어 레벨에서 알고리즘의 퍼포먼스와 질을 키우는데 좋은 지점이 된다.

OUT.color = fOcclusion / ( 2.0f * 8.0f );
 
// Range remapping
OUT.color = lerp( 0.1f, 0.6, saturate( OUT.color.x ) );

With the occlusion buffer generated, it is bound as a shader resource to be used during the final rendering pass. The rendered geometry simply has to calculate it's screen space texture coordinates, sample the occlusion buffer, and modulate the ambient term by that value. The sample file provided performs a simple five sample average, but more sophisticated filtering like a Gaussian blur could easily be used instead.

차폐 버퍼를 생성하면서 마지막 렌더링 패스 동안 사용되기 위해 쉐이더 리소스로 넘겼다. 렌더링된 지오메트리는 간단하게 화면 공간 텍스쳐 좌표로 계산하고 차폐 버퍼에 샘플링하고 그 값에 의해 환경값을 조절한다. 샘플 파일은 간단한 다섯 샘플을 평균내서 처리하지만 가우시안 블러와 같은 더 세련된 필터링은 쉽게 대신 사용될 수 있다.

SSAO Demo

Demo Download: SSAO_Demo

The demo program developed for this chapter provides a simple rendering of a series of cube models that is shaded only with our screen space ambient occlusion. The adjustable parameters discussed for this technique can be changed in real-time with the onscreen slider controls. Figure 6 below shows the occlusion buffer and the resulting final output rendering. Notice that the occlusion parameters have been adjusted to exaggerate the occlusion effect.

이 챕터를 위해 개발된 데모 프로그램은 우리 SSAO만으로 쉐이딩을 계산하는 큐브 모델들을 간단하게 랜더링한다. 이 기법에서 의논된 조정할만한 파라미터들은 화면상의 슬라이더 컨트롤로 실시간에 변경할 수 있다. 뒤의 Figure 6은 차폐 버퍼와 최종 결과물 렌더링을 보여준다. 차폐 파라미터들은 차폐 효과를 과장하기 위해 조정되어 진다는 것을 알아두자.

Figure 6 SSAO Occlusion Buffer.png
Figure 6: A sample ambient occlusion buffer from the demo program.


Figure 7 SSAO Screen Shot.png
Figure 7: The final output rendering from the demo program.

Conclusion

In this chapter we developed an efficient, screen space technique for adding realism to the ambient lighting term of the standard phong lighting model. This technique provides one implementation of the SSAO algorithm, but it is certainly not the only one. The current method can be modified for a given type of scene, with more or less occlusion for distant geometry. In addition, the minimum and maximum amounts of occlusion, interpolation techniques, and sampling kernels are all potential areas for improvement or simplification. This chapter has attempted to provide you with an insight into the inner workings of the SSAO technique as well as a sample implementation to get you started.

이번 챕터에서 우리는 표준 퐁 라이팅 모델에 환경 광 개념으로 사실성을 추가하기 위한 효율적인 화면 공간 기법을 개발해봤다. 이 기법은 SSAO 알고리즘의 한가지 구현을 제공하지만 딱 이것 하나만은 아니다. 현재 메소드는 씬의 주어진 타입에 맞춰 수정될 수 있고 지오메트리의 거리에 따라 더 혹은 덜 차폐를 계산할 수 있다. 게다가 차폐의 최소, 최대 값, 보간, 샘플링 커널은 모두 더 향상시키거나 간략화 할 수있는 부분이다. 이 챕터는 당신에게 SSAO 기법의 내부 동작에 대한 시야와 샘플 구현을 시작하는 것을 돕기 위해 만들어졌다.

반응형
Posted by msparkms
,
반응형

Introduction

Global illumination (GI) is a term used in computer graphics to refer to all lighting phenomena caused by interaction between surfaces (light rebounding off them, refracting, or getting blocked), for example: color bleeding, caustics, and shadows. Many times the term GI is used to refer only to color bleeding and realistic ambient lighting. Direct illumination – light that comes directly from a light source – is easily computed in real-time with today´s hardware, but we can´t say the same about GI because we need to gather information about nearby surfaces for every surface in the scene and the complexity of this quickly gets out of control. However, there are some approximations to GI that are easier to manage. When light travels through a scene, rebounding off surfaces, there are some places that have a smaller chance of getting hit with light: corners, tight gaps between objects, creases, etc. This results in those areas being darker than their surroundings. 
전역 조명(GI)은 표면들사이에서 일어나는 상호작용(빛이 표면들로부터 튕겨져 나오고 굴절되고 막히는 것)에 의해서 발생되는 모든 현상을 나타내기 위해 컴퓨터 그래픽스에서 사용되는 개념이다. 예를 들면 color bleeding(색이 있는 재질을 가진 물체의 주변에 재질의 색이 번지는 현상), caustics(초선, 표면에 굴절, 반사되어 빛이 모여서 일렁거리는 모양을 만드는 현상), shadows(그림자)이다. 전역 조명 용어는 대부분 오직 color bleeding과 현실적인 ambient lighting(환경광)을 나타내기 위해 사용된다. 직접 조명(DI) - 빛의 근원으로 부터 바로 오는 빛 - 은 오늘날의 하드웨어에서 실시간을 쉽게 계산될 수 있다. 그러나 우리는 전역 조명에 대해서는 같은 말을 할 수 없다. 씬 안에서 모든 표면에 인근에 있는 표면들에 대한 정보를 모을 필요가 있고 이 복잡한 정보는 빨리 처리될 수 없기 때문이다. 그러나 더 쉽게 관리할 수 있는 전역 조명의 근사 방법들이 몇가지 있다. 씬을 통해서 빛이 진행할 때, 표면들에 튕겨져 나오는데, 빛이 닿는 확률이 더 적은 어떤 장소들이 있을 것이다. : 구석, 오브젝트 사이의 타이트한 사이공간, 주름진 곳 등등. 이것은 그 장소들을 주변보다 더 어둡게 만든다.


This effect is called ambient occlusion (AO), and the usual method to simulate this darkening of certain areas of the scene involves testing, for each surface, how much it is “occluded” or “blocked from light” by other surfaces. Calculating this is faster than trying to account for all global lighting effects, but most existing AO algorithms still can’t run in real-time.

이 효과를 ambient occlusion(AO)라 부르고, 씬의 어떤 지역의 어두움을 시뮬레이션하기 위한 방법은 각 표면에 대해서, 다른 표면에 의해서 얼마나 많이 "차폐되었는가"나 "빛으로 부터 가려졌는가"를 테스트하는 것을 포함한다. 이것을 계산하는 것은 모든 전역 조명 효과를 처리하려는 것 보다 더 빠른지만 대부분의 존재하는 AO 알고리즘들은 여전히 실시간에 실행될 수 없다.


Real-time AO was out of the reach until Screen Space Ambient Occlusion (SSAO) appeared. SSAO is a method to approximate ambient occlusion in screen space. It was first used in games by Crytek, in their “Crysis” franchise and has been used in many other games since. In this article I will explain a simple and concise SSAO method that achieves better quality than the traditional implementation.

실시간 AO는 Screen Space Ambient Occlusion(SSAO)가 나타날때까지 논외였다. SSAO는 스크린 공간에서 AO를 근사치로 계산하는 하나의 방법이다. 이 방법은 Crytek에 의해 게임에서 먼저 사용되었다.  Crytek의 "Crysis"에서 사용되었고 그 이후로 많은 다른 게임들에서 사용되고 있다. 이 아티클을 통해서 기존의 구현보다 더 좋은 품질을 얻는 간단하면서도 간결한 SSAO 기법을 설명할 것이다.


Posted Image
The SSAO in Crysis

Prerequisites

The original implementation by Crytek had a depth buffer as input and worked roughly like this: for each pixel in the depth buffer, sample a few points in 3D around it, project them back to screen space and compare the depth of the sample and the depth at that position in the depth buffer to determine if the sample is in front (no occlusion) or behind a surface (it hits an occluding object). An occlusion buffer is generated by averaging the distances of occluded samples to the depth buffer. However this approach has some problems (such as self occlusion, haloing) that I will illustrate later.

Crytek의 원래 구현은 입력값으로 깊이 버퍼를 가졌고 대략 다음과 같이 작용되었다. : 깊이 버퍼의 각 픽셀에 대해서 3D 주변에 있는 몇개의 점들을 샘플링하고, 화면 공간으로 투영하고, 샘플링된 값이 앞에 있는지(차폐되지 않음) 표면의 뒤에 있는지(차폐 오브젝트에 닿았음)를 결정하기 위해 샘플링된 깊이와 깊이버퍼의 위치에 있는 깊이를 비교한다. 그러나 이 방식은 뒤에 그림으로 보여줄 몇가지 문제들을(self occlusion과 haloing) 가지고 있다.



The algorithm I describe here does all calculations in 2D, no projection is needed. It uses per-pixel position and normal buffers, so if you´re using a deferred renderer you have half of the work done already. If you´re not, you can try to reconstruct position from depth or you can store per-pixel position directly in a floating point buffer. I recommend the later if this is your first time implementing SSAO as I will not discuss position reconstruction from depth here. Either way, for the rest of the article I´ll assume you have both buffers available. Positions and normals need to be in view space.

내가 여기서 설명할 알고리즘은 2D에서 모두 계산되고 투영이 필요없다. 이것은 픽셀당 위치와 노말 버퍼를 사용해서 만약에 디퍼드 렌더러를 사용한다면 이미 작업의 반은 해놓은 것이다. 안 그렇다고 하더라도 깊이로부터 위치를 재구축해보거나 픽셀당 위치를 부동소수점버퍼에 직접 저장할 수도 있다. 나는 여기서 깊이로 부터 위치 재구축에 대해서 설명하지 않을 것이기 때문에 당신이 SSAO를 처음 구현해 본다면 나중의 방법을 추천한다. 어느쪽이든, 아티클의 남은 부분을 위해서 나는 당신이 두 버퍼를 둘 다 사용할 수 있다고 가정할 것이다. 위치와 노말은 뷰 공간의 것이 필요하다.


What we are going to do in this article is exactly this: take the position and normal buffer, and generate a one-component-per-pixel occlusion buffer. How to use this occlusion information is up to you; the usual way is to subtract it from the ambient lighting in your scene, but you can also use it in more convoluted or strange ways for NPR (non-photorealistic) rendering if you wish.

이 아티클에서 우리가 할 것은 정확히 이것이다. : 위치와 노말 버퍼를 가져와, 픽셀당 하나의 요소를 가지는 차폐 버퍼를 생성하는 것이다. 이 차폐 정보를 사용하는 방법은 당신에게 달려있다.; 보통의 경우에는 당신의 씬에 있는 환경광으로부터 빼준다. 그러나 만약 당신이 NPR(비 실사) 렌더링을 원한다면 더 복잡하거나 이상한 방법으로 사용할 수도 있다. 

Algorithm

Given any pixel in the scene, it is possible to calculate its ambient occlusion by treating all neighboring pixels as small spheres, and adding together their contributions. To simplify things, we will work with points instead of spheres: occluders will be just points with no orientation and the occludee (the pixel which receives occlusion) will be a pair. Then, the occlusion contribution of each occluder depends on two factors:

씬에서 어떤 픽셀이 주어졌을 때, 작은 구들로써 모든 이웃된 픽셀들을 다루고 픽셀들의 기여도를 더함으로써 AO를 계산할 수 있다. 단순화 하기 위해서, 우리는 구 대신에 점으로 작업할 것이다. : 차폐물은 단지 방향없는 점들로 되어 있고 occludee(차폐를 당하는 픽셀)은 한쌍이 될 것이다. 각각의 차폐물의 차폐 기여정도는 두가지 요소에 의존된다.


  • Distance “d” to the occludee.
  • Angle between the occludee´s normal “N” and the vector between occluder and occludee “V”.
With these two factors in mind, a simple formula to calculate occlusion is: Occlusion = max( 0.0, dot( N, V) ) * ( 1.0 / ( 1.0 + d ) )

  • occludee까지의 거리 "d"
  • occludee의 노말 "N"과 occluder와 occludee 사이의 벡터 "V"의 각도.
이 두 요소를 가지고, 차폐를 계산하는 간단한 공식은 : Occlusion = max( 0.0, dot( N, V) ) * ( 1.0 / ( 1.0 + d ) ) 과 같다.


The first term, max( 0.0, dot( N,V ) ), works based on the intuitive idea that points directly above the occludee contribute more than points near it but not quite right on top. The purpose of the second term ( 1.0 / ( 1.0 + d ) ) is to attenuate the effect linearly with distance. You could choose to use quadratic attenuation or any other function, it´s just a matter of taste.
첫번째 부분인 max( 0.0, dot( N,V ) )은 occludee 바로 위의 점들은 근처의 점들보다 더 많은 영향력을 준다는 직관적인 아이디어를 기반으로한다. 두번째 부분인 ( 1.0 / ( 1.0 + d ) ) 목적은 거리에 선형적으로 효과를 감소시킨다. 당신은 이차 감소나 어떤 다른 함수든 입맛대로 사용할 수 있다.

Posted Image

The algorithm is very easy: sample a few neighbors around the current pixel and accumulate their occlusion contribution using the formula above. To gather occlusion, I use 4 samples (<1,0>,<-1,0>,<0,1>,<0,-1>) rotated at 45º and 90º, and reflected using a random normal texture.
알고리즘은 매우 쉽다: 현재 픽셀 주변에 몇개의 이웃들을 샘플링하고 위의 공식을 사용해서 차폐 기여값을 누적시킨다. 차폐값을 모으기 위해 나는 45도, 90도 회전되고 랜덤 노말 텍스쳐를 사용해 반사된 부분에 대해 (<1,0>,<-1,0>,<0,1>,<0,-1>) 4 샘플링을 사용한다.


Some tricks can be applied to accelerate the calculations: you can use half-sized position and normal buffers, or you can also apply a bilateral blur to the resulting SSAO buffer to hide sampling artifacts if you wish. Note that these two techniques can be applied to any SSAO algorithm.

계산을 가속화하기 위해서 몇가지 트릭을 적용할 수 있다. : 절반의 사이즈의 위치, 노말 버퍼를 사용할 수 있거나, 원한다면 샘플링된 것의 결함을 숨기기 위해 SSAO 버퍼의 결과에 블러(양쪽 방향에 대한 블러인듯)를 적용할 수 있다. 이 두가지 테크닉은 어떠한 SSAO 알고리즘에도 적용할 수 있다.

This is the HLSL pixel shader code for the effect that has to be applied to a full screen quad:
이것은 풀 스크린 사각형에 적용될 효과에 대한 HLSL 픽셀 쉐이더 코드이다.

sampler g_buffer_norm;
sampler g_buffer_pos
;
sampler g_random
;
float random_size;
float g_sample_rad;
float g_intensity;
float g_scale;
float g_bias;

struct PS_INPUT
{
float2 uv
: TEXCOORD0;
};

struct PS_OUTPUT
{
float4 color
: COLOR0;
};

float3 getPosition
(in float2 uv)
{
return tex2D(g_buffer_pos,uv).xyz;
}

float3 getNormal
(in float2 uv)
{
return normalize(tex2D(g_buffer_norm, uv).xyz * 2.0f - 1.0f);
}

float2 getRandom
(in float2 uv)
{
return normalize(tex2D(g_random, g_screen_size * uv / random_size).xy * 2.0f - 1.0f);
}

float doAmbientOcclusion(in float2 tcoord,in float2 uv, in float3 p, in float3 cnorm)
{
float3 diff
= getPosition(tcoord + uv) - p;
const float3 v = normalize(diff);
const float d = length(diff)*g_scale;
return max(0.0,dot(cnorm,v)-g_bias)*(1.0/(1.0+d))*g_intensity;
}

PS_OUTPUT main
(PS_INPUT i)
{
PS_OUTPUT o
= (PS_OUTPUT)0;

o
.color.rgb = 1.0f;
const float2 vec[4] = {float2(1,0),float2(-1,0),
                        float2
(0,1),float2(0,-1)};

float3 p
= getPosition(i.uv);
float3 n
= getNormal(i.uv);
float2 rand
= getRandom(i.uv);

float ao = 0.0f;
float rad = g_sample_rad/p.z;

//**SSAO Calculation**//
int iterations = 4;
for (int j = 0; j < iterations; ++j)
{
  float2 coord1
= reflect(vec[j],rand)*rad;
  float2 coord2
= float2(coord1.x*0.707 - coord1.y*0.707,
                          coord1
.x*0.707 + coord1.y*0.707);
 
  ao
+= doAmbientOcclusion(i.uv,coord1*0.25, p, n);
  ao
+= doAmbientOcclusion(i.uv,coord2*0.5, p, n);
  ao
+= doAmbientOcclusion(i.uv,coord1*0.75, p, n);
  ao
+= doAmbientOcclusion(i.uv,coord2, p, n);
}
ao
/=(float)iterations*4.0;
//**END**//

//Do stuff here with your occlusion value “ao”: modulate ambient lighting, write it to a buffer for later //use, etc.
return o;
}

The concept is very similar to the image space approach presented in “Hardware Accelerated Ambient Occlusion Techniques on GPUs” [1] the main differences being the sampling pattern and the AO function. It can also be understood as an image-space version of “Dynamic Ambient Occlusion and Indirect Lighting” [2] Some details worth mentioning about the code:
이 개념은 “Hardware Accelerated Ambient Occlusion Techniques on GPUs” [1]에 설명된 이미지 공간에 접근하는 것과 매우 유사하고 샘플링되는 패턴과 AO 함수가 다르다. “Dynamic Ambient Occlusion and Indirect Lighting” [2] 의 Screen Space 버전으로써 이해할 수도 있다. 코드에 대해서 몇가지 디테일한 중요한 언급은 :


  • The radius is divided by p.z, to scale it depending on the distance to the camera. If you bypass this division, all pixels on screen will use the same sampling radius, and the output will lose the perspective illusion.
  • During the for loop, coord1 are the original sampling coordinates, at 90º. coord2 are the same coordinates, rotated 45º.
  • The random texture contains randomized normal vectors, so it is your average normal map. This is the random normal texture I use:

    Posted Image 

    It is tiled across the screen and then sampled for each pixel, using these texture coordinates:

    g_screen_size * uv / random_size 

    Where “g_screen_size” contains the width and height of the screen in pixels and “random_size” is the size of the random texture (the one I use is 64x64). The normal you obtain by sampling the texture is then used to reflect the sampling vector inside the for loop, thus getting a different sampling pattern for each pixel on the screen. (check out “interleaved sampling” in the references section)

  • 반지름은 카메라와의 거리에 의존하여 크기를 조절하기 위해 p.z로 나눈다. 만약 이 나누기 계산을 무시한다면 화면에 있는 모든 픽셀들은 같은 샘플링 반지름을 가질것이고, 결과물은 원근법을 잃게 될 것이다.
  • 루프를 도는 동안, coord1은 90도에 있는 원본 샘플링 좌표고 coord2는 45도 회전된 같은 좌표이다.
  • 랜덤 텍스쳐는 임의의 노말 벡터들을 포함하고 있어 평균 노말 맵이라 할 수 있다. 이것은 내가 사용하는 랜덤 노말 텍스쳐이다.:

    Posted Image 

    스크린에 걸쳐 타일링이 되고 이것들은 텍스쳐 좌표를 사용하여 각 픽셀로 샘플링된다. 

    g_screen_size * uv / random_size  

    g_screen_size는 화면의 너비와 높이를 포함하고 random_size는 랜덤 텍스쳐의 크기이다.(나는 64x64짜리를 사용한다). 텍스쳐가 샘플링 되는것에 의해 얻어지는 노말은 루프안에서 샘플링된 벡터를 반사하는데 사용되고, 따라서 화면에서 각 픽셀에 다른 샘플링된 패턴을 얻는다. (레퍼런스 색션에서 "interleaved sampling"을 확인해봐라.)


    At the end, the shader reduces to iterating trough some occluders, invoking our AO function for each of them and accumulating the results. There are four artist variables in it:


    결국에는, 쉐이더는 일부 occluder를 반복하는 것과 각각에 우리의 AO 함수를 적용하고, 결과를 누적하는 것을 줄여준다. 쉐이더에는 아티스트가 잡아줄 4개의 변수가 있다.


  • g_scale: scales distance between occluders and occludee.
  • g_bias: controls the width of the occlusion cone considered by the occludee.
  • g_sample_rad: the sampling radius.
  • g_intensity: the ao intensity.

  • g_scale: occluder와 occludee 사이에 거리의 크기를 정한다.
  • g_bias: occludee에 의해 고려되는 콘모양 차폐물의 너비를 조절한다.
  • g_sample_rad: 샘플링 반지름 범위
  • g_intensity: AO 정도(강함 정도)


Once you tweak the values a bit and see how the AO reacts to them, it becomes very intuitive to achieve the effect you want. 

값들을 조금씩 바꿔보고 그것들이 AO에 어떻게 영향을 끼치는지 보라, 원하는 효과를 얻기 위해서는 매우 직관적으로 되야한다.

Results

Posted Image
a) raw output, 1 pass 16 samples b] raw output, 1 pass 8 samples c) directional light only d) directional light – ao, 2 passes 16 samples each.


As you can see, the code is short and simple, and the results show no self occlusion and very little to no haloing. These are the two main problems of all the SSAO algorithms that use only the depth buffer as input, you can see them in these images:

보다시피 코드는 짧고 간단하고, 결과물은 self occlusion을 보여주지 않고 haloing을 거의 보여주지 않는다. 입력으로 오직 깊이 버퍼만 사용하는 모든 SSAO 알고리즘의 두가지 주요한 문제가 있다. 이 이미지에서 그것들을 볼 수 있다. :


Posted Image Posted Image


The self-occlusion appears because the traditional algorithm samples inside a sphere around each pixel, so in non-occluded planar surfaces at least half of the samples are marked as ‘occluded’. This yields a grayish color to the overall occlusion. Haloing causes soft white edges around objects, because in these areas self-occlusion does not take place. So getting rid of self-occlusion actually helps a lot hiding the halos.


The resulting occlusion from this method is also surprisingly consistent when moving the camera around. If you go for quality instead of speed, it is possible to use two or more passes of the algorithm (duplicate the for loop in the code) with different radiuses, one for capturing more global AO and other to bring out small crevices. With lighting and/or textures applied, the sampling artifacts are less apparent and because of this, usually you should not need an extra blurring pass.

전통적인 알고리즘은 각 픽셀 주변의 구안에서 샘플링이되서 샘플의 최소한 절반정도 차폐되지 않은 평면 표면도 차폐되었다고 표시되기 때문에 self occlusion이 나타난다. 이것은 차폐 전반에 회색조의 색을 나타내게한다. haloing은 오브젝트 주변에 부드러운 흰색 모서리로 나타나는데 이 영역안에서는 self occlusion이 일어나지 않기 때문이다.  self occlusion를 제거하는 것은 사실상 많은 haloing을 제거하는것에 도움을 준다.

이 방법으로부터의 차폐 결과는 또한 놀랍게도 카메라가 주변으로 이동할때도 일정하다. 만약 속도 대신에 퀄리티를 원한다면, 한번은 전역 AO를 위해서 나머지는 작은 틈을 가져오기 위해서 다른 반지름으로 두번 혹은 더 많은 패스(코드상에서 루프를 위해 중복되는)로 알고리즘을 사용할 수 있다. 조명 and/or 텍스쳐를 적용하면, 샘플링된 흠집들은 이 방법 때문에 적게 보여지고 보통 추가적인 블러 패스가 필요없다.


Taking it further 

I have described a down-to-earth, simple SSAO implementation that suits games very well. However, it is easy to extend it to take into account hidden surfaces that face away from the camera, obtaining better quality. Usually this would require three buffers: two position/depth buffers (front/back faces) and one normal buffer. But you can do it with only two buffers: store depth of front faces and back faces in red and green channels of a buffer respectively, then reconstruct position from each one. This way you have one buffer for positions and a second buffer for normal. 

나는 게임들에 매우 적합한 간단한 SSAO 구현을 실제적으로 설명하고 있다. 그러나, 더 나은 퀄리티를 얻기 위해서 카메라로 부터 면이 떨어진 은면들을 고려하기 위해서 구현을 확장하는 것은 쉽다. 보통 이것은 세개의 버퍼를 필요로한다. : 두개의 위치/깊이 버퍼(앞면/뒷면) 그리고 하나의 노말 버퍼이다. 그러나 오직 두개의 버퍼를 가지고 할 수 있다. : 앞면과 뒷면의 깊이를 빨간색, 초록색 채널에 각각 저장하고 각각으로 부터 위치를 재구축한다. 이 방법으로 당신은 위치를 위한 하나의 버퍼와 노말을 위한 버퍼를 가진다.

These are the results when taking 16 samples for each position buffer: 

각 위치 버퍼에 대해서 16 샘플링을 한 결과가 있다.

Posted Image
left: front faces occlusion, right: back faces occlusion

To implement it just and extra calls to “doAmbientOcclusion()” inside the sampling loop that sample the back faces position buffer when searching for occluders. As you can see, the back faces contribute very little and they require doubling the number of samples, almost doubling the render time. You could of course take fewer samples for back faces, but it is still not very practical.

이것을 구현하기 위해서 occluder를 찾을 때 뒷면 위치 버퍼를 샘플링하는 샘플링 루프 안에서 여분의 "doAmbientOcclusion()"함수를 호출해준다. 보시다시피, 뒷면은 매우 약간의 영향을 끼쳐서 샘플링 숫자를 두배로 올릴 필요가 있고 렌더링때 거의 두배로 올린다. 물론 뒷면에 더 적은 샘플링을 취할수는 있지만 실용적이지는 않다.

This is the extra code that needs to be added:

이것은 추가될 필요가 있는 여분의 코드이다:


inside the for loop, add these calls:

루프안에 이것들의 호출을 추가하라.

ao += doAmbientOcclusionBack(i.uv,coord1*(0.25+0.125), p, n);
ao
+= doAmbientOcclusionBack(i.uv,coord2*(0.5+0.125), p, n);
ao
+= doAmbientOcclusionBack(i.uv,coord1*(0.75+0.125), p, n);
ao
+= doAmbientOcclusionBack(i.uv,coord2*1.125, p, n);


Add these two functions to the shader:

이 두 함수를 쉐이더에 추가하라.

float3 getPositionBack(in float2 uv)
{
return tex2D(g_buffer_posb,uv).xyz;
}
float doAmbientOcclusionBack(in float2 tcoord,in float2 uv, in float3 p, in float3 cnorm)
{
float3 diff
= getPositionBack(tcoord + uv) - p;
const float3 v = normalize(diff);
const float d = length(diff)*g_scale;
return max(0.0,dot(cnorm,v)-g_bias)*(1.0/(1.0+d));
}


Add a sampler named “g_buffer_posb” containing the position of back faces. (draw the scene with front face culling enabled to generate it) Another small change that can be made, this time to improve speed instead of quality, is adding a simple LOD (level of detail) system to our shader. Change the fixed amount of iterations with this: 

뒷면의 위치를 포함할 "g_buffer_posb"라는 이름의 샘플러를 추가하라. (앞면 컬링을 하고 씬을 그리면 생성할 수 있다.) 퀄리티보다 속도를 향상시키기 위해서 이번에 해볼수 있는 또 하나의 작은 변화는 우리의 쉐이더에 간단한 LOD 시스템을 추가하는 것이다. 이것으로 반복의 고정된 횟수를 변경하라.

int iterations = lerp(6.0,2.0,p.z/g_far_clip); 

The variable “g_far_clip” is the distance of the far clipping plane, which must be passed to the shader. Now the amount of iterations applied to each pixel depends on distance to the camera. Thus, distant pixels perform a coarser sampling, improving performance with no noticeable quality loss. I´ve not used this in the performance measurements (below), however.

변수 "g_far_clip"은 원단면 클리핑 평면의 거리이고, 쉐이더에 전달해야만 한다. 이제는 각각의 픽셀에 적용되는 반복의 횟수는 카메라의 거리에 의존한다. 따라서 멀리 떨어져 있는 픽셀은 더 거칠게 샘플링 되고 알아볼정도의 퀄리티 손실 없이 퍼포먼스를 향상된다. 그러나 나는 퍼포먼스 측정때 이것을 사용하지는 않았다.

Conclusion and Performance Measurements

As I said at the beginning of the article, this method is very well suited for games using deferred lighting pipelines because it requires two buffers that are usually already available. It is straightforward to implement, and the quality is very good. It solves the self-occlusion issue and reduces haloing, but apart from that it has the same limitations as other screen-space ambient occlusion techniques: Disadvantages:

이 아티클의 시작때 내가 말했듯이, 이 방법은 보통 이미 이용할 수 있는 두 버퍼를 필요로 하기 때문에 디퍼드 라이팅 파이프라인을 사용하는 게임에 매우 적합하다. 이 방법은 구현하기 쉽고 퀄리티도 매우 좋다. 이 방법은 self occlusion 이슈를 해결하고 haloing을 줄여준다. 하지만 다른 SSAO 테크닉들과 같은 제한은 있다:

단점 :

  • Does not take into account hidden geometry (especially geometry outside the frustum).
  • The performance is very dependent on sampling radius and distance to the camera, since objects near the front plane of the frustum will use bigger radiuses than those far away.
  • The output is noisy.

  • 숨겨진 지오메트리를 고려하지 않는다.(특히 절두체 밖의 지오메트리)
  • 퍼포먼스는 샘플링 반지름과 카메라의 거리에 매우 의존적이고 절두체의 앞 평면 근처의 오브젝트들은 멀리 있는 것들 보다 더 큰 반지름을 사용할 것이다.
  • 결과물은 노이즈가 있다.

Speed wise, it is roughly equal to a 4x4 Gaussian blur for a 16 sample implementation, since it samples only 1 texture per sample and the AO function is really simple, but in practice it is a bit slower. Here´s a table showing the measured speed in a scene with the Hebe model at 900x650 with no blur applied on a Nvidia 8800GT:

속도면에서 16 샘플링 구현을 위한 4x4 가우시안 블러와 거의 동일하고 샘플링당 텍스쳐 1장씩만 들고 AO 함수도 정말 간단하지만 실용적인 면에서는 조금 느리다. 여기 Nvidia 8800GT에서 블러 없이 900x650 해상도에서 Hebe 모델을 띄운 씬의 속도를 측정한 표를 보자.

In these last screenshots you can see how this algorithm looks when applied to different models. At highest quality (32 samples front and back faces, very big radius, 3x3 bilateral blur):

이 마지막 스크린샷들에서 다른 모델에 적용되었을 때 이 알고리즘이 어떻게 보이는지 알 수 있을 것이다. 가장 높은 퀄리티(앞면, 뒷면 32 샘플링, 매우 큰 반지름, 3x3 블러 적용)


Posted Image


At lowest quality (8 samples front faces only, no blur, small radius):
가장 낮은 퀄리티(앞면 8 샘플링, 블러 없음, 작은 반지름)

Posted Image


It is also useful to consider how this technique compares to ray-traced AO. The purpose of this comparison is to see if the method would converge to real AO when using enough samples.

레이 트레이싱 기반의 AO와 이 기술을 비교하면 어떤지 고려하는데도 유용하다. 이 비교의 목적은 충분한 샘플링을 사용했을 때 이 방법이 실제 AO와 근접하게 모이는지를 보는 것이다.

Posted Image
Left: the SSAO presented here, 48 samples per pixel (32 for front faces and 16 for back faces), no blur. Right: Ray traced AO in Mental Ray. 32 samples, spread = 2.0, maxdistance = 1.0; falloff = 1.0.


One last word of advice: don´t expect to plug the shader into your pipeline and get a realistic look automatically. Despite this implementation having a good performance/quality ratio, SSAO is a time consuming effect and you should tweak it carefully to suit your needs and obtain the best performance possible. Add or remove samples, add a bilateral blur on top, change intensity, etc. You should also consider if SSAO is the way to go for you. Unless you have lots of dynamic objects in your scene, you should not need SSAO at all; maybe light maps are enough for your purpose as they can provide better quality for static scenes.

마지막 충고 : 당신의 파이프라인에 이 쉐이더를 붙인다고해서 자동적으로 실제와 같이 보이는 퀄리티를 기대하지 말아라. 이 구현은 좋은 퍼포먼스와 퀄리티를 가짐에도 불구하고 SSAO는 시간을 소비하는 효과라서 당신의 요구에 맞추면서 가능한 가장 좋은 퍼포먼스를 얻기 위해서 주의를 기울여야 한다. 샘플링을 추가하거나 제거하고 위에 블러링을 추가하고 밀도 정도치를 변경하는 등등. 당신은 SSAO가 당신을 위한 길인지에 대해서도 고려해봐야 한다. 당신이 씬에 동적인 오브젝트들을 많이 가지고 있지 않다면 SSAO가 전혀 필요하지 않다; 아마도 정적인 씬을 위해 더 좋은 퀄리티를 제공하기 위해서 라이트 맵은 당신의 목적에 충분할 것이다.

I hope you will benefit in some way from this method. All code included in this article is made available under the MIT license 

References

[1] Hardware Accelerated Ambient Occlusion Techniques on GPUs
(Perumaal Shanmugam) [2] Dynamic Ambient Occlusion and Indirect Lighting
(Michael Bunnell) 

[3] Image-Based Proxy Accumulation for Real-Time Soft Global Illumination
(Peter-Pike Sloan, Naga K. Govindaraju, Derek Nowrouzezahrai, John Snyder) 

[4] Interleaved Sampling
(Alexander Keller, Wolfgang Heidrich) 

Posted Image
Crytek´s Sponza rendered at 1024x768, 175 fps with a directional light.

Posted Image
The same scene rendered at 1024x768, 110 fps using SSAO medium settings: 16 samples, front faces, no blur. Ambient lighting has been multiplied by (1.0-AO).

The Sponza model was downloaded from Crytek's website.


About the Author(s)

José María Méndez is a 23 year old computer engineering student. He has been writing amateur games for 6 years and is currently working as lead programmer at a startup company called Minimal Drama Game Studio.

반응형
Posted by msparkms
,
반응형

펄린 노이즈(Perlin Noise)


펄린 노이즈 활용

  • 랜덤 지형 생성
  • 특정 매질의 느낌이 나는 랜덤한 텍스쳐 생성
  • 풀이나 식물군 배치
  • 나뭇가지 움직임
  • 바람, 파도의 움직임 등등
활용을 보면 알겠지만 랜덤한 데이터를 필요로 할 때 사용된다.
 


노이즈 함수 

노이즈 함수 == 시드기반의 난수 생성기




노이즈 함수를 이용해서 Y축값을 랜덤하게 생성한 그래프


위의 그래프를 부드럽게 보간한 그래프


정의

  • 진폭(Amplitude) : 파동의 최대 높이값
  • 진동수(Frequency) : 단위시간내에 일어난 진동의 횟수 (1 / 파장)



싸인파

진폭 : 최대 높이값
파장 : 같은 높이값이 다시 나올떄 까지의 거리
진동수 : 1 / 파장


노이즈파

진폭 : 최대값 - 최소값(랜덤한 빨간점 기준)
파장 : 빨간점 사이의 거리
진동수 : 1 / 파장


노이즈 함수를 이용한 파동



                      



                     



                     


펄린 노이즈 함수를 이용한 파동

위에 노이즈 함수의 합 == 펄린 노이즈 함수

Large, Medium, Small의 변화가 섞여 있어 우리가 원하는 다양한 결과를 보여준다.

이 기법을 2D로 적용시켜보자.

     


2D로 만든 각각의 노이즈들을 합치면 자연스러운 값을 얻을 수 있다.























지속성(Persistence)

다양한 결과를 위해 진폭과 진동수의 적절한 수정이 필요.

지속성이란 값을 도입하여 간략화 해보자.

이 값은 프랙탈로 유명한 멘델브로트란 사람이 만들었다고 한다.

frequency = 2i
amplitude = persistencei

빈도수는 2의 승수로 올라가고 진폭을 지속성의 승수를 적용하여 처리한다.

Frequency12481632
  
Persistence = 1/4+++++=
Amplitude:11/41/161/641/2561/1024result
  
Persistence = 1/2+++++=
Amplitude:11/21/41/81/161/32result
  
Persistence = 1 / root2+++++=
Amplitude:11/1.4141/21/2.8281/41/5.656result
  
Persistence = 1+++++=
Amplitude:111111result


옥타브(Octaves)

노이즈 함수들이 더해지는 정도

어느정도를 더할지는 자유지만 지속성이 1보다 크면 모르겠지만 대부분 1보다 작으면 금방 작아지므로 매우 작은 결과값들이 더해질 것이기 때문에 적정선을 찾으면 좋겠다.



노이즈 함수 만들기

일반 난수생성기는 생성할 때 마다 다른 값을 리턴해주지만 우리가 만들 노이즈 함수는 같은 시드값을 넘겨주면 같은 값을 넘겨주는 함수이다.

그러한 함수중에 하나는 다음과 같다.

function IntNoise(32-bit integer: x)			 

    x = (x<<13) ^ x;
    return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);    

  end IntNoise function

위의 함수는 하나의 예이고 소수를 이용해서 적절히 값을 만들어 낼 수 있을 것이다. 
하지만 테스트는 어느정도 필요할 것 같다.


보간

노이즈 함수를 만들었으면 구한 값을 보간하여 부드럽게 만들 필요가 있다.

선형보간

간단한 대신 결과가 너무 간단하게 나온다.


  function Linear_Interpolate(a, b, x)
	return  a*(1-x) + b*x
  end of function


코사인보간

선형보간보다 부드럽지만 연산이 느릴 수 있다.


function Cosine_Interpolate(a, b, x) ft = x * 3.1415927 f = (1 - cos(ft)) * .5 return a*(1-f) + b*f end of function


삼차곡선보간

결과가 매우 부드럽게 나오는 보간법으로 속도가 느릴 수 있다. 코사인보간과 비교했을 때 결과가 좋을 수도 있고 나쁠 수도 있다.

function Cubic_Interpolate(v0, v1, v2, v3,x)
	P = (v3 - v2) - (v0 - v1)
	Q = (v0 - v1) - P
	R = v2 - v0
	S = v1

	return Px3 + Qx2 + Rx + S
  end of function


부드러운 노이즈

보간으로도 어느정도 부드럽게 얻어낼 수 있지만 한계가 있다.

이미지 필터링과 같은 방식으로 이웃 값들을 얻어와 평균을 내는 방식을 사용해 더 부드러운 값을 얻어낸다.


1차원 처리

function Noise(x)
    .
    .
  end function

  function SmoothNoise_1D(x)

    return Noise(x)/2  +  Noise(x-1)/4  +  Noise(x+1)/4

  end function

2차원 처리

function Noise(x, y)
    .
    .
  end function

  function SmoothNoise_2D(x>, y)
    
    corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16
    sides   = ( Noise(x-1, y)  +Noise(x+1, y)  +Noise(x, y-1)  +Noise(x, y+1) ) /  8
    center  =  Noise(x, y) / 4

    return corners + sides + center


  end function



최종 결과물

지금까지 구현한 것을 모두 합쳐보자.


1차원 펄린 노이즈

function Noise1(integer x)
    x = (x<<13) ^ x;
    return ( 1.0 - ( (x * (x * x * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);    
  end function


  function SmoothedNoise_1(float x)
    return Noise(x)/2  +  Noise(x-1)/4  +  Noise(x+1)/4
  end function


  function InterpolatedNoise_1(float x)

      integer_X    = int(x)
      fractional_X = x - integer_X

      v1 = SmoothedNoise1(integer_X)
      v2 = SmoothedNoise1(integer_X + 1)

      return Interpolate(v1 , v2 , fractional_X)

  end function


  function PerlinNoise_1D(float x)

      total = 0
      p = persistence
      n = Number_Of_Octaves - 1

      loop i from 0 to n

          frequency = 2i
          amplitude = pi

          total = total + InterpolatedNoisei(x * frequency) * amplitude

      end of i loop

      return total

  end function


2차원 펄린 노이즈

function Noise1(integer x, integer y)
    n = x + y * 57
    n = (n<<13) ^ n;
    return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 7fffffff) / 1073741824.0);    
  end function

  function SmoothNoise_1(float x, float y)
    corners = ( Noise(x-1, y-1)+Noise(x+1, y-1)+Noise(x-1, y+1)+Noise(x+1, y+1) ) / 16
    sides   = ( Noise(x-1, y)  +Noise(x+1, y)  +Noise(x, y-1)  +Noise(x, y+1) ) /  8
    center  =  Noise(x, y) / 4
    return corners + sides + center
  end function

  function InterpolatedNoise_1(float x, float y)

      integer_X    = int(x)
      fractional_X = x - integer_X

      integer_Y    = int(y)
      fractional_Y = y - integer_Y

      v1 = SmoothedNoise1(integer_X,     integer_Y)
      v2 = SmoothedNoise1(integer_X + 1, integer_Y)
      v3 = SmoothedNoise1(integer_X,     integer_Y + 1)
      v4 = SmoothedNoise1(integer_X + 1, integer_Y + 1)

      i1 = Interpolate(v1 , v2 , fractional_X)
      i2 = Interpolate(v3 , v4 , fractional_X)

      return Interpolate(i1 , i2 , fractional_Y)

  end function


  function PerlinNoise_2D(float x, float y)

      total = 0
      p = persistence
      n = Number_Of_Octaves - 1

      loop i from 0 to n

          frequency = 2i
          amplitude = pi

          total = total + InterpolatedNoisei(x * frequency, y * frequency) * amplitude

      end of i loop

      return total

  end function


원본 글을 참고하여 간단하게 펄린 노이즈에 대해서 정리해 보았다.

다양한 곳에 많이 사용되는 것이니까 개념을 잘 읽혀놓고

노이즈를 만드는 함수나 툴킷 클래스를 만들어 놓으면 유용하게 사용할 수 있을 것이다.






반응형
Posted by msparkms
,
반응형

http://en.wikipedia.org/wiki/Deferred_shading

Deferred shading         From Wikipedia, the free encyclopedia

컴퓨터 그래픽스에서 디퍼드 렌더링은 쉐이더의 결과를 색상 프레임 버퍼에 즉시 쓰는 대신에 나중에 합쳐지기 위한 중간 저장버퍼에(G-Buffer 불리는) 쓰는  작은 파트로 나누고 계산되어 지는 쉐이딩 알고리즘 중에 하나인 삼차원 쉐이딩 기술이다. 현대 하드웨어의 구현은 불필요한 버텍스 변환을 피하기 위해 멀티 렌더 타겟(MRT) 사용하는 경향이 있다. 보통 한번에 필요로 하는 버퍼들은 쉐이딩 알고리즘에(예를들면 조명 방정식읽어질때(보통 입력 텍스쳐로만들어지고 최종 결과를 생성하기 위해 합쳐진다. 이러한 방식의 씬의 음영을 위한 계산과 메모리 대역폭은 가시적인 부분을 줄여지고 음영이 지는 깊이 복잡도는 줄어든다.


Advantages


디퍼드 쉐이딩의 주요한 장점은 조명으로 부터의 기하요소의 중복을 제거하는 것이다. 오직 한번의 지오메트리 패스를 필요로 하고 각각의 조명은 오직 실제 적용되는 픽셀들만 계산되어진다. 이것은 분명한 퍼포먼스 부하없이 씬에 많은 조명을 렌더링 있게 해준다. 진입부에 말한 다른 장점들도 있다. 이러한 장점들은 복잡한 조명 자원들의 관리를 간단하게 해주는 것과 다른 복잡한 쉐이더 자원 관리의 용이성, 소프트웨어 렌더링 파이프라인의 간략화를 포함한다.


Disadvantages


디퍼드 렌더링의 단점중 중요한 한가지는 알고리즘안에서 투명도를 다룰 없다는 것이다. 비록 문제가 Z-버퍼 씬에서는 일반적인 것이고 씬에서 투명한 부분의 렌더링을 늦추고 정렬하는 것에 의해 조절되는 경향이 있다고 할지라도. 깊이 필링은 디퍼드 렌더링에서 순서-독립적 투명도를 성취하기 위해 사용되어 있다. 그러나 추가적인 배치와 G-버퍼 크기의 비용이 있다. 다이렉트X 10 이후 버전을 지원하는 현재 하드웨어는 종종 상호적인 프레임율을 유지하기 위해서 충분히 빠른 배치들을 처리할 있다. 순서-독립적 투명도에 디퍼드 쉐이딩을 원할때는 같은 기법을 사용하는 포워드 쉐이딩보다 효과적이지 않다.

또다른 중요한 단점은 다중 머트리얼을 사용하기 어렵다는 것이다. 많은 다른 머트리얼들을 사용하는 것은 가능하지만 이미 크고 많은 양의 메모리 대역폭을 먹고 있는 G-버퍼에 많은 데이터의 저장을 요구한다.

기하 스테이지로부터 조명 스테이지가 분리되었기 때문에 생긴  중요한 단점중 하나인 하드웨어 안티 얼라이어싱은 이상 올바른 결과를 얻지 못한다. : 비록 기본 속성(디퓨즈, 노말 ) 렌더링할 사용되어지는 첫번째 패스는 안티 얼라이어싱을 사용할 있지만 안티 얼라이어싱을 필요로하는 전체 조명까지는 아니다. 제한을 극복하기 위한 보통의 기법중 하나는 마지막 이미지에 외곽선 추출을 하고 외곽선에 블러를 적용하는것이다. 하지만 최근에 고급의 -처리-외곽선-보정 기법이 개발되었다MLAA[5] (Killzone 3Dragon Age 2, 등등), FXAA[6] (Crysis 2FEAR 3Duke Nukem Forever), SRAA,[7] DLAA[8] (Star Wars: The Force Unleashed II), post MSAA (Crysis 2에서 디폴트 안티 얼라이어싱 기법). 하나의 유명한 기법은 Halo Reach에서 사용된 시간적-안티 얼라이어싱이다. 다이렉트X 10 디퍼디 쉐이딩에서 안티 얼라이어싱이 하드웨어에서 가능하게 멀티 샘플링된 렌더 타겟(10.1 버전에서는 깊이 버퍼도)에서 각각의 샘플에 접근하는 쉐이더의 기능을 소개했다이전 하드웨어에서 안티 얼라이어싱의 어떠한 이점을 잃게 할지도 모르지만  기능은 어떠한 경우에는 바람직한 안티 얼라이어싱의 형태를 만들어 주고 안티 얼라이어싱된 외곽선에 HDR 휘도 매핑을 정확하게 적용할 있게 해준다.


Deffered Lighting


디퍼드 라이팅은 (또한 -패스 조명이라 알려져 있는) 디퍼드 쉐이딩의 수정판이다. 기법은 디퍼드 쉐이딩에서 패스를 쓰던것 대신에 쓰리 패스를 사용한다. 기하요소가 넘어가는 첫번째 패스에서는 오직 픽셀당 조명(조도) 계산하기 위해 필요한 속성들만 G-버퍼에 쓴다. 오직 디퓨즈와 스페큘러 조명 데이터 출력후에 지연된 패스인 두번째 스크린-공간 패스는 조명 데이터와 최종 픽셀당 쉐이딩 출력값을 읽어와서 씬을 만들어야 한다. 디퍼드 라이팅의 분명한 장점은 G-버퍼의 크기를 극적으로 줄이는 것이다. 분명한 비용은 한번 대신 두번 기하구조를 렌더링할 필요가 있다는 것이다. 추가적인 비용은 디퍼드 쉐이딩에서의 디퍼드 패스는 오직 하나의 합쳐진 조도 값을 필요로 했지만 디퍼드 라이팅에서 디퍼드 패스는 디퓨즈와 스페큘러 조도를 분리해서 출력해야만 한다는 것이다. ( 부분은 기법을 조사해 봐야 정확하게 해석할 있겠음 -0-;;;)

G-버퍼의 크기를 줄여주기 때문에 기법은 부분적으로 디퍼드 쉐이딩의 심각한 단점을 극복할 있다. - 다중 머트리얼. 해결할 있는 하나의 문제는 MSAA이다. 디퍼드 라이팅은 DX9 하드웨어에서 MSAA 함께 사용될 있다.


Deferred lighting in commercial games

기법은 비디오 게임에서 많은 양의 동적 조명을 사용하고 필요한 쉐이더 명령의 복잡도를 줄여주는 기능을 컨트롤하기 때문에 사용이 증가되고 있다. 디퍼드 라이팅을 사용하는 게임의 몇가지 예를 들면 :

§  Alan Wake

§  Bioshock Infinite[11]

§  Blur

§  Brink

§  Crackdown and Crackdown 2[12]

§  Crysis 2[13]

§  Dead Space[14] and Dead Space 2[15]

§  Deus Ex: Human Revolution [16]

§  Grand Theft Auto IV

§  Halo: Reach [17]

§  inFamous and inFamous 2[18]

§  LittleBigPlanet and LittleBigPlanet 2[19]

§  Shift 2 UNLEASHED [20]

§  Stalker: Shadow of ChernobylClear Sky and Call of Prypiat[21]

§  Red Dead Redemption

§  Resistance series[22]

§  StarCraft II [23]

§  Uncharted and Uncharted 2[24]

§  Vanquish [25]

Deferred shading in commercial games

디퍼드 라이팅의 비교에서 기법은 높은 메모리 크기와 대역폭의 요구 때문에 매우 대중적이지는 않다. 특히 메모리 크기와 대역폭에 매우 제한적이고 자주 버틀넥이 걸리는 곳에서.

 

§  Battlefield 3[26]

§  Dungeons

§  Killzone 2 and Killzone 3[27]

§  Mafia 2

§  Metro 2033[28]

§  Rift

§  Shrek[29]

§  Splinter Cell: Conviction

§  Tabula Rasa[30]

§  Trine

§  Viva Pinata

§  Dota 2[31]

 

Deferred techniques in game engines

§  CryEngine 3 [32]

§  I-Novae [33]

§  Unity [34]

§  Frostbite 2 [35]

§  Unreal Engine 3 [36]

§  Chrome Engine

§  GameStart [37]

 

History

디퍼드 쉐이딩의 아이디어는 Michael Deering The triangle processor and normal vector shader: a VLSI system for high performance graphics 1998년도에 이름 붙여진 그의 대학 논문에서 소개되었다. 비록 논문은 "지연된"이라는 단어를 사용하지는 않았지만 중요한 컨셉은 소개되었다 : 픽셀은 깊이 해상도 후에 한번 음영 처리가 된다. G-버퍼를 사용하는 오늘날 우리에게 알려진 디퍼드 쉐이딩은 1990년에 Saito Takahashi 논문에 의해 소개되었다. 비록

들도 "지연된"이란 단어를 사용하지는 않았지만 말이다. 2004 그래픽 하드웨어 제품에서 구현을 시작으로 나타났다. 기법은 비디오 게임과 같은 프로그램에서 유명세를 얻었고 마침내 2008년에서 2010년에 메인 스트림이 되었다.

 

References

1.    ^ http://thecansin.com/Files/Deferred%20Rendering%20in%20XNA%204.pdf

2.    ^ "NVIDIA SDK 9.51 - Featured Code Samples". NVIDIA. 2007-01-17. Retrieved 2007-03-28.

3.    ^ http://diaryofagraphicsprogrammer.blogspot.com/2008/03/light-pre-pass-renderer.html

4.    ^ "Deferred shading tutorial". Pontifical Catholic University of Rio de Janeiro. Retrieved 2008-02-14.

5.    ^ http://igm.univ-mlv.fr/~biri/mlaa-gpu/TMLAA.pdf

6.    ^ http://www.ngohq.com/images/articles/fxaa/FXAA_WhitePaper.pdf

7.    ^ http://research.nvidia.com/publication/subpixel-reconstruction-antialiasing

8.    ^ http://and.intercon.ru/releases/talks/dlaagdc2011/

9.    ^ http://and.intercon.ru/releases/talks/dlaagdc2011/slides/

10.  ^ http://www.realtimerendering.com/blog/deferred-lighting-approaches/

11.  ^ http://gamer.blorge.com/2010/11/21/bioshock-infinite-development-is-ps3-focused-and-uses-uncharted-2-tech/

12.  ^ http://www.eurogamer.net/articles/digitalfoundry-crackdown2-tech-interview

13.  ^ http://www.slideshare.net/guest11b095/a-bit-more-deferred-cry-engine3

14.  ^ "Dead Space by Electronic Arts". NVIDIA. Retrieved 2008-02-14.

15.  ^ "Face-Off: Dead Space 2". Retrieved 2010-02-01.

16.  ^ http://translate.google.com/translate?hl=en&sl=ja&tl=en&u=http%3A%2F%2Fgame.watch.impress.co.jp%2Fdocs%2Fseries%2F3dcg%2F20100922_395310.html

17.  ^ http://www.eurogamer.net/articles/digitalfoundry-halo-reach-tech-interview

18.  ^ http://gamer.blorge.com/2010/11/21/bioshock-infinite-development-is-ps3-focused-and-uses-uncharted-2-tech/

19.  ^ http://www.digitalscrutiny.com/content/2011/01/littlebigplanet-2-tech-analysis/

20.  ^ http://www.eurogamer.net/articles/digitalfoundry-the-making-of-shift-2?page=2

21.  ^ Shishkovtsov, Oles (2005-03-07). "GPU Gems 2: Chapter 9. Deferred Shading in S.T.A.L.K.E.R"Nvidia. Retrieved 2011-02-02.

22.  ^ http://cmpmedia.vo.llnwd.net/o1/vault/gdc09/slides/gdc09_insomniac_prelighting.pdf

23.  ^ "StarCraft II Effects & techniques" (PDF). AMD. Retrieved 2010-02-28.

24.  ^ http://features.cgsociety.org/story_custom.php?story_id=5545

25.  ^ http://platinumgames.com/tag/deferred-rendering/

26.  ^ http://www.slideshare.net/DICEStudio/spubased-deferred-shading-in-battlefield-3-for-playstation-3

27.  ^ http://www.guerrilla-games.com/publications/dr_kz2_rsx_dev07.pdf

28.  ^ http://www.eurogamer.net/articles/digitalfoundry-tech-interview-metro-2033?page=2

29.  ^ "History - Electric Sheep Games". Retrieved 14 April 2011.

30.  ^ "Deferred shading in Tabula Rasa". NVIDIA. Retrieved 2008-02-14.

31.  ^ "Valve Developer Wiki - Dota 2". Retrieved 10 April 2012.

32.  ^ "CryENGINE 3 Specifications". Crytek GmbH. Retrieved 2009-03-12.[dead link]

33.  ^ "Infinity Development Journal – Deferred Lighting". I-Novae Studios. 2009-04-03. Retrieved 2011-01-26.

34.  ^ Vosburgh, Ethan (2010-09-09). "Unity 3 Feature Preview – Deferred Rendering". Unity Technologies. Retrieved 2011-01-26.

35.  ^ "Lighting you up in Battlefield 3"DICE. March 3, 2011. Retrieved September 15, 2011.

36.  ^ "Unreal Engine 3 Showcase - Samaritan". Epic Games. 2011-03-10. Retrieved 2011-07-07.

37.  ^ "GameStart – Feature List".

38.  ^ Deering, Michael; Stephanie Winner, Bic Schediwy, Chris Duffy, Neil Hunt. "The triangle processor and normal vector shader: a VLSI system for high performance graphics". ACM SIGGRAPH Computer Graphics (ACM Press) 22 (4): 21–30.

39.  ^ Saito, Takafumi; Tokiichiro Takahashi (1990). "Comprehensible rendering of 3-D shapes". ACM SIGGRAPH Computer Graphics (ACM Press) 24 (4): 197–206. doi:10.1145/97880.97901.

40.  ^ "Deferred Shading" (PDF). NVIDIA. Retrieved 2007-03-28.

41.  ^ Klint, Josh. Deferred Rendering in Leadwerks Engine. Leadwerks.

 

See also

§  S.T.A.L.K.E.R.

§  Leadwerks Engine

§  Rockstar Advanced Game Engine

§  Unity Engine

§  Havok Vision Engine

 

반응형
Posted by msparkms
,
반응형
http://www.codermind.com/articles/Raytracer-in-C++-Introduction-What-is-ray-tracing.html

raytracer output - global illumination - iso surfaces - hdr

This article is the foreword of a serie of article about ray tracing. It's probably a word that you may have heard without really knowing what it represents or without an idea of how to implement one using a programming language. This article and the following will try to fill that for you. Note that this introduction is covering generalities about ray tracing and will not enter into much details of our implementation. If you're interested about actual implementation, formulas and code please skip this and go to part one after this introduction.

이 아티클은 Ray tracing에 대한 아티클 시리즈중 서문이다. 아마도 Ray tracing이라는 단어는 무엇을 의미하는지 확실히 알지는 못하거나 프로그래밍 언어를 사용해서 어떻게 구현해야 할지 아이디어가 없더라도 들어는 봤을 것이다. 이 아티클과 다음에 나올 것들은 당신이 원하는 것을 채워줄 것이다. 이번 도입부는 Ray tracing에 대해서 우리가 할 구현의 많은 세부사항들은 시작하지 않을 것이고 일반적인 부분으로 구성하였다. 만약 실질적인 구현에 대해서 관심이 있다면 이 부분을 건너뛰고 이 도입부 뒤에 있는 내용으로 가서 공식과 코드를 보라.

What is ray tracing ?


Ray tracing is one of the numerous techniques that exist to render images with computers. The idea behind ray tracing is that physically correct images are composed by light and that light will usually come from a light source and bounce around as light rays (following a broken line path) in a scene before hitting our eyes or a camera. By being able to reproduce in computer simulation the path followed from a light source to our eye we would then be able to determine what our eye sees.

Ray tracing은 컴퓨터로 이미지들을 렌더링하기 위해 존재하는 수많은 기법들중 하나이다. Ray tracing의 아이디어는 물리적으로 올바른 상은 빛에 의해 구성된다는 것과 빛은 광원으로부터 나오고 씬 안에서 광선들은 우리 눈이나 카메라에 도달하기전에 산란한다(꺽인 선을 따라감)는 것이다.

Of course it's not as simple as it sounds. We need some method to follow these rays as the nature has an infinite amount of computation available but we have not. One of the most natural idea of ray tracing is that we only care about the rays that hit our eyes directly or after a few rebounds. The second idea is that our generated images will usually be grids of pixels with a limited resolution. Those two ideas together form the basis of most basic raytracers. We will place our point of view in a 3D scene, and we will shoot rays exclusively from this point of view and towards a representation of our 2D grid in space. We will then try to evaluate the amount of rebounds needed to go from the light source to our eye. This is mostly okay because the actual light simulation does not take into account the actual direction of the ray to be accurate. Of course it is a simplification we'll see later why.

물론 이것은 들리는 것 만큼 간단한 것은 아니다. 자연은 무한한 양의 계산을 할 수 있지만 우리는 그럴 수 없으므로 이 광선들을 따라갈 방법이 필요하다. Ray tracing의 자연적인 아이디어중 가장 중요한 것은 우리의 눈으로 바로 들어오는 광선이나 약간의 산란후에 들어오는 광선만 고려하면 된다는 것이다. 두번째 아이디어는 우리가 생성할 이미지들은 보통 제한된 해상도로 픽셀로 이루어진 격자라는 것이다. 이 두가지 아이디어 모두 대부분의 기초적인 Raytracer들의 기본이 되었다. 우리는 3D 씬으로 관점을 두어야 하고 이 관점으로 부터 광선을 쏘고 공간안의 2D 격자로 향하게 할 것이다. 그리고나서 우리는 광원에서 우리눈으로 오는데 필요한 재 산란된 양을 평가할 것이다. 이 방식은 일반적으로 괜찮다.  정확하게 말해서 실제 빛 시뮬레이션은 실제 광선의 방향을 고려하지는 않기 때문이다. 물론 우리가 나중에 알게될 단순화된 방법이다. 

How are raytracers used ?


The ideas behind ray tracing (in its most basic form) are so simple, we would at first like to use it everywhere. But it's not used everywhere.

Ray tracing의 배경이 되는 아이디어들은 매우 간단해서 우리는 처음에 모든 곳에 사용하고 싶어 한다. 하지만 모든 곳에서 사용되지는 않는다. 

Ray tracing has been used in production environment for off-line rendering for a few decades now. That is rendering that doesn't need to have finished the whole scene in less than a few milliseconds. Of course we should not generalize and let you know that several implementations of raytracer have been able to hit the "interactive" mark. Right now so called "real-time ray tracing" is a very active field right now, as it's been seen as the next big thing that 3D accelerators need to be accelerating. Raytracer are really liked in areas where the quality of reflections is important. A lot of effects that seem hard to achieve with other techniques are very natural using a raytracer. Reflection, refraction, depth of field, high quality shadows. Of course that doesn't necessarily mean they are fast.

Ray tracing은 현재 몇 십년동안 오프라인 렌더링으로 생산하는 환경에서 사용되어 왔다. 오프라인 렌더링에서는 몇 밀리세컨드보다 적은 시간에 완전한 씬을 완료할 필요가 없다. 물론 우리는 일반화 하지 않았고 "interactive" 하게 하려는 목표를 달성할 수 있는 raytracer의 몇몇가지의 구현방법들을 알지 못한다. 요즘에 "Real-time Ray tracing"이라 불리우는 분야가 매우 활성화되고 있어 3D 가속기의 가속을 필요로 하는것이 다음의 큰일로 보여지고 있다. Raytracer는 반사의 퀄리티가 중요한 곳에서 정말 환영받고 있다. 다른 기술들로는 성취하기 힘들어 보이는 많은 효과들은 raytracer를 사용하므로써 매우 자연스럽게 된다. 반사, 굴절, Depth of field, 고품질 그림자에서 사용된다. 물론 그것들이 빠르다는 것을 의미하는건 아니다.

Graphics card on the other hand they generate the majority of images these days but are very limited at ray tracing. Nobody can say if that limitation will be removed in the future but it is a strong one today. The alternative to ray tracing that graphics card use is rasterization. Rasterization has a different view on image generation. Its primitive is not rays, but it is triangles. For each triangle in the scene you would estimate its coverage on the screen and then for each visible pixel that is touched by a triangle you would compute its actual color. Graphics cards are very good at rasterization because they can do a lot of optimization related to this. Each triangle is drawn independently from the precedent in what we call an "immediate mode". This immediate mode knows only what the triangle is made of, and compute its color based on a serie of attributes like shader program, global constants, interpolated attributes, textures. A rasterizer would for example typically draw reflection using an intermediate pass called render to texture, a previous rasterizer pass would feed into itself, but with the same original limitations which then cause all kind of precision issues. This amnesia and several other optimizations are what allow the triangle drawing to be fast. Raytracing on the other hand doesn't forget about the whole geometry of the scene after the ray is launched. In fact it doesn't necessarily know in advance what triangles, or objects it will hit, and because of interreflexion they may not be constrained to a single portion of space. Ray tracing tends to be "global", rasterization tends to be "local". There is no branching besides simple decisions in rasterization, branching is everywhere on ray tracing.

반면에 그래픽카드는 요즘에 이미지들의 대다수를 생성하지만 Ray tracing에서는 제한적이다. 아무도 미래에 제한이 사라질 것이라고 말하지 않을 수 없지만 오늘날에는 아직 강한 제한이 있다. 그래픽카드에서 Ray tracing의 대안으로 래스터화를 사용한다. 래스터화는 이미지 생성에 다른 견해를 가진다. 이것의 원시 데이터는 광선이 아니라 삼각형이다. 씬 안에 있는 각각의 삼각형마다 화면안의 적용 범위를 추정하고 픽셀이 보이는지 여부를 판별하고 실제 색상을 계산해낸다. 그래픽카드들은 래스터화와 관련된 많은 최적화를 할 수 있었기 때문에 래스터화를 매우 능숙하다. 각각의 삼각형들은 "immediate mode"라 부르는것의 선례로 부터 독립적으로 그려진다. 이 즉시 모드는 오직 삼각형으로 구성되어있다고 알고있고 쉐이더 프로그램, 전역 상수, 속성의 보정, 텍스쳐들과 같은 일련의 속성들을 기반으로 색상을 계산한다. Rasterizer는 전형적인 예로 Render to Texture(이전의 Rasterizer 패스가 반영된)라 불리는 패스를 사용하여 반사를 그리지만 모든 정확도와 관련된 이슈들로 발생하는 근본적인 제한은 가지고 있다. 이런 기억상실증(그리지 않는 것을 말하는듯)과 몇가지 다른 최적화기법들은 삼각형을 빠르게 그릴 수 있게 해준다. 반면에 Raytracing은 광선이 발사된 후에 씬 전체의 Geometry에 대해서는 잊어버린다. 사실 삼각형이나 광선에 맞게될 오브젝트들이 무엇인지 미리 알 필요는 없다. 그것들의 상호반사는 부득이하게 공간의 한 위치에서 일어나지는 않을 것이기 때문이다. Raytracing은 "global"한 경향이 있고 Rasterization은 "local"한 경향이있다. Rasterization에서는 분기없이 간단한 결정을하지만 Raytracing에서는 모든곳에서 계산을 한다.

Ray tracing is not used everywhere in off line rendering either. The speed advantage of rasterization and other techniques (scan line rendering, mesh subdivision and fast rendering of micro facets) has often been hold against true "ray tracing solution", especially for primary rays. Primary rays are the one that hit the eye directly, instead of having rebound. Those primary rays are coherent and they can be accelerated with projective math (the attribute interpolation that fast rasterizers rely upon). For secondary ray (after a surface has been reflecting it, or refracting it), all bets are off because those nice properties disappear.

Raytracing은  Offline Rendering의 모든곳에서 사용되지는 않는다. Rasterization의 속도 이점과 다른 기술들(Scanline Rendering, 메쉬 분할, 소규모 면에 대한 빠른 렌더링)이 있어 종종 실제 "Raytracing 해법"을 특히 1차 광선들에서 나쁘게 보게되곤 한다. 1차 광선은 다시 튕겨나오는 것 대신에 눈으로 바로 들어오는 광선이다. 이 1차 광선들은 일관성 있고 투영 수학계산(빠른 Rasterizer들에 의존되는 속성의 보간)에 의해 가속될 수 있다. 2차 광선(표면에서 반사되거나 굴절된)에서는 멋진 성질이 사라지기 때문에 무효화 된다.

In the interest of full disclosure it should be noted that ray tracing maths can be used to generate data for rasterizer for example. Global illumination simulation may need to shoot ray to determine local properties such as ambiant occlusion or light bleeding. As long as the data is made local in the process that will be able to help a strict "immediate renderer".

알려져야할 밝혀진 사실들에서 흥미로운 점은 Raytracing 수학들은 Rasterizer의 예시를 만들기 위한 데이터를 생성하는대 사용될 수 있다는 것이다.  전역 조명 시뮬레이션은 Ambient occlusion이나 Light bleeding과 같은 지역적 성질을 결정하기 위해 광선을 발사할 필요가 있다. 데이터가 진행중에 지역적인 성질이 만들어지면 "immediate renderer"를 도울 수 있을 것이다.

Complications, infinity and recursion


Raytracers cannot and will not be a complete solution. No solution exist that can deal with true random infinity. It's often a trade off, trying to concentrate on what makes or break an image and its apparent realism. Even for off line renderer performance is important. It's hard to decide to shoot billions of rays per millions of pixel. Even if that's what the simulation seems to require. What trade offs do we need to do ? We need to decide to not follow every path possible.

Raytracer들은 완벽한 해법은 없다. 사실상 무한한 임의성을 다룰 수 있는 해법은 없다. 종종 이미지를 만들거나 깨뜨리는 것과 사실적으로 보이게 만드는 것중 어디에 집중하느냐에 따라 Trade off가 발생한다. 심지어 Offline Renderer에서조차 퍼포먼스는 중요하다. 몇백만 픽셀당 몇십억개의 광선을 발사하자고 결정하는건 어려운 일이다. 심지어 만약 그것이 시뮬레이션에서 요구되는 것을 보이게 하더라도 말이다. Trade off를 진행하기 위해 우리가 필요로 하는 일은 무엇인가? 우리는 가능한 모든 경로를 따라가게끔 결정하지 않을 필요가 있다.

Global illumination is a good example. In global illumination techniques such as photon mapping, we try to shorten the path between the light and the surface we're on. In all generality, doing full global illumination would require to cast infinite amount of ray in all the directions and see which percentage hit the light. We can do that but it's going to be very slow, instead, we'll first cast photons (using the exact same algorithm as ray tracing but in reverse from the light point of view) and see what surface they hit. Then we use that information to compute lighting in first approximation at each surface point. We only follow a new ray if we can have a good idea using a couple of rays (perfect reflection needs only one additional ray). Even then it can be expensive, as the tree of rays expands at a geometric rate. We have often to limit ourself to a maximum depth recursion.

전역 조명은 좋은 예시가 된다. Photon Mapping과 같은 전역조명 기술에서 우리는 빛과 우리가 보는 표면과의 경로를 짧게 하려고 한다. 일반적으로 전체 전역 조명을 하는 것은 모든 방향에 대해서 광선들을 무한한 양을 발사해보고 빛에 충돌하는 정도를 확인할 필요가 있다. 우리는 이렇게 할 수는 있지만 이것은 매우 느릴 것이므로 대신에 우리는 먼저 광자(Raytracing과 정확하게 같은 알고리즘을 사용하지만 광원의 위치에서 시선의 역방향으로 이루어진다.)들을 던저보고 표면과 맞는 것들을 확인한다. 그리고 나서 각 표면의 점들에서의 첫번째 근사치에서 빛을 계산하기 위한 정보로 사용한다. 우리가 한쌍의 광선을 이용하는 좋은 아이디어를 떠올릴 수 있다면(완벽한 반사는 오직 하나의 추가적인 광선만 필요로 한다.)하나의 새로운 광선만 쫓으면 된다. 광선의 트리가 기하급수적으로 팽창된다면 이 방식도 비싼 연산이 될 수 있다. 우리는 보통 우리 스스로 재귀에 대한 최대 깊이에 제한을 두어야 한다.

Acceleration structure.


Even then, we'll want to go faster. Making intersection tests becomes the bottleneck if we have thousand or millions of intersectable objects in the scene and go for a linear search of intersection for each ray. Instead we definitely need an acceleration structure. Often used are hierarchical representation of a scene. Something like a KD-Tree or octree would come to mind. The structure that we'll use may depend on the type of scene that we intend to render, or the constraints such as "it needs to be updated for dynamic objects", or "we can't address more than that amount of memory", etc. Acceleration structures can also hold photons for the photon mapping, and various other things (collisions for physics).

여기까지 진행해도 더 빨라지기를 원할 것이다. 만약 씬 안에서 검사해볼 오브젝트들이 몇천~몇십만개인데 각각의 광선에 대해서 순차적으로 충돌을 검사해본다면 충돌 테스트하는 부분은 BottleNeck이 될 것이다. 대신에 우리는 가속화 할 구조가 필요해진다. 보통 사용되는 것은 씬을 계층적으로 표현하는 것이다. KD-Tree나 Octree와 같은 것들이 생각날 것이다. 우리가 사용할 그 구조는 우리가 렌더링하려는 씬의 타입이나 "동적인 오브젝트들을 위해 업데이트 될 필요가 있다." 와 같은 제약사항이나 "메모리양 이상의 주소를 접근할 수 없다."와 같은 제약사항 등등에 의존적일 수 있다. 가속화 구조들은 Photon Mapping을 위한 광자들이나 다양한 다른것들(물리연산을 위한 충돌)에 의해 홀드 될 수 있다.

We can also exploit the coherence of primary rays. Some people will go as far as implementing two solutions, one that use rasterization or similar technique for primary rays and raytracing for anything beyond that. We of course need a good mean of communication between both stages and a hardware that is sufficiently competent at both.

우리는 1차 광선의 일관성을 이용할 수 있다. 어떤 사람들은 두가지 해법을 구현할 때까지 진행할 것이다. 하나는 Rasterization을 이용하거나 1차 광선들을 이용하는 것과 비슷한 테크닉이고 또 하나는 그것들 뒤에는 레이트레이싱을 하지 않는 것이다. 물론 각 스테이지와 모두에 충분히 유용한 하드웨어간의 통신의 좋은 방식을 필요로 한다.

------------------------------------------------------------------------------------------------------------------------------------------

-0-;; 평소에 아티클들을 읽어보기는 하지만 일일이 해석해본 건 몇번안된다.

역시 한글로 의미가 통하게 변역해보려고 하니까 힘들고 어색한 부분이 많다.

잘못해석한 부분도 있을 것이고 오타나 어색한 부분도 있을 수 있다.

지속적으로 읽어보면서 그때 그때 수정하는식으로 해야 할 듯 하다.;;;

이것도 많이 하다 보면 늘겠지 -0-;;;;;; ㅋ


반응형
Posted by msparkms
,

Bullet 엔진

프로그래밍 2011. 12. 26. 13:02
반응형


유명한 공개 물리 엔진이다.

2012년 11월 26일 기준으로 bullet-2.81-rev2613.zip가 최신 SDK이다.

현재 팀프로젝트로 막 시작한 ARPG 프로젝트에 사용할 물리 엔진이다.

비교적 DirectX와 붙이기 쉬워서 사용하기로 했다.


Bullet 엔진 : http://bulletphysics.org/wordpress/

반응형
Posted by msparkms
,
반응형
 

















흠 간단하게 네트워크 연결되는 FPS 게임을 만드는 책이 있었네 -0-;;;

몰랐다... 간단하게 읽어보았는데 확실히 게임을 만드는 위주로 나와있어서 나쁘지 않다.

한번 쭉 훑어보고 따라 쳐볼만한듯 ㅎㅎ 

엔진 만들때 참고하면 괜찮을듯..
반응형
Posted by msparkms
,
반응형
잘 정리해주셨네요~
반응형
Posted by msparkms
,
반응형

원제 : fast extraction of viewing frustum planes from the world-view-projection matrix

링크 : http://crazyjoke.free.fr/doc/3D/plane%20extraction.pdf

존에 시야 절두체를 구할때는 로컬 좌표계의 절두체를 만들고 행렬을 곱해서 변환시켰었는데 

여기 소개된 방법을 이용하면 월드-뷰-프로젝션 행렬의 값을 가지고 바로 만들어 낼 수 있다.

예전에 읽어봤던 내용이였고 스터디때 발표도 했었는데 다시 보게되어서 올린다.

Direct3D, OpenGL은 Handness와 좌표계 처리가 다르기 때문에 각각 구한 내용이 있고

평면의 방정식에 대해서도 간략하지만 잘 설명해 놓았다.

안 읽어봤으면 한번쯤은 읽어볼만한 내용이다.

요즘 보는 예제들 프러스텀 컬링은 다 이 방식을 사용한다. ㅎㅎ 
반응형
Posted by msparkms
,