在Opengl ES 2.0上设置透视投影使物体消失

发布于 2024-11-29 19:44:28 字数 6976 浏览 1 评论 0原文

我正在使用 opengl-es 2.0 开发一个项目,并且在设置透视投影时遇到一些问题。

如果我不设置透视投影并简单地将对象到世界矩阵(我相信它也称为模型矩阵)乘以顶点位置,则屏幕上的对象将正确渲染,它们看起来被拉伸,但就我知道,这是投影矩阵可以解决的问题。问题是,每当我设置透视矩阵并使用它时,屏幕上的对象就会消失,无论我如何移动它们,它们都不会出现在屏幕上。

获得模型-视图-投影矩阵的计算是在 CPU 中完成的,而 MVP 矩阵与实际对象空间顶点数据的最后乘法是在顶点着色器中完成的,这就是为什么我认为问题可能出在该过程上获得 MVP 矩阵。我已经运行了一堆单元测试,但是根据这些测试(以及我的线性代数基础知识),这些矩阵正在正确计算,而我整天的互联网研究目前没有帮助。 :-/

这是我用来计算 MVP 矩阵的代码:

Matrix4D projection_matrix;
projection_matrix.makePerspective(45.0f, 0.001f, 100.0f, 480.0f/320.0f);

Matrix4D view_matrix;
view_matrix.makeIdentity(); //Should be a real view matrix. TODO.

Matrix4D model_matrix(getWorldMatrix());

Matrix4D mvp_matrix(projection_matrix);
mvp_matrix *= view_matrix;
mvp_matrix *= model_matrix;

mMesh->draw(time, mvp_matrix.getRawData());

我认为这段代码是不言自明的,但以防万一,那些 Matrix4D 是 4x4 矩阵,对它们调用 makePerspective/makeIdentity 将使该矩阵成为透视图或单位矩阵。 Matrix4D 对象上的 getRawData() 调用以列主表示法的浮点数组形式返回矩阵数据,而 mMesh 变量是另一个对象,当调用 Draw 时,它将简单地将所有顶点和材质数据发送到着色器。

makePerspective 函数的代码如下:

Matrix4D& Matrix4D::makePerspective(const float field_of_view, 
        const float near, const float far, const float aspect_ratio) {
    float size = near * tanf(DEGREES_TO_RADIANS(field_of_view) / 2.0f); 

    return this->makeFrustum(-size, size, -size / aspect_ratio,
             size / aspect_ratio, near, far);
}

Matrix4D& Matrix4D::makeFrustum(const float left, const float right, 
        const float bottom, const float top, const float near, 
        const float far) {
    this->mRawData[0] = 2.0f * near / (right - left);  
    this->mRawData[1] = 0.0f; 
    this->mRawData[2] = 0.0f; 
    this->mRawData[3] = 0.0f;

    this->mRawData[4] = 0.0f; 
    this->mRawData[5] = 2.0f * near / (top - bottom); 
    this->mRawData[6] = 0.0f; 
    this->mRawData[7] =  0.0f;

    this->mRawData[8] = (right + left) / (right - left);
    this->mRawData[9] = (top + bottom) / (top - bottom); 
    this->mRawData[10] = - (far + near) / (far - near); 
    this->mRawData[11] = -1.0f;

    this->mRawData[12] = 0.0f; 
    this->mRawData[13] = 0.0f; 
    this->mRawData[14] = -2.0f * far * near / (far - near); 
    this->mRawData[15] = 0.0f;

    return *this;
}

getWorldMatrix() 调用执行此操作(带有一些相关代码):

const Matrix4D& getWorldMatrix() {
    return mWorldMatrix = 
            getTranslationMatrix() *
            getRotationMatrix() *
            getScaleMatrix();
}

const Matrix4D& getRotationMatrix() {
    return this->mRotationMatrix.makeRotationFromEuler(this->mPitchAngle,
        this->mRollAngle, this->mYawAngle);
}

const Matrix4D& getTranslationMatrix() {
    return this->mTranslationMatrix.makeTranslation(this->mPosition.x,
        this->mPosition.y, this->mPosition.z);
}

const Matrix4D& getScaleMatrix() {
    return this->mScaleMatrix.makeScale(this->mScaleX, this->mScaleY, this->mScaleZ);
}


///This code goes in the Matrix4D class.
Matrix4D& Matrix4D::makeTranslation(const float x, const float y, 
        const float z) {
    this->mRawData[0] = 1.0f; 
    this->mRawData[1] = 0.0f; 
    this->mRawData[2] = 0.0f; 
    this->mRawData[3] = 0.0f;

    this->mRawData[4] = 0.0f; 
    this->mRawData[5] = 1.0f; 
    this->mRawData[6] = 0.0f; 
    this->mRawData[7] = 0.0f;

    this->mRawData[8] = 0.0f; 
    this->mRawData[9] = 0.0f; 
    this->mRawData[10] = 1.0f; 
    this->mRawData[11] = 0.0f;

    this->mRawData[12] =    x; 
    this->mRawData[13] =    y; 
    this->mRawData[14] =    z; 
    this->mRawData[15] = 1.0f;

    return *this;
}

