Commit 91510eb3 authored by Gaëtan Renaudeau's avatar Gaëtan Renaudeau

continue docs

parent 96898fba
docs/
Examples/
# OSX # OSX
# #
.DS_Store .DS_Store
......
...@@ -4,19 +4,37 @@ GlslTransitions.forEach(function (t) { ...@@ -4,19 +4,37 @@ GlslTransitions.forEach(function (t) {
byName[t.name] = t; byName[t.name] = t;
}); });
const transitions = [ const transitions = [
[ "directionalwipe", function () {
const angle = Math.random() * 2 * Math.PI;
return {
direction: [ Math.cos(angle), Math.sin(angle) ]
};
} ],
[ "cube", function () { [ "cube", function () {
return { persp: 0.9 - Math.random()*Math.random(), unzoom: Math.random()*Math.random() }; return { persp: 0.9 - Math.random()*Math.random(), unzoom: Math.random()*Math.random() };
} ], } ],
[ "randomsquares", function () {
const size = Math.round(4 + 20 * Math.random());
return {
size: [ size, size ],
smoothness: Math.random()
};
} ],
"undulating burn out", "undulating burn out",
[ "CrossZoom", function () { [ "CrossZoom", function () {
return { strength: 0.5 * Math.random() }; return { strength: 0.5 * Math.random() };
} ], } ],
"swap",
[ "wind", function () {
return { size: 0.1+0.2 * Math.random() };
} ],
"glitch displace", "glitch displace",
[ "Mosaic", function () { [ "Mosaic", function () {
let dx = Math.round(Math.random() * 6 - 3), dy = Math.round(Math.random() * 6 - 3); let dx = Math.round(Math.random() * 6 - 3), dy = Math.round(Math.random() * 6 - 3);
if (dx===0 && dy===0) dy = -1; if (dx===0 && dy===0) dy = -1;
return { endx: dx, endy: dy }; return { endx: dx, endy: dy };
} ], } ],
"Dreamy",
[ "DoomScreenTransition", function () { [ "DoomScreenTransition", function () {
return { return {
barWidth: Math.round(6 + 20 * Math.random()), barWidth: Math.round(6 + 20 * Math.random()),
...@@ -25,21 +43,23 @@ const transitions = [ ...@@ -25,21 +43,23 @@ const transitions = [
frequency: Math.random() frequency: Math.random()
}; };
} ], } ],
[ "colourDistance", function () {
return { interpolationPower: 6 * Math.random() };
} ],
"swap",
[ "doorway", function () { [ "doorway", function () {
return { perspective: Math.random() * Math.random(), depth: 1 + 10 * Math.random() * Math.random() }; return { perspective: Math.random() * Math.random(), depth: 1 + 10 * Math.random() * Math.random() };
} ], } ],
"Star Wipe", "Star Wipe",
"pinwheel", "pinwheel",
"SimpleFlip",
"TilesScanline", "TilesScanline",
"Dreamy", [ "flyeye", function () {
return {
size: Math.random() * Math.random(),
zoom: 200 * Math.random() * Math.random(),
colorSeparation: 0.8 * Math.random() * Math.random()
};
} ],
"Swirl", "Swirl",
"burn", "burn",
"Radial", "Radial",
"SimpleFlip",
[ "ripple", function () { [ "ripple", function () {
return { return {
amplitude: 200 * Math.random(), amplitude: 200 * Math.random(),
...@@ -47,40 +67,13 @@ const transitions = [ ...@@ -47,40 +67,13 @@ const transitions = [
}; };
} ], } ],
"morph", "morph",
["ButterflyWaveScrawler", function () {
return {
amplitude: Math.random(),
waves: 50 * Math.random() * Math.random(),
colorSeparation: 0.8 * Math.random() * Math.random()
};
} ],
[ "flash", function () { [ "flash", function () {
return { flashIntensity: 4 * Math.random() }; return { flashIntensity: 4 * Math.random() };
} ], } ],
[ "randomsquares", function () {
const size = Math.round(4 + 20 * Math.random());
return {
size: [ size, size ],
smoothness: Math.random()
};
} ],
[ "flyeye", function () {
return {
size: Math.random() * Math.random(),
zoom: 200 * Math.random() * Math.random(),
colorSeparation: 0.8 * Math.random() * Math.random()
};
} ],
"squeeze", "squeeze",
[ "directionalwipe", function () {
const angle = Math.random() * 2 * Math.PI;
return {
direction: [ Math.cos(angle), Math.sin(angle) ]
};
} ],
"circleopen", "circleopen",
[ "wind", function () { [ "colourDistance", function () {
return { size: 0.1+0.2 * Math.random() }; return { interpolationPower: 6 * Math.random() };
} ] } ]
].map(function (obj) { ].map(function (obj) {
let name, genUniforms; let name, genUniforms;
...@@ -104,8 +97,11 @@ const transitions = [ ...@@ -104,8 +97,11 @@ const transitions = [
}; };
}); });
let _i = 0;
function random () { function random () {
const i = Math.floor(Math.random() * transitions.length); const i = _i;
_i = _i+1 >= transitions.length ? 0 : _i+1;
const t = transitions[i]; const t = transitions[i];
const uniforms = t.genUniforms && t.genUniforms() || {}; const uniforms = t.genUniforms && t.genUniforms() || {};
return { return {
......
...@@ -31,9 +31,7 @@ class HueRotate extends React.Component { ...@@ -31,9 +31,7 @@ class HueRotate extends React.Component {
shader={shaders.hueRotate} shader={shaders.hueRotate}
width={width} width={width}
height={height} height={height}
uniforms={{ uniforms={{ hue }}>
hue
}}>
<GL.Target uniform="tex">{children}</GL.Target> <GL.Target uniform="tex">{children}</GL.Target>
</GL.View>; </GL.View>;
} }
......
...@@ -2,7 +2,7 @@ const React = require("react-native"); ...@@ -2,7 +2,7 @@ const React = require("react-native");
const GL = require("gl-react-native"); const GL = require("gl-react-native");
const shaders = GL.Shaders.create({ const shaders = GL.Shaders.create({
pieProgress: { oneFingerResponse: {
frag: ` frag: `
precision mediump float; precision mediump float;
varying vec2 uv; varying vec2 uv;
...@@ -20,7 +20,7 @@ void main () { ...@@ -20,7 +20,7 @@ void main () {
} }
}); });
class PieProgress extends React.Component { class OneFingerResponse extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
this.state = { this.state = {
...@@ -65,10 +65,10 @@ class PieProgress extends React.Component { ...@@ -65,10 +65,10 @@ class PieProgress extends React.Component {
onResponderTerminate={this.onTouchEnd} onResponderTerminate={this.onTouchEnd}
width={width} width={width}
height={height} height={height}
shader={shaders.pieProgress} shader={shaders.oneFingerResponse}
uniforms={{ pressed, position }} uniforms={{ pressed, position }}
/>; />;
} }
} }
module.exports = PieProgress; module.exports = OneFingerResponse;
## Run the example
```
npm install
```
Then open AdvancedEffects.xcodeproj with XCode and run the application.
## Developing with the example
```
npm install react-native
npm install ../.. # everytime the lib code changes
```
(also make sure a `npm install` has been called on the root directory of gl-react-native).
...@@ -4,10 +4,11 @@ ...@@ -4,10 +4,11 @@
* [GL.Shaders.create](api/Shaders.create.md) * [GL.Shaders.create](api/Shaders.create.md)
* [GL.View](api/View.md) * [GL.View](api/View.md)
* [GL.Target](api/Target.md) * [GL.Target](api/Target.md)
* Examples * [Simple Examples](examples/simple.md)
* [Hello World](examples/1.md) * [Hello GL](examples/1.md)
* [Effects over Image](examples/2.md) * [Saturate an Image](examples/2.md)
* [Effects over anything](examples/3.md) * [Hue Rotate on Text+Image](examples/3.md)
* [Animation](examples/4.md) * [Progress Indicator](examples/4.md)
* [Touch responsive](examples/5.md) * [Touch Responsive](examples/5.md)
* [Upload Indicator](examples/6.md) * [Animation](examples/6.md)
* [Advanced Effects Examples](examples/advancedeffects.md)
# Hello GL
This minimal example shows the classical "Hello World" of OpenGL 2D drawing, showing a nice 2D gradient where:
- The RED component increases with the X position of the pixel.
- The GREEN component increases with the Y position of the pixel.
> N.B. a GLSL fragment uses a ["functional rendering"](http://greweb.me/2013/11/functional-rendering/)
paradigm, which means you have to implement a `Position => Color` function.
![](1.jpg)
```html
<HelloGL width={256} height={171} />
```
## Implementation
```js
const React = require("react-native");
const GL = require("gl-react-native");
const shaders = GL.Shaders.create({
helloGL: {
frag: `
precision highp float;
varying vec2 uv; // This variable vary in all pixel position (normalized from vec2(0.0,0.0) to vec2(1.0,1.0))
void main () { // This function is called FOR EACH PIXEL
gl_FragColor = vec4(uv.x, uv.y, 0.5, 1.0); // red vary over X, green vary over Y, blue is 50%, alpha is 100%.
}
`
}
});
class HelloGL extends React.Component {
render () {
const { width, height } = this.props;
return <GL.View
shader={shaders.helloGL}
width={width}
height={height}
/>;
}
}
```
# Saturate an Image
Effects on images like *saturation, contrast, brightness, inverse, hue,...* are very easy to implement in GLSL. Here is the example of **Saturation**.
![](2.gif)
```html
<Saturation
width={256}
height={171}
factor={saturationFactor}
image={{ uri: "http://i.imgur.com/iPKTONG.jpg" }}
/>
```
## Implementation
```js
const React = require("react-native");
const GL = require("gl-react-native");
const shaders = GL.Shaders.create({
saturation: {
frag: `
precision highp float;
varying vec2 uv;
uniform sampler2D image;
uniform float factor;
void main () {
vec4 c = texture2D(image, uv);
// Algorithm from Chapter 16 of OpenGL Shading Language
const vec3 W = vec3(0.2125, 0.7154, 0.0721);
gl_FragColor = vec4(mix(vec3(dot(c.rgb, W)), c.rgb, factor), c.a);
}
`
}
});
class Saturation extends React.Component {
render () {
const { width, height, factor, image } = this.props;
return <GL.View
shader={shaders.saturation}
width={width}
height={height}
uniforms={{ factor, image }}
/>;
}
}
```
# Hue Rotate on Text+Image
`gl-react-native` not only allow to add effects on top of images but also on top of any content. This example shows the Hue rotation effect on top of texts and image.
![](3.gif)
```html
<HueRotate
width={256}
height={180}
hue={hue}>
<Image style={{ width: 256, height: 244 }} source={{ uri: "http://i.imgur.com/qVxHrkY.jpg" }}/>
<Text style={styles.demoText1}>Throw me to the wolves</Text>
<Text style={styles.demoText2}>{text}</Text>
</HueRotate>
```
## Implementation
```js
const React = require("react-native");
const GL = require("gl-react-native");
const shaders = GL.Shaders.create({
hueRotate: {
frag: `
precision highp float;
varying vec2 uv;
uniform sampler2D tex;
uniform float hue;
const mat3 rgb2yiq = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);
const mat3 yiq2rgb = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.1070, 1.7046);
void main() {
vec3 yColor = rgb2yiq * texture2D(tex, uv).rgb;
float originalHue = atan(yColor.b, yColor.g);
float finalHue = originalHue + hue;
float chroma = sqrt(yColor.b*yColor.b+yColor.g*yColor.g);
vec3 yFinalColor = vec3(yColor.r, chroma * cos(finalHue), chroma * sin(finalHue));
gl_FragColor = vec4(yiq2rgb*yFinalColor, 1.0);
}
`
}
});
class HueRotate extends React.Component {
render () {
const { width, height, hue, children } = this.props;
return <GL.View
shader={shaders.hueRotate}
width={width}
height={height}
uniforms={{ hue }}>
<GL.Target uniform="tex">{children}</GL.Target>
</GL.View>;
}
}
```
The `GL.Target` describes which texture uniform is used for the rasterization of its children content.
Note how powerful it is to compose React Components that way.
# Progress Indicator
This example show a graphical composant implemented in OpenGL that would not be easy to implement with raw RN components.
> N.B. the pie is transparent and the background is black with 50% opacity. That would easily allow to put this view on top of an image to show an image upload progress.
![](4.gif)
```html
<PieProgress
width={256}
height={180}
progress={progress}
/>
```
*explicit props:*
```html
<PieProgress
width={256}
height={180}
progress={progress}
colorInside={[0,0,0,0]}
colorOutside={[0,0,0,0.5]}
radius={0.4}
/>
```
## Implementation
```js
const React = require("react-native");
const GL = require("gl-react-native");
const shaders = GL.Shaders.create({
pieProgress: {
frag: `
precision mediump float;
varying vec2 uv;
uniform vec4 colorInside, colorOutside;
uniform float radius;
uniform float progress;
uniform vec2 dim;
const vec2 center = vec2(0.5);
const float PI = acos(-1.0);
void main () {
vec2 norm = dim / min(dim.x, dim.y);
vec2 p = uv * norm - (norm-1.0)/2.0;
vec2 delta = p - center;
float inside =
step(length(delta), radius) *
step((PI + atan(delta.y, -delta.x)) / (2.0 * PI), progress);
gl_FragColor = mix(
colorOutside,
colorInside,
inside
);
}
`
}
});
class PieProgress extends React.Component {
render () {
const {
width,
height,
progress,
colorInside,
colorOutside,
radius
} = this.props;
return <GL.View
width={width}
height={height}
shader={shaders.pieProgress}
opaque={false}
uniforms={{
dim: [ width, height ],
progress,
colorInside,
colorOutside,
radius
}}
/>;
}
}
PieProgress.defaultProps = {
colorInside: [0, 0, 0, 0],
colorOutside: [0, 0, 0, 0.5],
radius: 0.4
};
```
Note the usage of complex uniform types.
The JavaScript uniform:
```js
dim: [ width, height ]
```
is mapped to GLSL uniform:
```glsl
uniform vec2 dim;
```
where dim.x == width and dim.y == height.
Same mapping happens for the colors: `[1, 0.5, 0.2, 1]` mapped to a `vec4` will spread into {r,g,b,a}.
> Complex types in GLSL have "dynamic" properties:
vecN `v` can simultaneously be used using:
>```
v[0] v[1] v[2] v[3] // array notation
v.r v.g v.b v.a // color notation
v.x v.y v.z v.w // position notation
v.s v.t v.u v.v // coord notation
```
# Touch Responsive
This example shows a simple rendering that responds to touch events (one finger only).
![](5.gif)
```html
<OneFingerResponse
width={256}
height={180}
/>
```
## Implementation
```js
const React = require("react-native");
const GL = require("gl-react-native");
const shaders = GL.Shaders.create({
oneFingerResponse: {
frag: `
precision mediump float;
varying vec2 uv;
uniform float pressed;
uniform vec2 position;
void main () {
float dist = pow(1.0 - distance(position, uv), 4.0);
float edgeDistX = pow(1.0 - distance(position.x, uv.x), 24.0);
float edgeDistY = pow(1.0 - distance(position.y, uv.y), 24.0);
gl_FragColor = pressed * vec4(0.8 * dist + edgeDistX, 0.7 * dist + edgeDistY, 0.6 * dist, 1.0);
}
`
}
});
class OneFingerResponse extends React.Component {
constructor (props) {
super(props);
this.state = {
pressed: 0,
position: [ 0, 0 ]
};
this.onTouchStart = this.onTouchStart.bind(this);
this.onTouchEnd = this.onTouchEnd.bind(this);
this.onTouchMove = this.onTouchMove.bind(this);
}
onTouchStart (evt) {
this.setState({
pressed: 1
});
this.onTouchMove(evt);
}
onTouchMove (evt) {
const { width, height } = this.props;
const { locationX, locationY } = evt.nativeEvent;
this.setState({
position: [
Math.max(0, Math.min(locationX/width, 1)),
Math.max(0, Math.min(1-locationY/height, 1))
]
});
}
onTouchEnd () {
this.setState({
pressed: 0
});
}
render () {
const { width, height } = this.props;
const { pressed, position } = this.state;
return <GL.View
onStartShouldSetResponderCapture={() => true}
onMoveShouldSetResponderCapture={() => true}
onResponderTerminationRequest={() => false}
onResponderGrant={this.onTouchStart}
onResponderMove={this.onTouchMove}
onResponderRelease={this.onTouchEnd}
onResponderTerminate={this.onTouchEnd}
width={width}
height={height}
shader={shaders.oneFingerResponse}
uniforms={{ pressed, position }}
/>;
}
}
```
The GLSL code shown here is a bit more advanced, we recommend you to take a look at the [GLSL specification](https://www.opengl.org/documentation/glsl/) to learn what the primitive function are doing.
# Animation
Any value can be programmatically animated in a render loop. This example extends the simple [Hello GL](1.md) to add a `value` uniform that is passed in blue color component. `value` is animated over time.
![](6.gif)
```html
<AnimatedHelloGL width={256} height={180} />
```
## Implementation
```js
const React = require("react-native");
const GL = require("gl-react-native");
const shaders = GL.Shaders.create({
helloGL: {
frag: `
precision highp float;
varying vec2 uv;
uniform float value;
void main () {
gl_FragColor = vec4(uv.x, uv.y, value, 1.0);
}
`
}
});
class HelloGL extends React.Component {
constructor (props) {
super(props);
this.state = {
value: 0
};
}
componentDidMount () {
const loop = time => {
requestAnimationFrame(loop);
this.setState({
value: (1 + Math.cos(time / 1000)) / 2 // cycle between 0 and 1
});
};
requestAnimationFrame(loop);
}
render () {
const { width, height } = this.props;
const { value } = this.state;
return <GL.View
shader={shaders.helloGL}
width={width}
height={height}
uniforms={{ value }}
/>;
}
}
```
This image diff could not be displayed because it is too large. You can view the blob instead.
# AdvancedEffects examples
*AdvancedEffects* is a React Native application that shows more advanced `gl-react-native` samples.
It performs non trivial animated effects that still runs at 60 FPS on a iPhone.
It is available in [`Examples/AdvancedEffects`](https://github.com/ProjectSeptemberInc/gl-react-native/tree/master/Examples/AdvancedEffects) folder.
![](advancedeffects.gif)
This image diff could not be displayed because it is too large. You can view the blob instead.
# Simple examples
*Simple* is a React Native application that shows minimal `gl-react-native` samples.
It is available in [`Examples/Simple`](https://github.com/ProjectSeptemberInc/gl-react-native/tree/master/Examples/Simple) folder.
![](simple.gif)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment