how can I get the z-Coordinate of an Object in 3D-space when I click on it. (Its not really an Object more an graph, I need to know what an user selected) I use JOGL.
Asked
Active
Viewed 598 times
0
-
3OpenGL doesn't know anything about "objects". It just renders triangles. – Nicol Bolas Jan 08 '16 at 21:27
-
You can do a lot of work with `glReadPixels`, but it's not magic. – Dietrich Epp Jan 08 '16 at 21:48
-
Hi, you can render each different element with a unique color in the back buffer, without even swapping buffers, and then retrieving it via the function Dietrich suggested – elect Jan 09 '16 at 10:36
-
I advise you to look at this accepted answer: http://stackoverflow.com/a/23911297/458157 – gouessej Jan 10 '16 at 17:53
-
Which profile are you using? I am coming in the next days with a new picking sample – elect Jan 10 '16 at 21:54
1 Answers
0
I just finished to port a picking sample from g-truck ogl-samples.
I will try to give you a quick explanation about the code.
We start by enabling the depth test
private boolean initTest(GL4 gl4) {
gl4.glEnable(GL_DEPTH_TEST);
return true;
}
In the initBuffer
we:
- generate all the buffer we need with
glGenBuffers
- bind the element buffer and we transfer the content of our indices. Each index refers to the vertex to use. We need to bind it first because
glBufferData
will be using whatever is bounded at the target specify by the first argument,GL_ELEMENT_ARRAY_BUFFER
in this case - do the same for the vertices themselves.
- get the
GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT
(it's a global parameter) to determine the minimum uniform block size to store our transform variable. This is necessary if we want to bind it viaglBindBufferRange
, function that we will not use, instead, for binding our picking buffer, this is why we pass just the size of a float,Float.BYTES
- the last argument of
glBufferData
is just an hint (it's up to OpenGL and the driver do what they want), as you see is static for the indices and vertices, because we are not gonna change them anymore, but is dynamic for the uniform buffers, since we will update them every frame.
Code:
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;
}
In the initTexture
we initialize our textures, we:
- generate both the textures with
glGenTextures
- set the
GL_UNPACK_ALIGNMENT
to 1 (default is usually 4 bytes), in order to avoid any problem at all, (because your horizontal texture size must match the alignment). - set the activeTexture to
GL_TEXTURE0
, there is a specific number of texture slots and you need to specify it before working on any texture. - bind the diffuse texture
- set the swizzle, that is what each channel will receive
- set the levels (mipmap), where 0 is the base (original/biggest)
- set the filters
- allocate the space, levels included with
glTexStorage2D
- transfer for each level the corresponding data
- reset back the
GL_UNPACK_ALIGNMENT
- bind to
GL_TEXTURE0
our other texturePICKING
- allocate a single 32b float storage and associate the
PICKING
texture to thePICKING
buffer withglTexBuffer
Code:
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;
}
In the initProgram
we initialize our program, by:
- generating a pipeline (composition of different shaders),
glGenProgramPipelines
- creating a vertex shader code
vertShaderCode
, whereGL_VERTEX_SHADER
is the shader type,SHADERS_ROOT
is the place where the shader source is located,SHADERS_SOURCE_UPDATE
is the name and"vert"
is the extension. - initializing it, similarly for the fragment shader
- grabbing the generated index and saving in
programName
- setting the program separable, nothing useful here, just pure sport,
glProgramParameteri
- adding both shader to our
shaderProgram
and linking and compiling it,link
- specifing which program stage our
pipelineName
has, glUseProgramStages
Code:
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");
}
In the initVertexArray
we:
- generate a single vertex array,
glGenVertexArrays
, and bind it,glBindVertexArray
- bind the vertices buffer and set the attribute for the position and the color, here interleaved. The position is identified by the attribute index
Semantic.Attr.POSITION
(this will match the one in the vertex shader), component size2
, typeGL_FLOAT
, normalizedfalse
, stride or the total size of each vertex attribute2 * 2 * Float.BYTES
and the offset in this attribute0
. Similarly for the color. - unbind the vertices buffer since it is not part of the vertex array state. It must be bound only for the
glVertexAttribPointer
so that OpenGL can know which buffer those parameters refers to. - enable the corresponding vertex attribute array,
glEnableVertexAttribArray
- bind the element (indices) array, part of the vertex array
Code:
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;
}
In the render
we:
- bind the
TRANSFORM
buffer that will contain our transformation matrix. - get a byteBuffer
pointer
out of that. - calculate the projection, view and model matrices and multiplying them in the same order p * v * m, called also mvp matrix.
- save our mvp matrix in our
pointer
and rewind the buffer (position set to 0 again). - unmap it to make sure it gets uploaded to the gpu
- set the viewport to match our window size
- set the clear depthValue to 1 (superflous, since it is the default value), clear depth, with the
depthValue
, and color buffer, with the color{1.0f, 0.5f, 0.0f, 1.0f}
- bind the pipeline
- set active texture 0
- bind the diffuse texture and the picking image texture
- bind the vertex array
- bind the transform uniform buffer
- render,
glDrawElementsInstancedBaseVertexBaseInstance
is overused it, but what is important is the primitive typeGL_TRIANGLES
, the number of indiceselementCount
and their typeGL_UNSIGNED_SHORT
- bind the picking texture buffer and retrieve its value
Code:
@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;
}
In our vertex shader, executed for each vertex, we:
- define the glsl version and profile
- define all the attribute indices, that must coincide with our coming from the
Semantic
we used previously - set some memory layout parameters, such as std140 and column_mayor (useless, default value for matrices)
- declare the
Transform
uniform buffer - declare a vec3 position and vec2 texCoord inputs
- declare a (built in, incomplete and useless) gl_PerVertex output
- declare a
Block
block output - save inside our block the incoming
texCoord
and insidegl_Position
our vertex in clip space position. The incomingposition
vertex is in Model space -> * model matrix = vertex in World space, * view/camera matrix = vertex in Camera/View space, * projection matrix = vertex in Clip space.
Code:
#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);
}
There may be are other stages after the vertex shader, such as tessellation control/evaluation and geometry, but they are not mandatory. The last stage is the fragment shader, executed once per fragment/pixel, that starts similarly, then we:
- declare the texture
diffuse
onbinding 0
, that matches with ourglActiveTexture(GL_TEXTURE0)
inside therender
and the imageBufferpicking
where we will save our depth identified bybinding 1
, that matches ourSemantic.Image.PICKING
inside ourrender.glBindImageTexture
- declare the picking coordinates, here hardcoded, but nothing stops you from turning them out as uniform variable and set it on runtime
- declare the incoming
Block
block holding the texture coordinates - declare the default output
color
- if the current fragment coordinates
gl_FragCoord
(built in function) corresponds to the picking coordinatespickingCoord
, save the current z valuegl_FragCoord.z
inside the imageBufferdepth
and set the outputcolor
tovec4(1, 0, 1, 1)
, otherwise we set it equal to the diffuse texture bytexture(diffuse, inBlock.texCoord.st)
.st
is part of the stqp selection, synonymous of xywz or rgba.
Code:
#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);
}
Finally in the end
we clean up all our OpenGL resources:
@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;
}

elect
- 6,765
- 10
- 53
- 119