Opengl使用着色器绘制不正确 - 矩阵设置/初始化存在问题吗?

3
当我渲染我的应用程序时,我期望看到一些矩形围绕窗口的边缘。但是我看到了这个...
所有对象的z值都为0.0f。如果我不使用着色器来渲染场景,所有对象都能正常显示。所以我认为这可能是矩阵计算问题?
有人知道我在矩阵设置方面哪里出错了吗?
"matrices"是一个自定义类,包含3个矩阵...
public class MatrixUtils {

    /* The different matrices */
    private Matrix4f modelMatrix = new Matrix4f();
    private Matrix4f viewMatrix = new Matrix4f();
    private Matrix4f projectionMatrix = new Matrix4f();

    public MatrixUtils(){
        loadIdentity(modelMatrix);
    }

    public void loadIdentity(Matrix4f matrix) {
        matrix.load(new float[][] {
                new float[] { 1, 0, 0, 0 },
                new float[] { 0, 1, 0, 0 },
                new float[] { 0, 0, 1, 0 },
                new float[] { 0, 0, 0, 1 },
        });
    }
}

在我的GLEventListener中,我设置了初始值的矩阵。在reshape方法中调用,设置投影、模型和视图矩阵。

/* (non-Javadoc)
 * @see javax.media.opengl.GLEventListener#reshape(javax.media.opengl.GLAutoDrawable, int, int, int, int)
 */
public void reshape(GLAutoDrawable gLDrawable, int x, int y, int width, int height) {

    setupOrtho(width, height, 0.1f, 100.0f);
}

一开始,模型和视图矩阵被设置为单位矩阵。投影使用正交矩阵。

private void setupOrtho(float width, float height, float znear, float zfar) {
    matrices.loadIdentity(matrices.getModelMatrix());
    matrices.loadIdentity(matrices.getViewMatrix());
    matrices.setViewMatrix(
        setupViewMatrix(
            new Vec3(0.0f, 0.0f, 25.0f), 
            new Vec3(0.0f, 0.0f, 0.0f), 
            new Vec3(0.0f, 1.0f, 0.0f)));
    matrices.setProjectionMatrix(ortho(0, width, 0, height, znear, zfar));
}

计算正交投影矩阵..

public Matrix4f ortho(float left, float right, float top, float bottom, float zfar, float znear) {
    return new Matrix4f(new float[][] {
            new float[] { 2 / (right - left), 0, 0, -((right + left) / (right - left)) },
            new float[] { 0, 2 / (top - bottom), 0, -((top + bottom) / (top - bottom)) },
            new float[] { 0, 0, -2 / (zfar - znear), -((zfar + znear) / (zfar - znear)) },
            new float[] { 0, 0, 0, 1 },
    });
}

计算 View 矩阵..

public Matrix4f setupViewMatrix(Vec3 position, Vec3 target, Vec3 up) {

    Vec3f f = (new Vec3f(target.sub(position))).normalize();
    Vec3f s = (new Vec3f(Vec3.cross(f, up))).normalize();
    Vec3f u = (new Vec3f(Vec3.cross(s, f)));

    return new Matrix4f(
            new float[] {
                    s.x, s.y, s.z, -Vec3.dot(s, position),
                    u.x, u.y, u.z, -Vec3.dot(u, position),
                    -f.x, -f.y, -f.z, Vec3.dot(f, position),
                    0.0f, 0.0f, 0.0f, 1.0f});
}

然后在我的display()循环内,我将所有3个矩阵传递到每个对象的draw()函数中。

public void display(GLAutoDrawable gLDrawable) {

    for (CustomObject obj : customObjects.size()){

        obj.draw(gl2, matrices, getShaderProgram(), obj.getPosition(), 0.0f);
    }

}

这是我的自定义对象设置vertexBuffer的方式...
int COORDS_PER_VERTEX = 3;
int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

