【问题标题】:GDI is not rendering, what am I doing wrong?GDI 不渲染,我做错了什么?
【发布时间】:2012-09-27 16:13:19
【问题描述】:

我正在尝试使用 GDI 为图形制作一个基本框架,以制作一些迷你游戏。 但是 GDI 根本没有渲染任何东西,我只是得到一个黑色的客户区,我不知道我在做什么错。

以下代码在不断运行的游戏循环中:

//render double buffered with GDI
            HDC frontBuffer = GetDC(m_hMainWnd);

            HDC backBuffer;
            HBITMAP bitmap;
            HBITMAP oldBitmap;

            backBuffer = CreateCompatibleDC(frontBuffer);
            bitmap = CreateCompatibleBitmap(frontBuffer, m_ClientWidth, m_ClientHeight);
            oldBitmap = (HBITMAP)SelectObject(backBuffer, bitmap);

            GDI->StartDrawing(backBuffer, m_ClientWidth, m_ClientHeight); //this basically selects pens and brushes etc
            Render(dRenderTime); //here I render some stuff
            GDI->StopDrawing(backBuffer); //selects old pens and brushes back

         //blit backbuffer to frontbuffer
            BitBlt(frontBuffer, 0, 0, m_ClientWidth, m_ClientHeight, backBuffer, 0, 0, SRCCOPY);

            SelectObject(backBuffer, oldBitmap);
            DeleteObject(bitmap);
            DeleteDC(backBuffer);

            ReleaseDC(m_hMainWnd, frontBuffer);
        }

我在这里做错了什么? 对不起,如果这是一个愚蠢的错误,我对windows编程一点也不擅长。

编辑:根据要求添加代码:

gdi.h

    #pragma once

#include <Windows.h>
#include "Macros.h"
#include "Transform.h"
#include "Text.h"

#define GDI gdi::getInstance()

class gdi
{
private:
  HPEN m_OldPen;
  HPEN m_Pen;
  HBRUSH m_OldBrush;
  HBRUSH m_Brush;
  HDC m_hdc;

  int m_DcWidth;
  int m_DcHeight;

  COLORREF m_Color;
  int m_LineWidth;

  DBlib::float3x3 m_Transform;

  gdi();

  void Transform(int& x_out, int& y_out, const DBlib::float2& p) const;
  void Transform_NDC_To_WC(int& x_out, int& y_out, const DBlib::float2& p) const;
  void Transfrom_WC_To_NDC(DBlib::float2& p_out, int x, int y) const;

public:
  ~gdi();

  static gdi* getInstance();

  void StartDrawing(HDC hdc, int dcwidth, int dcheight);
  void StopDrawing(HDC hdc);

  void SetColor(const DBlib::float3& col);
  void SetLineWidth(int width);

  void SetTransform(const DBlib::float3x3& transform);

  void DrawText(const DBlib::float2& p1, const std::tstring& s);
  void DrawLine(const DBlib::float2& p1, const DBlib::float2& p2);
  void DrawPolygon(const DBlib::float2* p, int size);
  void FillPolygon(const DBlib::float2* p, int size);
};

GDI.cpp

#include "gdi.h"

gdi* gdi::getInstance()
{
  static gdi instance;
  return &instance;
}

gdi::gdi()
{
  m_hdc = NULL;
  m_OldPen = NULL;
  m_OldBrush = NULL;
  m_LineWidth = 1;
  m_Color = RGB(0,0,0);
  m_DcWidth = -1;
  m_DcHeight = -1;
  m_Transform.set_identity();

  m_Pen = CreatePen(PS_SOLID,1,RGB(0,0,0));
  m_Brush = CreateSolidBrush(RGB(255,255,255));
}

gdi::~gdi()
{
    if(m_Pen) DeleteObject(m_Pen);
    if(m_Brush) DeleteObject(m_Brush);
}

void gdi::StartDrawing(HDC hdc, int dcwidth, int dcheight)
{
  m_hdc = hdc;
  m_DcWidth = dcwidth;
  m_DcHeight = dcheight;
  m_OldPen = (HPEN)SelectObject(hdc, m_Pen);
  m_OldBrush = (HBRUSH)SelectObject(hdc, m_Brush);
}

