1
votes

I'm trying to create a program with modern OpenGL code that displays a QUAD with a texture mapped onto it.

The texture should be stretched to match the quad size. Right now the texture repeats.

I've seen code that maps the texture to the quad if the each vertex and mapping is done individually in the code. However, I think now we are supposed to use vertex shaders? With OpenGL 4.5 or whatever, how is this supposed to be done?

"""
Open a window that displays a quad with an image on it.

"""

from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from PIL import Image
from math import *
from time import *


class MyApplication:
    """ Main application class. """

    def __init__(self):
        self.vao = 0

    def load_texture(self, file_name):
        image  = Image.open(file_name)
        width  = image.size[0]
        height = image.size[1]
        image_bytes  = image.convert("RGBA").tobytes ( "raw", "RGBA", 0, -1)
        texture = glGenTextures(1)

        glBindTexture     ( GL_TEXTURE_2D, texture )
        glTexParameterf   ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT )
        glTexParameterf   ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT )
        glTexParameteri   ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR )
        glTexParameteri   ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR )

        gluBuild2DMipmaps ( GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, image_bytes )

        return texture

    def compile_shaders(self):
        """ Get the shaders ready. """

        # Create a triangle with three (x, y, z, ?) points.
        vertex_shader_source = """
        #version 450 core
        void main( void)
        {
            // Declare a hard-coded array of positions
            const vec4 vertices[4] = vec4[4](vec4(-0.5,  0.5, 0.5, 1.0),
                                             vec4( 0.5,  0.5, 0.5, 1.0),
                                             vec4( 0.5, -0.5, 0.5, 1.0),
                                             vec4(-0.5, -0.5, 0.5, 1.0));

            // Index into our array using gl_VertexID
            gl_Position = vertices[gl_VertexID];
            }
        """

        vertex_shader = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(vertex_shader, vertex_shader_source)
        glCompileShader(vertex_shader)

        # Specify the color of our fragment (RGBA)

        texture = self.load_texture("test_ship.png")

        fragment_shader_source = """
        #version 450 core
        uniform sampler2D s;
        out vec4 color;
        void main(void)
        {
            color = texture(s, gl_FragCoord.xy / textureSize(s, 0));
        }
        """

        fragment_shader = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(fragment_shader, fragment_shader_source)
        glCompileShader(fragment_shader)

        # --- Create a program
        program = glCreateProgram()
        glAttachShader(program, vertex_shader)
        glAttachShader(program, fragment_shader)
        glLinkProgram(program)

        glGenVertexArrays(1, self.vao)
        glBindVertexArray(self.vao)

        # --- Clean up now that we don't need these shaders anymore.
        glDeleteShader(vertex_shader)
        glDeleteShader(fragment_shader)

        return program

    def startup(self):
        self.rendering_program = self.compile_shaders()
        self.vertex_array_object = GLuint()
        glCreateVertexArrays(1, self.vertex_array_object)

    def render(self):
        # Support transparency
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

        # Get a color based on the time
        color = (sin(time()), cos(time()), 0)

        # Clear the screen
        glClearBufferfv(GL_COLOR, 0, color)

        # Tell the computer what to render
        glUseProgram(self.rendering_program)
        glDrawArrays(GL_QUADS, 0, 4)

        # Display
        glutSwapBuffers()

    def animate(self):
        glutPostRedisplay()

    def run(self):
        glutInit(sys.argv)
        glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
        glutInitWindowSize(500, 500)
        glutInitWindowPosition(0, 0)

        glutCreateWindow(b"Simple PyOpenGL example")

        self.startup()

        glutIdleFunc(self.animate)
        glutDisplayFunc(self.render)

        glutMainLoop()


my_application = MyApplication()
my_application.run()
1

1 Answers

0
votes

I believe there are two problems here:

1) If you see a repeating pattern, given your current WRAP_S and WRAP_T settings, you can assume that your UV coordinates are wrong.

In your fragment shader, you get the UV coordinate using the following calculation:

color = texture(s, gl_FragCoord.xy / textureSize(s, 0));

However, looking at your quad coordinates, this is not going to map to a [0,1] interval, as required for texture coordinates. (if it goes below or beyond this interval, you will get a modulo of the value).

2) If you don't want it to repeat, use the following:

 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)