ShortBuffer drawListBuffer;
short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices
float squareCoordsTemp[] = {
    -(getWidth() / 2 * getP2M()),  (getHeight() / 2 * getP2M()), 0.0f,   // top left
    -(getWidth() / 2 * getP2M()), -(getHeight() / 2 * getP2M()), 0.0f,   // bottom left
    (getWidth() / 2 * getP2M()), -(getHeight() / 2 * getP2M()), 0.0f,   // bottom right
    (getWidth() / 2 * getP2M()),  (getHeight() / 2 * getP2M()), 0.0f }; // top right
squareCoords = squareCoordsTemp;

ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4); // # of coordinate values * 4 bytes per float
bb.order(ByteOrder.nativeOrder());
vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(squareCoords);
vertexBuffer.position(0);

// initialize byte buffer for the draw list
ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2); // # of coordinate values * 2 bytes per short
dlb.order(ByteOrder.nativeOrder());
drawListBuffer = dlb.asShortBuffer();
drawListBuffer.put(drawOrder);
drawListBuffer.position(0);

这是我的CustomObject绘制的方式..
public void draw(final GL2 gl2, MatrixUtils matrices, int shaderProgram, final Vec3 position, final float bodyAngle){

    gl2.glUseProgram(shaderProgram);

    // enable alpha
    gl2.glEnable(GL.GL_BLEND);
    gl2.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);

    // Set color for drawing
    setmColorHandle(gl2.glGetUniformLocation(shaderProgram, "vColor"));
    gl2.glUniform4fv(getmColorHandle(), 1, getColorArray(), 0);

    // get handle to vertex shader's vPosition member
    mPositionHandle = gl2.glGetAttribLocation(shaderProgram, "vPosition");

    // Enable a handle to the triangle vertices
    gl2.glEnableVertexAttribArray(mPositionHandle);

    // Prepare the triangle coordinate data
    gl2.glVertexAttribPointer(
        mPositionHandle, COORDS_PER_VERTEX,
        GL2.GL_FLOAT, false,
        vertexStride, vertexBuffer);

    // get handle to shape's transformation matrix
    mProj = gl2.glGetUniformLocation(shaderProgram, "mProj");
    mView = gl2.glGetUniformLocation(shaderProgram, "mView");
    mModel = gl2.glGetUniformLocation(shaderProgram, "mModel");

    // Apply the projection and view transformation

    // getP2M() == 60.0f .. pixels to meters for box2d
    matrices.loadIdentity(matrices.getModelMatrix());
    matrices.setModelMatrix(matrices.translate(matrices.getModelMatrix(), new Vec3(position.x * getP2M(), position.y * getP2M(), position.z * getP2M())));
    matrices.setModelMatrix(matrices.rotate(matrices.getModelMatrix(), bodyAngle, 0, 0, 1));

    gl2.glUniformMatrix4fv(mProj, 1, true, matrices.getProjectionMatrix().getValues(), 0);
    gl2.glUniformMatrix4fv(mView, 1, true, matrices.getViewMatrix().getValues(), 0);
    gl2.glUniformMatrix4fv(mModel, 1, true, matrices.getModelMatrix().getValues(), 0);

    // Draw the square
    gl2.glDrawElements(
        GL2.GL_TRIANGLES, drawOrder.length,
        GL2.GL_UNSIGNED_SHORT, drawListBuffer);

    // Disable vertex array
    gl2.glDisableVertexAttribArray(mPositionHandle);

    gl2.glDisable(GL.GL_BLEND);
    gl2.glUseProgram(0);
}

Vertex shader ..

#version 120

attribute vec4 vPosition;

uniform mat4 mProj;
uniform mat4 mView;
uniform mat4 mModel;

void main() {
    gl_Position = mProj * mView * mModel * vPosition;
}

fragment shader ..

#version 120

uniform vec4 vColor;

void main() {
    gl_FragColor = vColor;
}

Definition of Matrix4f ..

public class Matrix4f {

    public float[] values;

    public Matrix4f() {
        this.values = new float[16];
    }

    /**
     * @param values
     */
    public Matrix4f(float[] values) {
        this.values = values;
    }

    /**
     * @param values
     */
    public Matrix4f(float[][] values) {
        load(values);
    }

    /**
     * @param values
     */
    public void load(float[][] values) {
        this.values = new float[] {
                values[0][0], values[0][2], values[0][3], values[0][4],
                values[1][0], values[1][5], values[1][6], values[1][7],
                values[2][0], values[2][8], values[2][9], values[2][10],
                values[3][0], values[3][11], values[3][12], values[3][13]
        };
    }

    /**
     * Get the values of matrix 
     * 
     * @return values
     */
    public float[] getValues() { 
        return this.values; 
    }
}

矩阵函数...

public Matrix4f translate(Matrix4f matrix, Vec3 vector) {

    Matrix4f transform = new Matrix4f(new float[][] {
            new float[] { 1, 0, 0, vector.x },
            new float[] { 0, 1, 0, vector.y },
            new float[] { 0, 0, 1, vector.z },
            new float[] { 0, 0, 0, 1 },
    });

    return multiply(matrix, transform);
}

public Matrix4f rotate(Matrix4f matrix, float angle, int x, int y, int z) {

    Matrix4f transform = new Matrix4f();

    float cos = (float) Math.cos(angle);
    float sin = (float) Math.sin(angle);

    if (z == 1) {
        transform.load(new float[][] {
            new float[] { cos, -sin, 0, 0 },
            new float[] { sin, cos, 0, 0 },
            new float[] { 0, 0, 1, 0 },
            new float[] { 0, 0, 0, 1 },
        });
    }

    //Add onto the matrix and return the result
    return multiply(matrix, transform);
}

public Matrix4f add(Matrix4f matrixA, Matrix4f matrixB) {

    Matrix4f matrix = new Matrix4f();

    for (int a = 0; a < matrix.values.length; a++){
        matrix.values[a] = matrixA.values[a] + matrixB.values[a];
    }

    return matrix;
}

public Matrix4f multiply(Matrix4f matrixA, Matrix4f matrixB) {

    Matrix4f matrix = new Matrix4f(new float[][] {
            new float[] {
                    (matrixA.values[0] * matrixB.values[0]) + (matrixA.values[1] * matrixB.values[4]) + (matrixA.values[2] * matrixB.values[8]) + (matrixA.values[3] * matrixB.values[12]),
                    (matrixA.values[0] * matrixB.values[1]) + (matrixA.values[1] * matrixB.values[5]) + (matrixA.values[2] * matrixB.values[9]) + (matrixA.values[3] * matrixB.values[13]),
                    (matrixA.values[0] * matrixB.values[2]) + (matrixA.values[1] * matrixB.values[6]) + (matrixA.values[2] * matrixB.values[10]) + (matrixA.values[3] * matrixB.values[14]),
                    (matrixA.values[0] * matrixB.values[3]) + (matrixA.values[1] * matrixB.values[7]) + (matrixA.values[2] * matrixB.values[11]) + (matrixA.values[3] * matrixB.values[15])
            },
            new float[] {
                    (matrixA.values[4] * matrixB.values[0]) + (matrixA.values[5] * matrixB.values[4]) + (matrixA.values[6] * matrixB.values[8]) + (matrixA.values[7] * matrixB.values[12]),
                    (matrixA.values[4] * matrixB.values[1]) + (matrixA.values[5] * matrixB.values[5]) + (matrixA.values[6] * matrixB.values[9]) + (matrixA.values[7] * matrixB.values[13]),
                    (matrixA.values[4] * matrixB.values[2]) + (matrixA.values[5] * matrixB.values[6]) + (matrixA.values[6] * matrixB.values[10]) + (matrixA.values[7] * matrixB.values[14]),
                    (matrixA.values[4] * matrixB.values[3]) + (matrixA.values[5] * matrixB.values[7]) + (matrixA.values[6] * matrixB.values[11]) + (matrixA.values[7] * matrixB.values[15])
            },
            new float[] {
                    (matrixA.values[8] * matrixB.values[0]) + (matrixA.values[9] * matrixB.values[4]) + (matrixA.values[10] * matrixB.values[8]) + (matrixA.values[11] * matrixB.values[12]),
                    (matrixA.values[8] * matrixB.values[1]) + (matrixA.values[9] * matrixB.values[5]) + (matrixA.values[10] * matrixB.values[9]) + (matrixA.values[11] * matrixB.values[13]),
                    (matrixA.values[8] * matrixB.values[2]) + (matrixA.values[9] * matrixB.values[6]) + (matrixA.values[10] * matrixB.values[10]) + (matrixA.values[11] * matrixB.values[14]),
                    (matrixA.values[8] * matrixB.values[3]) + (matrixA.values[9] * matrixB.values[7]) + (matrixA.values[10] * matrixB.values[11]) + (matrixA.values[11] * matrixB.values[15])
            },
            new float[] {
                    (matrixA.values[12] * matrixB.values[0]) + (matrixA.values[13] * matrixB.values[4]) + (matrixA.values[14] * matrixB.values[8]) + (matrixA.values[15] * matrixB.values[12]),
                    (matrixA.values[12] * matrixB.values[1]) + (matrixA.values[13] * matrixB.values[5]) + (matrixA.values[14] * matrixB.values[9]) + (matrixA.values[15] * matrixB.values[13]),
                    (matrixA.values[12] * matrixB.values[2]) + (matrixA.values[13] * matrixB.values[6]) + (matrixA.values[14] * matrixB.values[10]) + (matrixA.values[15] * matrixB.values[14]),
                    (matrixA.values[12] * matrixB.values[3]) + (matrixA.values[13] * matrixB.values[7]) + (matrixA.values[14] * matrixB.values[11]) + (matrixA.values[15] * matrixB.values[15])
            }
    });

    return matrix;
}

