在使用PyOpenGL绘制纹理时如何翻转纹理?

3

我最近下载了一个使用pyOpenGl和pygame的批量渲染程序。我想编辑绘图函数,使其具有在绘制时翻转纹理的功能。这是批量渲染程序的代码。

import numpy
from OpenGL import GL
from OpenGL.GL import shaders
import ctypes

def setup_shaders(vpMatrix):
    global shaderProgram, vshader, fshader
    global texCoordAttribute, positionAttribute
    
    vshader = shaders.compileShader('''#version 440

    layout (location = 0) uniform mat4 vpMatrix;

    layout (location = 0) in vec2 vPosition;
    layout (location = 1) in vec2 vTexCoord0;
    //layout (location = 2) in vec4 vVertColor;

    out vec2 texCoord0;
    //out vec4 vertColor;

    void main(void) {
        gl_Position = vpMatrix * vec4(vPosition, 0.0, 1.0);
        texCoord0 = vTexCoord0;
    }''', GL.GL_VERTEX_SHADER)

    fshader = shaders.compileShader('''#version 440

    in vec2 texCoord0;
    //in vec4 vertColor;
    layout (location = 1) uniform sampler2D u_texture0;

    void main(void) {
        gl_FragColor = texture2D(u_texture0, texCoord0);
    }''', GL.GL_FRAGMENT_SHADER)

    shaderProgram = shaders.compileProgram(vshader, fshader)

    texCoordAttribute = GL.glGetAttribLocation(shaderProgram, 'vTexCoord0')
    positionAttribute = GL.glGetAttribLocation(shaderProgram, 'vPosition')
    texUniform = GL.glGetUniformLocation(shaderProgram, 'u_texture0')

    GL.glEnableVertexAttribArray(positionAttribute)
    GL.glEnableVertexAttribArray(texCoordAttribute)
    
    GL.glUseProgram(shaderProgram)
    
    GL.glUniformMatrix4fv(
        GL.glGetUniformLocation(shaderProgram, 'vpMatrix'),
        1,
        False,
        vpMatrix,
    )
    GL.glUniform1i(texUniform, 0)
    
    # TODO: cleanup (delete shaders)

class GLTexture:
    def __init__(self, textureId, width, height):
        self.texture = textureId
        self.width, self.height = width, height

    def delete(self):
        GL.glDeleteTextures([self.texture])

class GLTextureRegion:
    def __init__(self, glTexture, subX, subY, subWidth, subHeight):
        self.texture = glTexture
        #self.tx, self.ty = subX, subY
        self.tw, self.th = subWidth, subHeight

        self.normalizedCoords = (
            subX / glTexture.width, subY / glTexture.height,
            (subX + subWidth) / glTexture.width, subY / glTexture.height,
            subX / glTexture.width, (subY + subHeight) / glTexture.height,
            (subX + subWidth) / glTexture.width, (subY + subHeight) / glTexture.height,
        )

    def write_tex_coords(self, aj):
        nc = self.normalizedCoords
        aj[4 * 0 + 2] = nc[0]
        aj[4 * 0 + 3] = nc[1]
        aj[4 * 1 + 2] = nc[2]
        aj[4 * 1 + 3] = nc[3]
        aj[4 * 2 + 2] = nc[4]
        aj[4 * 2 + 3] = nc[5]
        aj[4 * 3 + 2] = nc[6]
        aj[4 * 3 + 3] = nc[7]

    def write_vertices(self, aj, x, y):
        aj[4 * 0 + 0] = x
        aj[4 * 0 + 1] = y
        aj[4 * 1 + 0] = x + self.tw
        aj[4 * 1 + 1] = y
        aj[4 * 2 + 0] = x
        aj[4 * 2 + 1] = y + self.th
        aj[4 * 3 + 0] = x + self.tw
        aj[4 * 3 + 1] = y + self.th

    def update_array(self, vboData, arrayIndex, x, y):
        aj = vboData[arrayIndex]
        self.write_vertices(aj, x, y)
        self.write_tex_coords(aj)

    def delete(self):
        '''deletes the underlying texture'''
        self.texture.delete()

