Skip to content

Commit

Permalink
webgpu-shader-toy v2024.09.08
Browse files Browse the repository at this point in the history
  • Loading branch information
ypujante committed Sep 8, 2024
1 parent 7906511 commit 607a587
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 16 deletions.
25 changes: 10 additions & 15 deletions webgpu-shader-toy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
width: 100%;
background-color: rgba(0, 0, 0, 0);
pointer-events: none;
z-index: 2;
}

#canvases #drop-zone #drop-zone-content {
Expand Down Expand Up @@ -297,7 +298,7 @@ <h3 class="error">This tool requires javascript (currently disabled)!</h3>
<div class="info">
<p>WebGPU Shader Toy is a free tool developed by pongasoft for experimenting with WebGPU fragment shaders and the <a href="https://www.w3.org/TR/WGSL/">WebGPU Shading Language</a> (WGSL).</p>
<p>This tool has no tracking, ads or server-side component: it is running 100% in your browser. It uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage">localStorage</a> to store information across browser sessions. </p>
<p>If you are having issues, try to <a href="?reset">reset</a>. You can provide feedback or report issues on <a href="https://github.com/pongasoft/webgpu-shader-toy">GitHub</a>.</p>
<p>If you are having issues, try to <a href="#" onclick="return reset();">reset</a>. You can provide feedback or report issues on <a href="https://github.com/pongasoft/webgpu-shader-toy">GitHub</a>.</p>
<p>This project originated from my interest in learning about WebGPU. It also served as a platform for developing a more comprehensive application for my library: <a href="https://github.com/pongasoft/emscripten-glfw">emscripten-glfw</a>.</p>
<p>This tool is using <a href="https://emscripten.org/">emscripten</a> (web assembly) for the compiler, <a href="https://github.com/ocornut/imgui">ImGui</a> and GLFW for the GUI (<a href="https://github.com/pongasoft/emscripten-glfw">emscripten-glfw</a> for the backend and WebGPU for the renderer.)</p>
<p>It has been inspired by the popular project <a href="https://www.shadertoy.com/">Shadertoy</a>.</p>
Expand Down Expand Up @@ -333,27 +334,21 @@ <h3>Something went wrong</h3>
<li>An unexpected problem has happened. Please check the console for more details.</li>
<li>Message (if any): <span class="message"></span></li>
<li>You can try to <a href="">reload</a> the tool.</li>
<li>If it doesn't work, you can try to <a href="?reset">reset</a> the tool.</li>
<li>If it doesn't work, you can try to <a href="#" onclick="return reset();">reset</a> the tool.</li>
<li>You can <a href="https://github.com/pongasoft/webgpu-shader-toy/issues" target="_blank">report</a> this problem.</li>
</ul>
</div>

<div id="footer"><a href="https://pongasoft.com" target="_blank">pongasoft</a> | <a href="?reset">Reset</a> | <a href="https://github.com/pongasoft/webgpu-shader-toy" target="_blank">v2024.08.18</a></div>
<div id="footer"><a href="https://pongasoft.com" target="_blank">pongasoft</a> | <a href="#" onclick="return reset();">Reset</a> | <a href="https://github.com/pongasoft/webgpu-shader-toy" target="_blank">v2024.09.08</a></div>

<script type='text/javascript'>
var Module = {isResizing: false};

// Detect reset: clean storage + reload the page
{
let urlParams = new URLSearchParams(window.location.search);
if (urlParams.has("reset")) {
localStorage.removeItem('shader_toy::State');
Module.resetRequested = true;
urlParams.delete("reset");
let newUrl = window.location.pathname + (urlParams.size > 0 ? ('?' + urlParams.toString()) : '');
window.history.replaceState({}, '', newUrl);
location.reload();
}
function reset() {
localStorage.removeItem('shader_toy::State');
Module.resetRequested = true;
location.reload(true);
return false;
}

function addClass(cssSelector, c) {
Expand Down Expand Up @@ -447,7 +442,7 @@ <h3>Something went wrong</h3>
// load the main wasm code
let scriptTag = document.createElement("script");
scriptTag.type = "text/javascript";
scriptTag.src = "index.js?v2024.08.18";
scriptTag.src = "index.js?v2024.09.08";
scriptTag.async = true;
document.head.appendChild(scriptTag);
})();
Expand Down
2 changes: 1 addition & 1 deletion webgpu-shader-toy/index.js

Large diffs are not rendered by default.

Binary file modified webgpu-shader-toy/index.wasm
Binary file not shown.
65 changes: 65 additions & 0 deletions webgpu-shader-toy/shaders/Fire.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// ported from https://www.shadertoy.com/view/4ttGWM
fn rand(n: vec2f) -> f32 {
return fract(sin(cos(dot(n, vec2f(12.9898,12.1414)))) * 83758.5453);
}

fn noise(n: vec2f) -> f32 {
const d = vec2f(0.0, 1.0);
var b = floor(n);
var f = smoothstep(vec2(0.0), vec2(1.0), fract(n));
return mix(mix(rand(b), rand(b + d.yx), f.x), mix(rand(b + d.xy), rand(b + d.yy), f.x), f.y);
}

fn fbm(n: vec2f) -> f32 {
var total = 0.0;
var amplitude = inputs.size.x / inputs.size.y * 0.5;
var vn = n;
for (var i : i32 = 0; i <5; i++) {
total += noise(vn) * amplitude;
vn += vn*1.7;
amplitude *= 0.47;
}
return total;
}

@fragment
fn fragmentMain(@builtin(position) pos: vec4f) -> @location(0) vec4f {
const c1 = vec3f(0.5, 0.0, 0.1);
const c2 = vec3f(0.9, 0.1, 0.0);
const c3 = vec3f(0.2, 0.1, 0.7);
const c4 = vec3f(1.0, 0.9, 0.1);
const c5 = vec3f(0.1);
const c6 = vec3f(0.9);

var iTime = inputs.time;
var fragCoord = vec2f(pos.x, inputs.size.y - pos.y);
var iResolution = inputs.size;

const speed = vec2(0.1, 0.9);
var shift = 1.327+sin(iTime*2.0)/2.4;
const alpha = 1.0;

var dist = 3.5-sin(iTime*0.4)/1.89;

var uv = fragCoord.xy / iResolution.xy;
var p = fragCoord.xy * dist / iResolution.xx;
p += sin(p.yx*4.0+vec2f(.2,-.3)*iTime)*0.04;
p += sin(p.yx*8.0+vec2f(.6,.1)*iTime)*0.01;

p.x -= iTime/1.1;
var q = fbm(p - iTime * 0.3+1.0*sin(iTime+0.5)/2.0);
var qb = fbm(p - iTime * 0.4+0.1*cos(iTime)/2.0);
var q2 = fbm(p - iTime * 0.44 - 5.0*cos(iTime)/2.0) - 6.0;
var q3 = fbm(p - iTime * 0.9 - 10.0*cos(iTime)/15.0) - 4.0;
var q4 = fbm(p - iTime * 1.4 - 20.0*sin(iTime)/14.0)+2.0;
q = (q + qb - .4 * q2 - 2.0 *q3 + .6*q4)/3.8;
var r = vec2f(fbm(p + q /2.0 + iTime * speed.x - p.x - p.y), fbm(p + q - iTime * speed.y));
var c = mix(c1, c2, fbm(p + r)) + mix(c3, c4, r.x) - mix(c5, c6, r.y);
var color = vec3f(1.0/(pow(c+1.61,vec3f(4.0))) * cos(shift * fragCoord.y / iResolution.y));

color = vec3f(1.0,.2,.05)/(pow((r.y+r.y)* max(.0,p.y)+0.1, 4.0));;
// color += (texture(iChannel0,uv*0.6+vec2f(.5,.1)).xyz*0.01*pow((r.y+r.y)*.65,5.0)+0.055)*mix( vec3(.9,.4,.3),vec3(.7,.5,.2), uv.y);
color = color/(1.0+max(vec3(0),color));

return vec4f(color.x, color.y, color.z, alpha);
}
119 changes: 119 additions & 0 deletions webgpu-shader-toy/shaders/Marble.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Ported from https://www.shadertoy.com/view/MtX3Ws
// License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
// Created by S. Guillitte 2015

