透视投影场景下如何使用面法线进行背面剔除

How to use face normal for back-face culling in perspective projection scenes

我正在用 C++ 编写低级 2D/3D 渲染引擎作为 MCU 平台显示驱动程序的一部分,但我在 3D 透视投影和面部剔除方面遇到了困难。

假设我的 3D 引擎正在使用 M,V,P 矩阵(与 OpenGL 固定管道中的模型、视图和投影矩阵的方式相同)。

思路是将光栅化的人脸法线转换为视图局部坐标(使用MV),并测试视图方向对应的坐标符号。相机视角方向和法线本身之间的点积也可以这样做。并根据标志要么栅格化要么跳过脸。这适用于平行射线投影......但是对于透视投影,这会导致误报(你可以看到“视觉上”倾斜到某个角度的面)。

对于填充表面,这只会影响性能,因为深度缓冲区会补救伪像,因此渲染看起来应该如此。然而线框是个问题:

补救方法是通过MVP变换面的顶点并进行透视分割。然后根据结果重新计算法线并将其用于面部剔除:

然而,在像 MCU 这样的慢速平台上,更多的操作可能会造成性能问题。所以我的问题是:

如果可以如何安全地使用面法线进行背面剔除?

我尝试通过 MVP 转换面中心及其在法线方向上的小位移来局部转换法线,然后使用透视划分从这两个点重新计算法线。仍然是两倍的操作然后直接使用正常但比 3x 好。但是结果不正确(看起来与直接使用 normal 几乎相同)。

我正在考虑以某种方式计算给定投影/位置的倾斜角度并测试:

dot(normal,view_direction) >= acos(min_tilt_angle_threshold)

但是不确定如何计算倾斜角度,也不知道它是否是一个安全的假设,也不知道是否必须应用 x、y 方向的比例,因为投影具有非平凡的纵横比,通常屏幕分辨率为矩形。

我目前的测试渲染代码是这样的:

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#ifndef _mesh_h
#define _mesh_h
//---------------------------------------------------------------------------
#include "vecmath.h"
//---------------------------------------------------------------------------
int view_flags;
float view_mv[16],view_p[16],view_mvp[16],view_ofs[3],view_sc[3];
static volatile float angx=0.0,angy=0.0,angz=5.0*deg;
//---------------------------------------------------------------------------
void obj2mv(float *xyz,const float *dir,float w)
    {
    // apply MV
    mat4_mul_vec3(xyz,view_mv,dir,w);
    }
//---------------------------------------------------------------------------
void obj2mvp(float *p,const float *pos)
    {
    float w;
    // apply MVP
    w=divide(1.0,mat4_mulw_vec3(p,view_mvp,pos,1.0));
    // perspective divide by w + scale x,y to pixels
    p[0]*=view_sc[0]*w;
    p[1]*=view_sc[1]*w;
    p[2]*=w;
    }
//---------------------------------------------------------------------------
void obj2scr(int &x,int &y,int &z,const float *pos)
    {
    float p[3];
    obj2mvp(p,pos);
    // center screen + usable depth range <-1,+1> -> <0,255> (8 bit depth buffer)
    p[2]++; p[2]*=view_sc[2];
    vec3_add(p,p,view_ofs);
    // float -> int
    x=p[0];
    y=p[1];
    z=p[2];
    }
//---------------------------------------------------------------------------
void mesh_view()
    {
    const float z0   =  7.5;
    const float znear=  5.0;
    const float zfar = 10.0;
//  const float FOVy = 10.0*deg;
    const float FOVy = 30.0*deg;

    const float q[3]={0.0,0.0,0.0};
    // modelview
    float m0[16]=
        {
         1.0, 0.0, 0.0,0.0,
         0.0, 1.0, 0.0,0.0,
         0.0, 0.0, 1.0,0.0,
         0.0, 0.0, -z0,1.0,
        };
    vec3_ld(view_ofs,0.5*float(lcd.xs),0.5*float(lcd.ys),0.0);
    vec3_ld(view_sc,0.5*float(lcd.xs),0.5*float(lcd.ys),0.5*255.0);
    mat4_perspective(view_p,FOVy,float(lcd.xs)/float(lcd.ys),znear,zfar);
    mat4_one(view_mv);
    mat4_rotx(view_mv,q,angx);
    mat4_roty(view_mv,q,angy);
    mat4_rotz(view_mv,q,angz);
    mat4_mul(view_mv,view_mv,m0);
    mat4_mul(view_mvp,view_mv,view_p);
    angx=fmod(angx+1.0*deg,2.0*M_PI);
    angy=fmod(angy+5.0*deg,2.0*M_PI);
    angz=fmod(angz+2.0*deg,2.0*M_PI);
    view_flags=0;
    }
