LoginSignup
2
5

More than 5 years have passed since last update.

OpenTKでテクスチャーを使ってテキストを描画する

Posted at

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();
        }

【描画結果】
20190317_IvyFEM DrawStringTest.jpg

4. まとめ

OpenTKでDrawString()してみました。テクスチャーを使えばテキストも描画できることがわかりました。

2
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
5