mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 07:46:12 +03:00
renderer: shadertoy functions
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
pub const c = @import("c.zig");
|
||||
pub const testing = @import("test.zig");
|
||||
pub usingnamespace @import("init.zig");
|
||||
pub usingnamespace @import("program.zig");
|
||||
pub usingnamespace @import("shader.zig");
|
||||
|
@ -35,7 +35,7 @@ pub const Program = opaque {
|
||||
}
|
||||
|
||||
pub fn spirvGetPtr(self: *Program) ![*]u8 {
|
||||
return c.glslang_program_SPIRV_get_ptr(@ptrCast(self));
|
||||
return @ptrCast(c.glslang_program_SPIRV_get_ptr(@ptrCast(self)));
|
||||
}
|
||||
|
||||
pub fn spirvGetMessages(self: *Program) ![:0]const u8 {
|
||||
|
@ -15,6 +15,7 @@ const WasmTarget = @import("os/wasm/target.zig").Target;
|
||||
pub usingnamespace @import("renderer/cursor.zig");
|
||||
pub usingnamespace @import("renderer/message.zig");
|
||||
pub usingnamespace @import("renderer/size.zig");
|
||||
pub const shadertoy = @import("renderer/shadertoy.zig");
|
||||
pub const Metal = @import("renderer/Metal.zig");
|
||||
pub const OpenGL = @import("renderer/OpenGL.zig");
|
||||
pub const WebGL = @import("renderer/WebGL.zig");
|
||||
|
27
src/renderer/shaders/shadertoy_prefix.glsl
Normal file
27
src/renderer/shaders/shadertoy_prefix.glsl
Normal file
@ -0,0 +1,27 @@
|
||||
#version 430 core
|
||||
|
||||
layout(binding = 0) uniform Globals {
|
||||
uniform vec3 iResolution;
|
||||
uniform float iTime;
|
||||
uniform float iTimeDelta;
|
||||
uniform float iFrameRate;
|
||||
uniform int iFrame;
|
||||
uniform float iChannelTime[4];
|
||||
uniform vec3 iChannelResolution[4];
|
||||
uniform vec4 iMouse;
|
||||
uniform vec4 iDate;
|
||||
uniform float iSampleRate;
|
||||
};
|
||||
|
||||
layout(binding = 0) uniform sampler2D iChannel0;
|
||||
layout(binding = 1) uniform sampler2D iChannel1;
|
||||
layout(binding = 2) uniform sampler2D iChannel2;
|
||||
layout(binding = 3) uniform sampler2D iChannel3;
|
||||
|
||||
layout(location = 0) in vec4 gl_FragCoord;
|
||||
layout(location = 0) out vec4 _fragColor;
|
||||
|
||||
#define texture2D texture
|
||||
|
||||
void mainImage( out vec4 fragColor, in vec2 fragCoord );
|
||||
void main() { mainImage (_fragColor, gl_FragCoord.xy); }
|
59
src/renderer/shaders/test_shadertoy_crt.glsl
Normal file
59
src/renderer/shaders/test_shadertoy_crt.glsl
Normal file
@ -0,0 +1,59 @@
|
||||
// This shader is NOT BUILD INTO Ghostty. This is only here for unit tests.
|
||||
|
||||
// Loosely based on postprocessing shader by inigo quilez, License Creative
|
||||
// Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
|
||||
|
||||
vec2 curve(vec2 uv)
|
||||
{
|
||||
uv = (uv - 0.5) * 2.0;
|
||||
uv *= 1.1;
|
||||
uv.x *= 1.0 + pow((abs(uv.y) / 5.0), 2.0);
|
||||
uv.y *= 1.0 + pow((abs(uv.x) / 4.0), 2.0);
|
||||
uv = (uv / 2.0) + 0.5;
|
||||
uv = uv *0.92 + 0.04;
|
||||
return uv;
|
||||
}
|
||||
void mainImage( out vec4 fragColor, in vec2 fragCoord )
|
||||
{
|
||||
vec2 q = fragCoord.xy / iResolution.xy;
|
||||
vec2 uv = q;
|
||||
uv = curve( uv );
|
||||
vec3 oricol = texture( iChannel0, vec2(q.x,q.y) ).xyz;
|
||||
vec3 col;
|
||||
float x = sin(0.3*iTime+uv.y*21.0)*sin(0.7*iTime+uv.y*29.0)*sin(0.3+0.33*iTime+uv.y*31.0)*0.0017;
|
||||
|
||||
col.r = texture(iChannel0,vec2(x+uv.x+0.001,uv.y+0.001)).x+0.05;
|
||||
col.g = texture(iChannel0,vec2(x+uv.x+0.000,uv.y-0.002)).y+0.05;
|
||||
col.b = texture(iChannel0,vec2(x+uv.x-0.002,uv.y+0.000)).z+0.05;
|
||||
col.r += 0.08*texture(iChannel0,0.75*vec2(x+0.025, -0.027)+vec2(uv.x+0.001,uv.y+0.001)).x;
|
||||
col.g += 0.05*texture(iChannel0,0.75*vec2(x+-0.022, -0.02)+vec2(uv.x+0.000,uv.y-0.002)).y;
|
||||
col.b += 0.08*texture(iChannel0,0.75*vec2(x+-0.02, -0.018)+vec2(uv.x-0.002,uv.y+0.000)).z;
|
||||
|
||||
col = clamp(col*0.6+0.4*col*col*1.0,0.0,1.0);
|
||||
|
||||
float vig = (0.0 + 1.0*16.0*uv.x*uv.y*(1.0-uv.x)*(1.0-uv.y));
|
||||
col *= vec3(pow(vig,0.3));
|
||||
|
||||
col *= vec3(0.95,1.05,0.95);
|
||||
col *= 2.8;
|
||||
|
||||
float scans = clamp( 0.35+0.35*sin(3.5*iTime+uv.y*iResolution.y*1.5), 0.0, 1.0);
|
||||
|
||||
float s = pow(scans,1.7);
|
||||
col = col*vec3( 0.4+0.7*s) ;
|
||||
|
||||
col *= 1.0+0.01*sin(110.0*iTime);
|
||||
if (uv.x < 0.0 || uv.x > 1.0)
|
||||
col *= 0.0;
|
||||
if (uv.y < 0.0 || uv.y > 1.0)
|
||||
col *= 0.0;
|
||||
|
||||
col*=1.0-0.65*vec3(clamp((mod(fragCoord.x, 2.0)-1.0)*2.0,0.0,1.0));
|
||||
|
||||
float comp = smoothstep( 0.1, 0.9, sin(iTime) );
|
||||
|
||||
// Remove the next line to stop cross-fade between original and postprocess
|
||||
// col = mix( col, oricol, comp );
|
||||
|
||||
fragColor = vec4(col,1.0);
|
||||
}
|
12
src/renderer/shaders/test_shadertoy_invalid.glsl
Normal file
12
src/renderer/shaders/test_shadertoy_invalid.glsl
Normal file
@ -0,0 +1,12 @@
|
||||
vec2 curve(vec2 uv)
|
||||
{
|
||||
uv = (uv - 0.5) * 2.0;
|
||||
uv *= 1.1;
|
||||
uv.x *= 1.0 + pow((abs(uv.y) / 5.0), 2.0);
|
||||
uv.y *= 1.0 + pow((abs(uv.x) / 4.0), 2.0);
|
||||
uv = (uv / 2.0) + 0.5;
|
||||
uv = uv *0.92 + 0.04;
|
||||
return uv;
|
||||
}
|
||||
|
||||
// Missing mainImage!
|
142
src/renderer/shadertoy.zig
Normal file
142
src/renderer/shadertoy.zig
Normal file
@ -0,0 +1,142 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const glslang = @import("glslang");
|
||||
|
||||
/// Convert a ShaderToy shader into valid GLSL.
|
||||
///
|
||||
/// ShaderToy shaders aren't full shaders, they're just implementing a
|
||||
/// mainImage function and don't define any of the uniforms. This function
|
||||
/// will convert the ShaderToy shader into a valid GLSL shader that can be
|
||||
/// compiled and linked.
|
||||
pub fn glslFromShader(writer: anytype, src: []const u8) !void {
|
||||
const prefix = @embedFile("shaders/shadertoy_prefix.glsl");
|
||||
try writer.writeAll(prefix);
|
||||
try writer.writeAll("\n\n");
|
||||
try writer.writeAll(src);
|
||||
}
|
||||
|
||||
/// Convert a GLSL shader into SPIR-V assembly.
|
||||
pub fn spirvFromGlsl(
|
||||
writer: anytype,
|
||||
errlog: ?*SpirvLog,
|
||||
src: [:0]const u8,
|
||||
) !void {
|
||||
// So we can run unit tests without fear.
|
||||
if (builtin.is_test) try glslang.testing.ensureInit();
|
||||
|
||||
const c = glslang.c;
|
||||
const input: c.glslang_input_t = .{
|
||||
.language = c.GLSLANG_SOURCE_GLSL,
|
||||
.stage = c.GLSLANG_STAGE_FRAGMENT,
|
||||
.client = c.GLSLANG_CLIENT_VULKAN,
|
||||
.client_version = c.GLSLANG_TARGET_VULKAN_1_2,
|
||||
.target_language = c.GLSLANG_TARGET_SPV,
|
||||
.target_language_version = c.GLSLANG_TARGET_SPV_1_5,
|
||||
.code = src.ptr,
|
||||
.default_version = 100,
|
||||
.default_profile = c.GLSLANG_NO_PROFILE,
|
||||
.force_default_version_and_profile = 0,
|
||||
.forward_compatible = 0,
|
||||
.messages = c.GLSLANG_MSG_DEFAULT_BIT,
|
||||
.resource = c.glslang_default_resource(),
|
||||
};
|
||||
|
||||
const shader = try glslang.Shader.create(&input);
|
||||
defer shader.delete();
|
||||
|
||||
shader.preprocess(&input) catch |err| {
|
||||
if (errlog) |ptr| ptr.fromShader(shader) catch {};
|
||||
return err;
|
||||
};
|
||||
shader.parse(&input) catch |err| {
|
||||
if (errlog) |ptr| ptr.fromShader(shader) catch {};
|
||||
return err;
|
||||
};
|
||||
|
||||
const program = try glslang.Program.create();
|
||||
defer program.delete();
|
||||
program.addShader(shader);
|
||||
program.link(
|
||||
c.GLSLANG_MSG_SPV_RULES_BIT |
|
||||
c.GLSLANG_MSG_VULKAN_RULES_BIT,
|
||||
) catch |err| {
|
||||
if (errlog) |ptr| ptr.fromProgram(program) catch {};
|
||||
return err;
|
||||
};
|
||||
program.spirvGenerate(c.GLSLANG_STAGE_FRAGMENT);
|
||||
const size = program.spirvGetSize();
|
||||
const ptr = try program.spirvGetPtr();
|
||||
try writer.writeAll(ptr[0..size]);
|
||||
}
|
||||
|
||||
/// Retrieve errors from spirv compilation.
|
||||
pub const SpirvLog = struct {
|
||||
alloc: Allocator,
|
||||
info: [:0]const u8 = "",
|
||||
debug: [:0]const u8 = "",
|
||||
|
||||
pub fn deinit(self: *const SpirvLog) void {
|
||||
if (self.info.len > 0) self.alloc.free(self.info);
|
||||
if (self.debug.len > 0) self.alloc.free(self.debug);
|
||||
}
|
||||
|
||||
fn fromShader(self: *SpirvLog, shader: *glslang.Shader) !void {
|
||||
const info = try shader.getInfoLog();
|
||||
const debug = try shader.getDebugInfoLog();
|
||||
self.info = "";
|
||||
self.debug = "";
|
||||
if (info.len > 0) self.info = try self.alloc.dupeZ(u8, info);
|
||||
if (debug.len > 0) self.debug = try self.alloc.dupeZ(u8, debug);
|
||||
}
|
||||
|
||||
fn fromProgram(self: *SpirvLog, program: *glslang.Program) !void {
|
||||
const info = try program.getInfoLog();
|
||||
const debug = try program.getDebugInfoLog();
|
||||
self.info = "";
|
||||
self.debug = "";
|
||||
if (info.len > 0) self.info = try self.alloc.dupeZ(u8, info);
|
||||
if (debug.len > 0) self.debug = try self.alloc.dupeZ(u8, debug);
|
||||
}
|
||||
};
|
||||
|
||||
/// Convert ShaderToy shader to null-terminated glsl for testing.
|
||||
fn testGlslZ(alloc: Allocator, src: []const u8) ![:0]const u8 {
|
||||
var list = std.ArrayList(u8).init(alloc);
|
||||
defer list.deinit();
|
||||
try glslFromShader(list.writer(), src);
|
||||
return try list.toOwnedSliceSentinel(0);
|
||||
}
|
||||
|
||||
test "spirv" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const src = try testGlslZ(alloc, test_crt);
|
||||
defer alloc.free(src);
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
var buf_stream = std.io.fixedBufferStream(&buf);
|
||||
const writer = buf_stream.writer();
|
||||
try spirvFromGlsl(writer, null, src);
|
||||
}
|
||||
|
||||
test "spirv invalid" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
||||
const src = try testGlslZ(alloc, test_invalid);
|
||||
defer alloc.free(src);
|
||||
|
||||
var buf: [4096]u8 = undefined;
|
||||
var buf_stream = std.io.fixedBufferStream(&buf);
|
||||
const writer = buf_stream.writer();
|
||||
|
||||
var errlog: SpirvLog = .{ .alloc = alloc };
|
||||
defer errlog.deinit();
|
||||
try testing.expectError(error.GlslangFailed, spirvFromGlsl(writer, &errlog, src));
|
||||
try testing.expect(errlog.info.len > 0);
|
||||
}
|
||||
|
||||
const test_crt = @embedFile("shaders/test_shadertoy_crt.glsl");
|
||||
const test_invalid = @embedFile("shaders/test_shadertoy_invalid.glsl");
|
Reference in New Issue
Block a user