SDL2 OpenGL C++ 移动使用 VBO 和 FBO 绘制的精灵

Posted

技术标签:

【中文标题】SDL2 OpenGL C++ 移动使用 VBO 和 FBO 绘制的精灵【英文标题】:SDL2 OpenGL C++ moving a sprite drawn with VBO and FBO 【发布时间】:2016-08-08 22:11:30 【问题描述】:

我使用 fbo 和 vbo 在我的 Sprite.cpp 中绑定并绘制了一个由两个三角形组成的矩形,这是我的 init 方法

void Sprite::init(float x, float y, float width, float height) 

_x = x;
_y = y;
_width = width;
_height = height;

if (_vboID == 0) 

    glGenBuffers(1, &_vboID);





vertexData[0] = x + width;
vertexData[1] = y + height;

vertexData[2] = x;
vertexData[3] = y + height;

vertexData[4] = x;
vertexData[5] = y;

vertexData[6] = x + width;
vertexData[7] = y + height;

vertexData[8] = x;
vertexData[9] = y;

vertexData[10] = x + width;
vertexData[11] = y;


glBindBuffer(GL_ARRAY_BUFFER, _vboID);

glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_DYNAMIC_DRAW);

glBindBuffer(GL_ARRAY_BUFFER, 0);

这里是draw方法

void Sprite::draw() 

glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glEnableVertexAttribArray(0);


glVertexAttribPointer(0, 2, 0x1406, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);

glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);

我显然错过了一些非常明显的概念,因为我似乎无法在任何地方找到解决方案,我的问题是当我按下 s 时,我的矩形不会向下移动。 (因为我使用的是 WASD 移动)这是输入处理程序的代码

void InputHandler::handleInput(SDL_Event* event, Sprite *toMove) 

switch (event->type) 

case SDL_KEYDOWN:

    if (event->key.keysym.sym == SDLK_a) 
        toMove->setMovementBooleans(MovementTuples::LEFT, true);
        std::cout << "Set left ";
    
    if (event->key.keysym.sym == SDLK_d) 
        toMove->setMovementBooleans(MovementTuples::RIGHT, true);
        std::cout << "Set right ";
    
    if (event->key.keysym.sym == SDLK_s) 
        toMove->setMovementBooleans(MovementTuples::DOWN, true);
        std::cout << "Set down ";
    
    if (event->key.keysym.sym == SDLK_w) 
        toMove->setMovementBooleans(MovementTuples::UP, true);
        std::cout << "Set up ";
    
    break;



case SDL_KEYUP:

    if (event->key.keysym.sym == SDLK_a) 
        toMove->setMovementBooleans(MovementTuples::LEFT, false);
    
    if (event->key.keysym.sym == SDLK_d) 
        toMove->setMovementBooleans(MovementTuples::RIGHT, false);
    
    if (event->key.keysym.sym == SDLK_s) 
        toMove->setMovementBooleans(MovementTuples::DOWN, false);
    
    if (event->key.keysym.sym == SDLK_w) 
        toMove->setMovementBooleans(MovementTuples::UP, false);
    



此方法是静态访问的。这是在我的精灵 cpp 中单击 s 键时向下移动精灵的代码

void Sprite::update() 

if (movementBooleans[MovementTuples::DOWN]) 
    _x -= .1f;
    init(_x, _y, _width, _height);
    std::cout << "Moved to: (" << _x << ", " << _y  << ")"<<  std::endl;

当我向下单击时,我的矩形完全消失,_x 值保持不变。我测试了输入,它完全按照我想要的方式工作,我似乎无法让矩形移动。谁能帮我弄清楚如何移动我的 Sprite,谢谢!

【问题讨论】:

【参考方案1】:

我正在使用 OpenGL。虽然我没有使用 SDL,因为我使用的是用户实现的输入处理程序,但概念应该是相同的。

我当前使用的游戏引擎有一个游戏类对象,它继承自一个引擎类对象,其中引擎类是一个单例对象。由于这个游戏引擎的结构; Engine 类需要 Game 类来实现虚拟键盘输入功能。这是原型的样子

