Merender ke tekstur dengan OpenGL C ++

Aug 16 2020

Saya mencoba membuat beberapa poligon menjadi tekstur, dan kemudian merender tekstur ke layar. Saya tidak yakin bagaimana men-debug kode saya karena itu akan membutuhkan untuk menyelidiki keadaan internal OpenGL, jadi saya akan menghargai tip tentang bagaimana men-debug diri sendiri lebih dari menunjukkan kesalahan yang telah saya lakukan. Bagaimanapun, saya mengomentari kode yang saya tulis menjelaskan apa yang saya harapkan dilakukan setiap baris.

Berikut adalah deskripsi tentang apa yang seharusnya dilakukan oleh kode tersebut. Pada dasarnya, saya membuat vertex shader yang memberikan posisi, UV, dan warna pada shader fragmen. Fragmen shader memiliki seragam untuk mengaktifkan pengambilan sampel tekstur, jika tidak maka hanya akan mengeluarkan warna masukan. Dalam kedua kasus, warna dikalikan dengan warna seragam. Pertama saya membuat tekstur, dan saya mengisinya dengan data piksel mentah merah dan hijau untuk diuji. Tekstur ini dirender dengan benar ke layar (saya melihat bagian merah dan hijau dengan benar saat saya menginisialisasi). Kemudian saya mencoba melakukan rendering sebenarnya pada tekstur. Saya mencoba membuat kotak biru kecil di tengahnya (sampler dinonaktifkan pada shader fragmen, seragam warna disetel ke biru) tetapi saya tidak bisa membuat kotak biru ini muncul pada tekstur yang diberikan.

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "utils.h"

#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <iostream>

using namespace std;
#define numVAOs 1
#define numVBOs 1

GLuint shaderProgram;
GLuint unifUseTexture, unifInTexture, unifTMat, unifDrawColor;
GLuint texture;
GLuint textureFrameBuffer;
GLuint vao[numVAOs];
GLuint vbo[numVBOs];

void drawRectangle() {

}
void init() {
    // Compile the shaderProgram
    shaderProgram = createShaderProgram("vertex.glsl","fragment.glsl");
    // Retrieve the uniform location
    unifUseTexture = glGetUniformLocation(shaderProgram,"useTexture");
    unifInTexture = glGetUniformLocation(shaderProgram,"inTexture");
    unifTMat = glGetUniformLocation(shaderProgram,"tMat");
    unifDrawColor = glGetUniformLocation(shaderProgram,"drawColor");
    // Create vertex array object and vertex buffer object
    glGenVertexArrays(numVAOs,vao);
    glBindVertexArray(vao[0]);
    float xyzuvrgbaSquare[54] = {
        /* C */ 1.0,-1.0,0.0, 1.0,0.0, 1.0,1.0,1.0,1.0,
        /* A */ -1.0,1.0,0.0, 0.0,1.0, 1.0,1.0,1.0,1.0,
        /* B */  1.0,1.0,0.0, 1.0,1.0, 1.0,1.0,1.0,1.0,
        /* A */ -1.0,1.0,0.0, 0.0,1.0, 1.0,1.0,1.0,1.0,
        /* C */ 1.0,-1.0,0.0, 1.0,0.0, 1.0,1.0,1.0,1.0,
        /* D */-1.0,-1.0,0.0, 0.0,0.0, 1.0,1.0,1.0,1.0
    };
    glGenBuffers(numVBOs,vbo);
    glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);
    glBufferData(GL_ARRAY_BUFFER, 4*54,xyzuvrgbaSquare,GL_STATIC_DRAW);
    // Associate vbo with the correct vertex attribute to display the rectangle
    glBindBuffer(GL_ARRAY_BUFFER,vbo[0]);
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,36,0); // inPosition
    glVertexAttribPointer(1,4,GL_FLOAT,GL_FALSE,36,(void*)20); // inColor
    glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,36,(void*)12); // inUV
    glEnableVertexAttribArray(0); // location=0 in the shader
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

    // Generate a small 128x128 texture. I followed the tutorial
    // over http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-14-render-to-texture/

    // generate a frameBuffer to contain the texture
    glGenFramebuffers(1,&textureFrameBuffer);
    // Bind it, so when I will generate the texture it will be associated with it
    glBindFramebuffer(GL_FRAMEBUFFER, textureFrameBuffer);
    glGenTextures(1,&texture);
    glBindTexture(GL_TEXTURE_2D,texture);
    // Put some raw data inside of it for testing purposes. I will fill it
    // half with green, half with red
    unsigned char* imageRaw = new unsigned char[4*128*128];
    for(int i=0; i<4*128*64; i+=4) {
        imageRaw[i] = 255;
        imageRaw[i+1] = 0;
        imageRaw[i+2] = 0;
        imageRaw[i+3] = 255;
        imageRaw[4*128*64+i] = 0;
        imageRaw[4*128*64+i+1] = 255;
        imageRaw[4*128*64+i+2] = 0;
        imageRaw[4*128*64+i+3] = 255;
    }
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,128,128,0,GL_RGBA,GL_UNSIGNED_BYTE,imageRaw);
    // Setup some required parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    // Draw a small blue square on the texture
    // So, activate the previously compiled shader program and setup the uniforms
    glUseProgram(shaderProgram);
    // First, create a transform matrix to make the square smaller (20% of texture)
    glm::mat4 tMat = glm::scale(glm::mat4(1.0f),glm::vec3(0.2,0.2,0));
    glUniformMatrix4fv(unifTMat,1,GL_FALSE,glm::value_ptr(tMat));
    // do not use a texture (ignore sampler2D in fragment shader)
    glUniform1i(unifUseTexture,0);
    // use the color BLUE for the rectangle
    glUniform4f(unifDrawColor,0.0,0.0,1.0,1.0);
    // Bind the textureFrameBuffer to render on the texture instead of the screen
    glBindFramebuffer(GL_FRAMEBUFFER,textureFrameBuffer);
    glFramebufferTexture(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,texture,0);
    GLenum drawBuffers[1] = {GL_COLOR_ATTACHMENT0};
    glDrawBuffers(1, drawBuffers);
    GLenum status = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER);
    if( status != GL_FRAMEBUFFER_COMPLETE ) {
        cout << "framebuffer status: " << status << endl;
    }
    // the vertex framebuffer and vertex attribute pointer have already been
    // described, so I'll just do the draw call here
    glDrawArrays(GL_TRIANGLES,0,6);

    // Display the textore on screen
    // Bind the screen framebuffer (0) so the following rendering will occurr on screen
    glBindFramebuffer(GL_FRAMEBUFFER,0);
    // Put a white background color
    glClearColor(1.0,1.0,1.0,1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    // Change properly the shader uniforms
    glUniform4f(unifDrawColor,1.0,1.0,1.0,1.0); // multiply by white, no changes
    glUniform1i(unifUseTexture,1); // set useTexture to True
    // Create a transform matrix to scale the rectangle so that it uses up only half screen
    tMat = glm::scale(glm::mat4(1.0f),glm::vec3(.5,.5,.0));
    glUniformMatrix4fv(unifTMat,1,GL_FALSE,glm::value_ptr(tMat));
    // Put the sampler2D 
    glActiveTexture(GL_TEXTURE0); // Work on texture0
    // 0 because of (binding = 0) on the fragment shader
    glBindTexture(GL_TEXTURE_2D,texture);
    

    glDrawArrays(GL_TRIANGLES,0,6); // 6 vertices
}


