C++ SDL2 OpenGL(核心配置文件 4.6)使用统一块来处理可变数量的游戏对象,但未正确传递制服

问题描述 投票:0回答:1

我一直在尝试让动态游戏对象将制服作为制服块传递给片段着色器,为了简单起见,在一些样板代码中显示了一个制服。代码编译,顶点和片段着色器也正确编译,但没有显示任何内容,这意味着我没有正确传递制服。

#include <SDL2/SDL.h>
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <iostream>

// Compile
// g++ test.cpp -lSDL2 -lGLEW -lGL -lglut

// point type for vertices of drawing plane
typedef struct
{

    GLfloat vec2[2];
    
} point_t;


// Drawing plane
const point_t drawing_plane_vertices[4] = 
{
     {-1.0, -1.0 }, 
     { 1.0, -1.0 }, 
     { 1.0,  1.0 }, 
     {-1.0,  1.0 }, 
};



typedef struct 
{

    float health;
    float attackStrength;
    
} Swordsman_t;


const GLuint MAX_NUM_SWORDSMAN = 100;

// Vertex shader source
const char* vertexShaderSource = R"(
    
    #version 430 core

    layout (location = 0) in vec2 position;

    out vec2 texCoord;

    void main() {
        gl_Position = vec4(position.x, position.y, 0.0f, 1.0f);
    }
)";

// Fragment shader source
const char* fragmentShaderSource = R"(
#version 430 core

#define MAX_NUM_SWORDSMAN 100

struct Swordsman
{

    float health;
    float attackStrength;

};

layout (std140) uniform uSwordsmanBlock
{


    Swordsman swordsman[MAX_NUM_SWORDSMAN];
    int num;

} uSwordsmanBlock_t;

uniform vec2 uresolution;
uniform float iTime;
vec2 fragCoord = gl_FragCoord.xy;

out vec4 fragColor;

void main(void)
{

    vec2 uv = fragCoord / uresolution * 2.0 - 1.0;
    uv.x *= (uresolution.x / uresolution.y);

    if (uSwordsmanBlock_t.num == 4)
    {

        // first problem this line isn't triggered.
        fragColor = vec4(1.0, 0.0, 0.0, 1.0);

    } else {
        // nothing, just black
        vec4 color = vec4(0.0);

        color.r = uSwordsmanBlock_t.swordsman[0].attackStrength;
        color.g = uSwordsmanBlock_t.swordsman[1].attackStrength;
        color.b = uSwordsmanBlock_t.swordsman[2].attackStrength;
        color.a = uSwordsmanBlock_t.swordsman[3].attackStrength;

        fragColor = vec4(color);

    }

}

)";


int main() {
    // Initialize SDL
    assert(SDL_Init(SDL_INIT_VIDEO) >= 0);

    // Set OpenGL context attributes
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6);
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    // Full screen desktop display mode
    SDL_DisplayMode fullscreen_desktopm;
    assert(SDL_GetDesktopDisplayMode(0, &fullscreen_desktopm) == 0);

    // Create SDL window
    SDL_Window* sdl_window = SDL_CreateWindow("OpenGL Circle Example", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, fullscreen_desktopm.w, fullscreen_desktopm.h, SDL_WINDOW_OPENGL);
    assert(sdl_window);

    SDL_SetWindowFullscreen(sdl_window, SDL_WINDOW_FULLSCREEN_DESKTOP);

    // Create OpenGL context
    SDL_GLContext gl_context = SDL_GL_CreateContext(sdl_window);
    assert(gl_context);

    // Initialize GLEW
    assert(glewInit() == GLEW_OK);

    // Compile and link shaders
    GLuint vertexShader_id = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader_id, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader_id);

    GLuint fragmentShader_id = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader_id, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader_id);

    GLuint shaderProgram_id = glCreateProgram();
    glAttachShader(shaderProgram_id, vertexShader_id);
    glAttachShader(shaderProgram_id, fragmentShader_id);
    glLinkProgram(shaderProgram_id);


    // Check shader compilation and linking errors
    GLint success;
    GLchar infoLog[512];

    glGetShaderiv(vertexShader_id, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader_id, 512, nullptr, infoLog);
        std::cerr << "Vertex shader compilation failed: " << infoLog << std::endl;
        return -1;
    }

    glGetShaderiv(fragmentShader_id, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader_id, 512, nullptr, infoLog);
        std::cerr << "Fragment shader compilation failed: " << infoLog << std::endl;
        return -1;
    }

    glGetProgramiv(shaderProgram_id, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram_id, 512, nullptr, infoLog);
        std::cerr << "Shader program linking failed: " << infoLog << std::endl;
        return -1;
    }


    // Illustrating dynamic drawing using uniform block
    // This would would be done with dynamic allocation...
    int numSwordsman = 4;

    // Example Swordsman struct objects
    Swordsman_t sm1 = {50.0,  0.0f};
    Swordsman_t sm2 = {25.0,  1.0f};
    Swordsman_t sm3 = {100.0, 0.0f};
    Swordsman_t sm4 = {15.0,  1.0f};

    Swordsman_t Swordsmen[numSwordsman] = {sm1, sm2, sm3, sm4};

    GLuint uSwordsmanBlock = glGetUniformBlockIndex(shaderProgram_id, "uSwordsmanBlock");
    assert(uSwordsmanBlock != GL_INVALID_INDEX);
    
    GLuint swordsmanuniformblock_id = 0;
    glUniformBlockBinding(shaderProgram_id, uSwordsmanBlock, swordsmanuniformblock_id);

    // Size of swords man block for use with subdata bind
    GLint blocksize = numSwordsman * sizeof(Swordsman_t);

    GLuint ubo_swordsman_id;
    glGenBuffers(1, &ubo_swordsman_id);
    glBindBuffer(GL_UNIFORM_BUFFER, ubo_swordsman_id);
    glBufferData(GL_UNIFORM_BUFFER, blocksize, nullptr, GL_DYNAMIC_DRAW); 
    glBindBufferBase(GL_UNIFORM_BUFFER, uSwordsmanBlock, ubo_swordsman_id);

    glDeleteShader(vertexShader_id);
    glDeleteShader(fragmentShader_id);

    // Vertex Buffer Object (VBO) and Vertex Array Object (VAO) setup
    GLuint vbo_id, vao_id;
    glGenVertexArrays(1, &vao_id);
    glBindVertexArray(vao_id);

    glGenBuffers(1, &vbo_id);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_id);
    glBufferData(GL_ARRAY_BUFFER, sizeof(drawing_plane_vertices), drawing_plane_vertices, GL_STATIC_DRAW);

    // Position attribute
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);

    // Unbind vao_id
    glBindVertexArray(0);

    // Main loop
    bool quit = false;
    SDL_Event event;

    GLint resolutionLoc = glGetUniformLocation(shaderProgram_id, "uresolution");
    GLint itimeLoc = glGetUniformLocation(shaderProgram_id, "iTime");

    float outerlcount = 0;

    while (!quit) 
    {

        Uint64 tickstartc = SDL_GetPerformanceCounter();

        while (SDL_PollEvent(&event)) 
        {
            if (event.type == SDL_QUIT) 
            {
                
                quit = true;
                break;
            
            } else if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) {

                quit = true;
                break;

            }
        }

        // Clear the buffer
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        // Use the shader program
        glUseProgram(shaderProgram_id);
    
        glUniform2f(resolutionLoc, static_cast<float>(fullscreen_desktopm.w), static_cast<float>(fullscreen_desktopm.h));
        glUniform1f(itimeLoc, outerlcount);

        // dynamic uniforms
        GLint blockindex = glGetUniformLocation(shaderProgram_id, "uSwordsmanBlock");
        glUniformBlockBinding(shaderProgram_id, blockindex, swordsmanuniformblock_id);

        glBindBuffer(GL_ARRAY_BUFFER, ubo_swordsman_id); // bound to correct buffer for this next operation.
        glBufferSubData(GL_UNIFORM_BUFFER, 0, numSwordsman * sizeof(Swordsman_t), Swordsmen);
        glUniform1i(glGetUniformLocation(shaderProgram_id, "uSwordsmanBlock.num"), numSwordsman);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        

        // Drawing plane vertices 
        glBindVertexArray(vao_id);
        glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

        // Swap the front and back buffers
        SDL_GL_SwapWindow(sdl_window);

        // Unbind vao_id and shader program
        // glBindBufferBase(GL_UNIFORM_BUFFER, 0);
        glBindVertexArray(0);
        glUseProgram(0);

        Uint64 tendc = SDL_GetPerformanceCounter();
        float t_elapsedms = (tendc - tickstartc) / static_cast<float>(SDL_GetPerformanceCounter()) * 1000.0f;

        SDL_Delay(floor(5.0f - t_elapsedms));

        outerlcount += 0.1;

    }

    // Cleanup
    glDeleteVertexArrays(1, &vao_id);
    glDeleteBuffers(1, &vbo_id);

    SDL_GL_DeleteContext(gl_context);
    SDL_DestroyWindow(sdl_window);
    SDL_Quit();

    return 0;
}


我尝试过使用这样的统一块:

struct Swordsman
{

    float health;
    float attackStrength;

};

layout (std140) uniform uSwordsmanBlock
{


    Swordsman swordsman[MAX_NUM_SWORDSMAN];
    int num;

};

并索引到

uSwordsmanBlock.swordsman[i]
,但它会导致编译错误。我也尝试过
GLint uNumSwordsman = glGetUniformLocation(shaderProgram_id, "uSwordsmanBlock.num"); assert(uNumSwordsman != GL_INVALID_INDEX);
,ChatGPT 告诉我这“不是问题”并且我的代码“工作正常”。

c++ opengl shader sdl-2 uniform
1个回答
0
投票

您似乎签署了

std140
布局,但没有意识到它对数组意味着什么:(OpenGL 4.5秒7.6.2.2“标准统一块布局”)

如果成员是标量或向量数组,则基本对齐和数组 stride 设置为匹配单个数组元素的基本对齐方式,根据 符合规则 (1)、(2) 和 (3),并向上舍入到 vec4 的基本对齐方式。这 数组末尾可能有填充;以下成员的基本偏移量 数组向上舍入到基本对齐的下一个倍数。

换句话说,每个

Swordsman
必须按16字节对齐。抄袭这个答案

struct alignas(16) Swordsman
{
    float health;
    float attackStrength;
};

或者,更简单的是,将 UBO 布局切换到

std430
布局,即

与 std140 类似,只是对标量和向量元素的数组和结构的对齐和步幅进行了一些优化(除了 vec3 元素,它们与 std140 保持不变)。具体来说,它们不再四舍五入为 16 字节的倍数。因此

float
的数组将与
float
的 C++ 数组匹配。

© www.soinside.com 2019 - 2024. All rights reserved.