【问题标题】:Is there a simple way to get the depth of an object in OpenGL (JOGL)有没有一种简单的方法可以在 OpenGL (JOGL) 中获取对象的深度
【发布时间】:2016-01-08 21:07:51
【问题描述】:

单击对象时如何在 3D 空间中获取对象的 z 坐标。 (它不是真正的对象,而是图形,我需要知道用户选择了什么)我使用 JOGL。

【问题讨论】:

  • OpenGL 对“对象”一无所知。它只是渲染三角形。
  • 你可以用glReadPixels做很多工作,但这并不神奇。
  • 您好,您可以在后台缓冲区中以独特的颜色渲染每个不同的元素,甚至无需交换缓冲区,然后通过 Dietrich 建议的函数检索它
  • 我建议你看看这个公认的答案:stackoverflow.com/a/23911297/458157
  • 您使用的是哪个配置文件?我将在接下来的几天里带着一个新的采摘样品来

标签: opengl jogl opengl-4


【解决方案1】:

我刚刚完成从g-truck ogl-samples 移植picking sample

我会尽量给你一个关于代码的快速解释。

我们首先启用深度测试

private boolean initTest(GL4 gl4) {

    gl4.glEnable(GL_DEPTH_TEST);

    return true;
}

initBuffer我们:

  • 使用glGenBuffers 生成我们需要的所有缓冲区
  • 绑定元素缓冲区,然后我们传输索引的内容。每个索引指的是要使用的顶点。我们需要先绑定它,因为glBufferData 将使用第一个参数指定的目标处的任何边界,在这种情况下为GL_ELEMENT_ARRAY_BUFFER
  • 对顶点本身执行相同操作。
  • 获取GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT(它是一个全局参数)以确定最小统一块大小来存储我们的变换变量。如果我们想通过 glBindBufferRange 绑定它,这是必要的,我们不会使用该函数,而是绑定我们的拾取缓冲区,这就是为什么我们只传递一个浮点的大小,Float.BYTES
  • glBufferData 的最后一个参数只是一个提示(这取决于 OpenGL 和驱动程序做他们想做的事),正如你所见,索引和顶点是静态的,因为我们不再更改它们,而是统一缓冲区是动态的,因为我们将在每一帧更新它们。

代码:

private boolean initBuffer(GL4 gl4) {

    gl4.glGenBuffers(Buffer.MAX.ordinal(), bufferName, 0);

    gl4.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferName[Buffer.ELEMENT.ordinal()]);
    ShortBuffer elementBuffer = GLBuffers.newDirectShortBuffer(elementData);
    gl4.glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementSize, elementBuffer, GL_STATIC_DRAW);
    gl4.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    gl4.glBindBuffer(GL_ARRAY_BUFFER, bufferName[Buffer.VERTEX.ordinal()]);
    FloatBuffer vertexBuffer = GLBuffers.newDirectFloatBuffer(vertexData);
    gl4.glBufferData(GL_ARRAY_BUFFER, vertexSize, vertexBuffer, GL_STATIC_DRAW);
    gl4.glBindBuffer(GL_ARRAY_BUFFER, 0);

    int[] uniformBufferOffset = {0};
    gl4.glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, uniformBufferOffset, 0);
    int uniformBlockSize = Math.max(projection.length * Float.BYTES, uniformBufferOffset[0]);

    gl4.glBindBuffer(GL_UNIFORM_BUFFER, bufferName[Buffer.TRANSFORM.ordinal()]);
    gl4.glBufferData(GL_UNIFORM_BUFFER, uniformBlockSize, null, GL_DYNAMIC_DRAW);
    gl4.glBindBuffer(GL_UNIFORM_BUFFER, 0);

    gl4.glBindBuffer(GL_TEXTURE_BUFFER, bufferName[Buffer.PICKING.ordinal()]);
    gl4.glBufferData(GL_TEXTURE_BUFFER, Float.BYTES, null, GL_DYNAMIC_READ);
    gl4.glBindBuffer(GL_TEXTURE_BUFFER, 0);

    return true;
}

initTexture我们初始化我们的纹理,我们:

  • 使用glGenTextures 生成两个纹理
  • GL_UNPACK_ALIGNMENT设置为1(默认通常为4字节),以避免任何问题,(因为您的水平纹理大小必须与对齐方式匹配)。
  • 将 activeTexture 设置为GL_TEXTURE0,有特定数量的纹理槽,您需要在处理任何纹理之前指定它。
  • 绑定漫反射纹理
  • 设置 swizzle,即每个频道将收到的内容
  • 设置级别(mipmap),其中 0 是基础(原始/最大)
  • 设置过滤器
  • 分配空间,包含glTexStorage2D的级别
  • 为每个级别传输相应的数据
  • 重置GL_UNPACK_ALIGNMENT
  • 绑定到GL_TEXTURE0我们的其他纹理PICKING
  • 分配单个 32b 浮点存储并将PICKING 纹理与PICKING 缓冲区与glTexBuffer 相关联

