Pyopengl 纹理渲染不正确,对象填充纯灰色

Pyopengl textures not rendering properly, object filled solid gray

我正在尝试将纹理(png 图像文件)添加到正在渲染的矩形中。但是,无论我使用什么图像,该对象都显示为灰色。我有一种感觉,我缺少与 tetxure 坐标有关的东西。在顶点缓冲区的每一行中,前两个数字代表 x 和 y 坐标,而后两个数字代表纹理坐标。

大多数在线回答表明图像数据传递到纹理中存在问题,或者未正确绑定。

from OpenGL.GL import *
import glfw
import numpy
import sys
from PIL import Image

class Shader:    
    def readshader(self, Title, filepath):
        #reads shader (This works so I have removed it for ease of reading)

    def CreateShader(self, filepath):
        program = glCreateProgram()
        VERT = self.readshader("VERTEX", filepath)
        vertShader = glCreateShader(GL_VERTEX_SHADER)
        self.Compileshader(vertShader, VERT, program, "Vertex")     
        FRAG = self.readshader("FRAGMENT", filepath)
        fragShader = glCreateShader(GL_FRAGMENT_SHADER)
        self.Compileshader(fragShader, FRAG, program, "Fragment")
        glLinkProgram(program)
        glValidateProgram(program)
        glDeleteShader(vertShader)
        glDeleteShader(fragShader)  
        return program

    def Compileshader(self, shader, shaderstring, program, type):
        glShaderSource(shader, shaderstring)
        glCompileShader(shader)
        status = glGetShaderiv(shader, GL_COMPILE_STATUS)
        if not status:
            info = glGetShaderInfoLog(shader)
            print("Error in " + type + " Shader:")
            print(info.decode("utf-8"))
            glDeleteShader(shader)
        else:
            glAttachShader(program, shader)

class Renderer:
    def __init__(self):
        self.vao = glGenVertexArrays(1)
        glBindVertexArray(self.vao)
        self.buffer = glGenBuffers(1)
        glBindBuffer(GL_ARRAY_BUFFER, self.buffer)
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, None)
        self.ibo = glGenBuffers(1)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ibo)

    def AttachTexture(self, NewTexture, Width, Height, Uniform, value):
        glEnable(GL_TEXTURE_2D)
        self.Texture = glGenTextures(1)
        glBindTexture(GL_TEXTURE_2D, self.Texture)

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)

        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NewTexture)

        glGenerateMipmap(GL_TEXTURE_2D); ##new

        glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 16, None) ##new
        glEnableVertexAttribArray(2)  ##new

        glBindTexture(GL_TEXTURE_2D, self.Texture)  #new

        glActiveTexture(GL_TEXTURE0 + value)
        location = glGetUniformLocation(self.program, Uniform)
        glUniform1i(location, value)

    def AttachShader(self, program):
        self.program = program
        glUseProgram(self.program)

    def GetUniformLocation(self, Uniform, r, g, b):
        location = glGetUniformLocation(self.program, Uniform)
        glUniform4f(location, r, g, b, 1.0)

    def ArrayBufferData(self, positions):
        glBufferData(GL_ARRAY_BUFFER, positions, GL_STATIC_DRAW)

    def IndexBufferData(self, indices):
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)

    def Unbind(self):
        glBindVertexArray(0)
        glUseProgram(0)
        glBindBuffer(GL_ARRAY_BUFFER, 0)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)

    def Bind(self):
        glBindVertexArray(self.vao)
        glUseProgram(self.program)
        glBindBuffer(GL_ARRAY_BUFFER, self.buffer)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.ibo)

    def DrawElements(self, length):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glDrawElements(GL_TRIANGLES, length, GL_UNSIGNED_INT, None)