const zoom=1.5;

fn cmul( a: vec2f, b: vec2f ) -> vec2f {
return vec2f( a.x*b.x - a.y*b.y, a.x*b.y + a.y*b.x );
}
fn csqr( a: vec2f ) -> vec2f {
return vec2f( a.x*a.x - a.y*a.y, 2.*a.x*a.y );
}


fn rot(a: f32) -> mat2x2f {
return mat2x2f(cos(a),sin(a),-sin(a),cos(a));
}

fn iSphere( ro: vec3f, rd: vec3f, sph: vec4f ) -> vec2f
{
var oc = ro - sph.xyz;
var b = dot( oc, rd );
var c = dot( oc, oc ) - sph.w*sph.w;
var h = b*b - c;
if( h<0.0 ) { return vec2f(-1.0); }
h = sqrt(h);
return vec2f(-b-h, -b+h );
}

fn map(aP: vec3f) -> f32 {

var res = 0.;
var p = aP;

var c = p;
for (var i: i32 = 0; i < 10; i++) {
p =.7*abs(p)/dot(p,p) -.7;
let tmp = csqr(p.yz);
p = vec3f(p.x, tmp);
p=p.zxy;
res += exp(-19. * abs(dot(p,c)));
}
return res/2.;
}



fn raymarch(ro: vec3f, rd: vec3f, tminmax: vec2f ) -> vec3f
{
var t = tminmax.x;
var dt = .02;
//float dt = .2 - .195*cos(iTime*.05);//animated
var col= vec3(0.);
var c = 0.;
for(var i: i32 =0; i<64; i++ )
{
t+=dt*exp(-2.*c);
if(t>tminmax.y) { break; }

c = map(ro+t*rd);

//col = .99*col+ .08*vec3f(c*c, c, c*c*c);//green
col = .99*col+ .08*vec3f(c*c*c, c*c, c);//blue
}
return col;
}


@fragment
fn fragmentMain(@builtin(position) pos: vec4f) -> @location(0) vec4f {
var iTime = inputs.time;
var fragCoord = vec2f(pos.x, inputs.size.y - pos.y);
var iResolution = inputs.size;
var iMouse = inputs.mouse;

var time = iTime;
var q = fragCoord.xy / iResolution.xy;
var p = -1.0 + 2.0 * q;
p.x *= iResolution.x/iResolution.y;
var m = vec2(0.);
if( iMouse.z>0.0 ) {
m = iMouse.xy/iResolution.xy*3.14;
}
m-=.5;

// camera
var ro = zoom*vec3f(4.);
let tmp1 = ro.yz * rot(m.y);
ro = vec3f(ro.x, tmp1);
let tmp2 = ro.xz * rot(m.x+ 0.1*time);
ro = vec3f(tmp2.x, ro.y, tmp2.y);
var ta = vec3f( 0.0 , 0.0, 0.0 );
var ww = normalize( ta - ro );
var uu = normalize( cross(ww,vec3f(0.0,1.0,0.0) ) );
var vv = normalize( cross(uu,ww));
var rd = normalize( p.x*uu + p.y*vv + 4.0*ww );


var tmm = iSphere( ro, rd, vec4f(0.,0.,0.,2.) );

// raymarch
var col = raymarch(ro,rd,tmm);
if (tmm.x<0.) {
// col = texture(iChannel0, rd).rgb;
col = vec3f(0);
}
else {
// vec3 nor=(ro+tmm.x*rd)/2.;
// nor = reflect(rd, nor);
// float fre = pow(.5+ clamp(dot(nor,rd),0.0,1.0), 3. )*1.3;
// col += texture(iChannel0, nor).rgb * fre;
}

// shade

col = .5 *(log(1.+col));
//col = clamp(col,0.,1.);
return vec4f( col, 1.0 );
}
33 changes: 33 additions & 0 deletions webgpu-shader-toy/shaders/Shader Art 1.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
fn palette(t: f32) -> vec3f {
const a = vec3f(0.5, 0.5, 0.5);
const b = vec3f(0.5, 0.5, 0.5);
const c = vec3f(1.0, 1.0, 1.0);
const d = vec3f(0.263, 0.416, 0.557);
return a + (b * cos(6.28318 * ((c*t) + d)));
}

