사람이 읽을 수있는 각도로 회전 행렬을 처음부터 작성하는 방법

opengl math matrix 3d


항상 3D 프로그래밍을 방해하는 한 가지는 수학의 작동 방식을 이해하지 못하는 것입니다. 나는 메소드와 함수를 사용하여 프로그래밍 흐름에서 수학을 잘 다룰 수 있으며, 그다음에는 명확하고 논리적이지만 수학 표기법에서는 머리 나 꼬리를 만들 수 없습니다.

나는 이것을 설명하려고 노력하는 기관의 비디오를 시청하는 웹 사이트를 읽었지만 모두 수학적 표기법을 사용하며 단순히 그것을 잃어 버렸습니다. 내 마음은 이해할 수있는 것으로 번역하지 않습니다. 거기에 결함이있을 수 있습니다.

또한 누군가의 코드를 사용하는 것이 관심이 아닙니다. 코드의 역학, 논리를 이해하고 싶습니다. 다른 사람의 코드를 기꺼이 사용하고 싶지만 어떻게 작동하는지 이해하고 싶습니다.

질문

수학 표기법, 프로그래밍 표기법 / 함수 / 유사 코드, 3 축 모두를 따라 행렬 변환을 구현하는 방법 없이 간단한 용어로 나에게 설명 할 수 있습니까 ?

이상적으로 내가 원하는 것은 glRotate와 비슷한 3 축의 각도를 정의하여 내가 가지고있는 사각형 / 삼각형 모음을 회전시키는 방법 / 객체를 작성하는 소재 / 이해입니다. (표시 목록에서 무언가가 바뀔 때마다 한 번의 호출로 수행되므로 OpenGL 함수에 액세스하지 않고 큐브 모양의 3D 회전을 프로그래밍하려고합니다.)

내가 무슨 짓을 한?

나는 수학의 걸림돌을 얻기 위해 90도 변환 함수를 만들려고 시도했지만 이론적으로 가장해야했던 적절한 행렬을 만드는 데 완전히 실패했습니다. http://jsfiddle.net/bLfg0tj8/5/ 에서 내 실패의 모든 시도를 볼 수 있습니다.

Vec3 = function(x,y,z) {
    this.x = x;
    this.y = y;
    this.z = z;
}
Matrix = function Matrix() {
    this.matrixPoints = new Array();    
    this.rotationPoint = new Vec3(0,0,0);
    this.rotationAngle = 90;
}
Matrix.prototype.addVector = function(vector) {
    this.matrixPoints.push(vector);
}
Matrix.prototype.setRotationPoint = function(vector) {
    this.rotationPoint = vector; 
}
Matrix.prototype.setRotationAngle = function(angle) {
    this.rotationAngle = angle;
}
Matrix.prototype.populate = function() {
    translateToOrigin =     [[1,0,0-this.rotationPoint.x],
                                  [0,1,0-this.rotationPoint.y],
                                  [0,0,0-this.rotationPoint.z]];
    rotationMatrix =         [[0,-1,0],
                                  [0,1,0],
                                  [0,0,1]];
    translateEnd =         [[1,0,this.rotationPoint.x],
                                  [0,1,this.rotationPoint.y],
                                  [0,0,this.rotationPoint.z]];
    currentColumn = 0;
    currentRow = 0;
    this.combomatrix = this.mergeMatrices(this.mergeMatrices(translateEnd,rotationMatrix),
                                          translateToOrigin);
}
Matrix.prototype.transform = function() {
    newmatrix = new Array();
    for(c = 0;c<this.matrixPoints.length;c++) {
        newmatrix.push(this.applyToVertex(this.matrixPoints[c]));
    }
    return newmatrix;
}
Matrix.prototype.applyToVertex = function(vertex) {
    ret = new Vec3(vertex.x,vertex.y,vertex.z);
    ret.x = ret.x + this.combomatrix[0][0] * vertex.x +
            this.combomatrix[0][1] * vertex.y +
            this.combomatrix[0][2] * vertex.z;
    
    ret.y = ret.y + this.combomatrix[1][0] * vertex.x +
            this.combomatrix[1][1] * vertex.y +
            this.combomatrix[1][2] * vertex.z;
    
    ret.z = ret.z + this.combomatrix[2][0] * vertex.x +
            this.combomatrix[2][1] * vertex.y +
            this.combomatrix[2][2] * vertex.z;
    return ret;
}
Matrix.prototype.mergeMatrices = function(lastStep, oneInFront) {
    step1 = [[0,0,0],[0,0,0],[0,0,0]];
    step1[0][0] =   lastStep[0][0] * oneInFront[0][0] + 
                    lastStep[0][1] * oneInFront[1][0] + 
                    lastStep[0][2] * oneInFront[2][0];
    
    step1[0][1] =   lastStep[0][0] * oneInFront[0][1] + 
                    lastStep[0][1] * oneInFront[1][1] + 
                    lastStep[0][2] * oneInFront[2][1];
    
    step1[0][2] =   lastStep[0][0] * oneInFront[0][2] + 
                    lastStep[0][1] * oneInFront[1][2] + 
                    lastStep[0][2] * oneInFront[2][2];
    //============================================================
    step1[1][0] =   lastStep[1][0] * oneInFront[0][0] + 
                    lastStep[1][1] * oneInFront[1][0] + 
                    lastStep[1][2] * oneInFront[2][0];
    
    step1[1][1] =   lastStep[1][0] * oneInFront[0][1] + 
                    lastStep[1][1] * oneInFront[1][1] + 
                    lastStep[1][2] * oneInFront[2][1];
    
    step1[1][2] =   lastStep[1][0] * oneInFront[0][2] + 
                    lastStep[1][1] * oneInFront[1][2] + 
                    lastStep[1][2] * oneInFront[2][2];
    //============================================================
    step1[2][0] =   lastStep[2][0] * oneInFront[0][0] + 
                    lastStep[2][1] * oneInFront[1][0] + 
                    lastStep[2][2] * oneInFront[2][0];
    
    step1[2][1] =   lastStep[2][0] * oneInFront[0][1] + 
                    lastStep[2][1] * oneInFront[1][1] + 
                    lastStep[2][2] * oneInFront[2][1];
    
    step1[2][2] =   lastStep[2][0] * oneInFront[0][2] + 
                    lastStep[2][1] * oneInFront[1][2] + 
                    lastStep[2][2] * oneInFront[2][2];
    return step1;
}
Matrix.prototype.getCurrentMatrix = function() {
    return this.matrixPoints;
}
myvectors = [new Vec3(50,50,0), new Vec3(20,80,0), new Vec3(80, 80, 0)];

function drawVectors(vectors,color) {
    for(c=0;c<vectors.length;c++) {
        document.getElementById("whoa").innerHTML += '<div style="color:'+color+';position:absolute;left:'+vectors[c].x+'px; top:'+vectors[c].y+'px;z-index:'+vectors[c].z+';">('+c+').</div>';
    }
}
matrix = new Matrix();
for(c=0;c<myvectors.length;c++) {
    matrix.addVector(myvectors[c]);
}
matrix.setRotationPoint(new Vec3(50,70,0));
matrix.populate();
somematrix = matrix.transform();
drawVectors(matrix.getCurrentMatrix(),"lime"); // draw current matrix that was hand coded
drawVectors([matrix.rotationPoint],'white'); // draw rotation point
drawVectors(somematrix,"red"); // transformed matrix... somehow two points merge
<div id="whoa" style="position:relative;top:50px;left:150px;background-color:green;color:red;width:400px;height:300px;">
    &nbsp;
</div>

녹색 텍스트는 원래 삼각형, 흰색 점 중심점, 빨간색 점 변환에 실패했습니다 (중심점 주위에 정렬되지 않기 때문에 생각합니다). 튜토리얼에서 매트릭스를 결합 된 매트릭스로 결합하는 방법을 생각했지만 어딘가에 망친 것 같습니다.

내가 말했듯이, 수학 표기법을 이해하고 말하기가 정말 어렵습니다. 그리고 대부분의 교사는 설명의 일부를 건너 뛰는 것이 도움이되지 않습니다. 행렬을 곱할 때 이해하는 데 2 ​​시간 만 걸렸으므로 곱셈을 유지하는 대신 각 단계를 함께 추가해야합니다. 예, 설명이 필요합니다.

내가 함께 일하고 싶어하는 실제적인 예

예를 들어, 월드에 위치한 wavefront obj 파일에서로드 된 큐브가 있습니다.

x = 50
y = 100
z = 200

큐브는 쿼드와 일부 uv 매핑을 사용하여 그려집니다. 문제 없습니다. 모든 텍스처가 올바르게 표시되어 아름답게 렌더링됩니다.

이들은 사각형을 사용하여 그려진 큐브의 각 "면"에 대한 위치 좌표입니다.

// Front face
-1.0, -1.0,  1.0,
 1.0, -1.0,  1.0,
 1.0,  1.0,  1.0,
-1.0,  1.0,  1.0,

// Back face
-1.0, -1.0, -1.0,
-1.0,  1.0, -1.0,
 1.0,  1.0, -1.0,
 1.0, -1.0, -1.0,

// Top face
-1.0,  1.0, -1.0,
-1.0,  1.0,  1.0,
 1.0,  1.0,  1.0,
 1.0,  1.0, -1.0,

// Bottom face
-1.0, -1.0, -1.0,
 1.0, -1.0, -1.0,
 1.0, -1.0,  1.0,
-1.0, -1.0,  1.0,

// Right face
 1.0, -1.0, -1.0,
 1.0,  1.0, -1.0,
 1.0,  1.0,  1.0,
 1.0, -1.0,  1.0,

// Left face
-1.0, -1.0, -1.0,
-1.0, -1.0,  1.0,
-1.0,  1.0,  1.0,
-1.0,  1.0, -1.0

그래서 이것은 모두 훌륭하게 작동합니다. 그러나이 큐브가 x 축을 따라 90도, z 축을 따라 45도 회전하려면 어떻게해야합니까? 데이터를 테셀 레이터 객체에 전달하는 순간 glRotate를 사용할 수 없습니다. 실제로 데이터 자체를 렌더링하지 않고 데이터를 가져 오기 때문에 opengl 함수를 통해 매트릭스 변환을 수행 할 수 없기 때문입니다.

데이터가 저장되는 방식은 다음과 같습니다.

WaveFrontObject()
   |
   |-> Groups(String groupname)
        |
        |-> Faces()
              |
              |-> Vertex(float x, float y, float z)[] 
              |-> Float UVmap[] corresponding to each vertex
              |-> drawFace() // Draws the face as a quad or triangle

위의 각 좌표는 "cube"그룹에서 wavefront 객체의면으로 저장됩니다.

큐브가 테셀 레이터에 추가되면 월드의 올바른 좌표로 변환되어 정상적으로 렌더링됩니다.

그러나 항상 동일하게 렌더링됩니다. 각도로 렌더링하려면이 시점에서 별도의 웨이브 프론트 오브젝트를 만들어야합니다. 내 의견으로는 수학으로 해결할 수있을 때 할 수있는 광기입니다.

답변에 필요

  1. 변환 행렬을 작성하는 방법과 수학을 설명하는 방법을 단계별로 설명하십시오.
  2. 변환 행렬을면의 쿼드 / 삼각형에 적용하는 방법

    x = 50.5 y = 100.5 z = 200.5

  3. 설명과 함께 사용할 예제 / 의사 코드.

설명하는 데 사용 된 프로그래밍 언어는 C 계열에있는 한 실제로 관련이 없습니다.

수학 표기법 / 말을 피하십시오. 나는 알파 베타가 무엇인지, 세타가 무엇인지, x 축, y 축 및 z 축이 무엇인지 알고 있습니다. 나는 어떤 각도인지는 알지만 수학자들이 찾은 이름을 모른다.

수학 이름을 사용하려면 3D 세계 / 코드에 무엇이 있고 어떻게 작성 / 계산되는지 설명하십시오.

나는 단순히 라인을 따라 메소드 / 객체를 만들고 싶다.

Matrix.transformVertices(vertices[], 90deg x, 45 deg y, 0 deg z);



Answer 1 Spektre


따라서 문제는 실제로 4x4 균질 변환 행렬 이해

남은 유일한 배후에 수학이 없다면 기하학적 표현 / 의미만으로 인간의 추상화 / 이해에 훨씬 좋습니다.

1. 4x4 매트릭스는 무엇입니까?

직교 좌표계의 표현이며 다음과 같이 구성됩니다.

  1. 3 개의 기본 벡터 (각 축당 하나씩) 빨강, 녹색, 파랑

    따라서 빨강, 녹색, 파랑 벡터가 서로 직교 하면 좌표계는 직교 합니다. 그것들이 또한 단위 벡터라면, 직교 정규입니다 (예를 들어 단위 행렬).

  2. 원점 회색

  3. 영사 및 균질면 (표시되지 않은 매트릭스의 나머지)

    이 제품은 따라서 균질 있어야 사용 지점 만 번 회전 및 번역을 가능하게하기 위해 존재하는 형상 수단 (x,y,z,w=1) 점 대 (x,y,z,w=0) 방향 벡터. 그것이 단지 (x,y,z) 라면 행렬은 3x3 이 될 것이고 이것은 번역하기에 충분하지 않습니다. 기하학적으로 설명하기 어려운 돌출부를 사용하지 않습니다.

이 레이아웃은 OpenGL 표기법으로 작성 되었으며 거기에 옮긴 표현도 있습니다 (벡터는 열이 아닌 행입니다)

이제이 좌표계에서 점으로 변환하는 방법 :

g=M*l;
l=Inverse(M)*g;

where:

  • M 은 변환 행렬입니다
  • lM 로컬 좌표계 점 (LCS)입니다.
  • g 는 GCS (global coordinate system point)입니다.

조옮김 버전 ( DirectX )의 경우 :

l=M*g;
g=Inverse(M)*l;

전치 된 직교 회전 행렬도 그 자체와 반대 이기 때문 입니다.

OpenGL transform matrix

2. 그것을 시각화하는 방법

그렇습니다. 매트릭스 숫자를 그릴 수는 있지만 처음에는 숫자가 변경되는 경우에는 의미가 없으므로 위의 그림과 같이 축 벡터를 그립니다. 각 축은 origin 에서 선까지의 선 origin + line_size*axis_vector

3. 그것을 구성하는 방법

축 벡터와 원점을 계산하여 행렬 안에 넣으십시오. 직교성을 보장하기 위해 교차 제품을 이용하십시오 (그러나 올바른 방향을 사용하도록 승수 순서에주의하십시오)

4. 효과

  • 회전은 축을 회전시켜 수행되므로 파라 메트릭 원 방정식으로 각 축을 계산할 수 있습니다 ...
  • 축에 축척 비율을 곱하여 축척을 수행합니다.
  • 기울어 짐은 수직이 아닌 축을 사용하고 있습니다.

5. 회전

대부분의 경우 증분 회전이 사용됩니다. 두 가지 유형이 있습니다

  • local rotation M'=M*rotation_matrix 평면이나 자동차 또는 플레이어를 제어하는 ​​것처럼 로컬 좌표 축을 중심으로 회전 합니다 ... 대부분의 엔진 / 게임은 이것을 사용하지 않고 대신 오일러 각도로 가짜로 만듭니다. OpenGL을 사용하는 대부분의 사람들은 이것이 가능하다는 것을 알지 못하기 때문에 glRotate/glTranslate 호출 의 스택 목록을 ...

  • 글로벌 회전 M'=Inverse(Inverse(M)*rotation_matrix) 글로벌 좌표계 축을 중심으로 회전합니다.

여기서 rotation_matrix 는 표준 회전 변환 행렬입니다.

다른 매트릭스 레이아웃 (조옮김)을 사용하는 경우 로컬 및 글로벌 회전은 다른 방법으로 계산됩니다 ...

다음 과 같은 3 개의 각도 에서 rotation_matrix 를 계산할 수도 있습니다 .

rotation_matrix=rotation_around_x(ax)*rotation_around_y(ay)*rotation_around_z(az);

Wiki 회전 행렬 참조 Basic rotations 의 3D Rx,Ry,Rz 가 필요합니다. 보시다시피 그들은 실제로 단위 원 매개 변수 방정식입니다. 곱셈 순서는 각도가 목표 위치로 수렴하는 방식을 변경합니다. 이것을 오일러 각도 라고하며 사용하지 않습니다 (단순히 변경하지 않고 단계 변경을 통합하면 더 간단하게 언급하지 않으면 제한이 없습니다).

어쨌든 변환 행렬을 오일러 각도로 변환하면 비교적 쉽게 볼 수 있습니다.

6. glRotate

glRotate 를 원한다면 3 각이 아닌 축을 중심으로 회전하기 때문에 쿼터니언을 대신 사용해야합니다! 해결 방법이 있습니다.

  1. 해당 축에 대해 변환 행렬 N 을 만듭니다.
  2. 그런 다음 행렬 M 을 변환하십시오.
  3. 각도로 N 회전
  4. 그런 다음 MN 에서 전역 좌표로 다시 변환하십시오.

또는 대신 Rodrigues_rotation_formula 를 사용할 수 있습니다

이 경우 매트릭스를 매트릭스로 /로부터 변환하려면 축을 점으로 변환하고 원점을 그대로 유지하지만 N 의 원점은 (0,0,0)이어야합니다! 또는 변형 된 벡터는 w=0 이어야합니다 .

7. 사용법

변환은 다음을 의미하는 누적입니다.

  • p'=M1*M2*M3*M4*p; M=M1*M2*M3*M4; p'=M*p 와 동일하고 ; p '= M * p

따라서 변환 할 점이 많은 경우 모든 변환을 단일 행렬로 미리 계산하여 사용하십시오. 이후의 모든 행렬에 포인트를 곱할 필요는 없습니다. 이제 개념은 다음과 같습니다.

3 개의 좌표계 가 있어야 합니다.

  • 카메라 C
  • 세계 (일반적으로 단위 행렬)
  • 객체 O (각 객체는 자체 매트릭스를 가짐)

따라서 8 개의 꼭지점이 p0,...,p7 인 큐브가있는 경우 객체 로컬 좌표에서 카메라 로컬 좌표로 각 점에서 변환을 수행해야합니다. 일부 gfx API는 그중 일부를 수행하므로 필요한 것만 적용하면 실제로 필요합니다.

  • p(i)'=inverse(C)*unit*M*p(i);

변환은 누적되며 단위 행렬은 아무것도 변경하지 않습니다.

  • Q=inverse(C)*M; p(i)'=Q*p(i);

따라서 그려진 객체에 대해 Q 를 계산하기 전에 객체의 각 점 p(i) 를 가져 와서 변환 된 p(i)' 계산하고 변환 된 것을 그립니다. p(i)' 는 로컬 카메라 좌표계에 있습니다. (화면의 x, y). 그러나 그리기 전에 투시가 없으므로 투영 행렬을 추가 하고 끝에서 z 좌표로 나눌 수 있습니다 ... 투영도 누적되므로 Q 안에있을 수도 있습니다

[edit1] C ++ 예제

//$$---- Form CPP ----
//---------------------------------------------------------------------------
// apart from math.h include you can ignore this machine generated VCL related code
#include <vcl.h>
#pragma hdrstop
#include "win_main.h"
#include <math.h>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main; // pointer to main window ...
//---------------------------------------------------------------------------
// Here is the important stuff some math first
//---------------------------------------------------------------------------
const double deg=M_PI/180.0;
double divide(double x,double y);
void  matrix_mul       (double *c,double *a,double *b); // c[16] = a[16] * b[16]
void  matrix_mul_vector(double *c,double *a,double *b); // c[ 4] = a[16] * b[ 4]
void  matrix_subdet    (double *c,double *a);           // c[16] = all subdets of a[16]
double matrix_subdet   (          double *a,int r,int s);//      = subdet(r,s) of a[16]
double matrix_det      (          double *a);           //       = det of a[16]
double matrix_det      (          double *a,double *b); //       = det of a[16] and subdets b[16]
void  matrix_inv       (double *c,double *a);           // c[16] = a[16] ^ -1
//---------------------------------------------------------------------------
double divide(double x,double y)
        {
        if (!y) return 0.0;
        return x/y;
        }
void  matrix_mul       (double *c,double *a,double *b)
        {
        double q[16];
        q[ 0]=(a[ 0]*b[ 0])+(a[ 1]*b[ 4])+(a[ 2]*b[ 8])+(a[ 3]*b[12]);
        q[ 1]=(a[ 0]*b[ 1])+(a[ 1]*b[ 5])+(a[ 2]*b[ 9])+(a[ 3]*b[13]);
        q[ 2]=(a[ 0]*b[ 2])+(a[ 1]*b[ 6])+(a[ 2]*b[10])+(a[ 3]*b[14]);
        q[ 3]=(a[ 0]*b[ 3])+(a[ 1]*b[ 7])+(a[ 2]*b[11])+(a[ 3]*b[15]);
        q[ 4]=(a[ 4]*b[ 0])+(a[ 5]*b[ 4])+(a[ 6]*b[ 8])+(a[ 7]*b[12]);
        q[ 5]=(a[ 4]*b[ 1])+(a[ 5]*b[ 5])+(a[ 6]*b[ 9])+(a[ 7]*b[13]);
        q[ 6]=(a[ 4]*b[ 2])+(a[ 5]*b[ 6])+(a[ 6]*b[10])+(a[ 7]*b[14]);
        q[ 7]=(a[ 4]*b[ 3])+(a[ 5]*b[ 7])+(a[ 6]*b[11])+(a[ 7]*b[15]);
        q[ 8]=(a[ 8]*b[ 0])+(a[ 9]*b[ 4])+(a[10]*b[ 8])+(a[11]*b[12]);
        q[ 9]=(a[ 8]*b[ 1])+(a[ 9]*b[ 5])+(a[10]*b[ 9])+(a[11]*b[13]);
        q[10]=(a[ 8]*b[ 2])+(a[ 9]*b[ 6])+(a[10]*b[10])+(a[11]*b[14]);
        q[11]=(a[ 8]*b[ 3])+(a[ 9]*b[ 7])+(a[10]*b[11])+(a[11]*b[15]);
        q[12]=(a[12]*b[ 0])+(a[13]*b[ 4])+(a[14]*b[ 8])+(a[15]*b[12]);
        q[13]=(a[12]*b[ 1])+(a[13]*b[ 5])+(a[14]*b[ 9])+(a[15]*b[13]);
        q[14]=(a[12]*b[ 2])+(a[13]*b[ 6])+(a[14]*b[10])+(a[15]*b[14]);
        q[15]=(a[12]*b[ 3])+(a[13]*b[ 7])+(a[14]*b[11])+(a[15]*b[15]);
        for(int i=0;i<16;i++) c[i]=q[i];
        }
void  matrix_mul_vector(double *c,double *a,double *b)
        {
        double q[3];
        q[0]=(a[ 0]*b[0])+(a[ 1]*b[1])+(a[ 2]*b[2])+(a[ 3]);
        q[1]=(a[ 4]*b[0])+(a[ 5]*b[1])+(a[ 6]*b[2])+(a[ 7]);
        q[2]=(a[ 8]*b[0])+(a[ 9]*b[1])+(a[10]*b[2])+(a[11]);
        for(int i=0;i<3;i++) c[i]=q[i];
        }
void  matrix_subdet    (double *c,double *a)
        {
        double   q[16];
        int     i,j;
        for (i=0;i<4;i++)
         for (j=0;j<4;j++)
          q[j+(i<<2)]=matrix_subdet(a,i,j);
        for (i=0;i<16;i++) c[i]=q[i];
        }
double matrix_subdet    (         double *a,int r,int s)
        {
        double   c,q[9];
        int     i,j,k;
        k=0;                            // q = sub matrix
        for (j=0;j<4;j++)
         if (j!=s)
          for (i=0;i<4;i++)
           if (i!=r)
                {
                q[k]=a[i+(j<<2)];
                k++;
                }
        c=0;
        c+=q[0]*q[4]*q[8];
        c+=q[1]*q[5]*q[6];
        c+=q[2]*q[3]*q[7];
        c-=q[0]*q[5]*q[7];
        c-=q[1]*q[3]*q[8];
        c-=q[2]*q[4]*q[6];
        if (int((r+s)&1)) c=-c;       // add signum
        return c;
        }
double matrix_det       (         double *a)
        {
        double c=0;
        c+=a[ 0]*matrix_subdet(a,0,0);
        c+=a[ 4]*matrix_subdet(a,0,1);
        c+=a[ 8]*matrix_subdet(a,0,2);
        c+=a[12]*matrix_subdet(a,0,3);
        return c;
        }
double matrix_det       (         double *a,double *b)
        {
        double c=0;
        c+=a[ 0]*b[ 0];
        c+=a[ 4]*b[ 1];
        c+=a[ 8]*b[ 2];
        c+=a[12]*b[ 3];
        return c;
        }
void  matrix_inv       (double *c,double *a)
        {
        double   d[16],D;
        matrix_subdet(d,a);
        D=matrix_det(a,d);
        if (D) D=1.0/D;
        for (int i=0;i<16;i++) c[i]=d[i]*D;
        }
//---------------------------------------------------------------------------
// now the object representation
//---------------------------------------------------------------------------
const int pnts=8;
double pnt[pnts*3]=     // Vertexes for 100x100x100 cube centered at (0,0,0)
    {
    -100.0,-100.0,-100.0,
    -100.0,+100.0,-100.0,
    +100.0,+100.0,-100.0,
    +100.0,-100.0,-100.0,
    -100.0,-100.0,+100.0,
    -100.0,+100.0,+100.0,
    +100.0,+100.0,+100.0,
    +100.0,-100.0,+100.0,
    };
const int facs=6;
int fac[facs*4]=        // faces (index of point used) no winding rule
    {
    0,1,2,3,
    4,5,6,7,
    0,1,5,4,
    1,2,6,5,
    2,3,7,6,
    3,0,4,7,
    };
double rep[16]=        // 4x4 transform matrix of object (unit from start) at (0,0,+100)
    {
    1.0,0.0,0.0,  0.0,
    0.0,1.0,0.0,  0.0,
    0.0,0.0,1.0,100.0,
    0.0,0.0,0.0,1.0,
    };
double eye[16]=        // 4x4 transform matrix of camera at (0,0,-150)
    {
    1.0,0.0,0.0,   0.0,
    0.0,1.0,0.0,   0.0,
    0.0,0.0,1.0,-150.0,
    0.0,0.0,0.0,1.0,
    };
//---------------------------------------------------------------------------
// this is how to draw it
//---------------------------------------------------------------------------
void obj(double *pnt,int pnts,int *fac,int facs,double *rep,double *ieye)
    {
    // variables for drawing
    int i;
    double p0[3],p1[3],p2[3],p3[3],m[16],d;
    // gfx api variables (change to your stuff) Main is the main form of this application
    TCanvas *scr=Main->bmp->Canvas;
    double xs2=Main->ClientWidth/2,ys2=Main->ClientHeight/2;
    double v=xs2*tan(30.0*deg); // 60 degree viewing angle perspective projection

    matrix_mul(m,ieye,rep);             // cumulate all needed transforms

    for (i=0;i<facs*4;)                 // go through all faces
        {
        // convert all points of face
        matrix_mul_vector(p0,m,&pnt[fac[i]*3]); i++;
        matrix_mul_vector(p1,m,&pnt[fac[i]*3]); i++;
        matrix_mul_vector(p2,m,&pnt[fac[i]*3]); i++;
        matrix_mul_vector(p3,m,&pnt[fac[i]*3]); i++;
        // here goes perspective divide by z coordinate if needed
        d=divide(v,p0[2]); p0[0]*=d; p0[1]*=d;
        d=divide(v,p1[2]); p1[0]*=d; p1[1]*=d;
        d=divide(v,p2[2]); p2[0]*=d; p2[1]*=d;
        d=divide(v,p3[2]); p3[0]*=d; p3[1]*=d;
        // here is viewport transform (just translate (0,0) to middle of screen in this case
        p0[0]+=xs2; p0[1]+=ys2;
        p1[0]+=xs2; p1[1]+=ys2;
        p2[0]+=xs2; p2[1]+=ys2;
        p3[0]+=xs2; p3[1]+=ys2;
        // draw quad
        // I use VCL GDI TCanvas you use what you have ...
        // and wireframe only to keep this simple (no Z buffer,winding culling,...)
        scr->Pen->Color=clAqua;     // perimeter wireframe
        scr->MoveTo(p0[0],p0[1]);
        scr->LineTo(p1[0],p1[1]);
        scr->LineTo(p2[0],p2[1]);
        scr->LineTo(p3[0],p3[1]);
        scr->LineTo(p0[0],p0[1]);
//      scr->Pen->Color=clBlue;     // face cross to visualy check if I correctly generate the fac[]
//      scr->MoveTo(p0[0],p0[1]);
//      scr->LineTo(p2[0],p2[1]);
//      scr->MoveTo(p1[0],p1[1]);
//      scr->LineTo(p3[0],p3[1]);
        }
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void TMain::draw()
    {
    if (!_redraw) return;
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));

    // compute inverse of camera need to compute just once for all objects
    double ieye[16];
    matrix_inv(ieye,eye);
    // draw all objects
    obj(pnt,pnts,fac,facs,rep,ieye);

    Main->Canvas->Draw(0,0,bmp);
    _redraw=false;
    }
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
    {
    // window constructor you can ignore this ... (just create a backbuffer bitmap here)
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    pyx=NULL;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
    {
    // window destructor release memory ... also ignoe this
    if (pyx) delete pyx;
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
    {
    // on resize event ... just resize/redraw backbuffer also can ignore this
    xs=ClientWidth;  xs2=xs>>1;
    ys=ClientHeight; ys2=ys>>1;
    bmp->Width=xs;
    bmp->Height=ys;
    if (pyx) delete pyx;
    pyx=new int*[ys];
    for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
    {
    // repaint event can ignore
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
    {
    // timer event to animate the cube ...
    _redraw=true;

    // rotate the object to see it in motion
    double ang,c,s;

    ang=5.0*deg; c=cos(ang); s=sin(ang);    // rotate baround z by 5 degrees per timer step
    double rz[16]= { c, s, 0, 0,
                    -s, c, 0, 0,
                     0, 0, 1, 0,
                     0, 0, 0, 1 };

    ang=1.0*deg; c=cos(ang); s=sin(ang);    // rotate baround x by 1 degrees per timer step
    double rx[16]= { 1, 0, 0, 0,
                     0, c, s, 0,
                     0,-s, c, 0,
                     0, 0, 0, 1 };
    matrix_mul(rep,rep,rz);
    matrix_mul(rep,rep,rx);

    draw();
    }
//---------------------------------------------------------------------------

다음은 그 모습입니다 :

cube example

뒷면 컬링 기능이있는 GIF 애니메이션 :

animation

[notes]

더 궁금한 점이 있으면 저에게 의견을 말하십시오 ...

[Edit2] 종종 필요한 기본 3D 벡터 작업

교차 / 도트 곱 또는 절대 값과 같은 벡터 연산을 계산하는 방법을 모르는 경우 다음을 참조하십시오.

// cross product: W = U x V
W.x=(U.y*V.z)-(U.z*V.y)
W.y=(U.z*V.x)-(U.x*V.z)
W.z=(U.x*V.y)-(U.y*V.x)
// dot product: a = (U.V)
a=U.x*V.x+U.y*V.y+U.z*V.z
// abs of vector a = |U|
a=sqrt((U.x*U.x)+(U.y*U.y)+(U.z*U.z))

여기 내 C ++ 벡터 수학 :

static double vector_tmp[3];
double divide(double x,double y) { if ((y>=-1e-30)&&(y<=+1e-30)) return 0.0; return x/y; }
double* vector_ld(double x,double y,double z)          { double *p=vector_tmp; p[0]=x; p[1]=y; p[2]=z; return p;}
double* vector_ld(double *p,double x,double y,double z) {                      p[0]=x; p[1]=y; p[2]=z; return p;}
void  vector_copy(double *c,double *a)         { for(int i=0;i<3;i++) c[i]=a[i];       }
void  vector_abs(double *c,double *a)          { for(int i=0;i<3;i++) c[i]=fabs(a[i]); }
void  vector_one(double *c,double *a)
        {
        double l=divide(1.0,sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])));
        c[0]=a[0]*l;
        c[1]=a[1]*l;
        c[2]=a[2]*l;
        }
void  vector_len(double *c,double *a,double l)
        {
        l=divide(l,sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])));
        c[0]=a[0]*l;
        c[1]=a[1]*l;
        c[2]=a[2]*l;
        }
