Last active
November 25, 2025 15:36
-
-
Save jmcd/c7b89f58ac92786455be236845084215 to your computer and use it in GitHub Desktop.
This is a port of https://x.com/XorDev/status/1894123951401378051 to C#.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // This is a port of https://x.com/XorDev/status/1894123951401378051 to C#. | |
| // You'll need to install Raylib-cs NuGet package (C# bindings round Raylib) as the thing that allows pixels to happen. | |
| // dotnet add package Raylib-cs | |
| using System.Numerics; | |
| using Raylib_cs; | |
| namespace NotShaders; | |
| internal abstract class Program | |
| { | |
| private static void Main(string[] args) | |
| { | |
| (int width, int height) screenSize = (800, 480); | |
| const int scale = 2; | |
| (int width, int height) textureSize = (screenSize.width / scale, screenSize.height / scale); | |
| Raylib.InitWindow(screenSize.width, screenSize.height, "Not a shader"); | |
| Raylib.SetTargetFPS(144); | |
| var screenBuffer = new byte[textureSize.width * textureSize.height * 4]; | |
| Texture2D screenTexture; | |
| { | |
| var blankImage = Raylib.GenImageColor(textureSize.width, textureSize.height, Color.Black); | |
| screenTexture = Raylib.LoadTextureFromImage(blankImage); | |
| Raylib.UnloadImage(blankImage); | |
| } | |
| Vector2 resV = new(textureSize.width, textureSize.height); | |
| while (!Raylib.WindowShouldClose()) | |
| { | |
| var time = (float)Raylib.GetTime(); | |
| Parallel.For(0, textureSize.height, y => | |
| { | |
| for (var x = 0; x < textureSize.width; ++x) | |
| { | |
| var pixelCoord = new Vector2(x, y); | |
| var pixelColor = ComputeShader(x, y, resV, time); | |
| var index = (y * textureSize.width + x) * 4; | |
| screenBuffer[index + 0] = pixelColor.R; | |
| screenBuffer[index + 1] = pixelColor.G; | |
| screenBuffer[index + 2] = pixelColor.B; | |
| screenBuffer[index + 3] = pixelColor.A; | |
| } | |
| }); | |
| unsafe | |
| { | |
| fixed (byte* data = screenBuffer) | |
| { | |
| Raylib.UpdateTexture(screenTexture, data); | |
| } | |
| } | |
| Raylib.BeginDrawing(); | |
| Raylib.DrawTextureEx(screenTexture, Vector2.Zero, 0, scale, Color.White); | |
| Raylib.DrawFPS(10, 10); | |
| Raylib.EndDrawing(); | |
| } | |
| Raylib.UnloadTexture(screenTexture); | |
| Raylib.CloseWindow(); | |
| } | |
| private static Color ComputeShader(int x, int y, Vector2 resolution, float time) | |
| { | |
| // Lower number, more like a big blob | |
| const int iterLim = 8; | |
| // Normalize xy to about [-1 to 1] range, centered on screen. | |
| Vector2 uv; | |
| { | |
| var xy = new Vector2(x, y); | |
| uv = (xy * 2f - resolution) / resolution.Y; | |
| } | |
| // Calculate length scale factor | |
| Vector2 lenScale; | |
| { | |
| var dotP = Vector2.Dot(uv, uv); | |
| lenScale = new Vector2(4f - 4f * MathF.Abs(0.7f - dotP)); | |
| } | |
| var v = uv * lenScale; | |
| var cumulativeColor = Vector4.Zero; // 'o' in original | |
| var i = Vector2.Zero; | |
| for (var k = 0; k < iterLim; k++) | |
| { | |
| i.Y += 1f; | |
| // Thank you, Claude, for explaining this bit! | |
| // Update position (Warping) | |
| // Corresponds to: v += cos(v.yx() * i.Y + i + t) / i.Y + .7; | |
| var angleInput = v.yx() * i.Y + i + time; | |
| v += Vector2.Cos(angleInput) / i.Y + 0.7f; | |
| // Accumulate Color | |
| // Corresponds to: o += (sin(v.xyyx()) + 1f) * abs(v.x - v.y) | |
| // This adds layers of color based on the warped position. | |
| var colorContribution = Vector4.Sin(v.xyyx()) + 1f; | |
| var intensity = float.Abs(v.X - v.Y); | |
| cumulativeColor += colorContribution * intensity; | |
| } | |
| var paletteVector = new Vector4(-1, 1, 2, 0); | |
| var exponentPart = lenScale.Y - 4f - uv.Y * paletteVector; | |
| cumulativeColor = MathV4.Tanh(5f * Vector4.Exp(exponentPart) / cumulativeColor); | |
| return new Color(cumulativeColor.X, cumulativeColor.Y, cumulativeColor.Z); | |
| } | |
| } | |
| public static class MathV2 | |
| { | |
| extension(Vector2 v) | |
| { | |
| public Vector4 xyyx() | |
| { | |
| return new Vector4(v.X, v.Y, v.Y, v.X); | |
| } | |
| public Vector2 yx() | |
| { | |
| return new Vector2(v.Y, v.X); | |
| } | |
| public static Vector2 operator +(Vector2 a, float s) | |
| { | |
| return new Vector2(a.X + s, a.Y + s); | |
| } | |
| } | |
| } | |
| public static class MathV4 | |
| { | |
| public static Vector4 Tanh(Vector4 v) | |
| { | |
| return new Vector4(MathF.Tanh(v.X), MathF.Tanh(v.Y), MathF.Tanh(v.Z), MathF.Tanh(v.W)); | |
| } | |
| extension(Vector4) | |
| { | |
| public static Vector4 operator +(Vector4 v, float s) | |
| { | |
| return new Vector4(v.X + s, v.Y + s, v.Z + s, v.W + s); | |
| } | |
| public static Vector4 operator *(float s, Vector4 v) | |
| { | |
| return new Vector4(s * v.X, s * v.Y, s * v.Z, s * v.W); | |
| } | |
| public static Vector4 operator -(float s, Vector4 a) | |
| { | |
| return new Vector4(s - a.X, s - a.Y, s - a.Z, s - a.W); | |
| } | |
| } | |
| } |
Author
jmcd
commented
Nov 25, 2025

Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment