├── .babelrc
├── .gitignore
├── README.md
├── a-chain
├── aframe-orbit-controls-component.min.js
├── aframe-v0.8.2.min.js
├── index.html
└── script.js
├── boilerplate.js
├── docs
├── OrbitControls.js
├── bundle.js
├── glsl-bumpy-sphere
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-cubes
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-juliafractal
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-just-a-cube
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-kaleidoscope
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-mandelbrot
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-random-lines
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-spinning-color-wheel
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-squishydonutspin
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── glsl-very-basic
│ ├── fragmentshader.glsl
│ └── vertexshader.glsl
├── index.html
├── three.min.js
└── threejs-icosphere-explode
│ └── scene.js
├── fonts
├── fa-brands-400.ttf
└── fa-brands.css
├── package.json
├── spinning-color-wheel
├── fragmentshader.glsl
├── index.html
└── vertexshader.glsl
├── src
├── app.css
├── app.js
├── button.js
├── code.css
├── code.js
├── codebutton.css
├── codebutton.js
├── index.js
├── menubutton.css
├── menubutton.js
├── select.css
├── select.js
├── shader.css
├── shader.js
└── threejs.js
├── style.css
├── webpack.config.js
├── yarn-error.log
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | presets: [
3 | [
4 | '@babel/preset-env',
5 | {
6 | modules: false
7 | }
8 | ],
9 | '@babel/preset-react'
10 | ],
11 | "plugins": [
12 | '@babel/plugin-transform-runtime'
13 | ]
14 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Shader Art
2 |
3 | ## Update 12/19/19
4 | I've created a whole new React-based viewer for these shaders. The links below will now take you to the code for each project.
5 |
6 | * 12/29/19 - [Icosphere Explode](https://github.com/captainpainway/shader-art/tree/master/docs/threejs-icosphere-explode): Experimenting with vertex and fragment shaders in Three.js. Click and drag the mouse to explode the icosphere.
7 | * 8/1/18 - [Random Lines](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-random-lines): Random lines.
8 | * 7/30/18 - [Bumpy Sphere](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-bumpy-sphere): Signed distance function ray marching. Time-based displacement and rotation with phong shading.
9 | * 7/29/18 - [Spinning Color Wheel](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-spinning-color-wheel): HSB color wheel, masked into a 2D donut, with time-based rotation.
10 | * 7/27/18 - [Very Basic](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-very-basic): Basic half-screen linear gradients.
11 | * 7/24/18 - [Just A Cube](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-just-a-cube): Just a cube.
12 | * 7/22/18 - [Julia Fractal](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-juliafractal): A time-based Julia fractal based off the Mandelbrot fractal.
13 | * 7/21/18 - [Mandelbrot](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-mandelbrot): A simple Mandelbrot fractal.
14 | * 7/20/18 - [A-Chain](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-a-chain): An experiment in using GLSL shaders with A-Frame/three.js.
15 | * 7/18/18 - [Squishy Donut Spin](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-squishydonutspin): Ray marching with signed distance functions. Twisting and rotating a primitive based on time.
16 | * 7/16/18 - [Kaleidoscope](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-kaleidoscope): Time-based constructive solid geometry and shading.
17 | * 7/15/18 - [Cubes](https://github.com/captainpainway/shader-art/tree/master/docs/glsl-cubes): Experimenting with ray marching, signed distance functions, phong shading, repetition, and camera movement.
18 |
--------------------------------------------------------------------------------
/a-chain/aframe-orbit-controls-component.min.js:
--------------------------------------------------------------------------------
1 | !function(t){function e(i){if(s[i])return s[i].exports;var a=s[i]={exports:{},id:i,loaded:!1};return t[i].call(a.exports,a,a.exports,e),a.loaded=!0,a.exports}var s={};return e.m=t,e.c=s,e.p="",e(0)}([function(t,e){if("undefined"==typeof AFRAME)throw new Error("Component attempted to register before AFRAME was available.");var s=THREE.Math.radToDeg;AFRAME.registerComponent("orbit-controls",{dependencies:["position","rotation"],schema:{enabled:{default:!0},target:{default:""},distance:{default:1},enableRotate:{default:!0},rotateSpeed:{default:1},enableZoom:{default:!0},zoomSpeed:{default:1},enablePan:{default:!0},keyPanSpeed:{default:7},enableDamping:{default:!1},dampingFactor:{default:.25},autoRotate:{default:!1},autoRotateSpeed:{default:2},enableKeys:{default:!0},minAzimuthAngle:{default:-(1/0)},maxAzimuthAngle:{default:1/0},minPolarAngle:{default:0},maxPolarAngle:{default:Math.PI},minZoom:{default:0},maxZoom:{default:1/0},invertZoom:{default:!1},minDistance:{default:0},maxDistance:{default:1/0},rotateTo:{type:"vec3",default:{x:0,y:0,z:0}},rotateToSpeed:{type:"number",default:.05},logPosition:{type:"boolean",default:!1},autoVRLookCam:{type:"boolean",default:!0}},multiple:!1,init:function(){this.sceneEl=this.el.sceneEl,this.object=this.el.object3D,this.target=this.sceneEl.querySelector(this.data.target).object3D.position,console.log("enabled: ",this.data.enabled),this.isRunning=!1,this.lookControls=null,this.data.autoVRLookCam&&(this.el.components["look-controls"]?this.lookControls=this.el.components["look-controls"]:(this.el.setAttribute("look-controls",""),this.lookControls=this.el.components["look-controls"]),this.lookControls.pause(),this.el.sceneEl.addEventListener("enter-vr",this.onEnterVR.bind(this),!1),this.el.sceneEl.addEventListener("exit-vr",this.onExitVR.bind(this),!1)),this.dolly=new THREE.Object3D,this.dolly.position.copy(this.object.position),this.savedPose=null,this.STATE={NONE:-1,ROTATE:0,DOLLY:1,PAN:2,TOUCH_ROTATE:3,TOUCH_DOLLY:4,TOUCH_PAN:5,ROTATE_TO:6},this.state=this.STATE.NONE,this.EPS=1e-6,this.lastPosition=new THREE.Vector3,this.lastQuaternion=new THREE.Quaternion,this.spherical=new THREE.Spherical,this.sphericalDelta=new THREE.Spherical,this.scale=1,this.zoomChanged=!1,this.rotateStart=new THREE.Vector2,this.rotateEnd=new THREE.Vector2,this.rotateDelta=new THREE.Vector2,this.panStart=new THREE.Vector2,this.panEnd=new THREE.Vector2,this.panDelta=new THREE.Vector2,this.panOffset=new THREE.Vector3,this.dollyStart=new THREE.Vector2,this.dollyEnd=new THREE.Vector2,this.dollyDelta=new THREE.Vector2,this.vector=new THREE.Vector3,this.desiredPosition=new THREE.Vector3,this.mouseButtons={ORBIT:THREE.MOUSE.LEFT,ZOOM:THREE.MOUSE.MIDDLE,PAN:THREE.MOUSE.RIGHT},this.keys={LEFT:37,UP:38,RIGHT:39,BOTTOM:40},this.bindMethods()},update:function(t){if(console.log("component update"),this.data.rotateTo){var e=new THREE.Vector3(this.data.rotateTo.x,this.data.rotateTo.y,this.data.rotateTo.z);this.desiredPosition.equals(e)||(this.desiredPosition.copy(e),this.rotateTo(this.desiredPosition))}this.dolly.position.copy(this.object.position),this.updateView(!0)},remove:function(){this.isRunning=!1,this.removeEventListeners(),this.el.sceneEl.removeEventListener("enter-vr",this.onEnterVR,!1),this.el.sceneEl.removeEventListener("exit-vr",this.onExitVR,!1)},tick:function(t){var e=!(!this.data.enabled||!this.isRunning)&&this.updateView();e===!0&&this.data.logPosition===!0&&console.log(this.el.object3D.position)},onEnterVR:function(t){this.saveCameraPose(),this.el.setAttribute("position",{x:0,y:2,z:5}),this.el.setAttribute("rotation",{x:0,y:0,z:0}),this.pause(),this.lookControls.play(),this.data.autoRotate&&console.warn("orbit-controls: Sorry, autoRotate is not implemented in VR mode")},onExitVR:function(t){this.lookControls.pause(),this.play(),this.restoreCameraPose(),this.updateView(!0)},pause:function(){this.isRunning=!1,this.removeEventListeners()},play:function(){this.isRunning=!0;var t,e;this.object.traverse(function(s){s instanceof THREE.PerspectiveCamera?(t=s,e="PerspectiveCamera"):s instanceof THREE.OrthographicCamera&&(t=s,e="OrthographicCamera")}),this.camera=t,this.cameraType=e,this.sceneEl.addEventListener("renderstart",this.onRenderTargetLoaded,!1),this.lookControls&&this.lookControls.pause(),this.canvasEl&&this.addEventListeners()},onRenderTargetLoaded:function(){this.sceneEl.removeEventListener("renderstart",this.onRenderTargetLoaded,!1),this.canvasEl=this.sceneEl.canvas,this.addEventListeners()},bindMethods:function(){this.onRenderTargetLoaded=this.onRenderTargetLoaded.bind(this),this.onContextMenu=this.onContextMenu.bind(this),this.onMouseDown=this.onMouseDown.bind(this),this.onMouseWheel=this.onMouseWheel.bind(this),this.onMouseMove=this.onMouseMove.bind(this),this.onMouseUp=this.onMouseUp.bind(this),this.onTouchStart=this.onTouchStart.bind(this),this.onTouchMove=this.onTouchMove.bind(this),this.onTouchEnd=this.onTouchEnd.bind(this),this.onKeyDown=this.onKeyDown.bind(this)},addEventListeners:function(){this.canvasEl.addEventListener("contextmenu",this.onContextMenu,!1),this.canvasEl.addEventListener("mousedown",this.onMouseDown,!1),this.canvasEl.addEventListener("mousewheel",this.onMouseWheel,!1),this.canvasEl.addEventListener("MozMousePixelScroll",this.onMouseWheel,!1),this.canvasEl.addEventListener("touchstart",this.onTouchStart,!1),this.canvasEl.addEventListener("touchend",this.onTouchEnd,!1),this.canvasEl.addEventListener("touchmove",this.onTouchMove,!1),window.addEventListener("keydown",this.onKeyDown,!1)},removeEventListeners:function(){this.canvasEl&&(this.canvasEl.removeEventListener("contextmenu",this.onContextMenu,!1),this.canvasEl.removeEventListener("mousedown",this.onMouseDown,!1),this.canvasEl.removeEventListener("mousewheel",this.onMouseWheel,!1),this.canvasEl.removeEventListener("MozMousePixelScroll",this.onMouseWheel,!1),this.canvasEl.removeEventListener("touchstart",this.onTouchStart,!1),this.canvasEl.removeEventListener("touchend",this.onTouchEnd,!1),this.canvasEl.removeEventListener("touchmove",this.onTouchMove,!1),this.canvasEl.removeEventListener("mousemove",this.onMouseMove,!1),this.canvasEl.removeEventListener("mouseup",this.onMouseUp,!1),this.canvasEl.removeEventListener("mouseout",this.onMouseUp,!1)),window.removeEventListener("keydown",this.onKeyDown,!1)},onContextMenu:function(t){t.preventDefault()},onMouseDown:function(t){if(this.data.enabled&&this.isRunning){if(t.button===this.mouseButtons.ORBIT&&(t.shiftKey||t.ctrlKey)){if(this.data.enablePan===!1)return;this.handleMouseDownPan(t),this.state=this.STATE.PAN}else if(t.button===this.mouseButtons.ORBIT){if(this.panOffset.set(0,0,0),this.data.enableRotate===!1)return;this.handleMouseDownRotate(t),this.state=this.STATE.ROTATE}else if(t.button===this.mouseButtons.ZOOM){if(this.panOffset.set(0,0,0),this.data.enableZoom===!1)return;this.handleMouseDownDolly(t),this.state=this.STATE.DOLLY}else if(t.button===this.mouseButtons.PAN){if(this.data.enablePan===!1)return;this.handleMouseDownPan(t),this.state=this.STATE.PAN}this.state!==this.STATE.NONE&&(this.canvasEl.addEventListener("mousemove",this.onMouseMove,!1),this.canvasEl.addEventListener("mouseup",this.onMouseUp,!1),this.canvasEl.addEventListener("mouseout",this.onMouseUp,!1),this.el.emit("start-drag-orbit-controls",null,!1))}},onMouseMove:function(t){if(this.data.enabled&&this.isRunning)if(t.preventDefault(),this.state===this.STATE.ROTATE){if(this.data.enableRotate===!1)return;this.handleMouseMoveRotate(t)}else if(this.state===this.STATE.DOLLY){if(this.data.enableZoom===!1)return;this.handleMouseMoveDolly(t)}else if(this.state===this.STATE.PAN){if(this.data.enablePan===!1)return;this.handleMouseMovePan(t)}},onMouseUp:function(t){this.data.enabled&&this.isRunning&&this.state!==this.STATE.ROTATE_TO&&(t.preventDefault(),t.stopPropagation(),this.handleMouseUp(t),this.canvasEl.removeEventListener("mousemove",this.onMouseMove,!1),this.canvasEl.removeEventListener("mouseup",this.onMouseUp,!1),this.canvasEl.removeEventListener("mouseout",this.onMouseUp,!1),this.state=this.STATE.NONE,this.el.emit("end-drag-orbit-controls",null,!1))},onMouseWheel:function(t){this.data.enabled&&this.isRunning&&this.data.enableZoom!==!1&&(this.state===this.STATE.NONE||this.state===this.STATE.ROTATE)&&(t.preventDefault(),t.stopPropagation(),this.handleMouseWheel(t))},onTouchStart:function(t){if(this.data.enabled&&this.isRunning){switch(t.touches.length){case 1:if(this.data.enableRotate===!1)return;this.handleTouchStartRotate(t),this.state=this.STATE.TOUCH_ROTATE;break;case 2:if(this.data.enableZoom===!1)return;this.handleTouchStartDolly(t),this.state=this.STATE.TOUCH_DOLLY;break;case 3:if(this.data.enablePan===!1)return;this.handleTouchStartPan(t),this.state=this.STATE.TOUCH_PAN;break;default:this.state=this.STATE.NONE}this.state!==this.STATE.NONE&&this.el.emit("start-drag-orbit-controls",null,!1)}},onTouchMove:function(t){if(this.data.enabled&&this.isRunning)switch(t.preventDefault(),t.stopPropagation(),t.touches.length){case 1:if(this.enableRotate===!1)return;if(this.state!==this.STATE.TOUCH_ROTATE)return;this.handleTouchMoveRotate(t);break;case 2:if(this.data.enableZoom===!1)return;if(this.state!==this.STATE.TOUCH_DOLLY)return;this.handleTouchMoveDolly(t);break;case 3:if(this.data.enablePan===!1)return;if(this.state!==this.STATE.TOUCH_PAN)return;this.handleTouchMovePan(t);break;default:this.state=this.STATE.NONE}},onTouchEnd:function(t){this.data.enabled&&this.isRunning&&(this.handleTouchEnd(t),this.el.emit("end-drag-orbit-controls",null,!1),this.state=this.STATE.NONE)},onKeyDown:function(t){this.data.enabled&&this.isRunning&&this.data.enableKeys!==!1&&this.data.enablePan!==!1&&this.handleKeyDown(t)},handleMouseDownRotate:function(t){this.rotateStart.set(t.clientX,t.clientY)},handleMouseDownDolly:function(t){this.dollyStart.set(t.clientX,t.clientY)},handleMouseDownPan:function(t){this.panStart.set(t.clientX,t.clientY)},handleMouseMoveRotate:function(t){this.rotateEnd.set(t.clientX,t.clientY),this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart);var e=this.canvasEl===document?this.canvasEl.body:this.canvasEl;this.rotateLeft(2*Math.PI*this.rotateDelta.x/e.clientWidth*this.data.rotateSpeed),this.rotateUp(2*Math.PI*this.rotateDelta.y/e.clientHeight*this.data.rotateSpeed),this.rotateStart.copy(this.rotateEnd),this.updateView()},handleMouseMoveDolly:function(t){this.dollyEnd.set(t.clientX,t.clientY),this.dollyDelta.subVectors(this.dollyEnd,this.dollyStart),this.dollyDelta.y>0?this.data.invertZoom?this.dollyOut(this.getZoomScale()):this.dollyIn(this.getZoomScale()):this.dollyDelta.y<0&&(this.data.invertZoom?this.dollyIn(this.getZoomScale()):this.dollyOut(this.getZoomScale())),this.dollyStart.copy(this.dollyEnd),this.updateView()},handleMouseMovePan:function(t){this.panEnd.set(t.clientX,t.clientY),this.panDelta.subVectors(this.panEnd,this.panStart),this.pan(this.panDelta.x,this.panDelta.y),this.panStart.copy(this.panEnd),this.updateView()},handleMouseUp:function(t){},handleMouseWheel:function(t){var e=0;void 0!==t.wheelDelta?e=t.wheelDelta:void 0!==t.detail&&(e=-t.detail),e>0?this.data.invertZoom?this.dollyIn(this.getZoomScale()):this.dollyOut(this.getZoomScale()):e<0&&(this.data.invertZoom?this.dollyOut(this.getZoomScale()):this.dollyIn(this.getZoomScale())),this.updateView()},handleTouchStartRotate:function(t){this.rotateStart.set(t.touches[0].pageX,t.touches[0].pageY)},handleTouchStartDolly:function(t){var e=t.touches[0].pageX-t.touches[1].pageX,s=t.touches[0].pageY-t.touches[1].pageY,i=Math.sqrt(e*e+s*s);this.dollyStart.set(0,i)},handleTouchStartPan:function(t){this.panStart.set(t.touches[0].pageX,t.touches[0].pageY)},handleTouchMoveRotate:function(t){this.rotateEnd.set(t.touches[0].pageX,t.touches[0].pageY),this.rotateDelta.subVectors(this.rotateEnd,this.rotateStart);var e=this.canvasEl===document?this.canvasEl.body:this.canvasEl;this.rotateLeft(2*Math.PI*this.rotateDelta.x/e.clientWidth*this.data.rotateSpeed),this.rotateUp(2*Math.PI*this.rotateDelta.y/e.clientHeight*this.data.rotateSpeed),this.rotateStart.copy(this.rotateEnd),this.updateView()},handleTouchMoveDolly:function(t){var e=t.touches[0].pageX-t.touches[1].pageX,s=t.touches[0].pageY-t.touches[1].pageY,i=Math.sqrt(e*e+s*s);this.dollyEnd.set(0,i),this.dollyDelta.subVectors(this.dollyEnd,this.dollyStart),this.dollyDelta.y>0?this.dollyIn(this.getZoomScale()):this.dollyDelta.y<0&&this.dollyOut(this.getZoomScale()),this.dollyStart.copy(this.dollyEnd),this.updateView()},handleTouchMovePan:function(t){this.panEnd.set(t.touches[0].pageX,t.touches[0].pageY),this.panDelta.subVectors(this.panEnd,this.panStart),this.pan(this.panDelta.x,this.panDelta.y),this.panStart.copy(this.panEnd),this.updateView()},handleTouchEnd:function(t){},handleKeyDown:function(t){switch(t.keyCode){case this.keys.UP:this.pan(0,this.data.keyPanSpeed),this.updateView();break;case this.keys.BOTTOM:this.pan(0,-this.data.keyPanSpeed),this.updateView();break;case this.keys.LEFT:this.pan(this.data.keyPanSpeed,0),this.updateView();break;case this.keys.RIGHT:this.pan(-this.data.keyPanSpeed,0),this.updateView()}},getAutoRotationAngle:function(){return 2*Math.PI/60/60*this.data.autoRotateSpeed},getZoomScale:function(){return Math.pow(.95,this.data.zoomSpeed)},rotateLeft:function(t){this.sphericalDelta.theta-=t},rotateUp:function(t){this.sphericalDelta.phi-=t},rotateTo:function(t){this.state=this.STATE.ROTATE_TO,this.desiredPosition.copy(t)},panHorizontally:function(t,e){var s=new THREE.Vector3;s.setFromMatrixColumn(e,0),s.multiplyScalar(-t),this.panOffset.add(s)},panVertically:function(t,e){var s=new THREE.Vector3;s.setFromMatrixColumn(e,1),s.multiplyScalar(t),this.panOffset.add(s)},pan:function(t,e){var s=new THREE.Vector3,i=this.canvasEl===document?this.canvasEl.body:this.canvasEl;if("PerspectiveCamera"===this.cameraType){var a=this.dolly.position;s.copy(a).sub(this.target);var o=s.length();o*=Math.tan(this.camera.fov/2*Math.PI/180),this.panHorizontally(2*t*o/i.clientHeight,this.object.matrix),this.panVertically(2*e*o/i.clientHeight,this.object.matrix)}else"OrthographicCamera"===this.cameraType?(this.panHorizontally(t*(this.dolly.right-this.dolly.left)/this.camera.zoom/i.clientWidth,this.object.matrix),this.panVertically(e*(this.dolly.top-this.dolly.bottom)/this.camera.zoom/i.clientHeight,this.object.matrix)):(console.warn("Trying to pan: WARNING: Orbit Controls encountered an unknown camera type - pan disabled."),this.data.enablePan=!1)},dollyIn:function(t){"PerspectiveCamera"===this.cameraType?this.scale*=t:"OrthographicCamera"===this.cameraType?(this.camera.zoom=Math.max(this.data.minZoom,Math.min(this.data.maxZoom,this.camera.zoom*t)),this.camera.updateProjectionMatrix(),this.zoomChanged=!0):(console.warn("Trying to dolly in: WARNING: Orbit Controls encountered an unknown camera type - dolly/zoom disabled."),this.data.enableZoom=!1)},dollyOut:function(t){"PerspectiveCamera"===this.cameraType?this.scale/=t:"OrthographicCamera"===this.cameraType?(this.camera.zoom=Math.max(this.data.minZoom,Math.min(this.data.maxZoom,this.camera.zoom/t)),this.camera.updateProjectionMatrix(),this.zoomChanged=!0):(console.warn("Trying to dolly out: WARNING: Orbit Controls encountered an unknown camera type - dolly/zoom disabled."),this.data.enableZoom=!1)},lookAtTarget:function(t,e){var s=new THREE.Vector3;s.subVectors(t.position,e).add(t.position),t.lookAt(s)},saveCameraPose:function(){this.savedPose||(this.savedPose={position:this.dolly.position,rotation:this.dolly.rotation})},restoreCameraPose:function(){this.savedPose&&(this.dolly.position.copy(this.savedPose.position),this.dolly.rotation.copy(this.savedPose.rotation),this.savedPose=null)},updateView:function(t){if(this.desiredPosition&&this.state===this.STATE.ROTATE_TO){var e=new THREE.Spherical;e.setFromVector3(this.desiredPosition);var i=e.phi-this.spherical.phi,a=e.theta-this.spherical.theta;this.sphericalDelta.set(this.spherical.radius,i*this.data.rotateToSpeed,a*this.data.rotateToSpeed)}var o=new THREE.Vector3,n=(new THREE.Quaternion).setFromUnitVectors(this.dolly.up,new THREE.Vector3(0,1,0)),h=n.clone().inverse();if(o.copy(this.dolly.position).sub(this.target),o.applyQuaternion(n),this.spherical.setFromVector3(o),this.data.autoRotate&&this.state===this.STATE.NONE&&this.rotateLeft(this.getAutoRotationAngle()),this.spherical.theta+=this.sphericalDelta.theta,this.spherical.phi+=this.sphericalDelta.phi,this.spherical.theta=Math.max(this.data.minAzimuthAngle,Math.min(this.data.maxAzimuthAngle,this.spherical.theta)),this.spherical.phi=Math.max(this.data.minPolarAngle,Math.min(this.data.maxPolarAngle,this.spherical.phi)),this.spherical.makeSafe(),this.spherical.radius*=this.scale,this.spherical.radius=Math.max(this.data.minDistance,Math.min(this.data.maxDistance,this.spherical.radius)),this.target.add(this.panOffset),o.setFromSpherical(this.spherical),o.applyQuaternion(h),this.dolly.position.copy(this.target).add(o),this.target&&this.lookAtTarget(this.dolly,this.target),this.data.enableDamping===!0?(this.sphericalDelta.theta*=1-this.data.dampingFactor,this.sphericalDelta.phi*=1-this.data.dampingFactor):this.sphericalDelta.set(0,0,0),this.scale=1,this.panOffset.set(0,0,0),t===!0||this.zoomChanged||this.lastPosition.distanceToSquared(this.dolly.position)>this.EPS||8*(1-this.lastQuaternion.dot(this.dolly.quaternion))>this.EPS){var l=this.calculateHMDQuaternion(),r=new THREE.Euler;return r.setFromQuaternion(l,"YXZ"),this.el.setAttribute("position",{x:this.dolly.position.x,y:this.dolly.position.y,z:this.dolly.position.z}),this.el.setAttribute("rotation",{x:s(r.x),y:s(r.y),z:s(r.z)}),this.lastPosition.copy(this.dolly.position),this.lastQuaternion.copy(this.dolly.quaternion),this.zoomChanged=!1,!0}return!1},calculateHMDQuaternion:function(){var t=new THREE.Quaternion;return function(){return t.copy(this.dolly.quaternion),t}}()})}]);
--------------------------------------------------------------------------------
/a-chain/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | A-Frame WebGL Test
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/a-chain/script.js:
--------------------------------------------------------------------------------
1 | AFRAME.registerShader('my-custom', {
2 | schema: {
3 | color: {type: 'color', is: 'uniform'}
4 | },
5 | raw: false,
6 | vertexShader:
7 | `varying vec2 vUv;
8 |
9 | void main() {
10 | vUv = uv;
11 | gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
12 | }`,
13 | fragmentShader:
14 | `varying vec2 vUv;
15 | uniform vec3 color;
16 |
17 | void main() {
18 | gl_FragColor = vec4(color, 1.0);
19 | }`
20 | });
21 |
--------------------------------------------------------------------------------
/boilerplate.js:
--------------------------------------------------------------------------------
1 | window.requestAnimationFrame = window.requestAnimationFrame || (() => {
2 | return window.webkitRequestAnimationFrame ||
3 | window.mozRequestAnimationFrame ||
4 | window.oRequestAnimationFrame ||
5 | window.msRequestAnimationFrame ||
6 | function (callback) {
7 | window.setTimeout(callback, 1000/60);
8 | }
9 | });
10 |
11 | let canvas,
12 | gl,
13 | buffer,
14 | vertex_shader,
15 | fragment_shader,
16 | currentProgram,
17 | vertex_position,
18 | timeLocation,
19 | resolutionLocation,
20 | parameters = {
21 | start_time: new Date().getTime(),
22 | time: 0,
23 | screenWidth: 0,
24 | screenHeight: 0
25 | };
26 |
27 | init();
28 |
29 | async function importShader(shader) {
30 | let response = await fetch(shader);
31 | let data = await response.text();
32 | return data;
33 | }
34 |
35 | async function init() {
36 | fragment_shader = await importShader('fragmentshader.glsl');
37 | vertex_shader = await importShader('vertexshader.glsl');
38 |
39 | canvas = document.querySelector('canvas');
40 |
41 | try {
42 | gl = canvas.getContext('experimental-webgl');
43 | } catch(err) {}
44 |
45 | if(!gl) {
46 | throw "Cannot create WebGL context."
47 | }
48 |
49 | buffer = gl.createBuffer();
50 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
51 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0]), gl.STATIC_DRAW);
52 |
53 | currentProgram = createProgram(vertex_shader, fragment_shader);
54 |
55 | timeLocation = gl.getUniformLocation(currentProgram, 'time');
56 | resolutionLocation = gl.getUniformLocation(currentProgram, 'resolution');
57 |
58 | animate();
59 | }
60 |
61 | function createProgram(vertex, fragment) {
62 | const program = gl.createProgram(),
63 | vert_shader = createShader(vertex, gl.VERTEX_SHADER),
64 | frag_shader = createShader(fragment, gl.FRAGMENT_SHADER);
65 |
66 | if (vert_shader === null || frag_shader === null) {
67 | return null;
68 | }
69 |
70 | gl.attachShader(program, vert_shader);
71 | gl.attachShader(program, frag_shader);
72 |
73 | gl.deleteShader(vert_shader);
74 | gl.deleteShader(frag_shader);
75 |
76 | gl.linkProgram(program);
77 |
78 | return program;
79 | }
80 |
81 | function createShader(src, type) {
82 | const shader = gl.createShader(type);
83 | gl.shaderSource(shader, src);
84 | gl.compileShader(shader);
85 |
86 | if(!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
87 | console.log((type == gl.VERTEX_SHADER ? "VERTEX" : "FRAGMENT") + " SHADER:\n" + gl.getShaderInfoLog(shader));
88 | return null;
89 | }
90 |
91 | return shader;
92 | }
93 |
94 | function resize() {
95 | if(canvas.width != canvas.clientWidth || canvas.height != canvas.clientHeight) {
96 | canvas.width = canvas.clientWidth;
97 | canvas.height = canvas.clientHeight;
98 | parameters.screenWidth = canvas.width;
99 | parameters.screenHeight = canvas.height;
100 | gl.viewport(0, 0, canvas.width, canvas.height);
101 | }
102 | }
103 |
104 | function animate() {
105 | resize();
106 | render();
107 | requestAnimationFrame(animate);
108 | }
109 |
110 | function render() {
111 | if(!currentProgram) {
112 | return;
113 | }
114 |
115 | parameters.time = new Date().getTime() - parameters.start_time;
116 |
117 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
118 | gl.useProgram(currentProgram);
119 | gl.uniform1f(timeLocation, parameters.time / 1000);
120 | gl.uniform2f(resolutionLocation, parameters.screenWidth, parameters.screenHeight);
121 |
122 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
123 | gl.vertexAttribPointer(vertex_position, 2, gl.FLOAT, false, 0, 0);
124 | gl.enableVertexAttribArray(vertex_position);
125 | gl.drawArrays(gl.TRIANGLES, 0, 6);
126 | gl.disableVertexAttribArray(vertex_position);
127 | }
128 |
--------------------------------------------------------------------------------
/docs/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | * @author ScieCode / http://github.com/sciecode
8 | */
9 |
10 | // This set of controls performs orbiting, dollying (zooming), and panning.
11 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
12 | //
13 | // Orbit - left mouse / touch: one-finger move
14 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
15 | // Pan - right mouse, or left mouse + ctrl/meta/shiftKey, or arrow keys / touch: two-finger move
16 |
17 | THREE.OrbitControls = function ( object, domElement ) {
18 |
19 | if ( domElement === undefined ) console.warn( 'THREE.OrbitControls: The second parameter "domElement" is now mandatory.' );
20 | if ( domElement === document ) console.error( 'THREE.OrbitControls: "document" should not be used as the target "domElement". Please use "renderer.domElement" instead.' );
21 |
22 | this.object = object;
23 | this.domElement = domElement;
24 |
25 | // Set to false to disable this control
26 | this.enabled = true;
27 |
28 | // "target" sets the location of focus, where the object orbits around
29 | this.target = new THREE.Vector3();
30 |
31 | // How far you can dolly in and out ( PerspectiveCamera only )
32 | this.minDistance = 0;
33 | this.maxDistance = Infinity;
34 |
35 | // How far you can zoom in and out ( OrthographicCamera only )
36 | this.minZoom = 0;
37 | this.maxZoom = Infinity;
38 |
39 | // How far you can orbit vertically, upper and lower limits.
40 | // Range is 0 to Math.PI radians.
41 | this.minPolarAngle = 0; // radians
42 | this.maxPolarAngle = Math.PI; // radians
43 |
44 | // How far you can orbit horizontally, upper and lower limits.
45 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
46 | this.minAzimuthAngle = - Infinity; // radians
47 | this.maxAzimuthAngle = Infinity; // radians
48 |
49 | // Set to true to enable damping (inertia)
50 | // If damping is enabled, you must call controls.update() in your animation loop
51 | this.enableDamping = false;
52 | this.dampingFactor = 0.05;
53 |
54 | // This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
55 | // Set to false to disable zooming
56 | this.enableZoom = true;
57 | this.zoomSpeed = 1.0;
58 |
59 | // Set to false to disable rotating
60 | this.enableRotate = true;
61 | this.rotateSpeed = 1.0;
62 |
63 | // Set to false to disable panning
64 | this.enablePan = true;
65 | this.panSpeed = 1.0;
66 | this.screenSpacePanning = false; // if true, pan in screen-space
67 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
68 |
69 | // Set to true to automatically rotate around the target
70 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
71 | this.autoRotate = false;
72 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
73 |
74 | // Set to false to disable use of the keys
75 | this.enableKeys = true;
76 |
77 | // The four arrow keys
78 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
79 |
80 | // Mouse buttons
81 | this.mouseButtons = { LEFT: THREE.MOUSE.ROTATE, MIDDLE: THREE.MOUSE.DOLLY, RIGHT: THREE.MOUSE.PAN };
82 |
83 | // Touch fingers
84 | this.touches = { ONE: THREE.TOUCH.ROTATE, TWO: THREE.TOUCH.DOLLY_PAN };
85 |
86 | // for reset
87 | this.target0 = this.target.clone();
88 | this.position0 = this.object.position.clone();
89 | this.zoom0 = this.object.zoom;
90 |
91 | //
92 | // public methods
93 | //
94 |
95 | this.getPolarAngle = function () {
96 |
97 | return spherical.phi;
98 |
99 | };
100 |
101 | this.getAzimuthalAngle = function () {
102 |
103 | return spherical.theta;
104 |
105 | };
106 |
107 | this.saveState = function () {
108 |
109 | scope.target0.copy( scope.target );
110 | scope.position0.copy( scope.object.position );
111 | scope.zoom0 = scope.object.zoom;
112 |
113 | };
114 |
115 | this.reset = function () {
116 |
117 | scope.target.copy( scope.target0 );
118 | scope.object.position.copy( scope.position0 );
119 | scope.object.zoom = scope.zoom0;
120 |
121 | scope.object.updateProjectionMatrix();
122 | scope.dispatchEvent( changeEvent );
123 |
124 | scope.update();
125 |
126 | state = STATE.NONE;
127 |
128 | };
129 |
130 | // this method is exposed, but perhaps it would be better if we can make it private...
131 | this.update = function () {
132 |
133 | var offset = new THREE.Vector3();
134 |
135 | // so camera.up is the orbit axis
136 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
137 | var quatInverse = quat.clone().inverse();
138 |
139 | var lastPosition = new THREE.Vector3();
140 | var lastQuaternion = new THREE.Quaternion();
141 |
142 | return function update() {
143 |
144 | var position = scope.object.position;
145 |
146 | offset.copy( position ).sub( scope.target );
147 |
148 | // rotate offset to "y-axis-is-up" space
149 | offset.applyQuaternion( quat );
150 |
151 | // angle from z-axis around y-axis
152 | spherical.setFromVector3( offset );
153 |
154 | if ( scope.autoRotate && state === STATE.NONE ) {
155 |
156 | rotateLeft( getAutoRotationAngle() );
157 |
158 | }
159 |
160 | if ( scope.enableDamping ) {
161 |
162 | spherical.theta += sphericalDelta.theta * scope.dampingFactor;
163 | spherical.phi += sphericalDelta.phi * scope.dampingFactor;
164 |
165 | } else {
166 |
167 | spherical.theta += sphericalDelta.theta;
168 | spherical.phi += sphericalDelta.phi;
169 |
170 | }
171 |
172 | // restrict theta to be between desired limits
173 | spherical.theta = Math.max( scope.minAzimuthAngle, Math.min( scope.maxAzimuthAngle, spherical.theta ) );
174 |
175 | // restrict phi to be between desired limits
176 | spherical.phi = Math.max( scope.minPolarAngle, Math.min( scope.maxPolarAngle, spherical.phi ) );
177 |
178 | spherical.makeSafe();
179 |
180 |
181 | spherical.radius *= scale;
182 |
183 | // restrict radius to be between desired limits
184 | spherical.radius = Math.max( scope.minDistance, Math.min( scope.maxDistance, spherical.radius ) );
185 |
186 | // move target to panned location
187 |
188 | if ( scope.enableDamping === true ) {
189 |
190 | scope.target.addScaledVector( panOffset, scope.dampingFactor );
191 |
192 | } else {
193 |
194 | scope.target.add( panOffset );
195 |
196 | }
197 |
198 | offset.setFromSpherical( spherical );
199 |
200 | // rotate offset back to "camera-up-vector-is-up" space
201 | offset.applyQuaternion( quatInverse );
202 |
203 | position.copy( scope.target ).add( offset );
204 |
205 | scope.object.lookAt( scope.target );
206 |
207 | if ( scope.enableDamping === true ) {
208 |
209 | sphericalDelta.theta *= ( 1 - scope.dampingFactor );
210 | sphericalDelta.phi *= ( 1 - scope.dampingFactor );
211 |
212 | panOffset.multiplyScalar( 1 - scope.dampingFactor );
213 |
214 | } else {
215 |
216 | sphericalDelta.set( 0, 0, 0 );
217 |
218 | panOffset.set( 0, 0, 0 );
219 |
220 | }
221 |
222 | scale = 1;
223 |
224 | // update condition is:
225 | // min(camera displacement, camera rotation in radians)^2 > EPS
226 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
227 |
228 | if ( zoomChanged ||
229 | lastPosition.distanceToSquared( scope.object.position ) > EPS ||
230 | 8 * ( 1 - lastQuaternion.dot( scope.object.quaternion ) ) > EPS ) {
231 |
232 | scope.dispatchEvent( changeEvent );
233 |
234 | lastPosition.copy( scope.object.position );
235 | lastQuaternion.copy( scope.object.quaternion );
236 | zoomChanged = false;
237 |
238 | return true;
239 |
240 | }
241 |
242 | return false;
243 |
244 | };
245 |
246 | }();
247 |
248 | this.dispose = function () {
249 |
250 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false );
251 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false );
252 | scope.domElement.removeEventListener( 'wheel', onMouseWheel, false );
253 |
254 | scope.domElement.removeEventListener( 'touchstart', onTouchStart, false );
255 | scope.domElement.removeEventListener( 'touchend', onTouchEnd, false );
256 | scope.domElement.removeEventListener( 'touchmove', onTouchMove, false );
257 |
258 | document.removeEventListener( 'mousemove', onMouseMove, false );
259 | document.removeEventListener( 'mouseup', onMouseUp, false );
260 |
261 | scope.domElement.removeEventListener( 'keydown', onKeyDown, false );
262 |
263 | //scope.dispatchEvent( { type: 'dispose' } ); // should this be added here?
264 |
265 | };
266 |
267 | //
268 | // internals
269 | //
270 |
271 | var scope = this;
272 |
273 | var changeEvent = { type: 'change' };
274 | var startEvent = { type: 'start' };
275 | var endEvent = { type: 'end' };
276 |
277 | var STATE = {
278 | NONE: - 1,
279 | ROTATE: 0,
280 | DOLLY: 1,
281 | PAN: 2,
282 | TOUCH_ROTATE: 3,
283 | TOUCH_PAN: 4,
284 | TOUCH_DOLLY_PAN: 5,
285 | TOUCH_DOLLY_ROTATE: 6
286 | };
287 |
288 | var state = STATE.NONE;
289 |
290 | var EPS = 0.000001;
291 |
292 | // current position in spherical coordinates
293 | var spherical = new THREE.Spherical();
294 | var sphericalDelta = new THREE.Spherical();
295 |
296 | var scale = 1;
297 | var panOffset = new THREE.Vector3();
298 | var zoomChanged = false;
299 |
300 | var rotateStart = new THREE.Vector2();
301 | var rotateEnd = new THREE.Vector2();
302 | var rotateDelta = new THREE.Vector2();
303 |
304 | var panStart = new THREE.Vector2();
305 | var panEnd = new THREE.Vector2();
306 | var panDelta = new THREE.Vector2();
307 |
308 | var dollyStart = new THREE.Vector2();
309 | var dollyEnd = new THREE.Vector2();
310 | var dollyDelta = new THREE.Vector2();
311 |
312 | function getAutoRotationAngle() {
313 |
314 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
315 |
316 | }
317 |
318 | function getZoomScale() {
319 |
320 | return Math.pow( 0.95, scope.zoomSpeed );
321 |
322 | }
323 |
324 | function rotateLeft( angle ) {
325 |
326 | sphericalDelta.theta -= angle;
327 |
328 | }
329 |
330 | function rotateUp( angle ) {
331 |
332 | sphericalDelta.phi -= angle;
333 |
334 | }
335 |
336 | var panLeft = function () {
337 |
338 | var v = new THREE.Vector3();
339 |
340 | return function panLeft( distance, objectMatrix ) {
341 |
342 | v.setFromMatrixColumn( objectMatrix, 0 ); // get X column of objectMatrix
343 | v.multiplyScalar( - distance );
344 |
345 | panOffset.add( v );
346 |
347 | };
348 |
349 | }();
350 |
351 | var panUp = function () {
352 |
353 | var v = new THREE.Vector3();
354 |
355 | return function panUp( distance, objectMatrix ) {
356 |
357 | if ( scope.screenSpacePanning === true ) {
358 |
359 | v.setFromMatrixColumn( objectMatrix, 1 );
360 |
361 | } else {
362 |
363 | v.setFromMatrixColumn( objectMatrix, 0 );
364 | v.crossVectors( scope.object.up, v );
365 |
366 | }
367 |
368 | v.multiplyScalar( distance );
369 |
370 | panOffset.add( v );
371 |
372 | };
373 |
374 | }();
375 |
376 | // deltaX and deltaY are in pixels; right and down are positive
377 | var pan = function () {
378 |
379 | var offset = new THREE.Vector3();
380 |
381 | return function pan( deltaX, deltaY ) {
382 |
383 | var element = scope.domElement;
384 |
385 | if ( scope.object.isPerspectiveCamera ) {
386 |
387 | // perspective
388 | var position = scope.object.position;
389 | offset.copy( position ).sub( scope.target );
390 | var targetDistance = offset.length();
391 |
392 | // half of the fov is center to top of screen
393 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
394 |
395 | // we use only clientHeight here so aspect ratio does not distort speed
396 | panLeft( 2 * deltaX * targetDistance / element.clientHeight, scope.object.matrix );
397 | panUp( 2 * deltaY * targetDistance / element.clientHeight, scope.object.matrix );
398 |
399 | } else if ( scope.object.isOrthographicCamera ) {
400 |
401 | // orthographic
402 | panLeft( deltaX * ( scope.object.right - scope.object.left ) / scope.object.zoom / element.clientWidth, scope.object.matrix );
403 | panUp( deltaY * ( scope.object.top - scope.object.bottom ) / scope.object.zoom / element.clientHeight, scope.object.matrix );
404 |
405 | } else {
406 |
407 | // camera neither orthographic nor perspective
408 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
409 | scope.enablePan = false;
410 |
411 | }
412 |
413 | };
414 |
415 | }();
416 |
417 | function dollyIn( dollyScale ) {
418 |
419 | if ( scope.object.isPerspectiveCamera ) {
420 |
421 | scale /= dollyScale;
422 |
423 | } else if ( scope.object.isOrthographicCamera ) {
424 |
425 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom * dollyScale ) );
426 | scope.object.updateProjectionMatrix();
427 | zoomChanged = true;
428 |
429 | } else {
430 |
431 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
432 | scope.enableZoom = false;
433 |
434 | }
435 |
436 | }
437 |
438 | function dollyOut( dollyScale ) {
439 |
440 | if ( scope.object.isPerspectiveCamera ) {
441 |
442 | scale *= dollyScale;
443 |
444 | } else if ( scope.object.isOrthographicCamera ) {
445 |
446 | scope.object.zoom = Math.max( scope.minZoom, Math.min( scope.maxZoom, scope.object.zoom / dollyScale ) );
447 | scope.object.updateProjectionMatrix();
448 | zoomChanged = true;
449 |
450 | } else {
451 |
452 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
453 | scope.enableZoom = false;
454 |
455 | }
456 |
457 | }
458 |
459 | //
460 | // event callbacks - update the object state
461 | //
462 |
463 | function handleMouseDownRotate( event ) {
464 |
465 | rotateStart.set( event.clientX, event.clientY );
466 |
467 | }
468 |
469 | function handleMouseDownDolly( event ) {
470 |
471 | dollyStart.set( event.clientX, event.clientY );
472 |
473 | }
474 |
475 | function handleMouseDownPan( event ) {
476 |
477 | panStart.set( event.clientX, event.clientY );
478 |
479 | }
480 |
481 | function handleMouseMoveRotate( event ) {
482 |
483 | rotateEnd.set( event.clientX, event.clientY );
484 |
485 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
486 |
487 | var element = scope.domElement;
488 |
489 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
490 |
491 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
492 |
493 | rotateStart.copy( rotateEnd );
494 |
495 | scope.update();
496 |
497 | }
498 |
499 | function handleMouseMoveDolly( event ) {
500 |
501 | dollyEnd.set( event.clientX, event.clientY );
502 |
503 | dollyDelta.subVectors( dollyEnd, dollyStart );
504 |
505 | if ( dollyDelta.y > 0 ) {
506 |
507 | dollyIn( getZoomScale() );
508 |
509 | } else if ( dollyDelta.y < 0 ) {
510 |
511 | dollyOut( getZoomScale() );
512 |
513 | }
514 |
515 | dollyStart.copy( dollyEnd );
516 |
517 | scope.update();
518 |
519 | }
520 |
521 | function handleMouseMovePan( event ) {
522 |
523 | panEnd.set( event.clientX, event.clientY );
524 |
525 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
526 |
527 | pan( panDelta.x, panDelta.y );
528 |
529 | panStart.copy( panEnd );
530 |
531 | scope.update();
532 |
533 | }
534 |
535 | function handleMouseUp( /*event*/ ) {
536 |
537 | // no-op
538 |
539 | }
540 |
541 | function handleMouseWheel( event ) {
542 |
543 | if ( event.deltaY < 0 ) {
544 |
545 | dollyOut( getZoomScale() );
546 |
547 | } else if ( event.deltaY > 0 ) {
548 |
549 | dollyIn( getZoomScale() );
550 |
551 | }
552 |
553 | scope.update();
554 |
555 | }
556 |
557 | function handleKeyDown( event ) {
558 |
559 | var needsUpdate = false;
560 |
561 | switch ( event.keyCode ) {
562 |
563 | case scope.keys.UP:
564 | pan( 0, scope.keyPanSpeed );
565 | needsUpdate = true;
566 | break;
567 |
568 | case scope.keys.BOTTOM:
569 | pan( 0, - scope.keyPanSpeed );
570 | needsUpdate = true;
571 | break;
572 |
573 | case scope.keys.LEFT:
574 | pan( scope.keyPanSpeed, 0 );
575 | needsUpdate = true;
576 | break;
577 |
578 | case scope.keys.RIGHT:
579 | pan( - scope.keyPanSpeed, 0 );
580 | needsUpdate = true;
581 | break;
582 |
583 | }
584 |
585 | if ( needsUpdate ) {
586 |
587 | // prevent the browser from scrolling on cursor keys
588 | event.preventDefault();
589 |
590 | scope.update();
591 |
592 | }
593 |
594 |
595 | }
596 |
597 | function handleTouchStartRotate( event ) {
598 |
599 | if ( event.touches.length == 1 ) {
600 |
601 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
602 |
603 | } else {
604 |
605 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
606 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
607 |
608 | rotateStart.set( x, y );
609 |
610 | }
611 |
612 | }
613 |
614 | function handleTouchStartPan( event ) {
615 |
616 | if ( event.touches.length == 1 ) {
617 |
618 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
619 |
620 | } else {
621 |
622 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
623 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
624 |
625 | panStart.set( x, y );
626 |
627 | }
628 |
629 | }
630 |
631 | function handleTouchStartDolly( event ) {
632 |
633 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
634 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
635 |
636 | var distance = Math.sqrt( dx * dx + dy * dy );
637 |
638 | dollyStart.set( 0, distance );
639 |
640 | }
641 |
642 | function handleTouchStartDollyPan( event ) {
643 |
644 | if ( scope.enableZoom ) handleTouchStartDolly( event );
645 |
646 | if ( scope.enablePan ) handleTouchStartPan( event );
647 |
648 | }
649 |
650 | function handleTouchStartDollyRotate( event ) {
651 |
652 | if ( scope.enableZoom ) handleTouchStartDolly( event );
653 |
654 | if ( scope.enableRotate ) handleTouchStartRotate( event );
655 |
656 | }
657 |
658 | function handleTouchMoveRotate( event ) {
659 |
660 | if ( event.touches.length == 1 ) {
661 |
662 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
663 |
664 | } else {
665 |
666 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
667 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
668 |
669 | rotateEnd.set( x, y );
670 |
671 | }
672 |
673 | rotateDelta.subVectors( rotateEnd, rotateStart ).multiplyScalar( scope.rotateSpeed );
674 |
675 | var element = scope.domElement;
676 |
677 | rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientHeight ); // yes, height
678 |
679 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight );
680 |
681 | rotateStart.copy( rotateEnd );
682 |
683 | }
684 |
685 | function handleTouchMovePan( event ) {
686 |
687 | if ( event.touches.length == 1 ) {
688 |
689 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
690 |
691 | } else {
692 |
693 | var x = 0.5 * ( event.touches[ 0 ].pageX + event.touches[ 1 ].pageX );
694 | var y = 0.5 * ( event.touches[ 0 ].pageY + event.touches[ 1 ].pageY );
695 |
696 | panEnd.set( x, y );
697 |
698 | }
699 |
700 | panDelta.subVectors( panEnd, panStart ).multiplyScalar( scope.panSpeed );
701 |
702 | pan( panDelta.x, panDelta.y );
703 |
704 | panStart.copy( panEnd );
705 |
706 | }
707 |
708 | function handleTouchMoveDolly( event ) {
709 |
710 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
711 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
712 |
713 | var distance = Math.sqrt( dx * dx + dy * dy );
714 |
715 | dollyEnd.set( 0, distance );
716 |
717 | dollyDelta.set( 0, Math.pow( dollyEnd.y / dollyStart.y, scope.zoomSpeed ) );
718 |
719 | dollyIn( dollyDelta.y );
720 |
721 | dollyStart.copy( dollyEnd );
722 |
723 | }
724 |
725 | function handleTouchMoveDollyPan( event ) {
726 |
727 | if ( scope.enableZoom ) handleTouchMoveDolly( event );
728 |
729 | if ( scope.enablePan ) handleTouchMovePan( event );
730 |
731 | }
732 |
733 | function handleTouchMoveDollyRotate( event ) {
734 |
735 | if ( scope.enableZoom ) handleTouchMoveDolly( event );
736 |
737 | if ( scope.enableRotate ) handleTouchMoveRotate( event );
738 |
739 | }
740 |
741 | function handleTouchEnd( /*event*/ ) {
742 |
743 | // no-op
744 |
745 | }
746 |
747 | //
748 | // event handlers - FSM: listen for events and reset state
749 | //
750 |
751 | function onMouseDown( event ) {
752 |
753 | if ( scope.enabled === false ) return;
754 |
755 | // Prevent the browser from scrolling.
756 |
757 | event.preventDefault();
758 |
759 | // Manually set the focus since calling preventDefault above
760 | // prevents the browser from setting it automatically.
761 |
762 | scope.domElement.focus ? scope.domElement.focus() : window.focus();
763 |
764 | switch ( event.button ) {
765 |
766 | case 0:
767 |
768 | switch ( scope.mouseButtons.LEFT ) {
769 |
770 | case THREE.MOUSE.ROTATE:
771 |
772 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
773 |
774 | if ( scope.enablePan === false ) return;
775 |
776 | handleMouseDownPan( event );
777 |
778 | state = STATE.PAN;
779 |
780 | } else {
781 |
782 | if ( scope.enableRotate === false ) return;
783 |
784 | handleMouseDownRotate( event );
785 |
786 | state = STATE.ROTATE;
787 |
788 | }
789 |
790 | break;
791 |
792 | case THREE.MOUSE.PAN:
793 |
794 | if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
795 |
796 | if ( scope.enableRotate === false ) return;
797 |
798 | handleMouseDownRotate( event );
799 |
800 | state = STATE.ROTATE;
801 |
802 | } else {
803 |
804 | if ( scope.enablePan === false ) return;
805 |
806 | handleMouseDownPan( event );
807 |
808 | state = STATE.PAN;
809 |
810 | }
811 |
812 | break;
813 |
814 | default:
815 |
816 | state = STATE.NONE;
817 |
818 | }
819 |
820 | break;
821 |
822 |
823 | case 1:
824 |
825 | switch ( scope.mouseButtons.MIDDLE ) {
826 |
827 | case THREE.MOUSE.DOLLY:
828 |
829 | if ( scope.enableZoom === false ) return;
830 |
831 | handleMouseDownDolly( event );
832 |
833 | state = STATE.DOLLY;
834 |
835 | break;
836 |
837 |
838 | default:
839 |
840 | state = STATE.NONE;
841 |
842 | }
843 |
844 | break;
845 |
846 | case 2:
847 |
848 | switch ( scope.mouseButtons.RIGHT ) {
849 |
850 | case THREE.MOUSE.ROTATE:
851 |
852 | if ( scope.enableRotate === false ) return;
853 |
854 | handleMouseDownRotate( event );
855 |
856 | state = STATE.ROTATE;
857 |
858 | break;
859 |
860 | case THREE.MOUSE.PAN:
861 |
862 | if ( scope.enablePan === false ) return;
863 |
864 | handleMouseDownPan( event );
865 |
866 | state = STATE.PAN;
867 |
868 | break;
869 |
870 | default:
871 |
872 | state = STATE.NONE;
873 |
874 | }
875 |
876 | break;
877 |
878 | }
879 |
880 | if ( state !== STATE.NONE ) {
881 |
882 | document.addEventListener( 'mousemove', onMouseMove, false );
883 | document.addEventListener( 'mouseup', onMouseUp, false );
884 |
885 | scope.dispatchEvent( startEvent );
886 |
887 | }
888 |
889 | }
890 |
891 | function onMouseMove( event ) {
892 |
893 | if ( scope.enabled === false ) return;
894 |
895 | event.preventDefault();
896 |
897 | switch ( state ) {
898 |
899 | case STATE.ROTATE:
900 |
901 | if ( scope.enableRotate === false ) return;
902 |
903 | handleMouseMoveRotate( event );
904 |
905 | break;
906 |
907 | case STATE.DOLLY:
908 |
909 | if ( scope.enableZoom === false ) return;
910 |
911 | handleMouseMoveDolly( event );
912 |
913 | break;
914 |
915 | case STATE.PAN:
916 |
917 | if ( scope.enablePan === false ) return;
918 |
919 | handleMouseMovePan( event );
920 |
921 | break;
922 |
923 | }
924 |
925 | }
926 |
927 | function onMouseUp( event ) {
928 |
929 | if ( scope.enabled === false ) return;
930 |
931 | handleMouseUp( event );
932 |
933 | document.removeEventListener( 'mousemove', onMouseMove, false );
934 | document.removeEventListener( 'mouseup', onMouseUp, false );
935 |
936 | scope.dispatchEvent( endEvent );
937 |
938 | state = STATE.NONE;
939 |
940 | }
941 |
942 | function onMouseWheel( event ) {
943 |
944 | if ( scope.enabled === false || scope.enableZoom === false || ( state !== STATE.NONE && state !== STATE.ROTATE ) ) return;
945 |
946 | event.preventDefault();
947 | event.stopPropagation();
948 |
949 | scope.dispatchEvent( startEvent );
950 |
951 | handleMouseWheel( event );
952 |
953 | scope.dispatchEvent( endEvent );
954 |
955 | }
956 |
957 | function onKeyDown( event ) {
958 |
959 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
960 |
961 | handleKeyDown( event );
962 |
963 | }
964 |
965 | function onTouchStart( event ) {
966 |
967 | if ( scope.enabled === false ) return;
968 |
969 | event.preventDefault();
970 |
971 | switch ( event.touches.length ) {
972 |
973 | case 1:
974 |
975 | switch ( scope.touches.ONE ) {
976 |
977 | case THREE.TOUCH.ROTATE:
978 |
979 | if ( scope.enableRotate === false ) return;
980 |
981 | handleTouchStartRotate( event );
982 |
983 | state = STATE.TOUCH_ROTATE;
984 |
985 | break;
986 |
987 | case THREE.TOUCH.PAN:
988 |
989 | if ( scope.enablePan === false ) return;
990 |
991 | handleTouchStartPan( event );
992 |
993 | state = STATE.TOUCH_PAN;
994 |
995 | break;
996 |
997 | default:
998 |
999 | state = STATE.NONE;
1000 |
1001 | }
1002 |
1003 | break;
1004 |
1005 | case 2:
1006 |
1007 | switch ( scope.touches.TWO ) {
1008 |
1009 | case THREE.TOUCH.DOLLY_PAN:
1010 |
1011 | if ( scope.enableZoom === false && scope.enablePan === false ) return;
1012 |
1013 | handleTouchStartDollyPan( event );
1014 |
1015 | state = STATE.TOUCH_DOLLY_PAN;
1016 |
1017 | break;
1018 |
1019 | case THREE.TOUCH.DOLLY_ROTATE:
1020 |
1021 | if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1022 |
1023 | handleTouchStartDollyRotate( event );
1024 |
1025 | state = STATE.TOUCH_DOLLY_ROTATE;
1026 |
1027 | break;
1028 |
1029 | default:
1030 |
1031 | state = STATE.NONE;
1032 |
1033 | }
1034 |
1035 | break;
1036 |
1037 | default:
1038 |
1039 | state = STATE.NONE;
1040 |
1041 | }
1042 |
1043 | if ( state !== STATE.NONE ) {
1044 |
1045 | scope.dispatchEvent( startEvent );
1046 |
1047 | }
1048 |
1049 | }
1050 |
1051 | function onTouchMove( event ) {
1052 |
1053 | if ( scope.enabled === false ) return;
1054 |
1055 | event.preventDefault();
1056 | event.stopPropagation();
1057 |
1058 | switch ( state ) {
1059 |
1060 | case STATE.TOUCH_ROTATE:
1061 |
1062 | if ( scope.enableRotate === false ) return;
1063 |
1064 | handleTouchMoveRotate( event );
1065 |
1066 | scope.update();
1067 |
1068 | break;
1069 |
1070 | case STATE.TOUCH_PAN:
1071 |
1072 | if ( scope.enablePan === false ) return;
1073 |
1074 | handleTouchMovePan( event );
1075 |
1076 | scope.update();
1077 |
1078 | break;
1079 |
1080 | case STATE.TOUCH_DOLLY_PAN:
1081 |
1082 | if ( scope.enableZoom === false && scope.enablePan === false ) return;
1083 |
1084 | handleTouchMoveDollyPan( event );
1085 |
1086 | scope.update();
1087 |
1088 | break;
1089 |
1090 | case STATE.TOUCH_DOLLY_ROTATE:
1091 |
1092 | if ( scope.enableZoom === false && scope.enableRotate === false ) return;
1093 |
1094 | handleTouchMoveDollyRotate( event );
1095 |
1096 | scope.update();
1097 |
1098 | break;
1099 |
1100 | default:
1101 |
1102 | state = STATE.NONE;
1103 |
1104 | }
1105 |
1106 | }
1107 |
1108 | function onTouchEnd( event ) {
1109 |
1110 | if ( scope.enabled === false ) return;
1111 |
1112 | handleTouchEnd( event );
1113 |
1114 | scope.dispatchEvent( endEvent );
1115 |
1116 | state = STATE.NONE;
1117 |
1118 | }
1119 |
1120 | function onContextMenu( event ) {
1121 |
1122 | if ( scope.enabled === false ) return;
1123 |
1124 | event.preventDefault();
1125 |
1126 | }
1127 |
1128 | //
1129 |
1130 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false );
1131 |
1132 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false );
1133 | scope.domElement.addEventListener( 'wheel', onMouseWheel, false );
1134 |
1135 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false );
1136 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false );
1137 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false );
1138 |
1139 | scope.domElement.addEventListener( 'keydown', onKeyDown, false );
1140 |
1141 | // make sure element can receive keys.
1142 |
1143 | if ( scope.domElement.tabIndex === - 1 ) {
1144 |
1145 | scope.domElement.tabIndex = 0;
1146 |
1147 | }
1148 |
1149 | // force an update at start
1150 |
1151 | this.update();
1152 |
1153 | };
1154 |
1155 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1156 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
1157 |
1158 |
1159 | // This set of controls performs orbiting, dollying (zooming), and panning.
1160 | // Unlike TrackballControls, it maintains the "up" direction object.up (+Y by default).
1161 | // This is very similar to OrbitControls, another set of touch behavior
1162 | //
1163 | // Orbit - right mouse, or left mouse + ctrl/meta/shiftKey / touch: two-finger rotate
1164 | // Zoom - middle mouse, or mousewheel / touch: two-finger spread or squish
1165 | // Pan - left mouse, or arrow keys / touch: one-finger move
1166 |
1167 | THREE.MapControls = function ( object, domElement ) {
1168 |
1169 | THREE.OrbitControls.call( this, object, domElement );
1170 |
1171 | this.mouseButtons.LEFT = THREE.MOUSE.PAN;
1172 | this.mouseButtons.RIGHT = THREE.MOUSE.ROTATE;
1173 |
1174 | this.touches.ONE = THREE.TOUCH.PAN;
1175 | this.touches.TWO = THREE.TOUCH.DOLLY_ROTATE;
1176 |
1177 | };
1178 |
1179 | THREE.MapControls.prototype = Object.create( THREE.EventDispatcher.prototype );
1180 | THREE.MapControls.prototype.constructor = THREE.MapControls;
1181 |
--------------------------------------------------------------------------------
/docs/glsl-bumpy-sphere/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform float time;
6 | uniform vec2 resolution;
7 |
8 | const int MAX_MARCHING_STEPS = 255;
9 | const float MIN_DIST = 0.0;
10 | const float MAX_DIST = 100.0;
11 | const float EPSILON = 0.0001;
12 | const vec3 AMBIENT_LIGHT = vec3(0.5);
13 |
14 | // Rotation function from https://www.shadertoy.com/view/4tcGDr
15 | mat3 rotateY(float theta) {
16 | float c = cos(theta);
17 | float s = sin(theta);
18 | return mat3(
19 | vec3(c, 0.0, s),
20 | vec3(0.0, 1.0, 0.0),
21 | vec3(-s, 0.0, c)
22 | );
23 | }
24 |
25 | float sphere(vec3 p, float s) {
26 | return length(p) - s;
27 | }
28 |
29 | float displace(vec3 p) {
30 | p = rotateY(time / 2.0) * p;
31 | float d1 = sphere(p, 8.0);
32 | float d2 = sin(sin(time / 5.0) * 1.2 * p.x) * sin(sin(time / 5.0) * 1.2 * p.y) * sin(sin(time / 5.0) * 1.2 * p.z);
33 | return d1 + d2;
34 | }
35 |
36 | float scene(vec3 p) {
37 | return displace(p);
38 | }
39 |
40 | /*
41 | https://www.shadertoy.com/view/llt3R4
42 | Return the shortest distance from the camera to the scene surface.
43 |
44 | cam: The ray origin, the scene camera.
45 | dir: The normalized direction in which to march the ray.
46 | start: The starting distance from the camera.
47 | end: The maximum distance from the camera before giving up.
48 | */
49 | float raymarch(vec3 cam, vec3 dir, float start, float end) {
50 | float depth = start; // Depth begins at start point.
51 | for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
52 | float dist = scene(cam + depth * dir);
53 | if (dist < EPSILON) {
54 | return depth; // If we hit the sphere, return that measurement.
55 | }
56 | depth += dist; // If we haven't hit the sphere yet, increment the depth.
57 | if (depth >= end) {
58 | return end; // If we make it to the end, we haven't hit the sphere.
59 | }
60 |
61 | }
62 | return end;
63 | }
64 |
65 | /*
66 | Return the normalized direction of the ray.
67 |
68 | fov: Vertical field of view in degrees.
69 | size: Resolution of the output image.
70 | fc: The x,y coordinate of the pixel in the output image.
71 | */
72 | vec3 rayDir(float fov, vec2 size, vec2 fc) {
73 | vec2 xy = fc - size / 2.0;
74 | float z = size.y / tan(radians(fov) / 2.0);
75 | return normalize(vec3(xy, -z));
76 | }
77 |
78 | /*
79 | Functions for phong shading.
80 | https://www.shadertoy.com/view/lt33z7
81 | Estimate the normal of the surface of the sphere at point p.
82 | */
83 | vec3 estNormal(vec3 p) {
84 | return normalize(vec3(
85 | scene(vec3(p.x + EPSILON, p.y, p.z)) - scene(vec3(p.x - EPSILON, p.y, p.z)),
86 | scene(vec3(p.x, p.y + EPSILON, p.z)) - scene(vec3(p.x, p.y - EPSILON, p.z)),
87 | scene(vec3(p.x, p.y, p.z + EPSILON)) - scene(vec3(p.x, p.y, p.z - EPSILON))
88 | ));
89 | }
90 |
91 | /*
92 | A light source for phong shading.
93 |
94 | d: Diffuse color.
95 | s: Specular color.
96 | alpha: Shininess coefficient.
97 | p: Position of the point being lit.
98 | cam: Camera position.
99 | pos: Position of the light.
100 | intensity: Color/intensity of the light.
101 | */
102 | vec3 light(vec3 d, vec3 s, float alpha, vec3 p, vec3 cam, vec3 pos, vec3 intensity) {
103 | vec3 N = estNormal(p);
104 | vec3 L = normalize(pos - p);
105 | vec3 V = normalize(cam - p);
106 | vec3 R = normalize(reflect(-L, N));
107 |
108 | float dotLN = dot(L, N);
109 | float dotRV = dot(R, V);
110 |
111 | if(dotLN < 0.0) {
112 | // The light is not visible from this point on the surface.
113 | return vec3(0.0);
114 | }
115 |
116 | if(dotRV < 0.0) {
117 | // Light reflection in the opposite direction of the camera, apply only diffuse.
118 | return intensity * (d * dotLN);
119 | }
120 |
121 | return intensity * (d * dotLN + s * pow(dotRV, alpha));
122 | }
123 |
124 | /*
125 | Apply phong shading with lights.
126 |
127 | a: Ambient color.
128 | d: Diffuse color.
129 | s: Specular color.
130 | alpha: Shininess coefficient.
131 | p: Position of the point being lit.
132 | cam: Camera position.
133 | */
134 | vec3 phongShading(vec3 a, vec3 d, vec3 s, float alpha, vec3 p, vec3 cam) {
135 | vec3 color = AMBIENT_LIGHT * a;
136 | vec3 lightPos = vec3(20.0, 20.0, 40.0);
137 | vec3 lightIntensity = vec3(0.4);
138 |
139 | color += light(d, s, alpha, p, cam, lightPos, lightIntensity);
140 |
141 | return color;
142 | }
143 |
144 | void main() {
145 | vec3 dir = rayDir(45.0, resolution, gl_FragCoord.xy);
146 | vec3 cam = vec3(0.0, 0.0, 50.0);
147 | float dist = raymarch(cam, dir, MIN_DIST, MAX_DIST);
148 |
149 | if(dist > MAX_DIST - EPSILON) {
150 | // Didn't hit anything.
151 | gl_FragColor = vec4(vec3(0.0), 1.0);
152 | return;
153 | }
154 |
155 | vec3 p = cam + dist * dir;
156 | vec3 a = vec3(0.0);
157 | vec3 d = vec3(0.95, 0.53, 0.7);
158 | vec3 s = vec3(1.0);
159 | float alpha = 100.0;
160 |
161 | vec3 color = phongShading(a, d, s, alpha, p, cam);
162 |
163 | gl_FragColor = vec4(color, 1.0);
164 | }
--------------------------------------------------------------------------------
/docs/glsl-bumpy-sphere/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/docs/glsl-cubes/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform float time;
6 | uniform vec2 resolution;
7 |
8 | const int MAX_MARCHING_STEPS = 1000;
9 | const float MIN_DISTANCE = 0.0;
10 | const float MAX_DISTANCE = 100.0;
11 | const float EPSILON = 0.0001;
12 |
13 | float roundBox(vec3 p, vec3 b, float r) {
14 | return length(max(abs(p) - b, 0.0)) - r;
15 | }
16 |
17 | float repeat(vec3 p, vec3 c) {
18 | vec3 q = mod(p, c) - 0.5 * c;
19 | return roundBox(q, vec3(0.2, 0.2, 0.2), 0.5);
20 | }
21 |
22 | float scene(vec3 p) {
23 | return repeat(p, vec3(3.0, 3.0, 3.0));
24 | }
25 |
26 | float raymarch(vec3 eye, vec3 direction, float start, float end) {
27 | float depth = start;
28 | for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
29 | float dist = scene(eye + depth * direction);
30 | if (dist < EPSILON) {
31 | return depth;
32 | }
33 | depth += dist;
34 | if (depth >= end) {
35 | return end;
36 | }
37 | }
38 | return end;
39 | }
40 |
41 | vec3 rayDirection(float fov, vec2 size, vec2 fragCoord) {
42 | vec2 xy = fragCoord - size / 2.0;
43 | float z = size.y / tan(radians(fov) / 2.0);
44 | return normalize(vec3(xy, -z));
45 | }
46 |
47 | vec3 estimateNormal(vec3 p) {
48 | return normalize(vec3(
49 | scene(vec3(p.x + EPSILON, p.y, p.z)) - scene(vec3(p.x - EPSILON, p.y, p.z)),
50 | scene(vec3(p.x, p.y + EPSILON, p.z)) - scene(vec3(p.x, p.y - EPSILON, p.z)),
51 | scene(vec3(p.x, p.y, p.z + EPSILON)) - scene(vec3(p.x, p.y, p.z - EPSILON))
52 | ));
53 | }
54 |
55 | vec3 phongLight(vec3 diffuse, vec3 specular, float alpha, vec3 p, vec3 eye, vec3 lightPos, vec3 lightIntensity) {
56 | vec3 N = estimateNormal(p);
57 | vec3 L = normalize(lightPos - p);
58 | vec3 V = normalize(eye - p);
59 | vec3 R = normalize(reflect(-L, N));
60 |
61 | float dotLN = dot(L, N);
62 | float dotRV = dot(R, V);
63 |
64 | if (dotLN < 0.0) {
65 | return vec3(0.0, 0.0, 0.0);
66 | }
67 |
68 | if (dotRV < 0.0) {
69 | return lightIntensity * (diffuse * dotLN);
70 | }
71 | return lightIntensity * (diffuse * dotLN + specular * pow(dotRV, alpha));
72 | }
73 |
74 | vec3 phongIllumination(vec3 ambient, vec3 diffuse, vec3 specular, float alpha, vec3 p, vec3 eye) {
75 | const vec3 ambientLight = 0.5 * vec3(1.0, 1.0, 1.0);
76 | vec3 color = ambientLight * ambient;
77 | vec3 light1Pos = vec3(4.0, 5.0, -time * 10.0);
78 | vec3 light1Intensity = vec3(0.4, 0.4, 0.4);
79 | color += phongLight(diffuse, specular, alpha, p, eye, light1Pos, light1Intensity);
80 | return color;
81 | }
82 |
83 | mat4 viewMatrix(vec3 eye, vec3 center, vec3 up) {
84 | vec3 f = normalize(center - eye);
85 | vec3 s = normalize(cross(f, up));
86 | vec3 u = cross(s, f);
87 | return mat4(
88 | vec4(s, 0.0),
89 | vec4(u, 0.0),
90 | vec4(-f, 0.0),
91 | vec4(0.0, 0.0, 0.0, 1)
92 | );
93 | }
94 |
95 | void main() {
96 | vec3 dir = rayDirection(50.0, resolution.xy, gl_FragCoord.xy);
97 | vec3 eye = vec3(3.0, 0.0, -time * 10.0);
98 | mat4 viewToWorld = viewMatrix(eye, vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
99 | vec3 worldDir = (viewToWorld * vec4(dir, 0.0)).xyz;
100 | float dist = raymarch(eye, dir, MIN_DISTANCE, MAX_DISTANCE);
101 |
102 | if (dist > MAX_DISTANCE - EPSILON) {
103 | vec2 st = gl_FragCoord.xy/resolution.xy;
104 | float pct = 0.6 - distance(st, vec2(0.5));
105 | vec3 color = vec3(0.0 * pct, 0.8 * pct, 1.0 * pct);
106 | gl_FragColor = vec4(color, 1.0);
107 | return;
108 | }
109 |
110 | vec3 p = eye + dist * dir;
111 | vec3 ambient = vec3(0.0, 0.6, 0.8);
112 | vec3 diffuse = vec3(0.0, 0.6, 0.8);
113 | vec3 specular = vec3(1.0, 1.0, 1.0);
114 | float shininess = 50.0;
115 |
116 | vec3 color = phongIllumination(ambient, diffuse, specular, shininess, p, eye);
117 |
118 | gl_FragColor = vec4(color, 1.0);
119 | }
--------------------------------------------------------------------------------
/docs/glsl-cubes/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/docs/glsl-juliafractal/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform vec2 resolution;
6 | uniform float time;
7 |
8 | const int ITER = 15;
9 | float SCALE = (resolution.x / resolution.y) * 0.002;
10 | vec2 c = vec2(sin(time * 0.5) + 0.5, sin(time * 0.5) + 0.3);
11 |
12 | vec3 julia(vec2 z) {
13 | for (int i = 0; i < ITER; i++) {
14 | float x = (z.x * z.x - z.y * z.y) + c.x;
15 | float y = (z.y * z.x + z.x * z.y) + c.y;
16 |
17 | if ((x * x + y * y) > 10.0) return vec3(0.0, 0.0, 0.0);
18 | z.x = x;
19 | z.y = y;
20 |
21 | }
22 | return vec3(0.0, z.x, z.x);
23 | }
24 |
25 | void main() {
26 | vec2 z;
27 |
28 | z.x = (gl_FragCoord.x) * SCALE - resolution.x * (SCALE / 2.0);
29 | z.y = (gl_FragCoord.y) * SCALE - resolution.y * (SCALE / 2.0);
30 |
31 | vec3 color = julia(z);
32 | gl_FragColor = vec4(color, 1.0);
33 | }
--------------------------------------------------------------------------------
/docs/glsl-juliafractal/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/docs/glsl-just-a-cube/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform float time;
6 | uniform vec2 resolution;
7 |
8 | const int MAX_MARCHING_STEPS = 1000;
9 | const float MIN_DISTANCE = 0.0;
10 | const float MAX_DISTANCE = 100.0;
11 | const float EPSILON = 0.0001;
12 |
13 | float cube(vec3 p, vec3 b) {
14 | return length(max(abs(p) - b, 0.0));
15 | }
16 |
17 | float scene(vec3 p) {
18 | return cube(p, vec3(1.0, 1.0, 1.0));
19 | }
20 |
21 | float raymarch(vec3 eye, vec3 direction, float start, float end) {
22 | float depth = start;
23 | for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
24 | float dist = scene(eye + depth * direction);
25 | if (dist < EPSILON) {
26 | return depth;
27 | }
28 | depth += dist;
29 | if (depth >= end) {
30 | return end;
31 | }
32 | }
33 | return end;
34 | }
35 |
36 | vec3 rayDirection(float fov, vec2 size, vec2 fragCoord) {
37 | vec2 xy = fragCoord - size / 2.0;
38 | float z = size.y / tan(radians(fov) / 2.0);
39 | return normalize(vec3(xy, -z));
40 | }
41 |
42 | vec3 estimateNormal(vec3 p) {
43 | return normalize(vec3(
44 | scene(vec3(p.x + EPSILON, p.y, p.z)) - scene(vec3(p.x - EPSILON, p.y, p.z)),
45 | scene(vec3(p.x, p.y + EPSILON, p.z)) - scene(vec3(p.x, p.y - EPSILON, p.z)),
46 | scene(vec3(p.x, p.y, p.z + EPSILON)) - scene(vec3(p.x, p.y, p.z - EPSILON))
47 | ));
48 | }
49 |
50 | vec3 phongLight(vec3 diffuse, vec3 specular, float alpha, vec3 p, vec3 eye, vec3 lightPos, vec3 lightIntensity) {
51 | vec3 N = estimateNormal(p);
52 | vec3 L = normalize(lightPos - p);
53 | vec3 V = normalize(eye - p);
54 | vec3 R = normalize(reflect(-L, N));
55 |
56 | float dotLN = dot(L, N);
57 | float dotRV = dot(R, V);
58 |
59 | if (dotLN < 0.0) {
60 | return vec3(0.0, 0.0, 0.0);
61 | }
62 |
63 | if (dotRV < 0.0) {
64 | return lightIntensity * (diffuse * dotLN);
65 | }
66 | return lightIntensity * (diffuse * dotLN + specular * pow(dotRV, alpha));
67 | }
68 |
69 | vec3 phongIllumination(vec3 ambient, vec3 diffuse, vec3 specular, float alpha, vec3 p, vec3 eye) {
70 | const vec3 ambientLight = 0.5 * vec3(1.0, 1.0, 1.0);
71 | vec3 color = ambientLight * ambient;
72 | vec3 light1Pos = vec3(4.0, 5.0, 20.0);
73 | vec3 light1Intensity = vec3(0.4, 0.4, 0.4);
74 | color += phongLight(diffuse, specular, alpha, p, eye, light1Pos, light1Intensity);
75 | return color;
76 | }
77 |
78 | mat4 viewMatrix(vec3 eye, vec3 center, vec3 up) {
79 | vec3 f = normalize(center - eye);
80 | vec3 s = normalize(cross(f, up));
81 | vec3 u = cross(s, f);
82 | return mat4(
83 | vec4(s, 0.0),
84 | vec4(u, 0.0),
85 | vec4(-f, 0.0),
86 | vec4(0.0, 0.0, 0.0, 1)
87 | );
88 | }
89 |
90 | void main() {
91 | vec3 dir = rayDirection(50.0, resolution.xy, gl_FragCoord.xy);
92 | vec3 eye = vec3(0.0, 0.0, 10.0);
93 | mat4 viewToWorld = viewMatrix(eye, vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
94 | vec3 worldDir = (viewToWorld * vec4(dir, 0.0)).xyz;
95 | float dist = raymarch(eye, dir, MIN_DISTANCE, MAX_DISTANCE);
96 |
97 | if (dist > MAX_DISTANCE - EPSILON) {
98 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
99 | return;
100 | }
101 |
102 | vec3 p = eye + dist * dir;
103 | vec3 ambient = vec3(sin(time), 0.9, cos(time));
104 | vec3 diffuse = vec3(0.0, 0.6, 0.8);
105 | vec3 specular = vec3(1.0, 1.0, 1.0);
106 | float shininess = 10.0;
107 |
108 | vec3 color = phongIllumination(ambient, diffuse, specular, shininess, p, eye);
109 |
110 | gl_FragColor = vec4(color, 1.0);
111 | }
--------------------------------------------------------------------------------
/docs/glsl-just-a-cube/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/docs/glsl-kaleidoscope/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform float time;
6 | uniform vec2 resolution;
7 |
8 | const int MAX_MARCHING_STEPS = 255;
9 | const float MIN_DIST = 0.0;
10 | const float MAX_DIST = 50.0;
11 | const float EPSILON = 0.0001;
12 |
13 | float box(vec3 p, vec3 b, float r, vec3 c) {
14 | vec3 q = mod(p, c) - 0.5 * c;
15 | return length(max(abs(q) - b, 0.0)) - r;
16 | }
17 |
18 | float sphere(vec3 p, float s, vec3 c) {
19 | vec3 q = mod(p, c) - 0.5 * c;
20 | return length(q) - s;
21 | }
22 |
23 | float boxMinusSphere(float d1, float d2) {
24 | return max(-d1, d2);
25 | }
26 |
27 | float scene(vec3 p) {
28 | vec3 c = vec3(5.0, 5.0, 5.0);
29 | float b = box(p, vec3(0.4, 0.4, 0.4), 0.1, c);
30 | float s = sphere(p, abs(cos(time)) * 0.19 + 0.6, c);
31 | float u = boxMinusSphere(s, b);
32 | return u;
33 | }
34 |
35 | float distanceCalc(vec3 eye, vec3 direction, float start, float end) {
36 | float depth = start;
37 | for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
38 | float dist = scene(eye + depth * direction);
39 | if (dist < EPSILON) {
40 | return depth;
41 | }
42 | depth += dist;
43 | if (depth >= end) {
44 | return end;
45 | }
46 | }
47 | return end;
48 | }
49 |
50 | vec3 rayDirection(float fov, vec2 size, vec2 fragCoord) {
51 | vec2 xy = fragCoord - size / 2.0;
52 | float z = size.y / tan(radians(fov) / 2.0);
53 | return normalize(vec3(xy, -z));
54 | }
55 |
56 | vec3 estimateNormal(vec3 p) {
57 | return normalize(vec3(
58 | scene(vec3(p.x + EPSILON, p.y, p.z)) - scene(vec3(p.x - EPSILON, p.y, p.z)),
59 | scene(vec3(p.x, p.y + EPSILON, p.z)) - scene(vec3(p.x, p.y - EPSILON, p.z)),
60 | scene(vec3(p.x, p.y, p.z + EPSILON)) - scene(vec3(p.x, p.y, p.z - EPSILON))
61 | ));
62 | }
63 |
64 | mat4 viewMatrix(vec3 eye, vec3 center, vec3 up) {
65 | vec3 f = normalize(center - eye);
66 | vec3 s = normalize(cross(f, up));
67 | vec3 u = cross(s, f);
68 | return mat4(
69 | vec4(s, 0.0),
70 | vec4(u, 0.0),
71 | vec4(-f, 0.0),
72 | vec4(0.0, 0.0, 0.0, 1)
73 | );
74 | }
75 |
76 | void main() {
77 | vec3 dir = rayDirection(45.0, resolution.xy, gl_FragCoord.xy);
78 | vec3 eye = vec3(sin(time / 5.0), abs(sin(time / 5.0)), sin(time / 5.0));
79 |
80 | mat4 viewToWorld = viewMatrix(eye, vec3(0.0, 0.0, 0.0), vec3(0.0, sin(time), cos(time)));
81 | vec3 worldDir = (viewToWorld * vec4(dir, 0.0)).xyz;
82 | float dist = distanceCalc(eye, worldDir, MIN_DIST, MAX_DIST);
83 |
84 | if (dist > MAX_DIST - EPSILON) {
85 | gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
86 | return;
87 | }
88 |
89 | vec3 p = abs(eye + (sin(time / 2.0) * dist) * dir);
90 | vec3 color = estimateNormal(p);
91 |
92 | gl_FragColor = vec4(color, 1.0);
93 | }
--------------------------------------------------------------------------------
/docs/glsl-kaleidoscope/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/docs/glsl-mandelbrot/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform vec2 resolution;
6 |
7 | const int ITER = 15;
8 | float SCALE = (resolution.x / resolution.y) * 0.002;
9 |
10 | float brot(vec2 c) {
11 | vec2 z = c;
12 | for (int i = 0; i < ITER; i++) {
13 | float x = (z.x * z.x - z.y * z.y) + c.x;
14 | float y = (z.y * z.x + z.x * z.y) + c.y;
15 |
16 | if ((x * x + y * y) > 40.0) return 0.0;
17 | z.x = x;
18 | z.y = y;
19 |
20 | }
21 | return abs(z.x) + abs(z.y);
22 | }
23 |
24 | void main() {
25 | vec2 z, c;
26 |
27 | c.x = (gl_FragCoord.x) * SCALE - resolution.x * (SCALE / 2.0) - 0.7;
28 | c.y = (gl_FragCoord.y) * SCALE - resolution.y * (SCALE / 2.0);
29 |
30 | vec3 color = vec3(0.03, brot(c) * 0.75, brot(c) * 0.7);
31 | gl_FragColor = vec4(color, 1.0);
32 | }
--------------------------------------------------------------------------------
/docs/glsl-mandelbrot/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/docs/glsl-random-lines/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | // https://thebookofshaders.com/10/
6 |
7 | uniform vec2 resolution;
8 | uniform float time;
9 |
10 | float random(vec2 s, float t) {
11 | return fract(sin(dot(s.xy, vec2(0.0, t))) * (time + 10.0));
12 | }
13 |
14 | void main() {
15 | vec2 s = gl_FragCoord.xy / resolution;
16 | gl_FragColor = vec4(vec3(random(s, 83.8), random(s, 14.3), random(s, 94.6)), 1.0);
17 | }
18 |
--------------------------------------------------------------------------------
/docs/glsl-random-lines/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
6 |
--------------------------------------------------------------------------------
/docs/glsl-spinning-color-wheel/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | // Inspiration from The Book of Shaders.
6 | // https://thebookofshaders.com/06/
7 |
8 | #define TWO_PI 6.28318530718
9 |
10 | uniform vec2 resolution;
11 | uniform float time;
12 |
13 | // https://www.shadertoy.com/view/MsS3Wc
14 | vec3 hsb2rgb(vec3 c) {
15 | vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0,
16 | 0.0,
17 | 1.0
18 | );
19 | rgb = rgb * rgb * (3.0 - 2.0 * rgb);
20 | return c.z * mix(vec3(1.0), rgb, c.y);
21 | }
22 |
23 | float circle(vec2 s, float rad) {
24 | // This calculation will make an oval at full screen size,
25 | // so I'm cheating and making the canvas a perfect square.
26 | vec2 dist = s - vec2(0.5);
27 | return 1.0 - smoothstep(rad - (rad * 0.01), rad + (rad * 0.01), dot(dist, dist) * 8.0);
28 | }
29 |
30 | void main() {
31 | // Screen dimensions and set the base color.
32 | vec2 s = gl_FragCoord.xy / resolution;
33 | vec3 color = vec3(0.0);
34 |
35 | // Calculate the color part of the wheel.
36 | vec2 toCenter = vec2(0.5) - s;
37 | float angle = atan(toCenter.y, toCenter.x);
38 | float radius = length(toCenter) * 4.0;
39 | // Adding the time variable to the angle calc makes the spinning happen.
40 | vec3 colors = hsb2rgb(vec3((angle / TWO_PI) + time, radius, 1.0));
41 |
42 | // Create the inner and outer circles.
43 | vec3 outerCircle = vec3(circle(s, 0.5));
44 | vec3 innerCircle = vec3(circle(s, 0.2));
45 |
46 | // Color the circle with the rgb colors, then subtract the inner circle.
47 | color = outerCircle * colors - innerCircle;
48 |
49 | // Output.
50 | gl_FragColor = vec4(color, 1.0);
51 | }
52 |
--------------------------------------------------------------------------------
/docs/glsl-spinning-color-wheel/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/docs/glsl-squishydonutspin/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | uniform float time;
6 | uniform vec2 resolution;
7 |
8 | const int MAX_MARCHING_STEPS = 1000;
9 | const float MIN_DISTANCE = 0.0;
10 | const float MAX_DISTANCE = 100.0;
11 | const float EPSILON = 0.0001;
12 |
13 | // Distance functions from http://iquilezles.org/www/articles/distfunctions/distfunctions.htm
14 | float tor(vec3 p, vec2 t) {
15 | vec2 q = vec2(length(p.xz) - t.x, p.y);
16 | return length(q) - t.y;
17 | }
18 |
19 | vec3 twist(vec3 p) {
20 | float c = cos(sin(time * 2.0) * 6.0 * p.y);
21 | float s = sin(sin(time * 2.0) * 6.0 * p.y);
22 | mat2 m = mat2(c, -s, s, c);
23 | return vec3(m * p.xz, p.y);
24 | }
25 |
26 | // Rotation matrix function from http://www.neilmendoza.com/glsl-rotation-about-an-arbitrary-axis/
27 | mat4 rotationMatrix(vec3 axis, float angle) {
28 | vec3 a = normalize(axis);
29 | float s = sin(angle) * -1.0;
30 | float c = cos(angle);
31 | float oc = 1.0 - c;
32 | return mat4(
33 | oc * a.x * a.x + c, oc * a.x * a.y - a.z * s, oc * a.z * a.x + a.y * s, 0.0,
34 | oc * a.x * a.y + a.z * s, oc * a.y * a.y + c, oc * a.y * a.z - a.x * s, 0.0,
35 | oc * a.z * a.x - a.y * s, oc * a.y * a.z + a.x * s, oc * a.z * a.z + c, 0.0,
36 | 0.0, 0.0, 0.0, 0.0
37 | );
38 | }
39 |
40 | vec3 rotator(vec3 p, mat4 m) {
41 | vec3 q = (m * vec4(p, 0.0)).xyz;
42 | return q;
43 | }
44 |
45 | float scene(vec3 p) {
46 | vec3 t = twist(p);
47 | mat4 matrix = rotationMatrix(vec3(0.0, 1.0, 1.0), time);
48 | vec3 r = rotator(t, matrix);
49 | return tor(r, vec2(0.6, 0.3));
50 | }
51 |
52 | // Raymarching and phong lighting equations from https://www.shadertoy.com/view/lt33z7
53 | float raymarch(vec3 eye, vec3 direction, float start, float end) {
54 | float depth = start;
55 | for (int i = 0; i < MAX_MARCHING_STEPS; i++) {
56 | float dist = scene(eye + depth * direction);
57 | if (dist < EPSILON) {
58 | return depth;
59 | }
60 | depth += dist;
61 | if (depth >= end) {
62 | return end;
63 | }
64 | }
65 | return end;
66 | }
67 |
68 | vec3 rayDirection(float fov, vec2 size, vec2 fragCoord) {
69 | vec2 xy = fragCoord - size / 2.0;
70 | float z = size.y / tan(radians(fov) / 2.0);
71 | return normalize(vec3(xy, -z));
72 | }
73 |
74 | vec3 estimateNormal(vec3 p) {
75 | return normalize(vec3(
76 | scene(vec3(p.x + EPSILON, p.y, p.z)) - scene(vec3(p.x - EPSILON, p.y, p.z)),
77 | scene(vec3(p.x, p.y + EPSILON, p.z)) - scene(vec3(p.x, p.y - EPSILON, p.z)),
78 | scene(vec3(p.x, p.y, p.z + EPSILON)) - scene(vec3(p.x, p.y, p.z - EPSILON))
79 | ));
80 | }
81 |
82 | vec3 phongLight(vec3 diffuse, vec3 specular, float alpha, vec3 p, vec3 eye, vec3 lightPos, vec3 lightIntensity) {
83 | vec3 N = estimateNormal(p);
84 | vec3 L = normalize(lightPos - p);
85 | vec3 V = normalize(eye - p);
86 | vec3 R = normalize(reflect(-L, N));
87 |
88 | float dotLN = dot(L, N);
89 | float dotRV = dot(R, V);
90 |
91 | if (dotLN < 0.0) {
92 | return vec3(0.0, 0.0, 0.0);
93 | }
94 |
95 | if (dotRV < 0.0) {
96 | return lightIntensity * (diffuse * dotLN);
97 | }
98 | return lightIntensity * (diffuse * dotLN + specular * pow(dotRV, alpha));
99 | }
100 |
101 | vec3 phongIllumination(vec3 ambient, vec3 diffuse, vec3 specular, float alpha, vec3 p, vec3 eye) {
102 | const vec3 ambientLight = 0.5 * vec3(1.0, 1.0, 1.0);
103 | vec3 color = ambientLight * ambient;
104 | vec3 light1Pos = vec3(4.0, 5.0, 5.0);
105 | vec3 light1Intensity = vec3(0.4, 0.4, 0.4);
106 | color += phongLight(diffuse, specular, alpha, p, eye, light1Pos, light1Intensity);
107 | return color;
108 | }
109 |
110 | mat4 viewMatrix(vec3 eye, vec3 center, vec3 up) {
111 | vec3 f = normalize(center - eye);
112 | vec3 s = normalize(cross(f, up));
113 | vec3 u = cross(s, f);
114 | return mat4(
115 | vec4(s, 0.0),
116 | vec4(u, 0.0),
117 | vec4(-f, 0.0),
118 | vec4(0.0, 0.0, 0.0, 1)
119 | );
120 | }
121 |
122 | void main() {
123 | vec3 dir = rayDirection(50.0, resolution.xy, gl_FragCoord.xy);
124 | vec3 eye = vec3(0.0, 0.0, 5.0);
125 | mat4 viewToWorld = viewMatrix(eye, vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
126 | vec3 worldDir = (viewToWorld * vec4(dir, 0.0)).xyz;
127 | float dist = raymarch(eye, dir, MIN_DISTANCE, MAX_DISTANCE);
128 |
129 | if (dist > MAX_DISTANCE - EPSILON) {
130 | gl_FragColor = vec4(0.1, 0.1, 0.1, 1.0);
131 | return;
132 | }
133 |
134 | vec3 p = eye + dist * dir;
135 | vec3 ambient = vec3(0.5, 0.6, 0.8);
136 | vec3 diffuse = vec3(0.5, 0.6, 0.8);
137 | vec3 specular = vec3(1.0, 1.0, 1.0);
138 | float shininess = 10.0;
139 |
140 | vec3 color = phongIllumination(ambient, diffuse, specular, shininess, p, eye);
141 |
142 | gl_FragColor = vec4(color, 1.0);
143 | }
--------------------------------------------------------------------------------
/docs/glsl-squishydonutspin/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/docs/glsl-very-basic/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | // This is the canvas width and height from boilerplate.js.
6 | uniform vec2 resolution;
7 |
8 | void main() {
9 | // Get the coordinates of each fragment shader pixel.
10 | vec2 s = gl_FragCoord.xy / resolution;
11 |
12 | vec3 color;
13 | if (gl_FragCoord.x < resolution.x / 2.0) {
14 | // Half the screen gets one horizontal gradient...
15 | color = vec3(1.0 * s.y, 0.7, 0.3);
16 | } else {
17 | // ...The other half of the screen gets another.
18 | color = vec3(0.3, 0.75 * s.y, 0.7);
19 | }
20 |
21 | // Put the colors on the screen.
22 | gl_FragColor = vec4(color, 1.0);
23 | }
24 |
--------------------------------------------------------------------------------
/docs/glsl-very-basic/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
6 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Shader Art
6 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/docs/threejs-icosphere-explode/scene.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | let camera, scene, renderer, controls;
3 | let material, mesh, materialShader;
4 | let particleMaterial, particleMaterialShader;
5 |
6 | init();
7 | animate();
8 |
9 | function init() {
10 |
11 | camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.01, 10 );
12 | camera.position.set(-1, 0, 1);
13 |
14 | scene = new THREE.Scene();
15 |
16 | var light = new THREE.PointLight( 0xffffff, 1, 10 );
17 | light.position.set(0, 1, 1)
18 | camera.add(light);
19 | scene.add(camera);
20 |
21 | var ambient = new THREE.AmbientLight(0xffffff, 0.5);
22 | scene.add(ambient);
23 |
24 | createOuterIco();
25 | createSparkles();
26 |
27 | renderer = new THREE.WebGLRenderer( { antialias: true } );
28 | renderer.setSize( window.innerWidth, window.innerHeight );
29 | let appendLocation = document.getElementById('threejs');
30 | appendLocation.appendChild( renderer.domElement );
31 |
32 | controls = new THREE.OrbitControls(camera, renderer.domElement);
33 | controls.autoRotate = true;
34 | controls.enableDamping = true;
35 |
36 | }
37 |
38 | function createOuterIco() {
39 | let geometry = new THREE.IcosahedronBufferGeometry( 0.5, 0 );
40 | material = new THREE.MeshPhongMaterial();
41 | material.color.setRGB(0.7, 0.9, 0.95);
42 | material.opacity = 1.0;
43 | material.transparent = true;
44 | material.side = THREE.DoubleSide;
45 |
46 | let position = geometry.attributes.position;
47 | let numfaces = position.count / 3;
48 | let displacement = new Float32Array(position.count * 3);
49 |
50 | for (let f = 0; f < numfaces; f++) {
51 | let index = 9*f;
52 |
53 | for (let i = 0; i < 3; i++) {
54 | displacement[index + (3 * i)] = 1.0;
55 | displacement[index + (3 * i) + 1] = 1.0;
56 | displacement[index + (3 * i) + 2] = 1.0;
57 | }
58 | }
59 |
60 | geometry.setAttribute('displacement', new THREE.BufferAttribute(displacement, 3));
61 |
62 | material.onBeforeCompile = (shader) => {
63 | const token = '#include ';
64 | shader.uniforms.amount = {value: 0.0};
65 | shader.vertexShader = `
66 | attribute vec3 displacement;
67 | uniform float amount;
68 | ` + shader.vertexShader;
69 |
70 | let shaderText = `
71 | vec3 newPosition = position + normal * displacement * amount;
72 | gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
73 | `;
74 |
75 | shader.vertexShader = shader.vertexShader.replace(token, shaderText);
76 | materialShader = shader;
77 | };
78 |
79 | mesh = new THREE.Mesh( geometry, material );
80 | scene.add( mesh );
81 | }
82 |
83 | function createSparkles() {
84 | let particles = new THREE.IcosahedronBufferGeometry(0.01, 4);
85 | particleMaterial = new THREE.PointsMaterial();
86 | let particleSystem = new THREE.Points(particles, particleMaterial);
87 |
88 | let position = particles.attributes.position;
89 | let numfaces = position.count / 3;
90 | let displacement = new Float32Array(position.count * 3);
91 |
92 |
93 | for (let f = 0; f < numfaces; f++) {
94 | let index = 9*f;
95 |
96 | for (let i = 0; i < 3; i++) {
97 | displacement[index + (3 * i)] = 1.0;
98 | displacement[index + (3 * i) + 1] = 1.0;
99 | displacement[index + (3 * i) + 2] = 1.0;
100 | }
101 | }
102 | particles.setAttribute('displacement', new THREE.BufferAttribute(displacement, 3, true));
103 |
104 | particleMaterial.onBeforeCompile = (shader) => {
105 | const token = '#include ';
106 | shader.uniforms.amount = {value: 0.0};
107 | shader.uniforms.u_resolution = {type: 'v2', value: new THREE.Vector2(renderer.domElement.width, renderer.domElement.height)};
108 | shader.vertexShader = `
109 | attribute vec3 displacement;
110 | uniform float amount;
111 | ` + shader.vertexShader;
112 |
113 | let shaderText = `
114 | gl_PointSize = 2.0;
115 | vec3 newPosition = position + normal * displacement * amount;
116 | gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
117 | `;
118 |
119 | shader.vertexShader = shader.vertexShader.replace(token, shaderText);
120 |
121 | shader.fragmentShader = `
122 | uniform vec2 u_resolution;
123 | ` + shader.fragmentShader;
124 |
125 | let fragShaderNoise = `
126 | float random (in vec2 st) {
127 | return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123);
128 | }
129 | float noise (in vec2 st) {
130 | vec2 i = floor(st);
131 | vec2 f = fract(st);
132 |
133 | // Four corners in 2D of a tile
134 | float a = random(i);
135 | float b = random(i + vec2(1.0, 0.0));
136 | float c = random(i + vec2(0.0, 1.0));
137 | float d = random(i + vec2(1.0, 1.0));
138 |
139 | // Smooth Interpolation
140 |
141 | // Cubic Hermine Curve. Same as SmoothStep()
142 | vec2 u = f*f*(3.0-2.0*f);
143 | // u = smoothstep(0.,1.,f);
144 |
145 | // Mix 4 corners percentages
146 | return mix(a, b, u.x) +
147 | (c - a)* u.y * (1.0 - u.x) +
148 | (d - b) * u.x * u.y;
149 | }
150 | `;
151 | let fragShaderMain = `
152 | vec2 st = gl_FragCoord.xy;
153 |
154 | // Scale the coordinate system to see
155 | // some noise in action
156 | vec2 pos = vec2(st / 8.0);
157 |
158 | // Use the noise function
159 | float n = noise(pos);
160 |
161 | gl_FragColor = vec4(vec3(n), 1.0);
162 | `;
163 | shader.fragmentShader = shader.fragmentShader.replace('#include ', fragShaderNoise);
164 | shader.fragmentShader = shader.fragmentShader.replace('#include ', fragShaderMain);
165 | particleMaterialShader = shader;
166 | };
167 |
168 | scene.add(particleSystem);
169 | }
170 |
171 | function animate() {
172 |
173 | requestAnimationFrame( animate );
174 |
175 | if (materialShader) {
176 | materialShader.uniforms.amount.value = Math.abs((controls.getPolarAngle() - Math.PI / 2)) * 0.2;
177 | material.opacity = 1.0 - Math.abs((controls.getPolarAngle() - Math.PI / 2)) * 0.1;
178 | }
179 | if (particleMaterialShader) {
180 | particleMaterialShader.uniforms.amount.value = Math.abs((controls.getPolarAngle() - Math.PI / 2)) * 0.8;
181 | }
182 |
183 | controls.update();
184 |
185 | renderer.render( scene, camera );
186 |
187 | }
188 | })()
189 |
--------------------------------------------------------------------------------
/fonts/fa-brands-400.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/captainpainway/shader-art/f554420e3f5443b6307afd81016fbb1eb5042666/fonts/fa-brands-400.ttf
--------------------------------------------------------------------------------
/fonts/fa-brands.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * Font Awesome Free 5.0.2 by @fontawesome - http://fontawesome.com
3 | * License - http://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 | */
5 | @font-face {
6 | font-family: 'Font Awesome 5 Brands';
7 | font-style: normal;
8 | font-weight: normal;
9 | src: url("./fa-brands-400.ttf");
10 | }
11 |
12 | .fab {
13 | font-family: 'Font Awesome 5 Brands'; }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "shader-art",
3 | "version": "1.0.0",
4 | "main": "src/index.js",
5 | "scripts": {
6 | "clean": "rm dist/bundle.js",
7 | "build-dev": "webpack -d --mode development",
8 | "build-prod": "webpack -p --mode production",
9 | "start": "webpack-dev-server --mode development"
10 | },
11 | "repository": "https://github.com/captainpainway/shader-art.git",
12 | "author": "Mary Knize ",
13 | "license": "MIT",
14 | "dependencies": {
15 | "react": "^16.12.0",
16 | "react-dom": "^16.12.0"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.7.2",
20 | "@babel/plugin-transform-runtime": "^7.6.2",
21 | "@babel/preset-env": "^7.7.1",
22 | "@babel/preset-react": "^7.7.0",
23 | "@babel/runtime": "^7.7.2",
24 | "@hot-loader/react-dom": "^16.11.0",
25 | "babel-loader": "^8.0.6",
26 | "css-loader": "^3.2.0",
27 | "style-loader": "^1.0.0",
28 | "webpack": "^4.41.2",
29 | "webpack-cli": "^3.3.10",
30 | "webpack-dev-server": "^3.9.0"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/spinning-color-wheel/fragmentshader.glsl:
--------------------------------------------------------------------------------
1 | #ifdef GL_ES
2 | precision highp float;
3 | #endif
4 |
5 | // Inspiration from The Book of Shaders.
6 | // https://thebookofshaders.com/06/
7 |
8 | #define TWO_PI 6.28318530718
9 |
10 | uniform vec2 resolution;
11 | uniform float time;
12 |
13 | // https://www.shadertoy.com/view/MsS3Wc
14 | vec3 hsb2rgb(vec3 c) {
15 | vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0,
16 | 0.0,
17 | 1.0
18 | );
19 | rgb = rgb * rgb * (3.0 - 2.0 * rgb);
20 | return c.z * mix(vec3(1.0), rgb, c.y);
21 | }
22 |
23 | float circle(vec2 s, float rad) {
24 | // This calculation will make an oval at full screen size,
25 | // so I'm cheating and making the canvas a perfect square.
26 | vec2 dist = s - vec2(0.5);
27 | return 1.0 - smoothstep(rad - (rad * 0.01), rad + (rad * 0.01), dot(dist, dist) * 8.0);
28 | }
29 |
30 | void main() {
31 | // Screen dimensions and set the base color.
32 | vec2 s = gl_FragCoord.xy / resolution;
33 | vec3 color = vec3(0.0);
34 |
35 | // Calculate the color part of the wheel.
36 | vec2 toCenter = vec2(0.5) - s;
37 | float angle = atan(toCenter.y, toCenter.x);
38 | float radius = length(toCenter) * 4.0;
39 | // Adding the time variable to the angle calc makes the spinning happen.
40 | vec3 colors = hsb2rgb(vec3((angle / TWO_PI) + time, radius, 1.0));
41 |
42 | // Create the inner and outer circles.
43 | vec3 outerCircle = vec3(circle(s, 0.5));
44 | vec3 innerCircle = vec3(circle(s, 0.2));
45 |
46 | // Color the circle with the rgb colors, then subtract the inner circle.
47 | color = outerCircle * colors - innerCircle;
48 |
49 | // Output.
50 | gl_FragColor = vec4(color, 1.0);
51 | }
52 |
--------------------------------------------------------------------------------
/spinning-color-wheel/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Spinning Color Wheel -- GLSL Shader Art -- Mary Knize
6 |
7 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/spinning-color-wheel/vertexshader.glsl:
--------------------------------------------------------------------------------
1 | attribute vec3 position;
2 |
3 | void main() {
4 | gl_Position = vec4(position, 1.0);
5 | }
--------------------------------------------------------------------------------
/src/app.css:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #222222;
3 | }
4 |
5 | .button {
6 | position: absolute;
7 | width: 40px;
8 | height: 40px;
9 | font-size: 32px;
10 | text-align: center;
11 | padding: 5px;
12 | box-sizing: border-box;
13 | cursor: pointer;
14 | }
15 |
16 | .select {
17 | background-color: #222;
18 | height: 100%;
19 | padding: 20px 0;
20 | box-sizing: border-box;
21 | font-family: sans-serif;
22 | -webkit-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75);
23 | -moz-box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75);
24 | box-shadow: 0px 0px 5px 0px rgba(0,0,0,0.75);
25 | }
26 |
--------------------------------------------------------------------------------
/src/app.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import Shader from './shader';
3 | import {Select} from './select';
4 | import {Three} from './threejs';
5 | import './app.css';
6 |
7 | class App extends React.Component {
8 | constructor(props) {
9 | super(props);
10 | this.shaders = [
11 | 'glsl-squishydonutspin',
12 | 'glsl-bumpy-sphere',
13 | 'glsl-kaleidoscope',
14 | 'glsl-mandelbrot',
15 | 'glsl-juliafractal',
16 | 'glsl-cubes',
17 | 'glsl-random-lines',
18 | 'glsl-spinning-color-wheel',
19 | 'glsl-just-a-cube',
20 | 'glsl-very-basic',
21 | 'threejs-icosphere-explode',
22 | ];
23 | this.state = {
24 | shader: this.shaders[0],
25 | openpopin: null
26 | };
27 | this.selectShader = this.selectShader.bind(this);
28 | this.vertex_shader = null;
29 | this.fragment_shader = null;
30 | }
31 |
32 | selectShader(value) {
33 | this.setState({shader: value});
34 | }
35 |
36 | render() {
37 | if (document.getElementById('three_script')) {
38 | document.body.removeChild(document.getElementById('three_script'));
39 | }
40 | let canvas;
41 | if (this.state.shader.search(/^threejs/) !== -1) {
42 | canvas =
43 | } else {
44 | canvas =
45 | }
46 | return (
47 |
48 |
51 | )
52 | }
53 | }
54 |
55 | export default App;
56 |
--------------------------------------------------------------------------------
/src/button.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const Button = (props) => {
4 | const toggleClosed = () => {
5 | const newclass = (status) => {
6 | return status === 'closed' ? '' : 'closed';
7 | };
8 | props.onToggle(newclass);
9 | };
10 |
11 | return (
12 |
13 | {props.children}
14 |
15 | )
16 | };
--------------------------------------------------------------------------------
/src/code.css:
--------------------------------------------------------------------------------
1 | .code {
2 | position: fixed;
3 | top: 0;
4 | left: 0;
5 | color: white;
6 | width: 500px;
7 | height: 100%;
8 | transition: all;
9 | transition-duration: 500ms;
10 | }
11 |
12 | .code.closed {
13 | transform: translateX(-505px);
14 | }
15 |
--------------------------------------------------------------------------------
/src/code.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Button} from './button';
3 | import './code.css';
4 | import {Codebutton} from "./codebutton";
5 |
6 | export const Code = (props) => {
7 | const [toggle, setToggle] = useState('closed');
8 |
9 | return (
10 |
11 |
14 |
15 |
{props.vertex}
16 |
{props.fragment}
17 |
18 |
19 | )
20 | };
21 |
--------------------------------------------------------------------------------
/src/codebutton.css:
--------------------------------------------------------------------------------
1 | .code1, .code2, .code3 {
2 | position: absolute;
3 | left: 10px;
4 | background-color: white;
5 | width: 30px;
6 | height: 5px;
7 | -webkit-border-radius: 5px;
8 | -moz-border-radius: 5px;
9 | border-radius: 5px;
10 | transition: all 600ms;
11 | }
12 |
13 | .code1.closed {
14 | top: 5px;
15 | transform: rotateZ(0deg);
16 | }
17 |
18 | .code2.closed {
19 | opacity: 100;
20 | }
21 |
22 | .code3.closed {
23 | top: 25px;
24 | transform: rotateZ(0deg);
25 | }
26 |
27 | .code1 {
28 | top: 15px;
29 | transform: rotateZ(45deg);
30 | }
31 |
32 | .code2 {
33 | top: 15px;
34 | opacity: 0;
35 | }
36 |
37 | .code3 {
38 | top: 15px;
39 | transform: rotateZ(-45deg);
40 | }
41 |
--------------------------------------------------------------------------------
/src/codebutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './codebutton.css';
3 |
4 | export const Codebutton = (props) => {
5 | return (
6 |
11 | )
12 | };
13 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDom from 'react-dom';
3 | import App from './app';
4 |
5 | ReactDom.render(, document.getElementById('app'));
6 |
--------------------------------------------------------------------------------
/src/menubutton.css:
--------------------------------------------------------------------------------
1 | .ham1, .ham2, .ham3 {
2 | position: absolute;
3 | left: 10px;
4 | background-color: white;
5 | width: 30px;
6 | height: 5px;
7 | -webkit-border-radius: 5px;
8 | -moz-border-radius: 5px;
9 | border-radius: 5px;
10 | transition: all 600ms;
11 | }
12 |
13 | .ham1.closed {
14 | top: 5px;
15 | transform: rotateZ(0deg);
16 | }
17 |
18 | .ham2.closed {
19 | opacity: 100;
20 | }
21 |
22 | .ham3.closed {
23 | top: 25px;
24 | transform: rotateZ(0deg);
25 | }
26 |
27 | .ham1 {
28 | top: 15px;
29 | transform: rotateZ(45deg);
30 | }
31 |
32 | .ham2 {
33 | top: 15px;
34 | opacity: 0;
35 | }
36 |
37 | .ham3 {
38 | top: 15px;
39 | transform: rotateZ(-45deg);
40 | }
41 |
--------------------------------------------------------------------------------
/src/menubutton.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './menubutton.css';
3 |
4 | export const Menubutton = (props) => {
5 | return (
6 |
11 | )
12 | };
13 |
--------------------------------------------------------------------------------
/src/select.css:
--------------------------------------------------------------------------------
1 | .menu {
2 | position: fixed;
3 | top: 0;
4 | left: 0;
5 | color: white;
6 | width: 300px;
7 | height: 100%;
8 | transition: all;
9 | transition-duration: 300ms;
10 | }
11 |
12 | .menu.closed {
13 | transform: translateX(-305px);
14 | }
15 |
16 | h1 {
17 | padding-left: 30px;
18 | }
19 |
20 | ul {
21 | margin: 0;
22 | padding: 0;
23 | list-style: none;
24 | }
25 |
26 | li {
27 | padding: 10px 20px 10px 30px;
28 | box-sizing: border-box;
29 | cursor: pointer;
30 | transition: all 500ms;
31 | }
32 |
33 | li:hover {
34 | background-color: #1f3c52;
35 | }
36 |
--------------------------------------------------------------------------------
/src/select.js:
--------------------------------------------------------------------------------
1 | import React, {useState} from 'react';
2 | import {Button} from './button';
3 | import './select.css';
4 | import {Menubutton} from "./menubutton";
5 |
6 | export const Select = (props) => {
7 | const [toggle, setToggle] = useState('closed');
8 | const [shader, setShader] = useState(props.shaderlist[0]);
9 |
10 | const newShader = (ele) => {
11 | setShader(ele);
12 | setToggle(!toggle ? 'closed': '');
13 | props.onSelect(ele);
14 | };
15 |
16 | const list = props.shaderlist.map(item => newShader(item)}>{item})
17 |
18 | return (
19 |
20 |
23 |
27 |
28 | )
29 | };
30 |
31 |
--------------------------------------------------------------------------------
/src/shader.css:
--------------------------------------------------------------------------------
1 | canvas {
2 | width: 100%;
3 | height: 100%;
4 | }
--------------------------------------------------------------------------------
/src/shader.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './shader.css';
3 |
4 | class Shader extends React.Component {
5 | constructor(props) {
6 | super(props);
7 | this.canvas = document.createElement('canvas');
8 | this.gl = null;
9 | this.currentProgram = null;
10 | this.timeLocation = null;
11 | this.resolutionLocation = null;
12 | this.buffer = null;
13 | this.parameters = {
14 | start_time: new Date().getTime(),
15 | screenWidth: 0,
16 | screenHeight: 0
17 | };
18 | this.state = {
19 | time: 0,
20 | vertex_shader: this.props.vertex,
21 | fragment_shader: this.props.fragment
22 | };
23 | this.animationFrame = null;
24 | }
25 |
26 | async componentDidMount() {
27 | await this.startShaders();
28 | await this.animate();
29 | }
30 |
31 | async startShaders() {
32 | const vertex_shader = await this.importShader(this.state.vertex_shader);
33 | const fragment_shader = await this.importShader(this.state.fragment_shader);
34 |
35 | try {
36 | this.gl = this.canvas.getContext('experimental-webgl');
37 | } catch(err) {}
38 |
39 | if(!this.gl) {
40 | throw "Cannot create WebGL context."
41 | }
42 |
43 | this.buffer = this.gl.createBuffer();
44 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
45 | this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0]), this.gl.STATIC_DRAW);
46 |
47 | this.currentProgram = this.createShaderProgram(vertex_shader, fragment_shader);
48 |
49 | this.timeLocation = this.gl.getUniformLocation(this.currentProgram, 'time');
50 | this.resolutionLocation = this.gl.getUniformLocation(this.currentProgram, 'resolution');
51 | }
52 |
53 | componentDidUpdate(prevProps, prevState, snapshot) {
54 | if (this.props != prevProps) {
55 | this.setState({vertex_shader: this.props.vertex});
56 | this.setState({fragment_shader: this.props.fragment});
57 | this.startShaders();
58 | }
59 | this.animationFrame = requestAnimationFrame(this.animate.bind(this));
60 | }
61 |
62 | componentWillUnmount() {
63 | cancelAnimationFrame(this.animationFrame);
64 | }
65 |
66 | async importShader(shader) {
67 | let response = await fetch(shader);
68 | return await response.text();
69 | }
70 |
71 | createShaderProgram(vertex, fragment) {
72 | const program = this.gl.createProgram(),
73 | vert_shader = this.createShader(vertex, this.gl.VERTEX_SHADER),
74 | frag_shader = this.createShader(fragment, this.gl.FRAGMENT_SHADER);
75 |
76 | if (vert_shader === null || frag_shader === null) {
77 | return null;
78 | }
79 |
80 | this.gl.attachShader(program, vert_shader);
81 | this.gl.attachShader(program, frag_shader);
82 |
83 | this.gl.deleteShader(vert_shader);
84 | this.gl.deleteShader(frag_shader);
85 |
86 | this.gl.linkProgram(program);
87 |
88 | return program;
89 | }
90 |
91 | createShader(src, type) {
92 | const shader = this.gl.createShader(type);
93 | this.gl.shaderSource(shader, src);
94 | this.gl.compileShader(shader);
95 |
96 | if(!this.gl.getShaderParameter(shader, this.gl.COMPILE_STATUS)) {
97 | console.log((type == this.gl.VERTEX_SHADER ? "VERTEX" : "FRAGMENT") + " SHADER:\n" + this.gl.getShaderInfoLog(shader));
98 | return null;
99 | }
100 |
101 | return shader;
102 | }
103 |
104 | resize() {
105 | if(this.canvas.width != this.canvas.clientWidth || this.canvas.height != this.canvas.clientHeight) {
106 | this.canvas.width = this.canvas.clientWidth;
107 | this.canvas.height = this.canvas.clientHeight;
108 | this.parameters.screenWidth = this.canvas.width;
109 | this.parameters.screenHeight = this.canvas.height;
110 | this.gl.viewport(0, 0, this.canvas.width, this.canvas.height);
111 | }
112 | }
113 |
114 | animate() {
115 | this.resize();
116 | this.setState({time: new Date().getTime() - this.parameters.start_time});
117 |
118 | this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);
119 | this.gl.useProgram(this.currentProgram);
120 | this.gl.uniform1f(this.timeLocation, this.state.time / 1000);
121 | this.gl.uniform2f(this.resolutionLocation, this.parameters.screenWidth, this.parameters.screenHeight);
122 |
123 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer);
124 | this.gl.vertexAttribPointer(this.vertex_position, 2, this.gl.FLOAT, false, 0, 0);
125 | this.gl.enableVertexAttribArray(this.vertex_position);
126 | this.gl.drawArrays(this.gl.TRIANGLES, 0, 6);
127 | this.gl.disableVertexAttribArray(this.vertex_position);
128 | }
129 |
130 | render() {
131 | return (
132 | {nodeElement && nodeElement.appendChild(this.canvas)}}/>
133 | )
134 | }
135 | }
136 |
137 | export default Shader;
138 |
--------------------------------------------------------------------------------
/src/threejs.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | export const Three = (props) => {
4 |
5 | let script = document.createElement('script');
6 | script.src = `${props.scene}`;
7 | script.id = 'three_script';
8 | document.body.appendChild(script);
9 |
10 | return (
11 |
12 | )
13 | };
14 |
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | overflow: hidden;
5 | }
6 |
7 | canvas {
8 | width: 100vw;
9 | height: 100vh;
10 | }
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const webpack = require('webpack');
2 | const path = require('path');
3 |
4 | const config = {
5 | entry: [
6 | './src/index.js'
7 | ],
8 | output: {
9 | path: path.resolve(__dirname, 'docs'),
10 | filename: 'bundle.js'
11 | },
12 | module: {
13 | rules: [
14 | {
15 | test: /\.(js|jsx)$/,
16 | use: 'babel-loader',
17 | exclude: /node_modules/
18 | },
19 | {
20 | test: /\.css$/,
21 | use: [
22 | 'style-loader',
23 | 'css-loader'
24 | ],
25 | exclude: /node_modules/
26 | }
27 | ]
28 | },
29 | resolve: {
30 | extensions: [
31 | '.js',
32 | '.jsx'
33 | ],
34 | },
35 | devServer: {
36 | contentBase: './docs'
37 | }
38 | };
39 |
40 | module.exports = config;
--------------------------------------------------------------------------------