代码:

private boolean initTexture(GL4 gl4) {

    try {
        jgli.Texture2D texture = new Texture2D(jgli.Load.load(TEXTURE_ROOT + "/" + TEXTURE_DIFFUSE));
        jgli.Gl.Format format = jgli.Gl.instance.translate(texture.format());

        gl4.glGenTextures(Texture.MAX.ordinal(), textureName, 0);

        // Diffuse
        {
            gl4.glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

            gl4.glActiveTexture(GL_TEXTURE0);
            gl4.glBindTexture(GL_TEXTURE_2D, textureName[Texture.DIFFUSE.ordinal()]);
            gl4.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);
            gl4.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_G, GL_GREEN);
            gl4.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_BLUE);
            gl4.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ALPHA);
            gl4.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
            gl4.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, texture.levels() - 1);
            gl4.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
            gl4.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

            gl4.glTexStorage2D(GL_TEXTURE_2D, texture.levels(), format.internal.value,
                    texture.dimensions(0)[0], texture.dimensions(0)[1]);

            for (int level = 0; level < texture.levels(); ++level) {
                gl4.glTexSubImage2D(GL_TEXTURE_2D, level,
                        0, 0,
                        texture.dimensions(level)[0], texture.dimensions(level)[1],
                        format.external.value, format.type.value,
                        texture.data(0, 0, level));
            }

            gl4.glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        }

        // Piking
        {
            gl4.glBindTexture(GL_TEXTURE_BUFFER, textureName[Texture.PICKING.ordinal()]);
            gl4.glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, bufferName[Buffer.PICKING.ordinal()]);
            gl4.glBindTexture(GL_TEXTURE_BUFFER, 0);
        }

    } catch (IOException ex) {
        Logger.getLogger(Gl_420_picking.class.getName()).log(Level.SEVERE, null, ex);
    }
    return true;
}

initProgram我们初始化我们的程序,通过:

  • 生成管道(不同着色器的组合),glGenProgramPipelines
  • 创建顶点着色器代码vertShaderCode,其中GL_VERTEX_SHADER为着色器类型,SHADERS_ROOT为着色器源所在位置,SHADERS_SOURCE_UPDATE为名称,"vert"为扩展名。李>
  • 初始化它,类似于片段着色器
  • 抓取生成的索引并保存在programName
  • 设置程序可分离,这​​里没什么用,纯运动,glProgramParameteri
  • 将两个着色器添加到我们的shaderProgram 并链接和编译它,link
  • 指定我们的pipelineName 具有哪个程序阶段,glUseProgramStages

代码:

private boolean initProgram(GL4 gl4) {

    boolean validated = true;

    gl4.glGenProgramPipelines(1, pipelineName, 0);

    // Create program
    if (validated) {

        ShaderProgram shaderProgram = new ShaderProgram();

        ShaderCode vertShaderCode = ShaderCode.create(gl4, GL_VERTEX_SHADER,
                this.getClass(), SHADERS_ROOT, null, SHADERS_SOURCE_UPDATE, "vert", null, true);
        ShaderCode fragShaderCode = ShaderCode.create(gl4, GL_FRAGMENT_SHADER,
                this.getClass(), SHADERS_ROOT, null, SHADERS_SOURCE_UPDATE, "frag", null, true);

        shaderProgram.init(gl4);
        programName = shaderProgram.program();

        gl4.glProgramParameteri(programName, GL_PROGRAM_SEPARABLE, GL_TRUE);

        shaderProgram.add(vertShaderCode);
        shaderProgram.add(fragShaderCode);
        shaderProgram.link(gl4, System.out);
    }

    if (validated) {

        gl4.glUseProgramStages(pipelineName[0], GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, programName);
    }

    return validated & checkError(gl4, "initProgram");
}

initVertexArray我们:

  • 生成单个顶点数组glGenVertexArrays,并绑定它glBindVertexArray
  • 绑定顶点缓冲区并设置位置和颜色的属性,这里交错。该位置由属性索引Semantic.Attr.POSITION(这将与顶点着色器中的那个匹配)、组件大小2、类型GL_FLOAT、规范化false、步幅或每个顶点属性的总大小@987654366 标识@ 和此属性中的偏移量0。颜色也是如此。
  • 取消绑定顶点缓冲区,因为它不是顶点数组状态的一部分。它必须只绑定到glVertexAttribPointer,以便 OpenGL 可以知道这些参数指的是哪个缓冲区。
  • 启用对应的顶点属性数组,glEnableVertexAttribArray
  • 绑定元素(索引)数组,顶点数组的一部分

代码:

private boolean initVertexArray(GL4 gl4) {

    gl4.glGenVertexArrays(1, vertexArrayName, 0);
    gl4.glBindVertexArray(vertexArrayName[0]);
    {
        gl4.glBindBuffer(GL_ARRAY_BUFFER, bufferName[Buffer.VERTEX.ordinal()]);
        gl4.glVertexAttribPointer(Semantic.Attr.POSITION, 2, GL_FLOAT, false, 2 * 2 * Float.BYTES, 0);
        gl4.glVertexAttribPointer(Semantic.Attr.TEXCOORD, 2, GL_FLOAT, false, 2 * 2 * Float.BYTES, 2 * Float.BYTES);
        gl4.glBindBuffer(GL_ARRAY_BUFFER, 0);

        gl4.glEnableVertexAttribArray(Semantic.Attr.POSITION);
        gl4.glEnableVertexAttribArray(Semantic.Attr.TEXCOORD);

        gl4.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferName[Buffer.ELEMENT.ordinal()]);
    }
    gl4.glBindVertexArray(0);

    return true;
}

render 我们:

  • 绑定将包含我们的转换矩阵的TRANSFORM 缓冲区。
  • 从中获取一个 byteBuffer pointer
  • 计算投影、视图和模型矩阵,并将它们以相同的顺序 p * v * m 相乘,也称为 mvp 矩阵。
  • 将我们的 mvp 矩阵保存在我们的 pointer 中并倒回缓冲区(位置再次设置为 0)。
  • 取消映射以确保将其上传到 gpu
  • 设置视口以匹配我们的窗口大小
  • 将清除深度值设置为1(多余,因为它是默认值),清除深度,使用depthValue,和颜色缓冲区,使用颜色{1.0f, 0.5f, 0.0f, 1.0f}
  • 绑定管道
  • 设置活动纹理 0
  • 绑定漫反射纹理和拾取图像纹理
  • 绑定顶点数组
  • 绑定变换统一缓冲区
  • 渲染,glDrawElementsInstancedBaseVertexBaseInstance 被过度使用了,但重要的是原始类型GL_TRIANGLES,索引数量elementCount 及其类型GL_UNSIGNED_SHORT
  • 绑定拾取纹理缓冲区并检索其值

代码:

@Override
protected boolean render(GL gl) {

    GL4 gl4 = (GL4) gl;

    {
        gl4.glBindBuffer(GL_UNIFORM_BUFFER, bufferName[Buffer.TRANSFORM.ordinal()]);
        ByteBuffer pointer = gl4.glMapBufferRange(
                GL_UNIFORM_BUFFER, 0, projection.length * Float.BYTES,
                GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);

        FloatUtil.makePerspective(projection, 0, true, (float) Math.PI * 0.25f,
                (float) windowSize.x / windowSize.y, 0.1f, 100.0f);
        FloatUtil.makeIdentity(model);

        FloatUtil.multMatrix(projection, view());
        FloatUtil.multMatrix(projection, model);

        for (float f : projection) {
            pointer.putFloat(f);
        }
        pointer.rewind();

        // Make sure the uniform buffer is uploaded
        gl4.glUnmapBuffer(GL_UNIFORM_BUFFER);
    }

    gl4.glViewportIndexedf(0, 0, 0, windowSize.x, windowSize.y);

    float[] depthValue = {1.0f};
    gl4.glClearBufferfv(GL_DEPTH, 0, depthValue, 0);
    gl4.glClearBufferfv(GL_COLOR, 0, new float[]{1.0f, 0.5f, 0.0f, 1.0f}, 0);

    gl4.glBindProgramPipeline(pipelineName[0]);
    gl4.glActiveTexture(GL_TEXTURE0);
    gl4.glBindTexture(GL_TEXTURE_2D, textureName[Texture.DIFFUSE.ordinal()]);
    gl4.glBindImageTexture(Semantic.Image.PICKING, textureName[Texture.PICKING.ordinal()],
            0, false, 0, GL_WRITE_ONLY, GL_R32F);
    gl4.glBindVertexArray(vertexArrayName[0]);
    gl4.glBindBufferBase(GL_UNIFORM_BUFFER, Semantic.Uniform.TRANSFORM0, bufferName[Buffer.TRANSFORM.ordinal()]);

    gl4.glDrawElementsInstancedBaseVertexBaseInstance(GL_TRIANGLES, elementCount, GL_UNSIGNED_SHORT, 0, 5, 0, 0);

    gl4.glBindBuffer(GL_ARRAY_BUFFER, bufferName[Buffer.PICKING.ordinal()]);
    ByteBuffer pointer = gl4.glMapBufferRange(GL_ARRAY_BUFFER, 0, Float.BYTES, GL_MAP_READ_BIT);
    float depth = pointer.getFloat();
    gl4.glUnmapBuffer(GL_ARRAY_BUFFER);

    System.out.printf("Depth: %2.3f\n", depth);

    return true;
}

在我们的顶点着色器中,为每个顶点执行,我们:

  • 定义 glsl 版本和配置文件
  • 定义所有属性索引,必须与我们来自之前使用的Semantic 一致
  • 设置一些内存布局参数,如std140和column_mayor(没用,矩阵的默认值)
  • 声明Transform统一缓冲区
  • 声明 vec3 位置和 vec2 texCoord 输入
  • 声明一个(内置的、不完整且无用的)gl_PerVertex 输出
  • 声明Block 块输出
  • 将传入的texCoordgl_Position 我们的顶点保存在剪辑空间位置中。传入的position 顶点在模型空间中 -> * 模型矩阵 = 世界空间中的顶点,* 视图/相机矩阵 = 相机/视图空间中的顶点,* 投影矩阵 = 剪辑空间中的顶点。

代码:

#version 420 core

#define POSITION    0
#define COLOR       3
#define TEXCOORD    4

#define TRANSFORM0  1

precision highp float;
precision highp int;
layout(std140, column_major) uniform;

layout(binding = TRANSFORM0) uniform Transform
{
    mat4 mvp;
} transform;

layout(location = POSITION) in vec3 position;
layout(location = TEXCOORD) in vec2 texCoord;

out gl_PerVertex
{
    vec4 gl_Position;
};

out Block
{
    vec2 texCoord;
} outBlock;

void main()
{   
    outBlock.texCoord = texCoord;
    gl_Position = transform.mvp * vec4(position, 1.0);
}

在顶点着色器之后可能还有其他阶段,例如曲面细分控制/评估和几何,但它们不是强制性的。 最后一个阶段是片段着色器,每个片段/像素执行一次,开始类似,然后我们:

  • binding 0 上声明纹理diffuse,与render 中的glActiveTexture(GL_TEXTURE0) 和imageBuffer picking 匹配,我们将在其中保存由binding 1 标识的深度,与Semantic.Image.PICKING 匹配在我们的render.glBindImageTexture
  • 声明拾取坐标,这里是硬编码的,但没有什么能阻止你将它们变成统一变量并在运行时设置它
  • 声明传入的 Block 块保存纹理坐标
  • 声明默认输出color
  • 如果当前片段坐标gl_FragCoord(内置函数)对应于拾取坐标pickingCoord,则将当前z值gl_FragCoord.z保存在imageBuffer中depth并将输出color设置为vec4(1, 0, 1, 1) ,否则我们将其设置为等于texture(diffuse, inBlock.texCoord.st) 的漫反射纹理。 st 是 stqp 选择的一部分,是 xywz 或 rgba 的同义词。

代码:

#version 420 core

#define FRAG_COLOR  0

precision highp float;
precision highp int;
layout(std140, column_major) uniform;

in vec4 gl_FragCoord;

layout(binding = 0) uniform sampler2D diffuse;
layout(binding = 1, r32f) writeonly uniform imageBuffer depth;

uvec2 pickingCoord = uvec2(320, 240);

in Block
{
    vec2 texCoord;
} inBlock;

layout(location = FRAG_COLOR, index = 0) out vec4 color;

void main()
{
    if(all(equal(pickingCoord, uvec2(gl_FragCoord.xy))))
    {
        imageStore(depth, 0, vec4(gl_FragCoord.z, 0, 0, 0));
        color = vec4(1, 0, 1, 1);
    }
    else
        color = texture(diffuse, inBlock.texCoord.st);
}

最后在end我们清理了我们所有的OpenGL资源:

@Override
protected boolean end(GL gl) {

    GL4 gl4 = (GL4) gl;

    gl4.glDeleteProgramPipelines(1, pipelineName, 0);
    gl4.glDeleteProgram(programName);
    gl4.glDeleteBuffers(Buffer.MAX.ordinal(), bufferName, 0);
    gl4.glDeleteTextures(Texture.MAX.ordinal(), textureName, 0);
    gl4.glDeleteVertexArrays(1, vertexArrayName, 0);

    return true;
}

【讨论】:

    猜你喜欢
    • 2011-04-20
    • 1970-01-01
    • 2011-03-02
    • 1970-01-01
    • 1970-01-01
    • 2011-01-27
    • 1970-01-01
    • 2017-07-05
    • 2011-01-15
    相关资源
    最近更新 更多