//---------------------------------------------------------------------------
void mesh_draw(const float *pnt,const int *fac) // render triangular mesh using matrix m
    {
    int mm;
    int i,i0,i1,i2;
    int x0,y0,z0,c0,x1,y1,z1,c1,x2,y2,z2,c2;
    float p[3],q[3],n[3],p0[3],p1[3],p2[3],np[3];
    for (i=0;fac[i]>=0;)
        {
        // point index for each vertexes of triangle face i
        i0=fac[i]; i++; i0+=i0+i0;
        i1=fac[i]; i++; i1+=i1+i1;
        i2=fac[i]; i++; i2+=i2+i2;
        // obtain screen space coordinates
        obj2scr(x0,y0,z0,pnt+i0);
        obj2scr(x1,y1,z1,pnt+i1);
        obj2scr(x2,y2,z2,pnt+i2);
        // computed normal
        vec3_sub(p,pnt+i1,pnt+i0);
        vec3_sub(q,pnt+i2,pnt+i1);
        vec3_mul(n,p,q);
        obj2mv(n,n,0.0);
        vec3_one(n,n);
        // normal with perspective
        obj2mvp(p0,pnt+i0);
        obj2mvp(p1,pnt+i1);
        obj2mvp(p2,pnt+i2);
        vec3_sub(p,p1,p0);
        vec3_sub(q,p2,p1);
        vec3_mul(np,p,q);
        vec3_one(np,np);

        // normal shading color
        c0=float(255.0*n[2]); c0=2+((14*c0)>>8);
        // flat shaded rendering
        c1=c2=c0;

        // back face culling
        if (np[2]<0.0) continue;

        // render face
//      mm=_mode_fill;                  // normal render
        mm=_mode_line; c1=c2=c0=15;     // wireframe render

        lcd.triangle(x0,y0,z0,c0,x1,y1,z1,c1,x2,y2,z2,c2,mm);
/*
        // render normals
        x0=(x0+x1+x2)/3;
        y0=(y0+y1+y2)/3;
        z0=(z0+z1+z2)/3;
        c0=16; c1=0;
        x1=x0+float(10.0*n[0]);
        y1=y0+float(10.0*n[1]);
        z1=z0+float(10.0*n[2]);
        lcd.line(x0,y0,z0,c0,x1,y1,z1,c1);
*/
        }
    }
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------

像这样硬编码网格的地方:

//---------------------------------------------------------------------------
float point_1[]=
    {
    -1.0,+1.0,-1.0,
    +1.0,+1.0,-1.0,
    +1.0,-1.0,-1.0,
    -1.0,-1.0,-1.0,
    -1.0,-1.0,+1.0,
    +1.0,-1.0,+1.0,
    +1.0,+1.0,+1.0,
    -1.0,+1.0,+1.0,
    };
const int points_1=sizeof(point_1)/sizeof(point_1[0]);
int face3_1[]=
    {
    0,1,2,
    0,2,3,
    4,5,6,
    4,6,7,
    3,2,5,
    3,5,4,
    2,1,6,
    2,6,5,
    1,0,7,
    1,7,6,
    0,3,4,
    0,4,7,
    -1
    };
//---------------------------------------------------------------------------

vecmath.h 是我的 3D 矢量数学库:

