Skip to content

Commit

Permalink
Merge pull request #57 from Shopify/fix/49
Browse files Browse the repository at this point in the history
Update documentation on uniform types + Minor shader improvements
  • Loading branch information
chrfalch authored Jan 3, 2022
2 parents f913a5c + b8b6b56 commit f859d14
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 29 deletions.
Binary file modified docs/docs/shaders/assets/simple-uniform.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 16 additions & 11 deletions docs/docs/shaders/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ if (!source) {
Creates a shader from source.
Shaders can be nested with one another.

| Name | Type | Description |
|:---------|:----------------|:------------------------------|
| source | `RuntimeEffect` | Compiled shaders |
| uniforms | `number` | uniform values |
| children | `Shader` | Shaders to be used as uniform |
| Name | Type | Description |
|:---------|:---------------------------------------------------------------------|:------------------------------|
| source | `RuntimeEffect` | Compiled shaders |
| uniforms | <code>{ [name: string]: number &#124; Vector &#124; number[]}</code> | uniform values |
| children | `Shader` | Shaders to be used as uniform |

### Simple Shader

Expand Down Expand Up @@ -63,25 +63,30 @@ const SimpleShader = () => {

### Using Uniforms

Uniforms are variables used to parametrize shaders.
The following uniform types are supported: `float`, `float2`, `float3`, `float4`, `float2x2`, `float3x3`, `float4x4`, `int`, `int2`, `int3` and, `int4`.

```tsx twoslash
import {Canvas, Skia, Paint, Shader, Fill} from "@shopify/react-native-skia";
import {Canvas, Skia, Paint, Shader, Fill, vec} from "@shopify/react-native-skia";

const source = Skia.RuntimeEffect.Make(`
uniform float blue;
uniform vec2 c;
uniform float r;
uniform float blue;
vec4 main(vec2 pos) {
vec2 normalized = pos/vec2(2 * r);
return distance(pos, vec2(r)) > r ? vec4(1) : vec4(normalized.x, normalized.y, blue, 1);
return distance(pos, c) > r ? vec4(1) : vec4(normalized, blue, 1);
}`)!;

const UniformShader = () => {
const blue = 1.0;
const r = 128;
const c = vec(2 * r, r);
const blue = 1.0;
return (
<Canvas style={{ flex: 1 }}>
<Paint>
<Shader source={source} uniforms={[blue, r]} />
<Shader source={source} uniforms={{ c, r, blue }} />
</Paint>
<Fill />
</Canvas>
Expand Down Expand Up @@ -122,4 +127,4 @@ const NestedShader = () => {
};
```

![Simple Shader](assets/nested.png)
![Simple Shader](assets/nested.png)
5 changes: 4 additions & 1 deletion example/src/Examples/Filters/Filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export const Filters = () => {
return (
<Canvas style={{ width, height }}>
<Paint>
<Shader source={source} uniforms={() => [mix(progress.value, 1, 100)]}>
<Shader
source={source}
uniforms={() => ({ r: mix(progress.value, 1, 100) })}
>
<ImageShader
source={require("../../assets/oslo.jpg")}
fit="cover"
Expand Down
6 changes: 2 additions & 4 deletions example/src/Examples/Hue/Hue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ const { width, height } = Dimensions.get("window");
const c = vec(width / 2, height / 2);

const source = Skia.RuntimeEffect.Make(`
uniform float cx;
uniform float cy;
uniform float2 c;
uniform float r;
${ShaderLib.Math}
Expand All @@ -31,7 +30,6 @@ float quadraticIn(float t) {
}
half4 main(vec2 uv) {
float2 c = vec2(cx, cy);
float mag = distance(uv, c);
float theta = normalizeRad(canvas2Polar(uv, c).x);
return hsv2rgb(vec3(theta/TAU, quadraticIn(mag/r), 1.0));
Expand All @@ -52,7 +50,7 @@ export const Hue = () => {
<Fill color="#1f1f1f" />
<Paint>
<BlurMask sigma={40} style="solid" />
<Shader source={source} uniforms={[c.x, c.y, r]} />
<Shader source={source} uniforms={{ c, r }} />
</Paint>
<Circle c={c} r={r} />
<Circle
Expand Down
11 changes: 7 additions & 4 deletions package/cpp/api/JsiSKRuntimeEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace RNSkia
{
auto uniforms = castUniforms(runtime, arguments[0]);
auto isOpaque = count >= 2 && arguments[1].isBool() ? arguments[1].getBool() : false;
auto matrix = count == 3 ? JsiSkMatrix::fromValue(runtime, arguments[2]).get() : nullptr;
auto matrix = count >= 3 && !arguments[2].isUndefined() && !arguments[2].isNull() ? JsiSkMatrix::fromValue(runtime, arguments[2]).get() : nullptr;

// Create and return shader as host object
auto shader = getObject()->makeShader(std::move(uniforms), nullptr,
Expand Down Expand Up @@ -60,7 +60,7 @@ namespace RNSkia
children.push_back(shader);
}

auto matrix = count == 4 ? JsiSkMatrix::fromValue(runtime, arguments[3]).get() : nullptr;
auto matrix = count >= 4 && !arguments[3].isUndefined() && !arguments[3].isNull() ? JsiSkMatrix::fromValue(runtime, arguments[3]).get() : nullptr;

// Create and return shader as host object
auto shader = getObject()->makeShader(std::move(uniforms), children.data(),
Expand Down Expand Up @@ -131,8 +131,11 @@ namespace RNSkia
// Convert to skia uniforms
for (int i = 0; i < jsiUniformsSize; i++)
{
// We only support floats for now
float value = jsiUniforms.getValueAtIndex(runtime, i).asNumber();
auto it = getObject()->uniforms().begin() + i;
RuntimeEffectUniform u = fromUniform(*it);
float fValue = jsiUniforms.getValueAtIndex(runtime, i).asNumber();
int iValue = static_cast<int>(fValue);
auto value = u.isInteger ? iValue : fValue;
memcpy(SkTAddOffset<void>(uniforms->writable_data(), i * sizeof(value)),
&value, sizeof(value));
}
Expand Down
35 changes: 29 additions & 6 deletions package/src/renderer/components/shaders/Shader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,47 @@ import type { ReactNode } from "react";

import { isShader } from "../../../skia";
import type { IRuntimeEffect } from "../../../skia";
import type { Vector, AnimatedProps, TransformProps } from "../../processors";
import { useDeclaration } from "../../nodes/Declaration";
import type { AnimatedProps } from "../../processors/Animations/Animations";
import { localMatrix } from "../../processors";
import { hasProperty } from "../../typeddash";

export interface ShaderProps {
const isVector = (obj: unknown): obj is Vector =>
hasProperty(obj, "x") && hasProperty(obj, "y");

type Uniform = number | number[] | Vector;

interface Uniforms {
[name: string]: Uniform;
}

export interface ShaderProps extends TransformProps {
source: IRuntimeEffect;
uniforms: number[];
uniforms: Uniforms;
isOpaque?: boolean;
children?: ReactNode | ReactNode[];
}

export const Shader = (props: AnimatedProps<ShaderProps>) => {
const declaration = useDeclaration<ShaderProps>(
props,
({ uniforms, source, isOpaque }, children) => {
({ uniforms, source, isOpaque, ...transform }, children) => {
const processedUniforms = new Array(source.getUniformCount())
.fill(0)
.map((_, i) => {
const name = source.getUniformName(i);
const value = uniforms[name];
if (isVector(value)) {
return [value.x, value.y];
}
return value;
})
.flat(4);
return source.makeShaderWithChildren(
uniforms,
processedUniforms,
isOpaque,
children.filter(isShader)
children.filter(isShader),
localMatrix(transform)
);
}
);
Expand Down
4 changes: 1 addition & 3 deletions package/src/renderer/processors/Shapes.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { ReactNode } from "react";

import type { IRect, IRRect } from "../../skia";
import { hasProperty } from "../typeddash";

import type { Vector as Point } from "./math/Vector";
import { vec } from "./math/Vector";
Expand All @@ -24,9 +25,6 @@ interface ScalarCircleDef {

export type CircleDef = PointCircleDef | ScalarCircleDef;

const hasProperty = (obj: unknown, key: string) =>
!!(typeof obj === "object" && obj !== null && key in obj);

const isCircleScalarDef = (def: CircleDef): def is ScalarCircleDef =>
hasProperty(def, "cx");
export const processCircle = (def: CircleDef) => {
Expand Down
3 changes: 3 additions & 0 deletions package/src/renderer/typeddash.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
export const mapKeys = <T>(obj: T) => Object.keys(obj) as (keyof T)[];

export const hasProperty = (obj: unknown, key: string) =>
!!(typeof obj === "object" && obj !== null && key in obj);

export const exhaustiveCheck = (a: never): never => {
throw new Error(`Unexhaustive handling for ${a}`);
};

0 comments on commit f859d14

Please sign in to comment.