Last active
November 7, 2024 20:23
-
-
Save mmozeiko/c7cd68ba0733a0d9e4f0a97691a50d39 to your computer and use it in GitHub Desktop.
compute shader for rendering monospaced glyphs in grid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
struct TerminalCell | |
{ | |
// cell index into GlyphTexture, should be two 16-bit (x,y) values packed: "x | (y << 16)" | |
uint GlyphIndex; | |
// 0xAABBGGRR encoded colors, nonzero alpha for Foreground indicates to render colored-glyph | |
// which means use RGB values from GlyphTexture directly as output, not as ClearType blending weights | |
uint Foreground; | |
uint Background; | |
}; | |
cbuffer ConstBuffer : register(b0) | |
{ | |
uint2 CellSize; | |
uint2 TermSize; | |
}; | |
// TermSize.x * TermSize.y amount of cells to render as output | |
StructuredBuffer<TerminalCell> Cells : register(t0); | |
// RGB blending weights for ClearType | |
// or alpha-premultipled RGB values for colored glyphs | |
Texture2D<float4> GlyphTexture : register(t1); | |
RWTexture2D<float4> Output : register(u0); | |
float3 GetColor(uint i) | |
{ | |
int r = i & 0xff; | |
int g = (i >> 8) & 0xff; | |
int b = (i >> 16) & 0xff; | |
return float3(r, g, b) / 255.0; | |
} | |
uint2 GetGlyphPos(uint GlyphIndex) | |
{ | |
return uint2(GlyphIndex & 0xffff, GlyphIndex >> 16) * CellSize; | |
} | |
// dispatch with (TermSize*CellSize+7)/8 groups for x,y and 1 for z | |
[numthreads(8, 8, 1)] | |
void shader(uint3 Id: SV_DispatchThreadID) | |
{ | |
uint2 ScreenPos = Id.xy; | |
// index of cell on screen | |
uint2 CellIndex = ScreenPos / CellSize; | |
// pixel position in cell | |
uint2 CellPos = ScreenPos % CellSize; | |
TerminalCell Cell = Cells[CellIndex.y * TermSize.x + CellIndex.x]; | |
// position where glyph starts in texture | |
uint2 GlyphPos = GetGlyphPos(Cell.GlyphIndex); | |
// absolute pixel location in texture to use for output | |
uint2 PixelPos = GlyphPos + CellPos; | |
float4 Tex = GlyphTexture[PixelPos]; | |
float3 Background = GetColor(Cell.Background); | |
float3 Foreground = GetColor(Cell.Foreground); | |
bool ColoredGlyph = (Cell.Foreground >> 24) != 0; | |
float3 Color; | |
if (ColoredGlyph) | |
{ | |
// colored glyphs are alpha premultiplied | |
Color = Background * (1.0 - Tex.a) + Tex.rgb; | |
} | |
else | |
{ | |
// TODO: proper ClearType blending | |
Color = lerp(Background, Foreground, Tex.rgb); | |
} | |
Output[ScreenPos] = float4(Color, 1); | |
} |
Thank you for your answer and also my apologies, I should have looked into the terminology of what monospace meant. I was thinking that the fonts are "proportional". So this technique know makes sense given that monospaced glyphs are all equal in width and are fixed-pitch, and each cell can take any glyph (mostly Latin).
In a text editor the fonts are proportional and usually they support smooth scrolling, so this technique won't work. My guess is a text editor would just send an array of quads to the gpu each frame(depending how many characters you're rendering on the screen), and issue one draw call.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
When you rasterize glyph with your font rasterizer you get whatever width & height it produces - you split the output into multiple cells and upload to font texture in gpu. Then this shader will render those cells next to each other without any spacing between them.
Casey convers texture generation in video here: https://www.youtube.com/watch?v=cGoQ3ceKX6g
Not sure what you mean by kerning. This is for monospace terminal rendering - there is no kerning for monospace characters.