簡體   English   中英

如何從 OpenGL 中的精靈表渲染單個精靈?

[英]How to render individual sprites from a spritesheet in OpenGL?

我有一個帶有以下參數的 spritesheet spritesheet

  • spritesheet 尺寸為 572px x 220px
  • 平鋪寬度和高度為 42px
  • 每個子精靈之間的間距為 2px

我還有一個Sprite class ,它允許我渲染單個精靈:

Sprite::Sprite(std::string filename)
{
    m_vaoID = 0;

    m_vboID[0] = 0;
    m_vboID[1] = 0;
    m_vboID[2] = 0;

    m_NumberOfVerts = 0;

    EBO[0] = 0;
    EBO[1] = 0;
    EBO[2] = 0;

    this->filename = filename;
}


Sprite::~Sprite() {
    glDeleteBuffers(2, m_vboID);
    glDeleteBuffers(2, EBO);
    glDeleteVertexArrays(1, &m_vaoID);
}

void Sprite::SetHeight(float height) {
    this->m_Height = std::move(height);
}

void Sprite::SetWidth(float width) {
    this->m_Width = std::move(width);
}

// initialise method
void Sprite::Init(Shader& shader, float colour[3])
{
    //create and bind the texture on the GPU
    glGenTextures(1, &m_TexName);
    glBindTexture(GL_TEXTURE_2D, m_TexName);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);  
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  


    //image loading error checking
    bool success = ImageLoading::loadImage(this->filename);
    if (!success) {
        std::cout << "Unable to load image file" << std::endl;
        glDeleteTextures(1, &m_TexName);
        return;
    }
    else
    {
        std::cout << "Image "<< this->filename <<" loaded " << std::endl;
    }

    float halfWidth = m_Width/2;
    float halfHeight = m_Height/2;
    
    //Create the geometry such that origin is at the center
    float vert[] = {
        -halfWidth,     halfHeight,     0.0f,   // top left
        -halfWidth,     -halfHeight,    0.0f,   // bottom left
        halfWidth,      -halfHeight,    0.0f,   // bottom right
        halfWidth,      halfHeight,     0.0f    // top right
    };
    

    //texture coordinates
    float tex[]{
        0.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
        1.0f, 1.0f
    };

    // colour array
    float col[]{
        colour[0], colour[1], colour[2],
        colour[0], colour[1], colour[2],
        colour[0], colour[1], colour[2],
        colour[0], colour[1], colour[2],
    };

    unsigned int indices[] = { 
        0, 1, 3,                // first triangle
        1, 2, 3                 // second triangle
    };
    

    //VAO allocation
    glGenVertexArrays(1, &m_vaoID);
    // First VAO setup
    glBindVertexArray(m_vaoID);
    // Buffers setup
    glGenBuffers(3, m_vboID);   // need three VBOs - vertices, colours, textures
    glGenBuffers(3, EBO);       // need three EBOs - vertices, colours, textures


    /// --------VERTICIES--------
    // VBO[0]
    glBindBuffer(GL_ARRAY_BUFFER, m_vboID[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vert), vert, GL_STATIC_DRAW);  
    // EBO[0]
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    //set the position - linked to the position shader input
    GLint vertexLocation = glGetAttribLocation(shader.handle(), "in_Position");
    glEnableVertexAttribArray(vertexLocation);
    glVertexAttribPointer(vertexLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);

    /// --------COLOURS--------
    // Bind and initilise storage of VBO[1] & EBO[1]
    glBindBuffer(GL_ARRAY_BUFFER, m_vboID[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(col), col, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    // set the colour - linked to the colour shader input 
    GLint colorLocation = glGetAttribLocation(shader.handle(), "in_Color");
    glEnableVertexAttribArray(colorLocation);
    glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);

    /// --------TEXTURES--------
    // Bind and initilise storage of VBO[2] & EBO[2]
    glBindBuffer(GL_ARRAY_BUFFER, m_vboID[2]);
    glBufferData(GL_ARRAY_BUFFER,sizeof(tex), tex, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO[2]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    //set the texture coords - linked to the texcoord shader input.
    GLint texLocation = glGetAttribLocation(shader.handle(), "in_TexCoord");
    glEnableVertexAttribArray(texLocation);
    glVertexAttribPointer(texLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);

    glEnableVertexAttribArray(0);
    glBindVertexArray(0);
}

// draw method
void Sprite::draw(Shader& shader, glm::mat4& ModelViewMatrix, glm::mat4& ProjectionMatrix)
{
    glUseProgram(shader.handle());  // use the shader

    //set the DiffuseMap in GLSL to the texture unit 0.
    glUniform1i(glGetUniformLocation(shader.handle(), "DiffuseMap"), 0);

    glBindTexture(GL_TEXTURE_2D, m_TexName);

    //set the uniform for the projectionmatrix
    glUniformMatrix4fv(glGetUniformLocation(shader.handle(), "ProjectionMatrix"), 1, GL_FALSE, &ProjectionMatrix[0][0]);

    //pass the uniform for the ModelView matrix to the shader
    glUniformMatrix4fv(glGetUniformLocation(shader.handle(), "ModelViewMatrix"), 1, GL_FALSE, &ModelViewMatrix[0][0]);

    //Draw the object
    glBindVertexArray(m_vaoID); 
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    glBindVertexArray(0); //unbind the vertex array object
    glUseProgram(0); //turn off the current shader
}

它能夠在初始化后初始化並繪制一個精靈( drawed spritesheet ):

    testObj = new GameObject("Some game obj", transformation);
    testObj->addComponent(assetPool.getSprite("assets/player/layerOne.png"));
    testObj->getComponent<Sprite>()->SetHeight(220.0f);
    testObj->getComponent<Sprite>()->SetWidth(572.0f);
    testObj->getComponent<Sprite>()->Init(myShader, red, 1.0f, 1.0f);

但是,我想單獨初始化每個子精靈,並且我有一個想法,即在迭代精靈表時,我必須計算每個單獨的子精靈的紋理坐標,即。 像這樣的東西:

 subsprite x coord = (column * tileWidth) + (column * spacing);
 subsprite y coord = (row * tileWidth) + (row * spacing);

並將這些 x,y 坐標應用為每個子精靈的紋理坐標。

問題:我的建議是 go 的正確方法嗎?我是否必須更改任何其他 OpenGL 代碼,即。 緩沖區或紋理綁定?

問題:我的建議是 go 的正確方法嗎

沒錯,如果你想繪制一個精靈,那么你必須計算紋理坐標,以便只繪制你想要的精靈。 請注意,紋理坐標不是以像素為單位計算的。 它們 go 從 0 到 1。

我是否必須更改任何其他 OpenGL 代碼,即。 緩沖區或紋理綁定?

您可能需要更改大量代碼才能從這里開始運行游戲,但這不是因為精靈表。

您談論“迭代精靈表並計算每個精靈的坐標”-我不確定您在說什么。 如果你想繪制每一個精靈,然后遍歷所有精靈,並為每一個精靈繪制它。 不然不行嗎?


您的“Sprite” class 中有很多代碼,它們執行諸如加載精靈表和設置頂點緩沖區之類的操作。 這意味着如果您以這種方式保存代碼,則每次您想要創建精靈時,您的程序都會加載精靈表並創建頂點緩沖區。 這就是為什么我說你可能不得不改變很多。

請注意,OpenGL 並不關心您的 Sprite class。 它只關心您按哪個順序調用哪個 OpenGL。 我提到這一點是因為很多初學者認為 OpenGL 確實在乎 - 他們會問諸如“我應該如何編寫我的精靈 class?”之類的問題。

相反,您可以考慮您的程序需要進行哪些調用,然后弄清楚您希望如何編寫類來進行這些調用(並且您根本不必使用類):您可能想要加載精靈表,並且創建一個紋理,並創建一個沒有任何數據的空頂點緩沖區。 這就是初始化。

然后,我假設您想使用相同的紋理在屏幕上渲染一大堆精靈,但紋理的不同部分,即工作表中的不同精靈。 這實際上只是在每個四邊形上渲染一堆具有不同紋理坐標的不同四邊形。 您通過將頂點數據放入緩沖區然后繪制它來繪制事物。

繪制四邊形的最簡單方法是編寫一個DrawSprite function,它用一個精靈的數據填充頂點緩沖區,調用glBufferDataglDrawElements 繪制四邊形的更快方法是將所有四邊形放入一個大緩沖區中,然后將它們全部繪制在一個 go 中。 盡管即使您以緩慢的方式進行繪制,您仍然可以繪制大量四邊形。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM