S&box Wiki

Compute Shaders

TODO - this isn't finished, it's not an exhaustive example.. there's lots more to add here.

You can use compute shaders to do stuff. Here's how you can write a really simple compute texture to generate a texture on the GPU at runtime.

C# side

You'll first need to create a SceneCustomObject so that you can call Dispatch within the render loop:

public class MySceneObject : SceneCustomObject { private MyTextureGenerator myTextureGenerator; public MySceneObject( SceneWorld sceneWorld ) : base( sceneWorld ) { myTextureGenerator = new(); } public override void RenderSceneObject() { base.RenderSceneObject(); myTextureGenerator.Render(); } }

You'll also probably want to wrap your compute stuff in a nice class so that everything's nice and tidy:

public MyTextureGenerator() { // Create a texture that we can use texture = Texture.Create( 512, 512 ) .WithUAVBinding() // Needs to have this if we're using it in a compute shader .WithFormat( ImageFormat.RGBA16161616F ) // Other formats are available :-) .Finish(); computeShader = new ComputeShader( "my_compute_shader" ); // This should be the name of your shader } public void Render() { computeShader.Attributes.Set( "OutputTexture", texture ); computeShader.Dispatch( texture.Width, texture.Height, 1 ); }

You also want to actually use that scene object in an entity... here's how you can do that:

public class MyModel : ModelEntity { private MySceneObject mySceneObject; public override void Spawn() { base.Spawn(); Transmit = TransmitType.Always; } public override void ClientSpawn() { mySceneObject = new( Map.Scene ) { Transform = this.Transform, Position = this.Position, Bounds = this.CollisionBounds + this.Position }; } protected override void OnDestroy() { base.OnDestroy(); mySceneObject?.Delete(); } }

Spawn your entity somewhere, make sure it's visible.

Shader

Compute shaders are similar to normal VS/FS shaders, but they're a bit shorter and you can forego a lot of the includes. Here's a really simple one that'll generate a solid pink texture:

//========================================================================================================================= // Optional //========================================================================================================================= HEADER { DevShader = true; Description = "My Cool Compute Shader"; } //========================================================================================================================= // Optional //========================================================================================================================= FEATURES { } //========================================================================================================================= MODES { Default(); } //========================================================================================================================= COMMON { #include "common/shared.hlsl" } //========================================================================================================================= CS { // Output texture RWTexture2D<float4> g_tOutput< Attribute( "OutputTexture" ); >; [numthreads(8, 8, 1)] void MainCs( uint uGroupIndex : SV_GroupIndex, uint3 vThreadId : SV_DispatchThreadID ) { g_tOutput[vThreadId.xy] = float4( 1, 0, 1, 1 ); } }

Tips

If you're writing a compute shader you probably want to see its contents. You can do that really easily using Render.Draw2D.Quad.

private static void DrawTexture( Texture texture, Rect rect ) { Render.Draw2D.Texture = texture; Render.Draw2D.Quad( rect.BottomLeft, rect.BottomRight, rect.TopRight, rect.TopLeft ); }