Matrix4D& Matrix4D::makeScale(const float x, const float y, 
        const float z) {
    this->mRawData[0] =    x; 
    this->mRawData[1] = 0.0f; 
    this->mRawData[2] = 0.0f; 
    this->mRawData[3] = 0.0f;

    this->mRawData[4] = 0.0f; 
    this->mRawData[5] =    y; 
    this->mRawData[6] = 0.0f; 
    this->mRawData[7] = 0.0f;

    this->mRawData[8] = 0.0f; 
    this->mRawData[9] = 0.0f; 
    this->mRawData[10] =    z; 
    this->mRawData[11] = 0.0f;

    this->mRawData[12] = 0.0f; 
    this->mRawData[13] = 0.0f; 
    this->mRawData[14] = 0.0f; 
    this->mRawData[15] = 1.0f;

    return *this;
} 

Matrix4D& Matrix4D::makeRotationFromEuler(const float angle_x, 
        const float angle_y, const float angle_z) {
    float a = cosf(angle_x);
    float b = sinf(angle_x);
    float c = cosf(angle_y);
    float d = sinf(angle_y);
    float e = cosf(angle_z);
    float f = sinf(angle_z);
    float ad = a * d;
    float bd = b * d;

    this->mRawData[0] = c * e;
    this->mRawData[1] = -bd * e + a * f;
    this->mRawData[2] = ad * e + b * f;
    this->mRawData[3] = 0.0f;

    this->mRawData[4] = -c * f; 
    this->mRawData[5] = bd * f + a * e;
    this->mRawData[6] = -ad * f + b * e; 
    this->mRawData[7] = 0.0f;

    this->mRawData[8] = -d; 
    this->mRawData[9] = -b * c;
    this->mRawData[10] = a * c; 
    this->mRawData[11] = 0.0f;

    this->mRawData[12] = 0.0f; 
    this->mRawData[13] = 0.0f;
    this->mRawData[14] = 0.0f; 
    this->mRawData[15] = 1.0f;

    return *this;
}

最后,顶点着色器几乎是这样的:

#version 110

const float c_one = 1.0;
const float c_cero = 0.0;

uniform float time;
uniform mat4 mvp_matrix;

attribute vec3 position;
attribute vec3 normal;
attribute vec2 texture_coordinate;

varying vec2 v_texture_coordinate;

void main()
{   
    gl_Position = mvp_matrix * vec4(position, c_one);
    v_texture_coordinate = texture_coordinate;
}

以防万一,正在渲染的对象渲染在位置 (0.0f, 0.0 f, -3.0f),所有三个轴均应用 0.5f 刻度。

我真的不知道可能出了什么问题,我希望有人能发现我可能缺少的东西,并且任何帮助将不胜感激。如果我可以在着色器上获得每个顶点的结果,那么调试会容易得多:-/。

作为旁注,我对如何计算视图或相机矩阵有疑问,据我所知,它只是一个带有相机必须执行的反转变换的矩阵,通过它我可以理解类似的内容,如果我想将相机向右移动 100 个单位,我向左移动 100 个单位,对吗?

编辑:只是想提供更多信息,也许这样有人就能够帮助我。我注意到上面的代码中的模型矩阵不正确,主要是因为矩阵顺序,我已将其更改为以下内容,现在模型矩阵看起来不错:

const Matrix4D& getWorldMatrix() {
    return mWorldMatrix = 
            getScaleMatrix() * getRotationMatrix() * getTranslationMatrix();
}

尽管如此,仍然没有运气。我的测试数据产生的矩阵如下:

Projection matrix:
[1.609506,0.000000,0.000000,0.000000]
[0.000000,2.414258,0.000000,0.000000]
[0.000000,0.000000,-1.000020,-0.002000]
[0.000000,0.000000,-1.000000,0.000000]



Model matrix:
[0.500000,0.000000,0.000000,0.000000]
[0.000000,0.500000,0.000000,0.000000]
[0.000000,0.000000,0.500000,-3.000000]
[0.000000,0.000000,0.000000,1.000000]



MVP matrix:
[0.804753,0.000000,0.000000,0.000000]
[0.000000,1.207129,0.000000,0.000000]
[0.000000,0.000000,2.499990,-0.001000]
[0.000000,0.000000,-1.000000,0.000000]

