【问题标题】:Texture Buffers and glMultiDrawElements纹理缓冲区和 glMultiDrawElements
【发布时间】:2012-10-22 19:05:59
【问题描述】:

背景故事: 我正在尝试使用单个绘图调用在屏幕上绘制尽可能多的正方形。我正在使用专门用于 2D 绘图的自定义 glsl 顶点着色器,它应该从 samplerBuffer 中提取正方形顶点的位置数据。因为我不需要担心旋转或缩放正方形,所以我需要做的就是将位置数据加载到缓冲区中,将纹理绑定到该缓冲区,然后使用采样器获取每个顶点在着色器中的位置。为了获得纹理的索引,我将每个元素索引存储为顶点的 z 分量。

对于一千个左右的方格,一切似乎都很好,但在那之后我开始变得奇怪地眨眼。似乎它没有在每个绘制步骤中绘制所有方格,或者可能没有使用所有位置,因此许多方格重叠。

奇怪的是,如果我使用 drawElements 而不是 drawElementsMulti,闪烁就会消失(当然,所有的正方形都被绘制为一个单独的对象,这是我不想要的)

我的一个问题是我的位置数据是否限制为最大纹理大小或最大纹理缓冲区大小。如果我被限制在更小的最大纹理尺寸,我该如何绕过它?所有纹理缓冲区空间都在那里一定是有原因的,但我显然不知道如何正确使用它。

我也在想,也许 glMultiDrawElements 正在做一些我没有考虑到采样器的事情。 Idk,在这一点上我真的很迷茫,但是..它适用于较小数量的正方形,所以我必须做正确的事情。

[EDIT] 代码已更改以反映以下建议(以及为了便于阅读),但问题仍然存在。

好的,这里有一些代码。首先是顶点着色器:

uniform mat3 projection;
attribute vec3 vertex;

uniform samplerBuffer positionSampler;

attribute vec4 vertex_color;
varying vec4 color;

float positionFetch(int index)
{
    // I've tried texelFetch here as well, same effect
    float value = texelFetchBuffer(positionSampler, index).r;
    return value;
}

void main(void)  
{ 
    color = vec4(1, 1, 1, 1);
    // use the z-component of the vertex to look up the position of this instance in the texture
    vec3 real_position = vec3(vertex.x + positionFetch(int(vertex.z)*2), vertex.y + positionFetch(int(vertex.z)*2+1), 1); 
    gl_Position = vec4(projection * real_position, 1); 
}

现在我的 GLRenderer,抱歉代码太多了,我真的想确保这里有足够的信息来获得答案。这真的让我发疯了,Java 的例子似乎很难找到(也许这段代码会帮助其他人的探索):

public class GLRenderer extends GLCanvas implements GLEventListener, WindowListener
{

    private static final long serialVersionUID = -8513201172428486833L;

    private static final int bytesPerFloat = Float.SIZE / Byte.SIZE;
    private static final int bytesPerShort = Short.SIZE / Byte.SIZE;

    public float viewWidth, viewHeight;
    public float screenWidth, screenHeight;

    private FPSAnimator animator;

    private boolean didInit = false;

    JFrame the_frame;
    SquareGeometry geometry;

    // Thought power of 2 might be required, doesn't seem to make a difference
    private static final int NUM_THINGS = 2*2*2*2*2*2*2*2*2*2*2*2*2*2; 

    float[] position = new float[NUM_THINGS*2];

    // Shader attributes
    private int shaderProgram, projectionAttribute, vertexAttribute, positionAttribute;

    public static void main(String[] args) 
    {
        new GLRenderer();
    }

    public GLRenderer()
    {
        // setup OpenGL Version 2
        super(new GLCapabilities(GLProfile.get(GLProfile.GL2)));

        addGLEventListener(this);
        setSize(1800, 1000);

        the_frame = new JFrame("Hello World");
        the_frame.getContentPane().add(this);
        the_frame.setSize(the_frame.getContentPane().getPreferredSize());
        the_frame.setVisible(true);
        the_frame.addWindowListener(this);

        animator = new FPSAnimator(this, 60);
        animator.start();
    }

    // Called by the drivers when the gl context is first made available
    public void init(GLAutoDrawable d)
    {        
        final GL2 gl = d.getGL().getGL2();
        IntBuffer asd = IntBuffer.allocate(1);
        gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_BUFFER_SIZE, asd);
        System.out.println(asd.get(0));
        asd = IntBuffer.allocate(1);
        gl.glGetIntegerv(GL2.GL_MAX_TEXTURE_SIZE, asd);
        System.out.println(asd.get(0));
        shaderProgram = ShaderLoader.compileProgram(gl, "default");
        gl.glLinkProgram(shaderProgram);