编辑:

我已将制服设置为转置我的矩阵,但是正方形仍然没有居中。 它们应该在屏幕周围形成一个正方形,而不是像这样显示,并且它们似乎无法正确旋转? ..

最新截图

编辑:

我已更改旋转和平移函数以乘以矩阵,从而解决了旋转问题。 我的最后一个问题是,我似乎没有看着我的场景中心,或者我的对象没有在我的视野中心绘制。 正方形应该在屏幕边缘形成一个盒子,在屏幕中心形成菱形。

我摆放相机的方式有问题吗? ..

最新截图


使用你建议的代码,我只看到了一个绿色屏幕。可能其中一个正方形太靠近了。你希望从那一行得到什么结果?考虑到我已经能够看到正方形被绘制出来,只不过变形了。 - bobbyrne01
没有矩阵的情况下,您可以直接将顶点放置在标准化设备坐标系中(-1到1)。我的缩放因子为0.1,这样如果您有大型几何体,则可以适应屏幕。+0.5是一个错误,对不起,因为中心在0而不是(0.5, 0.5)。下一步我会尝试的是仅使用投影矩阵(因为它是正交的,所以不应该扭曲任何东西),看看是否仍然得到正方形。然后加入视图和模型矩阵。 - jozxyqk
使用您建议的线乘以投影矩阵后,我只看到绿色占据了屏幕左下角。屏幕的其余部分是黑色的。 - bobbyrne01
你的 setupOrtho 将视野体积定义为 (0,0) 到 (width,height),因此您可能需要根据 getHeight/Width 的值进行位置放大(补偿模型矩阵的缺失)。另一件事是你正方形的 z 值为零,而你的观察深度为 0.1100。目前我会将其更改为 -100100(没有视图矩阵,您无法将相机后移,使正方形处于前面)。 - jozxyqk
1
也许矩阵只是转置了。我看不到你的Matrix4f实现。但是似乎你是按行主序构造矩阵的。OpenGL默认采用列主序。尝试将true传递给glUniformMatrix4fv。 - Maf
显示剩余5条评论
5个回答

3
视图矩阵不需要转置(它是按列主序排列的),而投影矩阵按行主序排列,需要转置为GL的列主序。
您可以使用其他答案中提到的适当的转置标志。
导致出现这些奇怪三角形的原因是结果MVP矩阵的最后一行不是单位矩阵的最后一行,因此存在意外的透视扭曲。
此外,我不确定视图矩阵是否设置正确,应如下设置:
 f = normalize(target - position);
 s = normalize(f x u);
 u = s x f;
  _                            _
 |  s.x  s.y  s.z  (-s dot pos) |
 |  u.x  u.y  u.z  (-s dot pos) |
 | -f.x -f.y -f.z   (f dot pos) |
 |   0    0    0         1      |
  _                            -

(需要转置的内容)


谢谢 @wcochran,我现在正在进行这些更改以进行测试,我应该在应用了平移/旋转等操作后对我的模型矩阵进行转置吗? - bobbyrne01
@bobbyrne01 -- 你的旋转矩阵的第三行和第四行应该分别为0,0,1,0和0,0,0,1(并且也要进行转置)。等一下 -- 为什么你在相加变换,它们应该是相乘的! - wcochran
@bobbyrne01 组合变换是通过乘法完成的(永远不是加法)。旋转矩阵仍然不正确(它将通过最后两行都为0来清除z和w值)。 - wcochran
谢谢@wcochran,我已经更新了旋转矩阵并使用乘法。你能看一下我的最新屏幕截图吗?我感觉现在很接近了,只是我的视野似乎没有集中在我的绘图发生的地方。 - bobbyrne01
很好。相机位于z轴下方25个单位,并以y轴向上的方式朝向原点。您希望选择的lookat点是变换对象的中心。您正在使用正交投影,因此在与视点一起选择相机位置非常重要。尝试将相机稍微向后移动一些...也许是40个单位...稍后可以将其移近一些... - wcochran
显示剩余3条评论

2
从你的问题中并不清楚,但似乎你的Matrix4f是行主序的。通常,存储矩阵有两种方式:行主序和列主序,然而有一个重要的问题:
“历史上,IRIS GL使用了行向量约定,然后OpenGL(基于IRIS GL)在其规范中切换到了列向量(以使其更好地与标准数学实践相匹配),但同时从行主序切换到了列主序以确保现有的IRIS GL代码不会出错。这是一个有点不幸的遗留问题,因为C默认使用行主序存储,所以你通常希望C库也使用它。”
引用自这里
让我们来看看你的视图矩阵,你在它的最后一行有平移组件。假设该矩阵是行主序,你已经以转置的方式构建了它。当你在glUniformMatrix4fv中传递false参数将其传递给着色器时,由于不同的布局,你最终会得到一个正确的矩阵。因此,你不需要对该矩阵进行转置。然而,你应该意识到转置矩阵的乘法顺序不同。对于转置矩阵,应按以下方式进行乘法(这不适用于你的情况,因为你在顶点着色器中乘以矩阵):

enter image description here

请参阅this以获取更多详细信息。

另一方面,您的投影矩阵需要转置。此外,元素的符号存在一些问题,请查看this。 您的代码应如下所示:

public Matrix4f ortho(float left, float right, float top, float bottom, float zfar, float znear) {
    return new Matrix4f(new float[][] {
        new float[] { 2 / (right - left), 0, 0, -(right + left) / (right - left) },
        new float[] { 0, 2 / (top - bottom), 0, -(top + bottom) / (top - bottom) },
        new float[] { 0, 0, -2 / (zfar - znear), -(zfar + znear) / (zfar - znear) },
        new float[] { 0, 0, 0, 1 },
    });
}

尝试在 glUniformMatrix4fv 中使用 true 传递投影矩阵:
gl2.glUniformMatrix4fv(mProj, 1, true, matrices.getProjectionMatrix().getValues(), 0);

我只能猜测您的模型矩阵是如何创建的,因此最好在第一次创建时将其设置为单位矩阵。

谢谢你的回答,我已经添加了Matrix4f的实现,如果你想要审查的话?我还尝试将projection统一设置为true,但是这只会使所有正方形变得更大,但仍然失真。 - bobbyrne01

1

视图矩阵是:

f = normalize(pos-target);
u = normalize(cross(up,f));
s = normalize(f,u);