我用来测试所有这些的网格是一个简单的立方体,每个轴上的值从 1.0f 到 -1.0f,以原点为中心。据我所知,这应该将最接近极限(0.0001f)的顶点定位在沿 z 轴的位置 -2.0f 上,因此立方体位于相机前面并在视锥体内。有人有任何线索吗?

I'm working on a project using opengl-es 2.0, and I'm having some trouble setting up perspective projection.

If I don't set up the perspective projection and simply multiply the object-to-world matrix (I believe it's also called model matrix) by the vertex positions, the objects on screen are rendered correctly, they appear stretched, but as far as I know, that's something the projection matrix would fix. The problem is, whenever I set the perspective matrix and use it, the objects on screen disappear, and no matter how much I move them around they never show up in screen.

The calculations to get the Model-View-Projection matrix are done in CPU and the last multiplication the MVP-Matrix by the actual object-space vertex data is done in the vertex shader, this is why I believe the problem might be on the process to get that MVP-Matrix. I've run a bunch of unit tests, but according to those tests (and my basic knowledge of linear algebra) those matrices are being correctly calculated, and my internet-research throughout the day isn't helping for now. :-/

This is the code I use to calculate the MVP-Matrix:

Matrix4D projection_matrix;
projection_matrix.makePerspective(45.0f, 0.001f, 100.0f, 480.0f/320.0f);

Matrix4D view_matrix;
view_matrix.makeIdentity(); //Should be a real view matrix. TODO.

Matrix4D model_matrix(getWorldMatrix());

Matrix4D mvp_matrix(projection_matrix);
mvp_matrix *= view_matrix;
mvp_matrix *= model_matrix;

mMesh->draw(time, mvp_matrix.getRawData());

I think this code is pretty self-explanatory, but just in case, those Matrix4D are 4x4 matrices, and calling makePerspective/makeIdentity on them will make that matrix the Perspective or Identity matrix. The getRawData() call on Matrix4D objects returns the matrix data as a float array in column-major notation, and the mMesh variable is another object which, when draw is called, will simply send all the vertex and material data to the shaders.

The makePerspective function's code is the following:

Matrix4D& Matrix4D::makePerspective(const float field_of_view, 
        const float near, const float far, const float aspect_ratio) {
    float size = near * tanf(DEGREES_TO_RADIANS(field_of_view) / 2.0f); 

    return this->makeFrustum(-size, size, -size / aspect_ratio,
             size / aspect_ratio, near, far);
}

Matrix4D& Matrix4D::makeFrustum(const float left, const float right, 
        const float bottom, const float top, const float near, 
        const float far) {
    this->mRawData[0] = 2.0f * near / (right - left);  
    this->mRawData[1] = 0.0f; 
    this->mRawData[2] = 0.0f; 
    this->mRawData[3] = 0.0f;

    this->mRawData[4] = 0.0f; 
    this->mRawData[5] = 2.0f * near / (top - bottom); 
    this->mRawData[6] = 0.0f; 
    this->mRawData[7] =  0.0f;

    this->mRawData[8] = (right + left) / (right - left);
    this->mRawData[9] = (top + bottom) / (top - bottom); 
    this->mRawData[10] = - (far + near) / (far - near); 
    this->mRawData[11] = -1.0f;

    this->mRawData[12] = 0.0f; 
    this->mRawData[13] = 0.0f; 
    this->mRawData[14] = -2.0f * far * near / (far - near); 
    this->mRawData[15] = 0.0f;

    return *this;
}

And the getWorldMatrix() call does this(with some related code):

const Matrix4D& getWorldMatrix() {
    return mWorldMatrix = 
            getTranslationMatrix() *
            getRotationMatrix() *
            getScaleMatrix();
}

const Matrix4D& getRotationMatrix() {
    return this->mRotationMatrix.makeRotationFromEuler(this->mPitchAngle,
        this->mRollAngle, this->mYawAngle);
}

const Matrix4D& getTranslationMatrix() {
    return this->mTranslationMatrix.makeTranslation(this->mPosition.x,
        this->mPosition.y, this->mPosition.z);
}

const Matrix4D& getScaleMatrix() {
    return this->mScaleMatrix.makeScale(this->mScaleX, this->mScaleY, this->mScaleZ);
}


///This code goes in the Matrix4D class.
Matrix4D& Matrix4D::makeTranslation(const float x, const float y, 
        const float z) {
    this->mRawData[0] = 1.0f; 
    this->mRawData[1] = 0.0f; 
    this->mRawData[2] = 0.0f; 
    this->mRawData[3] = 0.0f;

    this->mRawData[4] = 0.0f; 
    this->mRawData[5] = 1.0f; 
    this->mRawData[6] = 0.0f; 
    this->mRawData[7] = 0.0f;

    this->mRawData[8] = 0.0f; 
    this->mRawData[9] = 0.0f; 
    this->mRawData[10] = 1.0f; 
    this->mRawData[11] = 0.0f;

    this->mRawData[12] =    x; 
    this->mRawData[13] =    y; 
    this->mRawData[14] =    z; 
    this->mRawData[15] = 1.0f;

    return *this;
}