        _getShaderAttributes(gl);

        gl.glUseProgram(shaderProgram);
        _checkGLCapabilities(gl);
        _initGLSettings(gl);

        // Calculate batch of vertex data from dirt geometry
        geometry = new SquareGeometry(.1f);
        geometry.buildGeometry(viewWidth, viewHeight);
        geometry.finalizeGeometry(NUM_THINGS);

        geometry.vertexBufferID = _generateBufferID(gl);
        _loadVertexBuffer(gl, geometry);

        geometry.indexBufferID = _generateBufferID(gl);
        _loadIndexBuffer(gl, geometry);

        geometry.positionBufferID = _generateBufferID(gl);

        // initialize buffer object
        int size = NUM_THINGS * 2 * bytesPerFloat;
        System.out.println(size);

    IntBuffer bla = IntBuffer.allocate(1);
    gl.glGenTextures(1, bla);
    geometry.positionTextureID = bla.get(0);

        gl.glUniform1i(positionAttribute, 0);

        gl.glActiveTexture(GL2.GL_TEXTURE0);
        gl.glBindTexture(GL2.GL_TEXTURE_BUFFER, geometry.positionTextureID);
        gl.glBindBuffer(GL2.GL_TEXTURE_BUFFER, geometry.positionBufferID);
        gl.glBufferData(GL2.GL_TEXTURE_BUFFER, size, null, GL2.GL_DYNAMIC_DRAW);
        gl.glTexBuffer(GL2.GL_TEXTURE_BUFFER, GL2.GL_R32F, geometry.positionBufferID);
    }

    private void _initGLSettings(GL2 gl)
    {
        gl.glClearColor(0f, 0f, 0f, 1f);
    }

    private void _loadIndexBuffer(GL2 gl, SquareGeometry geometry)
    {
        gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, geometry.indexBufferID);
        gl.glBufferData(GL2.GL_ELEMENT_ARRAY_BUFFER, bytesPerShort*NUM_THINGS*geometry.getNumPoints(), geometry.indexBuffer, GL2.GL_STATIC_DRAW);
    }

    private void _loadVertexBuffer(GL2 gl, SquareGeometry geometry)
    {
        int numBytes = geometry.getNumPoints() * 3 * bytesPerFloat * NUM_THINGS;

        gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, geometry.vertexBufferID);
        gl.glBufferData(GL2.GL_ARRAY_BUFFER, numBytes, geometry.vertexBuffer, GL2.GL_STATIC_DRAW);
        gl.glEnableVertexAttribArray(vertexAttribute);
        gl.glVertexAttribPointer(vertexAttribute, 3, GL2.GL_FLOAT, false, 0, 0);
    }

    private int _generateBufferID(GL2 gl)
    {
        IntBuffer bufferIDBuffer = IntBuffer.allocate(1);
        gl.glGenBuffers(1, bufferIDBuffer);

        return bufferIDBuffer.get(0);
    }

    private void _checkGLCapabilities(GL2 gl)
    {
        // TODO: Respond to this information in a meaningful way.
        boolean VBOsupported = gl.isFunctionAvailable("glGenBuffersARB") && gl.isFunctionAvailable("glBindBufferARB")
                && gl.isFunctionAvailable("glBufferDataARB") && gl.isFunctionAvailable("glDeleteBuffersARB");

        System.out.println("VBO Supported: " + VBOsupported);
    }

    private void _getShaderAttributes(GL2 gl)
    {
        vertexAttribute = gl.glGetAttribLocation(shaderProgram, "vertex");
        projectionAttribute = gl.glGetUniformLocation(shaderProgram, "projection");
        positionAttribute = gl.glGetUniformLocation(shaderProgram, "positionSampler");
    }

    // Called by me on the first resize call, useful for things that can't be initialized until the screen size is known
    public void viewInit(GL2 gl)
    {
        for(int i = 0; i < NUM_THINGS; i++)
        {
            position[i*2] = (float) (Math.random()*viewWidth);
            position[i*2+1] = (float) (Math.random()*viewHeight);
        }

        gl.glUniformMatrix3fv(projectionAttribute, 1, false, Matrix.projection3f, 0);

        // Load position data into a texture buffer
        gl.glBindBuffer(GL2.GL_TEXTURE_BUFFER, geometry.positionBufferID);
        ByteBuffer textureBuffer = gl.glMapBuffer(GL2.GL_TEXTURE_BUFFER, GL2.GL_WRITE_ONLY);
        FloatBuffer textureFloatBuffer = textureBuffer.order(ByteOrder.nativeOrder()).asFloatBuffer();

        for(int i = 0; i < position.length; i++)
        {
            textureFloatBuffer.put(position[i]);
        }

        gl.glUnmapBuffer(GL2.GL_TEXTURE_BUFFER);
        gl.glBindBuffer(GL2.GL_TEXTURE_BUFFER, 0);
    }

    public void display(GLAutoDrawable d)
    {

        if (!didInit || geometry.vertexBufferID == 0)
        {
            return;
        }

        //long startDrawTime = System.currentTimeMillis();
        final GL2 gl = d.getGL().getGL2();

        gl.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);


        // If we were drawing any other buffers here we'd need to set this every time
        // but instead we just leave them bound after initialization, saves a little render time
        // No combination of these seems to fix the problem
        //gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, geometry.vertexBufferID);
        //gl.glVertexAttribPointer(vertexAttribute, 3, GL2.GL_FLOAT, false, 0, 0);
        //gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, geometry.indexBufferID);
        gl.glBindBuffer(GL2.GL_TEXTURE_BUFFER, geometry.positionBufferID);
        //gl.glActiveTexture(GL2.GL_TEXTURE0);
        //gl.glTexBuffer(GL2.GL_TEXTURE_BUFFER, GL2.GL_R32F, geometry.positionBufferID);

        _render(gl, geometry);

        // Also tried these
        //gl.glFlush();
        //gl.glFinish();
    }

    public void _render(GL2 gl, SquareGeometry geometry)
    {
        gl.glMultiDrawElements(geometry.drawMode, geometry.countBuffer, GL2.GL_UNSIGNED_SHORT, geometry.offsetBuffer, NUM_THINGS);
        // This one works, but isn't what I want
        //gl.glDrawElements(GL2.GL_LINE_LOOP, count, GL2.GL_UNSIGNED_SHORT, 0);
    }

    public void reshape(GLAutoDrawable d, int x, int y, int width, int height)
    {
        final GL2 gl = d.getGL().getGL2();
        gl.glViewport(0, 0, width, height);
        float ratio = (float) height / width;

        screenWidth = width;
        screenHeight = height;
        viewWidth = 100;
        viewHeight = viewWidth * ratio;

        Matrix.ortho3f(0, viewWidth, 0, viewHeight);

        if (!didInit)
        {
            viewInit(gl);
            didInit = true;
        } 
        else
        {
            // respond to view size changing
        }
    }
}