class Batch:
    def __init__(self, maxQuads = 10000):
        self.maxQuads = maxQuads

        self.vboIndices = GL.glGenBuffers(1)
        GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, self.vboIndices)
        vidA = []
        for i in range(maxQuads):
            vidA.extend([
                4 * i + 0,
                4 * i + 2,
                4 * i + 1,
                4 * i + 2,
                4 * i + 1,
                4 * i + 3
            ])
        self.vboIndexData = numpy.array(vidA, numpy.ushort)
        del vidA
        GL.glBufferData(
            GL.GL_ELEMENT_ARRAY_BUFFER,
            self.vboIndexData,
            GL.GL_STATIC_DRAW,
        )

        self.vbo = GL.glGenBuffers(1)  # texture coords & vertices
        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.vbo)
        self.vboData = numpy.zeros((maxQuads, 4 * 4), numpy.float32)
        GL.glBufferData(
            GL.GL_ARRAY_BUFFER,
            self.vboData,
            GL.GL_DYNAMIC_DRAW
        )

        self.currentTexture = None
        self.objectIndex = 0

    def begin(self):
        GL.glBindBuffer(
            GL.GL_ARRAY_BUFFER,
            self.vbo,
        )
        if self.currentTexture:
            GL.glBindTexture(
                GL.GL_TEXTURE_2D,
                self.currentTexture.texture,
            )
        GL.glEnable(GL.GL_TEXTURE_2D)
        GL.glActiveTexture(GL.GL_TEXTURE0)

    def draw(self, textureRegion, x, y, flip = False):
        
        
        if self.currentTexture != textureRegion.texture:
            self.flush()
            self.currentTexture = textureRegion.texture
            GL.glBindTexture(
                GL.GL_TEXTURE_2D,
                textureRegion.texture.texture,
            )
        elif self.objectIndex >= self.maxQuads:
            self.flush()
        
        textureRegion.update_array(
            self.vboData,
            self.objectIndex,
            x, y
        )
        
        self.objectIndex += 1

    def end(self):
        self.flush()

    def flush(self):
        if not self.objectIndex:
            return
        GL.glVertexAttribPointer(
            texCoordAttribute,
            2,
            GL.GL_FLOAT,
            GL.GL_TRUE,
            4 * self.vboData.itemsize,
            ctypes.c_void_p(2 * self.vboData.itemsize)
        )
        GL.glVertexAttribPointer(
            positionAttribute,
            2,
            GL.GL_FLOAT,
            GL.GL_FALSE,
            4 * self.vboData.itemsize,
            ctypes.c_void_p(0)
        )
        GL.glBufferSubData(
            GL.GL_ARRAY_BUFFER,
            0,
            16 * self.objectIndex * self.vboData.itemsize,
            self.vboData,
        )
        GL.glDrawElements(
            GL.GL_TRIANGLES,
            6 * self.objectIndex,
            GL.GL_UNSIGNED_SHORT,
            ctypes.c_void_p(0),
        )
        self.objectIndex = 0

    def delete(self):
        GL.glDeleteBuffers(1, [self.vbo])
        GL.glDeleteBuffers(1, [self.vboIndices])

到目前为止,根据我在互联网上找到的答案,我已经尝试了几种涉及到glscalef的方法,但是我无法使其对纹理产生任何影响。是否有一种方法可以在绘制函数中使其起作用,或者我必须尝试另一种完全不同的方法?
1个回答

4
您可以在顶点着色器中将纹理坐标的 y 分量 "翻转":
void main(void) {
    // [...]        

    texCoord0 = vec2(vTexCoord0.x, 1.0 - vTexCoord0.y);
}

如果您想单独"翻转"纹理,则需要翻转纹理坐标属性。将TrueFalse传递给参数flipY
class GLTextureRegion:
    # [...]

    def write_tex_coords(self, aj, flipY):
        nc = self.normalizedCoords
        for i in range(4):
            aj[i*4 + 2] = nc[i*2]
            aj[i*4 + 3] = 1-nc[i*2 + 1] if flipY else nc[i*2 + 1]

    # [...]

    def update_array(self, vboData, arrayIndex, x, y, flipY):
        aj = vboData[arrayIndex]
        self.write_vertices(aj, x, y)
        self.write_tex_coords(aj, flipY)

另外,您也可以通过在顶点着色器中使用一个uniform变量对纹理坐标进行缩放。通过以下方式初始化该uniform变量:

layout (location = 0) uniform mat4 vpMatrix;
layout (location = 1) uniform vec2 uTexCoordScale;

layout (location = 0) in vec2 vPosition;
layout (location = 1) in vec2 vTexCoord0;

out vec2 texCoord0;

void main(void) 
{
    gl_Position = vpMatrix * vec4(vPosition, 0.0, 1.0);
    texCoord0 = uTexCoordScale * vTexCoord0;
}

初始化均匀变量uTexCoordScale为(1.0, 1.0),如果您想要“翻转”纹理,请将其更改为(1.0, -1.0)。


我想要能够翻转单个纹理,但是这会翻转屏幕上的每个纹理,有没有办法改变这种情况,使其不会翻转所有东西,而只会翻转我想要的东西? - Evelyn

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接