Created
March 9, 2021 17:06
-
-
Save rsms/cb7ab1daa08b75a99115a1057dccc676 to your computer and use it in GitHub Desktop.
Simple Skia SkSL example. Compile & link with Skia (tested with f04e09cd9b8ce23cc8314b6e3769c67aa2d22a7a)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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