def main():
    TextureA = Image.open("Textures\Texture Test 2019.08.12 01.58.png").transpose(Image.FLIP_TOP_BOTTOM)
    Texture = numpy.frombuffer(TextureA.tobytes(), numpy.uint8)
    Width, Height = TextureA.size
    name = "OpenGL Testing"
    if not glfw.init():
        return
    window = glfw.create_window(640, 480, "Hello World", None, None)
    if not window:
        glfw.terminate()
    glfw.make_context_current(window)
    glfw.swap_interval(1)

    NewShader = Shader()
    program = NewShader.CreateShader("Shaders\Complete Shader 2019.08.12 02.41.txt")

    NewBuffer = Renderer()
    NewBuffer.AttachShader(program)
    positions = numpy.array([-0.5, -0.5, 0.0, 0.0\
                             ,0.5, -0.5, 1.0, 0.0\
                             ,0.5,  0.5, 1.0, 1.0\
                            ,-0.5,  0.5, 0.0, 1.0]\
                            ,dtype = 'float32')
    indices = numpy.array([0, 1, 2,\
                           2, 3, 0]\
                          ,dtype = 'int32')

    NewBuffer.ArrayBufferData(positions)
    NewBuffer.IndexBufferData(indices)

    red = 0.0
    increment = 0.05

    while not glfw.window_should_close(window):
        NewBuffer.Bind()
        if red > 1.0:
            increment = -0.05
        elif red < 0.0:
            increment = 0.05
        red += increment
        NewBuffer.GetUniformLocation("u_Color", red, 0.3, 0.8)
        NewBuffer.DrawElements(len(indices))
        NewBuffer.AttachTexture(Texture, Width, Height, "u_Texture", 0)

        glfw.swap_buffers(window)
        glfw.poll_events()

    glfw.terminate()

if __name__ == '__main__': main()

#For reference here is the vertex and fragment shader I am using:

"""
@VERTEX
#version 330 core

layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texCoord; 

out vec2 v_TexCoord;

void main()
{
    gl_Position = position;
    v_TexCoord = texCoord;
};
@FRAGMENT
#version 330 core

layout(location = 0) out vec4 color;

in vec2 v_TexCoord;

uniform vec4 u_Color;
uniform sampler2D u_Texture;

void main()
{
    vec4 texColor = texture(u_Texture, v_TexCoord);
    color = texColor;
};
"""

我认为您需要一个偏移量 8 作为第二个 glVertexAttribPointer 的最后一个参数,它定义了纹理坐标。

您使用的是交错数组,其中顶点坐标后跟 tex 坐标,对吗?因此,第一个属性指针将顶点坐标定义为 2 个浮点数,相隔 16 个字节(4 x 浮点数),从缓冲区的字节 0 开始。第二个属性指针将纹理坐标定义为 2 个浮点数,相隔 16 个字节, 从缓冲区的字节 0 开始。所以顶点坐标被重新用作纹理坐标。

纹理坐标的属性索引为1:

layout(location = 1) in vec2 texCoord;

因此,当您指定并启用通用顶点属性数组时,属性索引也必须为 1。参见 glVertexAttribPointer respectively glEnableVertexAttribArray

绑定命名缓冲区对象时,glVertexAttribPointer 的最后一个参数被视为缓冲区对象数据存储中的字节偏移量。
缓冲区的内容是顶点坐标,然后是2个纹理坐标:

x0, y0, u0, v0,   x1, y1, u1, v1,   x2, y2, u2, v2, ...

每个组件的大小为 4(大小为 float),因此步幅为 16 = 4*4。
顶点坐标的偏移量为0,偏移纹理坐标为8=2*4.

由于 glVertexAttribPointer is const GLvoid * you've to cast the parameter to ctypes.c_void_p 的偏移量(最后一个)参数类型:

glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, c_void_p(0))

如果偏移量为0,可以用None代替:

glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, None)

在指定通用顶点属性数据数组之前绑定缓冲区,设置适当的属性索引和偏移量:

from ctypes import c_void_p
glBindBuffer(GL_ARRAY_BUFFER, self.buffer)

glEnableVertexAttribArray(0)
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, None)

glEnableVertexAttribArray(1)
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 16, c_void_p(8))