Repository: emilwidlund/ASCII Branch: main Commit: e0ef82c08610 Files: 7 Total size: 5.9 KB Directory structure: gitextract_z7xha2au/ ├── .gitignore ├── .prettierrc ├── .vscode/ │ └── settings.json ├── README.md ├── package.json ├── src/ │ └── index.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ node_modules build .DS_STORE *-lock.json ================================================ FILE: .prettierrc ================================================ { "arrowParens": "avoid", "singleQuote": true, "printWidth": 120, "tabWidth": 4, "trailingComma": "none" } ================================================ FILE: .vscode/settings.json ================================================ { "editor.codeActionsOnSave": { "source.fixAll.eslint": true }, "editor.formatOnSave": true, "eslint.format.enable": true, "editor.defaultFormatter": "esbenp.prettier-vscode", "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" } } ================================================ FILE: README.md ================================================ # ASCII An ASCII effect for THREE.js - which runs as a fragment shader on the GPU. Inspired by https://github.com/darkroomengineering/aniso. ### Supported Props ```typescript interface IASCIIEffectProps { characters?: string; // The ASCII characters to use in brightness order dark -> light fontSize?: number; // Font Size of the characters drawn to the texture cellSize?: number; // Size of each cell in the grid color?: string; // Color of the characters invert?: boolean; // Flag which inverts the effect } ``` ### Example with @react-three/fiber ```jsx import React from 'react'; import { Canvas } from '@react-three/fiber'; import { EffectComposer } from '@react-three/postprocessing'; const Scene = () => { const asciiEffect = React.useMemo(() => new ASCIIEffect(), []); return ( ); }; ``` ================================================ FILE: package.json ================================================ { "name": "ascii", "version": "1.0.0", "description": "", "main": "build/index.js", "scripts": { "build": "tsc" }, "keywords": [], "author": "Emil Widlund", "license": "MIT", "dependencies": { "@types/three": "^0.151.0", "postprocessing": "6.30.2", "three": "^0.151.3", "typescript": "5.0.4" }, "type": "module" } ================================================ FILE: src/index.ts ================================================ import { CanvasTexture, Color, NearestFilter, RepeatWrapping, Texture, Uniform } from 'three'; import { Effect } from 'postprocessing'; const fragment = ` uniform sampler2D uCharacters; uniform float uCharactersCount; uniform float uCellSize; uniform bool uInvert; uniform vec3 uColor; const vec2 SIZE = vec2(16.); vec3 greyscale(vec3 color, float strength) { float g = dot(color, vec3(0.299, 0.587, 0.114)); return mix(color, vec3(g), strength); } vec3 greyscale(vec3 color) { return greyscale(color, 1.0); } void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { vec2 cell = resolution / uCellSize; vec2 grid = 1.0 / cell; vec2 pixelizedUV = grid * (0.5 + floor(uv / grid)); vec4 pixelized = texture2D(inputBuffer, pixelizedUV); float greyscaled = greyscale(pixelized.rgb).r; if (uInvert) { greyscaled = 1.0 - greyscaled; } float characterIndex = floor((uCharactersCount - 1.0) * greyscaled); vec2 characterPosition = vec2(mod(characterIndex, SIZE.x), floor(characterIndex / SIZE.y)); vec2 offset = vec2(characterPosition.x, -characterPosition.y) / SIZE; vec2 charUV = mod(uv * (cell / SIZE), 1.0 / SIZE) - vec2(0., 1.0 / SIZE) + offset; vec4 asciiCharacter = texture2D(uCharacters, charUV); asciiCharacter.rgb = uColor * asciiCharacter.r; asciiCharacter.a = pixelized.a; outputColor = asciiCharacter; } `; export interface IASCIIEffectProps { characters?: string; fontSize?: number; cellSize?: number; color?: string; invert?: boolean; } export class ASCIIEffect extends Effect { constructor({ characters = ` .:,'-^=*+?!|0#X%WM@`, fontSize = 54, cellSize = 16, color = '#ffffff', invert = false }: IASCIIEffectProps = {}) { const uniforms = new Map([ ['uCharacters', new Uniform(new Texture())], ['uCellSize', new Uniform(cellSize)], ['uCharactersCount', new Uniform(characters.length)], ['uColor', new Uniform(new Color(color))], ['uInvert', new Uniform(invert)] ]); super('ASCIIEffect', fragment, { uniforms }); const charactersTextureUniform = this.uniforms.get('uCharacters'); if (charactersTextureUniform) { charactersTextureUniform.value = this.createCharactersTexture(characters, fontSize); } } /** Draws the characters on a Canvas and returns a texture */ public createCharactersTexture(characters: string, fontSize: number): THREE.Texture { const canvas = document.createElement('canvas'); const SIZE = 1024; const MAX_PER_ROW = 16; const CELL = SIZE / MAX_PER_ROW; canvas.width = canvas.height = SIZE; const texture = new CanvasTexture( canvas, undefined, RepeatWrapping, RepeatWrapping, NearestFilter, NearestFilter ); const context = canvas.getContext('2d'); if (!context) { throw new Error('Context not available'); } context.clearRect(0, 0, SIZE, SIZE); context.font = `${fontSize}px arial`; context.textAlign = 'center'; context.textBaseline = 'middle'; context.fillStyle = '#fff'; for (let i = 0; i < characters.length; i++) { const char = characters[i]; const x = i % MAX_PER_ROW; const y = Math.floor(i / MAX_PER_ROW); context.fillText(char, x * CELL + CELL / 2, y * CELL + CELL / 2); } texture.needsUpdate = true; return texture; } } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { "lib": ["ESNext", "DOM"], "target": "ESNext", "module": "ESNext", "strict": true, "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true, "downlevelIteration": true, "outDir": "build", "declaration": true, "jsx": "react" }, "include": ["./src"] }