//---------------------------------------------------------------------------
//--- 32bit float vector math ver: 1.001 ------------------------------------
//---------------------------------------------------------------------------
#ifndef _vecmath_h
#define _vecmath_h
//---------------------------------------------------------------------------
#include <math.h>
//---------------------------------------------------------------------------
const float deg=M_PI/180.0;
const float rad=180.0/M_PI;
float vec3_tmp[4],mat4_tmp[16];
//---------------------------------------------------------------------------
float  divide       (float a,float b){ if (fabs(b)<1e-10) return 0.0; return a/b; }
float* vec3_ld      (float x,float y,float z);
float* vec3_ld      (float *p,float x,float y,float z);
void   vec3_rotx    (float *pos,const float *mid,float ang);
void   vec3_roty    (float *pos,const float *mid,float ang);
void   vec3_rotz    (float *pos,const float *mid,float ang);
void   mat4_rotx    (float *mat,const float *mid,float ang);
void   mat4_roty    (float *mat,const float *mid,float ang);
void   mat4_rotz    (float *mat,const float *mid,float ang);
void   vec3_copy    (float *c,const float *a);
void   vec3_abs     (float *c,const float *a);
void   vec3_one     (float *c,const float *a);
void   vec3_len     (float *c,const float *a,float l);
float  vec3_len     (         const float *a){ return sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2])); }
float  vec3_len2    (         const float *a){ return (a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2]); }
void   vec3_neg     (float *c,const float *a);
void   vec3_add     (float *c,const float *a,const float *b);
void   vec3_sub     (float *c,const float *a,const float *b);
void   vec3_mul     (float *c,const float *a,const float *b);
void   vec3_mul     (float *c,const float *a,const float  b);
void   vec3_mul     (float *c,const float  a,const float *b);
float  vec3_mul     (         const float *a,const float *b);
float* mat4_ld      (         float a0,float a1,float a2,float a3,float a4,float a5,float a6,float a7,float a8,float a9,float a10,float a11,float a12,float a13,float a14,float a15,float a16);
float* mat4_ld      (float *p,float a0,float a1,float a2,float a3,float a4,float a5,float a6,float a7,float a8,float a9,float a10,float a11,float a12,float a13,float a14,float a15,float a16);
void   mat4_copy    (float *c,const float *a);
void   mat4_set     (float *c,float  a);
void   mat4_one     (float *c);
void   mat4_add     (float *c,const float *a,const float *b);
void   mat4_sub     (float *c,const float *a,const float *b);
void   mat4_mul     (float *c,const float *a,const float *b);
void   mat4_mul_vec3(float *c,const float *a,const float *b,float w);
void   vec3_mul_mat4(float *c,const float *a,const float *b,float w);
float  mat4_mulw_vec3(float *c,const float *a,const float *b,float w);
float  vec3_mulw_mat4(float *c,const float *a,const float *b,float w);
void   mat4_subdet  (float *c,const float *a);
float  mat4_subdet  (         const float *a,int r,int s);
float  mat4_det     (         const float *a);
float  mat4_det     (         const float *a,const float *b);
void   mat4_inv     (float *c,const float *a);
void   mat4_perspective(float *per,float fovy,float aspect,float zNear,float zFar); // per[16] = perspective projection nmatrix fovy [rad] !!!
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
float* vec3_ld(float x,float y,float z)         { float *p=vec3_tmp;   p[0]=x; p[1]=y; p[2]=z; return p; }
float* vec3_ld(float *p,float x,float y,float z){                      p[0]=x; p[1]=y; p[2]=z; return p; }
void  vec3_copy(float *c,const float *a)        { for(int i=0;i<3;i++) c[i]=a[i];       }
void  vec3_abs(float *c,const float *a)         { for(int i=0;i<3;i++) c[i]=fabs(a[i]); }
void  vec3_one(float *c,const float *a)
    {
    float 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  vec3_len(float *c,const float *a,float 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  vec3_neg(float *c,const float *a)               { for(int i=0;i<3;i++) c[i]=-a[i];     }
void  vec3_add(float *c,const float *a,const float *b){ for(int i=0;i<3;i++) c[i]=a[i]+b[i]; }
void  vec3_sub(float *c,const float *a,const float *b){ for(int i=0;i<3;i++) c[i]=a[i]-b[i]; }
void  vec3_mul(float *c,const float *a,const float *b)
    {
    float   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  vec3_mul(float *c,const float *a,const float  b){ for(int i=0;i<3;i++) c[i]=a[i]*b; }
void  vec3_mul(float *c,const float  a,const float *b){ for(int i=0;i<3;i++) c[i]=a*b[i]; }
float vec3_mul(         const float *a,const float *b){ float c=0; for(int i=0;i<3;i++) c+=a[i]*b[i]; return c; }
//---------------------------------------------------------------------------
void vec3_rotx(float *pos,const float *mid,float ang)
    {
    float y,z,c,s;
    y=pos[1]-mid[1];
    z=pos[2]-mid[2];
    c=cos(ang);
    s=sin(ang);
    pos[1]=+(y*c)+(z*s)+mid[1];
    pos[2]=-(y*s)+(z*c)+mid[2];
    }
//---------------------------------------------------------------------------
void vec3_roty(float *pos,const float *mid,float ang)
    {
    float x,z,c,s;
    x=pos[0]-mid[0];
    z=pos[2]-mid[2];
    c=cos(ang);
    s=sin(ang);
    pos[0]=+(x*c)+(z*s)+mid[0];
    pos[2]=-(x*s)+(z*c)+mid[2];
    }
//---------------------------------------------------------------------------
void vec3_rotz(float *pos,const float *mid,float ang)
    {
    float x,y,c,s;
    x=pos[0]-mid[0];
    y=pos[1]-mid[1];
    c=cos(ang);
    s=sin(ang);
    pos[0]=+(x*c)+(y*s)+mid[0];
    pos[1]=-(x*s)+(y*c)+mid[1];
    }
//---------------------------------------------------------------------------
void mat4_rotx(float *mat,const float *mid,float ang)
    {
    const float c=cos(ang),s=sin(ang);
    const float rx[16]=
        { 1, 0, 0, 0,
          0, c, s, 0,
          0,-s, c, 0,
          0, 0, 0, 1 };
    vec3_sub(mat+12,mat+12,mid);
    mat4_mul(mat,mat,rx);
    vec3_add(mat+12,mat+12,mid);
    }
void mat4_roty(float *mat,const float *mid,float ang)
    {
    const float c=cos(ang),s=sin(ang);
    const float ry[16]=
        { c, 0, s, 0,
          0, 1, 0, 0,
         -s, 0, c, 0,
          0, 0, 0, 1 };
    vec3_sub(mat+12,mat+12,mid);
    mat4_mul(mat,mat,ry);
    vec3_add(mat+12,mat+12,mid);
    }
void mat4_rotz(float *mat,const float *mid,float ang)
    {
    const float c=cos(ang),s=sin(ang);
    const float rz[16]=
        { c, s, 0, 0,
         -s, c, 0, 0,
          0, 0, 1, 0,
          0, 0, 0, 1 };
    vec3_sub(mat+12,mat+12,mid);
    mat4_mul(mat,mat,rz);
    vec3_add(mat+12,mat+12,mid);
    }
//---------------------------------------------------------------------------
float* mat4_ld (         float a0,float a1,float a2,float a3,float a4,float a5,float a6,float a7,float a8,float a9,float a10,float a11,float a12,float a13,float a14,float a15,float a16) { float *p=mat4_tmp; p[0]=a0; p[1]=a1; p[2]=a2; p[3]=a3; p[4]=a4; p[5]=a5; p[6]=a6; p[7]=a7; p[8]=a8; p[9]=a9; p[10]=a10; p[11]=a11; p[12]=a12; p[13]=a13; p[14]=a14; p[15]=a15; return p; }
float* mat4_ld (float *p,float a0,float a1,float a2,float a3,float a4,float a5,float a6,float a7,float a8,float a9,float a10,float a11,float a12,float a13,float a14,float a15,float a16) {                      p[0]=a0; p[1]=a1; p[2]=a2; p[3]=a3; p[4]=a4; p[5]=a5; p[6]=a6; p[7]=a7; p[8]=a8; p[9]=a9; p[10]=a10; p[11]=a11; p[12]=a12; p[13]=a13; p[14]=a14; p[15]=a15; return p; }
void mat4_copy(float *c,const float *a){ for(int i=0;i<16;i++) c[i]=a[i]; }
void mat4_set (float *c,const float  a){ for(int i=0;i<16;i++) c[i]=a; };
void mat4_one (float *c){ for(int i=0;i<16;i++) c[i]=0; c[0]=1; c[5]=1; c[10]=1; c[15]=1; };
void mat4_add (float *c,const float *a,const float *b){ for(int i=0;i<16;i++) c[i]=a[i]+b[i]; }
void mat4_sub (float *c,const float *a,const float *b){ for(int i=0;i<16;i++) c[i]=a[i]-b[i]; }
void mat4_mul (float *c,const float *a,const float *b)
    {
    float 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 mat4_mul_vec3(float *c,const float *a,const float *b,float w)
    {
    float q[3];
    q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]*w);
    q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]*w);
    q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]*w);
    for(int i=0;i<3;i++) c[i]=q[i];
    }