void gdi::StopDrawing(HDC hdc)
{
  SelectObject(hdc, m_OldPen);
  SelectObject(hdc, m_OldBrush);

  m_hdc = NULL;
  m_DcWidth = -1;
  m_DcHeight = -1;
  m_OldPen = NULL;
  m_OldBrush = NULL;
}

void gdi::SetColor(const DBlib::float3& col)
{
    int r = static_cast<int>(DBlib::clamp(col.x*255.0f, 0.0f, 255.0f));
    int g = static_cast<int>(DBlib::clamp(col.y*255.0f, 0.0f, 255.0f));
    int b = static_cast<int>(DBlib::clamp(col.z*255.0f, 0.0f, 255.0f));
    m_Color = RGB(r,g,b);

    SetTextColor(m_hdc, m_Color);

    if(m_Pen) DeleteObject(m_Pen);
    if(m_Brush) DeleteObject(m_Brush);
    m_Pen= CreatePen(PS_SOLID, m_LineWidth, m_Color);
    m_Brush= CreateSolidBrush(m_Color);
}

void gdi::SetLineWidth(int width)
{
    m_LineWidth = width;

    if(m_Pen) DeleteObject(m_Pen);
    m_Pen= CreatePen(PS_SOLID, m_LineWidth, m_Color);
}

void gdi::SetTransform(const DBlib::float3x3& transform)
{
    m_Transform = transform;
}

void gdi::Transform_NDC_To_WC(int& x_out, int& y_out, const DBlib::float2& p) const
{
    x_out = static_cast<int>((p.x+1.0f)*(static_cast<float>(m_DcWidth)/2.0f));
    y_out = m_DcHeight - static_cast<int>((p.y+1.0f)*(static_cast<float>(m_DcHeight)/2.0f));
}

void gdi::Transfrom_WC_To_NDC(DBlib::float2& p_out, int x, int y) const
{
    p_out.x = static_cast<float>(x)*2.0f/static_cast<float>(m_DcWidth) - 1.0f;
    p_out.y = -(static_cast<float>(y)*2.0f/static_cast<float>(m_DcHeight) - 1.0f);
}

void gdi::Transform(int& x_out, int& y_out, const DBlib::float2& p) const
{
    Transform_NDC_To_WC(x_out, y_out, p*m_Transform);
}

void gdi::DrawText(const DBlib::float2& pos, const std::tstring& s)
{
    int x,y;
    Transform(x,y,pos);
    TextOut(m_hdc, x, y, s.c_str(), (int)s.size());
}

void gdi::DrawLine(const DBlib::float2& p1, const DBlib::float2& p2)
{
    int x1,y1,x2,y2;
    Transform(x1,y1,p1);
    Transform(x2,y2,p2);

    MoveToEx(m_hdc, x1, y1, NULL);
    LineTo(m_hdc, x2, y2);
}

void gdi::DrawPolygon(const DBlib::float2* p, int size)
{
    int* x = new int[size];
    int* y = new int[size];

    for(int i=0; i<size; ++i) {
        Transform(x[i],y[i],*(p+i));
    }
    for(int i=0; i<size; ++i) {
        MoveToEx(m_hdc, x[i], y[i], NULL);
        LineTo(m_hdc, x[(i+1)%size], y[(i+1)%size]);
    }

    delete[] x;
    delete[] y;
}

void gdi::FillPolygon(const DBlib::float2* p, int size)
{
    int* x = new int[size];
    int* y = new int[size];
    POINT* pts = new POINT[size];

    for(int i=0; i<size; ++i) {
        Transform(x[i],y[i],*(p+i));
        pts[i].x = static_cast<LONG>(x[i]);
        pts[i].y = static_cast<LONG>(y[i]);
    }

    Polygon(m_hdc, pts, size);

    delete[] x;
    delete[] y;
    delete[] pts;
}

App.cpp - 渲染方法定义

