From 53b47e05c85ecee9818c6d8312263f09725e915e Mon Sep 17 00:00:00 2001 From: git-chad Date: Fri, 22 Nov 2024 10:49:00 -0300 Subject: [PATCH 01/20] crt renderer init --- src/experiments/78.crt-renderer/index.tsx | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/experiments/78.crt-renderer/index.tsx diff --git a/src/experiments/78.crt-renderer/index.tsx b/src/experiments/78.crt-renderer/index.tsx new file mode 100644 index 0000000..e69de29 From 984354e7d9dd9fa2f7a452ce8f930ea7ae1cdde3 Mon Sep 17 00:00:00 2001 From: Vittorio Retrivi Date: Fri, 22 Nov 2024 11:15:41 -0300 Subject: [PATCH 02/20] initial --- public/experiments.json | 20 +++++++++++++++++- src/experiments/78.crt-renderer/index.tsx | 25 +++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/public/experiments.json b/public/experiments.json index 6f6386c..c340eb3 100644 --- a/public/experiments.json +++ b/public/experiments.json @@ -1,4 +1,13 @@ [ + { + "filename": "78.crt-renderer", + "title": "Crt renderer.", + "href": "/experiments/78.crt-renderer", + "tags": [], + "og": null, + "number": 78, + "contributors": [] + }, { "filename": "77.instanced-grass", "title": "Instanced grass.", @@ -6,7 +15,16 @@ "tags": [], "og": null, "number": 77, - "contributors": [] + "contributors": [ + { + "id": "MDQ6VXNlcjgyODQ0NDQ4", + "url": "https://github.com/git-chad", + "name": "Tobi Moccagatta", + "avatarUrl": "https://avatars.githubusercontent.com/u/82844448?u=c71fc101623e8d32861d8512f379b06a3378606f&v=4", + "email": "tobimocc@gmail.com", + "company": "Set & Forget" + } + ] }, { "filename": "76.butterfly-particle-sphere", diff --git a/src/experiments/78.crt-renderer/index.tsx b/src/experiments/78.crt-renderer/index.tsx index e69de29..0ca2d7e 100644 --- a/src/experiments/78.crt-renderer/index.tsx +++ b/src/experiments/78.crt-renderer/index.tsx @@ -0,0 +1,25 @@ +'use client' + +import { PerspectiveCamera } from '@react-three/drei' +import { Suspense } from 'react' + +import { R3FCanvasLayout } from '~/components/layout/r3f-canvas-layout' + +function CrtRenderer() { + return ( + <> + + + + ) +} + +CrtRenderer.Layout = R3FCanvasLayout +CrtRenderer.Title = 'CRT Renderer' +CrtRenderer.Description = 'CRT Renderer' + +export default CrtRenderer From d143870dcfbf9401f04e666a6d5bc11bc94f44ee Mon Sep 17 00:00:00 2001 From: git-chad Date: Fri, 22 Nov 2024 11:53:21 -0300 Subject: [PATCH 03/20] starter --- .../78.crt-renderer/_components/scene.tsx | 37 +++++++++++++++++++ src/experiments/78.crt-renderer/index.tsx | 15 +++++++- .../78.crt-renderer/shaders/shaders.ts | 7 ++++ 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 src/experiments/78.crt-renderer/_components/scene.tsx create mode 100644 src/experiments/78.crt-renderer/shaders/shaders.ts diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx new file mode 100644 index 0000000..525d147 --- /dev/null +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -0,0 +1,37 @@ +import { OrbitControls } from '@react-three/drei' +import { EffectComposer, wrapEffect } from '@react-three/postprocessing' +import { Effect } from 'postprocessing' +import React, { forwardRef } from 'react' + +import { fragment } from '../shaders/shaders' + +class CrtEffect extends Effect { + constructor() { + super('CrtEffect', fragment, { + uniforms: new Map([]) + }) + } +} + +const CrtMonitorEffect = wrapEffect(CrtEffect) + +const CrtMonitorEffectComponent = forwardRef((props, ref) => ( + +)) + +export function Scene() { + return ( + <> + + + + + + + + + + + + ) +} diff --git a/src/experiments/78.crt-renderer/index.tsx b/src/experiments/78.crt-renderer/index.tsx index 0ca2d7e..76ebf44 100644 --- a/src/experiments/78.crt-renderer/index.tsx +++ b/src/experiments/78.crt-renderer/index.tsx @@ -1,10 +1,12 @@ 'use client' -import { PerspectiveCamera } from '@react-three/drei' +import { Environment, PerspectiveCamera } from '@react-three/drei' import { Suspense } from 'react' import { R3FCanvasLayout } from '~/components/layout/r3f-canvas-layout' +import { Scene } from './_components/scene' + function CrtRenderer() { return ( <> @@ -13,7 +15,16 @@ function CrtRenderer() { position={[-8.544, -0.922, 9.638]} fov={65} /> - + + + + ) } diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts new file mode 100644 index 0000000..1f1a4a0 --- /dev/null +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -0,0 +1,7 @@ +export const fragment = /* glsl */ ` + +` + +export const vertex = /* glsl */ ` + +` From 2b335373b41bb1d3d93e45bf4d04312b0cd6c0c9 Mon Sep 17 00:00:00 2001 From: git-chad Date: Fri, 22 Nov 2024 12:20:49 -0300 Subject: [PATCH 04/20] custom effect --- .../78.crt-renderer/_components/scene.tsx | 16 ++++++---------- src/experiments/78.crt-renderer/index.tsx | 6 +----- .../78.crt-renderer/shaders/shaders.ts | 9 +++++++-- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 525d147..463e2b8 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -1,11 +1,10 @@ import { OrbitControls } from '@react-three/drei' import { EffectComposer, wrapEffect } from '@react-three/postprocessing' import { Effect } from 'postprocessing' -import React, { forwardRef } from 'react' import { fragment } from '../shaders/shaders' -class CrtEffect extends Effect { +class CrtEffectImpl extends Effect { constructor() { super('CrtEffect', fragment, { uniforms: new Map([]) @@ -13,24 +12,21 @@ class CrtEffect extends Effect { } } -const CrtMonitorEffect = wrapEffect(CrtEffect) - -const CrtMonitorEffectComponent = forwardRef((props, ref) => ( - -)) +export const CrtEffect = wrapEffect(CrtEffectImpl) export function Scene() { return ( <> - - + + - + {/* @ts-expect-error */} + ) diff --git a/src/experiments/78.crt-renderer/index.tsx b/src/experiments/78.crt-renderer/index.tsx index 76ebf44..514ad7b 100644 --- a/src/experiments/78.crt-renderer/index.tsx +++ b/src/experiments/78.crt-renderer/index.tsx @@ -10,11 +10,7 @@ import { Scene } from './_components/scene' function CrtRenderer() { return ( <> - + Date: Fri, 22 Nov 2024 12:30:24 -0300 Subject: [PATCH 05/20] warp --- .../78.crt-renderer/shaders/shaders.ts | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index ac7d811..94d4fff 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -1,12 +1,20 @@ export const fragment = /* glsl */ ` + const float warp = 0.75; + void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { - // Just output red to test if the effect is working - outputColor = vec4(1.0, 0.0, 0.0, 1.0); - } -` + + + // warp the uv + // vec2 dc = abs(0.5-uv); + // dc *= dc; + // vec2 warpedUv = uv; + // warpedUv.x -= 0.5; warpedUv.x *= 1.0+(dc.y*(0.3*warp)); warpedUv.x += 0.5; + // warpedUv.y -= 0.5; warpedUv.y *= 1.0+(dc.x*(0.4*warp)); warpedUv.y += 0.5; + // if (warpedUv.y > 1.0 || warpedUv.x < 0.0 || warpedUv.x > 1.0 || warpedUv.y < 0.0) { + // outputColor = vec4(0.0,0.0,0.0,1.0); + // return; + // } -export const vertex = /* glsl */ ` - void mainSupport(const in vec2 uv) { - // Empty but required - } + outputColor = inputColor; +} ` From 1f4c6080b451c20efbbdaaaf5dab518da0d0c8f9 Mon Sep 17 00:00:00 2001 From: Vittorio Retrivi Date: Fri, 22 Nov 2024 13:04:01 -0300 Subject: [PATCH 06/20] some effects --- .../78.crt-renderer/_components/scene.tsx | 45 +++++++++++++-- .../78.crt-renderer/shaders/shaders.ts | 57 +++++++++++++------ 2 files changed, 78 insertions(+), 24 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 463e2b8..31f6811 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -1,15 +1,27 @@ import { OrbitControls } from '@react-three/drei' -import { EffectComposer, wrapEffect } from '@react-three/postprocessing' +import { Bloom, EffectComposer, wrapEffect } from '@react-three/postprocessing' import { Effect } from 'postprocessing' +import { Uniform } from 'three' import { fragment } from '../shaders/shaders' class CrtEffectImpl extends Effect { constructor() { super('CrtEffect', fragment, { - uniforms: new Map([]) + uniforms: new Map([ + ['uTime', new Uniform(0)], + ['uNoiseIntensity', new Uniform(0.15)], + ['uWarpStrength', new Uniform(0.75)], + ['uScanlineIntensity', new Uniform(0.1)], + ['uScanlineFrequency', new Uniform(128.0)] + ]) }) } + + update(renderer: any, inputBuffer: any, deltaTime: number) { + // @ts-expect-error + this.uniforms.get('uTime').value += deltaTime + } } export const CrtEffect = wrapEffect(CrtEffectImpl) @@ -19,12 +31,33 @@ export function Scene() { <> - - - - + + + + + + + + + + + + + + + + + {/* @ts-expect-error */} diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index 94d4fff..f82f6b6 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -1,20 +1,41 @@ export const fragment = /* glsl */ ` - const float warp = 0.75; - - void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { - - - // warp the uv - // vec2 dc = abs(0.5-uv); - // dc *= dc; - // vec2 warpedUv = uv; - // warpedUv.x -= 0.5; warpedUv.x *= 1.0+(dc.y*(0.3*warp)); warpedUv.x += 0.5; - // warpedUv.y -= 0.5; warpedUv.y *= 1.0+(dc.x*(0.4*warp)); warpedUv.y += 0.5; - // if (warpedUv.y > 1.0 || warpedUv.x < 0.0 || warpedUv.x > 1.0 || warpedUv.y < 0.0) { - // outputColor = vec4(0.0,0.0,0.0,1.0); - // return; - // } - - outputColor = inputColor; -} + uniform float uTime; + + uniform float uNoiseIntensity; + + uniform float uWarpStrength; + + uniform float uScanlineIntensity; + uniform float uScanlineFrequency; + + + // Add noise function + float random(vec2 st) { + return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); + } + + void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { + + // warp the uv + vec2 dc = abs(0.5-uv); + dc *= dc; + vec2 warpedUv = uv; + warpedUv.x -= 0.5; warpedUv.x *= 1.0+(dc.y*(0.3*uWarpStrength)); warpedUv.x += 0.5; + warpedUv.y -= 0.5; warpedUv.y *= 1.0+(dc.x*(0.4*uWarpStrength)); warpedUv.y += 0.5; + if (warpedUv.y > 1.0 || warpedUv.x < 0.0 || warpedUv.x > 1.0 || warpedUv.y < 0.0) { + outputColor = vec4(0.0,0.0,0.0,1.0); + return; + } + + // Add scanlines + float scanLine = sin(warpedUv.y * uScanlineFrequency) * uScanlineIntensity; + vec4 color = inputColor; + color.rgb -= scanLine; + + // Add noise + float noise = random(uv + uTime) * uNoiseIntensity; + color.rgb += noise; + + outputColor = color; + } ` From 25652c7048ced307c873ea38f73fb5e38a0cc3e1 Mon Sep 17 00:00:00 2001 From: Vittorio Retrivi Date: Fri, 22 Nov 2024 14:26:55 -0300 Subject: [PATCH 07/20] some effects --- src/experiments/78.crt-renderer/_components/scene.tsx | 8 ++++---- src/experiments/78.crt-renderer/shaders/shaders.ts | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 31f6811..a8669d0 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -1,5 +1,5 @@ import { OrbitControls } from '@react-three/drei' -import { Bloom, EffectComposer, wrapEffect } from '@react-three/postprocessing' +import { EffectComposer, wrapEffect } from '@react-three/postprocessing' import { Effect } from 'postprocessing' import { Uniform } from 'three' @@ -12,8 +12,9 @@ class CrtEffectImpl extends Effect { ['uTime', new Uniform(0)], ['uNoiseIntensity', new Uniform(0.15)], ['uWarpStrength', new Uniform(0.75)], - ['uScanlineIntensity', new Uniform(0.1)], - ['uScanlineFrequency', new Uniform(128.0)] + ['uScanlineFrequency', new Uniform(128.0)], + ['uScanlineIntensity', new Uniform(0.05)], + ['uScanlineFrequency', new Uniform(2048.0)] ]) }) } @@ -57,7 +58,6 @@ export function Scene() { - {/* @ts-expect-error */} diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index f82f6b6..c69c063 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -27,9 +27,11 @@ export const fragment = /* glsl */ ` return; } + vec4 color = inputColor; + // Add scanlines float scanLine = sin(warpedUv.y * uScanlineFrequency) * uScanlineIntensity; - vec4 color = inputColor; + color = inputColor; color.rgb -= scanLine; // Add noise From 83230938d795230cd10e75797edbb2ec992d3fb4 Mon Sep 17 00:00:00 2001 From: git-chad Date: Fri, 22 Nov 2024 14:25:06 -0300 Subject: [PATCH 08/20] ... --- .../78.crt-renderer/_components/scene.tsx | 50 +++-------- .../78.crt-renderer/shaders/bluen-128.png | Bin 0 -> 31364 bytes .../78.crt-renderer/shaders/bluen-64.png | Bin 0 -> 7147 bytes .../78.crt-renderer/shaders/shaders.ts | 85 ++++++++++-------- 4 files changed, 64 insertions(+), 71 deletions(-) create mode 100644 src/experiments/78.crt-renderer/shaders/bluen-128.png create mode 100644 src/experiments/78.crt-renderer/shaders/bluen-64.png diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index a8669d0..8a50726 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -1,21 +1,15 @@ -import { OrbitControls } from '@react-three/drei' +import { OrbitControls, useTexture } from '@react-three/drei' import { EffectComposer, wrapEffect } from '@react-three/postprocessing' import { Effect } from 'postprocessing' -import { Uniform } from 'three' +import * as THREE from 'three' +import bn from '../shaders/bluen-128.png' import { fragment } from '../shaders/shaders' class CrtEffectImpl extends Effect { - constructor() { + constructor(uBNoise: THREE.Texture) { super('CrtEffect', fragment, { - uniforms: new Map([ - ['uTime', new Uniform(0)], - ['uNoiseIntensity', new Uniform(0.15)], - ['uWarpStrength', new Uniform(0.75)], - ['uScanlineFrequency', new Uniform(128.0)], - ['uScanlineIntensity', new Uniform(0.05)], - ['uScanlineFrequency', new Uniform(2048.0)] - ]) + uniforms: new Map([['uBNoise', new THREE.Uniform(uBNoise)]]) }) } @@ -28,38 +22,22 @@ class CrtEffectImpl extends Effect { export const CrtEffect = wrapEffect(CrtEffectImpl) export function Scene() { + const bluenoise = useTexture(bn.src) + bluenoise.wrapS = THREE.RepeatWrapping + bluenoise.wrapT = THREE.RepeatWrapping + return ( <> - - - - - - - - - - - - - - - - + + + + {/* @ts-expect-error */} - + ) diff --git a/src/experiments/78.crt-renderer/shaders/bluen-128.png b/src/experiments/78.crt-renderer/shaders/bluen-128.png new file mode 100644 index 0000000000000000000000000000000000000000..41aa44d430b0026914765a604de1c78b2a44911c GIT binary patch literal 31364 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
d@Ph4W}IU6N>ip9ZUf>M%%Z;tbQ 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 X4P9fhu6cF`00000NkvXXu0mjfy2+mk literal 0 HcmV?d00001 diff --git a/src/experiments/78.crt-renderer/shaders/bluen-64.png b/src/experiments/78.crt-renderer/shaders/bluen-64.png new file mode 100644 index 0000000000000000000000000000000000000000..5730c03ad479a530461ad33202397a7efd57c1be GIT binary patch literal 7147 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 1.0 || warpedUv.x < 0.0 || warpedUv.x > 1.0 || warpedUv.y < 0.0) { - outputColor = vec4(0.0,0.0,0.0,1.0); - return; - } + return color; + } - vec4 color = inputColor; + float bias = 0.15; - // Add scanlines - float scanLine = sin(warpedUv.y * uScanlineFrequency) * uScanlineIntensity; - color = inputColor; - color.rgb -= scanLine; - // Add noise - float noise = random(uv + uTime) * uNoiseIntensity; - color.rgb += noise; - outputColor = color; - } + void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { + vec4 color = texture2D(inputBuffer, uv); + + float lum = dot(vec3(0.2126, 0.7152, 0.0722), color.rgb); + color.rgb = basicDither(uv, lum); + + outputColor = color; + } ` From dc34e674b9d7bc1f106c5f31598f0d271fa23514 Mon Sep 17 00:00:00 2001 From: git-chad Date: Fri, 22 Nov 2024 15:26:18 -0300 Subject: [PATCH 09/20] basic crt --- .../78.crt-renderer/_components/scene.tsx | 62 +++++++++---- src/experiments/78.crt-renderer/index.tsx | 5 +- .../78.crt-renderer/shaders/shaders.ts | 90 ++++++++----------- 3 files changed, 84 insertions(+), 73 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 8a50726..91cec03 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -1,43 +1,73 @@ -import { OrbitControls, useTexture } from '@react-three/drei' +import { OrbitControls, useGLTF } from '@react-three/drei' +import { useFrame } from '@react-three/fiber' import { EffectComposer, wrapEffect } from '@react-three/postprocessing' +import { useControls } from 'leva' import { Effect } from 'postprocessing' +import { useRef } from 'react' import * as THREE from 'three' -import bn from '../shaders/bluen-128.png' import { fragment } from '../shaders/shaders' class CrtEffectImpl extends Effect { - constructor(uBNoise: THREE.Texture) { + constructor() { super('CrtEffect', fragment, { - uniforms: new Map([['uBNoise', new THREE.Uniform(uBNoise)]]) + uniforms: new Map([ + ['uColorNum', new THREE.Uniform(4.0)], + ['uPixelSize', new THREE.Uniform(4.0)] + ]) }) } - update(renderer: any, inputBuffer: any, deltaTime: number) { - // @ts-expect-error - this.uniforms.get('uTime').value += deltaTime - } + // update(renderer: any, inputBuffer: any, deltaTime: number) { + // // @ts-expect-error + // this.uniforms.get('uTime').value += deltaTime + // } } export const CrtEffect = wrapEffect(CrtEffectImpl) export function Scene() { - const bluenoise = useTexture(bn.src) - bluenoise.wrapS = THREE.RepeatWrapping - bluenoise.wrapT = THREE.RepeatWrapping + const crtEffect = useRef(null) + const { scene } = useGLTF('/models/monitor.glb') + + const { colorNum, pixelSize } = useControls({ + colorNum: { + value: 4.0, + min: 2.0, + max: 8.0, + step: 2.0 + }, + pixelSize: { + value: 4.0, + min: 1.0, + max: 16.0, + step: 2.0 + } + }) + + useFrame(() => { + if (!crtEffect.current) return + + // @ts-expect-error + crtEffect.current.uniforms.get('uColorNum').value = colorNum + // @ts-expect-error + crtEffect.current.uniforms.get('uPixelSize').value = pixelSize + }) return ( <> - - - - + + + + {/* @ts-expect-error */} - + + + {/* */} ) diff --git a/src/experiments/78.crt-renderer/index.tsx b/src/experiments/78.crt-renderer/index.tsx index 514ad7b..973d120 100644 --- a/src/experiments/78.crt-renderer/index.tsx +++ b/src/experiments/78.crt-renderer/index.tsx @@ -13,11 +13,10 @@ function CrtRenderer() { diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index 4a1374d..bdf5c8c 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -1,58 +1,40 @@ export const fragment = /* glsl */ ` -uniform sampler2D uBNoise; - precision highp float; -float random(vec2 c) { - return fract(sin(dot(c.xy, vec2(12.9898, 78.233))) * 43758.5453); - } - - vec3 basicDither(vec2 uv, float lum) { - vec3 color = vec3(0.0); - - if (lum < random(uv)) { - color = vec3(0.0); - } else { - color = vec3(1.0); - } - - return color; - } - - const mat4x4 bayerMatrix4x4 = mat4x4( - 0.0, 8.0, 2.0, 10.0, - 12.0, 4.0, 14.0, 6.0, - 3.0, 11.0, 1.0, 9.0, - 15.0, 7.0, 13.0, 5.0) / 16.0; - - vec3 orderedDither(vec2 uv, float lum) { - vec3 color = vec3(0.0); - float threshold = 0.0; - - int x = int(uv.x * resolution.x) % 4; - int y = int(uv.y * resolution.y) % 4; - - threshold = bayerMatrix4x4[x][y]; - - if (lum < threshold) { - color = vec3(0.0); - } else { - color = vec3(1.0); - } - - return color; - } - - float bias = 0.15; - - - - void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { - vec4 color = texture2D(inputBuffer, uv); - - float lum = dot(vec3(0.2126, 0.7152, 0.0722), color.rgb); - color.rgb = basicDither(uv, lum); - - outputColor = color; - } +uniform float uColorNum; +uniform float uPixelSize; + +const float bayerMatrix8x8[64] = float[64]( + 0.0/ 64.0, 48.0/ 64.0, 12.0/ 64.0, 60.0/ 64.0, 3.0/ 64.0, 51.0/ 64.0, 15.0/ 64.0, 63.0/ 64.0, + 32.0/ 64.0, 16.0/ 64.0, 44.0/ 64.0, 28.0/ 64.0, 35.0/ 64.0, 19.0/ 64.0, 47.0/ 64.0, 31.0/ 64.0, + 8.0/ 64.0, 56.0/ 64.0, 4.0/ 64.0, 52.0/ 64.0, 11.0/ 64.0, 59.0/ 64.0, 7.0/ 64.0, 55.0/ 64.0, + 40.0/ 64.0, 24.0/ 64.0, 36.0/ 64.0, 20.0/ 64.0, 43.0/ 64.0, 27.0/ 64.0, 39.0/ 64.0, 23.0/ 64.0, + 2.0/ 64.0, 50.0/ 64.0, 14.0/ 64.0, 62.0/ 64.0, 1.0/ 64.0, 49.0/ 64.0, 13.0/ 64.0, 61.0/ 64.0, + 34.0/ 64.0, 18.0/ 64.0, 46.0/ 64.0, 30.0/ 64.0, 33.0/ 64.0, 17.0/ 64.0, 45.0/ 64.0, 29.0/ 64.0, + 10.0/ 64.0, 58.0/ 64.0, 6.0/ 64.0, 54.0/ 64.0, 9.0/ 64.0, 57.0/ 64.0, 5.0/ 64.0, 53.0/ 64.0, + 42.0/ 64.0, 26.0/ 64.0, 38.0/ 64.0, 22.0/ 64.0, 41.0/ 64.0, 25.0/ 64.0, 37.0/ 64.0, 21.0 / 64.0 +); + +vec3 dither(vec2 uv, vec3 color) { + int x = int(uv.x * resolution.x) % 8; + int y = int(uv.y * resolution.y) % 8; + float threshold = bayerMatrix8x8[y * 8 + x] - 0.88; + + color.rgb += threshold; + color.r = floor(color.r * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); + color.g = floor(color.g * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); + color.b = floor(color.b * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); + + return color; +} + +void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { + vec2 normalizeduPixelSize = uPixelSize / resolution; + vec2 uvPixel = normalizeduPixelSize * floor(uv / normalizeduPixelSize); + + vec4 color = texture2D(inputBuffer, uvPixel); + color.rgb = dither(uvPixel, color.rgb); + + outputColor = color; +} ` From dad632e2f2ff48896e23f87324e6166e8a590013 Mon Sep 17 00:00:00 2001 From: Vittorio Retrivi Date: Fri, 22 Nov 2024 15:49:12 -0300 Subject: [PATCH 10/20] some effects --- .../78.crt-renderer/_components/scene.tsx | 25 ++++++++++------ src/experiments/78.crt-renderer/index.tsx | 8 ++--- .../78.crt-renderer/shaders/shaders.ts | 29 +++++++++++++++++++ 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 91cec03..b1ce79b 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -1,4 +1,4 @@ -import { OrbitControls, useGLTF } from '@react-three/drei' +import { OrbitControls, PerspectiveCamera, useGLTF } from '@react-three/drei' import { useFrame } from '@react-three/fiber' import { EffectComposer, wrapEffect } from '@react-three/postprocessing' import { useControls } from 'leva' @@ -13,15 +13,20 @@ class CrtEffectImpl extends Effect { super('CrtEffect', fragment, { uniforms: new Map([ ['uColorNum', new THREE.Uniform(4.0)], - ['uPixelSize', new THREE.Uniform(4.0)] + ['uPixelSize', new THREE.Uniform(4.0)], + ['uTime', new THREE.Uniform(0)], + ['uNoiseIntensity', new THREE.Uniform(0.15)], + ['uWarpStrength', new THREE.Uniform(0.75)], + ['uScanlineIntensity', new THREE.Uniform(0.1)], + ['uScanlineFrequency', new THREE.Uniform(1024.0)] ]) }) } - // update(renderer: any, inputBuffer: any, deltaTime: number) { - // // @ts-expect-error - // this.uniforms.get('uTime').value += deltaTime - // } + update(renderer: any, inputBuffer: any, deltaTime: number) { + // @ts-expect-error + this.uniforms.get('uTime').value += deltaTime + } } export const CrtEffect = wrapEffect(CrtEffectImpl) @@ -32,13 +37,13 @@ export function Scene() { const { colorNum, pixelSize } = useControls({ colorNum: { - value: 4.0, + value: 2.0, min: 2.0, max: 8.0, step: 2.0 }, pixelSize: { - value: 4.0, + value: 3.0, min: 1.0, max: 16.0, step: 2.0 @@ -58,10 +63,12 @@ export function Scene() { <> + + - + {/* @ts-expect-error */} diff --git a/src/experiments/78.crt-renderer/index.tsx b/src/experiments/78.crt-renderer/index.tsx index 973d120..5bd5730 100644 --- a/src/experiments/78.crt-renderer/index.tsx +++ b/src/experiments/78.crt-renderer/index.tsx @@ -13,10 +13,10 @@ function CrtRenderer() { diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index bdf5c8c..b94177c 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -4,6 +4,19 @@ precision highp float; uniform float uColorNum; uniform float uPixelSize; +uniform float uTime; + +uniform float uNoiseIntensity; + +uniform float uWarpStrength; + +uniform float uScanlineIntensity; +uniform float uScanlineFrequency; + +float random(vec2 c) { + return fract(sin(dot(c.xy, vec2(12.9898, 78.233))) * 43758.5453); +} + const float bayerMatrix8x8[64] = float[64]( 0.0/ 64.0, 48.0/ 64.0, 12.0/ 64.0, 60.0/ 64.0, 3.0/ 64.0, 51.0/ 64.0, 15.0/ 64.0, 63.0/ 64.0, 32.0/ 64.0, 16.0/ 64.0, 44.0/ 64.0, 28.0/ 64.0, 35.0/ 64.0, 19.0/ 64.0, 47.0/ 64.0, 31.0/ 64.0, @@ -29,12 +42,28 @@ vec3 dither(vec2 uv, vec3 color) { } void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { + vec2 normalizeduPixelSize = uPixelSize / resolution; vec2 uvPixel = normalizeduPixelSize * floor(uv / normalizeduPixelSize); vec4 color = texture2D(inputBuffer, uvPixel); color.rgb = dither(uvPixel, color.rgb); + // Add scanlines + float scanLine = sin(uv.y * uScanlineFrequency) * uScanlineIntensity; + color.rgb *= (1.0 - scanLine); + + + // Add noise + float noise = random(uv + uTime) * uNoiseIntensity; + color.rgb = mix(color.rgb, vec3(1.0), noise); + + // Add vignette + vec2 vignetteUV = uv * (1.0 - uv.yx); + float vignette = vignetteUV.x * vignetteUV.y * 15.0; + vignette = pow(vignette, 0.25); + color.rgb *= vignette; + outputColor = color; } ` From 0596c453b08870f48a143062de18c4c12677a5d0 Mon Sep 17 00:00:00 2001 From: Vittorio Retrivi Date: Fri, 22 Nov 2024 16:02:18 -0300 Subject: [PATCH 11/20] some effects --- .../78.crt-renderer/_components/scene.tsx | 13 ++++++++----- src/experiments/78.crt-renderer/shaders/shaders.ts | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index b1ce79b..c7320c3 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -1,6 +1,10 @@ import { OrbitControls, PerspectiveCamera, useGLTF } from '@react-three/drei' import { useFrame } from '@react-three/fiber' -import { EffectComposer, wrapEffect } from '@react-three/postprocessing' +import { + ChromaticAberration, + EffectComposer, + wrapEffect +} from '@react-three/postprocessing' import { useControls } from 'leva' import { Effect } from 'postprocessing' import { useRef } from 'react' @@ -40,13 +44,13 @@ export function Scene() { value: 2.0, min: 2.0, max: 8.0, - step: 2.0 + step: 1.0 }, pixelSize: { value: 3.0, min: 1.0, max: 16.0, - step: 2.0 + step: 1.0 } }) @@ -71,10 +75,9 @@ export function Scene() { + {/* @ts-expect-error */} - - {/* */} ) diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index b94177c..9d918e7 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -31,7 +31,7 @@ const float bayerMatrix8x8[64] = float[64]( vec3 dither(vec2 uv, vec3 color) { int x = int(uv.x * resolution.x) % 8; int y = int(uv.y * resolution.y) % 8; - float threshold = bayerMatrix8x8[y * 8 + x] - 0.88; + float threshold = bayerMatrix8x8[y * 8 + x] - 1.0; color.rgb += threshold; color.r = floor(color.r * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); From ec658dd36faf906f252f253b6ecce163359f5c35 Mon Sep 17 00:00:00 2001 From: Vittorio Retrivi Date: Fri, 22 Nov 2024 16:19:42 -0300 Subject: [PATCH 12/20] some effects --- .../78.crt-renderer/_components/scene.tsx | 2 +- .../78.crt-renderer/shaders/shaders.ts | 21 ++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index c7320c3..9a259e4 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -21,7 +21,7 @@ class CrtEffectImpl extends Effect { ['uTime', new THREE.Uniform(0)], ['uNoiseIntensity', new THREE.Uniform(0.15)], ['uWarpStrength', new THREE.Uniform(0.75)], - ['uScanlineIntensity', new THREE.Uniform(0.1)], + ['uScanlineIntensity', new THREE.Uniform(0.25)], ['uScanlineFrequency', new THREE.Uniform(1024.0)] ]) }) diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index 9d918e7..6c014cf 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -41,29 +41,40 @@ vec3 dither(vec2 uv, vec3 color) { return color; } + +const float curve = 0.25; + void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { + vec2 curveUV = uv * 2.0 - 1.0; + vec2 offset = curveUV.yx * curve; + curveUV += curveUV * offset * offset; + curveUV = curveUV * 0.5 + 0.5; + vec2 normalizeduPixelSize = uPixelSize / resolution; - vec2 uvPixel = normalizeduPixelSize * floor(uv / normalizeduPixelSize); + vec2 uvPixel = normalizeduPixelSize * floor(curveUV / normalizeduPixelSize); vec4 color = texture2D(inputBuffer, uvPixel); color.rgb = dither(uvPixel, color.rgb); // Add scanlines - float scanLine = sin(uv.y * uScanlineFrequency) * uScanlineIntensity; + float scanLine = sin(curveUV.y * uScanlineFrequency) * uScanlineIntensity; color.rgb *= (1.0 - scanLine); - // Add noise - float noise = random(uv + uTime) * uNoiseIntensity; + float noise = random(curveUV + uTime) * uNoiseIntensity; color.rgb = mix(color.rgb, vec3(1.0), noise); // Add vignette - vec2 vignetteUV = uv * (1.0 - uv.yx); + vec2 vignetteUV = curveUV * (1.0 - curveUV.yx); float vignette = vignetteUV.x * vignetteUV.y * 15.0; vignette = pow(vignette, 0.25); color.rgb *= vignette; + vec2 edge = smoothstep(0., 0.005, curveUV)*(1.-smoothstep(1.-0.005, 1., curveUV)); + color.rgb *= edge.x * edge.y; + + outputColor = color; } ` From 8a205ec2a9d91ce58dc71e65695b3a1c29d2429b Mon Sep 17 00:00:00 2001 From: git-chad Date: Fri, 22 Nov 2024 16:30:00 -0300 Subject: [PATCH 13/20] new leva --- .../78.crt-renderer/_components/scene.tsx | 12 +++++++++++- src/experiments/78.crt-renderer/shaders/shaders.ts | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 9a259e4..6e61783 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -18,6 +18,7 @@ class CrtEffectImpl extends Effect { uniforms: new Map([ ['uColorNum', new THREE.Uniform(4.0)], ['uPixelSize', new THREE.Uniform(4.0)], + ['uThresholdOffset', new THREE.Uniform(8)], ['uTime', new THREE.Uniform(0)], ['uNoiseIntensity', new THREE.Uniform(0.15)], ['uWarpStrength', new THREE.Uniform(0.75)], @@ -39,7 +40,7 @@ export function Scene() { const crtEffect = useRef(null) const { scene } = useGLTF('/models/monitor.glb') - const { colorNum, pixelSize } = useControls({ + const { colorNum, pixelSize, thresholdOffset } = useControls({ colorNum: { value: 2.0, min: 2.0, @@ -51,6 +52,12 @@ export function Scene() { min: 1.0, max: 16.0, step: 1.0 + }, + thresholdOffset: { + value: 8, + min: 0, + max: 16.0, + step: 1.0 } }) @@ -61,6 +68,8 @@ export function Scene() { crtEffect.current.uniforms.get('uColorNum').value = colorNum // @ts-expect-error crtEffect.current.uniforms.get('uPixelSize').value = pixelSize + // @ts-expect-error + crtEffect.current.uniforms.get('uThresholdOffset').value = thresholdOffset }) return ( @@ -75,6 +84,7 @@ export function Scene() { + {/* @ts-expect-error */} {/* @ts-expect-error */} diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index 6c014cf..7174a22 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -3,6 +3,7 @@ precision highp float; uniform float uColorNum; uniform float uPixelSize; +uniform int uThresholdOffset; uniform float uTime; @@ -31,7 +32,7 @@ const float bayerMatrix8x8[64] = float[64]( vec3 dither(vec2 uv, vec3 color) { int x = int(uv.x * resolution.x) % 8; int y = int(uv.y * resolution.y) % 8; - float threshold = bayerMatrix8x8[y * 8 + x] - 1.0; + float threshold = bayerMatrix8x8[y * uThresholdOffset + x] - 1.0; color.rgb += threshold; color.r = floor(color.r * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); From 71b19637bcd5509c734e29c587b8922c71e59d00 Mon Sep 17 00:00:00 2001 From: Vittorio Retrivi Date: Fri, 22 Nov 2024 16:32:19 -0300 Subject: [PATCH 14/20] some effects --- .../78.crt-renderer/shaders/shaders.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index 6c014cf..c4a1b6c 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -17,6 +17,30 @@ float random(vec2 c) { return fract(sin(dot(c.xy, vec2(12.9898, 78.233))) * 43758.5453); } +// 2D Noise based on Morgan McGuire @morgan3d +// https://www.shadertoy.com/view/4dS3Wd +float noise (in vec2 st) { + vec2 i = floor(st); + vec2 f = fract(st); + + float a = random(i); + float b = random(i + vec2(1.0, 0.0)); + float c = random(i + vec2(0.0, 1.0)); + float d = random(i + vec2(1.0, 1.0)); + + vec2 u = f*f*(3.0-2.0*f); + + return mix(a, b, u.x) + + (c - a)* u.y * (1.0 - u.x) + + (d - b) * u.x * u.y; +} + + +void mainUv(inout vec2 uv) { + float shake = (noise(vec2(uv.y) * sin(time * 400.0) * 100.0) - 0.5) * 0.0025; + uv.x += shake * 0.5; +} + const float bayerMatrix8x8[64] = float[64]( 0.0/ 64.0, 48.0/ 64.0, 12.0/ 64.0, 60.0/ 64.0, 3.0/ 64.0, 51.0/ 64.0, 15.0/ 64.0, 63.0/ 64.0, 32.0/ 64.0, 16.0/ 64.0, 44.0/ 64.0, 28.0/ 64.0, 35.0/ 64.0, 19.0/ 64.0, 47.0/ 64.0, 31.0/ 64.0, @@ -41,7 +65,6 @@ vec3 dither(vec2 uv, vec3 color) { return color; } - const float curve = 0.25; void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) { @@ -74,7 +97,6 @@ void mainImage(const in vec4 inputColor, const in vec2 uv, out vec4 outputColor) vec2 edge = smoothstep(0., 0.005, curveUV)*(1.-smoothstep(1.-0.005, 1., curveUV)); color.rgb *= edge.x * edge.y; - outputColor = color; } ` From 942a46d8ee029434a29966e3516d36a90a57ca9e Mon Sep 17 00:00:00 2001 From: git-chad Date: Mon, 25 Nov 2024 09:51:45 -0300 Subject: [PATCH 15/20] monochromatic toggle and more controls --- .../78.crt-renderer/_components/scene.tsx | 133 +++++++++++++++--- .../78.crt-renderer/shaders/shaders.ts | 16 ++- 2 files changed, 123 insertions(+), 26 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 6e61783..ed3b45b 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -5,7 +5,7 @@ import { EffectComposer, wrapEffect } from '@react-three/postprocessing' -import { useControls } from 'leva' +import { folder, useControls } from 'leva' import { Effect } from 'postprocessing' import { useRef } from 'react' import * as THREE from 'three' @@ -15,15 +15,23 @@ import { fragment } from '../shaders/shaders' class CrtEffectImpl extends Effect { constructor() { super('CrtEffect', fragment, { - uniforms: new Map([ + uniforms: new Map< + string, + THREE.Uniform + >([ ['uColorNum', new THREE.Uniform(4.0)], ['uPixelSize', new THREE.Uniform(4.0)], ['uThresholdOffset', new THREE.Uniform(8)], ['uTime', new THREE.Uniform(0)], - ['uNoiseIntensity', new THREE.Uniform(0.15)], + ['uNoiseIntensity', new THREE.Uniform(0.0)], ['uWarpStrength', new THREE.Uniform(0.75)], ['uScanlineIntensity', new THREE.Uniform(0.25)], - ['uScanlineFrequency', new THREE.Uniform(1024.0)] + ['uScanlineFrequency', new THREE.Uniform(1024.0)], + ['uIsMonochrome', new THREE.Uniform(false)], + [ + 'uMonochromeColor', + new THREE.Uniform(new THREE.Vector3(1.0, 0.5, 0.0)) + ] ]) }) } @@ -40,25 +48,86 @@ export function Scene() { const crtEffect = useRef(null) const { scene } = useGLTF('/models/monitor.glb') - const { colorNum, pixelSize, thresholdOffset } = useControls({ - colorNum: { - value: 2.0, - min: 2.0, - max: 8.0, - step: 1.0 - }, - pixelSize: { - value: 3.0, - min: 1.0, - max: 16.0, - step: 1.0 - }, - thresholdOffset: { - value: 8, - min: 0, - max: 16.0, - step: 1.0 - } + const { + colorNum, + pixelSize, + thresholdOffset, + isMonochrome, + monochromeColor, + noiseIntensity, + warpStrength, + scanlineIntensity, + scanlineFrequency + } = useControls({ + Pixelation: folder({ + colorNum: { + value: 2.0, + min: 2.0, + max: 8.0, + step: 1.0, + label: 'Color Numbers' + }, + pixelSize: { + value: 3.0, + min: 1.0, + max: 16.0, + step: 1.0, + label: 'Pixel Size' + }, + thresholdOffset: { + value: 8, + min: 0, + max: 16.0, + step: 1.0, + label: 'Threshold Offset' + } + }), + + 'CRT Effects': folder({ + noiseIntensity: { + value: 0.15, + min: 0, + max: 1, + step: 0.01, + label: 'Noise Intensity' + }, + warpStrength: { + value: 0.75, + min: 0, + max: 2, + step: 0.01, + label: 'Screen Warp' + } + }), + + Scanlines: folder({ + scanlineIntensity: { + value: 0.25, + min: 0, + max: 1, + step: 0.01, + label: 'Intensity' + }, + scanlineFrequency: { + value: 1024, + min: 100, + max: 2048, + step: 1, + label: 'Frequency' + } + }), + + 'Color Mode': folder({ + isMonochrome: { + value: false, + label: 'Monochrome' + }, + monochromeColor: { + value: '#ff8000', + render: (get) => get('isMonochrome'), + label: 'Color' + } + }) }) useFrame(() => { @@ -70,6 +139,24 @@ export function Scene() { crtEffect.current.uniforms.get('uPixelSize').value = pixelSize // @ts-expect-error crtEffect.current.uniforms.get('uThresholdOffset').value = thresholdOffset + // @ts-expect-error + crtEffect.current.uniforms.get('uIsMonochrome').value = isMonochrome + // @ts-expect-error + crtEffect.current.uniforms.get('uNoiseIntensity').value = noiseIntensity + // @ts-expect-error + crtEffect.current.uniforms.get('uWarpStrength').value = warpStrength + // @ts-expect-error + crtEffect.current.uniforms.get('uScanlineIntensity').value = + scanlineIntensity + // @ts-expect-error + crtEffect.current.uniforms.get('uScanlineFrequency').value = + scanlineFrequency + + const color = new THREE.Color(monochromeColor) + // @ts-expect-error + crtEffect.current.uniforms + .get('uMonochromeColor') + .value.set(color.r, color.g, color.b) }) return ( diff --git a/src/experiments/78.crt-renderer/shaders/shaders.ts b/src/experiments/78.crt-renderer/shaders/shaders.ts index fe96448..84f78a4 100644 --- a/src/experiments/78.crt-renderer/shaders/shaders.ts +++ b/src/experiments/78.crt-renderer/shaders/shaders.ts @@ -14,6 +14,9 @@ uniform float uWarpStrength; uniform float uScanlineIntensity; uniform float uScanlineFrequency; +uniform bool uIsMonochrome; +uniform vec3 uMonochromeColor; + float random(vec2 c) { return fract(sin(dot(c.xy, vec2(12.9898, 78.233))) * 43758.5453); } @@ -59,9 +62,16 @@ vec3 dither(vec2 uv, vec3 color) { float threshold = bayerMatrix8x8[y * uThresholdOffset + x] - 1.0; color.rgb += threshold; - color.r = floor(color.r * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); - color.g = floor(color.g * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); - color.b = floor(color.b * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); + + if (uIsMonochrome) { + float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114)); + gray = floor(gray * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); + color.rgb = gray * uMonochromeColor; + } else { + color.r = floor(color.r * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); + color.g = floor(color.g * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); + color.b = floor(color.b * (uColorNum - 1.0) + 0.5) / (uColorNum - 1.0); + } return color; } From 91b06eb84e6ccfefcf7a3739af6fefbe818a4f71 Mon Sep 17 00:00:00 2001 From: git-chad Date: Mon, 25 Nov 2024 10:02:12 -0300 Subject: [PATCH 16/20] fixed missing color picker --- src/experiments/78.crt-renderer/_components/scene.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index ed3b45b..15d8d14 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -46,7 +46,7 @@ export const CrtEffect = wrapEffect(CrtEffectImpl) export function Scene() { const crtEffect = useRef(null) - const { scene } = useGLTF('/models/monitor.glb') + const { scene } = useGLTF('/models/carpenter.glb') const { colorNum, @@ -124,7 +124,7 @@ export function Scene() { }, monochromeColor: { value: '#ff8000', - render: (get) => get('isMonochrome'), + render: (get) => get('Color Mode.isMonochrome'), label: 'Color' } }) From b0a69a042b6c9f42793071307af6e2b0a0bde53b Mon Sep 17 00:00:00 2001 From: git-chad Date: Mon, 25 Nov 2024 10:14:42 -0300 Subject: [PATCH 17/20] set canvas pixel ratio to 1 --- .../78.crt-renderer/_components/scene.tsx | 2 +- src/experiments/78.crt-renderer/index.tsx | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 15d8d14..8306a41 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -46,7 +46,7 @@ export const CrtEffect = wrapEffect(CrtEffectImpl) export function Scene() { const crtEffect = useRef(null) - const { scene } = useGLTF('/models/carpenter.glb') + const { scene } = useGLTF('/models/monitor.glb') const { colorNum, diff --git a/src/experiments/78.crt-renderer/index.tsx b/src/experiments/78.crt-renderer/index.tsx index 5bd5730..69ae05c 100644 --- a/src/experiments/78.crt-renderer/index.tsx +++ b/src/experiments/78.crt-renderer/index.tsx @@ -2,6 +2,7 @@ import { Environment, PerspectiveCamera } from '@react-three/drei' import { Suspense } from 'react' +import { SRGBColorSpace } from 'three' import { R3FCanvasLayout } from '~/components/layout/r3f-canvas-layout' @@ -24,8 +25,20 @@ function CrtRenderer() { ) } -CrtRenderer.Layout = R3FCanvasLayout CrtRenderer.Title = 'CRT Renderer' CrtRenderer.Description = 'CRT Renderer' +CrtRenderer.Layout = (props: any) => ( + +) + export default CrtRenderer From ec80d404991bc8186b1ec17a282a69017502a62c Mon Sep 17 00:00:00 2001 From: git-chad Date: Mon, 25 Nov 2024 10:22:39 -0300 Subject: [PATCH 18/20] contributors --- public/experiments.json | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/public/experiments.json b/public/experiments.json index c340eb3..4cca794 100644 --- a/public/experiments.json +++ b/public/experiments.json @@ -6,7 +6,24 @@ "tags": [], "og": null, "number": 78, - "contributors": [] + "contributors": [ + { + "id": "", + "url": "https://github.com/motiontx", + "name": "Vittorio Retrivi", + "avatarUrl": "https://avatars.githubusercontent.com/u/5198206?v=4", + "email": "vittorioretrivi@gmail.com", + "company": "@basementstudio" + }, + { + "id": "MDQ6VXNlcjgyODQ0NDQ4", + "url": "https://github.com/git-chad", + "name": "Tobi Moccagatta", + "avatarUrl": "https://avatars.githubusercontent.com/u/82844448?u=c71fc101623e8d32861d8512f379b06a3378606f&v=4", + "email": "tobimocc@gmail.com", + "company": "@basementstudio" + } + ] }, { "filename": "77.instanced-grass", @@ -22,7 +39,7 @@ "name": "Tobi Moccagatta", "avatarUrl": "https://avatars.githubusercontent.com/u/82844448?u=c71fc101623e8d32861d8512f379b06a3378606f&v=4", "email": "tobimocc@gmail.com", - "company": "Set & Forget" + "company": "@basementstudio" } ] }, @@ -1505,4 +1522,4 @@ } ] } -] \ No newline at end of file +] From de284b31e9ec599c6ceab226f40098017b0462a2 Mon Sep 17 00:00:00 2001 From: git-chad Date: Tue, 26 Nov 2024 19:46:47 -0300 Subject: [PATCH 19/20] moved shader updates to callback --- public/experiments.json | 21 +--------- .../78.crt-renderer/_components/scene.tsx | 41 ++++++++++++------- 2 files changed, 28 insertions(+), 34 deletions(-) diff --git a/public/experiments.json b/public/experiments.json index 4cca794..07ff302 100644 --- a/public/experiments.json +++ b/public/experiments.json @@ -6,24 +6,7 @@ "tags": [], "og": null, "number": 78, - "contributors": [ - { - "id": "", - "url": "https://github.com/motiontx", - "name": "Vittorio Retrivi", - "avatarUrl": "https://avatars.githubusercontent.com/u/5198206?v=4", - "email": "vittorioretrivi@gmail.com", - "company": "@basementstudio" - }, - { - "id": "MDQ6VXNlcjgyODQ0NDQ4", - "url": "https://github.com/git-chad", - "name": "Tobi Moccagatta", - "avatarUrl": "https://avatars.githubusercontent.com/u/82844448?u=c71fc101623e8d32861d8512f379b06a3378606f&v=4", - "email": "tobimocc@gmail.com", - "company": "@basementstudio" - } - ] + "contributors": [] }, { "filename": "77.instanced-grass", @@ -1522,4 +1505,4 @@ } ] } -] +] \ No newline at end of file diff --git a/src/experiments/78.crt-renderer/_components/scene.tsx b/src/experiments/78.crt-renderer/_components/scene.tsx index 8306a41..f409ada 100644 --- a/src/experiments/78.crt-renderer/_components/scene.tsx +++ b/src/experiments/78.crt-renderer/_components/scene.tsx @@ -7,7 +7,7 @@ import { } from '@react-three/postprocessing' import { folder, useControls } from 'leva' import { Effect } from 'postprocessing' -import { useRef } from 'react' +import { useCallback, useRef } from 'react' import * as THREE from 'three' import { fragment } from '../shaders/shaders' @@ -130,33 +130,44 @@ export function Scene() { }) }) - useFrame(() => { + const updateUniforms = useCallback(() => { if (!crtEffect.current) return + const uniforms = crtEffect.current.uniforms // @ts-expect-error - crtEffect.current.uniforms.get('uColorNum').value = colorNum + uniforms.get('uColorNum').value = colorNum // @ts-expect-error - crtEffect.current.uniforms.get('uPixelSize').value = pixelSize + uniforms.get('uPixelSize').value = pixelSize // @ts-expect-error - crtEffect.current.uniforms.get('uThresholdOffset').value = thresholdOffset + uniforms.get('uThresholdOffset').value = thresholdOffset // @ts-expect-error - crtEffect.current.uniforms.get('uIsMonochrome').value = isMonochrome + uniforms.get('uIsMonochrome').value = isMonochrome // @ts-expect-error - crtEffect.current.uniforms.get('uNoiseIntensity').value = noiseIntensity + uniforms.get('uNoiseIntensity').value = noiseIntensity // @ts-expect-error - crtEffect.current.uniforms.get('uWarpStrength').value = warpStrength + uniforms.get('uWarpStrength').value = warpStrength // @ts-expect-error - crtEffect.current.uniforms.get('uScanlineIntensity').value = - scanlineIntensity + uniforms.get('uScanlineIntensity').value = scanlineIntensity // @ts-expect-error - crtEffect.current.uniforms.get('uScanlineFrequency').value = - scanlineFrequency + uniforms.get('uScanlineFrequency').value = scanlineFrequency const color = new THREE.Color(monochromeColor) // @ts-expect-error - crtEffect.current.uniforms - .get('uMonochromeColor') - .value.set(color.r, color.g, color.b) + uniforms.get('uMonochromeColor').value.set(color.r, color.g, color.b) + }, [ + colorNum, + pixelSize, + thresholdOffset, + isMonochrome, + noiseIntensity, + warpStrength, + scanlineIntensity, + scanlineFrequency, + monochromeColor + ]) + + useFrame(() => { + updateUniforms() }) return ( From 9d3b844c521a2966a7f70b9c12fc09afbe5fa961 Mon Sep 17 00:00:00 2001 From: git-chad Date: Tue, 26 Nov 2024 19:47:41 -0300 Subject: [PATCH 20/20] removed textures --- .../78.crt-renderer/shaders/bluen-128.png | Bin 31364 -> 0 bytes .../78.crt-renderer/shaders/bluen-64.png | Bin 7147 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/experiments/78.crt-renderer/shaders/bluen-128.png delete mode 100644 src/experiments/78.crt-renderer/shaders/bluen-64.png diff --git a/src/experiments/78.crt-renderer/shaders/bluen-128.png b/src/experiments/78.crt-renderer/shaders/bluen-128.png deleted file mode 100644 index 41aa44d430b0026914765a604de1c78b2a44911c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31364 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
d@Ph4W}IU6N>ip9ZUf>M%%Z;tbQ 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 X4P9fhu6cF`00000NkvXXu0mjfy2+mk diff --git a/src/experiments/78.crt-renderer/shaders/bluen-64.png b/src/experiments/78.crt-renderer/shaders/bluen-64.png deleted file mode 100644 index 5730c03ad479a530461ad33202397a7efd57c1be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7147 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