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

Posted

技术标签:

【中文标题】Opengl 未使用着色器正确绘制 - 矩阵设置/初始化问题?【英文标题】:Opengl not drawing correctly using shader - issue with matrix setup/initialization? 【发布时间】:2015-05-11 15:58:34 【问题描述】:

当我渲染我的应用程序时,我希望在窗口边缘看到一些矩形。相反,我看到了这个..

所有对象都将位于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));

计算正交Projection矩阵..

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);

顶点着色器..

#version 120

attribute vec4 vPosition;

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

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

片段着色器..

#version 120

uniform vec4 vColor;

void main() 
    gl_FragColor = vColor;

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;

编辑:

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

编辑:

我已经更改了旋转和平移函数来乘以矩阵,从而解决了旋转问题。我的最后一个问题是我似乎没有在看场景的中心,或者我的对象没有画在我的视野中心。方块应围绕屏幕边缘形成一个框,屏幕中心为菱形。

我的相机位置有问题吗? ..

【问题讨论】:

您可以使用gl_Position = vec4(vPosition.xyz * 0.1 + 0.5, 1.0) 对其进行测试,看看它是否至少可以绘制,然后尝试缩小出现故障的矩阵。也许是行/列的主要内容,但只是猜测。 使用你建议的线路,我只是得到一个绿屏。假设这是距离太近的正方形之一。你对那条线有什么期待?考虑到我可以看到已经绘制了正方形,它们只是被扭曲了。 没有任何矩阵,您将顶点直接放在标准化设备坐标中(-1 到 1)。我的 0.1 比例是这样的,如果你有很大的几何图形,它会适合屏幕。 +0.5 是一个错误,对不起,因为中心是 0 而不是 (0.5, 0.5)。接下来我要尝试的是使用投影矩阵(它不应该扭曲任何东西,因为它是正交的),看看你是否仍然得到正方形。然后放入视图和模型矩阵。 使用您建议的线乘以投影矩阵,我只看到绿色消耗屏幕的左下角。屏幕的其余部分是黑色的。 也许矩阵只是转置了。我看不到您的 Matrix4f 实现。但似乎您正在按行主要顺序构建矩阵。 OpenGL 默认采用列优先顺序。尝试将 true 传递给 glUniformMatrix4fv。 【参考方案1】:

视图矩阵不需要需要转置(它以列为主),而投影矩阵以行为主,确实需要转置进入 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 -- 是的 -- 您的翻译是行优先的,因此需要转置!顺便说一句,最后一行应该是 0,0,0,1 not 0,0,0,0. @bobbyrne01 -- 旋转矩阵的第 3 行和第 4 行应分别为 0,0,1,0 和 0,0,0,1(也应转置)。等一下——你为什么要将变换添加在一起——它们应该相乘! 我已将 rotate() 更改为 multiplytranslate() 更改为 add。如果我将 translate() 更改为 multiply 我会得到一个空白屏幕。 @bobbyrne01 组合转换是通过乘法完成的(从不加法)。旋转矩阵仍然不正确(它将通过将最后两行全为 0 来消除 z 和 w 值)。【参考方案2】:

您的问题并不清楚,但您的 Matrix4f 似乎是行主要的。一般来说,存储矩阵有两种方式:行优先和列优先,但是有一个重要的问题:

从历史上看,IRIS GL 使用行向量约定,然后 OpenGL(基于 IRIS GL)在其规范中切换到列向量(以使其与标准数学实践更好地匹配),但同时切换存储从行优先到列优先的布局,以确保现有的 IRIS GL 代码不会中断。这是一个有点不幸的遗留问题,因为 C 默认使用行优先存储,所以您通常希望 C 库也使用它。

取自here

让我们看看你的视图矩阵,你在它的最后一行有翻译组件。假设矩阵是行优先的,您已经以转置方式构建它。当您将它传递给 glUniformMatrix4fv 中为 false 的着色器时,由于布局不同,您最终会得到一个正确的矩阵。因此,您不需要转置该矩阵。但是,您应该注意矩阵乘法的不同顺序,只要它们被转置。转置矩阵应按如下方式相乘(不适用于您的情况,因为您在顶点着色器中相乘矩阵):

更多详情请见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,但只是使所有方块变大但仍然变形。【参考方案3】:

视图矩阵是:

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),点(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 没有帮助 ortho 将 width/2,height/2 设置为 0,0。如果您希望 0,0 映射到 0,0 更改 matrices.setProjectionMatrix(ortho(0, width, 0, height, znear, zfar)); to matrices.setProjectionMatrix(ortho(-width, width, height, -height, znear, zfar));【参考方案4】:

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);

【讨论】:

仍然给出与最新截图相同的结果【参考方案5】:

还要检查你的顶点着色器,你有属性 vec4 vPosition,但你传入的是 vec3 数据。

我认为应该是属性 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。您能否在将项目、视图和模型矩阵传递给您的着色器程序之前打印出它们。

【讨论】:

好的,改成你的建议还是会给出最新的截图

以上是关于Opengl 未使用着色器正确绘制 - 矩阵设置/初始化问题?的主要内容,如果未能解决你的问题,请参考以下文章

具有独特着色器的 OpenGL 多次绘制调用会产生空白屏幕

OpenGL 片段着色器未写入 fbo 颜色缓冲区

如何使用OpenGL着色器在平面上绘制没有透视的纹理?

无法获得简单的 2D 着色器以在 C++ openGL 中绘制红色三角形(无编译器错误)

OpenGL几何着色器三角形不会没有错误地绘制

片段着色器中未使用纹理数据 - OpenGL