javascript micro-library based on three.js that will helping you initialize your shader scene more quickly
to make a film, a director needs 3 things: a scene, a camera and a monitor. it's exactly the same in the world of shaders. But implementing webgl natively is not an easy task. then it's really not easy to keep the code clean with this api. fortunately, new libraries have already solved this problem. thanks to them! Even though it has become easier to create webgl shaders with these libraries, sur still has to redefine the same instructions before we can have fun with our shaders.
<!-- source @see https://thebookofshaders.com/04/ -->
<body>
<div id="container"></div>
<script src="js/three.min.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
void main () {
gl_Position = vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 u_resolution;
uniform float u_time;
void main () {
vec2 st = gl_FragCoord.xy/u_resolution.xy;
gl_FragColor=vec4( st.x, st.y, 0.0, 1.0 );
}
</script>
<script>
var container;
var camera, scene, renderer;
var uniforms;
init();
animate();
function init() {
container = document.getElementById( 'container' );
camera = new THREE.Camera();
camera.position.z = 1;
scene = new THREE.Scene();
var geometry = new THREE.PlaneBufferGeometry( 2, 2 );
uniforms = {
u_time: { type: "f", value: 1.0 },
u_resolution: { type: "v2", value: new THREE.Vector2() },
u_mouse: { type: "v2", value: new THREE.Vector2() }
};
var material = new THREE.ShaderMaterial( {
uniforms: uniforms,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
container.appendChild( renderer.domElement );
onWindowResize();
window.addEventListener( 'resize', onWindowResize, false );
document.onmousemove = function(e){
uniforms.u_mouse.value.x = e.pageX
uniforms.u_mouse.value.y = e.pageY
}
}
function onWindowResize( event ) {
renderer.setSize( window.innerWidth, window.innerHeight );
uniforms.u_resolution.value.x = renderer.domElement.width;
uniforms.u_resolution.value.y = renderer.domElement.height;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
uniforms.u_time.value += 0.05;
renderer.render( scene, camera );
}
and if you stop implementing the same instructions to start a project every time. as a 3d developer, you are going to need assistance, you need a safe guy who will take care of your back, who will take care of the boring tasks that you don't want to repeat anymore. this guy may be w2gl. written in javascript this micro-library based on 3d engine libraries will allow you to have fun quickly with the obscure universe of shaders. note that w2gl is not there to replace the role of these libraries, it is a little overlay on these libraries which will precisely prepare your 3d scene and give you some super powers through a starter
object.
const starter = w2gl.init( {
THREE,
shader: {
myShaderName : {
vertex: `
void main () {
gl_Position = vec4(position, 1.0);
}
`,
fragment: `
uniform vec2 resolution;
uniform float time;
void main () {
vec2 st = gl_FragCoord.xy / resolution.xy;
gl_FragColor=vec4(st.x, st.y, 0.0, 1.0);
}
`
}
}
} );
starter.events.onresize( starter.screen.resize );
starter.events.onmousemove( starter.mouse.move );
i'm not a developer, i'm just a normal guy who likes programming with the desire to learn more about the dark side of force. last points, changes will be coming in the future for the good of all, i hope.
npm i w2gl
OR
yarn add w2gl
you can also download the project, after recover the file in dist/w2gl.js
and then add it in the folder of your project in which you store the external libraries to your development.
are you looking to create a scene quickly? If your answer is yes, you should try this!
import * as THREE from 'three';
import w2gl from 'w2gl';
import vertex from './shader/vertex.fs';
import fragment from './shader/fragment.fs';
// 1. first way, to obtain the starter object provided by the result of the init method
const starter = w2gl.init( { THREE, shader: { myShaderName : { vertex, fragment } } } );
console.log( starter ); // <-- et hop! w2gl is available
// 2. second way, to obtain the starter object provided by the callback function, pass as the second argument of the init method
w2gl.init( { THREE, shader: { myShaderName : { vertex, fragment } } }, starter => {
console.log( starter ); // <-- voilà! w2gl is ready in the callback scope only
} );
<script src="./src/three.js"></script>
<script src="./src/w2gl.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
void main () {
gl_Position = vec4( position, 1.0 );
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform vec2 resolution;
uniform float time;
void main () {
vec2 st = gl_FragCoord.xy/resolution.xy;
gl_FragColor=vec4( st.x, st.y, 0.0, 1.0 );
}
</script>
<script>
// 1. first way, to obtain the starter object provided by the result of the init method
var starter = w2gl.init( {
THREE,
shader: {
vertex: document.getElementById( 'vertexShader' ).innerHTML,
fragment: document.getElementById( 'fragmentShader' ).innerHTML
}
} );
console.log( starter ); // <-- et hop! w2gl is available
// 2. second way, to obtain the starter object provided by the callback function, pass as the second argument of the init method
w2gl.init( {
THREE,
shader: {
vertex: document.getElementById( 'vertexShader' ).innerHTML,
fragment: document.getElementById( 'fragmentShader' ).innerHTML
}
}, starter => {
console.log( starter ); // <-- voilà! w2gl is available and scoped
} );
</script>
-
the
init
method takes as an input argument:option
.option
{ Object }: collection. -
it's a collection of methods that will help you during the development of your shaders. It contains everything you need to focus on your main task.
{ THREE: {ACESFilmicToneMapping: 5, AddEquation: 100, AddOperation: 2, AdditiveBlending: 2, AlphaFormat: 1021, …} shader: {myShaderName: S} scene: {current: ob} camera: {current: db} renderer: {current: og} events: {onmousemove: ƒ, onresize: ƒ, clear: ƒ, init: ƒ, mousemove: ƒ, …} mouse: X {x: 5, y: 382, move: ƒ} screen: ea {width: 1306, height: 460, resize: ƒ} }
-
the
shader
object returnsTHREE.Mesh
created fromPlaneBufferGeometry
andShaderMaterial
. This is the one that contains your vertex shader and your shader fragment as well as the uniforms.to save time, I have already implemented basic uniforms.
// fragmentShader.fs || vertexShader.vs uniform vec2 mouse; uniform vec2 resolution; uniform float time;
// index.js starter.shader.myShaderName.material.uniforms /* output: { mouse: {type: "v2", value: new THREE.Vector2()} resolution: {type: "v2", value: new THREE.Vector2()} time: {type: "f", value: 0.0} } */
-
the scene object returns
THREE.Scene()
. This scene is the 3d space in which your mesh will be. -
the scene object returns
THREE.Camera()
. A basic camera without artifice that you don't need to initialize. -
the scene object returns
THREE.WebGLRenderer()
. I have it a little custom at once, so that it can increment the value oftimer.time
within the rendering loop. -
i have made available to you methods that you can use as event listeners to update your scene. indeed each of these methods are executed in the event listener corresponding to its name.
-
this method is called when the mouse is moving on the screen. You can retrieve the
window
object directly and then update your fragment shader only when a movement of the mouse is detected.f
{ Function }: callback.returns
{ Void }: undefined.onmousemove
=> execute in the event listener callbackwindow.addEventListener( 'mousemove', _ => {}, false );
starter.shader.myShaderName.onmousemove( event => { starter.shader.myShaderName.material.uniforms.mouse.value.x = event.clientX; starter.shader.myShaderName.material.uniforms.mouse.value.y = event.clientY; } );
-
this method is executed in a
requestAnimationFrame
.onrender
is called with as input argument atimer
object which will allow you to update your shader fragment.f
{ Function }: callback.returns
{ Void }: undefined.onrender
=> executed in thesetAnimationLoop
method.starter.shader.myShaderName.onrender( timer => { starter.shader.myShaderName.material.uniforms.time.value += timer.time; } );
-
this method is called when the dimensions of your screens have changed, an event argument is passed to it that you can use to update the resolution of your fragment shader only when a screen change has been detected.
f
{ Function }: callback.returns
{ Void }: undefined.onresize
=> execute in the event listener callbackwindow.addEventListener( 'resize', _ => {}, false );
starter.renderer.current.onresize( event => { starter.renderer.current.setSize( event.target.innerWidth, event.target.innerHeight ); } );
-
-
contains
new Mouse ()
which is nothing more than a simple two-dimensional vector. so you don't need to implement it anymore. It is accessible via thestarter
object. once initialized. You will have access to its positionsx
,y
.starter.mouse // output {x: 0, y: 0, move: ƒ} starter.events.onmousemove( starter.mouse.move ); // the mouse is initialized to update each time the onmousemove event is called.
-
it is the screen component, it contains the screen dimension and a resize function that you can call to though the
starter.events.onresize
.starter.screen // output {width: 343, height: 811, resize: ƒ} starter.events.onresize( starter.screen.resize ); // the screen is initialized to be set each time the onresize event is called.
-
reports
npm run test:reports
OR
yarn test:reports
watch
npm run test:watch
OR
yarn test:watch
- supports glsl #include
- more events
- documentation
- mode by default
- improve test cases
Copyright ©️ 2019 monsieurbadia
Released under the MIT license
logo by @mllemartins with 🖤
built by @monsieurbadia with 🖤
⭐️ this repository if this project helped you!