void App::Render(float dTime)
{
    GDI->SetColor(DBlib::float3(1.0f,1.0f,1.0f));
    GDI->SetLineWidth(50);
    GDI->DrawLine(DBlib::float2(-1.0f,-1.0f), DBlib::float2(1.0f,1.0f));
}

【问题讨论】:

  • 您的代码中的GDI 是什么?我认为这是您为包装渲染逻辑而制作的一个类,但是您需要展示它以供我们帮助。您的代码(快速浏览后)看起来不错,但您没有显示重要的部分。为什么Render 不是GDI 的一部分?
  • 另一件事是——你不应该一直在你的游戏循环中拥有整个渲染逻辑——你只需要在发生变化时渲染场景,然后你只需要渲染部分那改变了。渲染非常昂贵,如果您一直渲染场景,它将占用您游戏的大量周期。需要时将场景渲染到位图上,然后将该位图blit到屏幕上。
  • GDI 是一个单例类,具有一些方便的实用功能,例如 DrawLine、SetColor 等。Render() 基本上是 App 类的一个方法(这是游戏的起点),使用 GDI 命令来渲染东西。我没有添加代码,因为我试图将其保持在最低限度,并且我认为我的错误出在我发布的代码中。不过我现在会添加它。
  • 这对于游戏循环来说是正常的......无论如何,每一帧都会发生变化,除非你要玩一种不需要重绘每一帧的特定类型的游戏
  • 我对如何优化渲染非常熟悉,我之前用DirectX做过一些游戏。但我只想用 GDI 创建一些基本的东西。性能不会成为问题。

标签: winapi visual-c++ gdi


【解决方案1】:

这就是我以前进行 GDI 渲染的方式(从 VB6 翻译的旧生产代码)。这是对WM_PAINT 的响应,当您(在您的游戏循环中)或 Windows 使您的部分窗口无效时发送。

PAINTSTRUCT stPaintStruct;
HDC hPaintDC = BeginPaint(hWnd, &stPaintStruct);

if (hPaintDC != HANDLE_NULL)
{
    // establish clipping rect using stPaintStruct.rcPaint

    if (!m_bRendering)
    {
        m_bRendering = TRUE;

        // Render() knows the output bitmap and all the content to render
        // and makes sure resources are allocated / discarded during
        // rendering.
        Render ();

        m_bRendering = FALSE;
    }

    EndPaint (hWnd, &stPaintStruct);
    return (TRUE);
}

您通过使用InvalidateRect 使您的窗口无效来触发WM_PAINT

InvalidateRect ( hWnd, NULL, FALSE );

这是关于客户绘图的MSDN article。其中一些慢慢地回到我身边,谢谢你的提问。 :) 我好几年没做过 GDI了。

【讨论】:

  • 谢谢,我会这样尝试,看看是否有效,给我一些时间:)
  • 没问题 - 请记住,这是我从 5-6 年未见的旧 VB6 代码翻译而来的。 :) 注意错误。
  • 不,这对我不起作用,我在游戏循环中调用 InvalidateRect(),并使用 WM_PAINT 中的旧代码并添加了开始和结束绘制,但它仍然是我得到的相同黑屏...嗯...
  • 这可能是一些愚蠢的小错误导致整个事情发生故障。我讨厌那些错误!明天我会再看一下,用 DirectX 可以更快地做到这一点,但我想这些天我也必须学习 GDI,对吧? :p 感谢您花时间帮助我!
  • 如果您已经了解 DirectX,那么使用它会好得多。我不会将 GDI 用于游戏编程——性能会很糟糕。我花了很多时间在 GDI 中优化复杂矢量图像的绘制,但我认为你的游戏循环要紧凑得多。 GDI 适合商业应用程序,但游戏……嗯,你会折磨自己的。 :)
猜你喜欢
  • 1970-01-01
  • 2016-12-30
  • 2016-12-01
  • 1970-01-01
  • 2011-06-01
  • 1970-01-01
  • 2021-09-06
  • 2012-01-09
  • 2017-09-29
相关资源
最近更新 更多