4

I would like to use wood.jpg as the background for an OpenGL program. I would like this program to also show the model basketball.obj as shown in the picture: OpenGL Window

I've read several tutorials( Youtube channels sentdex, atibyte, The Cherno). I also tried the website learnopengl.com, opengl-tutorial.org, and codeloop.com. I've also used Udemy. None of these specifically show how to use a .jpg as a background for .obj models.

I can run code for multiple models that have been defined within the main code. I can also run code for multiple .obj files. But I have failed run code that can run both at the same time.

Can someone please help me? I can code in both OpenGL C++ and pyOpenGL for Python.

So far, the only success I have had in displaying the image wood.jpg as the background:

#Code modified from https://codeloop.org/python-modern-opengl-texturing-rectangle/

import glfw
from OpenGL.GL import *
import OpenGL.GL.shaders
import numpy as np
from PIL import Image


def main():
    if not glfw.init():
        return

    window = glfw.create_window(720, 600, "Pyopengl Texturing Rectangle", None, None)

    if not window:
        glfw.terminate()
        return

    glfw.make_context_current(window)

    # positions        colors               texture coords
    rectangle = [-1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
                 1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0,
                 1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0,
                 -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0]

    # convert to 32bit float

    rectangle = np.array(rectangle, dtype=np.float32)

    indicesRectange = [0, 1, 2,
               2, 3, 0]

    indicesRectange = np.array(indicesRectange, dtype=np.uint32)

    VERTEX_SHADER = """

           #version 330

           in vec3 position;
           in vec3 color;
           in vec2 InTexCoords;

           out vec3 newColor;
           out vec2 OutTexCoords;

           void main() {

            gl_Position = vec4(position, 1.0);
            newColor = color;
            OutTexCoords = InTexCoords;

             }


       """

    FRAGMENT_SHADER = """
        #version 330

         in vec3 newColor;
         in vec2 OutTexCoords;

         out vec4 outColor;
         uniform sampler2D samplerTex;

        void main() {

           outColor = texture(samplerTex, OutTexCoords);

        }

    """

    # Compile The Program and shaders

    shader = OpenGL.GL.shaders.compileProgram(OpenGL.GL.shaders.compileShader(VERTEX_SHADER, GL_VERTEX_SHADER),
                                              OpenGL.GL.shaders.compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER))

    # Create Buffer object in gpu
    VBO = glGenBuffers(1)
    # Bind the buffer
    glBindBuffer(GL_ARRAY_BUFFER, VBO)
    glBufferData(GL_ARRAY_BUFFER, 128, rectangle, GL_STATIC_DRAW)

    # Create EBO
    EBO = glGenBuffers(1)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesRectange, GL_STATIC_DRAW)

    # get the position from  shader
    #position = glGetAttribLocation(shader, 'position')
    position = 0
    glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(0))
    glEnableVertexAttribArray(position)

    # get the color from  shader
    #color = glGetAttribLocation(shader, 'color')
    color = 1
    glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(12))
    glEnableVertexAttribArray(color)

    #texCoords = glGetAttribLocation(shader, "InTexCoords")
    texCoords = 2
    glVertexAttribPointer(texCoords, 2, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(24))
    glEnableVertexAttribArray(texCoords)

    glBindAttribLocation(shader, position, 'position' )
    glBindAttribLocation(shader, color, 'color' )
    glBindAttribLocation(shader, texCoords, 'InTexCoords' )

    # Creating Texture
    texture = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, texture)
    # texture wrapping params
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
    # texture filtering params
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

    image = Image.open("wood.jpg")
    img_data = np.array(list(image.getdata()), np.uint8)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)

    glUseProgram(shader)

    glClearColor(1.0, 0.0, 0.0, 1.0)

    while not glfw.window_should_close(window):
        glfw.poll_events()

        glClear(GL_COLOR_BUFFER_BIT)

        # Draw Rectangle

        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)

        glfw.swap_buffers(window)

    glfw.terminate()


if __name__ == "__main__":
    main()

[UPDATE] This is the last code I attempted. It combines code from a texture tutorial, and code from a object loader tutorial:

# Object loading code by AtiBYte - OpenGL in python e15 - loading 3D .obj files. Youtube
# Background Texture loading code modified from CodeLoop.org https://codeloop.org/python-modern-opengl-texturing-rectangle/

import glfw
from OpenGL.GL import *
from OpenGL.GL.shaders import compileProgram, compileShader
import pyrr
from TextureLoader import load_texture
from ObjLoader import ObjLoader

# imports for background texture
import numpy as np
from PIL import Image

