port some examples into cookbook-rn

This commit is contained in:
Gaëtan Renaudeau 2016-12-26 16:50:51 +01:00
parent d0bc4db823
commit dc4ac09fe0
149 changed files with 4203 additions and 122 deletions

View File

@ -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",

View File

@ -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 (
<View style={styles.container}>
<HelloGL />
<HelloTexture />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
flexDirection: "column",
},
});
import App from "./src";
Exponent.registerRootComponent(App);

22
cookbook-rn/package.json Executable file → Normal file
View File

@ -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"
}
}

BIN
cookbook-rn/src/0BJobQo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
cookbook-rn/src/0PkQEk1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

BIN
cookbook-rn/src/0bUSEBX.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
cookbook-rn/src/2VP5osy.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

View File

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 155 KiB

BIN
cookbook-rn/src/CKlmtPs.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
cookbook-rn/src/G2Whuq3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
cookbook-rn/src/GQo1KWq.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

View File

@ -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 (
<View ref="root" {...this.panResponder.panHandlers}>
<Comp {...this.props} {...this.state} />
</View>
);
}
}
hoistNonReactStatics(TouchPositionResponding, Comp);
return TouchPositionResponding;
};

View File

@ -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 <C
{...this.props}
{...this.state}
/>;
}
};
hoistNonReactStatics(TL, C);
return TL;
}

99
cookbook-rn/src/Home.js Normal file
View File

@ -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: () =>
<View style={styles.title}>
<Text style={styles.titleText}>gl-react-native</Text>
</View>
},
};
props: {
navigator: *,
};
render() {
const {navigator} = this.props;
return (
<ScrollView style={styles.container} bounces={false}>
<View style={styles.subHeader}>
<Text style={styles.subHeaderText}>
a React Native library to write and compose WebGL shaders
</Text>
<View style={{ alignItems: "center", padding: 20 /* TMP it will be the home demo */ }}>
<Image
source={require("./logo.png")}
style={{ width: 200, height: 200 }}
/>
</View>
<Text style={styles.subHeaderText}>
Here is a collection of gl-react-native examples:
</Text>
</View>
<View style={styles.list}>
{Object.keys(examples).map(ex => {
const { title, description, Example } = examples[ex];
return <ListItem
key={ex}
title={title}
description={description||""}
disabled={!Example}
onPress={
Example
? () => navigator.push(Router.getRoute(ex))
: null
}
/>;
})}
</View>
</ScrollView>
);
}
}

BIN
cookbook-rn/src/IvpoR40.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -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 (
<TouchableOpacity style={styles.container} onPress={onPress} disabled={disabled}>
<Text style={[styles.title, disabled ? styles.titleDisabled : null ]}>{title}</Text>
<Text style={styles.description}>{description}</Text>
</TouchableOpacity>
);
}

BIN
cookbook-rn/src/MnOB9Le.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
cookbook-rn/src/NjbLHx2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 KiB

BIN
cookbook-rn/src/Otbz312.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

16
cookbook-rn/src/Router.js Normal file
View File

@ -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;
});

BIN
cookbook-rn/src/SzbbUvX.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
cookbook-rn/src/YqsZKgc.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@ -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 }) =>
<Blur1D
width={1}
height={1}
resolution={[ 1, 1 ]}
direction={[ 0, 0.1 ]}>
<Blur1D
width={1}
height={quality}
resolution={[ 1, quality ]}
direction={[ 0.1, 0 ]}>
{children}
</Blur1D>
</Blur1D>;
const TitleBlurMap = ({ children: title, threshold }) =>
<Node
shader={shaders.TitleBlurMap}
uniforms={{
threshold,
t:
<Blur factor={4} passes={4} width={200} height={200}>
{title}
</Blur>
}}
width={64}
height={64}
/>;
class Title extends PureComponent {
render () {
const { children, width, height } = this.props;
return <LinearCopy><JSON2D width={width} height={height}>{{
size: [ width, height ],
background: "#fff",
draws: [
{
"font": "bold 78px Didot,Georgia,serif",
"fillStyle": "#000",
"textBaseline": "top",
"textAlign": "center"
},
[ "fillText", children, width/2, 10, 84 ],
]
}}</JSON2D></LinearCopy>;
}
}
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 (
<Node
shader={shaders.ImageTitle}
uniforms={{
colorThreshold,
img,
imgBlurred:() => this.refs.imgBlurred,
title:() => this.refs.title,
imgTone:() => this.refs.imgTone,
blurMap:() => this.refs.blurMap,
}}>
<Bus ref="title">
<Title width={width} height={height}>
{text}
</Title>
</Bus>
<Bus ref="blurMap">
<TitleBlurMap threshold={0.7}>
{() => this.refs.title}
</TitleBlurMap>
</Bus>
<Bus ref="imgTone">
<AveragePixels quality={8}>
{img}
</AveragePixels>
</Bus>
<Bus ref="imgBlurred">
<BlurV
map={() => this.refs.blurMap}
factor={4}
passes={4}>
{img}
</BlurV>
</Bus>
</Node>
);
}
}
export default class Example extends Component {
render() {
const { title, colorThreshold, image } = this.props;
return (
<Surface width={450} height={300}>
<ImageTitle text={title} colorThreshold={colorThreshold}>
{image}
</ImageTitle>
</Surface>
);
}
static defaultProps = {
title: "Hello\nSan Francisco\n☻",
colorThreshold: 0.6,
image: require("./sf-6.jpg"),
};
}

View File

@ -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"),
] },
];

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 401 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

View File

@ -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 <Node shader={shaders.cursor} uniforms={{ style: [ x, y ] }} />;
}
}
// 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 (
<Surface width={300} height={300}>
<AnimatedCursor {...this.state} />
</Surface>
);
}
});

View File

@ -0,0 +1 @@
export const title = "Cursor spring effect with animated";

View File

@ -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 }) =>
<Node
shader={shaders.blurV1D}
uniforms={{ t, map, resolution: [ width, height ], direction }}
/>);
// And its N-pass version
import {directionForPass} from "../blurmulti";
export const BlurV =
connectSize(({ children, factor, map, passes }) => {
const rec = pass =>
pass <= 0
? children
: <BlurV1D map={map} direction={directionForPass(pass, factor, passes)}>
{rec(pass-1)}
</BlurV1D>;
return rec(passes);
});
export default class Example extends Component {
render() {
const { factor, passes, map } = this.props;
return (
<Surface width={600} height={284}>
<BlurV map={map} passes={passes} factor={factor}>
{require("../../NjbLHx2.jpg")}
</BlurV>
</Surface>
);
}
static defaultProps = {
factor: 2,
passes: 4,
map: StaticBlurMap.images[0],
};
}
import StaticBlurMap from "../../toolbox/StaticBlurMap";

View File

@ -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 },
];

View File

@ -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 }) =>
<Node
shader={shaders.ConicalGradiant}
uniforms={{ phase: time/3000 }}
/>);
export default class Example extends Component {
render() {
const { factor, passes } = this.props;
// <ConicalGradiant/> also needs to be computed once.
return (
<Surface width={600} height={284}>
<Bus ref="blurMapBus">
<ConicalGradiantLoop />
</Bus>
<BlurV map={() => this.refs.blurMapBus} passes={passes} factor={factor}>
{require("../../NjbLHx2.jpg")}
</BlurV>
</Surface>
);
}
static defaultProps = {
factor: 6,
passes: 4,
};
}

View File

@ -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) },
];

View File

@ -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 }) =>
<Node shader={shaders.Offset} uniforms={{ t, offset }} />;
const Example = respondToTouchPosition(class Example extends Component {
render() {
const { map, touching, touchPosition } = this.props;
// Sharing computation of a GL Node.
// <Offset /> 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 (
<Surface width={300} height={142}>
<Bus ref="blurMapBus">
<Offset
offset={
touching
? [
touchPosition.x - 0.5,
touchPosition.y - 0.5
]
: [ 0, 0 ]
}
t={map}
/>
</Bus>
<BlurV map={() => this.refs.blurMapBus} passes={6} factor={6}>
{require("../../NjbLHx2.jpg")}
</BlurV>
</Surface>
);
}
});
Example.defaultProps = {
map: StaticBlurMap.images[0],
};
export default Example;
import StaticBlurMap from "../../toolbox/StaticBlurMap";

View File

@ -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 },
];

View File

@ -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
: <Blur1D direction={directionForPass(pass, factor, passes)}>
{rec(pass-1)}
</Blur1D>;
return rec(passes);
});
export default class Example extends Component {
render() {
const { factor, passes } = this.props;
return (
<Surface width={400} height={300}>
<Blur passes={passes} factor={factor}>
{require("../../iPKTONG.jpg")}
</Blur>
</Surface>
);
}
static defaultProps = {
factor: 2,
passes: 4,
};
}

View File

@ -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) },
];

View File

@ -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 }) =>
<Node
shader={shaders.blur1D}
uniforms={{ t, resolution: [ width, height ], direction }}
/>);
// BlurXY is a basic blur that apply Blur1D on Y and then on X
export const BlurXY =
connectSize(({ factor, children }) =>
<Blur1D direction={[ factor, 0 ]}>
<Blur1D direction={[ 0, factor ]}>
{children}
</Blur1D>
</Blur1D>);
export default class Example extends Component {
render() {
const { factor } = this.props;
return (
<Surface width={400} height={300}>
<BlurXY factor={factor}>
{require("../../iPKTONG.jpg")}
</BlurXY>
</Surface>
);
}
static defaultProps = {
factor: 1,
};
}

View File

@ -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) },
];

View File

@ -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 (
<Surface width={400} height={300}>
<LinearCopy>
<BlurXY factor={factor} width={100} height={75}>
{require("../../iPKTONG.jpg")}
</BlurXY>
</LinearCopy>
</Surface>
// we have to wrap this in a <LinearCopy> so it upscales to the Surface size.
);
}
static defaultProps = {
factor: 0.5,
};
}

View File

@ -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) },
];

View File

@ -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 (
<Node
shader={shaders.ColoredDisc}
uniforms={{ fromColor, toColor }}
/>
);
}
}
export default class Example extends Component {
render() {
const { fromColor, toColor } = this.props;
return (
<Surface width={300} height={300}>
<ColoredDisc fromColor={fromColor} toColor={toColor} />
</Surface>
);
}
static defaultProps = {
fromColor: [ 1, 0, 1 ],
toColor: [ 1, 1, 0 ],
};
}

View File

@ -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.";

View File

@ -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),
};

View File

@ -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 }) =>
<Node
shader={shaders.colorify}
uniformsOptions={{ colorScale: { interpolation } }}
uniforms={{ colorScale, children }}
/>;
export default class Example extends Component {
render() {
const { interpolation, color } = this.props;
return (
<Surface width={400} height={300}>
<Colorify colorScale={colorScales[color]} interpolation={interpolation}>
{require("../../iPKTONG.jpg")}
</Colorify>
</Surface>
);
}
static defaultProps = {
interpolation: "linear",
color: Object.keys(colorScales)[0],
};
}

View File

@ -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 }) =>
<Surface width={400} height={20}>
{ interpolation==="linear"
? <LinearCopy>{colorScales[color]}</LinearCopy>
: <NearestCopy>{colorScales[color]}</NearestCopy> }
</Surface>;

View File

@ -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<maxIterationsShad; i++){
float h = map(ro + rd*dist);
shade = min(shade, smoothstep(0., 1., k*h/dist));
dist += clamp(h, .2, stepDist);
if (abs(h)<0.001 || dist > 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 }) =>
<Node
shader={shaders.desertPassage}
uniforms={{
iGlobalTime: time / 1000,
iChannel0: shadertoyTex17jpg,
}}
/>
export const DesertPassageLoop = timeLoop(DesertPassage, { frameRate: 30 });
export default () =>
<Surface width={400} height={400}>
<DesertPassageLoop />
</Surface>

View File

@ -0,0 +1 @@
export const title = "a Shadertoy more advanced example";

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 KiB

View File

@ -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 <Node
shader={shaders.crt}
uniforms={{
rubyTexture: children,
rubyInputSize: inSize,
rubyOutputSize: outSize,
rubyTextureSize: texSize,
distortion,
}}
/>;
}
}
const Desert = connectSize(DesertPassageLoop);
class ShowCaptured extends PureComponent {
render() {
const {t} = this.props;
return <Surface width={200} height={200}>
<Node shader={shaders.copy} uniforms={{ t }} />
</Surface>;
}
}
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 (
<View>
<Surface ref="surface"
width={400}
height={400}
webglContextAttributes={{ preserveDrawingBuffer: true }}>
<Bus ref="desert">{/* we use a Bus to have a ref for capture */}
<Desert width={128} height={128} />
</Bus>
<CRT
distortion={distortion}
texSize={[ 128, 128 ]}
inSize={[ 128, 128 ]}
outSize={[ 400, 400 ]}>
{() => this.refs.desert}
</CRT>
</Surface>
<View className="buttons">
<Button
onPress={this.onCapture}
title="capture"
/>
</View>
<View className="snaps">
<ShowCaptured t={surfacePixels} />
<ShowCaptured t={desertPixels} />
</View>
</View>
);
}
static defaultProps = {
distortion: 0.2
};
}

View File

@ -0,0 +1,9 @@
import makeFloatSlider from "../../toolbox/makeFloatSlider";
export const title = "Doom Shadertoy + CRT effect + snapshot()";
export const toolbox = [
{ prop: "distortion",
title: "Distortion",
Editor: makeFloatSlider(0, 1, 0.01) },
];

View File

@ -0,0 +1,37 @@
//@flow
import React from "react";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
import timeLoop from "../../HOC/timeLoop";
const shaders = Shaders.create({
squareTunnel: {
// from https://en.wikipedia.org/wiki/Shadertoy
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform float iGlobalTime;
void main() {
vec2 p = 2.0 * uv - vec2(1.0);
float a = atan(p.y,p.x);
float r = pow( pow(p.x*p.x,4.0) + pow(p.y*p.y,4.0), 1.0/8.0 );
vec2 uv = vec2( 1.0/r + 0.2*iGlobalTime, a );
float f = cos(12.0*uv.x)*cos(6.0*uv.y);
vec3 col = 0.5 + 0.5*sin( 3.1416*f + vec3(0.0,0.5,1.0) );
col = col*r;
gl_FragColor = vec4( col, 1.0 );
}` }
});
const SquareTunnel = ({ time }) =>
<Node
shader={shaders.squareTunnel}
uniforms={{ iGlobalTime: time / 1000 }}
/>
const DesertPassageLoop = timeLoop(SquareTunnel);
export default() =>
<Surface width={400} height={400}>
<DesertPassageLoop />
</Surface>

View File

@ -0,0 +1 @@
export const title = "a Shadertoy simple example";

View File

@ -0,0 +1,31 @@
//@flow
import React from "react";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
import { DiamondCrop } from "../diamondcrop";
import timeLoop from "../../HOC/timeLoop";
const shaders = Shaders.create({
helloRed: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform float red;
void main() {
gl_FragColor = vec4(red, uv.x, uv.y, 1.0);
}` }
});
const HelloGLAnimated = timeLoop( ({ time }) =>
<Node
shader={shaders.helloRed}
uniforms={{ red: Math.cos(time / 100) }}
/>
);
export default () =>
<Surface width={300} height={300}>
<DiamondCrop>
<HelloGLAnimated blue={0.8} />
</DiamondCrop>
</Surface>;

View File

@ -0,0 +1 @@
export const title = "animated HelloGL (red) + DiamondCrop";

View File

@ -0,0 +1,34 @@
//@flow
import React, { Component } from "react";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
const shaders = Shaders.create({
DiamondCrop: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform sampler2D t;
void main() {
gl_FragColor = mix(
texture2D(t, uv),
vec4(0.0),
step(0.5, abs(uv.x - 0.5) + abs(uv.y - 0.5))
);
}` },
});
export const DiamondCrop = ({ children: t }) =>
<Node shader={shaders.DiamondCrop} uniforms={{ t }} />;
export default class Example extends Component {
render() {
return (
<Surface width={300} height={300}>
<DiamondCrop>
{require("../../5EOyTDQ.jpg")}
</DiamondCrop>
</Surface>
);
}
}

View File

@ -0,0 +1 @@
export const title = "DiamondCrop on an image texture";

View File

@ -0,0 +1,17 @@
//@flow
import React, { Component } from "react";
import { Surface } from "gl-react-native";
import { DiamondCrop } from "../diamondcrop";
import { HelloBlue } from "../helloblue";
export default class Example extends Component {
render() {
return (
<Surface width={300} height={300}>
<DiamondCrop>
<HelloBlue blue={0.8} />
</DiamondCrop>
</Surface>
);
}
}

View File

@ -0,0 +1 @@
export const title = "HelloGL composed with DiamondCrop";

View File

@ -0,0 +1,66 @@
import React, { Component } from "react";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
import timeLoop from "../../HOC/timeLoop";
import respondToTouchPosition from "../../HOC/respondToTouchPosition";
const shaders = Shaders.create({
vignetteColorSeparationDistortion: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform sampler2D t;
uniform vec2 mouse;
uniform float time, amp, freq, moving;
vec2 lookup (vec2 offset, float amp2) {
return mod(
uv + amp2 * amp * vec2(
cos(freq*(uv.x+offset.x)+time),
sin(freq*(uv.y+offset.x)+time))
+ vec2(
moving * time/10.0,
0.0),
vec2(1.0));
}
void main() {
float dist = distance(uv, mouse);
float amp2 = pow(1.0 - dist, 2.0);
float colorSeparation = 0.02 * mix(amp2, 1.0, 0.5);
vec2 orientation = vec2(1.0, 0.0);
float a = (1.0-min(0.95, pow(1.8 * distance(uv, mouse), 4.0) +
0.5 * pow(distance(fract(50.0 * uv.y), 0.5), 2.0)));
gl_FragColor = vec4(a * vec3(
texture2D(t, lookup(colorSeparation * orientation, amp2)).r,
texture2D(t, lookup(-colorSeparation * orientation, amp2)).g,
texture2D(t, lookup(vec2(0.0), amp2)).b),
1.0);
}` }
});
const Vignette = timeLoop(({ children: t, time, mouse }) =>
<Node
shader={shaders.vignetteColorSeparationDistortion}
uniforms={{
t,
time: time / 1000, // seconds is better for float precision
mouse,
freq: 10 + 2 * Math.sin(0.0007*time),
amp: 0.05 + Math.max(0, 0.03*Math.cos(0.001 * time)),
moving: 0,
}}
/>);
export default respondToTouchPosition(class Example extends Component {
props: { touchPosition: { x: number, y: number } };
render() {
const { touchPosition: {x, y} } = this.props;
return (
<Surface width={500} height={400}>
<Vignette mouse={[ x, y ]}>
{require("../../2VP5osy.jpg")}
</Vignette>
</Surface>
);
}
});

View File

@ -0,0 +1,2 @@
export const title = "Crazy distortion effect on an image";
export const description = "Vignette color separation distortion + touch responsive";

17
cookbook-rn/src/examples/gen.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
echo "// This file is generated by \`npm run generate-examples\` from index.txt"
while read ex
do
if [ ! -z $ex ] && [ -d $ex ]
then
echo "import ${ex}_E from './$ex';";
echo "import * as ${ex}_m from './$ex/meta';";
echo "export const $ex={ Example: ${ex}_E, ...${ex}_m };"
elif [ ! -z _TODO_$ex ] && [ -d _TODO_$ex ]
then
echo "import * as ${ex}_m from './_TODO_$ex/meta';";
echo "export const $ex={ Example: null, ...${ex}_m };"
else
echo "$ex not found" 1>&2;
fi
done < index.txt;

View File

