2

How can I get the value of pixel color using glReadPixels()? I did so many try but getting wrong value.

My background color is blue(0,1,1) and I have drawn a circle with boundary color red(1,0,0) and I want to get the color of any of boundary point. So it must give me red. but I am getting background color.

Here is my code in Python3 and OpenGL

from OpenGL.GLU import *
from OpenGL.GLUT import *
import time
from  math import *
import numpy
import sys

def init():

    glClearColor(0.0,1.0,1.0,0.0)
    glClear(GL_COLOR_BUFFER_BIT)
    glPointSize(3.0)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(0.0,640.0,0.0,640.0)

def circle():

    for i in range(361):
        m=float(50*cos(i*pi/180.0))+320
        n=float(50*sin(i*pi/180.0))+320
        setpixc(m,n)

    print(m,n)
    redinput()

def redinput():

    global x,y
    x=int(input("enter x:"))
    y=int(input("enter y:"))
    setpixc(x,y)
    pixel=[]
    c=glReadPixels(x,y,1.0,1.0,GL_RGB,GL_UNSIGNED_BYTE,None)
    print(c)
    string_pixels=numpy_pixel.tolist()
    print(string_pixels)

def setpixc(xcor,ycor):

    glBegin(GL_POINTS)
    glColor3f(1.0,0.0,0.0)
    glVertex2f(xcor,ycor)
    glEnd()
    glFlush()

def Display():

    circle()
    print("hello")

def main():

    glutInit(sys.argv)

    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB)
    glutInitWindowSize(600,600)
    glutInitWindowPosition(10,10)
    glutCreateWindow("line-dda")

    glutDisplayFunc(Display)

    init()
    glutMainLoop()

main()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
MONA MINOR
  • 29
  • 3
  • Did the answer solve your question or are there remaining issues? Note, if an answer to your question has solved your issue completely, then you should accept the answer. (check mark to the left of the answer). I mention that, because you're a new user. If my answer didn't solve your question, the please tell me what is missing. – Rabbid76 Jan 20 '19 at 23:01

2 Answers2

3

You are using an orthographic projection, which which projects the coordinates to the rectangle form (0, 0) to (640, 640):

gluOrtho2D(0.0,640.0,0.0,640.0)

But your window size is (600, 600):

glutInitWindowSize(600,600)

This causes that the coordinates in the range from (0, 0) to (640, 640) are drawn to the viewport from (0, 0) to (600, 600), by glVertex2f:

But when the coordinates are read by glReadPixels, then you would have to use viewport (pixel) coordinates.

To solve your is you can change the window size from (600, 600) to (640, 640):

glutInitWindowSize(640, 640)

Now e.g.

x=270
y=320

will return a red pixel.


Note, if you don't want to change the window size, then you would have to scale the input coordinates by 600/640.

scale = 600/640
c=glReadPixels(x*scale,y*scale,1.0,1.0,GL_RGB,GL_UNSIGNED_BYTE,None)

e.g.

x = 270 * 600 / 640 = 253
y = 320 * 600 / 640 = 300 

Further note, that drawing by glBegin/glEnd sequences is deprecated since several years. Read about Fixed Function Pipeline and see Vertex Specification and Shader for a state of the art way of rendering.

Anyway, I recommend to use double buffering

glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)

and to do a single buffer swap, after the entire circle was drawn. Skip the glFlush call in setpixc and add a single glutSwapBuffers call to the Display function and don't forget to clear the display before rendering:

def Display():

    glClear(GL_COLOR_BUFFER_BIT)
    circle()

    glutSwapBuffers()
    glutPostRedisplay()

    redinput()
    print("hello")

It is up to you if you want to draw the circle by single points

def circle():
    glPointSize(3.0)
    glColor3f(1.0,0.0,0.0)
    glBegin(GL_POINTS)
    for i in range(360):
        m=float(50*cos(i*pi/180.0))+320
        n=float(50*sin(i*pi/180.0))+320
        glVertex2f(m,n)
    glEnd()

or a coherent line:

def circle():
    glLineWidth(3.0)
    glColor3f(1.0,0.0,0.0)
    glBegin(GL_LINE_LOOP)
    for i in range(360):
        m=float(50*cos(i*pi/180.0))+320
        n=float(50*sin(i*pi/180.0))+320
        glVertex2f(m,n)
    glEnd()

If you want to get the color of a pixel by a mouse click, the you can set a mouse call back by glutMouseFunc:

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from math import *

def init():
    global width, height

    glClearColor(0.0, 1.0, 1.0, 0.0)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluOrtho2D(0.0, width, 0.0, height)

def circle():
    glLineWidth(3.0)
    glColor3f(1.0, 0.0, 0.0)
    glBegin(GL_LINE_LOOP)
    for i in range(360):
        m=float(50*cos(i*pi/180.0))+320
        n=float(50*sin(i*pi/180.0))+320
        glVertex2f(m, n)
    glEnd()

def Mouse(button, state, x, y):
    global mouse_x, mouse_y, get_input

    if button == GLUT_LEFT_BUTTON and state == GLUT_DOWN:
        mouse_x = x
        mouse_y = height - y # the y coordinate of the mouse has to be flipped
        get_input = True

def redinput(x, y):
    c = glReadPixels(x, y, 1.0, 1.0, GL_RGB,GL_UNSIGNED_BYTE, None)
    print(c)

def Display():
    global mouse_x, mouse_y, get_input

    glClear(GL_COLOR_BUFFER_BIT)
    circle()

    glutSwapBuffers()
    glutPostRedisplay()

    if get_input: 
        redinput(mouse_x, mouse_y)
        get_input=False

def main():
    global width, height

    glutInit(sys.argv)

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
    glutInitWindowSize(width, height)
    glutInitWindowPosition(10, 10)
    glutCreateWindow("line-dda")

    glutDisplayFunc(Display)
    glutMouseFunc(Mouse)

    init()
    glutMainLoop()

width   = 640
height  = 640
mouse_x = 0
mouse_y = 0
get_input = False
main()
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
1

There are couple of problems with your code.

  • your window size is different from data you are using in gluOrtho2D
  • you are relying on pixel exact rasterization, and OpenGL does not guarantee it.

The answer you are searching for is described in "Red Book" (i.e. OpenGL Programming Guide), specifically in Appendix G, Programming Tips. I'd also suggest you to read Appendix H, Invariance. Online version can be found on following link: https://www.glprogramming.com/red/

Also,

  • you do not need to call glFlush after every single point drawn, call it once just before glReadPixels...
  • you are using glBegin/glEnd pair for every single point, which is huge waste of resources. you can draw complete circle using one glBegin/glEnd pair:

    glBegin(GL_POINTS)
    glColor3f(1.0, 0.0, 0.0)
    for i in range(361):
        x=float(50*cos(i*pi/180.0))+320
        y=float(50*sin(i*pi/180.0))+320
        glVertex2f(x,y)
    glEnd()
    
  • you are using very dense set of GL_POINTS to draw circle, but this will not prodice correct circle. If radius is smaller, you will have multiple rasterization of same window pixel. If you increase radius enough, it will result in set of unconnected points. In your situation, I would use GL_LINE_LOOP:

    glBegin(GL_LINE_LOOP)
    glColor3f(1.0, 0.0, 0.0)
    for i in range(0, 360, 5):
        x=float(50*cos(i*pi/180.0))+320
        y=float(50*sin(i*pi/180.0))+320
        glVertex2f(x,y)
    glEnd()
    
  • last but not least, this is ancient way of OpenGL usage. Unless you have very good reason, I'd suggest to move to some newer OpenGL version.

halfer
  • 19,824
  • 17
  • 99
  • 186
gordan.sikic
  • 1,174
  • 8
  • 8