'전체'에 해당되는 글 74건

  1. 2008/08/27 링크모음 (1)
  2. 2008/08/25 그누보드에서 발견한 지혜 1
  3. 2008/08/21 [D3DX] Vertex Normal (2)
  4. 2008/08/20 [D3DX] ID3DMesh 의 AddRef() 메서드에 대해 (6)
  5. 2008/07/08 [D3DX] 높이맵 완성! (3)
  6. 2008/07/08 [D3DX] 맵 타일 완성!
  7. 2008/07/08 [D3DX] ID3DXBuffer
  8. 2008/07/07 [D3DX] 서브셋의 개념
  9. 2008/07/03 메모리 접근위반에 관해
  10. 2008/07/02 [오늘의 영단어]adjacent
2008/08/27 13:27

링크모음

씬그래프 이론: http://blog.naver.com/jmsoul1?Redirect=Log&logNo=130023180634

이후 계속 추가 예정
이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 1
2008/08/25 12:27

그누보드에서 발견한 지혜 1

사용자가 로그인 했을때의 처리는 크게 2가지로 나눌수가 있다.

1 . 사용자가 로그인을 했을때 세션에 그 사용자의 모든 정보를 저장해놓고 페이지가 이동할때마다 그 세션에 접근해서 사용자 데이타를 참조하느냐, 2. 아니면 사용자가 로그인 했을때 사용자의 아이디 값만 세션에 저장해놓은후
페이지가 이동될때마다 매번 db에 접근해서 필요한 정보를 뽑아 오느냐 크게 2가지로 나눌수가 있다.

나는 이때까지 전자의 방식만을 사용했는데 그이유는

1. 일단 편하다. 페이지 이동될때마다 쿼리를 날려서 db에 접근할 필요가없고, 단지 세션에만 접근하는것이다.
 2. 부하가 적다. 후자의 경우라면 매번 db를 왕복해야 하는데 이것에 드는 비용(resource)이 꽤나 클 것이다. 하지만 세션은 서버내의 data 를 저장하고 있는 변수이기때문에 훨씬 빠르다.

그런데 왜 그누보드에서는 후자의 방식으로 처리를 할까?

곰곰히 생각해봤는데, 후자의 방식에는 몇가지 이점이 있다

1. 로그인된 상태에서, 사용자의 정보가 변경될 경우(포인트가 증가되었다든가, 회원 연락처가 변경되었다든가) 아무런 추가 프로그래밍 필요하지않다. 페이지 이동시에 어차피 db에 접근할것이기 때문에, db로 부터 가져오는 데이타는 당연히 갱신된 정보일 것이다. 그러므로 추가적인 갱신 프로그래밍이 필요하지 않다는 것이다.
만약 전자였다면 사용자의 정보가 변경될때마다 이 변경된 사용자에 대응되는 모든 세션 정보를 갱신하는 코드를 새로이 추가해야 할것이다.(DB와 세션의 동기화) 이것은 상당히 귀찮은 작업이 될것이다. 대부분의 웹사이트에서 사용자의 변경은 상당히 자주 일어남을 고려할때 그누보드 방식이 개발자의 수고를 덜어준다는것을 알 수 있다.

2. 만약 웹서버가 해킹당한다면 어떻게 될까. 세션은 서버의 메모리에 저장되는 것이므로 웹서버가 해킹을 당한다면 세션또한 안전하지 않을것이다. 이 세션에 사용자의 비밀번호, 연락처, 주민번호 같은것들이 저장되어있다고 생각해보자.(전자의 방식) 상당히 위험하다. 하지만 후자의 방식으로, 즉 사용자의 아이디만 세션에 저장되어있는 상태라면 전혀 위험하지않을것이다. 사용자 아이디만 가지고 해커가 뭘 할수 있겠는가? 웹서버를 해킹했다고 DB 까지 접근 할 수는 없다. 그러므로 후자의 방식이 더 보안적인 측면에서 볼때 튼튼하다는 것이다.

이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 0
2008/08/21 10:53

[D3DX] Vertex Normal

라이팅을 계산하는 방법에는 크게 두가지가 있는데

하나는 면(폴리곤) 마다 하나씩 NormalVector(법선벡터) 를 할당해줘서 면단위로 라이팅을 계산하는 방법과
또 하나는 정점(버텍스)마다 하나씩 NormalVector 를 할당해줘서 버텍스 단위로 라이팅을 계산하는 방법이다.