@ -0,0 +1,94 @@
//@flow
import React, { Component } from "react";
import { View, Text, StyleSheet } from "react-native";
import { Node, Visitor, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
import timeLoop from "../../HOC/timeLoop";
const styles = StyleSheet.create({
compileRoot: {
flexDirection: "row",
},
compile: {
padding: 6,
backgroundColor: "#000",
color: "#c22",
fontSize: 10,
lineHeight: 18,
fontFamily: "Courier New",
},
compileError: {
color: "#c22",
},
compileSuccess: {
color: "#2c2",
},
});
const Preview = timeLoop(({ frag, visitor, time }) =>
<Surface width={500} height={200} visitor={visitor}>
<Node shader={{ frag }} uniforms={{ time: time / 1000 }} />
</Surface>);
class DisplayError extends Component {
props: { error: ?string };
render() {
const { error } = this.props;
if (!error) return <Text style={[ styles.compile, styles.compileSuccess ]}>
Compilation success!
</Text>;
let err = error.message;
const i = err.indexOf("ERROR:");
if (i!==-1) err = "line "+err.slice(i + 9);
return <Text style={[ styles.compile, styles.compileError ]}>{err}</Text>;
}
}
export default class Example extends Component {
constructor() {
super();
const visitor = new Visitor();
visitor.onSurfaceDrawError = (error: Error) => this.setState({ error });
visitor.onSurfaceDrawEnd = () => this.setState({ error: null });
this.state = { error: null, visitor };
}
render() {
const { frag } = this.props;
const { error, visitor } = this.state;
return (
<View>
<Preview frag={frag} visitor={visitor} />
<DisplayError error={error} />
</View>
);
}
props: { frag: string };
state: { error: ?Error, visitor: Visitor };
static defaultProps = { // adapted from http://glslsandbox.com/e#27937.0
frag: GLSL`precision highp float;
varying vec2 uv;
uniform float time;
void main() {
float amnt;
float nd;
vec4 cbuff = vec4(0.0);
for(float i=0.0; i<5.0;i++){
nd = sin(3.17*0.8*uv.x + (i*0.1+sin(+time)*0.2) + time)*0.8+0.1 + uv.x;
amnt = 1.0/abs(nd-uv.y)*0.01;
cbuff += vec4(amnt, amnt*0.3 , amnt*uv.y, 90.0);
}
for(float i=0.0; i<1.0;i++){
nd = sin(3.14*2.0*uv.y + i*40.5 + time)*90.3*(uv.y+80.3)+0.5;
amnt = 1.0/abs(nd-uv.x)*0.015;
cbuff += vec4(amnt*0.2, amnt*0.2 , amnt*uv.x, 1.0);
}
gl_FragColor = cbuff;
}
`
};
}

View File

@ -0,0 +1,32 @@
import { StyleSheet } from "react-native";
export const title = "GLSL live editor";
import makeTextArea from "../../toolbox/makeTextArea";
const styles = StyleSheet.create({
editor: {
flex: 1,
height: 400,
padding: 10,
margin: 0,
backgroundColor: "#282c34",
color: "#ABB2BF",
fontSize: 10,
lineHeight: 1.5,
fontFamily: "Courier New",
},
});
export const overrideStyles = StyleSheet.create({
toolbox: {
paddingBottom: 0,
},
field: {
paddingVertical: 0,
paddingHorizontal: 0,
}
});
export const toolbox = [
{ prop: "frag",
Editor: makeTextArea(styles.editor) }
];

View File

@ -0,0 +1,83 @@
//@flow
import React from "react";
import {Backbuffer,Shaders,Node,GLSL,NearestCopy} from "gl-react";
import { Surface } from "gl-react-native";
import timeLoop from "../../HOC/timeLoop";
export const shaders = Shaders.create({
InitGameOfLife: {
// returns white or black randomly
frag: GLSL`
precision highp float;
// i
varying vec2 uv;
float random (vec2 uv) {
return fract(sin(dot(uv, vec2(12.9898,78.233))) * 43758.5453);
}
// i
void main() {
gl_FragColor = vec4(vec3(step(0.5, random(uv))), 1.0);
}`
},
GameOfLife: {
// implement Game Of Life.
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform float size;
uniform sampler2D t; // the previous world state
void main() {
float prev = step(0.5, texture2D(t, uv).r);
float c = 1.0 / size;
float sum =
step(0.5, texture2D(t, uv + vec2(-1.0, -1.0)*c).r) +
step(0.5, texture2D(t, uv + vec2(-1.0, 0.0)*c).r) +
step(0.5, texture2D(t, uv + vec2(-1.0, 1.0)*c).r) +
step(0.5, texture2D(t, uv + vec2( 0.0, 1.0)*c).r) +
step(0.5, texture2D(t, uv + vec2( 1.0, 1.0)*c).r) +
step(0.5, texture2D(t, uv + vec2( 1.0, 0.0)*c).r) +
step(0.5, texture2D(t, uv + vec2( 1.0, -1.0)*c).r) +
step(0.5, texture2D(t, uv + vec2( 0.0, -1.0)*c).r);
float next = prev==1.0 && sum >= 2.0 && sum <= 3.0 || sum == 3.0 ? 1.0 : 0.0;
gl_FragColor = vec4(vec3(next), 1.0);
}`
}
});
const refreshEveryTicks = 20;
export const GameOfLife = ({ tick }) => {
// Changing size is "destructive" and will reset the Game of Life state
const size = 16 * (1+Math.floor(tick/refreshEveryTicks));
// However, we can conditionally change shader/uniforms,
// React reconciliation will preserve the same <Node> instance,
// and our Game of Life state will get preserved!
return tick%refreshEveryTicks===0
? <Node
shader={shaders.InitGameOfLife}
width={size}
height={size}
backbuffering // makes Node holding 2 fbos that get swapped each draw time
sync // force <Node> to draw in sync each componentDidUpdate time
/>
: <Node
shader={shaders.GameOfLife}
width={size}
height={size}
backbuffering
sync
uniforms={{
t: Backbuffer, // Use previous frame buffer as a texture
size,
}}
/>;
};
const GameOfLifeLoop = timeLoop(GameOfLife, { refreshRate: 20 });
export default () =>
<Surface width={384} height={384}>
<NearestCopy>
<GameOfLifeLoop />
</NearestCopy>
</Surface>

View File

@ -0,0 +1 @@
export const title = "GameOfLife";

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

View File

@ -0,0 +1,37 @@
//@flow
import React, { Component } from "react";
import { Backbuffer, Node, NearestCopy } from "gl-react";
import { Surface } from "gl-react-native";
import {shaders} from "../gol";
import timeLoop from "../../HOC/timeLoop";
import gliderGunImage from "./glider-gun-64.png";
const GameOfLifeLoop = timeLoop(({ tick, size }) =>
<Node
shader={shaders.GameOfLife}
width={size}
height={size}
backbuffering
sync
uniforms={{
t: tick===0 ? gliderGunImage : Backbuffer,
size,
}}
/>, { refreshRate: 20 });
export default class Example extends Component {
render() {
return (
<Surface
width={500}
height={500}
preload={[ // preload textures before starting rendering
gliderGunImage
]}>
<NearestCopy>
<GameOfLifeLoop size={64} />
</NearestCopy>
</Surface>
);
}
}

View File

@ -0,0 +1,2 @@
export const title = "GameOfLife is initialized with a glider texture";
export const description = "Game of Life init with an image";

View File

@ -0,0 +1,45 @@
//@flow
import React from "react";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
import timeLoop from "../../HOC/timeLoop";
import {GameOfLife} from "../gol";
const shaders = Shaders.create({
Rotating: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform float angle, scale;
uniform sampler2D children;
void main() {
mat2 rotation = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));
vec2 p = (uv - vec2(0.5)) * rotation / scale + vec2(0.5);
gl_FragColor =
p.x < 0.0 || p.x > 1.0 || p.y < 0.0 || p.y > 1.0
? vec4(0.0)
: texture2D(children, p);
}` }
});
export const Rotating = ({ angle, scale, children }) =>
<Node
shader={shaders.Rotating}
uniformsOptions={{ children: { interpolation: "nearest" } }}
uniforms={{ angle, scale, children }}
/>;
const RotatingLoop = timeLoop(({ time, children }) => Rotating({
angle: (time / 1000) % (2 * Math.PI),
scale: 0.6 + 0.15 * Math.cos(time / 500),
children,
}));
const GameOfLifeLoop = timeLoop(GameOfLife, { refreshRate: 5 });
export default () =>
<Surface width={500} height={500}>
<RotatingLoop>
<GameOfLifeLoop />
</RotatingLoop>
</Surface>;

View File

@ -0,0 +1,2 @@
export const title = "Rotating GameOfLife. 2 loops";
export const description = "Compose Game of Life with Rotation";

View File

@ -0,0 +1,36 @@
//@flow
import React, { Component } from "react";
import { Surface } from "gl-react-native";
import timeLoop from "../../HOC/timeLoop";
import {GameOfLife} from "../gol";
import {Rotating} from "../golrot";
class PureGameOfLife extends Component {
shouldComponentUpdate ({ tick }) { // only tick should trigger redraw
return tick !== this.props.tick;
}
render() {
return <GameOfLife tick={this.props.tick} />;
}
}
const RotatingGameOfLife = ({ time }) =>
<Rotating
angle={(time / 1000) % (2 * Math.PI)}
scale={0.6 + 0.15 * Math.cos(time / 500)}>
<PureGameOfLife tick={Math.floor(time / 200)} />
</Rotating>;
export const RotatingGameOfLifeLoop = timeLoop(RotatingGameOfLife);
export default class Example extends Component {
render() {
return (
<Surface width={500} height={500}>
<RotatingGameOfLifeLoop />
</Surface>
);
}
}

View File

@ -0,0 +1,2 @@
export const title = "Rotating GameOfLife with sCU";
export const description = "Single update loop and shouldComponentUpdate";

View File

@ -0,0 +1,56 @@
//@flow
import React from "react";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
import timeLoop from "../../HOC/timeLoop";
const shaders = Shaders.create({
gradients: { frag: GLSL`
precision highp float;
varying vec2 uv;
uniform vec4 colors[3];
uniform vec2 particles[3];
void main () {
vec4 sum = vec4(0.0);
for (int i=0; i<3; i++) {
vec4 c = colors[i];
vec2 p = particles[i];
float d = c.a * smoothstep(0.6, 0.2, distance(p, uv));
sum += d * vec4(c.a * c.rgb, c.a);
}
if (sum.a > 1.0) {
sum.rgb /= sum.a;
sum.a = 1.0;
}
gl_FragColor = vec4(sum.a * sum.rgb, 1.0);
}`}
});
// Alternative syntax using React stateless function component
const Gradients = ({ time }) =>
<Node
shader={shaders.gradients}
uniforms={{
colors: [
[ Math.cos(0.002*time), Math.sin(0.002*time), 0.2, 1 ],
[ Math.sin(0.002*time), -Math.cos(0.002*time), 0.1, 1 ],
[ 0.3, Math.sin(3+0.002*time), Math.cos(1+0.003*time), 1 ]
],
particles: [
[ 0.3, 0.3 ],
[ 0.7, 0.5 ],
[ 0.4, 0.9 ]
]
}}
/>;
const GradientsLoop = timeLoop(Gradients);
export default () =>
<Surface width={300} height={300}>
<GradientsLoop />
</Surface>;
// NB: don't abuse the uniforms array:
// it's not meant to be used with lot of objects.
// GLSL 1 also don't support variable length in loops.

View File

@ -0,0 +1,2 @@
export const title = "Rotating radial gradients";
export const description = "More advanced animation";

View File

@ -0,0 +1,69 @@
//@flow
import React, { Component } from "react";
import { TouchableWithoutFeedback } from "react-native";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
import {Motion, spring} from "react-motion";
const shaders = Shaders.create({
Heart: { // inspired from http://glslsandbox.com/e#29521.0
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform sampler2D image;
uniform vec3 color;
uniform float over, toggle;
void main() {
float scale = 1.0 - 0.1 * over - 0.8 * toggle;
vec2 offset = vec2(0.0, -0.3 - 0.1 * over - 0.7 * toggle);
vec2 p = scale * (2.0 * uv - 1.0 + offset);
float a = atan(p.x, p.y) / ${Math.PI/* \o/ */};
float r = length(p);
float h = abs(a);
float d = (13.0*h - 22.0*h*h + 10.0*h*h*h - 0.3 * (1.0-over))/(6.0-5.0*h);
float f = step(r,d) * pow(max(1.0-r/d, 0.0),0.25);
vec3 t = texture2D(image, uv).rgb;
vec3 c = mix(color * (1.0 + 0.6 * t), t, min(0.8 * over + toggle, 1.0));
gl_FragColor = vec4(mix(vec3(1.0), c, f), 1.0);
}`
}
});
class InteractiveHeart extends Component {
state = { over: 0, toggle: 0 };
onPressIn = () => this.setState({ over: 1 });
onPressOut = () => this.setState({ over: 0 });
onPress = () => this.setState({
toggle: this.state.toggle ? 0 : 1
});
render() {
const { color, image } = this.props;
const { over, toggle } = this.state;
return (
<TouchableWithoutFeedback
onPress={this.onPress}
onPressIn={this.onPressIn}
onPressOut={this.onPressOut}>
<Surface width={300} height={300}>
<Motion
defaultStyle={{ over, toggle }}
style={{
over: spring(over, [150, 15]),
toggle: spring(toggle, [150, 15])
}}>{ ({ over, toggle }) =>
<Node
shader={shaders.Heart}
uniforms={{ color, image, over, toggle }}
/>
}</Motion>
</Surface>
</TouchableWithoutFeedback>
);
}
}
export default () =>
<InteractiveHeart
color={[ 1, 0, 0 ]}
image={require("../../GQo1KWq.jpg")}
/>;

View File

@ -0,0 +1 @@
export const title = "Heart animation";

View File

@ -0,0 +1,36 @@
//@flow
import React, { Component } from "react";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
const shaders = Shaders.create({
helloBlue: {
// uniforms are variables from JS. We pipe blue uniform into blue output color
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform float blue;
void main() {
gl_FragColor = vec4(uv.x, uv.y, blue, 1.0);
}` }
});
// We can make a <HelloBlue blue={0.5} /> that will render the concrete <Node/>
export class HelloBlue extends Component {
render() {
const { blue } = this.props;
return <Node shader={shaders.helloBlue} uniforms={{ blue }} />;
}
}
// Our example will pass the slider value to HelloBlue
export default class Example extends Component {
render() {
return (
<Surface width={300} height={300}>
<HelloBlue blue={this.props.blue} />
</Surface>
);
}
static defaultProps = { blue: 0.5 };
}

View File

@ -0,0 +1,9 @@
import makeFloatSlider from "../../toolbox/makeFloatSlider";
export const title = "Hello GL blue";
export const toolbox = [
{ prop: "blue",
title: "Blue Color",
Editor: makeFloatSlider(0, 1, 0.01) },
];

View File

@ -0,0 +1,21 @@
//@flow
import React, { Component } from "react";
import { Surface } from "gl-react-native";
// Reuse that previous HelloBlue component to animate it...
import { HelloBlue } from "../helloblue";
// timeLoop is an utility that animates a component.
// in a requestAnimationFrame loop and provide a time and tick prop
import timeLoop from "../../HOC/timeLoop";
export default timeLoop(class Example extends Component {
render() {
const { time } = this.props;
return (
<Surface width={300} height={300}>
<HelloBlue blue={0.5 + 0.5 * Math.cos(time / 500)} />
</Surface>
);
}
});

View File

@ -0,0 +1 @@
export const title = "Hello GL blue animated";

View File

@ -0,0 +1,34 @@
//@flow
import React, { Component } from "react";
import { Shaders, Node, GLSL } from "gl-react";
import { Surface } from "gl-react-native";
// in gl-react you need to statically define "shaders":
const shaders = Shaders.create({
helloGL: {
// This is our first fragment shader in GLSL language (OpenGL Shading Language)
// (GLSL code gets compiled and run on the GPU)
frag: GLSL`
precision highp float;
varying vec2 uv;
void main() {
gl_FragColor = vec4(uv.x, uv.y, 0.5, 1.0);
}`
// the main() function is called FOR EACH PIXELS
// the varying uv is a vec2 where x and y respectively varying from 0.0 to 1.0.
// we set in output the pixel color using the vec4(r,g,b,a) format.
// see [GLSL_ES_Specification_1.0.17](http://www.khronos.org/registry/gles/specs/2.0/GLSL_ES_Specification_1.0.17.pdf)
}
});
export default class Example extends Component {
render() {
return (
<Surface width={300} height={300}>
<Node shader={shaders.helloGL} />
</Surface>
);
// Surface creates the canvas, an area of pixels where you can draw.
// Node instanciates a "shader program" with the fragment shader defined above.
}
}

View File

@ -0,0 +1 @@
export const title = "Hello GL";

View File

@ -0,0 +1,615 @@
/**
* This celullar automaton is extracted from a game I wrote in 2014 for JS13K:
* https://github.com/gre/ibex
*
* Technical article: http://greweb.me/2014/09/ibex-cellular-automata/
*/
//@flow
import React, { Component } from "react";
import { Shaders, Node, GLSL, Backbuffer } from "gl-react";
import { Surface } from "gl-react-native";
import ndarray from "ndarray";
import timeLoop from "../../HOC/timeLoop";
const shaders = Shaders.create({
IBEXRender: {
frag: GLSL`
precision highp float;
varying vec2 uv;
uniform vec2 size;
uniform sampler2D state;
uniform vec3 CL[9];
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
vec3 colorFor (int e) {
if(e==0) return CL[0];
if(e==1) return CL[1];
if(e==2) return CL[2];
if(e==3) return CL[3];
if(e==4) return CL[4];
if(e==5) return CL[5];
if(e==6) return CL[6];
if(e==7) return CL[7];
return CL[8];
}
int getState (vec2 pos) {
vec2 uv = (floor(pos) + 0.5) / size;
bool outOfBound = uv.x<0.0||uv.x>1.0||uv.y<0.0||uv.y>1.0;
if (outOfBound) return pos.y < 0.0 ? 1 : 0;
float cel = texture2D(state, uv).r;
return int(floor(.5 + 9. * cel));
}
vec3 stateColorPass (int e, vec2 pos) {
return (
mix(1.0, rand(pos), 0.05*float(e==1) + 0.2*float(e==8)) +
float(e==8) * (
step(0.97, rand(pos)) * vec3(3.0, 0.0, 0.0) +
step(rand(pos), 0.02) * vec3(1.5, -0.5, 0.5)
)
) * colorFor(e);
}
void main(){
vec2 statePos = uv * size;
vec2 statePosFloor = floor(statePos);
vec3 stateColor = stateColorPass(getState(statePosFloor), statePosFloor);
vec3 c = stateColor;
vec2 pixelPos = fract(statePos);
vec3 pixelColor = -vec3(0.03) * (pixelPos.x - pixelPos.y);
c += pixelColor;
gl_FragColor = vec4(c, 1.0);
}
`
},
IBEXLogic: {
frag: GLSL`
#define RAND (S_=vec2(rand(S_), rand(S_+9.))).x
#define AnyADJ(e) (NW==e||SE==e||NE==e||SW==e||NN==e||SS==e||EE==e||WW==e)
precision highp float;
int A = 0;
int E = 1;
int F = 2;
int W = 3;
int V = 4;
int S = 5;
int Al = 6;
int Ar = 7;
int G = 8;
varying vec2 uv;
uniform vec2 SZ;
uniform float SD;
uniform float TI;
uniform float TS;
uniform float ST;
uniform sampler2D state;
uniform bool RU;
uniform bool draw;
uniform ivec2 DP;
uniform float DR;
uniform int DO;
uniform float forestGrowFactor;
int get (int x_, int y_) {
vec2 u = (SZ * uv + vec2(x_, y_)) / SZ;
return (u.x < 0.0 || u.x >= 1.0 || u.y < 0.0 || u.y >= 1.0) ? 0 :
int(floor(.5 + 9. * texture2D(state, u).r));
}
bool between (float f, float a, float b) {
return a <= f && f <= b;
}
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
float grassDistrib (vec2 p) {
return mix(
rand(vec2(p.x)),
0.5*(1.0+(cos(sin(p.y*0.01 + p.x * 0.05) +
(1.0 + 0.3*sin(p.x*0.01)) * p.y * 0.08))),
0.6
);
}
bool hellTriggerPosition (vec2 p) {
if (TS==0.0) return false;
float hellTickStart = 800.0;
float hellTickInterv = 70.0;
float hellSize = 5.0;
float dt = TI - TS - hellTickStart;
float x = floor(dt / hellTickInterv);
float y = (dt - x * hellTickInterv);
return distance(vec2(2.0 * (hellSize * x - ST), hellSize * y), p) <= hellSize;
}
void main () {
vec2 p = uv * SZ;
vec2 S_ = p + 0.001 * TI;
int NW = get(-1, 1);
int NN = get( 0, 1);
int NE = get( 1, 1);
int WW = get(-1, 0);
int CC = get( 0, 0);
int EE = get( 1, 0);
int SW = get(-1,-1);
int SS = get( 0,-1);
int SE = get( 1,-1);
bool prevIsSolid = CC==E||CC==G||CC==V||CC==S;
int r = A;
int grassMaxHeight = int(20.0 * pow(grassDistrib(p), 1.3));
float rainRelativeTime = mod(TI, 300.0);
float volcRelativeTime = mod(TI, 25.0);
if (
-0.05 * float(NW==W) + -0.40 * float(NN==W) + -0.05 * float(NE==W) +
-0.50 * float(WW==W) + -0.50 * float(CC==W) + -0.50 * float(EE==W) +
0.35 * float(SW==F) + 0.90 * float(SS==F) + 0.35 * float(SE==F)
>= 0.9 - 0.6 * RAND
||
CC == F && RAND < 0.9 && AnyADJ(G)
) {
r = F;
}
if (
between(
0.3 * float(NW==W) + 0.9 * float(NN==W) + 0.3 * float(NE==W) +
0.1 * float(WW==W) + -0.3 * float(CC==F) + 0.1 * float(EE==W) +
-0.3 * float(SS==F)
,
0.9 - 0.6 * RAND,
1.4 + 0.3 * RAND
)
||
!prevIsSolid &&
RAND < 0.98 &&
( (WW==W||NW==W) && SW==E || (EE==W||NE==W) && SE==E )
||
!prevIsSolid &&
p.y >= SZ.y-1.0 &&
rainRelativeTime < 100.0 &&
between(
p.x -
(rand(vec2(SD*0.7 + TI - rainRelativeTime)) * SZ.x)
,
0.0,
100.0 * rand(vec2(SD + TI - rainRelativeTime))
)
||
!prevIsSolid && (
0.9 * float(NW==S) + 1.0 * float(NN==S) + 0.9 * float(NE==S) +
0.7 * float(WW==S) + 0.7 * float(EE==S)
>= 1.0 - 0.3 * RAND
)
) {
r = W;
}
if (CC == E) {
if (!(WW==A && EE==A)) r = E;
if (
RAND < 0.3 && (
1.0 * float(NW==W) + 1.2 * float(NN==W) + 1.0 * float(NE==W) +
0.5 * float(WW==W) + 0.5 * float(EE==W) +
0.3 * float(SW==W) + 0.2 * float(SS==W) + 0.3 * float(SE==W)
>= 3.0 - 2.5 * RAND
)
||
RAND < 0.01 && ( WW==S || NN==S || EE==S )
) {
r = S;
}
if (
RAND < 0.01 + 0.02 * smoothstep(500.0, 5000.0, ST + p.x) &&
( int(WW==V) + int(SS==V) + int(EE==V) + int(SE==V) + int(SW==V) > 1 )
) {
r = V;
}
}
if (grassMaxHeight > 0) {
if (CC == G) {
r = G;
if (
CC == G &&
RAND < 0.9 && (
AnyADJ(F) ||
AnyADJ(V)
)) {
r = F;
}
}
else if (!prevIsSolid && (AnyADJ(E) || AnyADJ(G) || AnyADJ(S))) {
if (RAND < 0.03 * forestGrowFactor &&
get(0, -grassMaxHeight) != G && (
SS==G && RAND < 0.07 ||
SS==E && RAND < 0.02 ||
AnyADJ(W) ||
AnyADJ(S)
)
) {
r = G;
}
}
}
if ((!prevIsSolid || CC==F) && SS==V) {
r = F;
}
if (CC == V) {
r = V;
if (
NW==W || NN==W || NE==W || EE==W || WW==W
) {
r = RAND < 0.8 ? S : E;
}
if (
RAND<0.005 &&
( int(SW==F||SW==V) + int(SS==F||SS==V) + int(SE==F||SE==V) < 2 )
) {
r = E;
}
if (
int(NW==S) + int(SE==S) + int(NE==S) + int(SW==S) +
int(NN==S) + int(SS==S) + int(EE==S) + int(WW==S)
> 1
) {
r = RAND < 0.2 ? V : (RAND < 0.8 ? S : E);
}
}
if (prevIsSolid &&
p.y <= 2.0 &&
volcRelativeTime <= 1.0 &&
RAND < 0.3 &&
between(
p.x -
rand(vec2(SD*0.01 + TI - volcRelativeTime)) * SZ.x
,
0.0,
10.0 * rand(vec2(SD*0.07 + TI - volcRelativeTime))
)
) {
r = V;
}
if (CC == S) {
r = S;
if (
RAND<0.06 &&
( int(NW==W||NW==S) + int(NN==W||NN==S) + int(NE==W||NE==S) < 1 )
||
( EE==F || WW==F || SS==F || SW==F || SE==F )
) {
r = E;
}
if (AnyADJ(V)) {
r = RAND < 0.15 ? V : (RAND < 0.6 ? S : E);
}
}
if (r == A) {
if (RAND < 0.00001) r = Al;
if (RAND < 0.00001) r = Ar;
}
int wind = r==Al ? Al : r == Ar ? Ar : 0;
float f =
(-0.1+0.05*(RAND-0.5)) * float(NW==Ar) + 0.12 * float(NE==Al) +
-0.65 * float(WW==Ar) + 0.65 * float(EE==Al) +
-0.1 * float(SW==Ar) + (0.2+0.05*(RAND-0.5)) * float(SE==Al) ;
if (between(f, 0.4 * RAND, 0.95)) {
wind = Al;
}
else if (between(f, -0.95, -0.4 * RAND)) {
wind = Ar;
}
if (wind != 0) {
if (r == A) {
r = wind;
}
else if(r == F) {
if (RAND < 0.4) r = wind;
}
else if (r == W) {
if (RAND < 0.1) r = wind;
}
}
if (draw) {
vec2 pos = floor(p);
if (distance(pos, vec2(DP)) <= DR) {
if (DO == W) {
if (prevIsSolid && CC!=G) {
r = S;
}
else if (!prevIsSolid && mod(pos.x + pos.y, 2.0)==0.0) {
r = W;
}
}
else if (DO == F) {
r = prevIsSolid ? V : F;
}
else {
r = DO;
}
}
}
if (hellTriggerPosition(p)) {
r = prevIsSolid ? V : F;
}
if (!RU) {
if (r == F || r == W|| r == G) r = A;
if (r == V || r == S) r = E;
}
gl_FragColor = vec4(float(r) / 9.0, 0.0, 0.0, 1.0);
}`
}
});
class IBEXLogic extends Component {
state = {
seed: Math.random(),
};
shouldComponentUpdate({ tick }) {
return tick !== this.props.tick;
}
render() {
const {
size,
tick,
initialState,
forestGrowFactor,
waterFactor,
fireFactor,
} = this.props;
const { seed } = this.state;
let draw=false, drawPosition=[0,0], drawRadius=0, drawElement=0;
let w = Math.random() < waterFactor, f = Math.random() < fireFactor;
if (w && f) {
if (Math.random() * waterFactor - Math.random() * fireFactor > 0) {
f = false;
}
else {
w = false;
}
}
if (w) {
draw = true;
drawPosition=[
size[0]*Math.random(),
size[1]*(0.8 + 0.2 * Math.random()),
];
drawRadius = 4;
drawElement = 3;
console.log(drawElement, drawPosition, drawPosition);
}
else if (f) {
draw = true;
drawPosition=[
size[0]*Math.random(),
0,
];
drawRadius = 4;
drawElement = 2;
console.log(drawElement, drawPosition, drawPosition);
}
return <Node
shader={shaders.IBEXLogic}
sync
backbuffering
uniformsOptions={{ state: { interpolation: "nearest" } }}
uniforms={{
state: tick===0 ? initialState : Backbuffer,
SZ: size,
SD: seed,
TI: tick,
TS: 0, // tick start
RU: true, // logic running
ST: true, // render started
draw,
DP: drawPosition, // draw position
DR: drawRadius, // draw radius
DO: drawElement, // the element that is being drawn
forestGrowFactor,
}}
/>;
}
}
var colors = [
[0.11, 0.16, 0.23], // 0: air
[0.74, 0.66, 0.51], // 1: earth
[0.84, 0.17, 0.08], // 2: fire
[0.40, 0.75, 0.90], // 3: water
[0.60, 0.00, 0.00], // 4: volcano (fire spawner)
[0.30, 0.60, 0.70], // 5: source (water spawner)
[0.15, 0.20, 0.27], // 6: wind left
[0.07, 0.12, 0.19], // 7: wind right
[0.20, 0.60, 0.20] // 8: grass (forest)
];
const IBEXRender = ({ size, children: state }) =>
<Node
shader={shaders.IBEXRender}
uniformsOptions={{ state: { interpolation: "nearest" } }}
uniforms={{
state,
size,
CL: colors,
}}
/>;
const Game = timeLoop(class extends Component {
state = {
tick: 0,
lastTickTime: this.props.time,
};
componentWillReceiveProps({ time, speed }) {
this.setState(({ tick, lastTickTime }) => {
const delta = 1000/speed;
if (time-lastTickTime > delta) {
return {
tick: tick + 1,
lastTickTime: lastTickTime + delta,
};
}
});
}
render() {
const {
size,
initialState,
forestGrowFactor,
waterFactor,
fireFactor
} = this.props;
const {
tick,
} = this.state;
return <IBEXRender size={size}>
<IBEXLogic
initialState={initialState}
size={size}
tick={tick}
forestGrowFactor={forestGrowFactor}
waterFactor={waterFactor}
fireFactor={fireFactor}
/>
</IBEXRender>;
}
});
// This should be implemented in a shader (it's a cellular automaton too)
// but it's how it was done in the game
function generate (startX: number, worldSize: [number,number]) {
var worldPixelRawBuf = new Uint8Array(worldSize[0] * worldSize[1] * 4);
var worldPixelBuf = new Uint8Array(worldSize[0] * worldSize[1]);
var waterInGeneration = 0;
var volcanoInGeneration = 0;
var w = worldSize[0], h = worldSize[1];
function step (a, b, x) {
return Math.max(0, Math.min((x-a) / (b-a), 1));
}
function affectColor (buf, i, c) {
buf[i] = ~~(256 * c / 9);
buf[i+3] = 1;
}
function get (b, x, y) {
if (x >= 0 && x < w && y >= 0 && y < h) {
return b[x + y * w];
}
return y > 50 ? 1 : 0;
}
function set (b, x, y, e) {
if (x >= 0 && x < w && y >= 0 && y < h) {
b[x + y * w] = e;
}
}
var K = 26;
var x, y, i, k, e;
for (x = startX; x < worldSize[0]; ++x) {
for (y = 0; y < worldSize[1]; ++y) {
e = +(Math.random() >
0.22
+ 0.3 * (step(0, 20, y)
+ step(worldSize[1]-20, worldSize[1] - 2, y)));
set(worldPixelBuf, x, y, e);
}
}
var swp = new Uint8Array(worldPixelBuf);
var cur = worldPixelBuf;
for (k = 0; k < K; ++k) {
for (x = startX; x < worldSize[0]; ++x) {
for (y = 0; y < worldSize[1]; ++y) {
var me = get(cur, x, y);
var sum =
0.1 * me +
(0.9 + 0.1 * Math.random()) * (get(cur, x-1, y-1)?1:0) +
(0.9 + 0.1 * Math.random()) * (get(cur, x, y-1)?1:0) +
(0.9 + 0.1 * Math.random()) * (get(cur, x+1, y-1)?1:0) +
(1.4 + 0.2 * Math.random()) * (get(cur, x-1, y)?1:0) +
(1.1 + 0.2 * Math.random()) * (get(cur, x+1, y)?1:0) +
(1.6 - 0.1 * Math.random()) * (get(cur, x-1, y+1)?1:0) +
(1.2 - 0.2 * Math.random()) * (get(cur, x, y+1)?1:0) +
(1.0 - 0.1 * Math.random()) * (get(cur, x+1, y+1?1:0));
let e = +(sum <= 6 + (Math.random()-0.5) * (1-k/K));
if (e && sum >= 6 - Math.random() * waterInGeneration + 4 * step(110, 0, y)) e = 5;
if (e && sum >= 6 - Math.random() * volcanoInGeneration + 6 * step(20, 60, y)) e = 4;
set(swp, x, y, e);
}
}
var tmp = swp;
swp = cur;
cur = tmp;
}
if (swp === cur) worldPixelBuf = swp;
for (i = 0; i < worldPixelBuf.length; ++i) {
affectColor(worldPixelRawBuf, 4 * i, worldPixelBuf[i]);
}
return ndarray(worldPixelRawBuf, [ worldSize[0], worldSize[1], 4]).transpose(1, 0, 2).step(1, -1, 1);
}
const size = [200,200];
export default class Example extends Component {
state = {
initialState: generate(0, size),
};
render() {
const { forestGrowFactor, fireFactor, waterFactor, speed } = this.props;
const { initialState } = this.state;
return (
<Surface width={400} height={400}>
<Game
speed={speed}
size={size}
initialState={initialState}
forestGrowFactor={forestGrowFactor}
fireFactor={fireFactor}
waterFactor={waterFactor}
/>
</Surface>
);
}
static defaultProps = {
speed: 60,
forestGrowFactor: 1,
fireFactor: 0,
waterFactor: 0,
};
}
/**
* Game Rule Interactions.
*
* Each interaction use various probability. Some are very rare, some frequent.
/!\ here air means wind /!\ it is different of empty, the empty space is
* called "Nothing" aka N)
*
* Primary elements: Water, Fire, Earth, Air
* =======
* Water + Nothing => fall / slide
* Fire + Nothing => grow
* Air + Nothing => move (directional wind)
* Water + Air => Water is deviated (wind)
* Fire + Air => Fire decrease
* Earth + Water => rarely creates Water Source (water infiltration)
* Earth + Fire => rarely creates Volcano (fire melt ground into lava)
*
* Secondary elements: Source, Volcano
* =========
* Source + Nothing => creates Water (on bottom).
* Volcano + Nothing => creates Fire (on top)
* Volcano + Source => IF source on top of volcano: sometimes creates Ground.
* OTHERWISE: sometimes creates volcano.
* Volcano + Water => rarely creates Source.
* Earth + Volcano => rarely Volcano expand / grow up in the Earth.
* Earth + Source => rarely Source expand / infiltrate in the Earth.
* Source + Fire => Source die.
*
* Cases where nothing happens:
* Water + Fire
* Earth + Nothing
* Volcano + Fire
* Volcano + Air
* Earth + Air
* Source + Air
* Source + Water
*/

View File

@ -0,0 +1,18 @@
import makeFloatSlider from "../../toolbox/makeFloatSlider";
export const title = "IBEX (js13k 2014 greweb)";
export const toolbox = [
{ prop: "speed",
title: (value) => "Simulation Speed ("+value+" FPS)",
Editor: makeFloatSlider(1, 60, 1) },
{ prop: "forestGrowFactor",
title: "Forest Grow Factor",
Editor: makeFloatSlider(0, 50, 1) },
{ prop: "waterFactor",
title: "Water Factor",
Editor: makeFloatSlider(0, 1, 0.01) },
{ prop: "fireFactor",
title: "Volcano Factor",
Editor: makeFloatSlider(0, 1, 0.01) },
];

View File

@ -0,0 +1,99 @@
// This file is generated by `npm run generate-examples` from index.txt
import hellogl_E from './hellogl';
import * as hellogl_m from './hellogl/meta';
export const hellogl={ Example: hellogl_E, ...hellogl_m };
import helloblue_E from './helloblue';
import * as helloblue_m from './helloblue/meta';
export const helloblue={ Example: helloblue_E, ...helloblue_m };
import helloblueanim_E from './helloblueanim';
import * as helloblueanim_m from './helloblueanim/meta';
export const helloblueanim={ Example: helloblueanim_E, ...helloblueanim_m };
import colordisc_E from './colordisc';
import * as colordisc_m from './colordisc/meta';
export const colordisc={ Example: colordisc_E, ...colordisc_m };
import gradients_E from './gradients';
import * as gradients_m from './gradients/meta';
export const gradients={ Example: gradients_E, ...gradients_m };
import diamondcrop_E from './diamondcrop';
import * as diamondcrop_m from './diamondcrop/meta';
export const diamondcrop={ Example: diamondcrop_E, ...diamondcrop_m };
import diamondhello_E from './diamondhello';
import * as diamondhello_m from './diamondhello/meta';
export const diamondhello={ Example: diamondhello_E, ...diamondhello_m };
import diamondanim_E from './diamondanim';
import * as diamondanim_m from './diamondanim/meta';
export const diamondanim={ Example: diamondanim_E, ...diamondanim_m };
import heart_E from './heart';
import * as heart_m from './heart/meta';
export const heart={ Example: heart_E, ...heart_m };
import animated_E from './animated';
import * as animated_m from './animated/meta';
export const animated={ Example: animated_E, ...animated_m };
import saturation_E from './saturation';
import * as saturation_m from './saturation/meta';
export const saturation={ Example: saturation_E, ...saturation_m };
import colorscale_E from './colorscale';
import * as colorscale_m from './colorscale/meta';
export const colorscale={ Example: colorscale_E, ...colorscale_m };
import distortion_E from './distortion';
import * as distortion_m from './distortion/meta';
export const distortion={ Example: distortion_E, ...distortion_m };
import transitions_E from './transitions';
import * as transitions_m from './transitions/meta';
export const transitions={ Example: transitions_E, ...transitions_m };
import glsledit_E from './glsledit';
import * as glsledit_m from './glsledit/meta';
export const glsledit={ Example: glsledit_E, ...glsledit_m };
import paint_E from './paint';
import * as paint_m from './paint/meta';
export const paint={ Example: paint_E, ...paint_m };
import pixeleditor_E from './pixeleditor';
import * as pixeleditor_m from './pixeleditor/meta';
export const pixeleditor={ Example: pixeleditor_E, ...pixeleditor_m };
import blurxy_E from './blurxy';
import * as blurxy_m from './blurxy/meta';
export const blurxy={ Example: blurxy_E, ...blurxy_m };
import blurxydownscale_E from './blurxydownscale';
import * as blurxydownscale_m from './blurxydownscale/meta';
export const blurxydownscale={ Example: blurxydownscale_E, ...blurxydownscale_m };
import blurmulti_E from './blurmulti';
import * as blurmulti_m from './blurmulti/meta';
export const blurmulti={ Example: blurmulti_E, ...blurmulti_m };
import blurmap_E from './blurmap';
import * as blurmap_m from './blurmap/meta';
export const blurmap={ Example: blurmap_E, ...blurmap_m };
import blurmapmouse_E from './blurmapmouse';
import * as blurmapmouse_m from './blurmapmouse/meta';
export const blurmapmouse={ Example: blurmapmouse_E, ...blurmapmouse_m };
import blurmapdyn_E from './blurmapdyn';
import * as blurmapdyn_m from './blurmapdyn/meta';
export const blurmapdyn={ Example: blurmapdyn_E, ...blurmapdyn_m };
import * as blurimgtitle_m from './_TODO_blurimgtitle/meta';
export const blurimgtitle={ Example: null, ...blurimgtitle_m };
import gol_E from './gol';
import * as gol_m from './gol/meta';
export const gol={ Example: gol_E, ...gol_m };
import golglider_E from './golglider';
import * as golglider_m from './golglider/meta';
export const golglider={ Example: golglider_E, ...golglider_m };
import golrot_E from './golrot';
import * as golrot_m from './golrot/meta';
export const golrot={ Example: golrot_E, ...golrot_m };
import golrotscu_E from './golrotscu';
import * as golrotscu_m from './golrotscu/meta';
export const golrotscu={ Example: golrotscu_E, ...golrotscu_m };
import mergechannels_E from './mergechannels';
import * as mergechannels_m from './mergechannels/meta';
export const mergechannels={ Example: mergechannels_E, ...mergechannels_m };
import demotunnel_E from './demotunnel';
import * as demotunnel_m from './demotunnel/meta';
export const demotunnel={ Example: demotunnel_E, ...demotunnel_m };
import demodesert_E from './demodesert';
import * as demodesert_m from './demodesert/meta';
export const demodesert={ Example: demodesert_E, ...demodesert_m };
import demodesertcrt_E from './demodesertcrt';
import * as demodesertcrt_m from './demodesertcrt/meta';
export const demodesertcrt={ Example: demodesertcrt_E, ...demodesertcrt_m };
import ibex_E from './ibex';
import * as ibex_m from './ibex/meta';
export const ibex={ Example: ibex_E, ...ibex_m };

View File

@ -0,0 +1,33 @@
hellogl
helloblue
helloblueanim
colordisc
gradients
diamondcrop
diamondhello
diamondanim
heart
animated
saturation
colorscale
distortion
transitions
glsledit
paint
pixeleditor
blurxy
blurxydownscale
blurmulti
blurmap
blurmapmouse
blurmapdyn
blurimgtitle
gol
golglider
golrot
golrotscu
mergechannels
demotunnel
demodesert
demodesertcrt
ibex

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Some files were not shown because too many files have changed in this diff Show More