연구소!!/Tips2013. 9. 6. 14:33

// operator new example

#include <iostream>     // std::cout
#include <new>          // ::operator new

struct MyClass {
  int data[100];
  MyClass() {std::cout << "constructed [" << this << "]\n";}
};

int main () {

  std::cout << "1: ";
  MyClass * p1 = new MyClass;
      // allocates memory by calling: operator new (sizeof(MyClass))
      // and then constructs an object at the newly allocated space

  std::cout << "2: ";
  MyClass * p2 = new (std::nothrow) MyClass;
      // allocates memory by calling: operator new (sizeof(MyClass),std::nothrow)
      // and then constructs an object at the newly allocated space

  std::cout << "3: ";
  new (p2) MyClass;
      // does not allocate memory -- calls: operator new (sizeof(MyClass),p2)
      // but constructs an object at p2

  // Notice though that calling this function directly does not construct an object:
  std::cout << "4: ";
  MyClass * p3 = (MyClass*) ::operator new (sizeof(MyClass));
      // allocates memory by calling: operator new (sizeof(MyClass))
      // but does not call MyClass's constructor

  delete p1;
  delete p2;
  delete p3;

  return 0;
}


'연구소!! > Tips' 카테고리의 다른 글

c++ new  (0) 2013.09.06
Optimizing Graphics for Mobile  (0) 2012.09.26
Visual Studio 폴더 설정으로 소스를 깔끔하게 관리하자!  (0) 2012.04.03
SIMD의 퍼포먼스 테스트  (0) 2012.03.26
정밀한 타이머 측정  (0) 2012.01.13
Posted by yunei

댓글을 달아 주세요

연구소!!/Tips2012. 9. 26. 14:18

Optimizing Graphics for Mobile

I was recently asked about the best way to optimize rendering for mobile devices.  I thought it would be a good idea to share those same tips.  Most of these techniques apply to rendering in general, but these tips were given with OpenGL ES in mind.  Without further ado:

Before spending any time doing a rendering optimization - _especially_ if it involves a significant re-architecture of the engine - I try to measure the potential gain.

I always use a fixed scene with a known performance measurement before beginning optimization.  This way, you know exactly your gain or loss.  Always measure performance in milliseconds if at all possible.  When dealing with “FPS” it’s tough to get an objective measure of a performance gain or loss.  An optimization that takes your game from 20 ms to 15 ms translates to a gain of “17 fps” (50 fps to 66.66 fps), while the same optimization from 45 ms to 40 ms means your frame rate only changes slightly (22.2 fps to 25 fps), but that’s the same 5 ms gain – and 5 ms is _huge_ in GPU performance-land.

The first thing I like to do is to comment out the actual eglSwapBuffers() call.  This will measure all the CPU work (setting OpenGL state, draw calls, etc) but won’t actually draw anything.  This should give you a theoretical “best performance possible” for your current rendering engine, as if drawing everything was free.  If performance doesn't change much, or at all, it’s likely you have CPU side bottlenecks that need to be addressed.  Note that you may also need to remove any glFlush() / glFinish() calls as well.

Next on my list of candidates to measure is blending.  The mobile GPUs (especially the PowerVR series) are powerful, but tend to have a problem with fill-rate and blending.  Blending requires reading and writing to the frame buffer, and can often be a big performance hit.  This one is easy to test.  Using your performance test scene, simply call glDisable(GL_BLEND) instead of enabling it and measure the change in performance.  I also often set glBlendFunc(GL_ONE, GL_ZERO) just to be safe when testing this.

Related to blending is fill-rate.  Reduce the number of pixels you’re rendering, and measure the performance gain.  You should be able to do this simply by setting glViewport() to a smaller sub-region of the screen.  If your performance increases, it means you’re fill bound, which means pixel processing (including blending, pixel shaders, per-pixel lighting calculations, and large numbers of pixels being drawn and re-drawn) are the current bottleneck.

To measure the number of pixels that might be drawn over and over again, simply change all your draw calls to use a dark red texture, and change the blend mode to GL_ONE, GL_ONE.  This will show you where you’re overdrawing too much – dark is good, bright is bad.

Textures can also impact performance and memory.  Another quick test is to create, at engine start up, a single 2x2 RGB texture and use this in place of all your other glBindTexture calls.  This will tell you if you are texture fetch bound or not.  If performance changes a lot, consider decreasing resolution, mip-mapping (actually, you should be mip-mapping all the time anyways), and changing to compressed texture formats.

Regarding mip mapping, my favorite texture filtering mode for the mobile chips is bi-linear.  This means linear filtering for up-sampling and down-sampling, but point filtering for mip levels.  It’s high quality but performant.  The next higher quality version is tri-linear (which also filters between mip levels), but that really kills a lot of the mobile chips.  Heck, it even hurts on the PS3.  You set bilinear filtering like so:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

Make sure you’re culling back faces – a lot of people miss this, and it results in things costing 2x as much to draw as they should (since the GPU is drawing the front side, AND the side you never see)

Vertex cache coherency can play a big role.  Using indexed triangle lists (i.e. glDrawElements as opposed to glDrawArrays can be huge – rather than setting 6 vertices for a simple quad, you only set 4, and pass in 6 indices into that array (something like {0, 1, 2, 0, 2, 3} for example, depending on culling and how your data is organized).  This means less data for the GPU to process, less redundant vertex data, and if you use a cache optimizer, you can re-organize your indices to keep the same vertices in the GPU cache as long as possible.

Lastly, setting the vertex data itself can have a large impact.  If you’re using the immediate mode GL methods (glVertex3f, glTexCoord2f, etc) then you’ll definitely get a big gain from vertex buffer objects.  Even if you don’t yet use VBOs, you can use a similar approach (create a vertex data array and set it with glVertexPointer, glTexCoordPointer, etc and draw with glDrawElements).

Texture atlases often come up as a good optimization.  I've only ever used a texture atlas for font rendering.  It’s the sort of low-level optimization that is often not needed.  Of course, you want to do the minimal amount of work, so you don’t want to bounce around setting different textures at random, and instead should bind a texture, draw all sprites using that texture, etc, but texture atlases are often more trouble than they’re worth IMO.

Regarding multi-texture – If you’re doing something in two or more rendering passes that can be done in a single pass with multi-texturing, multi-texturing is almost always faster.  Easy to test this, too – if you assume multi-texturing is free (even though it’s not totally free), just disable your subsequent rendering passes and measure the performance difference.  You just need to be careful about your target hardware.  OpenGL ES 1.1 requires only 2 texture stages (although many devices support more), so it’s something to keep in mind.  My engine only ever tries to use 2 stages max, because I don’t want to have to worry about compatibility with older hardware.  I believe first gen iPhone/iPod Touch uses the PowerVR chip that only supports 2 texture stages.


출처 : http://graphicsforgames.blogspot.kr/2011/04/optimizing-graphics-for-mobile.html

'연구소!! > Tips' 카테고리의 다른 글

c++ new  (0) 2013.09.06
Optimizing Graphics for Mobile  (0) 2012.09.26
Visual Studio 폴더 설정으로 소스를 깔끔하게 관리하자!  (0) 2012.04.03
SIMD의 퍼포먼스 테스트  (0) 2012.03.26
정밀한 타이머 측정  (0) 2012.01.13
Posted by yunei

댓글을 달아 주세요

연구소!!/수학2012. 4. 8. 14:02

이전까지 우리는 복소수 & 삼각함수 & 오일러의 공식에 대해 알아보았다.

특히 이전까지는 단순히 (5, 3)처럼 익숙한 데카르트 좌표계를 사용했던 것을 극 좌표계를 통해 (축, 각도) 형식으로 표현하는 법을 알았다.

다시한번 (2, 2)를 극 좌표계를 통해 나타내면

 

 

위와 같이 표현이 된다.

 

 

 

위 부분은 같은 좌표를 어떻게 표현하느냐의 방법이므로, 그렇게 이해하면 된다.

위 부분을 이해해놓는 것이 중요한데,

실제 쿼터니온을 사용하여 우리가 회전을 표현할 때 (회전축, 회전각도) 의 형태로 표현하고 사용하기 때문이다.

 

 

자 이제 쿼터니온이 어떻게 나왔는지 한번 살펴보자.

 

http://navercast.naver.com/contents.nhn?contents_id=4132&category_type=series

 

포스트를 보면 쿼터니온이 어떻게 나왔는지 자세하게 설명이 되어있다.

 

정리하자면 복소수가 수 체계에 편입된 이후로 복소수보다 더 큰 단위의 새로운 수의 체계가 편입될 수 있느냐 하는 것이다. 

 

 

에서 생각되는게 복소수가 기하학적으로 2차원을 표현하므로, 3차원을 표현하는 새로운 수를 생각하게 된다.

 

 

그래서 기본 발상은 복소수에 허수부를 하나 더 추가하여  와 같은 형태의 수를 생각하게 된다.

 

또한 복소수에서 발전되는 수이기 때문에 복소수의 기본 성질을 그대로 다 가져와야 하는데

 

문제는 곱셈에 있었다.

 

 

우리가 이전에 많이 보았듯이 복소수의 곱셈은 곧 원점을 중심으로 하는 회전을 뜻하게 된다.

 

따라서 형태를 가지는 두개의 수를 곱하면 원점을 중심으로 하는 회전 변환이 이루어져야 한다. 

2차원 복소 평면에서 0도 회전을 뜻하는 (1 + 0i)와 90도 회전을 뜻하는 (0 +1i)를 곱하면

 

이와 같은 결과가 나와 회전변환으로 계산이 이루어지듯이

 

도 마찬가지의 계산이 이루어져야 한다.

그런데 그러기 위해서는 다음과 같은 성질들을 만족해야 하는 부분들이 있다.

 

 

           -출처 : 네이버 캐스트(http://navercast.naver.com/contents.nhn?contents_id=4132&category_type=series) -

 

자, 3차원 좌표계에서 하나의 축은 실수축이고 나머지 2개의 축은 허수축이다.

여기에서

 

따라서

 

이것을 만족해야 하는데

 

와 같은  결과가 나온다. 즉 와 가 동일하다는 것인데,

문제는 가 동일하면 형태가 아닌 의 형태가 되기 때문에 만족할 수 없다.

 

해밀턴이라는 수학자는 오랫동안 이 문제에 당면하여 고민에 고민을 거듭하다가 드디어 해결책을 찾은 것이 바로

우리가 배우려고 하는 Quaternion, 즉 사원수 되신다.

 

형태의 수의 곱셈에 대해 끊임없이 고민하다, 어느날 머릿속에 전구가 번쩍 켜지며 울려퍼지길

 

'허수부는 두개가 아닌 세개'

'곱하는 순서에 따라 그 결과도 달라져야 한다'

라는 두가지 아이디어가 떠올랐다고 한다. 이 아이디어를 정리한 개념이 바로

 

이라는 것. 사원수를 처음 접할 때 항상 접하는 위 문장이 이제 조금 이해가 갈 수 있다.

i, j, k 모두는 허수이기 때문에 제곱하면 -1이라는 결과가 나와야 하고, i * j * k 역시 -1이여야 한다.

하지만 i, j, k모두 서로 다른 수가 되어야 한다.

 

그런 수가 존재하냐고? 애시당초에 허수란 개념이 존재할 수 없는 수의 개념이다.

허수라는 것은 수학적인 개념중 하나로 그저 어떤 문제를 풀기위한 도구나 마찬가지이므로, 그 개념을 정한 사람이 그렇다고 하면 그냥 그렇다고 넘기면 된다.

정확히 i라는 숫자가 어떤 숫자인지, j는 어떤 숫자인지 그런것은 신경쓰지 않아도 된다.

 

자,

와 같은 복소수 이후에 새로운 수 체계가 하나 정립되었다.

 

이 쿼터니온의 세 허수 i, j, k에 대한 서로의 곱에 대한 결과는 다음과 같다. 

이것에서 중요한 것은 곱의 순서에 따라 그 결과 값이 다르다는 것이다.

사실 곱의 순서에 따라 그 결과가 달라진다는 것은 그 이전까지의 곱셈의 교환법칙이 적용되지 않는 새로운 구조인데,

이 구조를 비가환 구조라 한다. 이건 상식선에서 그냥 알아두는것도 좋다.

 

위에 링크된 페이지에 대한 정리 아닌 정리는 이제 끝이 났다.

 

쿼터니온은 복소수에 이어 기하학적으로 3차원 공간에 대한 새로운 수 체계라 하였고,

복소수가 2차원 회전에 이용되듯이 쿼터니온은 3차원 회전에 이용된다.

 

그리고 복소수에서 발전된 개념이기 때문에 복소수의 개념이 그대로 적용된다. 단 하나 곱셈의 교환법칙은 통하지 않는다.

 

두 쿼터니온의 곱의 결과이다. 위에서 정의된 데로 허수부를 정리하면 마찬가지로 쿼터니온의 형태가 나온다.

 

그런데 쿼터니온의 곱셈을 만족하는 i, j, k를 정립하였지만

회전변환을 나타내는 곱셈은 그렇지 못하다.

 

복소평면에서의 복소수는 2차원이기 때문에 따로 축이란 개념이 없이 원점을 기준으로 하여 회전을 한다.

하지만 쿼터니온의 경우는 3차원의 좌표계 이기 때문에 회전하는 각도 이외에도 회전 축이 필요하다는 것이다.

 

즉,

 

축이 2개인 복소 평면에서 90도 회전하면 한가지의 결과밖에 안나오지만

 

 

축이 3개인 3차원 좌표계에선 90도를 회전하더라도 어느 축에 대해 회전하느냐에 따라 그 결과값이 완전히 달라지게 되는 것이다.

 

따라서

에서

a : 회전각도

b, c, d : 회전축(3차원 좌표계의 각 기저에 해당하는)

로 정의 하고 곱셈을 하는데, 결과가 그렇지 못하다는 것이다.

 

좌표공간 (1, 1, 0)에 해당하는 쿼터니온은 위에 정의한 대로 이고

i축을 기준으로 각을 회전하는 쿼터니온은 이다.

 

이 둘의 곱은

라는 결과가 나오는데,

 

좌표 * 각도 = 좌표, 즉 임의의 좌표값을 회전하면 죄표값이 결과로 나와야 하는데 실수부가 -1인 각도값이 나온다.

즉, 쿼터니온의 곱으로 회전변환을 표현할 수 없는 것이다.

 

해밀턴은 여기서 꼼수를 하나 썼는데, 바로 켤레사원수를 한번 더 곱해준 것이다.

 

 

 

처럼 실수부가 사라지고 온전하게 하나의 좌표를 나타내게 된다.

 

 

여기까지 배웠으면 이제 통상 책에서 설명하는 쿼터니온을 이요한 벡터의 회전변환 같은 부분을 이해할 수 있게 된다.

쿼터니온을 이용해서 벡터의 회전변환, 구형 보간 등등은 다른 곳에서도 자료가 많으니 따로 찾아보면 되겠다.

 

쿼터니온이랑 수가 대체 어디서 어떻게 생겼는지, 또 왜 이상하게 특수한 연산을 해야 회전을 나타내는건지에 대한 의문점들이

이번에 포스팅 하기 위해 여기저기를 찾다보니 자연스럽게 알 수 있었다.

 

참고 자료 :

http://blog.naver.com/sjahn0?Redirect=Log&logNo=60022799871

http://blog.naver.com/skout123?Redirect=Log&logNo=50136552568

http://blog.naver.com/dalsapcho?Redirect=Log&logNo=20131746494

http://navercast.naver.com/contents.nhn?contents_id=4132&path=|453|490|&leafId=644

http://navercast.naver.com/contents.nhn?contents_id=4132&path=|453|490|&leafId=644

 

사실 참고 자료라기 보다는 거의 모든 내용이 해당 자료들의 내용이다.

단지 내 스스로가 다시 헷갈릴 때 쉽게 복습하기 위해서 내 기준, 내 위주로 정리한 수준밖에 되질 않는다.

 

좀 더 정확하고 자세하게 알고 싶으면 해당 자료의 링크를 통해서 스스로 공부를 해보면 되겠다.

'연구소!! > 수학' 카테고리의 다른 글

[Quaternion] 5 Quaternion  (0) 2012.04.08
[Quaternion] 4 오일러의 공식  (0) 2012.04.07
[Quaternion] 3 삼각함수  (0) 2012.04.07
[Quaternion] 2 수체계&복소수  (3) 2012.04.07
[Quaternion] 1 소개  (0) 2012.04.06
Posted by yunei

댓글을 달아 주세요