diff --git a/cookbook-rn/exp.json b/cookbook-rn/exp.json index 00fb47b..0db86a3 100755 --- a/cookbook-rn/exp.json +++ b/cookbook-rn/exp.json @@ -1,8 +1,8 @@ { - "name": "glrn-ex", - "description": "An empty new project", - "slug": "glrn-ex", - "sdkVersion": "11.0.3", + "name": "glrn-cookbook", + "description": "GL React Native Cookbook", + "slug": "glrn-cookbook", + "sdkVersion": "12.0.0", "version": "1.0.0", "orientation": "portrait", "primaryColor": "#cccccc", diff --git a/cookbook-rn/main.js b/cookbook-rn/main.js index 14e672e..857fe66 100755 --- a/cookbook-rn/main.js +++ b/cookbook-rn/main.js @@ -1,32 +1,4 @@ +import "exponent"; import Exponent from "exponent"; -import React from "react"; -import { - StyleSheet, - Text, - View, -} from "react-native"; -import HelloGL from "./HelloGL"; -import HelloTexture from "./HelloTexture"; - -class App extends React.Component { - render() { - return ( - - - - - ); - } -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: "#fff", - alignItems: "center", - justifyContent: "center", - flexDirection: "column", - }, -}); - +import App from "./src"; Exponent.registerRootComponent(App); diff --git a/cookbook-rn/package.json b/cookbook-rn/package.json old mode 100755 new mode 100644 index 832e7f6..54be233 --- a/cookbook-rn/package.json +++ b/cookbook-rn/package.json @@ -1,15 +1,23 @@ { - "name": "glrn-ex", + "name": "glrn-cookbook", "version": "0.0.0", - "description": "Hello Exponent!", - "author": null, + "description": "GL React Native cookbook", + "author": "Gaëtan Renaudeau", "private": true, "main": "main.js", "dependencies": { - "exponent": "^11.0.3-rc.1", - "gl-react": "file:../packages/gl-react", - "gl-react-native": "file:../packages/gl-react-native", + "@exponent/ex-navigation": "^2.3.0", + "exponent": "^12.0.5", + "gl-react": "^3.0.0-alpha1", + "gl-react-native": "^3.0.0-alpha1", + "glsl-transitions": "^2016.12.26", + "ndarray": "^1.0.18", + "raf": "^3.3.0", "react": "~15.3.2", - "react-native": "github:exponentjs/react-native#sdk-11.0.2" + "react-motion": "^0.4.7", + "react-native": "github:exponentjs/react-native#sdk-12.0.0" + }, + "scripts": { + "generate-examples": "cd src/examples; ./gen.sh 1> index.js" } } diff --git a/cookbook-rn/src/0BJobQo.jpg b/cookbook-rn/src/0BJobQo.jpg new file mode 100644 index 0000000..99504be Binary files /dev/null and b/cookbook-rn/src/0BJobQo.jpg differ diff --git a/cookbook-rn/src/0PkQEk1.jpg b/cookbook-rn/src/0PkQEk1.jpg new file mode 100644 index 0000000..70dd467 Binary files /dev/null and b/cookbook-rn/src/0PkQEk1.jpg differ diff --git a/cookbook-rn/src/0bUSEBX.jpg b/cookbook-rn/src/0bUSEBX.jpg new file mode 100644 index 0000000..9e04401 Binary files /dev/null and b/cookbook-rn/src/0bUSEBX.jpg differ diff --git a/cookbook-rn/src/2VP5osy.jpg b/cookbook-rn/src/2VP5osy.jpg new file mode 100644 index 0000000..58b7dce Binary files /dev/null and b/cookbook-rn/src/2VP5osy.jpg differ diff --git a/cookbook-rn/5EOyTDQ.jpg b/cookbook-rn/src/5EOyTDQ.jpg similarity index 100% rename from cookbook-rn/5EOyTDQ.jpg rename to cookbook-rn/src/5EOyTDQ.jpg diff --git a/cookbook-rn/src/CKlmtPs.jpg b/cookbook-rn/src/CKlmtPs.jpg new file mode 100644 index 0000000..0526a41 Binary files /dev/null and b/cookbook-rn/src/CKlmtPs.jpg differ diff --git a/cookbook-rn/src/G2Whuq3.jpg b/cookbook-rn/src/G2Whuq3.jpg new file mode 100644 index 0000000..5167d6b Binary files /dev/null and b/cookbook-rn/src/G2Whuq3.jpg differ diff --git a/cookbook-rn/src/GQo1KWq.jpg b/cookbook-rn/src/GQo1KWq.jpg new file mode 100644 index 0000000..7cb1fff Binary files /dev/null and b/cookbook-rn/src/GQo1KWq.jpg differ diff --git a/cookbook-rn/src/HOC/respondToTouchPosition.js b/cookbook-rn/src/HOC/respondToTouchPosition.js new file mode 100644 index 0000000..2f41401 --- /dev/null +++ b/cookbook-rn/src/HOC/respondToTouchPosition.js @@ -0,0 +1,76 @@ +//@flow +import React, { Component } from "react"; +import { PanResponder, View } from "react-native"; +import hoistNonReactStatics from "hoist-non-react-statics"; + +type Pos = { x: number, y: number }; +type State = { touching: boolean, touchPosition: Pos }; + +export default (Comp: ReactClass<*>, { + initialPosition = { x: 0.5, y: 0.5 } +}: { initialPosition: Pos } = {}) => { + class TouchPositionResponding extends Component { + state: State = { + touching: false, + touchPosition: initialPosition, + }; + initialContainerPos: [number, number]; + initialDragPos: [number, number]; + size: [number, number]; + panResponder = PanResponder.create({ + onStartShouldSetPanResponder: () => true, + onStartShouldSetPanResponderCapture: () => true, + onMoveShouldSetPanResponder: () => true, + onMoveShouldSetPanResponderCapture: () => true, + onPanResponderGrant: (evt) => { + const { pageX, pageY } = evt.nativeEvent; + this.initialDragPos = [ pageX, pageY ]; + this.refs.root.measure((x, y, w, h, initialPageX, initialPageY) => { + this.initialContainerPos = [ initialPageX, initialPageY ]; + this.size = [ w, h ]; + this.setState({ + touching: true, + touchPosition: { + x: (pageX - initialPageX) / w, + y: 1 - (pageY - initialPageY) / h, + } + }); + }); + }, + onPanResponderMove: (evt, gestureState) => { + const [ pageX, pageY ] = this.initialDragPos; + const [ initialPageX, initialPageY ] = this.initialContainerPos; + const { dx, dy } = gestureState; + const [ w, h ] = this.size; + this.setState({ + touchPosition: { + x: (pageX + dx - initialPageX) / w, + y: 1 - (pageY + dy - initialPageY) / h, + } + }); + }, + onPanResponderTerminationRequest: () => true, + onPanResponderRelease: () => this._onEnd(), + onPanResponderTerminate: () => this._onEnd(), + onShouldBlockNativeResponder: () => true, + }); + _onEnd = () => { + if (this.state.touching) { + this.setState({ + touching: false, + }); + } + }; + render() { + return ( + + + + ); + } + } + + hoistNonReactStatics(TouchPositionResponding, Comp); + + return TouchPositionResponding; +}; diff --git a/cookbook-rn/src/HOC/timeLoop.js b/cookbook-rn/src/HOC/timeLoop.js new file mode 100644 index 0000000..8db07f5 --- /dev/null +++ b/cookbook-rn/src/HOC/timeLoop.js @@ -0,0 +1,50 @@ +//@flow +import React, { PureComponent } from "react"; +import raf from "raf"; +import hoistNonReactStatics from "hoist-non-react-statics"; + +// NB this is only an utility for the examples +export default ( + C: ReactClass<*>, + { refreshRate = 60 }: { refreshRate?: number } = {} +): ReactClass<*> => { + class TL extends PureComponent { + static displayName = `timeLoop(${C.displayName||C.name||""})`; + state: { time: number }; + state = { + time: 0, + tick: 0, + }; + _r: any; + componentDidMount() { + let startTime: number, lastTime: number; + let interval = 1000 / refreshRate; + lastTime = -interval; + const loop = (t: number) => { + this._r = raf(loop); + if (!startTime) startTime = t; + if (t - lastTime > interval) { + lastTime = t; + this.setState({ + time: t - startTime, + tick: this.state.tick + 1, + }); + } + }; + this._r = raf(loop); + } + componentWillUnmount() { + raf.cancel(this._r); + } + render() { + return ; + } + }; + + hoistNonReactStatics(TL, C); + + return TL; +} diff --git a/cookbook-rn/HelloGL.js b/cookbook-rn/src/HelloGL.js similarity index 100% rename from cookbook-rn/HelloGL.js rename to cookbook-rn/src/HelloGL.js diff --git a/cookbook-rn/HelloTexture.js b/cookbook-rn/src/HelloTexture.js similarity index 100% rename from cookbook-rn/HelloTexture.js rename to cookbook-rn/src/HelloTexture.js diff --git a/cookbook-rn/src/Home.js b/cookbook-rn/src/Home.js new file mode 100644 index 0000000..af810b0 --- /dev/null +++ b/cookbook-rn/src/Home.js @@ -0,0 +1,99 @@ + +import React from "react"; +import { + Image, + StyleSheet, + Text, + ScrollView, + View, +} from "react-native"; +import ListItem from "./ListItem"; +import * as examples from "./examples"; +import Router from "./Router"; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#fff", + flexDirection: "column", + }, + list: { + flex: 1, + }, + subHeader: { + padding: 10, + backgroundColor: "#f9f9f9", + }, + subHeaderText: { + color: "#333", + fontSize: 12, + fontStyle: "italic", + }, + title: { + flex: 1, + flexDirection: "row", + alignItems: "center", + justifyContent: "center", + }, + titleImage: { + width: 24, + height: 24, + marginRight: 12, + }, + titleText: { + fontWeight: "bold", + color: "#fff", + fontSize: 18, + }, +}); + +export default class Home extends React.Component { + static route = { + navigationBar: { + renderTitle: () => + + gl-react-native + + }, + }; + props: { + navigator: *, + }; + render() { + const {navigator} = this.props; + return ( + + + + a React Native library to write and compose WebGL shaders + + + + + + Here is a collection of gl-react-native examples: + + + + {Object.keys(examples).map(ex => { + const { title, description, Example } = examples[ex]; + return navigator.push(Router.getRoute(ex)) + : null + } + />; + })} + + + ); + } +} diff --git a/cookbook-rn/src/IvpoR40.jpg b/cookbook-rn/src/IvpoR40.jpg new file mode 100644 index 0000000..a2170c2 Binary files /dev/null and b/cookbook-rn/src/IvpoR40.jpg differ diff --git a/cookbook-rn/src/ListItem.js b/cookbook-rn/src/ListItem.js new file mode 100644 index 0000000..e8b7133 --- /dev/null +++ b/cookbook-rn/src/ListItem.js @@ -0,0 +1,47 @@ +//@flow +import React from "react"; +import { + StyleSheet, + Text, + TouchableOpacity, +} from "react-native"; + +const styles = StyleSheet.create({ + container: { + backgroundColor: "#fff", + borderBottomWidth: StyleSheet.hairlineWidth, + borderBottomColor: "rgba(0, 0, 0, .1)", + paddingVertical: 10, + paddingHorizontal: 14, + }, + title: { + fontSize: 16, + fontWeight: "bold", + color: "#222", + margin: 2, + }, + titleDisabled: { + color: "#aaa", + }, + description: { + fontSize: 12, + color: "#888", + margin: 2, + }, +}); + +type Props = { + title: string, + description: string, + onPress: Function, + disabled?: boolean, +}; + +export default function ListItem({ title, description, onPress, disabled }: Props) { + return ( + + {title} + {description} + + ); +} diff --git a/cookbook-rn/src/MnOB9Le.jpg b/cookbook-rn/src/MnOB9Le.jpg new file mode 100644 index 0000000..19a846e Binary files /dev/null and b/cookbook-rn/src/MnOB9Le.jpg differ diff --git a/cookbook-rn/src/NjbLHx2.jpg b/cookbook-rn/src/NjbLHx2.jpg new file mode 100644 index 0000000..b381cd7 Binary files /dev/null and b/cookbook-rn/src/NjbLHx2.jpg differ diff --git a/cookbook-rn/src/Otbz312.jpg b/cookbook-rn/src/Otbz312.jpg new file mode 100644 index 0000000..01f4c9b Binary files /dev/null and b/cookbook-rn/src/Otbz312.jpg differ diff --git a/cookbook-rn/src/Router.js b/cookbook-rn/src/Router.js new file mode 100644 index 0000000..7c2fc3b --- /dev/null +++ b/cookbook-rn/src/Router.js @@ -0,0 +1,16 @@ +//@flow +import { + createRouter, +} from "@exponent/ex-navigation"; +import * as examples from "./examples"; +import Home from "./Home"; +import makeExample from "./makeExample"; +export default createRouter(() => { + const routes = { + home: () => Home, + }; + Object.keys(examples).map(k => { + routes[k] = () => makeExample(examples[k], k); + }); + return routes; +}); diff --git a/cookbook-rn/src/SzbbUvX.jpg b/cookbook-rn/src/SzbbUvX.jpg new file mode 100644 index 0000000..f4f24dd Binary files /dev/null and b/cookbook-rn/src/SzbbUvX.jpg differ diff --git a/cookbook-rn/src/YqsZKgc.jpg b/cookbook-rn/src/YqsZKgc.jpg new file mode 100644 index 0000000..0f19ae9 Binary files /dev/null and b/cookbook-rn/src/YqsZKgc.jpg differ diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/index.js b/cookbook-rn/src/examples/_TODO_blurimgtitle/index.js new file mode 100644 index 0000000..74ad498 --- /dev/null +++ b/cookbook-rn/src/examples/_TODO_blurimgtitle/index.js @@ -0,0 +1,165 @@ +//@flow +import React, { PureComponent, Component, PropTypes } from "react"; +import { Shaders, Node, GLSL, Bus, LinearCopy } from "gl-react"; +import { Surface } from "gl-react-native"; +import JSON2D from "react-json2d"; +import {Blur1D} from "../blurxy"; +import {Blur} from "../blurmulti"; +import {BlurV} from "../blurmap"; + +const shaders = Shaders.create({ + ImageTitle: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D img, imgBlurred, imgTone, title, blurMap; +uniform float colorThreshold; +float monochrome (vec3 c) { + return 0.2125 * c.r + 0.7154 * c.g + 0.0721 * c.b; +} +void main() { + float blurFactor = texture2D(blurMap, uv).r; + vec4 bgColor = mix( + texture2D(img, uv), + texture2D(imgBlurred, uv), + step(0.01, blurFactor) + ); + vec4 textColor = vec4(vec3( + step(monochrome(texture2D(imgTone, uv).rgb), colorThreshold) + ), 1.0); + float isText = 1.0 - texture2D(title, uv).r; + gl_FragColor = mix(bgColor, textColor, isText); +}` + }, + TitleBlurMap: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +uniform float threshold; +void main() { +gl_FragColor = vec4( + vec3(smoothstep(1.0, threshold, texture2D(t, uv).r)), + 1.0); +}` + }, +}); + +const AveragePixels = ({ children, quality }) => + + + {children} + + ; + +const TitleBlurMap = ({ children: title, threshold }) => + + {title} + + }} + width={64} + height={64} + />; + +class Title extends PureComponent { + render () { + const { children, width, height } = this.props; + return {{ + size: [ width, height ], + background: "#fff", + draws: [ + { + "font": "bold 78px Didot,Georgia,serif", + "fillStyle": "#000", + "textBaseline": "top", + "textAlign": "center" + }, + [ "fillText", children, width/2, 10, 84 ], + ] + }}; + } +} + +class ImageTitle extends Component { + static contextTypes = { + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + }; + render() { + const { children: img, text, colorThreshold } = this.props; + const { width, height } = this.context; + return ( + this.refs.imgBlurred, + title:() => this.refs.title, + imgTone:() => this.refs.imgTone, + blurMap:() => this.refs.blurMap, + }}> + + + + {text} + + + + + + {() => this.refs.title} + + + + + + {img} + + + + + this.refs.blurMap} + factor={4} + passes={4}> + {img} + + + + + ); + } +} + +export default class Example extends Component { + render() { + const { title, colorThreshold, image } = this.props; + return ( + + + {image} + + + ); + } + + static defaultProps = { + title: "Hello\nSan Francisco\n☻", + colorThreshold: 0.6, + image: require("./sf-6.jpg"), + }; +} diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/meta.js b/cookbook-rn/src/examples/_TODO_blurimgtitle/meta.js new file mode 100644 index 0000000..c48e0ed --- /dev/null +++ b/cookbook-rn/src/examples/_TODO_blurimgtitle/meta.js @@ -0,0 +1,36 @@ +import ImagesPicker from "../../toolbox/ImagesPicker"; +import makeTextArea from "../../toolbox/makeTextArea"; +import makeFloatSlider from "../../toolbox/makeFloatSlider"; + +export const title = "Dynamic Blur Image Title"; + +export const toolbox = [ + { prop: "title", + title: "Title", + Editor: makeTextArea({ + height: 140, + padding: 6, + fontFamily: "Didot,Georgia,serif", + fontSize: "36px", + lineHeight: "42px", + fontWeight: "bold", + textAlign: "center", + }) }, + { prop: "colorThreshold", + title: "Color Threshold", + Editor: makeFloatSlider(0, 1, 0.01) }, // FIXME black <-> white + { prop: "image", + title: "Image", + Editor: ImagesPicker, + style: { width: 400 }, + imageStyle: { maxWidth: 56, maxHeight: 56, marginBottom: 16, }, + images: [ + require("./sf-1.jpg"), + require("./sf-2.jpg"), + require("./sf-3.jpg"), + require("./sf-4.jpg"), + require("./sf-5.jpg"), + require("./sf-6.jpg"), + require("./sf-7.jpg"), + ] }, +]; diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-1.jpg b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-1.jpg new file mode 100644 index 0000000..ecf1d69 Binary files /dev/null and b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-1.jpg differ diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-2.jpg b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-2.jpg new file mode 100644 index 0000000..bf98e68 Binary files /dev/null and b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-2.jpg differ diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-3.jpg b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-3.jpg new file mode 100644 index 0000000..a27d486 Binary files /dev/null and b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-3.jpg differ diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-4.jpg b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-4.jpg new file mode 100644 index 0000000..2f75b7d Binary files /dev/null and b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-4.jpg differ diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-5.jpg b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-5.jpg new file mode 100644 index 0000000..c81c6de Binary files /dev/null and b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-5.jpg differ diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-6.jpg b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-6.jpg new file mode 100644 index 0000000..b73a869 Binary files /dev/null and b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-6.jpg differ diff --git a/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-7.jpg b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-7.jpg new file mode 100644 index 0000000..710bec7 Binary files /dev/null and b/cookbook-rn/src/examples/_TODO_blurimgtitle/sf-7.jpg differ diff --git a/cookbook-rn/src/examples/animated/index.js b/cookbook-rn/src/examples/animated/index.js new file mode 100644 index 0000000..4e2b9db --- /dev/null +++ b/cookbook-rn/src/examples/animated/index.js @@ -0,0 +1,54 @@ +//@flow +import React, { Component } from "react"; +import { Animated, PanResponder, View } from "react-native"; +import { Shaders, Node, GLSL } from "gl-react"; +import { Surface } from "gl-react-native"; +import respondToTouchPosition from "../../HOC/respondToTouchPosition"; + +const shaders = Shaders.create({ + cursor: { frag: GLSL` +precision lowp float; varying vec2 uv; uniform vec2 style; +void main() { + float dist = pow(1.0 - distance(style, uv), 8.0); + gl_FragColor = vec4(smoothstep(2.0, 0.2, distance(style, uv)) * vec3( + 1.0 * dist + pow(1.0 - distance(style.y, uv.y), 16.0), + 0.5 * dist + pow(1.0 - distance(style.y, uv.y), 32.0), + 0.2 * dist + pow(1.0 - distance(style.x, uv.x), 32.0)), 1.0); +}` } +}); + +class Cursor extends Component { + render() { + const { style: { x, y } } = this.props; + return ; + } +} + +// using "style" is a hack. see https://github.com/animatedjs/animated/issues/45 +const AnimatedCursor = Animated.createAnimatedComponent(Cursor); + +export default respondToTouchPosition(class Example extends Component { + props: { + touchPosition: { + x: number, + y: number, + }, + }; + state = { + style: new Animated.ValueXY(this.props.touchPosition) + }; + componentWillReceiveProps ({ touchPosition }) { + if (this.props.touchPosition !== touchPosition) { + Animated.spring(this.state.style, { + toValue: touchPosition, + }).start(); + } + } + render() { + return ( + + + + ); + } +}); diff --git a/cookbook-rn/src/examples/animated/meta.js b/cookbook-rn/src/examples/animated/meta.js new file mode 100644 index 0000000..f354047 --- /dev/null +++ b/cookbook-rn/src/examples/animated/meta.js @@ -0,0 +1 @@ +export const title = "Cursor spring effect with animated"; diff --git a/cookbook-rn/src/examples/blurmap/index.js b/cookbook-rn/src/examples/blurmap/index.js new file mode 100644 index 0000000..0aed353 --- /dev/null +++ b/cookbook-rn/src/examples/blurmap/index.js @@ -0,0 +1,67 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, GLSL, connectSize } from "gl-react"; +import { Surface } from "gl-react-native"; + +const shaders = Shaders.create({ + blurV1D: { + frag: GLSL`precision highp float; +varying vec2 uv; +uniform sampler2D t, map; +uniform vec2 direction, resolution; +vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { + vec4 color = vec4(0.0); + vec2 off1 = vec2(1.3846153846) * direction; + vec2 off2 = vec2(3.2307692308) * direction; + color += texture2D(image, uv) * 0.2270270270; + color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; + color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; + return color; +} +void main() { + gl_FragColor = blur9(t, uv, resolution, direction * texture2D(map, uv).rg); +}` } +}); + +// Same concept than Blur1D except it takes one more prop: +// a map texture that tells the blur intensity for a given position. +export const BlurV1D = + connectSize(({ children: t, direction, map, width, height }) => + ); + +// And its N-pass version +import {directionForPass} from "../blurmulti"; +export const BlurV = + connectSize(({ children, factor, map, passes }) => { + const rec = pass => + pass <= 0 + ? children + : + {rec(pass-1)} + ; + return rec(passes); + }); + +export default class Example extends Component { + render() { + const { factor, passes, map } = this.props; + return ( + + + {require("../../NjbLHx2.jpg")} + + + ); + } + static defaultProps = { + factor: 2, + passes: 4, + map: StaticBlurMap.images[0], + }; +} +import StaticBlurMap from "../../toolbox/StaticBlurMap"; diff --git a/cookbook-rn/src/examples/blurmap/meta.js b/cookbook-rn/src/examples/blurmap/meta.js new file mode 100644 index 0000000..9dab93f --- /dev/null +++ b/cookbook-rn/src/examples/blurmap/meta.js @@ -0,0 +1,14 @@ +import StaticBlurMap from "../../toolbox/StaticBlurMap"; +import makeFloatSlider from "../../toolbox/makeFloatSlider"; +export const title = "Blur with intensity map & multi-pass"; +export const toolbox = [ + { prop: "factor", + title: "Blur", + Editor: makeFloatSlider(0, 8, 0.2) }, + { prop: "passes", + title: value => `Blur Passes (${value})`, + Editor: makeFloatSlider(1, 8, 1) }, + { prop: "map", + title: "Blur Texture Map", + Editor: StaticBlurMap }, +]; diff --git a/cookbook-rn/src/examples/blurmapdyn/index.js b/cookbook-rn/src/examples/blurmapdyn/index.js new file mode 100644 index 0000000..8adc89b --- /dev/null +++ b/cookbook-rn/src/examples/blurmapdyn/index.js @@ -0,0 +1,46 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, Bus, GLSL } from "gl-react"; +import { Surface } from "gl-react-native"; +import {BlurV} from "../blurmap"; +import timeLoop from "../../HOC/timeLoop"; + +const shaders = Shaders.create({ + ConicalGradiant: { + frag: GLSL`precision highp float; +varying vec2 uv; +uniform float phase; +const float PI = 3.14159; +void main () { + gl_FragColor = vec4(vec3( + mod(phase + atan(uv.x-0.5, uv.y-0.5)/(2.0*PI), 1.0) + ), 1.0); +}` } +}); + +const ConicalGradiantLoop = timeLoop(({ time }) => + ); + +export default class Example extends Component { + render() { + const { factor, passes } = this.props; + // also needs to be computed once. + return ( + + + + + this.refs.blurMapBus} passes={passes} factor={factor}> + {require("../../NjbLHx2.jpg")} + + + ); + } + static defaultProps = { + factor: 6, + passes: 4, + }; +} diff --git a/cookbook-rn/src/examples/blurmapdyn/meta.js b/cookbook-rn/src/examples/blurmapdyn/meta.js new file mode 100644 index 0000000..fa59c05 --- /dev/null +++ b/cookbook-rn/src/examples/blurmapdyn/meta.js @@ -0,0 +1,11 @@ +import makeFloatSlider from "../../toolbox/makeFloatSlider"; +export const title = "Blur with dynamic shader mapping"; +export const desc = "Any arbitrary shader can be used as a blur map!"; +export const toolbox = [ + { prop: "factor", + title: "Blur", + Editor: makeFloatSlider(0, 8, 0.2) }, + { prop: "passes", + title: value => `Blur Passes (${value})`, + Editor: makeFloatSlider(1, 8, 1) }, +]; diff --git a/cookbook-rn/src/examples/blurmapmouse/index.js b/cookbook-rn/src/examples/blurmapmouse/index.js new file mode 100644 index 0000000..5434025 --- /dev/null +++ b/cookbook-rn/src/examples/blurmapmouse/index.js @@ -0,0 +1,61 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, Bus, GLSL } from "gl-react"; +import { Surface } from "gl-react-native"; +import {BlurV} from "../blurmap"; +import respondToTouchPosition from "../../HOC/respondToTouchPosition"; + +const shaders = Shaders.create({ + Offset: { + frag: GLSL`precision highp float; +varying vec2 uv; +uniform sampler2D t; +uniform vec2 offset; +void main () { + gl_FragColor = texture2D(t, uv + offset); +}` + } +}); + +const Offset = ({ t, offset }) => + ; + +const Example = respondToTouchPosition(class Example extends Component { + render() { + const { map, touching, touchPosition } = this.props; +// Sharing computation of a GL Node. +// should not be passed straight to BlurV's map because +// it would duplicates it in the tree ([passes] times) +// Instead, we need to express a graph and share the +// computation with a Bus ref. +// We pass to BlurV's map prop a function that resolve that ref. + return ( + + + + + this.refs.blurMapBus} passes={6} factor={6}> + {require("../../NjbLHx2.jpg")} + + + ); + } +}); + +Example.defaultProps = { + map: StaticBlurMap.images[0], +}; + +export default Example; + +import StaticBlurMap from "../../toolbox/StaticBlurMap"; diff --git a/cookbook-rn/src/examples/blurmapmouse/meta.js b/cookbook-rn/src/examples/blurmapmouse/meta.js new file mode 100644 index 0000000..a1541c9 --- /dev/null +++ b/cookbook-rn/src/examples/blurmapmouse/meta.js @@ -0,0 +1,8 @@ +import StaticBlurMap from "../../toolbox/StaticBlurMap"; +export const title = "Blur map and Mouse position"; +export const description = "Dynamically change Blur Map with touch move"; +export const toolbox = [ + { prop: "map", + title: "Blur Texture Map", + Editor: StaticBlurMap }, +]; diff --git a/cookbook-rn/src/examples/blurmulti/index.js b/cookbook-rn/src/examples/blurmulti/index.js new file mode 100644 index 0000000..9b1ecce --- /dev/null +++ b/cookbook-rn/src/examples/blurmulti/index.js @@ -0,0 +1,45 @@ +//@flow +import React, { Component } from "react"; +import { connectSize } from "gl-react"; +import { Surface } from "gl-react-native"; +import { Blur1D } from "../blurxy"; + +// empirical strategy to chose a 2d vector for a blur pass +const NORM = Math.sqrt(2)/2; +export const directionForPass = (p: number, factor: number, total: number) => { + const f = factor * 2 * Math.ceil(p / 2) / total; + switch ((p-1) % 4) { // alternate horizontal, vertical and 2 diagonals + case 0: return [f,0]; + case 1: return [0,f]; + case 2: return [f*NORM,f*NORM]; + default: return [f*NORM,-f*NORM]; + } +} + +// recursively apply Blur1D to make a multi pass Blur component +export const Blur = connectSize(({ children, factor, passes }) => { + const rec = pass => + pass <= 0 + ? children + : + {rec(pass-1)} + ; + return rec(passes); +}); + +export default class Example extends Component { + render() { + const { factor, passes } = this.props; + return ( + + + {require("../../iPKTONG.jpg")} + + + ); + } + static defaultProps = { + factor: 2, + passes: 4, + }; +} diff --git a/cookbook-rn/src/examples/blurmulti/meta.js b/cookbook-rn/src/examples/blurmulti/meta.js new file mode 100644 index 0000000..a29154d --- /dev/null +++ b/cookbook-rn/src/examples/blurmulti/meta.js @@ -0,0 +1,11 @@ +import makeFloatSlider from "../../toolbox/makeFloatSlider"; +export const title = "multi-pass Blur"; + +export const toolbox = [ + { prop: "factor", + title: "Blur", + Editor: makeFloatSlider(0, 8, 0.2) }, + { prop: "passes", + title: passes => `Blur Passes (${passes})`, + Editor: makeFloatSlider(0, 8, 1) }, +]; diff --git a/cookbook-rn/src/examples/blurxy/index.js b/cookbook-rn/src/examples/blurxy/index.js new file mode 100644 index 0000000..2fae448 --- /dev/null +++ b/cookbook-rn/src/examples/blurxy/index.js @@ -0,0 +1,61 @@ +//@flow +import React, { Component } from "react"; +import {Shaders, Node, GLSL, connectSize} from "gl-react"; +import { Surface } from "gl-react-native"; + +const shaders = Shaders.create({ + blur1D: { // blur9: from https://github.com/Jam3/glsl-fast-gaussian-blur + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D t; +uniform vec2 direction, resolution; +vec4 blur9(sampler2D image, vec2 uv, vec2 resolution, vec2 direction) { + vec4 color = vec4(0.0); + vec2 off1 = vec2(1.3846153846) * direction; + vec2 off2 = vec2(3.2307692308) * direction; + color += texture2D(image, uv) * 0.2270270270; + color += texture2D(image, uv + (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv - (off1 / resolution)) * 0.3162162162; + color += texture2D(image, uv + (off2 / resolution)) * 0.0702702703; + color += texture2D(image, uv - (off2 / resolution)) * 0.0702702703; + return color; +} +void main() { + gl_FragColor = blur9(t, uv, resolution, direction); +}` } +}); + +// This implements a blur on a single direction (x or y axis for instance) +// connectSize will inject for us the width/height from context if not provided +export const Blur1D = + connectSize(({ children: t, direction, width, height }) => + ); + +// BlurXY is a basic blur that apply Blur1D on Y and then on X +export const BlurXY = + connectSize(({ factor, children }) => + + + {children} + + ); + +export default class Example extends Component { + render() { + const { factor } = this.props; + return ( + + + {require("../../iPKTONG.jpg")} + + + ); + } + static defaultProps = { + factor: 1, + }; +} diff --git a/cookbook-rn/src/examples/blurxy/meta.js b/cookbook-rn/src/examples/blurxy/meta.js new file mode 100644 index 0000000..48fcec6 --- /dev/null +++ b/cookbook-rn/src/examples/blurxy/meta.js @@ -0,0 +1,8 @@ +import makeFloatSlider from "../../toolbox/makeFloatSlider"; +export const title = "simple Blur (2-passes)"; + +export const toolbox = [ + { prop: "factor", + title: "Blur", + Editor: makeFloatSlider(0, 8, 0.2) }, +]; diff --git a/cookbook-rn/src/examples/blurxydownscale/index.js b/cookbook-rn/src/examples/blurxydownscale/index.js new file mode 100644 index 0000000..bdc9b7a --- /dev/null +++ b/cookbook-rn/src/examples/blurxydownscale/index.js @@ -0,0 +1,24 @@ +//@flow +import React, { Component } from "react"; +import {LinearCopy} from "gl-react"; +import { Surface } from "gl-react-native"; +import {BlurXY} from "../blurxy"; + +export default class Example extends Component { + render() { + const { factor } = this.props; + return ( + + + + {require("../../iPKTONG.jpg")} + + + +// we have to wrap this in a so it upscales to the Surface size. + ); + } + static defaultProps = { + factor: 0.5, + }; +} diff --git a/cookbook-rn/src/examples/blurxydownscale/meta.js b/cookbook-rn/src/examples/blurxydownscale/meta.js new file mode 100644 index 0000000..26c4302 --- /dev/null +++ b/cookbook-rn/src/examples/blurxydownscale/meta.js @@ -0,0 +1,8 @@ +import makeFloatSlider from "../../toolbox/makeFloatSlider"; +export const title = "simple Blur + downscale"; + +export const toolbox = [ + { prop: "factor", + title: "Blur", + Editor: makeFloatSlider(0.2, 0.8, 0.02) }, +]; diff --git a/cookbook-rn/src/examples/colordisc/index.js b/cookbook-rn/src/examples/colordisc/index.js new file mode 100644 index 0000000..691c316 --- /dev/null +++ b/cookbook-rn/src/examples/colordisc/index.js @@ -0,0 +1,47 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, GLSL } from "gl-react"; +import { Surface } from "gl-react-native"; +const shaders = Shaders.create({ + ColoredDisc: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform vec3 fromColor, toColor; +void main() { + float d = 2.0 * distance(uv, vec2(0.5)); + gl_FragColor = mix( + vec4(mix(fromColor, toColor, d), 1.0), + vec4(0.0), + step(1.0, d) + ); +}` } +}); + +class ColoredDisc extends Component { + render() { + // fromColor/toColor must be array of 3 numbers because defined as vec3 type. + const { fromColor, toColor } = this.props; + return ( + + ); + } +} + +export default class Example extends Component { + render() { + const { fromColor, toColor } = this.props; + return ( + + + + ); + } + static defaultProps = { + fromColor: [ 1, 0, 1 ], + toColor: [ 1, 1, 0 ], + }; +} diff --git a/cookbook-rn/src/examples/colordisc/meta.js b/cookbook-rn/src/examples/colordisc/meta.js new file mode 100644 index 0000000..bf02d53 --- /dev/null +++ b/cookbook-rn/src/examples/colordisc/meta.js @@ -0,0 +1,9 @@ +import Vec3ColorPicker from "../../toolbox/Vec3ColorPicker"; +export const toolbox = [ + { prop: "fromColor", title: "fromColor", Editor: Vec3ColorPicker }, + { prop: "toColor", title: "toColor", Editor: Vec3ColorPicker }, +]; + +export const title = "Colored Disc"; + +export const description = "Implement a simple radial gradient with {fromColor, toColor} uniforms."; diff --git a/cookbook-rn/src/examples/colorscale/colorScales.js b/cookbook-rn/src/examples/colorscale/colorScales.js new file mode 100644 index 0000000..07119de --- /dev/null +++ b/cookbook-rn/src/examples/colorscale/colorScales.js @@ -0,0 +1,58 @@ +const ndarray = require("ndarray"); + +module.exports = { + heatmap: ndarray(new Float64Array([ + 1, 0, 0, + 1, 0.6, 0, + 0.4, 1, 0.4, + 0.1, 0.7, 1, + 0, 0, 1, + ]), [5, 1, 3]).step(-1, 1, 1), + + monochrome: ndarray(new Float64Array([ + 1, 1, 1, + 0.1, 0.2, 0.3, + ]), [2, 1, 3]).step(-1, 1, 1), + + opacity: ndarray(new Float64Array([ + 0, + 1, + ]), [2, 1, 1]).step(-1, 1, 1), // see gl-texture2d rule: https://github.com/stackgl/gl-texture2d#var-tex--createtexturegl-array + + // from https://github.com/gka/chroma.js/blob/master/src/colors/colorbrewer.coffee + OrRd: ndarray(new Float64Array([1.00,0.97,0.93,1.00,0.91,0.78,0.99,0.83,0.62,0.99,0.73,0.52,0.99,0.55,0.35,0.94,0.40,0.28,0.84,0.19,0.12,0.70,0.00,0.00,0.50,0.00,0.00]), [9,1,3]).step(-1, 1, 1), + PuBu: ndarray(new Float64Array([1.00,0.97,0.98,0.93,0.91,0.95,0.82,0.82,0.90,0.65,0.74,0.86,0.45,0.66,0.81,0.21,0.56,0.75,0.02,0.44,0.69,0.02,0.35,0.55,0.01,0.22,0.35]), [9,1,3]).step(-1, 1, 1), + BuPu: ndarray(new Float64Array([0.97,0.99,0.99,0.88,0.93,0.96,0.75,0.83,0.90,0.62,0.74,0.85,0.55,0.59,0.78,0.55,0.42,0.69,0.53,0.25,0.62,0.51,0.06,0.49,0.30,0.00,0.29]), [9,1,3]).step(-1, 1, 1), + Oranges: ndarray(new Float64Array([1.00,0.96,0.92,1.00,0.90,0.81,0.99,0.82,0.64,0.99,0.68,0.42,0.99,0.55,0.24,0.95,0.41,0.07,0.85,0.28,0.00,0.65,0.21,0.01,0.50,0.15,0.02]), [9,1,3]).step(-1, 1, 1), + BuGn: ndarray(new Float64Array([0.97,0.99,0.99,0.90,0.96,0.98,0.80,0.93,0.90,0.60,0.85,0.79,0.40,0.76,0.64,0.25,0.68,0.46,0.14,0.55,0.27,0.00,0.43,0.17,0.00,0.27,0.11]), [9,1,3]).step(-1, 1, 1), + YlOrBr: ndarray(new Float64Array([1.00,1.00,0.90,1.00,0.97,0.74,1.00,0.89,0.57,1.00,0.77,0.31,1.00,0.60,0.16,0.93,0.44,0.08,0.80,0.30,0.01,0.60,0.20,0.02,0.40,0.15,0.02]), [9,1,3]).step(-1, 1, 1), + YlGn: ndarray(new Float64Array([1.00,1.00,0.90,0.97,0.99,0.73,0.85,0.94,0.64,0.68,0.87,0.56,0.47,0.78,0.47,0.25,0.67,0.36,0.14,0.52,0.26,0.00,0.41,0.22,0.00,0.27,0.16]), [9,1,3]).step(-1, 1, 1), + Reds: ndarray(new Float64Array([1.00,0.96,0.94,1.00,0.88,0.82,0.99,0.73,0.63,0.99,0.57,0.45,0.98,0.42,0.29,0.94,0.23,0.17,0.80,0.09,0.11,0.65,0.06,0.08,0.40,0.00,0.05]), [9,1,3]).step(-1, 1, 1), + RdPu: ndarray(new Float64Array([1.00,0.97,0.95,0.99,0.88,0.87,0.99,0.77,0.75,0.98,0.62,0.71,0.97,0.41,0.63,0.87,0.20,0.59,0.68,0.00,0.49,0.48,0.00,0.47,0.29,0.00,0.42]), [9,1,3]).step(-1, 1, 1), + Greens: ndarray(new Float64Array([0.97,0.99,0.96,0.90,0.96,0.88,0.78,0.91,0.75,0.63,0.85,0.61,0.45,0.77,0.46,0.25,0.67,0.36,0.14,0.55,0.27,0.00,0.43,0.17,0.00,0.27,0.11]), [9,1,3]).step(-1, 1, 1), + YlGnBu: ndarray(new Float64Array([1.00,1.00,0.85,0.93,0.97,0.69,0.78,0.91,0.71,0.50,0.80,0.73,0.25,0.71,0.77,0.11,0.57,0.75,0.13,0.37,0.66,0.15,0.20,0.58,0.03,0.11,0.35]), [9,1,3]).step(-1, 1, 1), + Purples: ndarray(new Float64Array([0.99,0.98,0.99,0.94,0.93,0.96,0.85,0.85,0.92,0.74,0.74,0.86,0.62,0.60,0.78,0.50,0.49,0.73,0.42,0.32,0.64,0.33,0.15,0.56,0.25,0.00,0.49]), [9,1,3]).step(-1, 1, 1), + GnBu: ndarray(new Float64Array([0.97,0.99,0.94,0.88,0.95,0.86,0.80,0.92,0.77,0.66,0.87,0.71,0.48,0.80,0.77,0.31,0.70,0.83,0.17,0.55,0.75,0.03,0.41,0.67,0.03,0.25,0.51]), [9,1,3]).step(-1, 1, 1), + Greys: ndarray(new Float64Array([1.00,1.00,1.00,0.94,0.94,0.94,0.85,0.85,0.85,0.74,0.74,0.74,0.59,0.59,0.59,0.45,0.45,0.45,0.32,0.32,0.32,0.15,0.15,0.15,0.00,0.00,0.00]), [9,1,3]).step(-1, 1, 1), + YlOrRd: ndarray(new Float64Array([1.00,1.00,0.80,1.00,0.93,0.63,1.00,0.85,0.46,1.00,0.70,0.30,0.99,0.55,0.24,0.99,0.31,0.16,0.89,0.10,0.11,0.74,0.00,0.15,0.50,0.00,0.15]), [9,1,3]).step(-1, 1, 1), + PuRd: ndarray(new Float64Array([0.97,0.96,0.98,0.91,0.88,0.94,0.83,0.73,0.85,0.79,0.58,0.78,0.87,0.40,0.69,0.91,0.16,0.54,0.81,0.07,0.34,0.60,0.00,0.26,0.40,0.00,0.12]), [9,1,3]).step(-1, 1, 1), + Blues: ndarray(new Float64Array([0.97,0.98,1.00,0.87,0.92,0.97,0.78,0.86,0.94,0.62,0.79,0.88,0.42,0.68,0.84,0.26,0.57,0.78,0.13,0.44,0.71,0.03,0.32,0.61,0.03,0.19,0.42]), [9,1,3]).step(-1, 1, 1), + PuBuGn: ndarray(new Float64Array([1.00,0.97,0.98,0.93,0.89,0.94,0.82,0.82,0.90,0.65,0.74,0.86,0.40,0.66,0.81,0.21,0.56,0.75,0.01,0.51,0.54,0.00,0.42,0.35,0.00,0.27,0.21]), [9,1,3]).step(-1, 1, 1), + Spectral: ndarray(new Float64Array([0.62,0.00,0.26,0.84,0.24,0.31,0.96,0.43,0.26,0.99,0.68,0.38,1.00,0.88,0.55,1.00,1.00,0.75,0.90,0.96,0.60,0.67,0.87,0.64,0.40,0.76,0.65,0.20,0.53,0.74,0.37,0.31,0.64]), [11,1,3]).step(-1, 1, 1), + RdYlGn: ndarray(new Float64Array([0.65,0.00,0.15,0.84,0.19,0.15,0.96,0.43,0.26,0.99,0.68,0.38,1.00,0.88,0.55,1.00,1.00,0.75,0.85,0.94,0.55,0.65,0.85,0.42,0.40,0.74,0.39,0.10,0.60,0.31,0.00,0.41,0.22]), [11,1,3]).step(-1, 1, 1), + RdBu: ndarray(new Float64Array([0.40,0.00,0.12,0.70,0.09,0.17,0.84,0.38,0.30,0.96,0.65,0.51,0.99,0.86,0.78,0.97,0.97,0.97,0.82,0.90,0.94,0.57,0.77,0.87,0.26,0.58,0.76,0.13,0.40,0.67,0.02,0.19,0.38]), [11,1,3]).step(-1, 1, 1), + PiYG: ndarray(new Float64Array([0.56,0.00,0.32,0.77,0.11,0.49,0.87,0.47,0.68,0.95,0.71,0.85,0.99,0.88,0.94,0.97,0.97,0.97,0.90,0.96,0.82,0.72,0.88,0.53,0.50,0.74,0.25,0.30,0.57,0.13,0.15,0.39,0.10]), [11,1,3]).step(-1, 1, 1), + PRGn: ndarray(new Float64Array([0.25,0.00,0.29,0.46,0.16,0.51,0.60,0.44,0.67,0.76,0.65,0.81,0.91,0.83,0.91,0.97,0.97,0.97,0.85,0.94,0.83,0.65,0.86,0.63,0.35,0.68,0.38,0.11,0.47,0.22,0.00,0.27,0.11]), [11,1,3]).step(-1, 1, 1), + RdYlBu: ndarray(new Float64Array([0.65,0.00,0.15,0.84,0.19,0.15,0.96,0.43,0.26,0.99,0.68,0.38,1.00,0.88,0.56,1.00,1.00,0.75,0.88,0.95,0.97,0.67,0.85,0.91,0.45,0.68,0.82,0.27,0.46,0.71,0.19,0.21,0.58]), [11,1,3]).step(-1, 1, 1), + BrBG: ndarray(new Float64Array([0.33,0.19,0.02,0.55,0.32,0.04,0.75,0.51,0.18,0.87,0.76,0.49,0.96,0.91,0.76,0.96,0.96,0.96,0.78,0.92,0.90,0.50,0.80,0.76,0.21,0.59,0.56,0.00,0.40,0.37,0.00,0.24,0.19]), [11,1,3]).step(-1, 1, 1), + RdGy: ndarray(new Float64Array([0.40,0.00,0.12,0.70,0.09,0.17,0.84,0.38,0.30,0.96,0.65,0.51,0.99,0.86,0.78,1.00,1.00,1.00,0.88,0.88,0.88,0.73,0.73,0.73,0.53,0.53,0.53,0.30,0.30,0.30,0.10,0.10,0.10]), [11,1,3]).step(-1, 1, 1), + PuOr: ndarray(new Float64Array([0.50,0.23,0.03,0.70,0.35,0.02,0.88,0.51,0.08,0.99,0.72,0.39,1.00,0.88,0.71,0.97,0.97,0.97,0.85,0.85,0.92,0.70,0.67,0.82,0.50,0.45,0.67,0.33,0.15,0.53,0.18,0.00,0.29]), [11,1,3]).step(-1, 1, 1), + Set2: ndarray(new Float64Array([0.40,0.76,0.65,0.99,0.55,0.38,0.55,0.63,0.80,0.91,0.54,0.76,0.65,0.85,0.33,1.00,0.85,0.18,0.90,0.77,0.58,0.70,0.70,0.70]), [8,1,3]).step(-1, 1, 1), + Accent: ndarray(new Float64Array([0.50,0.79,0.50,0.75,0.68,0.83,0.99,0.75,0.53,1.00,1.00,0.60,0.22,0.42,0.69,0.94,0.01,0.50,0.75,0.36,0.09,0.40,0.40,0.40]), [8,1,3]).step(-1, 1, 1), + Set1: ndarray(new Float64Array([0.89,0.10,0.11,0.22,0.49,0.72,0.30,0.69,0.29,0.60,0.31,0.64,1.00,0.50,0.00,1.00,1.00,0.20,0.65,0.34,0.16,0.97,0.51,0.75,0.60,0.60,0.60]), [9,1,3]).step(-1, 1, 1), + Set3: ndarray(new Float64Array([0.55,0.83,0.78,1.00,1.00,0.70,0.75,0.73,0.85,0.98,0.50,0.45,0.50,0.69,0.83,0.99,0.71,0.38,0.70,0.87,0.41,0.99,0.80,0.90,0.85,0.85,0.85,0.74,0.50,0.74,0.80,0.92,0.77,1.00,0.93,0.44]), [12,1,3]).step(-1, 1, 1), + Dark2: ndarray(new Float64Array([0.11,0.62,0.47,0.85,0.37,0.01,0.46,0.44,0.70,0.91,0.16,0.54,0.40,0.65,0.12,0.90,0.67,0.01,0.65,0.46,0.11,0.40,0.40,0.40]), [8,1,3]).step(-1, 1, 1), + Paired: ndarray(new Float64Array([0.65,0.81,0.89,0.12,0.47,0.71,0.70,0.87,0.54,0.20,0.63,0.17,0.98,0.60,0.60,0.89,0.10,0.11,0.99,0.75,0.44,1.00,0.50,0.00,0.79,0.70,0.84,0.42,0.24,0.60,1.00,1.00,0.60,0.69,0.35,0.16]), [12,1,3]).step(-1, 1, 1), + Pastel2: ndarray(new Float64Array([0.70,0.89,0.80,0.99,0.80,0.67,0.80,0.84,0.91,0.96,0.79,0.89,0.90,0.96,0.79,1.00,0.95,0.68,0.95,0.89,0.80,0.80,0.80,0.80]), [8,1,3]).step(-1, 1, 1), + Pastel1: ndarray(new Float64Array([0.98,0.71,0.68,0.70,0.80,0.89,0.80,0.92,0.77,0.87,0.80,0.89,1.00,0.85,0.65,1.00,1.00,0.80,0.90,0.85,0.74,0.99,0.85,0.93,0.95,0.95,0.95]), [9,1,3]).step(-1, 1, 1), +}; diff --git a/cookbook-rn/src/examples/colorscale/index.js b/cookbook-rn/src/examples/colorscale/index.js new file mode 100644 index 0000000..674aa45 --- /dev/null +++ b/cookbook-rn/src/examples/colorscale/index.js @@ -0,0 +1,44 @@ +//@flow +import React, { Component } from "react"; +import { Shaders, Node, GLSL } from "gl-react"; +import { Surface } from "gl-react-native"; +import colorScales from "./colorScales"; export {colorScales}; + +const shaders = Shaders.create({ + colorify: { + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D children, colorScale; +float greyscale (vec3 c) { return 0.2125 * c.r + 0.7154 * c.g + 0.0721 * c.b; } +void main() { + vec4 original = texture2D(children, uv); + vec4 newcolor = texture2D(colorScale, vec2(greyscale(original.rgb), 0.5)); + gl_FragColor = vec4(newcolor.rgb, original.a * newcolor.a); +}` } +}); + +export const Colorify = +({ children, colorScale, interpolation }) => + ; + +export default class Example extends Component { + render() { + const { interpolation, color } = this.props; + return ( + + + {require("../../iPKTONG.jpg")} + + + ); + } + static defaultProps = { + interpolation: "linear", + color: Object.keys(colorScales)[0], + }; +} diff --git a/cookbook-rn/src/examples/colorscale/meta.js b/cookbook-rn/src/examples/colorscale/meta.js new file mode 100644 index 0000000..9709312 --- /dev/null +++ b/cookbook-rn/src/examples/colorscale/meta.js @@ -0,0 +1,27 @@ +import React from "react"; +import colorScales from "./colorScales"; +import makeSelect from "../../toolbox/makeSelect"; +import { LinearCopy, NearestCopy } from "gl-react"; +import { Surface } from "gl-react-native"; + +export const title = "color mapping with gradient texture"; +export const description = "A gradient texture defines the color mapping of the image greyscale."; + +export const toolbox = [ + { prop: "color", + title: "color scale", + Editor: makeSelect(Object.keys(colorScales).map(cs => + ({ key: cs, label: cs }))) }, + { prop: "interpolation", + Editor: makeSelect([ + { key: "linear", label: "linear interpolation" }, + { key: "nearest", label: "nearest interpolation" }, + ]) } +]; + +export const ToolboxFooter = ({ color, interpolation }) => + + { interpolation==="linear" + ? {colorScales[color]} + : {colorScales[color]} } + ; diff --git a/cookbook-rn/src/examples/demodesert/index.js b/cookbook-rn/src/examples/demodesert/index.js new file mode 100644 index 0000000..6e92926 --- /dev/null +++ b/cookbook-rn/src/examples/demodesert/index.js @@ -0,0 +1,226 @@ +//@flow +import React from "react"; +import { Shaders, Node, GLSL } from "gl-react"; +import { Surface } from "gl-react-native"; +import timeLoop from "../../HOC/timeLoop"; +import shadertoyTex17jpg from "./shadertoy-tex17.jpg"; + +const shaders = Shaders.create({ + desertPassage: { +// from https://www.shadertoy.com/view/XtyGzc + frag: GLSL` +precision mediump float; +varying vec2 uv; +uniform float iGlobalTime; +uniform sampler2D iChannel0; +#define FAR 80. +mat2 rot2( float th ){ vec2 a = sin(vec2(1.5707963, 0) + th); return mat2(a, -a.y, a.x); } +float hash( float n ){ return fract(cos(n)*45758.5453); } +float hash( vec3 p ){ return fract(sin(dot(p, vec3(7, 157, 113)))*45758.5453); } +float drawObject(in vec3 p){ p = fract(p)-.5; return dot(p, p); } +float cellTile(in vec3 p){ + vec4 d; + d.x = drawObject(p - vec3(.81, .62, .53)); + p.xy = vec2(p.y-p.x, p.y + p.x)*.7071; + d.y = drawObject(p - vec3(.39, .2, .11)); + p.yz = vec2(p.z-p.y, p.z + p.y)*.7071; + d.z = drawObject(p - vec3(.62, .24, .06)); + p.xz = vec2(p.z-p.x, p.z + p.x)*.7071; + d.w = drawObject(p - vec3(.2, .82, .64)); + d.xy = min(d.xz, d.yw); + return min(d.x, d.y)*2.66; +} +vec2 path(in float z){ return vec2(20.*sin(z * .04), 4.*cos(z * .09) + 3.*(sin(z*.025) - 1.)); } +float surfFunc(in vec3 p){ + float c = cellTile(p/6.); + return mix(c, cos(c*6.283*2.)*.5 + .5, .125); +} +float smin(float a, float b , float s){ + float h = clamp( 0.5 + 0.5*(b-a)/s, 0. , 1.); + return mix(b, a, h) - h*(1.0-h)*s; +} +float smax(float a, float b, float s){ + float h = clamp( 0.5 + 0.5*(a-b)/s, 0., 1.); + return mix(b, a, h) + h*(1.0-h)*s; +} +float map(vec3 p){ + float sf = surfFunc(p); + float cav = dot(cos(p*3.14159265/8.), sin(p.yzx*3.14159265/8.)) + 2.; + p.xy -= path(p.z); + float tun = 1.5 - length(p.xy*vec2(1, .4)); + tun = smax(tun, 1.-cav, 2.) + .75 + (.5-sf); + float gr = p.y + 7. - cav*.5 + (.5-sf)*.5; + float rf = p.y - 15.; + return smax(smin(tun, gr, .1), rf, 1.); +} +float trace(in vec3 ro, in vec3 rd){ + float t = 0., h; + for(int i=0; i<128; i++){ + h = map(ro+rd*t); + if(abs(h)<0.002*(t*.25 + 1.) || t>FAR) break; + t += h*.8; + } + return min(t, FAR); +} +vec3 normal(in vec3 p) +{ + vec2 e = vec2(-1., 1.)*0.001; + return normalize(e.yxx*map(p + e.yxx) + e.xxy*map(p + e.xxy) + + e.xyx*map(p + e.xyx) + e.yyy*map(p + e.yyy) ); +} +vec3 tex3D( sampler2D t, in vec3 p, in vec3 n ){ + n = max(abs(n) - .2, .001); + n /= (n.x + n.y + n.z ); + p = (texture2D(t, p.yz)*n.x + texture2D(t, p.zx)*n.y + texture2D(t, p.xy)*n.z).xyz; + return p*p; +} +vec3 doBumpMap( sampler2D tx, in vec3 p, in vec3 n, float bf){ + const vec2 e = vec2(0.001, 0); + mat3 m = mat3( tex3D(tx, p - e.xyy, n), tex3D(tx, p - e.yxy, n), tex3D(tx, p - e.yyx, n)); + vec3 g = vec3(0.299, 0.587, 0.114)*m; + g = (g - dot(tex3D(tx, p , n), vec3(0.299, 0.587, 0.114)) )/e.x; g -= n*dot(n, g); + return normalize( n + g*bf ); +} +float n3D(in vec3 p){ + const vec3 s = vec3(7, 157, 113); + vec3 ip = floor(p); p -= ip; + vec4 h = vec4(0., s.yz, s.y + s.z) + dot(ip, s); + p = p*p*(3. - 2.*p); + h = mix(fract(sin(h)*43758.5453), fract(sin(h + s.x)*43758.5453), p.x); + h.xy = mix(h.xz, h.yw, p.y); + return mix(h.x, h.y, p.z); +} +float bumpSurf3D( in vec3 p){ + float bmp = cellTile(p/3.)*.8 + cellTile(p)*.2; + float ns = n3D(p*6. - bmp*6.); + return mix(bmp, 1. - abs(ns-.333)/.667, .05); +} +vec3 doBumpMap(in vec3 p, in vec3 nor, float bumpfactor){ + const vec2 e = vec2(0.001, 0); + float ref = bumpSurf3D(p); + vec3 grad = (vec3(bumpSurf3D(p - e.xyy), + bumpSurf3D(p - e.yxy), + bumpSurf3D(p - e.yyx) )-ref)/e.x; + grad -= nor*dot(nor, grad); + return normalize( nor + grad*bumpfactor ); +} +float softShadow(in vec3 ro, in vec3 rd, in float start, in float end, in float k){ + float shade = 1.0; + const int maxIterationsShad = 10; + float dist = start; + float stepDist = end/float(maxIterationsShad); + for (int i=0; i end) break; + } + return min(max(shade, 0.) + .1, 1.); +} +float calculateAO( in vec3 p, in vec3 n) +{ + float ao = 0.0, l; + const float nbIte = 6.0; + const float maxDist = 3.; + for(float i=1.; i< nbIte+.5; i++ ){ + l = (i*.66 + hash(i)*.34)/nbIte*maxDist; + ao += (l - map( p + n*l ))/(1.+ l); + } + return clamp( 1.-ao/nbIte, 0., 1.); +} +vec3 getSky(){ return vec3(2., 1.4, .7); } +float trig3(in vec3 p){ + p = cos(p*2. + (cos(p.yzx) + 1.)*1.57); + return dot(p, vec3(0.1666)) + 0.5; +} +float trigNoise3D(in vec3 p){ + const mat3 m3RotTheta = mat3(0.25, -0.866, 0.433, 0.9665, 0.25, -0.2455127, -0.058, 0.433, 0.899519 )*1.5; + float res = 0.; + float t = trig3(p*3.14159265); + p += (t); + p = m3RotTheta*p; + res += t; + t = trig3(p*3.14159265); + p += (t)*0.7071; + p = m3RotTheta*p; + res += t*0.7071; + t = trig3(p*3.14159265); + res += t*0.5; + return res/2.2071; +} +float hash31(vec3 p){ return fract(sin(dot(p, vec3(127.1, 311.7, 74.7)))*43758.5453); } +float getMist(in vec3 ro, in vec3 rd, in vec3 lp, in float t){ + float mist = 0.; + ro += rd*t/3.; + for (int i = 0; i<3; i++){ + float sDi = length(lp-ro)/FAR; + float sAtt = 1./(1. + sDi*0.1 + sDi*sDi*0.01); + mist += trigNoise3D(ro/2.)*sAtt; + ro += rd*t/3.; + } + return clamp(mist/1.5 + hash31(ro)*0.1-0.05, 0., 1.); +} +void main() { + vec2 u = uv * 2.0 - 1.0; + vec3 ro = vec3(0, 0, iGlobalTime*8.); + vec3 lookAt = ro + vec3(0, 0, .5); + ro.xy += path(ro.z); + lookAt.xy += path(lookAt.z); + float FOV = 3.14159265/2.5; + vec3 forward = normalize(lookAt - ro); + vec3 right = normalize(vec3(forward.z, 0, -forward.x )); + vec3 up = cross(forward, right); + vec3 rd = normalize(forward + FOV*u.x*right + FOV*u.y*up); + rd.xy = rot2( path(lookAt.z).x/64. )*rd.xy; + vec3 lp = vec3(FAR*.5, FAR, FAR) + vec3(0, 0, ro.z); + float t = trace(ro, rd); + vec3 sky = getSky(); + vec3 col = sky; + vec3 sp = ro+t*rd; + float pathHeight = sp.y-path(sp.z).y; + if (t < FAR){ + vec3 sn = normal( sp ); + vec3 ld = lp-sp; + ld /= max(length(ld), 0.001); + const float tSize = 1./4.; + sn = doBumpMap(sp, sn, .75/(1. + t/FAR*.25)); + float bf = (pathHeight + 5. < 0.)? .05: .025; + sn = doBumpMap(iChannel0, sp*tSize, sn, bf/(1. + t/FAR)); + float shd = softShadow(sp, ld, 0.05, FAR, 8.); + float ao = calculateAO(sp, sn); + float dif = max( dot( ld, sn ), 0.0); + float spe = pow(max( dot( reflect(-ld, sn), -rd ), 0.0 ), 5.); + float fre = clamp(1.0 + dot(rd, sn), 0.0, 1.0); + float Schlick = pow( 1. - max(dot(rd, normalize(rd + ld)), 0.), 5.0); + float fre2 = mix(.2, 1., Schlick); + float amb = fre*fre2*.7 + .05; + col = clamp(mix(vec3(1.152, 0.4275,.153), vec3(.225, 0.05985, 0.0153), -sn.y*.5 + pathHeight*.5 + 1.75), vec3(.9, 0.534375, 0.239), vec3(.9, .855, .765)); + col = smoothstep(-.5, 1., tex3D(iChannel0, sp*tSize, sn)*2.)*(col + vec3(.225, .21375, .19125)); + col += smoothstep(0., 1., -pathHeight - 5.5)*fre*.25; + col += getSky()*fre*fre2; + col = (col*(dif + .1) + vec3(1)*fre2*spe)*shd*ao + amb*pow(col, vec3(2.)); + } + float dust = getMist(ro, rd, lp, t)*(1.-clamp((pathHeight - 5.)*.125, 0., 1.)); + sky = getSky()*mix(1., .75, dust); + col = mix(col, sky, min(t*t*1.5/FAR/FAR, 1.)); + u = uv; + col = min(col, 1.)*pow( 16.0*u.x*u.y*(1.0-u.x)*(1.0-u.y) , .125); + gl_FragColor = vec4(sqrt(clamp(col, 0., 1.)), 1); +}` } +}); + +const DesertPassage = ({ time }) => + + +export const DesertPassageLoop = timeLoop(DesertPassage, { frameRate: 30 }); + +export default () => + + + diff --git a/cookbook-rn/src/examples/demodesert/meta.js b/cookbook-rn/src/examples/demodesert/meta.js new file mode 100644 index 0000000..03aaaf6 --- /dev/null +++ b/cookbook-rn/src/examples/demodesert/meta.js @@ -0,0 +1 @@ +export const title = "a Shadertoy more advanced example"; diff --git a/cookbook-rn/src/examples/demodesert/shadertoy-tex17.jpg b/cookbook-rn/src/examples/demodesert/shadertoy-tex17.jpg new file mode 100644 index 0000000..cc0bfa7 Binary files /dev/null and b/cookbook-rn/src/examples/demodesert/shadertoy-tex17.jpg differ diff --git a/cookbook-rn/src/examples/demodesertcrt/index.js b/cookbook-rn/src/examples/demodesertcrt/index.js new file mode 100644 index 0000000..bdbd6d2 --- /dev/null +++ b/cookbook-rn/src/examples/demodesertcrt/index.js @@ -0,0 +1,160 @@ +//@flow +import React, { Component, PureComponent } from "react"; +import { View, Button } from "react-native"; +import {Shaders, Node, GLSL, Bus, connectSize} from "gl-react"; +import { Surface } from "gl-react-native"; +import {DesertPassageLoop} from "../demodesert"; + +const shaders = Shaders.create({ + crt: { +// adapted from http://bit.ly/2eR1iKi + frag: GLSL` +precision highp float; +varying vec2 uv; +uniform sampler2D rubyTexture; +uniform vec2 rubyInputSize; +uniform vec2 rubyOutputSize; +uniform vec2 rubyTextureSize; +uniform float distortion; +#define TEX2D(c) pow(texture2D(rubyTexture, (c)), vec4(inputGamma)) +#define FIX(c) max(abs(c), 1e-6); +#define PI 3.141592653589 +#define phase 0.0 +#define inputGamma 2.2 +#define outputGamma 2.5 +vec2 radialDistortion(vec2 coord) { + coord *= rubyTextureSize / rubyInputSize; + vec2 cc = coord - 0.5; + float dist = dot(cc, cc) * distortion; + return (coord + cc * (1.0 + dist) * dist) * rubyInputSize / rubyTextureSize; +} +vec4 scanlineWeights(float distance, vec4 color) +{ + vec4 wid = 2.0 + 2.0 * pow(color, vec4(4.0)); + vec4 weights = vec4(distance * 3.333333); + return 0.51 * exp(-pow(weights * sqrt(2.0 / wid), wid)) / (0.18 + 0.06 * wid); +} +void main() +{ + vec2 one = 1.0 / rubyTextureSize; + vec2 xy = radialDistortion(uv.xy); + vec2 uv_ratio = fract(xy * rubyTextureSize) - vec2(0.5); + xy = (floor(xy * rubyTextureSize) + vec2(0.5)) / rubyTextureSize; + vec4 coeffs = PI * vec4(1.0 + uv_ratio.x, uv_ratio.x, 1.0 - uv_ratio.x, 2.0 - uv_ratio.x); + coeffs = FIX(coeffs); + coeffs = 2.0 * sin(coeffs) * sin(coeffs / 2.0) / (coeffs * coeffs); + coeffs /= dot(coeffs, vec4(1.0)); + vec4 col = clamp(coeffs.x * TEX2D(xy + vec2(-one.x, 0.0)) + coeffs.y * TEX2D(xy) + coeffs.z * TEX2D(xy + vec2(one.x, 0.0)) + coeffs.w * TEX2D(xy + vec2(2.0 * one.x, 0.0)), 0.0, 1.0); + vec4 col2 = clamp(coeffs.x * TEX2D(xy + vec2(-one.x, one.y)) + coeffs.y * TEX2D(xy + vec2(0.0, one.y)) + coeffs.z * TEX2D(xy + one) + coeffs.w * TEX2D(xy + vec2(2.0 * one.x, one.y)), 0.0, 1.0); + vec4 weights = scanlineWeights(abs(uv_ratio.y) , col); + vec4 weights2 = scanlineWeights(1.0 - uv_ratio.y, col2); + vec3 mul_res = (col * weights + col2 * weights2).xyz; + float mod_factor = uv.x * rubyOutputSize.x * rubyTextureSize.x / rubyInputSize.x; + vec3 dotMaskWeights = mix( + vec3(1.05, 0.75, 1.05), + vec3(0.75, 1.05, 0.75), + floor(mod(mod_factor, 2.0)) + ); + mul_res *= dotMaskWeights; + mul_res = pow(mul_res, vec3(1.0 / (2.0 * inputGamma - outputGamma))); + gl_FragColor = vec4(mul_res, 1.0); +}` }, + copy: { + frag: GLSL` + precision highp float; + varying vec2 uv; + uniform sampler2D t; + void main(){ + gl_FragColor=texture2D(t,uv); + }`, + } +}); + +class CRT extends Component { + props: { + children?: any, + distortion: number, + inSize: [number, number], + outSize: [number, number], + texSize: [number, number], + }; + render() { + const { children, inSize, outSize, texSize, distortion } = this.props; + return ; + } +} + +const Desert = connectSize(DesertPassageLoop); + +class ShowCaptured extends PureComponent { + render() { + const {t} = this.props; + return + + ; + } +} + +export default class Example extends Component { + state = { + surfacePixels: null, + desertPixels: null, + }; + + onCapture = () => + this.setState({ + surfacePixels: this.refs.surface.capture(), + desertPixels: this.refs.desert.capture(), + }); + + render() { + const { distortion } = this.props; + const { surfacePixels, desertPixels } = this.state; + return ( + + + + {/* we use a Bus to have a ref for capture */} + + + + + {() => this.refs.desert} + + + + + +