最后一位是 SquareGeometry 类,它保存所有的 bufferID 和顶点数据,但也负责正确填充顶点缓冲区,以便每个顶点的 z 分量可以作为位置纹理的索引:

public class SquareGeometry
{
    public float[] vertices = null;

    ShortBuffer indexBuffer;
    IntBuffer countBuffer;
    PointerBuffer offsetBuffer;
    FloatBuffer vertexBuffer;

    public int vertexBufferID = 0;
    public int indexBufferID = 0;
    public int positionBufferID = 0;
    public int positionTextureID = 0;

    public int drawMode;

    protected float width = 0;
    protected float height = 0;

    public SquareGeometry(float size)
    {
        width = size;
        height = size;
    }

    public void buildGeometry(float viewWidth, float viewHeight)
    {
        vertices = new float[4 * 2];
        vertices[0] = -width/2;
        vertices[1] = -height/2;
        vertices[2] = -width/2;
        vertices[3] = height/2;
        vertices[4] = width/2;
        vertices[5] = height/2;
        vertices[6] = width/2;
        vertices[7] = -height/2;

        drawMode = GL2.GL_POLYGON;
    }

    public void finalizeGeometry(int numInstances)
    {
        if(vertices == null) return;

        int num_vertices = this.getNumPoints();
        int total_num_vertices = numInstances * num_vertices;

        // initialize vertex Buffer (# of coordinate values * 4 bytes per float)  
        ByteBuffer vbb = ByteBuffer.allocateDirect(total_num_vertices * 3 * Float.SIZE);
        vbb.order(ByteOrder.nativeOrder());
        vertexBuffer = vbb.asFloatBuffer();

        for(int i = 0; i < numInstances; i++)
        {
            for(int v = 0; v < num_vertices; v++)
            {
                int vertex_index = v * 2;
                vertexBuffer.put(vertices[vertex_index]);
                vertexBuffer.put(vertices[vertex_index+1]);
                vertexBuffer.put(i);
            }
        }
        vertexBuffer.rewind();

        // Create the indices
        vbb = ByteBuffer.allocateDirect(total_num_vertices * Short.SIZE);
        vbb.order(ByteOrder.nativeOrder());
        indexBuffer = vbb.asShortBuffer();

        for(int i = 0; i < total_num_vertices; i++)
        {
            indexBuffer.put((short) (i));
        }
        indexBuffer.rewind();

        // Create the counts
        vbb = ByteBuffer.allocateDirect(numInstances * Integer.SIZE);
        vbb.order(ByteOrder.nativeOrder());
        countBuffer = vbb.asIntBuffer();
        for(int i = 0; i < numInstances; i++)
        {
            countBuffer.put(num_vertices);
        }
        countBuffer.rewind();

        // create the offsets
        offsetBuffer = PointerBuffer.allocateDirect(numInstances);
        for(int i = 0; i < numInstances; i++)
        {
            offsetBuffer.put(num_vertices*i*2);
        }
        offsetBuffer.rewind();
    }