@fragment
fn fragmentMain(@builtin(position) pos: vec4f) -> @location(0) vec4f {
var uv = (pos.xy * 2.0 - inputs.size.xy) / inputs.size.y;
let uv0 = uv;
var finalColor = vec3f(0.0);

for(var i = 0; i < 4; i++) {
uv = fract(uv * 1.5) - 0.5;

var d = length(uv) * exp(-length(uv0));

var col = palette(length(uv0) + f32(i) * 0.4 + inputs.time * 0.4);

d = sin(d * 8.0 + inputs.time) / 8.0;
d = abs(d);

d = pow(0.01 / d, 1.2);

finalColor += col * d;
}

return vec4f(finalColor, 1.0);
}

// Ported from Shader Art Coding (https://www.youtube.com/watch?v=f4s1h2YETNY)
77 changes: 77 additions & 0 deletions webgpu-shader-toy/shaders/Shader Art 2.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
fn palette(t: f32) -> vec3f {
const a = vec3f(0.5, 0.5, 0.5);
const b = vec3f(0.5, 0.5, 0.5);
const c = vec3f(1.0, 1.0, 1.0);
const d = vec3f(0.263, 0.416, 0.557);
return a + (b * cos(6.28318 * ((c*t) + d)));
}

fn sdOctahedron(p: vec3f, s: f32) -> f32 {
let q = abs(p);
return (q.x + q.y + q.z - s) * 0.57735027;
}

fn rot2D(angle: f32) -> mat2x2<f32> {
let s = sin(angle);
let c = cos(angle);
return mat2x2<f32>(c, -s, s, c);
}

fn fmod(a: f32, b: f32) -> f32 {
return a - floor(a / b) * b;
}

fn map(p: vec3f) -> f32 {
var q = p;

q.z += inputs.time * .4f;

q.x = fract(q.x) - 0.5;
q.y = fract(q.y) - 0.5;
q.z = fmod(q.z, 0.25f) - 0.125f;

let box = sdOctahedron(q, 0.15f);

return box;
}

@fragment
fn fragmentMain(@builtin(position) pos: vec4f) -> @location(0) vec4f {
let iResolution = inputs.size.xy; // match naming in video
let iTime = inputs.time; // match naming in video
let fragCoord = vec2f(pos.x, iResolution.y - pos.y); // match naming in video + glsl coordinates
var uv = (fragCoord * 2.0 - iResolution.xy) / iResolution.y;

// Initialization
let ro = vec3f(0, 0, -3);
let rd = normalize(vec3f(uv, 1));
var col = vec3f(0);

var t = f32(0);

let m = vec2f(cos(iTime * 0.2), sin(iTime * 0.2));

// Raymarching
var i = 0;
for(; i < 80; i++)
{
var p = ro + rd * t;

p = vec3f(p.xy * rot2D(t * 0.2 * m.x), p.z);

p.y += sin(t * (m.y + 1.0) * 0.5) * 0.35;

var d = map(p);

t += d;

if(d < 0.001 || t > 100.0) { break; }
}

// Coloring
col = palette(t * 0.04f + f32(i) * 0.005);

return vec4f(col, 1.0);
}

// Ported from Shader Art Coding (https://youtu.be/khblXafu7iA?si=2DLA9nY-aR9_FpYi)

0 comments on commit 607a587

Please sign in to comment.