图形渲染:
我习惯了win32和Borland C++环境,所以我坚持使用,但在其他环境上的差异大多仅在类名上。首先是一些方法:
- 控制台/文本模式
您可以使用文本图形(ASCII art 我认为是英文)。其中 point 由 character 表示。 Intensity 由或多或少填充的字符组成。通常有一个按强度排序的字符表,如" ..:+*#",并使用它而不是颜色。要打印出一些东西,你可以使用iostream,比如cout << "text" << endl; 或printf from stdio 我想(我已经有十多年没有使用旧式控制台输出了)。
文本模式视频 RAM (VRAM) 从 0B000:0000 开始,如果您有权限,您可以像这样直接访问:
char far *scr = (char far*)0x0B0000000;
scr[0] = 'A'; // Print A to left upper corner
但是在 Windows 上您可以忘记直接访问。
- VGA 图形模式
(仅限 DOS,而不是 Windows;这是对 VGA 硬件的直接访问)。这是一个小例子:
// Turbo C++ for 16-bit real mode DOS
//==============================================================================
char far* scr; // VGA screen
const _sx= 320; // Physical screen size
const _sy= 200;
//==============================================================================
void gfxinit();
void cls();
void pnt(int x,int y,char c);
//==============================================================================
void gfxinit()
{
asm { mov ax,19 // This switches VGA to 320*200*256 color mode (fits inside a single 64 KB segment so no funny stuff is needed)
int 16
}
for (int i=0;i<256;i++) asm { // This overwrites 256 color palette with some BW gradients
mov dx,0x3C8
mov ax,i
out dx,al // Overwrite color al = i
inc dx
shr al,2 // al=al>>2
out dx,al // r,g,b or b,g,r not sure now
out dx,al // All values are 6-bit long, therefore the shr al,2
out dx,al
}
scr=(char far*)0xA0000000; // VRAM start address
}
//==============================================================================
void cls() // This clears the screen with zeros
{
asm { push es
mov ax,0xA000
mov es,ax
mov di,0x0000
sub ax,ax
mov cx,32000
rep stosw
pop es
}
}
//==============================================================================
void pnt(int x,int y,char c) // This draws a single point of color c
{
unsigned int adr;
if (x<_sx)
if (x>=0)
if (y<_sy)
if (y>=0)
{
y=y*_sx;
adr=x+y;
scr[adr]=c;
}
}
//==============================================================================
VESA 访问类似,但您必须处理段交叉和分页。这是一个小的 Turbo C++ 示例:
VESA.h
// Turbo C++, still 16-bit DOS,
// but using VESA calls to set modes instead of VGA registers
//==============================================================================
//=== Globals: =================================================================
//==============================================================================
char far* scr=(char far*)0xA0000000; // VGA/VESA memory pointer
int VESA_page,VESA_pages; // Actual page and total pages
int VESA_xs,VESA_ys,VESA_bpp; // Video mode properties
int VESA_page_xy[64]={-1,-1}; // Starting x,y for each page
const int VESAmodes[]= // Usable video modes table
{
320, 200, 8,0x150,
640, 480, 8,0x101,
800, 600, 8,0x103,
1024, 768, 8,0x105,
1280,1024, 8,0x107,
320, 200,16,0x10E,
640, 480,16,0x111,
800, 600,16,0x114,
1024, 768,16,0x117,
320, 200,32,0x10F,
640, 480,32,0x112,
800, 600,32,0x115,
0,0,0,0
};
//==============================================================================
//=== Headers: =================================================================
//==============================================================================
int VESAmode(int xs,int ys,int bpp); // Set video mode
void VESApage(int page); // Set page
void VESAexit(); // Return to VGA text mode
void VESAcls(); // Clear with 0
void VESApnt(int x,int y,unsigned int c); // Render 8/16 bpp point
void VESApnt32(int x,int y,int r,int g ,int b); // render 32bpp point
//==============================================================================
//=== Graphic: =================================================================
//==============================================================================
int VESAmode(int xs,int ys,int bpp)
{
int i,mode,x,y;
unsigned int adr0,adr,dx,dy;
// find video mode
for (i=0;VESAmodes[i];i+=4)
if (VESAmodes[i+0]==xs)
if (VESAmodes[i+1]==ys)
if (VESAmodes[i+2]==bpp)
break;
if (!VESAmodes[i]) return 0;
mode=VESAmodes[i+3];
VESA_xs=xs;
VESA_ys=ys;
VESA_bpp=bpp;
// Compute start x,y for each page>0
dx=bpp>>3;
dy=xs*dx;
VESA_pages=1;
for (adr=i=x=y=0;y<VESA_ys;y++)
{
adr0=adr;
adr+=dy;
if (adr0>adr)
{
while (adr>0) { adr-=dx; x--; }
while (x<0) { x+=VESA_xs; y--; }
VESA_page_xy[i]=x; i++;
VESA_page_xy[i]=y+1; i++;
VESA_pages++;
}
}
VESA_page_xy[i]=-1; i++;
VESA_page_xy[i]=-1; i++;
// Set video mode
asm {
mov bx,mode
mov ax,0x4F02
int 16
}
VESApage(0);
/*
// Set palette to grayscale
if (VESAbpp==8)
for (int i=0;i<256;i++) asm {
mov dx,0x3C8
mov ax,i
out dx,al
inc dx
shr al,2
out dx,al
out dx,al
out dx,al
}
*/
return 1;
}
//==============================================================================
void VESApage(int page)
{
int p=page;
asm {
mov dx,p
mov bx,0
mov ax,0x4f05
int 16
}
VESA_page=page;
}
//==============================================================================
void VESAexit()
{
asm {
// Wait for key press
mov ax,0
int 0x16
// VGA 80x25 text mode
mov ax,3
int 16
}
}
//==============================================================================
void VESAcls()
{
int i;
for (i=0;i<VESA_pages;i++)
{
VESApage(i);
asm {
push es
mov ax,0xA000
mov es,ax
mov di,0x0000
mov ax,0
mov cx,32000
rep stosw
pop es
}
}
}
//==============================================================================
void VESApnt(int x,int y,unsigned int c)
{
unsigned int adr;
int p;
// inside screen?
if ((x>=0)&&(x<VESA_xs))
if ((y>=0)&&(y<VESA_ys))
{
// Low 16 bit of address
adr=y;
adr*=VESA_xs;
adr+=x;
adr*=(VESA_bpp>>3);
// Page
for (p=0;VESA_page_xy[p+p+0]>=0;p++)
{
if (VESA_page_xy[p+p+1]>y) break;
if (VESA_page_xy[p+p+1]<y) continue;
if (VESA_page_xy[p+p+0]>x) break;
}
if (p!=VESA_page) VESApage(p);
// Render
scr[adr]=c;
if (VESA_bpp==16)
{
adr++; if (adr==0) VESApage(p+1);
scr[adr]=(c>>8);
}
}
}
//==============================================================================
void VESApnt32(int x,int y,int r,int g ,int b)
{
unsigned int adr;
int p;
// inside screen?
if ((x>=0)&&(x<VESA_xs))
if ((y>=0)&&(y<VESA_ys))
{
// Low 16 bit of address
adr=y;
adr*=VESA_xs;
adr+=x;
adr*=(VESA_bpp>>3);
// Page
for (p=0;VESA_page_xy[p+p+0]>=0;p++)
{
if (VESA_page_xy[p+p+1]>y) break;
if (VESA_page_xy[p+p+1]<y) continue;
if (VESA_page_xy[p+p+0]>x) break;
}
if (p!=VESA_page) VESApage(p);
// Render
scr[adr]=b; adr++; if (adr==0) VESApage(p+1);
scr[adr]=g; adr++; if (adr==0) VESApage(p+1);
scr[adr]=r;
}
}
//==============================================================================
//=== End. =====================================================================
//==============================================================================
main.cpp
//==============================================================================
//=== Includes: ================================================================
//==============================================================================
#include "vesa.h"
//==============================================================================
//=== Main: ====================================================================
//==============================================================================
void main()
{
if (!VESAmode(800,600,32)) return;
VESAcls();
int x,y;
unsigned int c;
for (y=0;y<VESA_ys;y++)
for (x=0;x<VESA_xs;x++)
{
if (VESA_bpp== 8)
{
c=x+y;
VESApnt(x,y,c);
}
if (VESA_bpp==16)
{
c=(x&31)+((y&63)<<5);
VESApnt(x,y,c);
}
if (VESA_bpp==32) VESApnt32(x,y,x,x+y,y);
}
VESAexit();
}
//==============================================================================
//=== End. =====================================================================
//==============================================================================
-
GDI - 可在 Windows 上使用
Canvas 是 Windows 上可视组件的图形子组件。在 Borland 中有一个名为 Canvas 的类 TCanvas。所有窗口也有它,PaintBoxes, Bitmaps, ...。它是 Windows 和您的应用程序之间的 GDI 接口。它有像 Pen, Brush, and Font 这样的子组件,用于线条、填充或文本纸、文本墨水。
Form1->Canvas->Pen->Color=clYellow;
Form1->Canvas->MoveTo(10,10);
Form1->Canvas->LineTo(100,150);
Form1 是我的 VCL 窗口。这段代码画了一条黄线。
GDI 有很多类似Arc, Ellipse, Pixels[][],... 的功能。有关详细信息,请参阅 IDE 的内置帮助。
- GDI 位图
这是一个特殊的对象。它是一个带有 OS 图形句柄(DC 设备上下文)的位图。这允许位图类似于窗口并且可以访问 GDI:
Graphics::TBitmap *bmp=new Graphics::TBitmap;
bmp->Width=100;
bmp->Height=100;
bmp->HandleType=bmDIB; // Allows use of ScanLine
bmp->PixelFormat=pf32bit; // 32-bit - the same as int so we can use int* for pixels pointer
这将创建一个 VCL 位图并将其设置为100x100x32 bit 并具有直接访问权限。现在您可以访问ScanLine 属性。 bmp->Canvas 也存在,所以你也可以做所有 GDI 的事情。
int *p=bmp->ScanLine[10]; // p = pointer to y=10 line of bitmap
p[20]=0; // Draw dot on x=20,y=10 color=0x00000000 which is black
int c = p[15]; // Read pixel x=15,y=10 from bitmap to c
注意将x,y 留在位图中,否则将引发异常。颜色编码取决于pixelformat,通常是0x00RRGGBB 或0x00BBGGRR。我认为这种方法是您的最佳选择。此外,您可以将任何 GDI 对象绘制到任何其他 GDI 对象:
Form1->Canvas->Draw(0, 0, bmp);
这会将您的位图绘制到窗口上,因此您可以实际看到它。
- 图形库
有很多,但最常用的是OpenGL和DirectX。我更喜欢 OpenGL,因为它实现起来更简单(至少对于初学者来说),而且 OpenGL 是跨平台的,而 DirectX 仅适用于 Windows。此外,当我开始编码时,没有任何 DirecX。当我开始使用 OpenGL 时,所有供应商都将它包含在驱动程序中。现在,唯一仍保持最新状态的供应商是 Nvidia 和 ATI (AMD)。它们之间几乎总是存在一些驱动程序问题,但总的来说,Nvidia 更适合 OpenGL(在 DirectX 实现中有错误),ATI(仅限 AMD 版本) 更适合 DirectX(它在OpenGL 实现)。但是对于基本操作,你很好(问题出现在更高级的功能上)。
英特尔、SiS 等供应商已停止在较新的 OpenGL 版本上实施。至少,我不知道有比 OpenGL 3.3 更好的驱动程序。
要开始使用 OpenGL,请参阅OpenGL get Device Context。
我强烈建议先从 GDI + Bitmap 开始。你可以和他们一起做很多事情。我仍在使用它进行非复杂渲染。
如前所述,我对 Borland(VCL 风格)友好,所以如果您使用不同的编译器/IDE,请更改 GDI 对象名称以对应您的环境。我认为画布是相同的,位图是HBitmap,但最好检查您的帮助/文档。至少您知道要搜索什么。
其他平台和东西