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"]
}