我正在尝试为我们公司的软件加载各种图像格式以在 OpenGL 中使用 alpha 混合显示,但我似乎对此并不走运。我正在尝试像这样加载位图:
private Bitmap LoadBitmap(string filename)
{
Bitmap original = new Bitmap(filename);
Bitmap toReturn = new Bitmap(original.Width, original.Height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(toReturn))
{
gr.DrawImage(original, new Rectangle(0, 0, toReturn.Width, toReturn.Height));
}
original.Dispose();
return toReturn;
}
将其加载到这样的纹理中:
private uint[] GetTexture(Bitmap convertToTexture)
{
// Setup return value
uint[] toReturn = null;
// Make sure the device and the bitmap exists
if (convertToTexture != null)
{
// Dispose the imagery first
DisposeTexture();
mImageryTexture = new uint[1];
// Setup the bitmap
Rectangle rect = new Rectangle(Point.Empty, convertToTexture.Size);
BitmapData bitmapdata = convertToTexture.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
// Bind the texture
glGenTextures(1, mImageryTexture);
glBindTexture(GL_TEXTURE_2D, mImageryTexture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, (int)GL_RGB8, convertToTexture.Width, convertToTexture.Height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, bitmapdata.Scan0);
// Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Clamp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
// Release the bitmap
convertToTexture.UnlockBits(bitmapdata);
// Set the return value to this imagery
toReturn = mImageryTexture;
}
return toReturn;
}
并使用这样的纹理:
public bool DrawGLScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Test bitmap
Rectangle rect = new Rectangle();
rect.Size = testBitmap.Size;
RenderQuad(testBitmap, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test JPEG
rect.Size = testJpeg.Size;
RenderQuad(testJpeg, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test PNG
rect.Size = testPng.Size;
RenderQuad(testPng, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test GIF
rect.Size = testGif.Size;
RenderQuad(testGif, rect);
RenderLines(rect);
return true;
}
private void RenderQuad(Bitmap image, Rectangle rect)
{
uint[] imageryTexture = GetTexture(image);
// Color all the vertices white with transparency
glEnable(GL_TEXTURE_2D);
glColor4ub(255, 255, 255, 255);
///////////////////////////////////////////////////
// Draw the image of the area.
///////////////////////////////////////////////////
// Fill the path with the background transparent color.
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindTexture(GL_TEXTURE_2D, imageryTexture[0]);
// Building a quad of the image
glBegin(GL_QUADS);
// top left of texture
glTexCoord2f(0, 1);
glVertex2i(rect.Left, rect.Top);
// top right of texture
glTexCoord2f(1, 1);
glVertex2i(rect.Right, rect.Top);
// bottom right of texture
glTexCoord2f(1, 0);
glVertex2i(rect.Right, rect.Bottom);
// bottom left of texture
glTexCoord2f(0, 0);
glVertex2i(rect.Left, rect.Bottom);
glEnd();
}
private void RenderLines(Rectangle rect)
{
// Fill the path with the background transparent color.
// Color all the vertices transparent gray
Color LineColor = Color.Tomato;
glColor4ub(LineColor.R, LineColor.G, LineColor.B, 255);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Building a quad of the image
glBegin(GL_QUADS);
// top left of texture
glVertex2i(rect.Left, rect.Top);
// top right of texture
glVertex2i(rect.Right, rect.Top);
// bottom right of texture
glVertex2i(rect.Right, rect.Bottom);
// bottom left of texture
glVertex2i(rect.Left, rect.Bottom);
glEnd();
}
我究竟做错了什么?我可以加载一些位图文件,但不能加载其他文件。PNG、JPEG 和 GIF 根本不起作用。我得到的只是白色方块(来自顶点颜色)。
这是下面的完整代码。我使用 NeHe 的第 2 课代码作为此代码的基础(因为我无法完整发布公司软件代码):
public class OpenGLForm : Form
{
#region Member Variables
private const string AbsolutePath = @"C:\<path-to-images>\";
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
private Bitmap testBitmap = null;
private Bitmap testJpeg = null;
private Bitmap testPng = null;
private Bitmap testGif = null;
private uint[] mImageryTexture = null;
private static uint _hwnd = 0;
private static uint _hDC = 0;
private static uint _hRC = 0;
private bool _appActive = true;
private bool _done = true;
public bool Done
{
get
{
return _done;
}
set
{
_done = value;
}
}
#endregion
// Lots of OpenGL function and constant declaration here
#region Win32 Interop
// Constant values were found in the "WinUser.h" header file.
public const int WM_ACTIVATEAPP = 0x001C;
public const int WA_ACTIVE = 1;
public const int WA_CLICKACTIVE = 2;
public const int CDS_FULLSCREEN = 0x00000004; // Flag for ChangeDisplaySettings
public const int DISP_CHANGE_SUCCESSFUL = 0; // Return value for ChangeDisplaySettings
// Constant values were found in the "WinGDI.h" header file.
public const int CCHDEVICENAME = 32; // size of a device name string
public const int CCHFORMNAME = 32; // size of a form name string
public const int DM_BITSPERPEL = 0x40000;
public const int DM_PELSWIDTH = 0x80000;
public const int DM_PELSHEIGHT = 0x100000;
public const int BITSPIXEL = 12; // number of bits per pixel
public const uint PFD_DOUBLEBUFFER = 0x00000001; // PIXELFORMATDESCRIPTOR flag
public const uint PFD_DRAW_TO_WINDOW = 0x00000004; // PIXELFORMATDESCRIPTOR flag
public const uint PFD_SUPPORT_OPENGL = 0x00000020; // PIXELFORMATDESCRIPTOR flag
public const uint PFD_TYPE_RGBA = 0; // pixel type
public const uint PFD_MAIN_PLANE = 0; // layer type
[StructLayout(LayoutKind.Sequential)]
public struct PIXELFORMATDESCRIPTOR
{
public ushort nSize;
public ushort nVersion;
public uint dwFlags;
public byte iPixelType;
public byte cColorBits;
public byte cRedBits;
public byte cRedShift;
public byte cGreenBits;
public byte cGreenShift;
public byte cBlueBits;
public byte cBlueShift;
public byte cAlphaBits;
public byte cAlphaShift;
public byte cAccumBits;
public byte cAccumRedBits;
public byte cAccumGreenBits;
public byte cAccumBlueBits;
public byte cAccumAlphaBits;
public byte cDepthBits;
public byte cStencilBits;
public byte cAuxBuffers;
public byte iLayerType;
public byte bReserved;
public uint dwLayerMask;
public uint dwVisibleMask;
public uint dwDamageMask;
}
// by marking the structure with CharSet.Auto, the structure will get marshaled as Unicode characters
// on Unicode platforms, if not the name fields would always get marshaled as arrays of ANSI characters
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class DEVMODE
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHDEVICENAME)]
public char[] dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public DEVMODE_UNION u;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHFORMNAME)]
public char[] dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlagsOrdmNup; // union of dmDisplayFlags and dmNup
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
// modeling a union in C#, each possible struct data type starts at FieldOffset 0
[StructLayout(LayoutKind.Explicit)]
public struct DEVMODE_UNION
{
[FieldOffset(0)]
public short dmOrientation;
[FieldOffset(2)]
public short dmPaperSize;
[FieldOffset(4)]
public short dmPaperLength;
[FieldOffset(6)]
public short dmPaperWidth;
[FieldOffset(8)]
public short dmScale;
[FieldOffset(10)]
public short dmCopies;
[FieldOffset(12)]
public short dmDefaultSource;
[FieldOffset(14)]
public short dmPrintQuality;
[FieldOffset(0)]
public int dmPosition_x;
[FieldOffset(4)]
public int dmPosition_y;
[FieldOffset(0)]
public int dmDisplayOrientation;
[FieldOffset(0)]
public int dmDisplayFixedOutput;
}
#endregion
#region OpenGLSetup
private bool SetupPixelFormat(ref uint hdc)
{
PIXELFORMATDESCRIPTOR pfd = new PIXELFORMATDESCRIPTOR();
ushort pfdSize = (ushort)Marshal.SizeOf(typeof(PIXELFORMATDESCRIPTOR)); // sizeof(PIXELFORMATDESCRIPTOR)
pfd.nSize = pfdSize; // size of pfd
pfd.nVersion = 1; // version number
pfd.dwFlags = (PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER); // flags
pfd.iPixelType = (byte)PFD_TYPE_RGBA; // RGBA type
pfd.cColorBits = (byte)GetDeviceCaps(hdc, BITSPIXEL); // color depth
pfd.cRedBits = 0; // color bits ignored
pfd.cRedShift = 0;
pfd.cGreenBits = 0;
pfd.cGreenShift = 0;
pfd.cBlueBits = 0;
pfd.cBlueShift = 0;
pfd.cAlphaBits = 0; // no alpha buffer
pfd.cAlphaShift = 0; // shift bit ignored
pfd.cAccumBits = 0; // no accumulation buffer
pfd.cAccumRedBits = 0; // accum bits ignored
pfd.cAccumGreenBits = 0;
pfd.cAccumBlueBits = 0;
pfd.cAccumAlphaBits = 0;
pfd.cDepthBits = 32; // 32-bit z-buffer
pfd.cStencilBits = 0; // no stencil buffer
pfd.cAuxBuffers = 0; // no auxiliary buffer
pfd.iLayerType = (byte)PFD_MAIN_PLANE; // main layer
pfd.bReserved = 0; // reserved
pfd.dwLayerMask = 0; // layer masks ignored
pfd.dwVisibleMask = 0;
pfd.dwDamageMask = 0;
int pixelformat = ChoosePixelFormat(hdc, ref pfd);
if (pixelformat == 0) // Did Windows Find A Matching Pixel Format?
{
MessageBox.Show("Can't Find A Suitable PixelFormat.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
if (SetPixelFormat(hdc, pixelformat, ref pfd) == 0) // Are We Able To Set The Pixel Format?
{
MessageBox.Show("Can't Set The PixelFormat.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
return true;
}
private bool InitGL() // All Setup For OpenGL Goes Here
{
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
// Add alpha blending support
glDepthFunc(GL_LEQUAL);
glDisable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
return true; // Initialization Went OK
}
public bool SetupRenderingContext()
{
if (!CreateGLWindow())
{
return false; // initialization failed, quit
}
_hwnd = (uint)((this.Handle).ToInt32());
_hDC = GetDC(_hwnd);
if (_hDC == 0)
{
MessageBox.Show("Can't Create A GL Device Context", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
// not doing the following wglSwapBuffers() on the DC will result in a failure to subsequently create the RC
wglSwapBuffers(_hDC);
if (!SetupPixelFormat(ref _hDC))
{
return false;
}
// create the rendering context and make it current
_hRC = wglCreateContext(_hDC);
if (_hRC == 0) // Are We Able To Get A Rendering Context?
{
MessageBox.Show("Can't Create A GL Rendering Context.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
if (!wglMakeCurrent(_hDC, _hRC)) // Try To Activate The Rendering Context
{
MessageBox.Show("Can't Activate The GL Rendering Context.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
OpenGLForm_Resize(this, new EventArgs()); // Set up the perspective GL screen
return InitGL(); // Initialize Our Newly Created GL Window
}
#endregion
#region FormSetup
private bool CreateGLWindow()
{
Resize += new EventHandler(OpenGLForm_Resize);
TopMost = false;
WindowState = System.Windows.Forms.FormWindowState.Normal;
FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable;
// The cursor is displayed only if the display count is greater than or equal to 0
do
{
}while (ShowCursor(true) < 0);
return true;
}
#endregion
#region Constructor/Destructor
public OpenGLForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
testBitmap = LoadBitmap(AbsolutePath + "bitmap.bmp");
testJpeg = LoadBitmap(AbsolutePath + "jpeg.jpg");
testPng = LoadBitmap(AbsolutePath + "png.png");
testGif = LoadBitmap(AbsolutePath + "gif.gif");
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (components != null)
{
components.Dispose();
}
if (_hRC != 0) // Do We Have A Rendering Context?
{
if (!wglMakeCurrent(0, 0)) // Are We Able To Release The DC And RC Contexts?
{
MessageBox.Show("Release Of DC And RC Failed.", "Shutdown Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
if (!wglDeleteContext(_hRC)) // Are We Able To Delete The RC?
{
MessageBox.Show("Release Rendering Context Failed.", "Shutdown Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
if (_hDC != 0 && ReleaseDC(_hwnd, _hDC) == 0) // Are We Able To Release The DC
{
MessageBox.Show("Release Device Context Failed.", "Shutdown Error", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
base.Dispose(disposing);
}
private Bitmap LoadBitmap(string filename)
{
Bitmap original = new Bitmap(filename);
Bitmap toReturn = new Bitmap(original.Width, original.Height, PixelFormat.Format24bppRgb);
using (Graphics gr = Graphics.FromImage(toReturn))
{
gr.DrawImage(original, new Rectangle(0, 0, toReturn.Width, toReturn.Height));
}
original.Dispose();
return toReturn;
}
#endregion
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
//
// OpenGLForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(632, 453);
this.KeyPreview = true;
this.Name = "OpenGLForm";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "\"NeHe\'s First Polygon Tutorial\"";
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.OpenGLForm_KeyUp);
}
#endregion
#region Events
protected override void WndProc(ref System.Windows.Forms.Message m)
{
// Listen for operating system messages.
switch (m.Msg)
{
// The WM_ACTIVATEAPP message occurs when the application
// becomes the active application or becomes inactive.
case WM_ACTIVATEAPP:
{
// The WParam value identifies what is occurring.
_appActive = ((int)m.WParam == WA_ACTIVE || (int)m.WParam == WA_CLICKACTIVE);
// Invalidate to get new scene painted.
Invalidate();
break;
}
default:
{
break;
}
}
base.WndProc(ref m);
}
/*!
This will stop the display from flickering on Paint event
*/
protected override void OnPaintBackground(PaintEventArgs e)
{
}
protected override void OnPaint(PaintEventArgs e)
{
// make sure the app is active
if (_appActive)
{
DrawGLScene();
wglSwapBuffers(_hDC);
Invalidate();
}
}
private void OpenGLForm_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Escape:
{
Close();
break;
}
default:
{
break;
}
}
}
private void OpenGLForm_Resize(object sender, EventArgs e) // Resize And Initialize The GL Window
{
int width = ClientRectangle.Width;
int height = ClientRectangle.Height;
if (height == 0) // Prevent A Divide By Zero By
{
height = 1; // Making Height Equal One
}
// Switch to projection mode
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Change to ortho-graphic camera, with top left as (0, 0) coordinate,
// and bottom right as the control size
gluOrtho2D(0, ClientRectangle.Width, ClientRectangle.Height, 0);
glViewport(0, 0, ClientRectangle.Width, ClientRectangle.Height);
// Switch back to model view
glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); // Reset The Modelview Matrix
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
while (true)
{
OpenGLForm form = new OpenGLForm(); // create the form
if (!form.SetupRenderingContext()) // setup form and OpenGL
{
break; // initialization failed, quit
}
Application.Run(form);
if (form.Done) // Was There A Quit Received?
{
form.DisposeTexture();
break;
}
}
}
public bool DrawGLScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
// Test bitmap
Rectangle rect = new Rectangle();
rect.Size = testBitmap.Size;
RenderQuad(testBitmap, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test JPEG
rect.Size = testJpeg.Size;
RenderQuad(testJpeg, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test PNG
rect.Size = testPng.Size;
RenderQuad(testPng, rect);
RenderLines(rect);
rect.X += rect.Width;
// Test GIF
rect.Size = testGif.Size;
RenderQuad(testGif, rect);
RenderLines(rect);
return true;
}
private void RenderQuad(Bitmap image, Rectangle rect)
{
uint[] imageryTexture = GetTexture(image);
// Color all the vertices white with transparency
glEnable(GL_TEXTURE_2D);
glColor4ub(255, 255, 255, 255);
///////////////////////////////////////////////////
// Draw the image of the area.
///////////////////////////////////////////////////
// Fill the path with the background transparent color.
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glBindTexture(GL_TEXTURE_2D, imageryTexture[0]);
// Building a quad of the image
glBegin(GL_QUADS);
// top left of texture
glTexCoord2f(0, 1);
glVertex2i(rect.Left, rect.Top);
// top right of texture
glTexCoord2f(1, 1);
glVertex2i(rect.Right, rect.Top);
// bottom right of texture
glTexCoord2f(1, 0);
glVertex2i(rect.Right, rect.Bottom);
// bottom left of texture
glTexCoord2f(0, 0);
glVertex2i(rect.Left, rect.Bottom);
glEnd();
}
private void RenderLines(Rectangle rect)
{
// Fill the path with the background transparent color.
// Color all the vertices transparent gray
Color LineColor = Color.Tomato;
glColor4ub(LineColor.R, LineColor.G, LineColor.B, 255);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
// Building a quad of the image
glBegin(GL_QUADS);
// top left of texture
glVertex2i(rect.Left, rect.Top);
// top right of texture
glVertex2i(rect.Right, rect.Top);
// bottom right of texture
glVertex2i(rect.Right, rect.Bottom);
// bottom left of texture
glVertex2i(rect.Left, rect.Bottom);
glEnd();
}
// Creates a texture from a given bitmap.
private uint[] GetTexture(Bitmap convertToTexture)
{
// Setup return value
uint[] toReturn = null;
// Make sure the device and the bitmap exists
if (convertToTexture != null)
{
// Dispose the imagery first
DisposeTexture();
mImageryTexture = new uint[1];
// Setup the bitmap
Rectangle rect = new Rectangle(Point.Empty, convertToTexture.Size);
BitmapData bitmapdata = convertToTexture.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
// Bind the texture
glGenTextures(1, mImageryTexture);
glBindTexture(GL_TEXTURE_2D, mImageryTexture[0]);
glTexImage2D(GL_TEXTURE_2D, 0, (int)GL_RGB8, convertToTexture.Width, convertToTexture.Height, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, bitmapdata.Scan0);
// Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// Clamp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
// Release the bitmap
convertToTexture.UnlockBits(bitmapdata);
// Set the return value to this imagery
toReturn = mImageryTexture;
}
return toReturn;
}
// Disposes the currently held texture.
public void DisposeTexture()
{
if (mImageryTexture != null)
{
glDeleteTextures(1, mImageryTexture);
mImageryTexture = null;
}
}
}