Surface shaders (experimental)
This chapter is experimental. You can write what a Renderer draws in C#.
Forms that compile (5)
Section titled “Forms that compile (5)”| Syntax | Description | Note |
|---|---|---|
[Surface] static Color4 M(SurfaceId id, ...) | Deciding the color of a pixel | Read a buffer by position with buf.Sample(id.UV); the nearest cell is returned as is, without interpolation. Put it on a Renderer with Gpu.Show(nameof(M), rend, buf, gain), passing the values on every call. |
Vector3.Dot(id.Normal, toLight) | Shading from the surface normal | |
Vector3.Dot(id.Normal, id.ViewDir) | Using the view direction | |
static Color4 M(SurfaceId id, GpuBuffer2D buf, int n, bool b, Vector2 v, Vector3 w) | Value-type parameters | Pass the values at the same positions in Gpu.Show. |
[Surface] static Vector3 M(VertexId v, ...) | Moving vertices |
Forms that don’t compile (4)
Section titled “Forms that don’t compile (4)”| Syntax | Description | Error | Reason | Alternative |
|---|---|---|---|---|
static Color4 M(int n) | The first parameter is neither type, so the role is undetermined | TUKI0001 | by design | Take SurfaceId to return a color, VertexId to return a position |
static float M(SurfaceId id) | The color side does not return a pixel value | TUKI0001 | by design | Return Color4 |
static Vector3 M(VertexId v) alone | Nothing is drawn without a color side | TUKI0001 | by design | Write the color side under the same name |
id.Shadow | How shadows land depends on how other objects are drawn | CS1061 | by design | Use a hand-written shader |
Members of the identifiers
Section titled “Members of the identifiers”| Syntax | Types | Contents |
|---|---|---|
id.UV | Vector2 | Position on the surface (0 to 1). The value set on the mesh arrives as is |
id.Normal | Vector3 | Direction the surface faces (world space, length 1). It keeps the original direction even when vertices are moved |
id.ViewDir | Vector3 | Direction from that pixel toward the viewer (world space, length 1) |
v.Position | Vector3 | Position of the vertex (mesh space). The return value is in the same space |
v.UV | Vector2 | Position on the surface (0 to 1) |
SurfaceId and VertexId have these 5 members; any other name is CS1061.
Syntax
Section titled “Syntax”Deciding the color of a pixel
Section titled “Deciding the color of a pixel”The color you return becomes that pixel.
using UnityEngine;using Tsukimi;
public class ParSurfaceTint : TsukimiBehaviour{ public Renderer display; public float gain; private GpuBuffer2D heat;
[Kernel] static Color4 Step(KernelId id, GpuBuffer2D prev) { return new Color4(prev[id].R * 0.99f, 0f, 0f, 1f); }
[Surface] static Color4 Tint(SurfaceId id, GpuBuffer2D buf, float gain) { float t = buf.Sample(id.UV).R * gain; return new Color4(t, t * t, 0f, 1f); }
void Start() { heat = Gpu.Buffer(64, 64); }
void Update() { Gpu.Show(nameof(Tint), display, heat, gain); }}Shading from the surface normal
Section titled “Shading from the surface normal”You choose the light direction yourself; lights placed in the scene are not readable.
using UnityEngine;using Tsukimi;
public class ParSurfaceShading : TsukimiBehaviour{ public Renderer display; private GpuBuffer2D buf;
[Surface] static Color4 Lit(SurfaceId id, GpuBuffer2D buf) { Vector3 toLight = new Vector3(0.3f, 1f, 0.2f).normalized; float lit = Mathf.Clamp01(Vector3.Dot(id.Normal, toLight) * 0.5f + 0.5f); Color4 albedo = buf.Sample(id.UV); return albedo * lit; }
void Start() { buf = Gpu.Buffer(32, 32); }
void Update() { Gpu.Show(nameof(Lit), display, buf); }}Using the view direction
Section titled “Using the view direction”Useful for effects that strengthen at grazing angles.
using UnityEngine;using Tsukimi;
public class ParSurfaceRim : TsukimiBehaviour{ public Renderer display; public Vector4 glow; private GpuBuffer2D mist;
[Surface] static Color4 Fogged(SurfaceId id, GpuBuffer2D mist, Vector4 glow) { float m = mist.Sample(id.UV).R; float rim = 1f - Mathf.Abs(Vector3.Dot(id.Normal, id.ViewDir)); return Color4.Lerp(new Color4(m, m, m, 1f), new Color4(glow), rim); }
void Start() { mist = Gpu.Buffer(64, 64); }
void Update() { Gpu.Show(nameof(Fogged), display, mist, glow); }}Value-type parameters
Section titled “Value-type parameters”Besides GpuBuffer2D and float, parameters can be int bool Vector2 Vector3 Vector4.
using UnityEngine;using Tsukimi;
public class ParSurfaceValueArgs : TsukimiBehaviour{ public Renderer display; public int bands; public bool invert; public Vector2 shift; public Vector3 tint; private GpuBuffer2D buf;
// Besides GpuBuffer2D and float, parameters can be int, bool, Vector2, Vector3, or Vector4 [Surface] static Color4 Banded(SurfaceId id, GpuBuffer2D buf, int bands, bool invert, Vector2 shift, Vector3 tint) { float v = buf.Sample(id.UV + shift).R; float stepped = Mathf.Floor(v * bands) / 8f; float t = invert ? 1f - stepped : stepped; return new Color4(t * tint.x, t * tint.y, t * tint.z, 1f); }
void Start() { buf = Gpu.Buffer(64, 64); }
void Update() { Gpu.Show(nameof(Banded), display, buf, bands, invert, shift, tint); }}Moving vertices
Section titled “Moving vertices”Writing the color side under the same name makes them one shader.
using UnityEngine;using Tsukimi;
public class ParSurfaceVertex : TsukimiBehaviour{ public Renderer display; public float amplitude; private GpuBuffer2D height;
static float ToHeight(Color4 c, float amplitude) { return (c.R - 0.5f) * amplitude; }
[Kernel] static Color4 Wave(KernelId id, GpuBuffer2D prev) { return new Color4(prev[id].R, 0.5f, 0.5f, 1f); }
[Surface] static Vector3 Surf(VertexId v, GpuBuffer2D height, float amplitude) { return v.Position + new Vector3(0f, ToHeight(height.Sample(v.UV), amplitude), 0f); }
[Surface] static Color4 Surf(SurfaceId id, GpuBuffer2D height, float amplitude) { float d = 1f / height.width; float hx = ToHeight(height.Sample(id.UV + new Vector2(d, 0f)), amplitude) - ToHeight(height.Sample(id.UV - new Vector2(d, 0f)), amplitude); float hy = ToHeight(height.Sample(id.UV + new Vector2(0f, d)), amplitude) - ToHeight(height.Sample(id.UV - new Vector2(0f, d)), amplitude); Vector3 n = new Vector3(-hx, 0.2f, -hy).normalized; float lit = Mathf.Clamp01(Vector3.Dot(n, new Vector3(0.3f, 1f, 0.2f).normalized) * 0.5f + 0.5f); return new Color4(0.2f * lit, 0.5f * lit, 0.8f * lit, 1f); }
void Start() { height = Gpu.Buffer(128, 128); }
void Update() { Gpu.Show(nameof(Surf), display, height, amplitude); }}