void  vector_neg(double *c,double *a)          { for(int i=0;i<3;i++) c[i]=-a[i];      }
void  vector_add(double *c,double *a,double *b) { for(int i=0;i<3;i++) c[i]=a[i]+b[i]; }
void  vector_sub(double *c,double *a,double *b) { for(int i=0;i<3;i++) c[i]=a[i]-b[i]; }
void  vector_mul(double *c,double *a,double *b) // cross
        {
        double   q[3];
        q[0]=(a[1]*b[2])-(a[2]*b[1]);
        q[1]=(a[2]*b[0])-(a[0]*b[2]);
        q[2]=(a[0]*b[1])-(a[1]*b[0]);
        for(int i=0;i<3;i++) c[i]=q[i];
        }
void  vector_mul(double *c,double *a,double  b) { for(int i=0;i<3;i++) c[i]=a[i]*b; }
void  vector_mul(double *c,double  a,double *b) { for(int i=0;i<3;i++) c[i]=a*b[i]; }
double vector_mul(         double *a,double *b) { double c=0; for(int i=0;i<3;i++) c+=a[i]*b[i]; return c; } // dot
double vector_len(double *a) { return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])); }
double vector_len2(double *a) { return (a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2]); }

[Edit3] 키보드를 통한 카메라 및 객체 제어를위한 로컬 회전

이것은 최근에 많은 질문을 받았으므로 데모를 사용하여 내 예를 들어보십시오.