############## Background Texture ######################
# positions        colors               texture coords
rectangle = [-1.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
             1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0,
             1.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0,
             -1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 1.0]

# convert to 32bit float

rectangle = np.array(rectangle, dtype=np.float32)

indicesBackground = [0, 1, 2,
                     2, 3, 0]

indicesBackground = np.array(indicesBackground, dtype=np.uint32)

VERTEX_SHADER = """

       #version 330

       in vec3 position;
       in vec3 color;
       in vec2 InTexCoords;

       out vec3 newColor;
       out vec2 OutTexCoords;

       void main() {

        gl_Position = vec4(position, 1.0);
        newColor = color;
        OutTexCoords = InTexCoords;

         }


   """

FRAGMENT_SHADER = """
    #version 330

     in vec3 newColor;
     in vec2 OutTexCoords;

     out vec4 outColor;
     uniform sampler2D samplerTex;

    void main() {

       outColor = texture(samplerTex, OutTexCoords);

    }

"""
########################################################


vertex_src = """
# version 330

layout(location = 0) in vec3 a_position;
layout(location = 1) in vec2 a_texture;
layout(location = 2) in vec3 a_normal;

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

out vec2 v_texture;

void main()
{
    gl_Position = projection * view * model * vec4(a_position, 1.0);
    v_texture = a_texture;
}
"""

fragment_src = """
# version 330

in vec2 v_texture;

out vec4 out_color;

uniform sampler2D s_texture;

void main()
{
    out_color = texture(s_texture, v_texture);
}
"""


# glfw callback functions
def window_resize(window, width, height):
    glViewport(0, 0, width, height)
    projection = pyrr.matrix44.create_perspective_projection_matrix(45, width / height, 0.1, 100)
    glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)


# initializing glfw library
if not glfw.init():
    raise Exception("glfw can not be initialized!")

# creating the window
window = glfw.create_window(1280, 720, "My OpenGL window", None, None)

# check if window was created
if not window:
    glfw.terminate()
    raise Exception("glfw window can not be created!")

# set window's position
glfw.set_window_pos(window, 400, 200)

# set the callback function for window resize
glfw.set_window_size_callback(window, window_resize)

# make the context current
glfw.make_context_current(window)

# load here the 3d meshes
chibi_indices, chibi_buffer = ObjLoader.load_model("meshes/chibi.obj")
monkey_indices, monkey_buffer = ObjLoader.load_model("meshes/monkey.obj")

shaderObj = compileProgram(compileShader(vertex_src, GL_VERTEX_SHADER), compileShader(fragment_src, GL_FRAGMENT_SHADER))

# VAO and VBO
VAO = glGenVertexArrays(2)
VBO = glGenBuffers(3) #edited from 2 to 3 for background texture
# EBO = glGenBuffers(1)

# Chibi VAO
glBindVertexArray(VAO[0])
# Chibi Vertex Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, VBO[0])
glBufferData(GL_ARRAY_BUFFER, chibi_buffer.nbytes, chibi_buffer, GL_STATIC_DRAW)

# glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
# glBufferData(GL_ELEMENT_ARRAY_BUFFER, chibi_indices.nbytes, chibi_indices, GL_STATIC_DRAW)

# chibi vertices
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, chibi_buffer.itemsize * 8, ctypes.c_void_p(0))
# chibi textures
glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, chibi_buffer.itemsize * 8, ctypes.c_void_p(12))
# chibi normals
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, chibi_buffer.itemsize * 8, ctypes.c_void_p(20))
glEnableVertexAttribArray(2)

# Monkey VAO
glBindVertexArray(VAO[1])
# Monkey Vertex Buffer Object
glBindBuffer(GL_ARRAY_BUFFER, VBO[1])
glBufferData(GL_ARRAY_BUFFER, monkey_buffer.nbytes, monkey_buffer, GL_STATIC_DRAW)

# monkey vertices
glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, monkey_buffer.itemsize * 8, ctypes.c_void_p(0))
# monkey textures
glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, monkey_buffer.itemsize * 8, ctypes.c_void_p(12))
# monkey normals
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, monkey_buffer.itemsize * 8, ctypes.c_void_p(20))
glEnableVertexAttribArray(2)


############### Background Texture #################################
# Bind the buffer
glBindBuffer(GL_ARRAY_BUFFER, VBO[2])
glBufferData(GL_ARRAY_BUFFER, 128, rectangle, GL_STATIC_DRAW)

# Create EBO
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBackground, GL_STATIC_DRAW)

# get the position from  shader
position = glGetAttribLocation(shaderObj, 'position')
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(0))
glEnableVertexAttribArray(position)