void vec3_mul_mat4(float *c,const float *a,const float *b,float w)
    {
    float q[3];
    q[0]=(a[0]*b[ 0])+(a[1]*b[ 1])+(a[2]*b[ 2])+(w*b[ 3]);
    q[1]=(a[0]*b[ 4])+(a[1]*b[ 5])+(a[2]*b[ 6])+(w*b[ 7]);
    q[2]=(a[0]*b[ 8])+(a[1]*b[ 9])+(a[2]*b[10])+(w*b[11]);
    for(int i=0;i<3;i++) c[i]=q[i];
    }
float mat4_mulw_vec3(float *c,const float *a,const float *b,float w)
    {
    float q[4];
    q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]*w);
    q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]*w);
    q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]*w);
    q[3]=(a[ 3]*b[0])+(a[ 7]*b[1])+(a[11]*b[2])+(a[15]*w);
    for(int i=0;i<3;i++) c[i]=q[i];
    return q[3];
    }
float vec3_mulw_mat4(float *c,const float *a,const float *b,float w)
    {
    float q[4];
    q[0]=(a[0]*b[ 0])+(a[1]*b[ 1])+(a[2]*b[ 2])+(w*b[ 3]);
    q[1]=(a[0]*b[ 4])+(a[1]*b[ 5])+(a[2]*b[ 6])+(w*b[ 7]);
    q[2]=(a[0]*b[ 8])+(a[1]*b[ 9])+(a[2]*b[10])+(w*b[11]);
    q[3]=(a[0]*b[12])+(a[1]*b[13])+(a[2]*b[14])+(w*b[15]);
    for(int i=0;i<3;i++) c[i]=q[i];
    return q[3];
    }
void mat4_subdet(float *c,const float *a)
    {
    float   q[16];
    int     i,j;
    for (i=0;i<4;i++)
     for (j=0;j<4;j++)
      q[j+(i<<2)]=mat4_subdet(a,i,j);
    for (i=0;i<16;i++) c[i]=q[i];
    }
float mat4_subdet(const float *a,int r,int s)
    {
    float   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;
    }
float mat4_det(const float *a)
    {
    float c=0;
    c+=a[ 0]*mat4_subdet(a,0,0);
    c+=a[ 4]*mat4_subdet(a,0,1);
    c+=a[ 8]*mat4_subdet(a,0,2);
    c+=a[12]*mat4_subdet(a,0,3);
    return c;
    }
float mat4_det(const float *a,const float *b)
    {
    float 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 mat4_inv(float *c,const float *a)
    {
    float   d[16],D;
    mat4_subdet(d,a);
    D=mat4_det(a,d);
    if (D) D=1.0/D;
    for (int i=0;i<16;i++) c[i]=d[i]*D;
    }
void mat4_perspective(float *per,float fovy,float aspect,float zNear,float zFar)
    {
    float f;
    for (int i=0;i<16;i++) per[i]=0.0;
    // original gluProjection
//  f=divide(1.0,tan(0.5*fovy))
//  per[ 0]=f/aspect;
//  per[ 5]=f;
    // corrected gluProjection
    f=divide(1.0,tan(0.5*fovy*aspect));
    per[ 0]=f;
    per[ 5]=f*aspect;
    // z range
    per[10]=divide(zFar+zNear,zNear-zFar);
    per[11]=-1.0;
    per[14]=divide(2.0*zFar*zNear,zNear-zFar);
//  zNear=divide(-per[11],per[10]);     // get znear from perspective projection matrix
    }
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------

lcd class 不包括在内(它不适合 30K 的限制而且它并不重要,因为它只是低级光栅化)这里唯一重要的是它的 xs,ys 屏幕分辨率和深度缓冲区的成员基于 8 位无符号整数。

在我调用的计时器中使用很简单

mesh_view();
mesh_draw(point_1,face3_1);

通常,面法线不用于背面剔除。取而代之的是,光栅化器使用三角形顶点的屏幕位置。基本上,如果顶点在屏幕上按顺时针顺序排列,则该面被认为是背对着的。

此外,可以有一个三角形,其法线指向远离视线方向但面向相机。