fragment shader source input

This commit is contained in:
Gyuri Horák 2023-02-14 17:01:06 +01:00
parent 9ed0c18487
commit f212b27a09
Signed by: dyuri
GPG Key ID: 4993F07B3EAE8D38
2 changed files with 95 additions and 28 deletions

View File

@ -4,20 +4,54 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>&lt;repa-shader&gt; demo</title> <title>&lt;repa-shader&gt; demo</title>
<script type="module" src="../src/repa-shader.js"></script> <script type="module" src="../src/repa-shader.js"></script>
<style>
body {
margin: 0;
padding: 0;
background: repeating-linear-gradient(45deg, #333, #333 10px, #666 10px, #666 20px);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
#fsinput {
width: 90vw;
height: 50vh;
}
</style>
</head> </head>
<body> <body>
<repa-shader> <repa-shader fs-input="fsinput" alpha mouse>
<script type="x-shader/x-vertex"> <script type="x-shader/x-vertex">
attribute vec3 position; attribute vec3 position;
void main() { void main() {
gl_Position = vec4(position, 1.0); gl_Position = vec4(position, 1.0);
} }
</script> </script>
<script type="x-shader/x-fragment"> <script type="x-shader/x-fragment">
void main() { void main() {
outColor = vec4(1.0, 0.0, 0.0, 1.0); vec2 uv = gl_FragCoord.xy / resolution.xy;
} vec3 col = .5 + .5 * cos(uv.xyx + time + vec3(0, 2, 4));
</script>
float dist = distance(uv, mouse);
float circle = smoothstep(.1, .2, dist) * .5 + .5;
vec4 acolor = vec4(col * circle, circle);
outColor = vec4(acolor);
}
</script>
</repa-shader> </repa-shader>
<div>
<textarea id="fsinput"></textarea>
</div>
<button id="update">Update</button>
<script>
const updateButton = document.getElementById('update');
updateButton.addEventListener('click', () => {
const fsinput = document.getElementById('fsinput');
const shader = document.querySelector('repa-shader');
shader.render(fsinput.value);
});
</script>
</body> </body>
</html> </html>

View File

@ -30,18 +30,20 @@ void main() {
`, `,
}; };
const DEMO = ` const DEMO_FS = `
precision highp float; precision highp float;
uniform vec2 resolution; uniform vec2 resolution;
uniform vec2 mouse; uniform vec2 mouse;
uniform float time; uniform float time;
out vec4 outColor; out vec4 outColor;
void main(){ void main() {
vec2 r=resolution, p=(gl_FragCoord.xy*2.-r)/min(r.x,r.y)-mouse; vec2 uv = gl_FragCoord.xy / resolution.xy;
for (int i=0;i<8;++i) { vec3 col = .5 + .5 * cos(uv.xyx + time + vec3(0, 2, 4));
p.xy=abs(p)/abs(dot(p,p))-vec2(.9+cos(time*.2)*.4);
} float dist = distance(uv, mouse);
outColor=vec4(p.xxy,1); float circle = smoothstep(.1, .2, dist) * .5 + .5;
vec4 acolor = vec4(col * circle, circle);
outColor = vec4(acolor);
} }
`; `;
@ -61,7 +63,7 @@ class RepaShader extends HTMLElement {
} }
if (!this._gl) { if (!this._gl) {
const glopts = this._cfg.glopts || {alpha: true, preserveDrawingBuffer: true}; const glopts = this._cfg.glopts || {alpha: this.hasAttribute('alpha'), preserveDrawingBuffer: true};
this._gl = this._target.getContext('webgl2', glopts); this._gl = this._target.getContext('webgl2', glopts);
if (!this._gl) { if (!this._gl) {
this.logger.error("WebGL2 not supported"); this.logger.error("WebGL2 not supported");
@ -81,7 +83,7 @@ class RepaShader extends HTMLElement {
this._gl.clearColor(0,0,0,1); this._gl.clearColor(0,0,0,1);
// TODO remove // TODO remove
this.demo(); this.render(this.getFragmentShaderSource());
} }
render(source, time) { render(source, time) {
@ -276,21 +278,22 @@ void main(){
// - no `precision`, but has `main()` -> twigl geeker300es // - no `precision`, but has `main()` -> twigl geeker300es
// - no `precision`, no `main()` -> twigl geekest300es // - no `precision`, no `main()` -> twigl geekest300es
// TODO: mrt // TODO: mrt
if (!this.mode) { let mode = this.mode;
if (!mode) {
const hasPrecision = this._fsSource.includes('precision'); const hasPrecision = this._fsSource.includes('precision');
const hasMain = this._fsSource.includes('main()'); const hasMain = this._fsSource.includes('main()');
if (hasPrecision) { if (hasPrecision) {
this.mode = 'classic'; mode = 'classic';
} else if (hasMain) { } else if (hasMain) {
this.mode = 'geeker'; mode = 'geeker';
} else { } else {
this.mode = 'geekest'; mode = 'geekest';
} }
} }
let start = ''; let start = '';
let end = ''; let end = '';
switch (this.mode) { switch (mode) {
case 'classic': case 'classic':
start = CHUNKS.es300; start = CHUNKS.es300;
break; break;
@ -306,8 +309,38 @@ void main(){
return `${start}\n${this._fsSource}\n${end}`; return `${start}\n${this._fsSource}\n${end}`;
} }
demo() { getFragmentShaderSource() {
this.render(DEMO); let source = '';
// text area editor
let fsInput = this.shadowRoot.querySelector('textarea[name="fragment-shader"]') || this.querySelector('textarea[name="fragment-shader"]');
if (!fsInput) {
const fsInputId = this.getAttribute('fs-input');
fsInput = document.getElementById(fsInputId);
}
if (fsInput) {
source = fsInput.value;
}
// script tag
if (!source) {
const fsEl = this.querySelector('script[type="x-shader/x-fragment"]');
if (fsEl) {
source = fsEl.textContent;
}
}
// fallback demo
if (!source) {
source = DEMO_FS;
}
// fill back to textarea
if (fsInput) {
fsInput.value = source;
}
return source;
} }
} }