Skip to content

Instantly share code, notes, and snippets.

@rsms
Created March 9, 2021 17:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rsms/cb7ab1daa08b75a99115a1057dccc676 to your computer and use it in GitHub Desktop.
Save rsms/cb7ab1daa08b75a99115a1057dccc676 to your computer and use it in GitHub Desktop.
Simple Skia SkSL example. Compile & link with Skia (tested with f04e09cd9b8ce23cc8314b6e3769c67aa2d22a7a)
#include <stdio.h>
#include "tools/sk_app/Application.h"
#include "tools/sk_app/Window.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFont.h"
#include "include/core/SkTime.h"
#include "include/core/SkGraphics.h"
#include "include/core/SkSurface.h"
#include "include/effects/SkGradientShader.h"
#include "include/effects/SkPerlinNoiseShader.h"
#include "include/effects/SkRuntimeEffect.h"
#ifdef DEBUG
#define dlog(format, ...) \
fprintf(stderr, "D " format " (%s:%d)\n", ##__VA_ARGS__, __FILE__, __LINE__)
#define errlog(format, ...) \
fprintf(stderr, "E " format " (%s:%d)\n", ##__VA_ARGS__, __FILE__, __LINE__)
#else
#define dlog(...) do{}while(0)
#define errlog(format, ...) fprintf(stderr, "E " format "\n", ##__VA_ARGS__)
#endif
using namespace sk_app;
using float4 = std::array<float, 4>;
class App : public Application, Window::Layer {
public:
App(int argc, char** argv, void* platformData);
~App() override;
void onIdle() override;
void onBackendCreated() override;
void onAttach(Window* window) override;
void onPaint(SkSurface*) override;
bool onChar(SkUnichar c, skui::ModifierKey modifiers) override;
void buildTestShader();
private:
void updateTitle();
Window* fWindow;
Window::BackendType fBackendType;
double fTimeBase = 0;
SkRuntimeShaderBuilder* fShaderBuilder = nullptr;
};
Application* Application::Create(int argc, char** argv, void* platformData) {
return new App(argc, argv, platformData);
}
App::App(int argc, char** argv, void* platformData)
: fBackendType(Window::kMetal_BackendType)
// : fBackendType(Window::kNativeGL_BackendType)
{
SkGraphics::Init();
buildTestShader();
fWindow = Window::CreateNativeWindow(platformData);
fWindow->setRequestedDisplayParams(DisplayParams());
fWindow->pushLayer(this);
fWindow->attach(fBackendType);
}
App::~App() {
fWindow->detach();
delete fWindow;
}
void App::updateTitle() {
if (!fWindow /*|| fWindow->sampleCount() <= 1*/ ) {
return;
}
SkString title("playbit scene ");
title.append(
fBackendType == Window::kRaster_BackendType ? "(Raster)" :
fBackendType == Window::kMetal_BackendType ? "(Metal)" :
"(OpenGL)");
fWindow->setTitle(title.c_str());
}
void App::onAttach(Window* window) {
dlog("App::onAttach");
}
void App::buildTestShader() {
// compile an SkSL shader
const char* sksl_src = R"(
uniform float uTime;
vec3 mod289(vec3 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec2 mod289(vec2 x) {
return x - floor(x * (1.0 / 289.0)) * 289.0;
}
vec3 permute(vec3 x) {
return mod289(((x*34.0)+1.0)*x);
}
// simplex noise
float snoise(vec2 v) {
const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
-0.577350269189626, // -1.0 + 2.0 * C.x
0.024390243902439); // 1.0 / 41.0
// First corner
vec2 i = floor(v + dot(v, C.yy) );
vec2 x0 = v - i + dot(i, C.xx);
// Other corners
vec2 i1;
//i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
//i1.y = 1.0 - i1.x;
i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
// x0 = x0 - 0.0 + 0.0 * C.xx ;
// x1 = x0 - i1 + 1.0 * C.xx ;
// x2 = x0 - 1.0 + 2.0 * C.xx ;
vec4 x12 = x0.xyxy + C.xxzz;
x12.xy -= i1;
// Permutations
i = mod289(i); // Avoid truncation effects in permutation
vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
+ i.x + vec3(0.0, i1.x, 1.0 ));
vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
m = m*m ;
m = m*m ;
// Gradients: 41 points uniformly over a line, mapped onto a diamond.
// The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
vec3 x = 2.0 * fract(p * C.www) - 1.0;
vec3 h = abs(x) - 0.5;
vec3 ox = floor(x + 0.5);
vec3 a0 = x - ox;
// Normalise gradients implicitly by scaling m
// Approximation of: m *= inversesqrt( a0*a0 + h*h );
m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
// Compute final noise value at P
vec3 g;
g.x = a0.x * x0.x + h.x * x0.y;
g.yz = a0.yz * x12.xz + h.yz * x12.yw;
return 130.0 * dot(m, g);
}
float4 main(float2 fragCoord) {
float scale = 0.002;
float2 st = fragCoord * 0.0013;
return float4(
float3(
scale * ((sk_FragCoord.x) - 0.5) * abs(sin(uTime * 0.1)),
scale * ((sk_FragCoord.y) - 0.5) * abs(cos(uTime * 0.4)),
abs(sin(uTime * 0.5))
) * (0.8 + snoise(st + abs(uTime * 0.2))*0.2),
1);
}
)";
auto [effect, errorText] = SkRuntimeEffect::Make(SkString(sksl_src));
if (!effect) {
errlog("sksl didn't compile: %s", errorText.c_str());
return;
}
dlog("sksl compiled OK");
if (fShaderBuilder) {
delete fShaderBuilder;
}
fShaderBuilder = new SkRuntimeShaderBuilder(std::move(effect));
}
void App::onBackendCreated() {
dlog("App::onBackendCreated");
this->updateTitle();
fWindow->show();
fWindow->inval();
fTimeBase = SkTime::GetSecs();
}
void App::onPaint(SkSurface* surface) {
auto time = (float)(SkTime::GetSecs() - fTimeBase);
auto canvas = surface->getCanvas();
// Clear background
canvas->clear(SK_ColorWHITE);
if (fShaderBuilder) {
if (!fShaderBuilder->uniform("uTime").set(&time, 1)) {
errlog("failed to set uniform uTime");
}
auto shader = fShaderBuilder->makeShader(nullptr, false);
if (!shader) {
errlog("fShaderBuilder->makeShader failed");
} else {
SkPaint shader1;
shader1.setShader(std::move(shader));
shader1.setBlendMode(SkBlendMode::kSrc);
canvas->save();
canvas->drawPaint(shader1);
//SkPaint noiseShader;
//noiseShader.setShader(SkPerlinNoiseShader::MakeFractalNoise(0.5, 0.5, 4, time*120, nullptr));
//noiseShader.setAlphaf(0.5);
//canvas->drawPaint(noiseShader);
canvas->restore();
}
}
}
void App::onIdle() {
// Just re-paint continously
fWindow->inval();
}
bool App::onChar(SkUnichar c, skui::ModifierKey modifiers) {
if (' ' == c) {
fBackendType = (
fBackendType == Window::kRaster_BackendType ? Window::kNativeGL_BackendType :
fBackendType == Window::kNativeGL_BackendType ? Window::kMetal_BackendType :
Window::kRaster_BackendType
);
fWindow->detach();
fWindow->attach(fBackendType);
}
return true;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment