2016-01-08 24 views
0

當我點擊它時,如何獲得3D空間中對象的z座標。 (它不是一個對象更多的圖形,我需要知道用戶選擇了什麼)我使用JOGL。有沒有一種簡單的方法來獲取OpenGL中對象的深度(JOGL)

+3

的OpenGL不知道 「對象」 任何東西。它只是呈現三角形。 –

+0

你可以用'glReadPixels'做很多工作,但這不是魔術。 –

+0

嗨,你可以使用後臺緩衝區中的獨特顏色渲染每個不同的元素,甚至無需交換緩衝區,然後通過函數Dietrich建議 – elect

回答

0

我剛剛完成從g-truck ogl-samples移植picking sample

我會盡力給你一個關於代碼的快速解釋。

首先,我們啓用深度測試

private boolean initTest(GL4 gl4) { 

    gl4.glEnable(GL_DEPTH_TEST); 

    return true; 
} 

在我們:

  • 產生我們需要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,存在特定數量的紋理槽,並且在處理任何紋理之前需要指定它。
  • 綁定的漫反射紋理
  • 設置拌和,也就是每個信道將接收
  • 設置的級別(mip映射),其中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

  • 生成一個pi peline(不同着色的組合物),glGenProgramPipelines
  • 創建頂點着色器代碼vertShaderCode,其中GL_VERTEX_SHADER是着色器類型,SHADERS_ROOT是其中着色器源位於的地方,SHADERS_SOURCE_UPDATE是名稱和"vert"是擴展名。
  • 初始化它,同樣對片段着色器
  • 抓住生成的索引和programName
  • 保存設置程序可分的,沒有什麼用處這裏,只是純粹的運動,glProgramParameteri
  • 同時添加着色我們shaderProgram和鏈接和編譯它,link
  • specifing哪個程序階段我們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,步幅或每個頂點屬性2 * 2 * Float.BYTES的總大小以及此屬性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緩衝區,它將包含我們的轉換格式離子矩陣。
  • 得到一個字節緩衝區pointer
  • 計算矩陣的投影,視圖和模型並將它們以相同的順序乘以p * v * m,也稱爲mvp矩陣。
  • 將我們的mvp矩陣保存在我們的pointer中並倒回緩衝區(再次將位置設置爲0)。
  • 取消映射它,以確保它被上傳至GPU
  • 設置視口,以配合我們的窗口大小
  • 設定明確depthValue爲1(superflous,因爲它是默認值),淨深,與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位置和VEC 2 texCoord輸入
  • 聲明(內置的,不完整的和無用的)gl_PerVertex輸出
  • 聲明一個Block塊輸出
  • 節省我們的塊傳入texCoord內部和內部gl_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); 
} 

可能有是頂點着色器之後的其他階段,比如鑲嵌控制/評價和幾何形狀,但它們不是必需的。 最後階段是片段着色器,每個片段/像素執行一次,啓動類似的,那麼我們:

  • 聲明紋理diffusebinding 0,與我們glActiveTexture(GL_TEXTURE0)render和ImageBuffer的picking內部匹配,我們將拯救我們的深度由binding 1鑑定,符合我們的Semantic.Image.PICKING裏面我們render.glBindImageTexture
  • 聲明採摘座標,這裏硬編碼,但沒有阻止你把它們作爲進行統一變量,並將其設置在運行時
  • 聲明傳入Block塊保持的紋理座標
  • 聲明默認輸出color
  • 如果當前片段的座標gl_FragCoord(內置函數)對應於拾取座標pickingCoord,保存ImageBuffer的depth內的當前z值gl_FragCoord.z並將輸出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; 
} 
相關問題