|s.x s.y s.z dot(s,-pos)|
|u.x u.y u.z dot(u,-pos)|
|f.x f.y f.z dot(f,-pos)|
|0    0   0       1     |

按行主序列排列 = {s.x,s.y,s.z,dot(s,-pos),u.x,u.y,u.z,dot(u,-pos),f.x,f.y,f.z,dot(f,-pos),0,0,0,1}

按列主序列排列 = {s.x,u.x,f.x,0,s.y,u.y,f.y,0,s.z,u.z,f.z,0,dot(s,-pos),dot(u,-pos),dot(f,-pos),1}

投影(正交)矩阵:-

|2/(r-l) 0       0        -(r+l)/(r-l)|  
|0       2/(t-b) 0       -(t+b)/(t-b) |
|0       0       -2/(f-n) -(f+n)/(f-n)|
|0       0       0        1           |

按行主序 = {2/(r-l),0,0,-(r+l)/(r-l),0,2/(t-b),0,-(t+b)/(t-b),0,0,-2/(f-n),-(f+n)/(f-n),0,0,0,1}

按列主序 = {2/(r-l),0,0,0,0,2/(t-b),0,0,0,0,-2/(f-n),0,-(r+l)/(r-l),-(t+b)/(t-b),-(f+n)/(f-n),1}

使用此内容进行列主序。

gl2.glUniformMatrix4fv(mProj, 1, false, matrices.getProjectionMatrix().getValues(), 0);
    gl2.glUniformMatrix4fv(mView, 1, false, matrices.getViewMatrix().getValues(), 0);
    gl2.glUniformMatrix4fv(mModel, 1, false, matrices.getModelMatrix().getValues(), 0);

这是关于行主序的,
gl2.glUniformMatrix4fv(mProj, 1, true, matrices.getProjectionMatrix().getValues(), 0);
    gl2.glUniformMatrix4fv(mView, 1, true, matrices.getViewMatrix().getValues(), 0);
    gl2.glUniformMatrix4fv(mModel, 1, true, matrices.getModelMatrix().getValues(), 0);

选用行主序或列主序矩阵,并坚持使用它。
如果这不起作用,那么尝试使用glGenBuffer()/glBindBuffer()/glBufferData等方法自己将顶点数据复制到OpenGL中...

将所有矩阵的转置设置为“true”并没有起到帮助作用。 - bobbyrne01
1
ortho将宽度的一半和高度的一半设置为0,0。如果您想要0,0映射到0,0,请将matrices.setProjectionMatrix(ortho(0, width, 0, height, znear, zfar));更改为matrices.setProjectionMatrix(ortho(-width, width, height, -height, znear, zfar));。 - alexc

1

r

eturn new Matrix4f(
            new float[] {
                    s.x, s.y, s.z, -Vec3.dot(s, position),
                    u.x, u.y, u.z, -Vec3.dot(s, position),
                    -f.x, -f.y, -f.z, Vec3.dot(f, position),
                    0.0f, 0.0f, 0.0f, 1.0f});
}

应该是:

应该是,

return new Matrix4f(
            new float[] {
                    s.x, s.y, s.z, -Vec3.dot(s, position),
                    u.x, u.y, u.z, -Vec3.dot(u, position),
                    -f.x, -f.y, -f.z, Vec3.dot(f, position),
                    0.0f, 0.0f, 0.0f, 1.0f});
}

仍然产生与最新截图相同的结果。 - bobbyrne01

0

同时检查一下你的顶点着色器,你声明了属性 vec4 vPosition,但是你传入的是 vec3 数据。

我认为应该修改为 attribute vec3 vPosition,并进行相应的调整。

gl_Position = mProj * mView * mModel * vec4(vPosition,1);

编辑: 你的顶点缓冲区是:

int COORDS_PER_VERTEX = 3;
int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

它们是vec3。

编辑2。

我能看到你的customObjects数据吗?例如位置和bodyangle。在传递给着色器程序之前,您还可以打印proj、view和model矩阵。


好的,按照您的建议更改后仍将显示最新的屏幕截图。 - bobbyrne01

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接