引擎类

class Engine : public  Singleton 
protected:
    // Protected Members
private:
    // Private Members

public:
    // virtual destructor & public functions
protected:
    // explicit protected Constructor & protected functions
private:
    bool messageHandler( unsigned uMsg, WPARAM wParam, LPARAM lParam );

    virtual void keyboardInput( unsigned vkCode, bool isPressed ) = 0;

    // Other Private Functions

; // Engine

游戏类

class Game sealed : public Engine 
private:
    // private members here

public:
    // Constructor and virtual Destructor

private:
   virtual void keyBoardInput( unsigned vkCode, bool isPressed ) override;
; // Game

这里是 Windows 的 messageHandler() 函数

// ----------------------------------------------------------------------------
// messageHandler()
bool Engine::messageHandler( unsigned uMsg, WPARAM wParam, LPARAM lParam ) 
    switch( uMsg ) 
        case WM_CLOSE: 
            PostQuitMessage( 0 );
            return true;
        
        case WM_SYSKEYDOWN : 
            if ( ( VK_MENU == wParam ) && ( lParam & 0x1000000 ) ) 
                wParam = VK_RMENU; // Alt Key
            
            // Fall Through
        
        case WM_KEYDOWN: 
            if ( ( VK_RETURN == wParam ) && ( lParam & 0x1000000 ) ) 
                wParam = VK_SEPARATOR;

             else if ( ( VK_CONTROL == wParam ) && ( lParam & 0x1000000 ) ) 
                wParam = VK_RCONTROL;
            

            if ( 0 == ( lParam & 0x40000000 ) )  // Supress Key Repeats
                keyboardInput( wParam, true );
            
            return true;
        
        case WM_SYSKEYUP: 
            if ( ( VK_MENU == wParam ) && ( lParam & 0x1000000 ) ) 
                wParam = VK_RMENU; // Alt Key
            
            // Fall Through
        
        case WM_KEYUP: 
            if ( ( VK_RETURN == wParam ) && ( lParam & 0x1000000 ) ) 
                wParam = VK_SEPARATOR;

             else if ( ( VK_CONTROL == wParam ) && ( lParam & 0x1000000 ) ) 
                wParam = VK_RCONTROL;
            

            keyboardInput( wParam, false );

            return true;
        
        case WM_MOUSEMOVE: 
            // Mouse Motion Detected, Coordinates Are WRT Window Therefore
            // 0,0 Is The Coordinate Of The Top Left Corner Of The Window
            m_mouseState.position = glm::ivec2( LOWORD( lParam ), HIWORD( lParam ) );
            mouseInput();
            return true;
        
        case WM_LBUTTONDOWN: 
            m_mouseState.isButtonPressed[MOUSE_LEFT_BUTTON] = true;
            mouseInput();
            return true;
        
        case WM_LBUTTONUP: 
            m_mouseState.isButtonPressed[MOUSE_LEFT_BUTTON] = false;
            mouseInput();
            return true;
        
        case WM_RBUTTONDOWN: 
            m_mouseState.isButtonPressed[MOUSE_RIGHT_BUTTON] = true;
            mouseInput();
            return true;
        
        case WM_RBUTTONUP: 
            m_mouseState.isButtonPressed[MOUSE_RIGHT_BUTTON] = false;
            mouseInput();
            return true;
        
        case WM_MBUTTONDOWN: 
            m_mouseState.isButtonPressed[MOUSE_MIDDLE_BUTTON] = true;
            mouseInput();
            return true;
        
        case WM_MBUTTONUP: 
            m_mouseState.isButtonPressed[MOUSE_MIDDLE_BUTTON] = false;
            mouseInput();
            return true;
        
        case WM_MOUSEWHEEL: 
            // Mouse Wheel Moved
            // wParam Contains How Much It Was Moved
            return true;
        
        default: 
            return false; // Did Not Handle The Message
        
    
 // messageHandler