# get the color from  shader
# color = glGetAttribLocation(shader, 'color')
color = 1
glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(12))
glEnableVertexAttribArray(color)

# texCoords = glGetAttribLocation(shader, "InTexCoords")
texCoords = 2
glVertexAttribPointer(texCoords, 2, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(24))
glEnableVertexAttribArray(texCoords)

glBindAttribLocation(shaderObj, position, 'position')
glBindAttribLocation(shaderObj, color, 'color')
glBindAttribLocation(shaderObj, texCoords, 'InTexCoords')

# Creating Texture
texture = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texture)
# texture wrapping params
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
# texture filtering params
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

image = Image.open("wood.jpg")
img_data = np.array(list(image.getdata()), np.uint8)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 512, 512, 0, GL_RGB, GL_UNSIGNED_BYTE, img_data)
#####################################################################

textures = glGenTextures(2)
load_texture("meshes/chibi.png", textures[0])
load_texture("meshes/monkey.jpg", textures[1])

glUseProgram(shaderObj)
glClearColor(0, 0.1, 0.1, 1)
glEnable(GL_DEPTH_TEST)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

projection = pyrr.matrix44.create_perspective_projection_matrix(45, 1280 / 720, 0.1, 100)
chibi_pos = pyrr.matrix44.create_from_translation(pyrr.Vector3([0, -5, -10]))
monkey_pos = pyrr.matrix44.create_from_translation(pyrr.Vector3([-4, 0, 0]))

# eye, target, up
view = pyrr.matrix44.create_look_at(pyrr.Vector3([0, 0, 8]), pyrr.Vector3([0, 0, 0]), pyrr.Vector3([0, 1, 0]))

model_loc = glGetUniformLocation(shaderObj, "model")
proj_loc = glGetUniformLocation(shaderObj, "projection")
view_loc = glGetUniformLocation(shaderObj, "view")

glUniformMatrix4fv(proj_loc, 1, GL_FALSE, projection)
glUniformMatrix4fv(view_loc, 1, GL_FALSE, view)

# the main application loop
while not glfw.window_should_close(window):
    glfw.poll_events()

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    rot_y = pyrr.Matrix44.from_y_rotation(0.8 * glfw.get_time())
    model = pyrr.matrix44.multiply(rot_y, chibi_pos)

    # draw the chibi character
    glBindVertexArray(VAO[0])
    glBindTexture(GL_TEXTURE_2D, textures[0])
    glUniformMatrix4fv(model_loc, 1, GL_FALSE, model)
    glDrawArrays(GL_TRIANGLES, 0, len(chibi_indices))

    rot_y = pyrr.Matrix44.from_y_rotation(-0.8 * glfw.get_time())
    model = pyrr.matrix44.multiply(rot_y, monkey_pos)

    # draw the monkey head
    glBindVertexArray(VAO[1])
    glBindTexture(GL_TEXTURE_2D, textures[1])
    glUniformMatrix4fv(model_loc, 1, GL_FALSE, model)
    glDrawArrays(GL_TRIANGLES, 0, len(monkey_indices))

    # Draw Background Texture
    glDrawElements(GL_TRIANGLES, len(chibi_indices), GL_UNSIGNED_INT, None)

    glfw.swap_buffers(window)

# terminate glfw, free up allocated resources
glfw.terminate()
Robin Alvarenga
  • 321
  • 2
  • 14

2 Answers2

1

You have 2 shader programs, hence you have to compile both shader programs and you have to install the shader program before you draw the geometry by glUseProgram:

shaderObj = compileProgram(compileShader(vertex_src, GL_VERTEX_SHADER), compileShader(fragment_src, GL_FRAGMENT_SHADER))

shaderObjBackground = compileProgram(compileShader(VERTEX_SHADER, GL_VERTEX_SHADER), compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER))
############### Background Texture #################################
backgroundVAO = glGenVertexArrays(1)
glBindVertexArray(backgroundVAO)

# Bind the buffer
glBindBuffer(GL_ARRAY_BUFFER, VBO[2])
glBufferData(GL_ARRAY_BUFFER, 128, rectangle, GL_STATIC_DRAW)

# Create EBO
EBO = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBackground, GL_STATIC_DRAW)

# get the position from  shader
position = glGetAttribLocation(shaderObjBackground, 'position')
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(0))
glEnableVertexAttribArray(position)

# get the color from  shader
color = glGetAttribLocation(shaderObjBackground, 'color')
glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(12))
glEnableVertexAttribArray(color)