    public int getNumPoints() 
    {
        return vertices.length/2;
    }
}

【问题讨论】:

  • 还有一些注意事项:我没有发布处理创建投影矩阵的矩阵类,所以你只需要相信我。此外,glGetError 返回全 0 :(
  • 还要检查 -Djogl.debug.TraceGL -Djogl.debug.DebugGL
  • 哇,非常感谢。这里有很多输出,已经看到我做错了一些事情,但到目前为止还没有奇迹般的修复。
  • 我刚刚添加了 gl.glFlush();和 gl.glFinish();到平局结束,我认为这是 opengl 的 vsync 方式,似乎没有任何区别。
  • 你正在使用 GLEventListener,所以 swapBuffers 将在没有 glFlush 等的情况下被调用

标签: java opengl glsl jogl


【解决方案1】:

好的,首先,您没有在着色器中设置 gl_Color ,这可能是这里的问题,您只能幸运地使用少量数字。这是一个变化的,但你也有片段着色器来获取价值吗?

您绝不会确保 NUM_THINGS*2

您还绑定了 positionBufferID 缓冲区,然后取消绑定但永远不会重新绑定。

您创建了 positionTextureID,但从未将任何数据放在那里。这也是您放入采样器 positionSampler 并尝试访问的内容。

是的,有很多问题,但我的直觉告诉我,最后一个问题可能是这里真正的问题。

【讨论】:

  • 还有一个片段着色器,除了设置颜色之外什么都不做,所以不是这样。我完全同意 GL_MAX_TEXTURE_SIZE 的事情,但我希望你能解释为什么我受此限制,或者更确切地说,如何解决它。我想开发一种方法,以便我可以使用整个 GL_MAX_TEXTURE_BUFFER_SIZE。重新绑定 positionBufferID 并不能解决问题,我并不认为这是必要的。我认为我通过 gl.glTexBuffer 将 TEXTURE0 指向 positionBufferID 缓冲区来为纹理提供数据。不是这样吗?感谢您的帮助。
  • 我不能肯定地说;我的机器上没有 Java ;-) 但纹理中没有数据。除非你能告诉我 positionTextureID 是在哪里填充数据,否则我认为这是你的问题。
  • 但是我怎么把数据放进去呢?我正在从这个示例jotschi.de/?p=424 开始工作,类似地,除了 glTexBuffer 之外,似乎从未使用过实际的纹理。再说一遍,这不是数据的来源吗?我填充了 positionBuffer,然后告诉 positionTexture 使用那个缓冲区?还是我在这里遗漏了什么?
  • 你需要记住openGL是一个状态机。当前操作总是受到状态的影响。在您发布的链接中,第 176 行发生了奇迹。创建纹理,然后绑定,然后关联到缓冲区。 glTexBufferExt 有效,因为它绑定到当前活动纹理。在 GLRender 第 94 行之后,您需要调用 glTexBuffer(Ext) 函数。
【解决方案2】:

好的,我已经解决了,但我仍然不清楚最初的问题是什么。我通过简化绘图以使用 drawArrays 而不是 drawElements 或 multiDrawElements 来修复它。我真的不确定为什么我认为我需要它们,因为在这种情况下我真的不需要。我很确定我在索引和偏移量方面搞砸了一些事情。

此外,就绑定纹理缓冲区的正确方法而言,我上面的代码和我在评论中发布的链接中找到的示例都不正确。

如果有人对像这样使用纹理缓冲区的正确方法感兴趣,我只是在http://zebadiah.me/?p=44 上写了一篇相当广泛的文章。谢谢大家的帮助。

【讨论】:

    猜你喜欢
    • 2014-12-05
    • 2014-07-11
    • 2014-07-31
    • 2012-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-14
    • 2011-11-24
    相关资源
    最近更新 更多