18

OpenGL フォントのレンダリングには多くの疑問があり、それらの多くはテクスチャ アトラス (高速だが間違っている) または文字列テクスチャ (固定テキストのみ) で解決されます。

ただし、これらのアプローチは不十分であり、時代遅れのようです (シェーダーを使用してこれをより良く/より速く行うにはどうすればよいでしょうか?)。OpenGL 4.1 の場合、「今日は何を使用する必要がありますか?」という優れた質問があります。

バージョン 4.1 の OpenGL でのテキスト レンダリングの最新技術は何ですか?

では、今日の iOS GL ES 2では何を使用すればよいのでしょうか?

オープンソース (または商用ソリューションでさえ) がないように見えることに失望しています。多くのチームがこの車輪の再発明に数週間の開発時間を費やし、カーニングやスペースなどの方法を徐々に学習していることを私は知っています (うーん) - しかし、「フォント」全体を書き直すよりも良い方法があるに違いありません。最初から?


私が見る限り、これには 2 つの部分があります。

  1. フォントを使用してテキストをレンダリングするにはどうすればよいですか?
  2. 出力をどのように表示しますか?

1(レンダリング方法)については、Appleは「正しい」レンダリング出力を取得する多くの方法を提供していますが、「簡単な」ものはOpenGLをサポートしていません(おそらく他のいくつかはサポートしています-たとえば、CoreText出力をマップする簡単な方法はありますかOpenGL に?)。

2 (表示方法) については、シェーダー、VBO、グリフ テクスチャ、ルックアップ テクスチャ、およびその他のテクニック (たとえば、上記のリンクの OpenGL 4.1 のもの?) があります。

私が知っている 2 つの一般的な OpenGL アプローチを次に示します。

  1. テクスチャ アトラス (すべてのグリフを 1 回レンダリングしてから、共有テクスチャから文字ごとに 1 つのテクスチャ クワッドをレンダリング)
    1. 1980 年代の「ビットマップ フォント」を使用していない限り、これは間違っています (その場合でも、重要なフォントを修正する必要がある場合、テクスチャ アトラスは見た目よりも多くの作業を必要とします)。
    2. (フォントは「グリフのコレクション」ではありません。膨大な量の配置、レイアウト、折り返し、間隔、カーニング、スタイリング、色付け、重み付けなどがあります。テクスチャ アトラスは失敗します)
  2. 固定文字列 (任意の Apple クラスを使用して正しくレンダリングし、バッキング イメージ データのスクリーンショットを作成し、テクスチャとしてアップロードします)
    1. 人間的には、これは高速です。フレーム レンダリングでは、これは非常に遅くなります。多くの変更テキストでこれを行うと、フレームレートが床を通り抜けます
    2. 技術的には、それはおおむね正しい (完全ではない: この方法でいくつかの情報を失う) が、非常に非効率的である

私も見ましたが、次のことについて良い点と悪い点の両方を聞きました。

  1. Imagination/PowerVR "Print3D" (リンクが壊れています) (GPU を製造している会社から! しかし、彼らのサイトはテキスト レンダリング ページを移動/削除しました)
  2. FreeType (前処理、解釈、大量のコード、追加のライブラリが必要ですか?)
  3. ...および/または FTGL http://sourceforge.net/projects/ftgl/ (噂: 遅い? バグがある? 長い間更新されていない?)
  4. Font-Stash http://digestingduck.blogspot.co.uk/2009/08/font-stash.html (高品質ですが、非常に遅いですか?)
  5. 1.

Apple 独自の OS / 標準ライブラリ内で、テキスト レンダリングのいくつかのソースを知っています。注意:私はこれらのほとんどを 2D レンダリング プロジェクトで詳細に使用しました。異なるレンダリングを出力することについての私の記述は、直接の経験に基づいています。

  1. NSString を使用した CoreGraphics
    1. 最も簡単な方法: 「CGRect に」レンダリングする
    2. 人々が推奨する「固定文字列」アプローチのわずかに高速なバージョンのようです (ただし、ほとんど同じであると予想されます)。
  2. プレーンテキストの UILabel と UITextArea
    1. 注意: それらは同じではありません! smae テキストのレンダリング方法のわずかな違い
  3. 上記のいずれかにレンダリングされる NSAttributedString
    1. 繰り返しますが、レンダリングが異なります(私が知っている違いはかなり微妙で、「バグ」として分類されます。これに関するさまざまなSOの質問)
  4. CATextLayer
    1. iOS フォントと古い C レンダリングのハイブリッド。「完全ではない」無料でブリッジされた CFFont / UIFont を使用します。これにより、レンダリングの違いや奇妙さがさらに明らかになります
  5. コアテキスト
    1. ...究極の解決策?しかし、それ自身の獣...
4

3 に答える 3

8

さらに実験を行ったところ、テクスチャ アトラスと Valve の符号付き差分テクスチャ (ビットマップ グリフを解像度に依存しない高解像度テクスチャに変換できる) と組み合わせると、CoreText が完璧なソリューションになるようです。

...しかし、まだ動作していません。まだ実験中です。


更新: Apple のドキュメントによると、最終的な詳細 (レンダリングするグリフとグリフのレイアウト) を除くすべてにアクセスできると書かれています (ドキュメントによると、行のレイアウトとグリフの数は取得できますが、グリフ自体は取得できません)。明確な理由はありませんが、このコア情報が CoreText から欠落しているようです (もしそうなら、CT はほとんど価値がありません。実際の glpyhs + グリフごとのデータを取得する方法を見つけることができるかどうかをまだ探しています)。