最后这是keyboardInput()函数

// ----------------------------------------------------------------------------
// keyboardInput()
void Game::keyboardInput( unsigned vkCode, bool isPressed ) 
    std::ostringstream strStream;
    strStream << "Key 0" << std::hex << vkCode << " was " << ( isPressed ? "Pressed" : "Released" );
    Logger::log( strStream );

    if ( VK_ESCAPE == vkCode ) 
        PostQuitMessage( 0 );
    

    static bool keyPressed[256] =  0 ;
    if ( vkCode < 256 ) 
        keyPressed[vkCode] = isPressed;
    

    if ( isPressed ) 
        return;
    

    switch ( vkCode ) 
        case VK_DOWN:
        case 'S' : 
            // do logic here
            break;
        
        case VK_LEFT:
        case 'A' : 
            // do logic here
            break;
        
        case VK_RIGHT:
        case 'D' : 
            // do logic here
            break;
        
        case VK_UP: 
        case 'W' : 
            // do logic here
            break;
        
    

 // handleKeyboard

当您仔细查看代码时;我不是在轮询以查看何时按下该键,而是在查询或等待查看何时释放该键。这种逻辑的原因是这样的;当你按下一个键时,它可以重复按下而不会达到向上状态。这就像在任何文本编辑器中按住相同的键,您将看到相同的键显示在屏幕上。解决此问题或避免此行为;我们做相反的逻辑。

我们查找密钥的释放时间。该键只能释放一次,必须再次按下才能再次进入释放状态。这样,您每次按键都会发生一次程序动作,然后释放。

现在,如果您注意到我们确实会检查该键是否处于按下状态,如果是则我们返回;这很重要,因为如果您不这样做,您的程序将自动执行消息处理程序中的操作,因为默认情况下按键状态已经处于释放状态。因此,这里发生的情况是在调用Game::keyboardInput()Engine::messageHandler() 函数时渲染帧或更新帧期间,如果它检测到按键已将其状态更改为true,则它会从函数返回并再次连续调用直到您释放密钥;一旦键被释放并且状态变回释放它然后跳过 if 语句并直接转到您正在处理的键的 switch 语句。

【讨论】:

+1 表示努力,但这实际上与我的问题无关,是的,我明白你在做什么,但我的问题是如何移动我的精灵......我的输入有效。跨度> 根据您向我描述的内容:“..., my problem is when I press s my rectangle does not move down. ...”然后您展示了您的 inputHandler() 的逻辑然后您继续说“When I click down, my rectangle completely disappear and the _x value stays the same; ...”并且行为听起来好像是关键被反复按下。在您的Sprite::update() 中尝试使用从0.1f0.05f 的较小值,而不是修改_x 变量,用_y 替换它。如果你想垂直移动它,增加和减少你的向量的 Y 分量。 您也没有显示调用这些函数的代码。 我希望它像那样连续发射,我把它设置为 0.00001f 并且它做了同样的事情 在我的 messageHandler 中让我执行重复的关键操作,我所要做的就是注释掉这段代码:if ( 0 == ( lParam &amp; 0x40000000 ) ) // Supress Key Repeats keyboardInput( wParam, true ); ,因为这会抑制多个关键操作。【参考方案2】:

如果你想移动 OpenGL 形状,你真的应该做矩阵变换。

我个人使用 GLM 为我完成所有繁重的工作,所以我所要做的就是在转换完成后将矩阵传递给 GLSL。

【讨论】:

以上是关于SDL2 OpenGL C++ 移动使用 VBO 和 FBO 绘制的精灵的主要内容,如果未能解决你的问题,请参考以下文章

OpenGL 深度测试不工作 (GLEW/SDL2)

C++ 和 OpenGL,VBO 顶点与骨骼信息交错

OpenGL 不绘制到 SDL2 窗口

在 openGL + SDL 2.0 中绘制四面体

将 OpenGL 绘制列表转换为顶点数组或 VBO

OpenGL C++ SDL 2D 阴影