Picking object with Texture

Hello,

Below is my codes which works perfectly fine that I can select object with Stencil buffer.

[Vertex Shader]

#version 450 core
layout (location = 0) in vec3 aPos;

out vec4 aColor;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform vec4 color;
uniform float size;

void main() {
    aColor = color;
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    gl_PointSize = size;
}

[Fragment Shader]

#version 450 core
in vec4 aColor;

out vec4 FragColor;

void main() {
    FragColor = aColor;
}

[Vertices and Indices]

float vertice[] = {
    -0.2, 0.2, -0.2,
    -0.2, 0.2, 0.2,
    0.2, 0.2, -0.2,
    0.2, 0.2, 0.2,
    0.2, -0.2, -0.2,
    0.2, -0.2, 0.2,
    -0.2, -0.2, -0.2,
    -0.2, -0.2, 0.2
};

unsigned int surfaceIncides[] = {
    0, 3, 2,
    3, 0, 1,
    4, 7, 6,
    7, 4, 5,
    1, 5, 3,
    5, 1, 7,
    2, 6, 0,
    6, 2, 4,
    3, 4, 2,
    4, 3, 5,
    0, 7, 1,
    7, 0, 6
};

unsigned int VBO, VAO;
GLuint surfaceEAB;

// VAO & VBO
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);

glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertice), vertice, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glEnableVertexAttribArray(0);

// EAB
glGenBuffers(1, &surfaceEAB);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surfaceEAB);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(surfaceIncides), surfaceIncides, GL_STATIC_DRAW);

[Drawing codes]

glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

// 1st cube
glStencilFunc(GL_ALWAYS, 1, -1);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surfaceEAB);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

// 2nd cube
glStencilFunc(GL_ALWAYS, 2, -1);

// Draw points
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, surfaceEAB);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);

[Select object with Stencil buffer]

GLint viewport[4];
GLubyte color[4] = { 0, };
GLfloat depth;
GLuint index;

glGetIntegerv(GL_VIEWPORT, viewport);
        
glReadPixels(x, viewport[3] - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color);
glReadPixels(x, viewport[3] - y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
glReadPixels(x, viewport[3] - y, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT, &index);

glm::vec4 views = glm::vec4(0, 0, WindowWidth, WindowHeight);
glm::vec3 wincoord = glm::vec3(x, WindowHeight - y - 1, depth);
glm::vec3 glCoord = glm::unProject(wincoord, view, prjMatrix, views);

printf("Clicked on pixel %.2f, %.2f, color %02hhx%02hhx%02hhx%02hhx, depth %f, stencil index %u\nCoordinates in object space: %f, %f, %f\n", x, y, color[0], color[1], color[2], color[3], depth, index, glCoord.x, glCoord.y, glCoord.z);

However when the drawing goes with Textures/Lightings, I can’t seemed to select object with Stencil buffer, below is what I’ve tried so far.

[Vertex Shader]

#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

out VS_OUT {
    vec3 FragPos;
    vec3 Normal;
    vec2 TexCoords;
} vs_out;

uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;

void main() {
    vs_out.FragPos = vec3(model * vec4(aPos, 1.0));   
    vs_out.TexCoords = aTexCoords;
        
    mat3 normalMatrix = transpose(inverse(mat3(model)));
    vs_out.Normal = normalize(normalMatrix * aNormal);
    
    gl_Position = projection * view * model * vec4(aPos, 1.0);
}

[Fragment Shader]

#version 450 core
layout (location = 0) out vec4 FragColor;
layout (location = 1) out vec4 BrightColor;

in VS_OUT {
    vec3 FragPos;
    vec3 Normal;
    vec2 TexCoords;
} fs_in;

struct Light {
    vec3 Position;
    vec3 Color;
};

uniform Light lights[4];
uniform sampler2D diffuseTexture;
uniform vec3 viewPos;

out vec4 FragColor;

void main() {
    vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb;
    vec3 normal = normalize(fs_in.Normal);
    
    // Ambient
    vec3 ambient = 0.0 * color;
    
    // Lighting
    vec3 lighting = vec3(0.0);
    vec3 viewDir = normalize(viewPos - fs_in.FragPos);
    
    for (int i = 0; i < 4; i++) {
        // Diffuse
        vec3 lightDir = normalize(lights[i].Position - fs_in.FragPos);
        float diff = max(dot(lightDir, normal), 0.0);
        vec3 result = lights[i].Color * diff * color;
        
        // Attenuation (use quadratic as we have gamma correction)
        float distance = length(fs_in.FragPos - lights[i].Position);
        result *= 1.0 / (distance * distance);
        lighting += result;          
    }

    vec3 result = ambient + lighting;

    // Check whether result is higher than some threshold, if so, output as bloom threshold color
    float brightness = dot(result, vec3(0.2126, 0.7152, 0.0722));
    
    if (brightness > 1.0)
        BrightColor = vec4(result, 1.0);
    
    else
        BrightColor = vec4(0.0, 0.0, 0.0, 1.0);
    
    FragColor = vec4(result, 1.0);
}

[Vertices]

    float vertices[] = {
    // back face            // noarmal         // texture 
    -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
    1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f, // top-right
    1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 0.0f, // bottom-right         
    1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 1.0f, 1.0f, // top-right
    -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 0.0f, // bottom-left
    -1.0f,  1.0f, -1.0f,  0.0f,  0.0f, -1.0f, 0.0f, 1.0f, // top-left
    
    // front face
    -1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f, // bottom-left
    1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 0.0f, // bottom-right
    1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f, // top-right
    1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f, 1.0f, // top-right
    -1.0f,  1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 1.0f, // top-left
    -1.0f, -1.0f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f, 0.0f, // bottom-left
    
    // left face
    -1.0f,  1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-right
    -1.0f,  1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 1.0f, // top-left
    -1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-left
    -1.0f, -1.0f, -1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-left
    -1.0f, -1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 0.0f, 0.0f, // bottom-right
    -1.0f,  1.0f,  1.0f, -1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-right
    
    // right face
    1.0f,  1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-left
    1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-right
    1.0f,  1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 1.0f, // top-right         
    1.0f, -1.0f, -1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 1.0f, // bottom-right
    1.0f,  1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 1.0f, 0.0f, // top-left
    1.0f, -1.0f,  1.0f,  1.0f,  0.0f,  0.0f, 0.0f, 0.0f, // bottom-left     
    
    // bottom face
    -1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f, // top-right
    1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 1.0f, // top-left
    1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f, // bottom-left
    1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 1.0f, 0.0f, // bottom-left
    -1.0f, -1.0f,  1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 0.0f, // bottom-right
    -1.0f, -1.0f, -1.0f,  0.0f, -1.0f,  0.0f, 0.0f, 1.0f, // top-right
    
    // top face
    -1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f, // top-left
    1.0f,  1.0f , 1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f, // bottom-right
    1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 1.0f, // top-right     
    1.0f,  1.0f,  1.0f,  0.0f,  1.0f,  0.0f, 1.0f, 0.0f, // bottom-right
    -1.0f,  1.0f, -1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 1.0f, // top-left
    -1.0f,  1.0f,  1.0f,  0.0f,  1.0f,  0.0f, 0.0f, 0.0f  // bottom-left        
};

unsigned int VBO, VAO;

glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);

glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(VAO);

glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);

glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));

glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));

glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);

[Drawing codes]

glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

// Bind Texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);

// 1st cube
glStencilFunc(GL_ALWAYS, 1, -1);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);

// 2nd cube
glStencilFunc(GL_ALWAYS, 2, -1);
glBindVertexArray(VAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);

[Select object with Stencil buffer]

GLint viewport[4];
GLubyte color[4] = { 0, };
GLfloat depth;
GLuint index;

glGetIntegerv(GL_VIEWPORT, viewport);
        
glReadPixels(x, viewport[3] - y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, color);
glReadPixels(x, viewport[3] - y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth);
glReadPixels(x, viewport[3] - y, 1, 1, GL_STENCIL_INDEX, GL_UNSIGNED_INT, &index);

glm::vec4 views = glm::vec4(0, 0, WindowWidth, WindowHeight);
glm::vec3 wincoord = glm::vec3(x, WindowHeight - y - 1, depth);
glm::vec3 glCoord = glm::unProject(wincoord, view, prjMatrix, views);

printf("Clicked on pixel %.2f, %.2f, color %02hhx%02hhx%02hhx%02hhx, depth %f, stencil index %u\nCoordinates in object space: %f, %f, %f\n", x, y, color[0], color[1], color[2], color[3], depth, index, glCoord.x, glCoord.y, glCoord.z);

What I get is always the last index of object whatever I select from, can anyone help me to fix my issue?

Thanks a lot!

I’ve fixed problem by drawing objects again without textures/lighting when user clicks on screen.
So I can find object under mouse cursor but it looks like Stencil buffer can only support range 1 to 255, it means that if I draw objects more than 255, those can’t be selected?

Is there any other approaches I can take? or to have workaround with current? :sob:

The stencil buffer is usually only 8bit per pixel, so yes that puts a fairly small limit on the number of objects you can distinguish.
One alternative is to render to an offscreen render target (using a FramebufferObject and attached textures). That render target need not store colors, but it could use e.g. a single channel UInt32 format to store a 32bit object index. You’ll need a different fragment shader that outputs that object index instead of color for this as well.

It’s also worth mentioning that you could just intersect the ray with your 2 cubes on the CPU. That’s likely to be considerably faster (lower latency) than farming this task out to the GPU.

Thanks, I will try FBO as well :slight_smile:

Thanks, will try ray intersection too :slight_smile:

i’m using the framebuffer approach too. i render everything into a FBO with an additional ivec4 (4-dimensional integer vector) attachment, and read the pixel under the cursor each frame … works quite well, you can store some information in each pixel, for example object ID / node ID / mesh ID / material ID. (its quite useful if you have scene graphs as models and complex meshes)

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.