texCoords = glGetAttribLocation(shaderObjBackground, "InTexCoords")
glVertexAttribPointer(texCoords, 2, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(24))
glEnableVertexAttribArray(texCoords)
# the main application loop
while not glfw.window_should_close(window):
    glfw.poll_events()

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

    glUseProgram(shaderObj)

    rot_y = pyrr.Matrix44.from_y_rotation(0.8 * glfw.get_time())
    model = pyrr.matrix44.multiply(rot_y, chibi_pos)

    # draw the chibi character
    glBindVertexArray(VAO[0])
    glBindTexture(GL_TEXTURE_2D, textures[0])
    glUniformMatrix4fv(model_loc, 1, GL_FALSE, model)
    glDrawArrays(GL_TRIANGLES, 0, len(chibi_indices))

    rot_y = pyrr.Matrix44.from_y_rotation(-0.8 * glfw.get_time())
    model = pyrr.matrix44.multiply(rot_y, monkey_pos)

    # draw the monkey head
    glBindVertexArray(VAO[1])
    glBindTexture(GL_TEXTURE_2D, textures[1])
    glUniformMatrix4fv(model_loc, 1, GL_FALSE, model)
    glDrawArrays(GL_TRIANGLES, 0, len(monkey_indices))


    glUseProgram(shaderObjBackground)

    # Draw Background Texture
    glBindVertexArray(backgroundVAO)
    glBindTexture(GL_TEXTURE_2D, texture)
    glDrawElements(GL_TRIANGLES, len(chibi_indices), GL_UNSIGNED_INT, None)

    glfw.swap_buffers(window)

Ensure that the background texture is always in the back of all the other geometry.

You can achieve this by setting the z coordinate to a value near 1.0 in VERTEX_SHADER vertex shader:

gl_Position = vec4(position, 1.0);

gl_Position = vec4(position.xy, 0.999, 1.0);

Alternatively you can change the depth test function to GL_LEQUAL when you draw the background texture

glDepthFunc(GL_LEQUAL)

and set the clip space z coordinate equal the w component. For instance:

gl_Position = vec4(position, 1.0);
gl_Position.z = gl_Position.w;
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • This line game me problems, so I edited it out: glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, 32, ctypes.c_void_p(12)) When I run the new code, only the background texture, wood.jpg, shows. I when edit out glUseProgram(shaderObjBackground) and its glBindVertexArray, glBindTexture, and glDrawElements I can see the .obj models without the background texture. So far, I have not been able to see the .obj model in front of the background texture wood.jpg. Any thoughts? – Robin Alvarenga Oct 17 '20 at 12:58
  • I changed the VERTEX_SHADER so that it is now gl_Position = vec4(position.xy, 1.0, 1.0);, but now wood.jpg does not show at all. Was there anything else I needed to do? – Robin Alvarenga Oct 17 '20 at 13:13
  • gl_Position = position.xyzz; turns everything into a solid tan color. – Robin Alvarenga Oct 17 '20 at 13:23
  • Yes, I only changed one line. – Robin Alvarenga Oct 17 '20 at 13:37
  • At least I can show both wood.jpg and the .obj models in the same window. I verified this by changing the 'rectangle' variable so that the background texture only takes up half the window area. Doing so allows me to see both the wood.jpg and .obj models in the same window. I'm going to get some sleep now, hopefully we can figure something out once I'm well rested. Thanks for you help so far @Rabbid76 – Robin Alvarenga Oct 17 '20 at 13:37
  • It works now! I used gl_Position = vec4(position, 1.0) and gl_Position.z = gl_Position.w in my VERTEX_SHADER. Then I added glDepthFunc(GL_LEQUAL) in my while() loop right before the background texture is drawn. Thank you so much @Rabbid76! You're awesome! – Robin Alvarenga Oct 17 '20 at 19:33
  • gl_Position = vec4(position, 0.999, 1.0); I believe this was meant to be gl_Position = vec4(position.x, position.y, 0.999, 1.0); This is what worked for me . – Robin Alvarenga Jan 04 '21 at 23:43
  • 1
    @RobinAlvarenga Yes of course `gl_Position = vec4(position.xy, 0.999, 1.0);` – Rabbid76 Jan 05 '21 at 05:57
0

The following modification worked for me for the same problem. However, I am wondering that what is it doing underneath.... Is it not working on layer by layer rendering and just doing actual perspective transform similar to pin hole camera model?

glDepthFunc(GL_LEQUAL) before background

and below in the shader

gl_Position = vec4(position, 1.0);
gl_Position.z = gl_Position.w;

Disclaimer!!! very new to opengl