#1. はじめに
OpenTKでテキストを描画するには、まずテキストのビットマップを作成し、それをテクスチャーとして貼りつける方法がよさそうです。
OpenTK.GLControlで使用できるWPF版DrawString()
を実装してみました。
#2. DrawString()
public static void DrawString(string str, double fontSize)
{
//var window = System.Windows.Application.Current.MainWindow;
double[] glDoubleColor = new double[4];
GL.GetDouble(GetPName.CurrentColor, glDoubleColor);
var glColor = new System.Windows.Media.Color();
glColor.R = (byte)(glDoubleColor[0] * 255);
glColor.G = (byte)(glDoubleColor[1] * 255);
glColor.B = (byte)(glDoubleColor[2] * 255);
glColor.A = (byte)(glDoubleColor[3] * 255); //255;
System.Windows.Media.Brush foreground = new System.Windows.Media.SolidColorBrush(glColor);
//System.Windows.Media.Brush foreground = window.Foreground;
var text = new System.Windows.Media.FormattedText(
str,
new System.Globalization.CultureInfo("en-us"),
System.Windows.FlowDirection.LeftToRight,
new System.Windows.Media.Typeface(
window.FontFamily,
System.Windows.FontStyles.Normal,
System.Windows.FontWeights.Normal,
new System.Windows.FontStretch()),
fontSize, //window.FontSize,
foreground);
var drawingVisual = new System.Windows.Media.DrawingVisual();
using (System.Windows.Media.DrawingContext drawingContext = drawingVisual.RenderOpen())
{
drawingContext.DrawText(text, new System.Windows.Point(0, 0));
}
System.Windows.Media.Imaging.RenderTargetBitmap bmp = null;
{
int width = (int)text.Width;
int height = (int)text.Height;
int dpiX = 96;
int dpiY = 96;
bmp = new System.Windows.Media.Imaging.RenderTargetBitmap(
width, height, dpiX, dpiY, System.Windows.Media.PixelFormats.Pbgra32);
}
bmp.Render(drawingVisual);
int bmpWidth = bmp.PixelWidth;
int bmpHeight = bmp.PixelHeight;
int stride = bmpWidth * 4;
byte[] tmpbits = new byte[stride * bmpHeight];
var rectangle = new System.Windows.Int32Rect(0, 0, bmpWidth, bmpHeight);
bmp.CopyPixels(rectangle, tmpbits, stride, 0);
// 上下反転する
byte[] bits = new byte[stride * bmpHeight];
for (int h = 0; h < bmpHeight; h++)
{
for (int w = 0; w < stride; w++)
{
bits[h * stride + w] = tmpbits[(bmpHeight - 1 - h) * stride + w];
}
}
// check
//var png = new System.Windows.Media.Imaging.PngBitmapEncoder();
//png.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(bmp));
//using (var fs = new FileStream("1.png", FileMode.Create))
//{
// png.Save(fs);
//}
bool isTexture = GL.IsEnabled(EnableCap.Texture2D);
bool isBlend = GL.IsEnabled(EnableCap.Blend);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
int texture = GL.GenTexture();
GL.BindTexture(TextureTarget.Texture2D, texture);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter,
(int)TextureMinFilter.Linear);
GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter,
(int)TextureMagFilter.Linear);
GL.TexImage2D(TextureTarget.Texture2D, 0,
PixelInternalFormat.Rgba,
bmpWidth, bmpHeight, 0,
PixelFormat.Bgra, PixelType.UnsignedByte, bits);
GL.PushMatrix();
OpenTK.Matrix4d m;
GL.GetDouble(GetPName.ModelviewMatrix, out m);
GL.Scale(1.0 / m.M11, 1.0 / m.M22, 1.0 / m.M33);
GL.Scale(FontScale, FontScale, 1.0); // TODO: これを計算で求める必要がある
GL.Begin(PrimitiveType.Quads);
GL.TexCoord2(0, 0);
GL.Vertex2(0, 0);
GL.TexCoord2(1, 0);
GL.Vertex2(bmpWidth, 0);
GL.TexCoord2(1, 1);
GL.Vertex2(bmpWidth, bmpHeight);
GL.TexCoord2(0, 1);
GL.Vertex2(0, bmpHeight);
GL.End();
GL.PopMatrix();
if (!isTexture)
{
GL.Disable(EnableCap.Texture2D);
}
if (!isBlend)
{
GL.Disable(EnableCap.Blend);
}
}
描画領域は、(-asp, -1)~(+asp, 1) (asp = ウィンドウの幅 / ウィンドウの高さ)に規格化しています。
#3. 使用例
public void DrawStringTest(Camera Camera, OpenTK.GLControl glControl)
{
// ウィンドウの幅、高さの取得
int[] viewport = new int[4];
GL.GetInteger(GetPName.Viewport, viewport);
int winW = viewport[2];
int winH = viewport[3];
int fontSize = 18;
fontSize = (int)(fontSize * winW / (double)400);
GL.ClearColor(Color4.White);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.PolygonOffsetFill);
GL.PolygonOffset(1.1f, 4.0f);
GL.MatrixMode(MatrixMode.Modelview);
GL.LoadIdentity();
Camera.Fit(new BoundingBox3D(0, winW, 0, winH, 0, 0));
OpenGLUtils.SetModelViewTransform(Camera);
double asp = (winW + 1.0) / (winH + 1.0);
GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix();
GL.LoadIdentity();
GL.Ortho(-asp, asp, -1.0, 1.0, -1.0, 1.0);
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadIdentity();
string text = "DrawStringテスト";
int n = 20;
for (int i = 0; i < n; i++)
{
double xx = -asp + asp * 2.0 * i / (double)n;
double yy = 1.0 - 2.0 * (i + 1) / (double)n;
OpenTK.Vector2d drawpp = new OpenTK.Vector2d(xx, yy);
GL.Translate(drawpp.X, drawpp.Y, 1.0);
double ratio = 1.0 - i / (double)n;
GL.Color3(1.0 * ratio, 0.5 * ratio, 0.5 * ratio);
OpenGLUtils.DrawString(text, fontSize);
GL.Translate(-drawpp.X, -drawpp.Y, -1.0);
}
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Modelview);
GL.PopMatrix();
glControl.SwapBuffers();
}
#4. まとめ
OpenTKでDrawString()
してみました。テクスチャーを使えばテキストも描画できることがわかりました。