From 44d8512709bb9acc746b4fde08267d5c98aae312 Mon Sep 17 00:00:00 2001 From: Jason Reed Date: Sat, 9 Dec 2023 18:50:59 -0500 Subject: [PATCH] WebGL rendering of font --- public/assets/frag.frag | 21 ++++++++++++++++++++- src/core/assets.ts | 6 +++++- src/ui/gl-render.ts | 21 +++++++++++++++++++-- src/ui/sprite-sheet.ts | 6 ++++++ 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/public/assets/frag.frag b/public/assets/frag.frag index ac0662c..eaa5ac7 100644 --- a/public/assets/frag.frag +++ b/public/assets/frag.frag @@ -5,6 +5,9 @@ const int CHUNK_SIZE = 16; const float NUM_SPRITES_PER_SHEET = 16.; // in both directions const float SPRITE_SIZE = 32.; +const float NUM_FONT_CELLS_PER_SHEET = 8.; // in both directions +const float FONT_CELL_SIZE = 64.; + out vec4 outputColor; // World coordinates of the origin of the chunk @@ -19,6 +22,9 @@ uniform mat3 u_world_from_canvas; // Sprite sheet uniform sampler2D u_spriteTexture; +// Font Sheet +uniform sampler2D u_fontTexture; + // Chunk data uniform sampler2D u_chunkDataTexture; @@ -33,6 +39,19 @@ bool less_dist(vec2 v, float d) { return v.x * v.x + v.y * v.y < d * d; } +// p_in_world_fp is the fractional part of p_in_world. It is in [0,1]² +// sprite_coords is actually an ivec. It is in [0,NUM_SPRITES_PER_SHEET]² +vec4 get_sprite_pixel(vec2 p_in_world_fp, vec2 sprite_coords, float sharpness) { + if (sprite_coords.x == 12. || sprite_coords.x == 13.) { + int letter = int((sprite_coords.x - 12.) * NUM_SPRITES_PER_SHEET + sprite_coords.y); + vec2 font_coords = vec2(letter / int( NUM_FONT_CELLS_PER_SHEET), letter % int(NUM_FONT_CELLS_PER_SHEET)); + float sdf = texture(u_fontTexture, (p_in_world_fp + font_coords) / NUM_FONT_CELLS_PER_SHEET).r; + float amount = clamp(0.5 + sharpness * (sdf - 0.5), 0., 1.); + return vec4(amount * vec3(0.6) + (1. - amount) * vec3(1.), 1.); + } + return texture(u_spriteTexture, (p_in_world_fp + sprite_coords) / NUM_SPRITES_PER_SHEET); +} + vec4 getColor() { vec3 p_in_canvas = vec3(gl_FragCoord.xy, 1.0); p_in_canvas.y = u_canvasSize.y - p_in_canvas.y; @@ -49,7 +68,7 @@ vec4 getColor() { p_in_world_fp = clamp(p_in_world_fp, 0.5/SPRITE_SIZE, 1. - 1./SPRITE_SIZE); vec2 sprite_coords = round(255.0 * texture(u_chunkDataTexture, (coords_within_chunk + vec2(0.5,0.5)) / float(CHUNK_SIZE) )).xy; - vec4 bgcolor = texture(u_spriteTexture, (p_in_world_fp + sprite_coords) / NUM_SPRITES_PER_SHEET); + vec4 bgcolor = get_sprite_pixel(p_in_world_fp, sprite_coords, 1. / (u_world_from_canvas[0][0] * 6.)); vec2 off = abs(p_in_world - p_in_world_r); float ch_amount = max(crosshair(off.xy), crosshair(off.yx)); diff --git a/src/core/assets.ts b/src/core/assets.ts index efeaf08..66b90b1 100644 --- a/src/core/assets.ts +++ b/src/core/assets.ts @@ -1,10 +1,11 @@ -import { prerenderSpriteSheet } from "../ui/sprite-sheet"; +import { prerenderFontSheet, prerenderSpriteSheet } from "../ui/sprite-sheet"; import { Buffer, imgProm } from "../util/dutil"; import { grab } from "../util/util"; type Assets = { dictionary: Record, spriteSheetBuf: Buffer, + fontSheetBuf: Buffer, vert: string, frag: string, }; @@ -15,12 +16,14 @@ type Assets = { let assets: Assets = { dictionary: { 'foo': true, 'bar': true, 'baz': true }, spriteSheetBuf: undefined as any, // cheating here and assuming tests won't use toolbarImg + fontSheetBuf: undefined as any, // cheating here and assuming tests won't use fontSheetImg vert: '', frag: '', } export async function initAssets() { const spriteSheetImg = await imgProm('assets/toolbar.png'); + const fontSheetImg = await imgProm('assets/font-sheet.png'); const vert = await grab('assets/vertex.vert'); const frag = await grab('assets/frag.frag'); const wordlist = (await (await fetch('assets/dictionary.txt')).text()) @@ -29,6 +32,7 @@ export async function initAssets() { assets = { dictionary, spriteSheetBuf: prerenderSpriteSheet(spriteSheetImg), + fontSheetBuf: prerenderFontSheet(fontSheetImg), vert, frag, }; diff --git a/src/ui/gl-render.ts b/src/ui/gl-render.ts index e06d166..72991a2 100644 --- a/src/ui/gl-render.ts +++ b/src/ui/gl-render.ts @@ -26,6 +26,7 @@ export type GlEnv = { const SPRITE_TEXTURE_UNIT = 0; const CHUNK_DATA_TEXTURE_UNIT = 1; +const FONT_TEXTURE_UNIT = 2; function drawChunk(gl: WebGL2RenderingContext, env: GlEnv, p_in_chunk: Point, state: GameState, chunk_from_canvas: SE2): void { const { prog, chunkBoundsBuffer, chunkImdat } = env; @@ -49,6 +50,9 @@ function drawChunk(gl: WebGL2RenderingContext, env: GlEnv, p_in_chunk: Point, st const u_spriteTexture = gl.getUniformLocation(prog, 'u_spriteTexture'); gl.uniform1i(u_spriteTexture, SPRITE_TEXTURE_UNIT); + const u_fontTexture = gl.getUniformLocation(prog, 'u_fontTexture'); + gl.uniform1i(u_fontTexture, FONT_TEXTURE_UNIT); + const u_chunkDataTexture = gl.getUniformLocation(prog, 'u_chunkDataTexture'); gl.uniform1i(u_chunkDataTexture, CHUNK_DATA_TEXTURE_UNIT); @@ -153,8 +157,6 @@ export function renderGlPane(ci: CanvasGlInfo, env: GlEnv, state: GameState): vo else { actuallyRender(); } - - } export function glInitialize(ci: CanvasGlInfo, dispatch: Dispatch): GlEnv { @@ -181,6 +183,21 @@ export function glInitialize(ci: CanvasGlInfo, dispatch: Dispatch): GlEnv { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, spriteImdat); + // Font texture + const fontTexture = gl.createTexture(); + if (fontTexture == null) { + throw new Error(`couldn't create font texture`); + } + gl.activeTexture(gl.TEXTURE0 + FONT_TEXTURE_UNIT); + gl.bindTexture(gl.TEXTURE_2D, fontTexture); + + const fontImdat = imageDataOfBuffer(getAssets().fontSheetBuf); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, fontImdat); + // Chunk data texture const chunkDataTexture = gl.createTexture(); if (chunkDataTexture == null) { diff --git a/src/ui/sprite-sheet.ts b/src/ui/sprite-sheet.ts index d9231e1..254c0c9 100644 --- a/src/ui/sprite-sheet.ts +++ b/src/ui/sprite-sheet.ts @@ -68,3 +68,9 @@ export function prerenderSpriteSheet(img: HTMLImageElement): Buffer { } return buf; } + +export function prerenderFontSheet(img: HTMLImageElement): Buffer { + const buf = buffer({ x: img.width, y: img.height }); + buf.d.drawImage(img, 0, 0); + return buf; +}