int main(int argc, char** argv) {
    // Build the window
    if (!glfwInit()) exit(EXIT_FAILURE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR,4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR,3);
    GLFWwindow* window = glfwCreateWindow(600,600,"Dashboard",NULL,NULL);
    glfwMakeContextCurrent(window);
    if(glewInit() != GLEW_OK) exit(EXIT_FAILURE);
    glfwSwapInterval(1);
    init();
    while(!glfwWindowShouldClose(window)) {
        //display(window,glfwGetTime());
        glfwSwapBuffers(window);
        glfwPollEvents();
    }
    glfwDestroyWindow(window);
    glfwTerminate();
    exit(EXIT_SUCCESS);
}

edit: Saya lupa meletakkan kode shader di sini, meskipun masalahnya bukan di dalam shader karena berfungsi saat digunakan untuk membuat tekstur ke layar.

vertex.glsl:

#version 430
layout (location=0) in vec3 inPosition;
layout (location=1) in vec4 inColor;
layout (location=2) in vec2 inUV;

uniform mat4 tMat;
uniform vec4 drawColor;

out vec4 varyingColor;
out vec2 varyingUV;

void main(void) {
    gl_Position = tMat * vec4(inPosition,1.0);
    varyingColor = inColor*drawColor;
    varyingUV = inUV;
}

fragment.glsl:

#version 430
in vec4 varyingColor;
in vec2 varyingUV;
layout(location = 0) out vec4 color;

layout (binding=0) uniform sampler2D inTexture;
uniform bool useTexture;

void main(void) {
    if( useTexture )
        color = vec4(texture(inTexture,varyingUV).rgb,1.0) * varyingColor;
    else
        color = varyingColor;
}

Jawaban

1 Rabbid76 Aug 16 2020 at 23:48

Tekstur yang ditempelkan pada framebuffer memiliki ukuran yang berbeda dengan jendela. Karenanya Anda harus menyesuaikan viewport rectangle ( glViewport) ke ukuran ukuran framebuffer yang saat ini terikat, sebelum menggambar geometri:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 128, 128, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageRaw);

// [...]

glBindFramebuffer(GL_FRAMEBUFFER, textureFrameBuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture,0);
glViewport(0, 0, 128, 128);

// [...]

glDrawArrays(GL_TRIANGLES, 0, 6);

// [...]

glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(0, 0, WIDTH, HEIGHT);

// [...]

glDrawArrays(GL_TRIANGLES, 0, 6);