전자를 사용하면 명암이 폴리곤 단위로 입혀지므로 뚝뚝 끊겨있다는 느낌을 받을것이다
반면에 후자를 사용하면 DirectX 내부적으로 버텍스 사이에 보간을 해서(추측임) 라이팅이 부드럽게 나오도록 할 것이다.
 
CreateTeapt 같은 곡면이 있는 메쉬를 화면에 그려보면 라이팅(Diffuse)이 폴리곤의 경계를 구분할수 없을 정도로 아주 부드럽게 들어가있다는 것을 발견할 수 있다.

그럼 버텍스 마다 법선벡터를 어떻게 정의해야할까?
A라는 버텍스가 있다고할때, 이 A 버텍스를 공유하는 이 폴리곤들 각각의 법선벡터를 구한후 평균값을 버텍스 A의 법선벡터로 정하면 된다.(이 버텍스를 공유하고있는 폴리곤은 1개 일수도있고 2개일수도있고. 100개가 될수도있다.)

그렇다면 버텍스 마다 자신을 공유하고있는 폴리곤들을 어떻게 관리하지.?
버텍스당 매핑되는 polygons 라는 배열변수를 선언해서 폴리곤들의 인덱스값을 넣어놓는다든가 해서 초기에 한번 계산하고 메모리에서 해제해 버리는 방법을 생각하고있다(어차피 법선벡터는 한번 계산되어진후에는 다시 계산될 필요가없으므로)

이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 2
2008/08/20 17:19

[D3DX] ID3DMesh 의 AddRef() 메서드에 대해

D3DXLoadHierachyMeshFromX() 함수를 사용할때

 HRESULT result = D3DXLoadMeshHierarchyFromX(L"mesh.X", D3DXMESH_MANAGED, m_device,
 m_allocator, NULL, &m_frameRoot, NULL);

보다시피 4번째 인자에 ID3DXAllocateHierarchy 를 구현한 객체를 하나 넘겨주게된다.
D3DXLoadMeshHierachyFromX 함수를 호출하면 m_allocator의 CrateFrame 메서드와 CreateMeshContainer 메서드를 호출하는데 여기서 CreateMeshContainer 를 호출할때 D3DXMESHDATA 의 포인터를 인자로 넘겨준다
CreateMeshContainer 메서드는 이 값을 받아서 일련의 처리를 하게되는데 다음과같다.

CreateMeshContainer(..... , D3DXMESHDATA *meshData , ... ,
                                LPD3DXMESHCONTAINER *refNewMeshContainer, ....)
{
   ....
   ....
    newMeshContainer.MeshData.pMesh = meshData.pMesh;
   *refNewMeshContainer = newMeshContainer;
}

이런식으로해서 함수의 종료부분에 새로만든 D3DXMESHCONTAINER 구조체의 포인터를 반환한다.
그런데 여기서 문제가 하나있다

CreateMeshContainer 함수가 종료된후에 pMesh 가 가리키고 있던 메모리가 해제되어버리는 일이 발생한다.
그렇게 되면 반환된 newMeshContainer 의 MeshData.pMesh 또한 쓰레기값이 되어버린다.

그럼 무엇이 문제일까.... 이거갖고 2시간은 고민했다..-ㅠ-;;

문제는 레퍼런스카운트 였다..

CreateMeshContainer 가 종료된후에 D3DXLoadHierarchyMeshFromX 함수는 자신이 넘긴 D3DXMESHDATA 의 pMesh 의 레퍼런스 카운트가 얼마인지 검사해서 증가되지않았다면 pMesh 가 가리키는 메모리를 해제해버리는것이다..(어디까지나 나의 추측이다) 그래서 pMesh 의 레퍼런스카운트를 증가해야하는데
바로이때 사용하는것이 pMesh.AddRef(); 이다. (ID3DXMesh 인터페이스에 AddRef 가 정의되어있다)
이 메서드는 프로그래머가 명시적으로 호출해줘서 pMesh 에 대한 레퍼런스카운트가 증가되었다는것을
D3DXLoadHierarchyMeshFromX 함수에 알려서 메모리 해제를 막는 기능을 할것이다.

수정된 코드는 아래와같다.

CreateMeshContainer(..... , D3DXMESHDATA *meshData , ... ,
                                LPD3DXMESHCONTAINER *refNewMeshContainer, ....)
{
   ....
   ....
    newMeshContainer.MeshData.pMesh = meshData.pMesh;
    meshData->pMesh->AddRef();
   *refNewMeshContainer = newMeshContainer;
}

이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 6
2008/07/08 21:39

[D3DX] 높이맵 완성!

사용자 삽입 이미지

::  높이맵을 적용하여 1089개의 버텍스가 렌더링된 모습

