~vam-jam/vamist.dev

0409fdf82f7e396f21f607d37436a59f33a69e9b — Vamist 3 months ago 7ec0ed6
[dev] Use Dithering + palette
5 files changed, 155 insertions(+), 13 deletions(-)

A public/fiery-dreams-1x.png
M src/blobs/Cube.js
M src/blobs/EmptyCube.js
M src/engine/engine.js
M src/playground.js
A public/fiery-dreams-1x.png => public/fiery-dreams-1x.png +0 -0
M src/blobs/Cube.js => src/blobs/Cube.js +4 -7
@@ 3,6 3,7 @@ import {
	Color,
	Mesh,
	MeshBasicMaterial,
	MeshLambertMaterial,
	MeshNormalMaterial,
} from "three";
import { Blob } from "./blob";


@@ 54,12 55,8 @@ export function ThrowingCube(position) {
	const cube = new Cube(
		position,
		new BoxGeometry(0.1, 0.1, 0.1),
		new MeshBasicMaterial({
			color: new Color(
				Math.random() * 1.0,
				Math.random() * 1.0,
				Math.random() * 1.0,
			),
		new MeshLambertMaterial({
			color: new Color(0.2, 0.2, 0.2),
		}),
	);



@@ 76,7 73,7 @@ export function BasicSpinningCube(position) {
	const cube = new Cube(
		position,
		new BoxGeometry(0.2, 0.2, 0.2),
		new MeshNormalMaterial(),
		new MeshLambertMaterial({ color: new Color(0.2, 0.2, 0.2) }),
	);

	cube.OnTick = (_) => {

M src/blobs/EmptyCube.js => src/blobs/EmptyCube.js +2 -2
@@ 1,4 1,4 @@
import { Color, Mesh, MeshBasicMaterial } from "three";
import { Color, Mesh, MeshBasicMaterial, MeshLambertMaterial } from "three";
import { Blob } from "./blob";
import { ColliderDesc, RigidBodyDesc, TriMeshFlags } from "@dimforge/rapier3d";



@@ 20,7 20,7 @@ export class EmptyCube extends Blob {

			this.mesh = new Mesh(
				mesh.geometry,
				new MeshBasicMaterial({ color: new Color(0, 0, 0) }),
				new MeshLambertMaterial({ color: new Color(0.1, 0.1, 0.1) }),
			);

			this.mesh.position.set(

M src/engine/engine.js => src/engine/engine.js +117 -2
@@ 1,6 1,7 @@
import { RawIntegrationParameters } from "@dimforge/rapier3d/rapier_wasm3d";
import {
	BoxGeometry,
	Color,
	// BufferAttribute,
	// BufferGeometry,
	Euler,


@@ 8,6 9,7 @@ import {
	Mesh,
	MeshNormalMaterial,
	Quaternion,
	ShaderMaterial,
	Vector3,
	WebGLRenderer,
} from "three";


@@ 17,6 19,7 @@ import { RenderPixelatedPass } from "three/addons/postprocessing/RenderPixelated
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { ColliderDesc, World } from "@dimforge/rapier3d";
import { OutputPass } from "three/addons/postprocessing/OutputPass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";
// import { LineBasicNodeMaterial } from "three/examples/jsm/nodes/Nodes.js";

export class Engine {


@@ 63,8 66,8 @@ export class Engine {
		this.renderer = new WebGLRenderer({
			powerPreference: "high-performance",
			antialias: false,
			stencil: false,
			depth: false,
			// stencil: false,
			// depth: false,
		});
		this.renderer.setSize(this.width, this.height);



@@ 75,6 78,14 @@ export class Engine {
		this.renderPass.depthEdgeStrength = 0.1;
		this.renderPass.normalEdgeStrength = 0.1;

		this.loadPaletteShader(
			// "https://i.postimg.cc/7hSv4jBg/dynasty38-1x.png",
			"./fiery-dreams-1x.png",
			(shaderPass) => {
				this.composer.addPass(shaderPass);
			},
		);

		this.composer.addPass(this.renderPass);

		// this.outputPass = new OutputPass();


@@ 124,6 135,110 @@ export class Engine {
		});
	}

	// Using code from: https://nas.sr/text/yesterdays-pixels-today/
	paletteShader(palette) {
		console.log(palette.length);
		return new ShaderMaterial({
			uniforms: {
				tDiffuse: { value: null },
				palette: { value: palette },
				threshold: { value: 0.1 },
			},
			vertexShader: [
				"out vec2 vUv;",
				"void main() {",
				"vUv = uv;",
				"gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);",
				"}",
			].join("\n"),
			fragmentShader: [
				"uniform vec3 palette[" + palette.length + "];",
				"uniform sampler2D tDiffuse;",
				"uniform float threshold;",
				"in vec2 vUv;",
				"void main() {",
				"vec3 color = texture2D(tDiffuse, vUv).rgb;",
				"float total = gl_FragCoord.x + gl_FragCoord.y;",
				"bool isEven = mod(total, 2.0)==0.0;",
				"float closestDistance = 1.0;",
				"vec3 closestColor = palette[0];",
				"int firstIndex = 0;",
				"int secondIndex = 0;",
				"float secondClosestDistance = 1.0;",
				"vec3 secondClosestColor = palette[0];",
				/*
				 * loop through the palette colors and compute the two closest colors
				 * to the input pixel color
				 */
				"for(int i=0;i<" + palette.length + "; i++) {",
				"float d = distance(color, palette[i]);",
				"if(d <= closestDistance) {",
				"secondIndex = firstIndex;",
				"secondClosestDistance = closestDistance;",
				"secondClosestColor = closestColor;",
				"firstIndex = i;",
				"closestDistance = d;",
				"closestColor = palette[i];",
				"} else if (d <= secondClosestDistance) {",
				"secondIndex = i;",
				"secondClosestDistance = d;",
				"secondClosestColor = palette[i];",
				"}",
				"}",
				/*
				 * if the two closest colors are within the threshold of each other
				 * preform a dither
				 */
				"if(distance(closestDistance, secondClosestDistance) < threshold) {",
				"vec3 a = firstIndex < secondIndex ? closestColor : secondClosestColor;",
				"vec3 b = firstIndex < secondIndex ? secondClosestColor : closestColor;",
				"gl_FragColor = vec4(isEven ? a : b, 1.0);",
				/* otherwise use the closest color */
				"} else {",
				"gl_FragColor = vec4(closestColor, 1.0);",
				"}",
				"}",
			].join("\n"),
		});
	}

	imageData(img) {
		const canvas = document.createElement("canvas");
		const context = canvas.getContext("2d");
		canvas.width = img.naturalWidth;
		canvas.height = img.naturalHeight;
		context.drawImage(img, 0, 0);

		return context.getImageData(0, 0, img.naturalWidth, img.naturalHeight);
	}

	palette(img) {
		const palette = [];
		const pixels = this.imageData(img);

		for (let i = 0; i < pixels.data.length; i += 4) {
			palette.push(
				new Color(
					pixels.data[i] / 256.0,
					pixels.data[i + 1] / 256.0,
					pixels.data[i + 2] / 256.0,
				),
			);
		}

		return palette;
	}

	loadPaletteShader(url, cb) {
		const image = new Image();
		image.crossOrigin = "Anonymous";
		image.src = url;
		image.addEventListener("load", () => {
			const pass = new ShaderPass(this.paletteShader(this.palette(image)));
			cb(pass);
		});
	}

	/** @param {PerspectiveCamera} camera **/
	SetCamera(camera) {
		this.camera = camera;

M src/playground.js => src/playground.js +32 -2
@@ 1,7 1,19 @@
import { Engine } from "./engine/engine";
import { ThrowingCube } from "./blobs/Cube";
import { Cube, ThrowingCube } from "./blobs/Cube";
import { SpinningEmptyCube } from "./blobs/EmptyCube";
import { Color, PerspectiveCamera, Scene, Vector3 } from "three";
import {
	AmbientLight,
	BoxGeometry,
	Color,
	DirectionalLight,
	HemisphereLight,
	MeshNormalMaterial,
	PerspectiveCamera,
	PointLight,
	Scene,
	SpotLight,
	Vector3,
} from "three";
import { BlackBar } from "./static/bar";

const engine = new Engine(window.innerWidth, window.innerHeight);


@@ 17,6 29,24 @@ engine.SetCamera(
);
engine.camera.position.z = 1;

const directionalLight = new DirectionalLight(0xffffff, 1.0);
directionalLight.position.z += 1;
home.add(directionalLight);

const pointLight = new PointLight(0xffffff, 10.0, 0, 1.0);
home.add(pointLight);
//
// const lightCube = new Cube(
// 	pointLight.position,
// 	new BoxGeometry(0.1, 0.1, 0.1),
// 	new MeshNormalMaterial(),
// );
//
// home.add(lightCube.mesh);

// const light = new AmbientLight(0x404040, 10);
// home.add(light);

engine.RenderFrame();

SpinningEmptyCube(