-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Better shader docs #3154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better shader docs #3154
Changes from 1 commit
c024413
2f67e5a
47e22ec
6a370ca
8d0c325
948b2e2
a387b47
d6ac66a
d0c268d
b2459d1
c3fc4d2
a451ab4
072cda2
50966cd
392f796
9653524
948b3d1
16ae6e5
27c6ddb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -566,6 +566,115 @@ For a more advanced example, [try realtime vertex displacement](https://glitch.c | |
|
|
||
|  | ||
|
|
||
| ## Using a Custom Shader and Component Together | ||
|
|
||
| Let's take the realtime vertex displacement shader example above, and | ||
| add the capability to apply an offset based upon the camera's position. | ||
|
|
||
| We declare that offset as a uniform vec3 value `myOffset`: | ||
|
|
||
| ```js | ||
| AFRAME.registerShader('displacement-offset', { | ||
| schema: { | ||
| time1000: {type:'time', is:'uniform'}, | ||
| myOffset: {type:'vec3', is:'uniform'} | ||
| }, | ||
| vertexShader: vertexShader, | ||
| fragmentShader: fragmentShader | ||
| }); | ||
| ``` | ||
|
|
||
| that the vertex shader uses: | ||
|
|
||
| ```glsl | ||
| // vertex.glsl | ||
|
|
||
| #pragma glslify: pnoise3 = require(glsl-noise/periodic/3d) | ||
|
|
||
| // | ||
| // Based on @thespite's article: | ||
| // | ||
| // "Vertex displacement with a noise function using GLSL and three.js" | ||
| // Source: https://www.clicktorelease.com/blog/vertex-displacement-noise-3d-webgl-glsl-three-js/ | ||
| // | ||
|
|
||
| varying float noise; | ||
| uniform float time1000; | ||
| uniform vec3 myOffset; | ||
|
|
||
| float turbulence( vec3 p ) { | ||
|
|
||
| float w = 100.0; | ||
| float t = -.5; | ||
|
|
||
| for (float f = 1.0 ; f <= 10.0 ; f++ ){ | ||
| float power = pow( 2.0, f ); | ||
| t += abs( pnoise3( vec3( power * p ), vec3( 10.0, 10.0, 10.0 ) ) / power ); | ||
| } | ||
|
|
||
| return t; | ||
|
|
||
| } | ||
|
|
||
| void main() { | ||
| float time = time1000 / 1000.0; | ||
| noise = 10.0 * -.10 * turbulence( .5 * normal + time / 3.0 ); | ||
| float b = 5.0 * pnoise3( 0.05 * position, vec3( 100.0 ) ); | ||
| float displacement = (- 10. * noise + b) / 50.0; | ||
|
|
||
| vec3 newPosition = position + normal * displacement + myOffset; | ||
| gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 ); | ||
| } | ||
| ``` | ||
|
|
||
| So how do we update `myOffset` to be the camera position, | ||
| so the vertex shader behaves correctly? | ||
|
|
||
| The typical method to do this in A-Frame is to create a custom component | ||
| with the desired functionality, and attach it to the appropriate entity. | ||
|
|
||
| Note that the shader property is exposed via the `material` component, | ||
| so we modify the single property of interest using a form of `setAttribute()`. | ||
|
|
||
| As best practice to avoid creating garbage for performance reasons: | ||
| - we do not use the form of `setAttribute` that takes an object as second argument. | ||
| - we create a component property to hold the offset, to avoid creating a new THREE.Vector3 every tick. | ||
|
|
||
| ```js | ||
| AFRAME.registerComponent('myoffset-updater', { | ||
| init: function () { | ||
| this.offset = new THREE.Vector3(); | ||
| }, | ||
|
|
||
| tick: function (t, dt) { | ||
| this.offset.copy(this.el.sceneEl.camera.el.getAttribute('position')); | ||
| this.offset.y = 0; | ||
| this.el.setAttribute('material', 'myOffset', this.offset); | ||
| } | ||
| }); | ||
| ``` | ||
|
|
||
| We then apply the custom component to the entity with the custom shader: | ||
|
|
||
| ``` | ||
| <a-scene> | ||
| <a-sphere material="shader:displacement-offset" | ||
| myoffset-updater | ||
| scale="1 1 1" | ||
| radius="0.2" | ||
| position="0 1.5 -2" | ||
| segments-height="128" | ||
| segments-width="128"> | ||
| <a-animation attribute="scale" direction="alternate-reverse" dur="5000" from="1 1 1" to="4 4 4" repeat="indefinite"></a-animation> | ||
| </a-sphere> | ||
| <a-box color="#CCC" width="3" depth="3" height="0.1" position="0 0 -2"></a-box> | ||
| </a-scene> | ||
| ``` | ||
|
|
||
| Voila! | ||
|
|
||
| * [Live demo](https://aframe-displacement-offset-registershader.glitch.me/) | ||
| * [Remix this on Glitch](https://glitch.com/edit/#!/aframe-displacement-offset-registershader) | ||
|
|
||
| ## Register a Custom Component Using THREE Material | ||
|
|
||
|
|
@@ -625,132 +734,3 @@ AFRAME.registerShader('line-dashed', { | |
| } | ||
| }); | ||
| ``` | ||
|
|
||
| ### Example — GLSL and Shaders, Using Custom Component | ||
|
|
||
| For more customized visual effects, we can write GLSL shaders and apply them to A-Frame entities. We'll do this using [THREE.ShaderMaterial](https://threejs.org/docs/#api/materials/ShaderMaterial) and a custom component. GLSL shaders can also be used with the `registerShader` API, but for many cases — here, we need a `tick()` handler to update the shader's clock — using a component can be easier. | ||
|
|
||
| > NOTE: GLSL, the syntax used to write shaders, may seem a bit scary at first. For a gentle (and free!) introduction, we recommend [The Book of Shaders](http://thebookofshaders.com/). | ||
|
|
||
| Component: | ||
|
|
||
| ```js | ||
| // material-grid-glitch.js | ||
|
|
||
| const vertexShader = ` | ||
| /// PLACEHOLDER /// | ||
| `; | ||
| const fragmentShader = ` | ||
| /// PLACEHOLDER /// | ||
| `; | ||
|
|
||
| AFRAME.registerComponent('material-grid-glitch', { | ||
| schema: {color: {type: 'color'}}, | ||
|
|
||
| /** | ||
| * Creates a new THREE.ShaderMaterial using the two shaders defined | ||
| * in vertex.glsl and fragment.glsl. | ||
| */ | ||
| init: function () { | ||
| const data = this.data; | ||
|
|
||
| this.material = new THREE.ShaderMaterial({ | ||
| uniforms: { | ||
| time: { value: 0.0 }, | ||
| color: { value: new THREE.Color(data.color) } | ||
| }, | ||
| vertexShader, | ||
| fragmentShader | ||
| }); | ||
|
|
||
| this.applyToMesh(); | ||
| this.el.addEventListener('model-loaded', () => this.applyToMesh()); | ||
| }, | ||
|
|
||
|
|
||
| /** | ||
| * Update the ShaderMaterial when component data changes. | ||
| */ | ||
| update: function () { | ||
| this.material.uniforms.color.value.set(this.data.color); | ||
| }, | ||
|
|
||
| /** | ||
| * Apply the material to the current entity. | ||
| */ | ||
| applyToMesh: function() { | ||
| const mesh = this.el.getObject3D('mesh'); | ||
| if (mesh) { | ||
| mesh.material = this.material; | ||
| } | ||
| }, | ||
|
|
||
| /** | ||
| * On each frame, update the 'time' uniform in the shaders. | ||
| */ | ||
| tick: function (t) { | ||
| this.material.uniforms.time.value = t / 1000; | ||
| } | ||
|
|
||
| }) | ||
| ``` | ||
|
|
||
| Next, we can put our shaders into the placeholders above. Every material will have two shaders: a vertex and a fragment shader. | ||
|
|
||
| ```glsl | ||
| // vertex.glsl | ||
|
|
||
| varying vec2 vUv; | ||
|
|
||
| void main() { | ||
| vUv = uv; | ||
| gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); | ||
| } | ||
| ``` | ||
|
|
||
| ```glsl | ||
| // fragment.glsl | ||
|
|
||
| varying vec2 vUv; | ||
| uniform vec3 color; | ||
| uniform float time; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So the reason I didn't use
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To clarify, there is inherent support for type
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the DOM and A-Frame we use miliseconds for time. I would keep the name
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I may not be doing a good job of explaining. The shader examples that are time-varying already use a uniform named
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed to |
||
|
|
||
| void main() { | ||
| // Use sin(time), which curves between 0 and 1 over time, | ||
| // to determine the mix of two colors: | ||
| // (a) Dynamic color where 'R' and 'B' channels come | ||
| // from a modulus of the UV coordinates. | ||
| // (b) Base color. | ||
| // | ||
| // The color itself is a vec4 containing RGBA values 0-1. | ||
| gl_FragColor = mix( | ||
| vec4(mod(vUv , 0.05) * 20.0, 1.0, 1.0), | ||
| vec4(color, 1.0), | ||
| sin(time) | ||
| ); | ||
| } | ||
| ``` | ||
|
|
||
| Finally, here is the HTML markup to put it all together: | ||
|
|
||
| ```html | ||
| <!-- index.html --> | ||
|
|
||
| <a-scene> | ||
| <a-sphere material-grid-glitch="color: blue;" | ||
| radius="0.5" | ||
| position="0 1.5 -2"> | ||
| </a-sphere> | ||
| </a-scene> | ||
| ``` | ||
|
|
||
| * [Live demo](https://aframe-simple-shader.glitch.me/) | ||
| * [Remix this on Glitch](https://glitch.com/edit/#!/aframe-simple-shader) | ||
|
|
||
|  | ||
|
|
||
| *** | ||
|
|
||
| For a more advanced example, [try realtime vertex displacement](https://glitch.com/edit/#!/aframe-displacement-shader). | ||
|
|
||
|  | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I probably wouldn't go to the trouble of explaining how to set it up here, but worth noting somewhere that:
(similar options are available for rollup etc.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done