사용자 삽입 이미지



:: 높이맵정보를 가지고 있는 이미지 파일(웹에올리기 위해 jpg로 확장자 변환하였다.)



먼저 33 * 33 타일을 만들었다

버텍스수는 33 * 33 = 1089가 되므로 픽셀수도 1089개 되어야함..

33 * 33 짜리 이미지는 포토샵에서 필터 - 렌더 - Difference Clouds 을 적용하여 만들었다.

이미지 - 적용 에서 그레이스케일로한후 save as 에서 .raw 확장자로 저장하게되면

한 픽셀당 8 비트가 할당된 이미지가 만들어지게된다(총 1089바이트)

색상이 흰색에 가까울수록 255에 가깝고 검은색에 가까울수록 0에 가깝다

이제, 이 픽셀들을 버텍스들과 매핑시킬때 버텍스의 y 좌표값에 이 바이트값을 넣어주면 높이가 표현된다.

맵이미지 파일을 읽어올때는 fopen() 과 fscanf() 를 사용해서 읽었다.

읽은후에는 버텍스버퍼 락을걸고 1089 번 루프를 돌면서 각 버텍스의 y성분만 건드려주면된다.

제법 간지나게 나온듯해서 뿌듯하다 -_-흐흐..

.. 그리고 몇가지 이슈사항..

1. D3DXCreateVertexBuffer9 로 버텍스 버퍼를 생성할때 D3DUSAGE 인자를 넣어야하는데..
높이맵처럼 버텍스의 특정 성분(y좌표)만 수정이 빈번하게 이루어진다면 어떤 플래그를 넣어야할까..
D3DUSAGE_WRITEONLY, D3DUSAGE_DYNAMIC(이걸로 해봤더니 버텍스버퍼에 쓰기할때 에러난다), 등등등..
플래그가 여러개가 있는데.. 뭘쓰는게 가장 최적화에좋을지..

2. 1바이트는 0 부터 255까지 나타낼수있는데 이 값을 그대로 버텍스의 Y성분에 적용해버리면 안되고
0.03 정도를 곱해서 전체적인 수치를 좀 낮춰줘야한다.. 왜냐하면.. 버텍스 간의 간격이 1로되어있는데
높이가 255면 아예 화면에 보이지도 않을정도로 커져버리기때문..
이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 3
2008/07/08 15:44

[D3DX] 맵 타일 완성!

사용자 삽입 이미지

3D 세계의 맵을 구성하는 가장 기본적인 단위인 "타일" 이다.
아무리 거대한 맵이라도 이 타일이 합쳐져서 큰 맵하나가 되는것이다.
이제 여기에 Y (높이맵) 값만 적용해주면 그럴싸한 지형이 완성될것이다.!!

이 타일의 버텍스버퍼와 인덱스버퍼를 만드는 코드는 다음과 같이작성했다.

void Cdx3dView::initPlane()
{
 int width = 15; // 가로 버텍스수 15개
 int height = 15; //세로 버텍스수 15개
 int total = width*height; // 총 버텍스수
 float gap = 1.0f; //버텍스 간의 거리
 
 PVertex arrTemp[225];
 for(int i=0;i<width;i++) // 행
 {
  for(int j=0;j<width;j++) // 열
  {
   arrTemp[ j + i*width ].position = D3DXVECTOR3(gap*(float)j, 0.0f, gap*(float)i);  
  }
 }
 void *pTemp;

 m_pd3dDevice->CreateVertexBuffer(sizeof(PVertex)*total, 0, PVertex::FVF, D3DPOOL_MANAGED, &planeVB, 0);
 planeVB->Lock(0, sizeof(PVertex)*total, (void**)&pTemp, 0);
 memcpy(pTemp, arrTemp, sizeof(arrTemp));
 planeVB->Unlock();

 int numPolygon = (width -1) * (width -1) * 2;
 PIndex arrTemp2[392]; // 폴리곤 갯수만큼 인덱스 생성.
 int cntPolygon = 0;
 for(int i=0;i<width-1;i++)
 {
  if(cntPolygon >= numPolygon){ break; }

  for(int j=0;j<width-1;j++)
  {
   //하단 폴리곤
   arrTemp2[cntPolygon].a = i*width + j;
   arrTemp2[cntPolygon].b = i*width + (j+1);
   arrTemp2[cntPolygon].c = (i+1)*width + (j+1);
   cntPolygon++;

   //상단 폴리곤
   arrTemp2[cntPolygon].a = i*width + j;
   arrTemp2[cntPolygon].b = (i+1) * width + j;
   arrTemp2[cntPolygon].c = (i+1) * width + (j+1);
   cntPolygon++;
  }
 }

 m_pd3dDevice->CreateIndexBuffer(sizeof(PIndex)*numPolygon, 0, D3DFMT_INDEX32, D3DPOOL_MANAGED, &planeIB, 0);
 void *pIB;
 planeIB->Lock(0, sizeof(arrTemp2), (void **)&pIB, 0);
 memcpy(pIB, arrTemp2, sizeof(arrTemp2));
 planeIB->Unlock();
}