更新 2: これは Apple の CT で適切に動作するようになりました (ただし、異なるテクスチャはありません) が、最終的には 3 つのクラス ファイル、10 のデータ構造、約 300 行のコード、およびそれをレンダリングするための OpenGL コードになります。SOの答えには多すぎます:(。

簡単な答えは次のとおりです。はい、できます。次の場合に機能します。

  1. CTFrameSetter の作成
  2. 理論上の 2D フレームの CTFrame を作成する
  3. GL テクスチャに変換する CGContext を作成します。
  4. グリフごとに調べて、Apple が CGContext にレンダリングできるようにする
  5. Apple がグリフをレンダリングするたびに、境界ボックスを計算し (これは難しい)、どこかに保存します
  6. そして、一意のグリフ ID を保存します (これは、たとえば「o」、「f」、および「of」(1 つのグリフ!) で異なります)。
  7. 最後に、CGContext をテクスチャとして GL に送信します。

レンダリングするときは、Apple が作成したグリフ ID のリストを使用し、それぞれに保存された情報とテクスチャを使用して、アップロードしたテクスチャから個々のグリフを引き出すテクスチャ座標でクワッドをレンダリングします。

これは動作し、高速で、すべてのフォントで動作し、すべてのフォント レイアウトとカーニングが正しくなります。

于 2013-09-02T20:10:23.650 に答える
3

1.

で任意の文字列を作成しますNSMutableAttributedString

let mabstring = NSMutableAttributedString(string: "This is a test of characterAttribute.")
mabstring.beginEditing()
var matrix = CGAffineTransform(rotationAngle: CGFloat(GLKMathDegreesToRadians(0)))
let font = CTFontCreateWithName("Georgia" as CFString?, 40, &matrix)
mabstring.addAttribute(kCTFontAttributeName as String, value: font, range: NSRange(location: 0, length: 4))
var number: Int8 = 2
let kdl = CFNumberCreate(kCFAllocatorDefault, .sInt8Type, &number)!
mabstring.addAttribute(kCTStrokeWidthAttributeName as String, value: kdl, range: NSRange(location: 0, length: mabstring.length))
mabstring.endEditing()

2.

CTFrame を作成します。CoreTextによるrect計算mabstringCTFramesetterSuggestFrameSizeWithConstraints

let framesetter = CTFramesetterCreateWithAttributedString(mabstring)
let path = CGMutablePath()
path.addRect(rect)
let frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, nil)

3.

ビットマップ コンテキストを作成します。

let imageWidth = Int(rect.width)
let imageHeight = Int(rect.height)
var rawData = [UInt8](repeating: 0, count: Int(imageWidth * imageHeight * 4))
let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Big.rawValue | CGImageAlphaInfo.premultipliedLast.rawValue)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let bitsPerComponent = 8
let bytesPerRow = Int(rect.width) * 4
let context = CGContext(data: &rawData, width: imageWidth, height: imageHeight, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue)!

4.

CTFrame をビットマップ コンテキストで描画します。

CTFrameDraw(frame, context)

これで、生のピクセル データが得られましたrawDataOpenGL TextureMTLTexture、で作成UIImageしてrawDataもOKです。


例、

OpenGL テクスチャへ: UIImage をテクスチャに変換します。

テクスチャをセットアップします。

GLuint textureID;    
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(1, &textureID);

glBindTexture(GL_TEXTURE_2D, textureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);

//to MTLTexture
let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .rgba8Unorm, width: Int(imageWidth), height: Int(imageHeight), mipmapped: true)
let device = MTLCreateSystemDefaultDevice()!
let texture = device.makeTexture(descriptor: textureDescriptor)
let region = MTLRegionMake2D(0, 0, Int(imageWidth), Int(imageHeight))
texture.replace(region: region, mipmapLevel: 0, withBytes: &rawData, bytesPerRow: imageRef.bytesPerRow)

//to UIImage
  let providerRef = CGDataProvider(data: NSData(bytes: &rawData, length: rawData.count * MemoryLayout.size(ofValue: UInt8(0))))
  let renderingIntent =  CGColorRenderingIntent.defaultIntent
  let imageRef = CGImage(width: imageWidth, height: imageHeight, bitsPerComponent: 8, bitsPerPixel: 32, bytesPerRow: bytesPerRow, space: rgbColorSpace, bitmapInfo: bitmapInfo, provider: providerRef!, decode: nil, shouldInterpolate: false, intent: renderingIntent)!
  let image = UIImage.init(cgImage: imageRef)
于 2017-01-23T03:16:43.433 に答える
2

この投稿が古いことは知っていますが、アプリケーションでこれを正確に実行しようとしているときに遭遇しました。私の検索では、このサンプルプロジェクトに出くわしました

http://metalbyexample.com/rendering-text-in-metal-with-signed-distance-fields/

これは、テクスチャ アトラスと符号付き距離フィールドの技術を使用した OpenGL による CoreText の完全な実装です。私が望んでいた結果を達成するのに大いに役立ちました。これが他の誰かに役立つことを願っています。

于 2017-08-23T19:35:07.127 に答える