C++ 截锥体与透视矩阵

C++ 截锥体与透视矩阵,c++,math,3d,linear-algebra,C++,Math,3d,Linear Algebra,我有一些转换矩阵的代码,并且添加了一些处理截头体和透视图的函数 问题是,当我调用透视图时,每件事物看起来都好像我有一个很大的视场(在屏幕的中间,在边缘非常伸展)。 当我增加“近”值时,这种效果会更糟 void Frustum(T left, T right, T bottom, T top, T zNear, T zFar) { m[0][0]=2.0f*zNear/(right-left); m[1][0]=0.0f; m[2][

我有一些转换矩阵的代码,并且添加了一些处理截头体和透视图的函数

问题是,当我调用透视图时,每件事物看起来都好像我有一个很大的视场(在屏幕的中间,在边缘非常伸展)。 当我增加“近”值时,这种效果会更糟

    void Frustum(T left, T right, T bottom, T top, T zNear, T zFar)
    {
        m[0][0]=2.0f*zNear/(right-left);
        m[1][0]=0.0f;
        m[2][0]=(right+left)/(right-left);
        m[3][0]=0.0f;
        m[0][1]=0.0f;
        m[1][1]=2.0f*zNear/(top-bottom);
        m[2][1]=(top+bottom)/(top-bottom);
        m[3][1]=0.0f;
        m[0][2]=0.0f;
        m[1][2]=0.0f;
        m[2][2]=-(zFar+zNear)/(zFar-zNear);
        m[3][2]=-2.0f*zFar*zNear/(zFar-zNear);
        m[0][3]=0.0f;
        m[1][3]=0.0f;
        m[2][3]=-1.0f;
        m[3][3]=0.0f;
    }

    void Perspective(T fovy,T aspectRatio,T zNear,T zFar)
    {
        T xmin,xmax,ymin,ymax;
        ymax= zNear* tan(fovy*Math<T>::PI/360.0);
        ymin= -ymax;
        xmin= ymin*aspectRatio;
        xmax= ymax*aspectRatio;
        Frustum(xmin,xmax,ymin,ymax,zNear,zFar);
    }
void平截头体(T左、T右、T底、T顶、T轴、T轴)
{
m[0][0]=2.0f*zNear/(右-左);
m[1][0]=0.0f;
m[2][0]=(右+左)/(右-左);
m[3][0]=0.0f;
m[0][1]=0.0f;
m[1][1]=2.0f*zNear/(上下);
m[2][1]=(顶部+底部)/(顶部-底部);
m[3][1]=0.0f;
m[0][2]=0.0f;
m[1][2]=0.0f;
m[2][2]=-(zFar+zNear)/(zFar-zNear);
m[3][2]=-2.0f*zFar*zNear/(zFar-zNear);
m[0][3]=0.0f;
m[1][3]=0.0f;
m[2][3]=-1.0f;
m[3][3]=0.0f;
}
空洞透视图(T fovy、T aspectRatio、T zNear、T zFar)
{
T xmin,xmax,ymin,ymax;
ymax=zNear*tan(fovy*数学::PI/360.0);
ymin=-ymax;
xmin=ymin*aspectRatio;
xmax=ymax*aspectRatio;
平截头体(xmin、xmax、ymin、ymax、zNear、zFar);
}
当类被模板化时,T是一个float或double(对于测试,我只使用float)

我使用的测试值是
fovy=“60”znear=“1.0”zfar=“1000.0”
,当我将它们更改为
fovy=“60”znear=“10.0”zfar=“1000.0”
时,情况会更糟

请注意,矩阵是DirectX样式,但我在OpenGL中使用它们,因此我更改了着色器中的乘法顺序

你们看到我的代码有什么问题吗

谢谢

计算

z_delta=(zfar-znear);//used two times!
direction=(right-left);//used two times!
height=(top-bottom);//used two times!
znear_2=2.0f*znear; //used 3 times!
只有一次!然后在你想去的任何地方使用它

而且,除法非常昂贵,您可以预先计算除法并存储在数组中,您只需要在执行过程中从数组中获取预先计算的元素


例如:如果顶部和底部范围很小,那么您可以在一个小数组中表示计算(实际上它是一个除数和一个除数的映射)

我认为问题在于
Perspective()
:在计算
ymax
时,您不应该乘以
zNear
。从数学上讲,你是以单位距离投射到一个平面上,而不是
zNear
平面;这种价值在那里没有任何意义


尝试只设置
ymax=tan(fovy*Math::PI/360.0)
。仍然不能保证它是正确的,但这应该是非常明智的…

问题是,当我开始使用转置的矩阵(DirectX样式)时,我想我还必须转置平截头体数学,这不是一个好主意

这就是平截头体函数的外观:

    void Frustum(T left, T right, T bottom, T top, T zNear, T zFar)
    {
        T zDelta = (zFar-zNear);
        T dir = (right-left);
        T height = (top-bottom);
        T zNear2 = 2*zNear;

        m[0][0]=2.0f*zNear/dir;
        m[0][1]=0.0f;
        m[0][2]=(right+left)/dir;
        m[0][3]=0.0f;
        m[1][0]=0.0f;
        m[1][1]=zNear2/height;
        m[1][2]=(top+bottom)/height;
        m[1][3]=0.0f;
        m[2][0]=0.0f;
        m[2][1]=0.0f;
        m[2][2]=-(zFar+zNear)/zDelta;
        m[2][3]=-zNear2*zFar/zDelta;
        m[3][0]=0.0f;
        m[3][1]=0.0f;
        m[3][2]=-1.0f;
        m[3][3]=0.0f;
    }

请尝试将znear更改为16.0,并告诉我现在是否更快您是否使用了任何优化?因为您将znear设置为1.0意味着它不用于乘法,所以当您将znear设置为1.0时,您可以去掉3个乘法。我没有使用任何优化。如前所述,如果我将znear设置为10,它将更加混乱。我渲染的对象会更小,基本上看起来它离得很远,当我旋转相机时,它看起来会向显示器的边缘弯曲。将其设置为16将使其看起来更糟。如果您没有使用优化,请阅读下面的答案为什么?任何半体面的编译器都将执行此优化。给数量起描述性的名字也许有意义。我从来没有完全理解过投影数学,但我很确定这与它无关。看看这个: