如何告诉OpenGL立即更新着色器制服?

时间:2016-10-24 17:27:48

标签: opengl vertex-shader

我试图做一些非常简单的事情,我感到非常沮丧。我已经设置了一个简单的顶点着色器,因为显然你现在需要一个让OpenGL做任何事情或者可怕的弃用神对你皱眉的东西:

#version 110

uniform mat4 scene_matrix;
attribute vec4 a_position;

void main()
{
    gl_Position = scene_matrix * a_position;
}

奇妙。在捣乱10个小时以创建一个合适的矩阵后,我将其传递到着色器glUniformMatrix4并且它的工作非常好。也就是说,直到我想渲染多个THING。

所以我的目标是:对于场景中的每个对象,我根据对象的坐标计算适当的世界矩阵,调用glUniformMatrix4告诉顶点着色器关于矩阵,调用glBegin() ,然后绘制愚蠢的对象,并调用glEnd()。不幸的是,它可怜地在同一个愚蠢的地方(最后一个物体的位置)绘制所有物体。显然它是懒惰的缓冲事物,并且直到最后都没有运行顶点着色器,因为它已经忘记了以前的矩阵。但是我怎么告诉它不要这样做?

FloatBuffer matrixBuf = ByteBuffer.allocateDirect(16 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
for (int i = 0; i < 500; i++) {
    matrixBuf.rewind();
    matrices.world.translate(0.1, 0.1, 0.5);
    matrices.calc();
    matrices.combined.put(matrixBuf);
    matrixBuf.rewind();
    glUniformMatrix4(glGetUniformLocation(programId, "scene_matrix"), false, matrixBuf);

    glBegin(GL_TRIANGLES);
    {
        //final int T = 1;
        final float z = 0f;
        final float R = 0.5f;
        //glVertexAttrib2f(T, 0, 1);
        glVertex3f(-R, -R, z);

        //glVertexAttrib2f(T, 1, 0);
        glVertex3f(R, R, z);

        //glVertexAttrib2f(T, 0, 0);
        glVertex3f(-R, R, z);

        /*//glVertexAttrib2f(T, 0, 1);
        glVertex3f(-R, -R, z);

        //glVertexAttrib2f(T, 1, 1);
        glVertex3f(R, -R, z);

        //glVertexAttrib2f(T, 1, 0);
        glVertex3f(R, R, z);*/
    }
    glEnd();

    //glFlush(); // without this, all the triangles are in the same place
}

我发现在每个glFlush()之后调用glEnd()可以解决问题。但是,这会导致性能问题。我只绘制了500个物体(每个物体都是一个可怜的三角形)并且它已经最大化了CPU并且电脑中出现了电容器呜呜声。感觉不对。我确定glFlush()必须是矫枉过正。

我发现在glLinkProgram(programId);之后用glUniformMatrix4重新安排程序也可以解决问题,但它仍然会慢一个数量级。

我到处都看。我只想知道用于告诉它的函数的名称是什么,然后运行顶点着色器 ,然后我可以重新配置下一个对象的制服。

或者这不是我应该做的事情?我应该放弃顶点管道,在Java端进行顶点的所有矩阵变换并传入已经变换的顶点吗?我应该屈服于弃用恶魔并使用传统的OpenGL矩阵堆栈来判断它是否更合作?任何帮助表示赞赏。

3 个答案:

答案 0 :(得分:4)

您犯的第一个重大错误就是使用glBegin / glEnd。这已经与固定功能管道一起弃用。

为什么你看到所有对象都被绘制在同一个地方的原因:因为你的变换矩阵在循环变量下是不变的,即循环的迭代对你的变换矩阵的值没有影响:这个块在这里:

matrixBuf.rewind();
matrices.world.translate(0.1, 0.1, 0.5);
matrices.calc();
matrices.combined.put(matrixBuf);
matrixBuf.rewind();

必须依赖于i某种方式, matrices.combined实际上会与翻译结合使用,但是方法名称.put会明确表示,它只是被替换了。

答案 1 :(得分:1)

许多状态更改要求您在对象对渲染管道可见之前对其进行绑定。

对于着色器绑定意味着glUseProgram。我不记得什么规范说制服和重复使用着色器。

使用glDrawArrays很简单。您只需将顶点数据设置为数组即可。启用并设置glVertexAttribPointer。在简单的示例情况下,您甚至不必使用缓冲区对象。您可以将glEnableVertexAttribArray视为glBegin。然后将数据放入顶点数组作为对glVertex *()的调用。 glVertexAttribPointer和glDraw *()可以像glEnd一样强硬。

对简单DrawArrays的优化是使用带有DrawElements的索引来避免数据中的重复顶点。然后将顶点和索引放到缓冲对象中会使一切变得快速。

奖励:这个案例看起来有点像你可以跳过矩阵来简化你的制服到翻译矢量。

答案 2 :(得分:0)

所以根据评论的答案是:没有这样的函数调用;这应该是自然而然的。

那为什么不呢?主知道。可能是一个有缺陷的司机,可能是我的傻瓜,可能两者兼而有之。在任何情况下,如果我使用glDrawArraysglDrawElements而不是像glVertex3这样的固定功能管道调用,问题会立即完全消失。 (是的,这绝对是我改变在工作和不工作之间切换它的唯一方法。)对于那些在淹没之前一直试图从浅薄的事物开始的人来说,这不是一个很好的体验除非你已经知道他们做什么API,否则所有记录不佳的人的权重。

我发现这个很好的Stack Overflow帖子总结了这些顶点规范函数的功能https://stackoverflow.com/a/8705304

以下是此处参考的图纸代码:

顶点着色器:

#version 110

uniform mat4 scene_matrix;
attribute vec4 a_position;
attribute vec2 a_texcoord;
varying vec2 v_texcoord;

void main()
{
    gl_Position = scene_matrix * a_position;

    // pass texture coordinates on to fragment shader
    v_texcoord = a_texcoord;
}

片段着色器:

#version 110

uniform sampler2D texture;
varying vec2 v_texcoord;

void main()
{
    gl_FragColor = texture2D(texture, v_texcoord);
    //gl_FragColor = vec4(1, 0, 0, 1);
}

绘制一个东西的代码:

// Define a shape (a square)
// First 3 numbers on each line are vertex coords, next 2 are corresponding tex coords
final float R = 0.5f;
float[] vertices = {
    -R, -R, 0, 0, 1,
    +R, +R, 0, 1, 0,
    -R, +R, 0, 0, 0,
    -R, -R, 0, 0, 1,
    +R, -R, 0, 1, 1,
    +R, +R, 0, 1, 0,
};

// Create and bind a buffer object
int bufferId = glGenBuffers();
glBindBuffer(GL_ARRAY_BUFFER, bufferId);

// Just Java fluff: local buffer used for transferring data to the native method
FloatBuffer vertices1 = ByteBuffer.allocateDirect(vertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
vertices1.put(vertices);
vertices1.rewind();

// Load the vertices into the proper buffer
glBufferData(GL_ARRAY_BUFFER, vertices1, GL_STREAM_DRAW);

// Define layout of data in buffer:
// First, vertex coords (attrib "0"):
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, false, 5 * 4, 0 * 4);
// The last 2 params to glVertexAttribPointer are stride and offset.
// Stride tells the total number of bytes between the start of one vertex
// and the start of the next, 5 * 4 because the data rows above have 5 elements
// and each element is a 4-byte float.
// Offset tells the byte offset of the start of the first element in the buffer,
// 0 * 4 because our coord data begins right at the beginning of the buffer.

// Now, tex coords (attrib "1"):
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, false, 5 * 4, 3 * 4);
// Stride is the same as above, but need non-zero offset because the tex
// coords start after the 3 vertex coords in each row of data in the buffer.

// Call glDrawArrays as many times as you want to repeatedly,
// possibly with different uniform values, and with no need
// for glBegin(), glEnd(), or repeated glFlush().
glDrawArrays(GL_TRIANGLES, 0, vertices.length);

// Cleanup
glDeleteBuffers(bufferId);