Matrix4D& Matrix4D::makeScale(const float x, const float y, 
        const float z) {
    this->mRawData[0] =    x; 
    this->mRawData[1] = 0.0f; 
    this->mRawData[2] = 0.0f; 
    this->mRawData[3] = 0.0f;

    this->mRawData[4] = 0.0f; 
    this->mRawData[5] =    y; 
    this->mRawData[6] = 0.0f; 
    this->mRawData[7] = 0.0f;

    this->mRawData[8] = 0.0f; 
    this->mRawData[9] = 0.0f; 
    this->mRawData[10] =    z; 
    this->mRawData[11] = 0.0f;

    this->mRawData[12] = 0.0f; 
    this->mRawData[13] = 0.0f; 
    this->mRawData[14] = 0.0f; 
    this->mRawData[15] = 1.0f;

    return *this;
} 

Matrix4D& Matrix4D::makeRotationFromEuler(const float angle_x, 
        const float angle_y, const float angle_z) {
    float a = cosf(angle_x);
    float b = sinf(angle_x);
    float c = cosf(angle_y);
    float d = sinf(angle_y);
    float e = cosf(angle_z);
    float f = sinf(angle_z);
    float ad = a * d;
    float bd = b * d;

    this->mRawData[0] = c * e;
    this->mRawData[1] = -bd * e + a * f;
    this->mRawData[2] = ad * e + b * f;
    this->mRawData[3] = 0.0f;

    this->mRawData[4] = -c * f; 
    this->mRawData[5] = bd * f + a * e;
    this->mRawData[6] = -ad * f + b * e; 
    this->mRawData[7] = 0.0f;

    this->mRawData[8] = -d; 
    this->mRawData[9] = -b * c;
    this->mRawData[10] = a * c; 
    this->mRawData[11] = 0.0f;

    this->mRawData[12] = 0.0f; 
    this->mRawData[13] = 0.0f;
    this->mRawData[14] = 0.0f; 
    this->mRawData[15] = 1.0f;

    return *this;
}

Finally, the vertex shader is pretty much this:

#version 110

const float c_one = 1.0;
const float c_cero = 0.0;

uniform float time;
uniform mat4 mvp_matrix;

attribute vec3 position;
attribute vec3 normal;
attribute vec2 texture_coordinate;

varying vec2 v_texture_coordinate;

void main()
{   
    gl_Position = mvp_matrix * vec4(position, c_one);
    v_texture_coordinate = texture_coordinate;
}

Just in case, the object being rendered is rendered on position (0.0f, 0.0f, -3.0f) with 0.5f scale applied to all the three axis's.

I don't really know what could be wrong, I'm hoping someone can spot what I may be missing, and any help would be appreciated. Debugging this would be a lot easier if I could get per-vertex results on the shader :-/.

As a side note, I'm having doubts on how to calculate the View or camera matrix, as far as I know it's simply a matrix with the inverted transformations the camera has to do, by which I understand something like, if I want to move the camera 100 units to the right, I move it 100 units to the left, is that right?

EDIT: Just trying to give more information, maybe that way someone will be able to help me. I've noticed the model matrix is incorrect with the code above, mostly because of the matrix order, I've changed it to the following and now the model matrix seems good:

const Matrix4D& getWorldMatrix() {
    return mWorldMatrix = 
            getScaleMatrix() * getRotationMatrix() * getTranslationMatrix();
}

Despite this, still no luck. The matrices resulting from my test data are these:

Projection matrix:
[1.609506,0.000000,0.000000,0.000000]
[0.000000,2.414258,0.000000,0.000000]
[0.000000,0.000000,-1.000020,-0.002000]
[0.000000,0.000000,-1.000000,0.000000]



Model matrix:
[0.500000,0.000000,0.000000,0.000000]
[0.000000,0.500000,0.000000,0.000000]
[0.000000,0.000000,0.500000,-3.000000]
[0.000000,0.000000,0.000000,1.000000]



MVP matrix:
[0.804753,0.000000,0.000000,0.000000]
[0.000000,1.207129,0.000000,0.000000]
[0.000000,0.000000,2.499990,-0.001000]
[0.000000,0.000000,-1.000000,0.000000]

And the mesh I'm using to test all this is a simple cube going from 1.0f to -1.0f on each axis, centered on the origin. As far as I know, this should position the vertex closest to the near limit (0.0001f) on position -2.0f along the z axis, so the cube is in front of the camera and withing the view frustum. Any clues someone?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文