지금 이걸보면 타일의 중심이 3d 좌표계의 중심과 일치하지않는데.. 이건 좋은방법이 아닌것같다

타일의 중심과 3d 좌표계의 중심(원점)이 일치하게 하는게 좋을것같다

어차피 이 타일이라는것은 월드에서 반복적으로 그려져야 하므로.. 원점을 중심으로 하는 타일을 만들어서

이것을 재활용 하는 방식이 될것이다.. 즉 이 원점을 중심으로 하는 타일이 맵의 "템플릿"(틀) 이 될것이다.

이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 0
2008/07/08 11:16

[D3DX] ID3DXBuffer

D3DXLoadMeshFromX(....)를 호출하면 여러가지 정보를 프로그래머에게 넘겨주는데
이때 사용하는것이 ID3DXBuffer 이다

x파일로부터 메쉬를 읽어들일때, 그 메쉬의 메터리얼정보나 인접삼각형 정보까지 넘어오는데 이
정보들이 ID3DXBuffer 로 넘어온다.
ID3DXBuffer에 속한 버퍼의 타입이 결정되어있지 않으므로 명시적으로 타입을 정해야한다

받아올때 다음과같이 받아오면된다

매터리얼 정보를 받아온다고할때

ID3DXBuffer *buffer;
D3DXLoadMeshFromX( , , , ,&buffer , ) //다른 인수에대한 설명은 생략.
D3DXMATERIAL *mtrls = (D3DXMATERIAL *)(buffer -> GetBufferPointer());
mtrls[0].MatD3D // D3DMATERIAL9 구조체에 접근
mtrsl[0].pTextureFileName // 텍스쳐파일명(문자열)에 접근

이렇게 하면된다.

ID3DXBuffer 의 GetBufferPointer() 메서드는 ID3DXBuffer 가 관리하고있는 버퍼의 첫번째 메모리 블록주소를 리턴한다. 이것을 D3DXMATERIAL * 로 형변환을 하게되면 버퍼(배열)에 접근할때 오프셋이 달라지는효과가 나는것이다 D3DXMATERIAL 의 크기를 72바이트라고 가정할때, mtrls[0], mtrls[1] 둘의 메모리 블락수 차이는 72 가 되는 것이다. 즉 mtrls[1] 은 mtrls[0] 이 존재하는 메모리보다 72 블락 뒤에 있다는것이다.

이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 0
2008/07/07 17:58

[D3DX] 서브셋의 개념

하나의 메쉬에는 여러개의 서브셋이 존재할 수 있다.
서브셋이라는 개념이 필요한 이유가뭘까?
집이라는 오브젝트가 있다고해보자. 이 집은 지붕, 창문, 벽 으로 이루어져있다. 그런데 지붕, 창문, 벽의 재질이 모두 동일할까?  동일하지않다. 그래서 서브셋이라는 개념이 존재하는것이다. 집이라는 메쉬하나를 3개의 서브셋으로 분리해야만 각각의 메터리얼(재질)을 분리하여 적용할수있게된다.

텍스쳐를 적용할때도 마찬가지이다. 텍스쳐도 사실 개념적으로 메터리얼이기때문에 다이렉트X 에서
다음과같은 구조체를 만들어놓은거라고 생각한다.

D3DXMATERIAL

이 구조체를 까보면

두개의 속성이 있다

MatD3D : D3DMATERIAL9 구조체와 동일한 구조를 가진 구조체이다.
              Ambient, Diffuse, Specular, Emissive, Power 의 속성을 가졌다.

pTextureFileName : 문자열이며 텍스쳐 이미지의 파일명을 나타낸다.

x파일(맥스에서 작업한)을 읽어올때 D3DXMATERIAL 타입의 배열을 받아올수있는데,(매터리얼 버퍼라고 한다)
이 배열의 길이만큼 for문을 돌려서 메쉬를 그리면 된다.

코드로 간략하게 나타내보자.

D3DXMATERIAL materials[3]; //길이 3의 메터리얼 버퍼가 존재한다고 가정.

for(int i=0;i<3;i++){
      device -> SetMaterial(materials[i].MatD3D);
      // materials[i].pTextureFileName 을 참조하여 텍스쳐 객체(ID3DXTexture 였나?) 생성
     device -> SetTexture(....);
     mesh -> DrawSubset(i);
}

이 모든 설명을 2줄로 요약하면..

메터리얼과 서브셋은 1:1 대응관계이다.
즉, 메터리얼 1개에 대응되는 서브셋은 1개 !!! , 서브셋 1개에 대응되는 메터리얼도 1개 !!!

이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 0
2008/07/03 11:39

메모리 접근위반에 관해

지식인 QnA 펌

질문

decoding.exe의 0x00413f9d에 처리되지 않은 예외가 있습니다. 0xC0000005: 0x00000000 위치를 기록하는 동안 액세스 위반이 발생했습니다. 엑세스 위반이라는 것으로 실행이 안되는데요.

0x00413f9d 라는 부분은 어떻게 해석을 해야 할지와 엑세스 위반이라는 것은 무엇인지

답변 부탁드리겠습니다~(__)


답변

엑세스 위반(Access Violation)은, 프로세스가 접근할 권한이 없는 메모리 영역에 접근하고자 했을 때 발생합니다.


윈도우는 메모리의 모든 영역을 가상메모리 페이지 단위로 관리하는데, 이 메모리 영역마다 접근 권한을 설정합니다. 이 권한은 프로세스가 가진 속성별로 관리됩니다. 크게 사용자가 만든 사용자 프로세스와, 윈도우 시스템에서 돌리는 커널 프로세스로 구분할 수 있죠. 윈도우에서 관리하는 메모리는 커널에서만 읽고 쓸수 있는 영억, 커널에서만 읽을 수 있는 영억,  사용자프로세스에서도 읽을 수 있는 영역, 사용자 프로세스가 읽고 쓸 수 있는 영역 등으로, 가상메모리 페이지에 권한이 설정되어 있습니다.

따라서 적절한 권한이 없는 프로세스가 제한된 메모리 영역에 읽고 쓰기를 시도할 때 엑세스 위반이 발생합니다. 대표적인 예로 "0xC0000005: 0x00000000 위치를 기록하는 동안 엑세스 위반이 발생했습니다"라는 예외는 0x00000000 번지, 즉 널 포인터에 대하여 쓰기를 시도했다는 뜻이죠. 0x00000000번지로 시작하는 메모리 영역은 유저 프로세스에 대해서는 읽기/쓰기가 금지된 영역입니다. 시스템 운영에 중요한 정보가 기록되어 있기 때문이죠. 따라서 사용자가 만든 프로그램이 이 위치를 접근하고자 하는 것은 이와 같은 에러를 냅니다.

에러 메시지에서, "decoding.exe의 0x00413f9d에 처리되지 않은 예외가 있습니다. 0xC0000005: 0x00000000 위치를 기록하는 동안 액세스 위반이 발생했습니다."의 0x00413f9d는, 0x00000000위치를 접근하고자 하는 명령이 수행된, 명령이 저장된 번지수입니다. 디버깅할 때 해당 번지수의 명령이 무엇인가를 살펴보면 비교적 헤메지 않고 버그를 잡을 수 있습니다.


답변

이런 경우는 대부분..

메모리 할당이 되지 않은 포인터(NULL Pointer)에 값을 넣었거나..

할당치를 초과하여 데이터가 입력되었을 때입니다..


답변

0x00413f9d 는 decoding.exe 이란 프로그램의 code 영역 주소입니다.

즉 decoding.exe의 0x00413f9d 주소에서 예외처리가 발생했다는것입니다.

프로그램은 대부분 0x00400000이 시작번지이므로

offset 0x00013f9d에서 0xc0000005 에러가 발생했다는것입니다.


답변

0x00413f9d는 메모리의 주소를 말하는 것으로, 동적 메모리를 할당하지 않았거나

할당항 메모리를 넘어서 접근 할 경우에 이러한 에러가 나게 됩니다.


int *temp;

temp[0] = 10; // 메모리 에러(할당하지 않고 사용한 경우)


int *temp1;

temp1 = new int [10];

temp1[15] = 10; //메모리 에러(할당은 하였지만 할당항 메모리를 넘어서 사용한 경우

 
이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 0
2008/07/02 14:54

[오늘의 영단어]adjacent

adjacent [어드제이슨트]
이웃의

adjacency[어드제이선시]
이웃
인접물
이올린에 북마크하기(0) 이올린에 추천하기(0)
Trackback 0 Comment 0