Restore SkLightingShader and associated classes
This reverts https://skia-review.googlesource.com/c/skia/+/31140 (Remove SkLightingShader and associated classes) and updates the classes to ToT Change-Id: I3b1df1704cca8907aa00f081a7e93339b65ad4fa Reviewed-on: https://skia-review.googlesource.com/141545 Reviewed-by: Mike Reed <reed@google.com> Commit-Queue: Robert Phillips <robertphillips@google.com>
This commit is contained in:
parent
9e0d7e4072
commit
a8cdbd7431
160
gm/lightingshader.cpp
Normal file
160
gm/lightingshader.cpp
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
#include "sk_tool_utils.h"
|
||||
#include "SkLightingShader.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkPoint3.h"
|
||||
#include "SkShader.h"
|
||||
|
||||
// Create a hemispherical normal map
|
||||
static SkBitmap make_hemi_normalmap(int texSize) {
|
||||
SkBitmap hemi;
|
||||
hemi.allocN32Pixels(texSize, texSize);
|
||||
|
||||
sk_tool_utils::create_hemi_normal_map(&hemi, SkIRect::MakeWH(texSize, texSize));
|
||||
return hemi;
|
||||
}
|
||||
|
||||
// Create a truncated pyramid normal map
|
||||
static SkBitmap make_frustum_normalmap(int texSize) {
|
||||
SkBitmap frustum;
|
||||
frustum.allocN32Pixels(texSize, texSize);
|
||||
|
||||
sk_tool_utils::create_frustum_normal_map(&frustum, SkIRect::MakeWH(texSize, texSize));
|
||||
return frustum;
|
||||
}
|
||||
|
||||
// Create a tetrahedral normal map
|
||||
static SkBitmap make_tetra_normalmap(int texSize) {
|
||||
SkBitmap tetra;
|
||||
tetra.allocN32Pixels(texSize, texSize);
|
||||
|
||||
sk_tool_utils::create_tetra_normal_map(&tetra, SkIRect::MakeWH(texSize, texSize));
|
||||
return tetra;
|
||||
}
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
// This GM exercises lighting shaders by drawing rotated and non-rotated normal mapped rects with
|
||||
// a directional light off to the viewers right.
|
||||
class LightingShaderGM : public GM {
|
||||
public:
|
||||
LightingShaderGM() {
|
||||
this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
|
||||
}
|
||||
|
||||
protected:
|
||||
enum NormalMap {
|
||||
kHemi_NormalMap,
|
||||
kFrustum_NormalMap,
|
||||
kTetra_NormalMap,
|
||||
|
||||
kLast_NormalMap = kTetra_NormalMap
|
||||
};
|
||||
|
||||
static constexpr int kNormalMapCount = kLast_NormalMap+1;
|
||||
|
||||
SkString onShortName() override { return SkString("lightingshader"); }
|
||||
|
||||
SkISize onISize() override { return SkISize::Make(kGMSize, kGMSize); }
|
||||
|
||||
void onOnceBeforeDraw() override {
|
||||
{
|
||||
SkLights::Builder builder;
|
||||
|
||||
// The direction vector is towards the light w/ +Z coming out of the screen
|
||||
builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
|
||||
SkVector3::Make(SK_ScalarRoot2Over2,
|
||||
0.0f,
|
||||
SK_ScalarRoot2Over2)));
|
||||
builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
|
||||
|
||||
fLights = builder.finish();
|
||||
}
|
||||
|
||||
fDiffuse = sk_tool_utils::create_checkerboard_bitmap(
|
||||
kTexSize, kTexSize,
|
||||
sk_tool_utils::color_to_565(0x0),
|
||||
sk_tool_utils::color_to_565(0xFF804020),
|
||||
8);
|
||||
|
||||
fNormalMaps[kHemi_NormalMap] = make_hemi_normalmap(kTexSize);
|
||||
fNormalMaps[kFrustum_NormalMap] = make_frustum_normalmap(kTexSize);
|
||||
fNormalMaps[kTetra_NormalMap] = make_tetra_normalmap(kTexSize);
|
||||
}
|
||||
|
||||
void drawRect(SkCanvas* canvas, const SkRect& r, NormalMap mapType) {
|
||||
|
||||
SkRect bitmapBounds = SkRect::MakeIWH(fDiffuse.width(), fDiffuse.height());
|
||||
|
||||
SkMatrix matrix;
|
||||
matrix.setRectToRect(bitmapBounds, r, SkMatrix::kFill_ScaleToFit);
|
||||
|
||||
const SkMatrix& ctm = canvas->getTotalMatrix();
|
||||
|
||||
SkPaint paint;
|
||||
sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(fDiffuse,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix);
|
||||
sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(fNormalMaps[mapType],
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix);
|
||||
sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap),
|
||||
ctm);
|
||||
paint.setShader(SkLightingShader::Make(std::move(diffuseShader), std::move(normalSource),
|
||||
fLights));
|
||||
|
||||
canvas->drawRect(r, paint);
|
||||
}
|
||||
|
||||
// Draw an axis-aligned and rotated version of the normal mapped rect
|
||||
void drawPair(SkCanvas* canvas, const SkRect& r, NormalMap mapType, const SkVector& v) {
|
||||
SkMatrix m;
|
||||
m.setRotate(45.0f, r.centerX(), r.centerY());
|
||||
m.postTranslate(kScale * v.fX, kScale * v.fY);
|
||||
|
||||
this->drawRect(canvas, r, mapType);
|
||||
|
||||
canvas->save();
|
||||
canvas->setMatrix(m);
|
||||
this->drawRect(canvas, r, mapType);
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
SkRect r;
|
||||
|
||||
r = SkRect::MakeWH(SkIntToScalar(kTexSize), SkIntToScalar(kTexSize));
|
||||
this->drawPair(canvas, r, kHemi_NormalMap, SkVector::Make(1.0f, 0.0f));
|
||||
|
||||
r.offset(kGMSize - kTexSize, 0);
|
||||
this->drawPair(canvas, r, kFrustum_NormalMap, SkVector::Make(0.0f, 1.0f));
|
||||
|
||||
r.offset(0, kGMSize - kTexSize);
|
||||
this->drawPair(canvas, r, kTetra_NormalMap, SkVector::Make(-1.0, 0.0f));
|
||||
|
||||
r.offset(kTexSize - kGMSize, 0);
|
||||
this->drawPair(canvas, r, kHemi_NormalMap, SkVector::Make(0.0f, -1));
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kTexSize = 128;
|
||||
static constexpr int kGMSize = 512;
|
||||
static constexpr SkScalar kScale = kGMSize/2.0f - kTexSize/2.0f;
|
||||
|
||||
SkBitmap fDiffuse;
|
||||
SkBitmap fNormalMaps[kNormalMapCount];
|
||||
|
||||
sk_sp<SkLights> fLights;
|
||||
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DEF_GM(return new LightingShaderGM;)
|
||||
}
|
278
gm/lightingshader2.cpp
Normal file
278
gm/lightingshader2.cpp
Normal file
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
#include "sk_tool_utils.h"
|
||||
#include "SkLightingShader.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkPoint3.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkTypeface.h"
|
||||
|
||||
// Create a truncated pyramid normal map
|
||||
static SkBitmap make_frustum_normalmap(int texSize) {
|
||||
SkBitmap frustum;
|
||||
frustum.allocN32Pixels(texSize, texSize);
|
||||
|
||||
sk_tool_utils::create_frustum_normal_map(&frustum, SkIRect::MakeWH(texSize, texSize));
|
||||
return frustum;
|
||||
}
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
// This GM exercises lighting shaders. Specifically, nullptr arguments, scaling when using
|
||||
// normal maps, paint transparency, zero directional lights, multiple directional lights.
|
||||
class LightingShader2GM : public GM {
|
||||
public:
|
||||
LightingShader2GM() : fRect(SkRect::MakeIWH(kTexSize, kTexSize)) {
|
||||
this->setBGColor(sk_tool_utils::color_to_565(0xFF0000CC));
|
||||
}
|
||||
|
||||
protected:
|
||||
SkString onShortName() override {
|
||||
return SkString("lightingshader2");
|
||||
}
|
||||
|
||||
SkISize onISize() override {
|
||||
return SkISize::Make(600, 740);
|
||||
}
|
||||
|
||||
void onOnceBeforeDraw() override {
|
||||
// The light direction is towards the light with +Z coming out of the screen
|
||||
const SkVector3 kLightFromUpperRight = SkVector3::Make(0.788f, 0.394f, 0.473f);
|
||||
const SkVector3 kLightFromUpperLeft = SkVector3::Make(-0.788f, 0.394f, 0.473f);
|
||||
|
||||
// Standard set of lights
|
||||
{
|
||||
SkLights::Builder builder;
|
||||
builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
|
||||
kLightFromUpperRight));
|
||||
builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
|
||||
fLights = builder.finish();
|
||||
}
|
||||
|
||||
// No directional lights
|
||||
{
|
||||
SkLights::Builder builder;
|
||||
builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
|
||||
fLightsNoDir = builder.finish();
|
||||
}
|
||||
|
||||
// Two directional lights
|
||||
{
|
||||
SkLights::Builder builder;
|
||||
builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 0.0f, 0.0f),
|
||||
kLightFromUpperRight));
|
||||
builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(0.0f, 1.0f, 0.0f),
|
||||
kLightFromUpperLeft));
|
||||
builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
|
||||
fLightsTwoDir = builder.finish();
|
||||
}
|
||||
|
||||
SkMatrix matrix;
|
||||
SkRect bitmapBounds = SkRect::MakeIWH(kTexSize, kTexSize);
|
||||
matrix.setRectToRect(bitmapBounds, fRect, SkMatrix::kFill_ScaleToFit);
|
||||
|
||||
SkBitmap opaqueDiffuseMap = sk_tool_utils::create_checkerboard_bitmap(
|
||||
kTexSize, kTexSize, SK_ColorBLACK,
|
||||
sk_tool_utils::color_to_565(0xFF808080),
|
||||
8);
|
||||
fOpaqueDiffuse = SkShader::MakeBitmapShader(opaqueDiffuseMap, SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode, &matrix);
|
||||
|
||||
SkBitmap translucentDiffuseMap = sk_tool_utils::create_checkerboard_bitmap(
|
||||
kTexSize, kTexSize,
|
||||
SkColorSetARGB(0x55, 0x00, 0x00, 0x00),
|
||||
SkColorSetARGB(0x55, 0x80, 0x80, 0x80),
|
||||
8);
|
||||
fTranslucentDiffuse = SkShader::MakeBitmapShader(translucentDiffuseMap,
|
||||
SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode, &matrix);
|
||||
|
||||
SkBitmap normalMap = make_frustum_normalmap(kTexSize);
|
||||
fNormalMapShader = SkShader::MakeBitmapShader(normalMap, SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode, &matrix);
|
||||
|
||||
}
|
||||
|
||||
// Scales shape around origin, rotates shape around origin, then translates shape to origin
|
||||
void positionCTM(SkCanvas *canvas, SkScalar scaleX, SkScalar scaleY, SkScalar rotate) const {
|
||||
canvas->translate(kTexSize/2.0f, kTexSize/2.0f);
|
||||
canvas->scale(scaleX, scaleY);
|
||||
canvas->rotate(rotate);
|
||||
canvas->translate(-kTexSize/2.0f, -kTexSize/2.0f);
|
||||
}
|
||||
|
||||
void drawRect(SkCanvas* canvas, SkScalar scaleX, SkScalar scaleY,
|
||||
SkScalar rotate, bool useNormalSource, bool useDiffuseShader,
|
||||
bool useTranslucentPaint, bool useTranslucentShader, sk_sp<SkLights> lights) {
|
||||
canvas->save();
|
||||
|
||||
this->positionCTM(canvas, scaleX, scaleY, rotate);
|
||||
|
||||
const SkMatrix& ctm = canvas->getTotalMatrix();
|
||||
|
||||
SkPaint paint;
|
||||
sk_sp<SkNormalSource> normalSource = nullptr;
|
||||
sk_sp<SkShader> diffuseShader = nullptr;
|
||||
|
||||
if (useNormalSource) {
|
||||
normalSource = SkNormalSource::MakeFromNormalMap(fNormalMapShader, ctm);
|
||||
}
|
||||
|
||||
if (useDiffuseShader) {
|
||||
diffuseShader = (useTranslucentShader) ? fTranslucentDiffuse : fOpaqueDiffuse;
|
||||
} else {
|
||||
paint.setColor(SK_ColorGREEN);
|
||||
}
|
||||
|
||||
if (useTranslucentPaint) {
|
||||
paint.setAlpha(0x99);
|
||||
}
|
||||
|
||||
paint.setShader(SkLightingShader::Make(std::move(diffuseShader), std::move(normalSource),
|
||||
std::move(lights)));
|
||||
canvas->drawRect(fRect, paint);
|
||||
|
||||
canvas->restore();
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
SkPaint labelPaint;
|
||||
labelPaint.setTypeface(sk_tool_utils::create_portable_typeface("sans-serif",SkFontStyle()));
|
||||
labelPaint.setAntiAlias(true);
|
||||
labelPaint.setTextSize(kLabelSize);
|
||||
|
||||
int gridNum = 0;
|
||||
|
||||
// Running through all possible bool parameter combinations
|
||||
for (bool useNormalSource : {true, false}) {
|
||||
for (bool useDiffuseShader : {true, false}) {
|
||||
for (bool useTranslucentPaint : {true, false}) {
|
||||
for (bool useTranslucentShader : {true, false}) {
|
||||
|
||||
// Determining position
|
||||
SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
|
||||
SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
|
||||
|
||||
canvas->save();
|
||||
|
||||
canvas->translate(xPos, yPos);
|
||||
this->drawRect(canvas, 1.0f, 1.0f, 0.f, useNormalSource, useDiffuseShader,
|
||||
useTranslucentPaint, useTranslucentShader, fLights);
|
||||
// Drawing labels
|
||||
canvas->translate(0.0f, SkIntToScalar(kTexSize));
|
||||
{
|
||||
canvas->translate(0.0f, kLabelSize);
|
||||
SkString label;
|
||||
label.appendf("useNormalSource: %d", useNormalSource);
|
||||
canvas->drawString(label, 0.0f, 0.0f, labelPaint);
|
||||
}
|
||||
{
|
||||
canvas->translate(0.0f, kLabelSize);
|
||||
SkString label;
|
||||
label.appendf("useDiffuseShader: %d", useDiffuseShader);
|
||||
canvas->drawString(label, 0.0f, 0.0f, labelPaint);
|
||||
}
|
||||
{
|
||||
canvas->translate(0.0f, kLabelSize);
|
||||
SkString label;
|
||||
label.appendf("useTranslucentPaint: %d", useTranslucentPaint);
|
||||
canvas->drawString(label, 0.0f, 0.0f, labelPaint);
|
||||
}
|
||||
{
|
||||
canvas->translate(0.0f, kLabelSize);
|
||||
SkString label;
|
||||
label.appendf("useTranslucentShader: %d", useTranslucentShader);
|
||||
canvas->drawString(label, 0.0f, 0.0f, labelPaint);
|
||||
}
|
||||
|
||||
canvas->restore();
|
||||
|
||||
gridNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Rotation/scale test
|
||||
{
|
||||
SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
|
||||
SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
|
||||
|
||||
canvas->save();
|
||||
canvas->translate(xPos, yPos);
|
||||
this->drawRect(canvas, 0.6f, 0.6f, 45.0f, true, true, true, true, fLights);
|
||||
canvas->restore();
|
||||
|
||||
gridNum++;
|
||||
}
|
||||
|
||||
// Anisotropic scale test
|
||||
{
|
||||
SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
|
||||
SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
|
||||
|
||||
canvas->save();
|
||||
canvas->translate(xPos, yPos);
|
||||
this->drawRect(canvas, 0.6f, 0.4f, 30.0f, true, true, true, true, fLights);
|
||||
canvas->restore();
|
||||
|
||||
gridNum++;
|
||||
}
|
||||
|
||||
// No directional lights test
|
||||
{
|
||||
SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
|
||||
SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
|
||||
|
||||
canvas->save();
|
||||
canvas->translate(xPos, yPos);
|
||||
this->drawRect(canvas, 1.0f, 1.0f, 0.0f, true, true, false, false, fLightsNoDir);
|
||||
canvas->restore();
|
||||
|
||||
gridNum++;
|
||||
}
|
||||
|
||||
// Two directional lights test
|
||||
{
|
||||
SkScalar xPos = (gridNum % kGridColumnNum) * kGridCellWidth;
|
||||
SkScalar yPos = (gridNum / kGridColumnNum) * kGridCellWidth;
|
||||
|
||||
canvas->save();
|
||||
canvas->translate(xPos, yPos);
|
||||
this->drawRect(canvas, 1.0f, 1.0f, 0.0f, true, true, false, false, fLightsTwoDir);
|
||||
canvas->restore();
|
||||
|
||||
gridNum++;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int kTexSize = 96;
|
||||
static constexpr int kNumBooleanParams = 4;
|
||||
static constexpr SkScalar kLabelSize = 10.0f;
|
||||
static constexpr int kGridColumnNum = 4;
|
||||
static constexpr SkScalar kGridCellWidth = kTexSize + 20.0f + kNumBooleanParams * kLabelSize;
|
||||
|
||||
sk_sp<SkShader> fOpaqueDiffuse;
|
||||
sk_sp<SkShader> fTranslucentDiffuse;
|
||||
sk_sp<SkShader> fNormalMapShader;
|
||||
|
||||
const SkRect fRect;
|
||||
sk_sp<SkLights> fLights;
|
||||
sk_sp<SkLights> fLightsNoDir;
|
||||
sk_sp<SkLights> fLightsTwoDir;
|
||||
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DEF_GM(return new LightingShader2GM;)
|
||||
}
|
10
gn/core.gni
10
gn/core.gni
@ -165,6 +165,7 @@ skia_core_sources = [
|
||||
"$_src/core/SkImageInfo.cpp",
|
||||
"$_src/core/SkImageCacherator.h",
|
||||
"$_src/core/SkImageGenerator.cpp",
|
||||
"$_src/core/SkLights.cpp",
|
||||
"$_src/core/SkLineClipper.cpp",
|
||||
"$_src/core/SkLiteDL.cpp",
|
||||
"$_src/core/SkLiteRecorder.cpp",
|
||||
@ -199,6 +200,12 @@ skia_core_sources = [
|
||||
"$_src/core/SkNextID.h",
|
||||
"$_src/core/SkLatticeIter.cpp",
|
||||
"$_src/core/SkLatticeIter.h",
|
||||
"$_src/core/SkNormalFlatSource.cpp",
|
||||
"$_src/core/SkNormalFlatSource.h",
|
||||
"$_src/core/SkNormalMapSource.cpp",
|
||||
"$_src/core/SkNormalMapSource.h",
|
||||
"$_src/core/SkNormalSource.cpp",
|
||||
"$_src/core/SkNormalSource.h",
|
||||
"$_src/core/SkNx.h",
|
||||
"$_src/core/SkOpts.cpp",
|
||||
"$_src/core/SkOpts.h",
|
||||
@ -374,6 +381,8 @@ skia_core_sources = [
|
||||
"$_src/shaders/SkEmptyShader.h",
|
||||
"$_src/shaders/SkImageShader.cpp",
|
||||
"$_src/shaders/SkImageShader.h",
|
||||
"$_src/shaders/SkLightingShader.cpp",
|
||||
"$_src/shaders/SkLightingShader.h",
|
||||
"$_src/shaders/SkLocalMatrixShader.cpp",
|
||||
"$_src/shaders/SkLocalMatrixShader.h",
|
||||
"$_src/shaders/SkPictureShader.cpp",
|
||||
@ -406,6 +415,7 @@ skia_core_sources = [
|
||||
"$_include/core/SkImageFilter.h",
|
||||
"$_include/core/SkImageGenerator.h",
|
||||
"$_include/core/SkImageInfo.h",
|
||||
"$_include/core/SkLights.h",
|
||||
"$_include/core/SkMallocPixelRef.h",
|
||||
"$_include/core/SkMaskFilter.h",
|
||||
"$_include/core/SkMath.h",
|
||||
|
@ -198,6 +198,8 @@ gm_sources = [
|
||||
"$_gm/lcdoverlap.cpp",
|
||||
"$_gm/lcdtext.cpp",
|
||||
"$_gm/lighting.cpp",
|
||||
"$_gm/lightingshader.cpp",
|
||||
"$_gm/lightingshader2.cpp",
|
||||
"$_gm/linepaths.cpp",
|
||||
"$_gm/localmatriximagefilter.cpp",
|
||||
"$_gm/localmatriximageshader.cpp",
|
||||
|
@ -60,7 +60,9 @@ samples_sources = [
|
||||
"$_samplecode/SampleLayerMask.cpp",
|
||||
"$_samplecode/SampleLayers.cpp",
|
||||
"$_samplecode/SampleLCD.cpp",
|
||||
"$_samplecode/SampleLighting.cpp",
|
||||
"$_samplecode/SampleLines.cpp",
|
||||
"$_samplecode/SampleLitAtlas.cpp",
|
||||
"$_samplecode/SampleManyRects.cpp",
|
||||
"$_samplecode/SampleMeasure.cpp",
|
||||
"$_samplecode/SampleMegaStroke.cpp",
|
||||
|
@ -37,7 +37,7 @@ public:
|
||||
kSkShaderBase_Type,
|
||||
kSkUnused_Type, // used to be SkUnitMapper
|
||||
kSkUnused_Type2,
|
||||
kSkUnused_Type3, // used to be SkNormalSource
|
||||
kSkNormalSource_Type,
|
||||
};
|
||||
|
||||
typedef sk_sp<SkFlattenable> (*Factory)(SkReadBuffer&);
|
||||
|
195
include/core/SkLights.h
Normal file
195
include/core/SkLights.h
Normal file
@ -0,0 +1,195 @@
|
||||
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkLights_DEFINED
|
||||
#define SkLights_DEFINED
|
||||
|
||||
#include "SkPoint3.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "../private/SkTArray.h"
|
||||
|
||||
class SkColorSpaceXformer;
|
||||
class SkReadBuffer;
|
||||
class SkWriteBuffer;
|
||||
|
||||
/** \class SkLights
|
||||
SkLights encapsulates a set of directional, point and ambient lights for use with the
|
||||
SkLightingShader.
|
||||
*/
|
||||
class SK_API SkLights : public SkRefCnt {
|
||||
public:
|
||||
class Light {
|
||||
public:
|
||||
enum LightType {
|
||||
kDirectional_LightType,
|
||||
kPoint_LightType
|
||||
};
|
||||
|
||||
Light(const Light& other)
|
||||
: fType(other.fType)
|
||||
, fColor(other.fColor)
|
||||
, fDirOrPos(other.fDirOrPos)
|
||||
, fIntensity(other.fIntensity) {}
|
||||
|
||||
Light(Light&& other)
|
||||
: fType(other.fType)
|
||||
, fColor(other.fColor)
|
||||
, fDirOrPos(other.fDirOrPos)
|
||||
, fIntensity(other.fIntensity) {}
|
||||
|
||||
static Light MakeDirectional(const SkColor3f& color, const SkVector3& dir) {
|
||||
Light light(kDirectional_LightType, color, dir, 0.0f);
|
||||
if (!light.fDirOrPos.normalize()) {
|
||||
light.fDirOrPos.set(0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
return light;
|
||||
}
|
||||
|
||||
static Light MakePoint(const SkColor3f& color, const SkPoint3& pos, SkScalar intensity) {
|
||||
return Light(kPoint_LightType, color, pos, intensity);
|
||||
}
|
||||
|
||||
LightType type() const { return fType; }
|
||||
const SkColor3f& color() const { return fColor; }
|
||||
const SkVector3& dir() const {
|
||||
SkASSERT(kDirectional_LightType == fType);
|
||||
return fDirOrPos;
|
||||
}
|
||||
const SkPoint3& pos() const {
|
||||
SkASSERT(kPoint_LightType == fType);
|
||||
return fDirOrPos;
|
||||
}
|
||||
SkScalar intensity() const {
|
||||
SkASSERT(kPoint_LightType == fType);
|
||||
return fIntensity;
|
||||
}
|
||||
|
||||
Light& operator=(const Light& other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
fType = other.fType;
|
||||
fColor = other.fColor;
|
||||
fDirOrPos = other.fDirOrPos;
|
||||
fIntensity = other.fIntensity;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const Light& other) {
|
||||
return (fType == other.fType) &&
|
||||
(fColor == other.fColor) &&
|
||||
(fDirOrPos == other.fDirOrPos) &&
|
||||
(fIntensity == other.fIntensity);
|
||||
}
|
||||
|
||||
bool operator!=(const Light& other) { return !(this->operator==(other)); }
|
||||
|
||||
private:
|
||||
friend class SkLights;
|
||||
|
||||
Light(LightType type, const SkColor3f& color, const SkVector3& dirOrPos,
|
||||
SkScalar intensity)
|
||||
: fType(type)
|
||||
, fColor(color)
|
||||
, fDirOrPos(dirOrPos)
|
||||
, fIntensity(intensity) {}
|
||||
|
||||
LightType fType;
|
||||
SkColor3f fColor; // linear (unpremul) color. Range is 0..1 in each channel.
|
||||
|
||||
SkVector3 fDirOrPos; // For directional lights, holds the direction towards the
|
||||
// light (+Z is out of the screen).
|
||||
// If degenerate, it will be replaced with (0, 0, 1).
|
||||
// For point lights, holds location of point light
|
||||
|
||||
SkScalar fIntensity; // For point lights, dictates the light intensity.
|
||||
// Simply a multiplier to the final light output value.
|
||||
};
|
||||
|
||||
class Builder {
|
||||
public:
|
||||
Builder() : fLights(new SkLights) {}
|
||||
|
||||
void add(const Light& light) {
|
||||
if (fLights) {
|
||||
fLights->fLights.push_back(light);
|
||||
}
|
||||
}
|
||||
|
||||
void add(Light&& light) {
|
||||
if (fLights) {
|
||||
fLights->fLights.push_back(std::move(light));
|
||||
}
|
||||
}
|
||||
|
||||
void setAmbientLightColor(const SkColor3f& color) {
|
||||
if (fLights) {
|
||||
fLights->fAmbientLightColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
sk_sp<SkLights> finish() {
|
||||
return std::move(fLights);
|
||||
}
|
||||
|
||||
private:
|
||||
sk_sp<SkLights> fLights;
|
||||
};
|
||||
|
||||
/** Returns number of lights not including the ambient light.
|
||||
|
||||
@return number of lights not including the ambient light
|
||||
*/
|
||||
int numLights() const { return fLights.count(); }
|
||||
|
||||
/** Returns the index-th light.
|
||||
|
||||
@param index the index of the desired light
|
||||
@return the index-th light
|
||||
*/
|
||||
const Light& light(int index) const { return fLights[index]; }
|
||||
|
||||
/** Returns the ambient light.
|
||||
|
||||
@return the ambient light
|
||||
*/
|
||||
const SkColor3f& ambientLightColor() const {
|
||||
return fAmbientLightColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recreate an SkLights object that was serialized into a buffer.
|
||||
*
|
||||
* @param SkReadBuffer Serialized blob data.
|
||||
* @return A new SkLights representing the serialized data, or NULL if the buffer is
|
||||
* invalid.
|
||||
*/
|
||||
static sk_sp<SkLights> MakeFromBuffer(SkReadBuffer& buf);
|
||||
|
||||
/**
|
||||
* Serialize to a buffer.
|
||||
*
|
||||
* @param buffer the write buffer to write out to
|
||||
*/
|
||||
void flatten(SkWriteBuffer& buf) const;
|
||||
|
||||
private:
|
||||
friend class SkLightingShaderImpl;
|
||||
|
||||
SkLights() : fAmbientLightColor(SkColor3f::Make(0.0f, 0.0f, 0.0f)) {}
|
||||
|
||||
sk_sp<SkLights> makeColorSpace(SkColorSpaceXformer* xformer) const;
|
||||
|
||||
SkTArray<Light> fLights;
|
||||
SkColor3f fAmbientLightColor;
|
||||
|
||||
typedef SkRefCnt INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
109
samplecode/SampleLighting.cpp
Normal file
109
samplecode/SampleLighting.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "DecodeFile.h"
|
||||
#include "SampleCode.h"
|
||||
#include "Resources.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkLightingShader.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkPoint3.h"
|
||||
|
||||
static sk_sp<SkLights> create_lights(SkScalar angle, SkScalar blue) {
|
||||
|
||||
const SkVector3 dir = SkVector3::Make(SkScalarSin(angle)*SkScalarSin(SK_ScalarPI*0.25f),
|
||||
SkScalarCos(angle)*SkScalarSin(SK_ScalarPI*0.25f),
|
||||
SkScalarCos(SK_ScalarPI*0.25f));
|
||||
|
||||
SkLights::Builder builder;
|
||||
|
||||
builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, blue), dir));
|
||||
builder.setAmbientLightColor(SkColor3f::Make(0.1f, 0.1f, 0.1f));
|
||||
|
||||
return builder.finish();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class LightingView : public SampleView {
|
||||
public:
|
||||
LightingView() : fLightAngle(0.0f) , fColorFactor(0.0f) {
|
||||
{
|
||||
SkBitmap diffuseBitmap;
|
||||
SkAssertResult(GetResourceAsBitmap("images/brickwork-texture.jpg", &diffuseBitmap));
|
||||
|
||||
fRect = SkRect::MakeIWH(diffuseBitmap.width(), diffuseBitmap.height());
|
||||
|
||||
fDiffuseShader = SkShader::MakeBitmapShader(diffuseBitmap,
|
||||
SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
{
|
||||
SkBitmap normalBitmap;
|
||||
SkAssertResult(GetResourceAsBitmap("images/brickwork_normal-map.jpg", &normalBitmap));
|
||||
|
||||
sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(normalBitmap,
|
||||
SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode,
|
||||
nullptr);
|
||||
fNormalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap), SkMatrix::I());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
// overrides from SkEventSink
|
||||
bool onQuery(SkEvent* evt) override {
|
||||
if (SampleCode::TitleQ(*evt)) {
|
||||
SampleCode::TitleR(evt, "Lighting");
|
||||
return true;
|
||||
}
|
||||
return this->INHERITED::onQuery(evt);
|
||||
}
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
sk_sp<SkLights> lights(create_lights(fLightAngle, fColorFactor));
|
||||
|
||||
SkPaint paint;
|
||||
paint.setShader(SkLightingShader::Make(fDiffuseShader,
|
||||
fNormalSource,
|
||||
std::move(lights)));
|
||||
paint.setColor(SK_ColorBLACK);
|
||||
|
||||
canvas->drawRect(fRect, paint);
|
||||
}
|
||||
|
||||
SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
|
||||
return this->INHERITED::onFindClickHandler(x, y, modi);
|
||||
}
|
||||
|
||||
bool onAnimate(const SkAnimTimer& timer) override {
|
||||
fLightAngle += 0.015f;
|
||||
fColorFactor += 0.01f;
|
||||
if (fColorFactor > 1.0f) {
|
||||
fColorFactor = 0.0f;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
SkRect fRect;
|
||||
sk_sp<SkShader> fDiffuseShader;
|
||||
sk_sp<SkNormalSource> fNormalSource;
|
||||
|
||||
SkScalar fLightAngle;
|
||||
SkScalar fColorFactor;
|
||||
|
||||
typedef SampleView INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static SkView* MyFactory() { return new LightingView; }
|
||||
static SkViewRegister reg(MyFactory);
|
503
samplecode/SampleLitAtlas.cpp
Normal file
503
samplecode/SampleLitAtlas.cpp
Normal file
@ -0,0 +1,503 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SampleCode.h"
|
||||
#include "SkAnimTimer.h"
|
||||
#include "SkBitmapProcShader.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkDrawable.h"
|
||||
#include "SkLightingShader.h"
|
||||
#include "SkLights.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkRandom.h"
|
||||
#include "SkRSXform.h"
|
||||
#include "SkView.h"
|
||||
|
||||
#include "sk_tool_utils.h"
|
||||
|
||||
// A crude normal mapped asteroids-like sample
|
||||
class DrawLitAtlasDrawable : public SkDrawable {
|
||||
public:
|
||||
DrawLitAtlasDrawable(const SkRect& r)
|
||||
: fBounds(r)
|
||||
, fUseColors(false)
|
||||
, fLightDir(SkVector3::Make(1.0f, 0.0f, 0.0f)) {
|
||||
fAtlas = MakeAtlas();
|
||||
|
||||
SkRandom rand;
|
||||
for (int i = 0; i < kNumAsteroids; ++i) {
|
||||
fAsteroids[i].initAsteroid(&rand, fBounds, &fDiffTex[i], &fNormTex[i]);
|
||||
}
|
||||
|
||||
fShip.initShip(fBounds, &fDiffTex[kNumAsteroids], &fNormTex[kNumAsteroids]);
|
||||
|
||||
this->updateLights();
|
||||
}
|
||||
|
||||
void toggleUseColors() {
|
||||
fUseColors = !fUseColors;
|
||||
}
|
||||
|
||||
void rotateLight() {
|
||||
SkScalar c;
|
||||
SkScalar s = SkScalarSinCos(SK_ScalarPI/6.0f, &c);
|
||||
|
||||
SkScalar newX = c * fLightDir.fX - s * fLightDir.fY;
|
||||
SkScalar newY = s * fLightDir.fX + c * fLightDir.fY;
|
||||
|
||||
fLightDir.set(newX, newY, 0.0f);
|
||||
|
||||
this->updateLights();
|
||||
}
|
||||
|
||||
void left() {
|
||||
SkScalar newRot = SkScalarMod(fShip.rot() + (2*SK_ScalarPI - SK_ScalarPI/32.0f),
|
||||
2 * SK_ScalarPI);
|
||||
fShip.setRot(newRot);
|
||||
}
|
||||
|
||||
void right() {
|
||||
SkScalar newRot = SkScalarMod(fShip.rot() + SK_ScalarPI/32.0f, 2 * SK_ScalarPI);
|
||||
fShip.setRot(newRot);
|
||||
}
|
||||
|
||||
void thrust() {
|
||||
SkScalar c;
|
||||
SkScalar s = SkScalarSinCos(fShip.rot(), &c);
|
||||
|
||||
SkVector newVel = fShip.velocity();
|
||||
newVel.fX += s;
|
||||
newVel.fY += -c;
|
||||
|
||||
SkScalar len = newVel.length();
|
||||
if (len > kMaxShipSpeed) {
|
||||
newVel.setLength(SkIntToScalar(kMaxShipSpeed));
|
||||
}
|
||||
|
||||
fShip.setVelocity(newVel);
|
||||
}
|
||||
|
||||
protected:
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
SkRSXform xforms[kNumAsteroids+kNumShips];
|
||||
SkColor colors[kNumAsteroids+kNumShips];
|
||||
|
||||
for (int i = 0; i < kNumAsteroids; ++i) {
|
||||
fAsteroids[i].advance(fBounds);
|
||||
xforms[i] = fAsteroids[i].asRSXform();
|
||||
if (fUseColors) {
|
||||
colors[i] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
fShip.advance(fBounds);
|
||||
xforms[kNumAsteroids] = fShip.asRSXform();
|
||||
if (fUseColors) {
|
||||
colors[kNumAsteroids] = SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF);
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
canvas->drawBitmap(fAtlas, 0, 0); // just to see the atlas
|
||||
|
||||
this->drawLightDir(canvas, fBounds.centerX(), fBounds.centerY());
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// TODO: revitalize when drawLitAtlas API lands
|
||||
SkPaint paint;
|
||||
paint.setFilterQuality(kLow_SkFilterQuality);
|
||||
|
||||
const SkRect cull = this->getBounds();
|
||||
const SkColor* colorsPtr = fUseColors ? colors : NULL;
|
||||
|
||||
canvas->drawLitAtlas(fAtlas, xforms, fDiffTex, fNormTex, colorsPtr, kNumAsteroids+1,
|
||||
SkXfermode::kModulate_Mode, &cull, &paint, fLights);
|
||||
#else
|
||||
SkMatrix diffMat, normalMat;
|
||||
|
||||
for (int i = 0; i < kNumAsteroids+1; ++i) {
|
||||
colors[i] = colors[i] & 0xFF000000; // to silence compilers
|
||||
SkPaint paint;
|
||||
|
||||
SkRect r = fDiffTex[i];
|
||||
r.offsetTo(0, 0);
|
||||
|
||||
diffMat.setRectToRect(fDiffTex[i], r, SkMatrix::kFill_ScaleToFit);
|
||||
normalMat.setRectToRect(fNormTex[i], r, SkMatrix::kFill_ScaleToFit);
|
||||
|
||||
SkMatrix m;
|
||||
m.setRSXform(xforms[i]);
|
||||
|
||||
sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(fAtlas, SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode, &normalMat);
|
||||
sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(
|
||||
std::move(normalMap), m);
|
||||
sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(fAtlas,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &diffMat);
|
||||
paint.setShader(SkLightingShader::Make(std::move(diffuseShader),
|
||||
std::move(normalSource), fLights));
|
||||
|
||||
canvas->save();
|
||||
canvas->setMatrix(m);
|
||||
canvas->drawRect(r, paint);
|
||||
canvas->restore();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
{
|
||||
SkPaint paint;
|
||||
paint.setColor(SK_ColorRED);
|
||||
|
||||
for (int i = 0; i < kNumAsteroids; ++i) {
|
||||
canvas->drawCircle(fAsteroids[i].pos().x(), fAsteroids[i].pos().y(), 2, paint);
|
||||
}
|
||||
canvas->drawCircle(fShip.pos().x(), fShip.pos().y(), 2, paint);
|
||||
|
||||
paint.setStyle(SkPaint::kStroke_Style);
|
||||
canvas->drawRect(this->getBounds(), paint);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SkRect onGetBounds() override {
|
||||
return fBounds;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
enum ObjType {
|
||||
kBigAsteroid_ObjType = 0,
|
||||
kMedAsteroid_ObjType,
|
||||
kSmAsteroid_ObjType,
|
||||
kShip_ObjType,
|
||||
|
||||
kLast_ObjType = kShip_ObjType
|
||||
};
|
||||
|
||||
static const int kObjTypeCount = kLast_ObjType + 1;
|
||||
|
||||
void updateLights() {
|
||||
SkLights::Builder builder;
|
||||
|
||||
builder.add(SkLights::Light::MakeDirectional(
|
||||
SkColor3f::Make(1.0f, 1.0f, 1.0f), fLightDir));
|
||||
builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
|
||||
|
||||
fLights = builder.finish();
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
// Draw a vector to the light
|
||||
void drawLightDir(SkCanvas* canvas, SkScalar centerX, SkScalar centerY) {
|
||||
static const int kBgLen = 30;
|
||||
static const int kSmLen = 5;
|
||||
|
||||
// TODO: change the lighting coordinate system to be right handed
|
||||
SkPoint p1 = SkPoint::Make(centerX + kBgLen * fLightDir.fX,
|
||||
centerY - kBgLen * fLightDir.fY);
|
||||
SkPoint p2 = SkPoint::Make(centerX + (kBgLen-kSmLen) * fLightDir.fX,
|
||||
centerY - (kBgLen-kSmLen) * fLightDir.fY);
|
||||
|
||||
SkPaint p;
|
||||
canvas->drawLine(centerX, centerY, p1.fX, p1.fY, p);
|
||||
canvas->drawLine(p1.fX, p1.fY,
|
||||
p2.fX - kSmLen * fLightDir.fY, p2.fY - kSmLen * fLightDir.fX, p);
|
||||
canvas->drawLine(p1.fX, p1.fY,
|
||||
p2.fX + kSmLen * fLightDir.fY, p2.fY + kSmLen * fLightDir.fX, p);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create the mixed diffuse & normal atlas
|
||||
//
|
||||
// big color circle | big normal hemi
|
||||
// ------------------------------------
|
||||
// med color circle | med normal pyra
|
||||
// ------------------------------------
|
||||
// sm color circle | sm normal hemi
|
||||
// ------------------------------------
|
||||
// big ship | big tetra normal
|
||||
static SkBitmap MakeAtlas() {
|
||||
|
||||
SkBitmap atlas;
|
||||
atlas.allocN32Pixels(kAtlasWidth, kAtlasHeight);
|
||||
|
||||
for (int y = 0; y < kAtlasHeight; ++y) {
|
||||
int x = 0;
|
||||
for ( ; x < kBigSize+kPad; ++x) {
|
||||
*atlas.getAddr32(x, y) = SK_ColorTRANSPARENT;
|
||||
}
|
||||
for ( ; x < kAtlasWidth; ++x) {
|
||||
*atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0x88, 0x88, 0xFF);
|
||||
}
|
||||
}
|
||||
|
||||
// big asteroid
|
||||
{
|
||||
SkPoint bigCenter = SkPoint::Make(kDiffXOff + kBigSize/2.0f, kBigYOff + kBigSize/2.0f);
|
||||
|
||||
for (int y = kBigYOff; y < kBigYOff+kBigSize; ++y) {
|
||||
for (int x = kDiffXOff; x < kDiffXOff+kBigSize; ++x) {
|
||||
SkScalar distSq = (x - bigCenter.fX) * (x - bigCenter.fX) +
|
||||
(y - bigCenter.fY) * (y - bigCenter.fY);
|
||||
if (distSq > kBigSize*kBigSize/4.0f) {
|
||||
*atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
|
||||
} else {
|
||||
*atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0xFF, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sk_tool_utils::create_hemi_normal_map(&atlas,
|
||||
SkIRect::MakeXYWH(kNormXOff, kBigYOff,
|
||||
kBigSize, kBigSize));
|
||||
}
|
||||
|
||||
// medium asteroid
|
||||
{
|
||||
for (int y = kMedYOff; y < kMedYOff+kMedSize; ++y) {
|
||||
for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
|
||||
*atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0);
|
||||
}
|
||||
}
|
||||
|
||||
sk_tool_utils::create_frustum_normal_map(&atlas,
|
||||
SkIRect::MakeXYWH(kNormXOff, kMedYOff,
|
||||
kMedSize, kMedSize));
|
||||
}
|
||||
|
||||
// small asteroid
|
||||
{
|
||||
SkPoint smCenter = SkPoint::Make(kDiffXOff + kSmSize/2.0f, kSmYOff + kSmSize/2.0f);
|
||||
|
||||
for (int y = kSmYOff; y < kSmYOff+kSmSize; ++y) {
|
||||
for (int x = kDiffXOff; x < kDiffXOff+kSmSize; ++x) {
|
||||
SkScalar distSq = (x - smCenter.fX) * (x - smCenter.fX) +
|
||||
(y - smCenter.fY) * (y - smCenter.fY);
|
||||
if (distSq > kSmSize*kSmSize/4.0f) {
|
||||
*atlas.getAddr32(x, y) = SkPreMultiplyARGB(0, 0, 0, 0);
|
||||
} else {
|
||||
*atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0, 0xFF);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sk_tool_utils::create_hemi_normal_map(&atlas,
|
||||
SkIRect::MakeXYWH(kNormXOff, kSmYOff,
|
||||
kSmSize, kSmSize));
|
||||
}
|
||||
|
||||
// ship
|
||||
{
|
||||
SkScalar shipMidLine = kDiffXOff + kMedSize/2.0f;
|
||||
|
||||
for (int y = kShipYOff; y < kShipYOff+kMedSize; ++y) {
|
||||
SkScalar scaledY = (y - kShipYOff)/(float)kMedSize; // 0..1
|
||||
|
||||
for (int x = kDiffXOff; x < kDiffXOff+kMedSize; ++x) {
|
||||
SkScalar scaledX;
|
||||
|
||||
if (x < shipMidLine) {
|
||||
scaledX = 1.0f - (x - kDiffXOff)/(kMedSize/2.0f); // 0..1
|
||||
} else {
|
||||
scaledX = (x - shipMidLine)/(kMedSize/2.0f); // 0..1
|
||||
}
|
||||
|
||||
if (scaledX < scaledY) {
|
||||
*atlas.getAddr32(x, y) = SkPackARGB32(0xFF, 0, 0xFF, 0xFF);
|
||||
} else {
|
||||
*atlas.getAddr32(x, y) = SkPackARGB32(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sk_tool_utils::create_tetra_normal_map(&atlas,
|
||||
SkIRect::MakeXYWH(kNormXOff, kShipYOff,
|
||||
kMedSize, kMedSize));
|
||||
}
|
||||
|
||||
return atlas;
|
||||
}
|
||||
|
||||
class ObjectRecord {
|
||||
public:
|
||||
void initAsteroid(SkRandom *rand, const SkRect& bounds,
|
||||
SkRect* diffTex, SkRect* normTex) {
|
||||
static const SkScalar gMaxSpeeds[3] = { 1, 2, 5 }; // smaller asteroids can go faster
|
||||
static const SkScalar gYOffs[3] = { kBigYOff, kMedYOff, kSmYOff };
|
||||
static const SkScalar gSizes[3] = { kBigSize, kMedSize, kSmSize };
|
||||
|
||||
static unsigned int asteroidType = 0;
|
||||
fObjType = static_cast<ObjType>(asteroidType++ % 3);
|
||||
|
||||
fPosition.set(bounds.fLeft + rand->nextUScalar1() * bounds.width(),
|
||||
bounds.fTop + rand->nextUScalar1() * bounds.height());
|
||||
fVelocity.fX = rand->nextSScalar1();
|
||||
fVelocity.fY = sqrt(1.0f - fVelocity.fX * fVelocity.fX);
|
||||
SkASSERT(SkScalarNearlyEqual(fVelocity.length(), 1.0f));
|
||||
fVelocity *= gMaxSpeeds[fObjType];
|
||||
fRot = 0;
|
||||
fDeltaRot = rand->nextSScalar1() / 32;
|
||||
|
||||
diffTex->setXYWH(SkIntToScalar(kDiffXOff), gYOffs[fObjType],
|
||||
gSizes[fObjType], gSizes[fObjType]);
|
||||
normTex->setXYWH(SkIntToScalar(kNormXOff), gYOffs[fObjType],
|
||||
gSizes[fObjType], gSizes[fObjType]);
|
||||
}
|
||||
|
||||
void initShip(const SkRect& bounds, SkRect* diffTex, SkRect* normTex) {
|
||||
fObjType = kShip_ObjType;
|
||||
fPosition.set(bounds.centerX(), bounds.centerY());
|
||||
fVelocity = SkVector::Make(0.0f, 0.0f);
|
||||
fRot = 0.0f;
|
||||
fDeltaRot = 0.0f;
|
||||
|
||||
diffTex->setXYWH(SkIntToScalar(kDiffXOff), SkIntToScalar(kShipYOff),
|
||||
SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
|
||||
normTex->setXYWH(SkIntToScalar(kNormXOff), SkIntToScalar(kShipYOff),
|
||||
SkIntToScalar(kMedSize), SkIntToScalar(kMedSize));
|
||||
}
|
||||
|
||||
void advance(const SkRect& bounds) {
|
||||
fPosition += fVelocity;
|
||||
if (fPosition.fX > bounds.right()) {
|
||||
SkASSERT(fVelocity.fX > 0);
|
||||
fVelocity.fX = -fVelocity.fX;
|
||||
} else if (fPosition.fX < bounds.left()) {
|
||||
SkASSERT(fVelocity.fX < 0);
|
||||
fVelocity.fX = -fVelocity.fX;
|
||||
}
|
||||
if (fPosition.fY > bounds.bottom()) {
|
||||
if (fVelocity.fY > 0) {
|
||||
fVelocity.fY = -fVelocity.fY;
|
||||
}
|
||||
} else if (fPosition.fY < bounds.top()) {
|
||||
if (fVelocity.fY < 0) {
|
||||
fVelocity.fY = -fVelocity.fY;
|
||||
}
|
||||
}
|
||||
|
||||
fRot += fDeltaRot;
|
||||
fRot = SkScalarMod(fRot, 2 * SK_ScalarPI);
|
||||
}
|
||||
|
||||
const SkPoint& pos() const { return fPosition; }
|
||||
|
||||
SkScalar rot() const { return fRot; }
|
||||
void setRot(SkScalar rot) { fRot = rot; }
|
||||
|
||||
const SkPoint& velocity() const { return fVelocity; }
|
||||
void setVelocity(const SkPoint& velocity) { fVelocity = velocity; }
|
||||
|
||||
SkRSXform asRSXform() const {
|
||||
static const SkScalar gHalfSizes[kObjTypeCount] = {
|
||||
SkScalarHalf(kBigSize),
|
||||
SkScalarHalf(kMedSize),
|
||||
SkScalarHalf(kSmSize),
|
||||
SkScalarHalf(kMedSize),
|
||||
};
|
||||
|
||||
return SkRSXform::MakeFromRadians(1.0f, fRot, fPosition.x(), fPosition.y(),
|
||||
gHalfSizes[fObjType],
|
||||
gHalfSizes[fObjType]);
|
||||
}
|
||||
|
||||
private:
|
||||
ObjType fObjType;
|
||||
SkPoint fPosition;
|
||||
SkVector fVelocity;
|
||||
SkScalar fRot; // In radians.
|
||||
SkScalar fDeltaRot; // In radiands. Not used by ship.
|
||||
};
|
||||
|
||||
private:
|
||||
static const int kNumLights = 2;
|
||||
static const int kNumAsteroids = 6;
|
||||
static const int kNumShips = 1;
|
||||
|
||||
static const int kBigSize = 128;
|
||||
static const int kMedSize = 64;
|
||||
static const int kSmSize = 32;
|
||||
static const int kPad = 1;
|
||||
static const int kAtlasWidth = kBigSize + kBigSize + 2 * kPad; // 2 pads in the middle
|
||||
static const int kAtlasHeight = kBigSize + kMedSize + kSmSize + kMedSize + 3 * kPad;
|
||||
|
||||
static const int kDiffXOff = 0;
|
||||
static const int kNormXOff = kBigSize + 2 * kPad;
|
||||
|
||||
static const int kBigYOff = 0;
|
||||
static const int kMedYOff = kBigSize + kPad;
|
||||
static const int kSmYOff = kMedYOff + kMedSize + kPad;
|
||||
static const int kShipYOff = kSmYOff + kSmSize + kPad;
|
||||
static const int kMaxShipSpeed = 5;
|
||||
|
||||
SkBitmap fAtlas;
|
||||
ObjectRecord fAsteroids[kNumAsteroids];
|
||||
ObjectRecord fShip;
|
||||
SkRect fDiffTex[kNumAsteroids+kNumShips];
|
||||
SkRect fNormTex[kNumAsteroids+kNumShips];
|
||||
SkRect fBounds;
|
||||
bool fUseColors;
|
||||
SkVector3 fLightDir;
|
||||
sk_sp<SkLights> fLights;
|
||||
|
||||
typedef SkDrawable INHERITED;
|
||||
};
|
||||
|
||||
class DrawLitAtlasView : public SampleView {
|
||||
public:
|
||||
DrawLitAtlasView() : fDrawable(new DrawLitAtlasDrawable(SkRect::MakeWH(640, 480))) {}
|
||||
|
||||
protected:
|
||||
bool onQuery(SkEvent* evt) override {
|
||||
if (SampleCode::TitleQ(*evt)) {
|
||||
SampleCode::TitleR(evt, "DrawLitAtlas");
|
||||
return true;
|
||||
}
|
||||
SkUnichar uni;
|
||||
if (SampleCode::CharQ(*evt, &uni)) {
|
||||
switch (uni) {
|
||||
case 'C':
|
||||
fDrawable->toggleUseColors();
|
||||
return true;
|
||||
case 'j':
|
||||
fDrawable->left();
|
||||
return true;
|
||||
case 'k':
|
||||
fDrawable->thrust();
|
||||
return true;
|
||||
case 'l':
|
||||
fDrawable->right();
|
||||
return true;
|
||||
case 'o':
|
||||
fDrawable->rotateLight();
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return this->INHERITED::onQuery(evt);
|
||||
}
|
||||
|
||||
void onDrawContent(SkCanvas* canvas) override {
|
||||
canvas->drawDrawable(fDrawable.get());
|
||||
}
|
||||
|
||||
bool onAnimate(const SkAnimTimer& timer) override {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
sk_sp<DrawLitAtlasDrawable> fDrawable;
|
||||
|
||||
typedef SampleView INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static SkView* MyFactory() { return new DrawLitAtlasView; }
|
||||
static SkViewRegister reg(MyFactory);
|
91
src/core/SkLights.cpp
Normal file
91
src/core/SkLights.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkColorSpaceXformer.h"
|
||||
#include "SkLights.h"
|
||||
#include "SkReadBuffer.h"
|
||||
|
||||
sk_sp<SkLights> SkLights::MakeFromBuffer(SkReadBuffer& buf) {
|
||||
Builder builder;
|
||||
|
||||
SkColor3f ambColor;
|
||||
if (!buf.readScalarArray(&ambColor.fX, 3)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
builder.setAmbientLightColor(ambColor);
|
||||
|
||||
int numLights = buf.readInt();
|
||||
|
||||
for (int l = 0; l < numLights; ++l) {
|
||||
bool isPoint = buf.readBool();
|
||||
|
||||
SkColor3f color;
|
||||
if (!buf.readScalarArray(&color.fX, 3)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SkVector3 dirOrPos;
|
||||
if (!buf.readScalarArray(&dirOrPos.fX, 3)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (isPoint) {
|
||||
SkScalar intensity;
|
||||
intensity = buf.readScalar();
|
||||
Light light = Light::MakePoint(color, dirOrPos, intensity);
|
||||
builder.add(light);
|
||||
} else {
|
||||
Light light = Light::MakeDirectional(color, dirOrPos);
|
||||
builder.add(light);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.finish();
|
||||
}
|
||||
|
||||
static SkColor3f xform_color(const SkColor3f& color, SkColorSpaceXformer* xformer) {
|
||||
SkColor origColor = SkColorSetARGB(0xFF,
|
||||
SkScalarRoundToInt(color.fX),
|
||||
SkScalarRoundToInt(color.fY),
|
||||
SkScalarRoundToInt(color.fZ));
|
||||
SkColor xformedColor = xformer->apply(origColor);
|
||||
return SkColor3f::Make(SkIntToScalar(SkGetPackedR32(xformedColor)),
|
||||
SkIntToScalar(SkGetPackedG32(xformedColor)),
|
||||
SkIntToScalar(SkGetPackedB32(xformedColor)));
|
||||
}
|
||||
|
||||
sk_sp<SkLights> SkLights::makeColorSpace(SkColorSpaceXformer* xformer) const {
|
||||
SkLights::Builder builder;
|
||||
for (int i = 0; i < this->numLights(); i++) {
|
||||
Light light(fLights[i].type(), xform_color(fLights[i].color(), xformer),
|
||||
fLights[i].fDirOrPos, fLights[i].fIntensity);
|
||||
builder.add(light);
|
||||
}
|
||||
builder.setAmbientLightColor(xform_color(fAmbientLightColor, xformer));
|
||||
return builder.finish();
|
||||
}
|
||||
|
||||
void SkLights::flatten(SkWriteBuffer& buf) const {
|
||||
buf.writeScalarArray(&this->ambientLightColor().fX, 3);
|
||||
|
||||
buf.writeInt(this->numLights());
|
||||
for (int l = 0; l < this->numLights(); ++l) {
|
||||
const Light& light = this->light(l);
|
||||
|
||||
bool isPoint = Light::kPoint_LightType == light.type();
|
||||
|
||||
buf.writeBool(isPoint);
|
||||
buf.writeScalarArray(&light.color().fX, 3);
|
||||
buf.writeScalarArray(&light.dir().fX, 3);
|
||||
|
||||
if (isPoint) {
|
||||
buf.writeScalar(light.intensity());
|
||||
}
|
||||
}
|
||||
}
|
100
src/core/SkNormalFlatSource.cpp
Normal file
100
src/core/SkNormalFlatSource.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkNormalFlatSource.h"
|
||||
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkPoint3.h"
|
||||
#include "SkReadBuffer.h"
|
||||
#include "SkWriteBuffer.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "glsl/GrGLSLFragmentProcessor.h"
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
|
||||
class NormalFlatFP : public GrFragmentProcessor {
|
||||
public:
|
||||
static std::unique_ptr<GrFragmentProcessor> Make() {
|
||||
return std::unique_ptr<GrFragmentProcessor>(new NormalFlatFP());
|
||||
}
|
||||
|
||||
const char* name() const override { return "NormalFlatFP"; }
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> clone() const override { return Make(); }
|
||||
|
||||
private:
|
||||
class GLSLNormalFlatFP : public GrGLSLFragmentProcessor {
|
||||
public:
|
||||
GLSLNormalFlatFP() {}
|
||||
|
||||
void emitCode(EmitArgs& args) override {
|
||||
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
||||
|
||||
fragBuilder->codeAppendf("%s = float4(0, 0, 1, 0);", args.fOutputColor);
|
||||
}
|
||||
|
||||
private:
|
||||
void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override {}
|
||||
};
|
||||
|
||||
NormalFlatFP()
|
||||
: INHERITED(kFlatNormalsFP_ClassID, kConstantOutputForConstantInput_OptimizationFlag) {
|
||||
}
|
||||
|
||||
void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {}
|
||||
|
||||
GrColor4f constantOutputForConstantInput(GrColor4f) const override {
|
||||
return GrColor4f(0, 0, 1, 0);
|
||||
}
|
||||
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalFlatFP; }
|
||||
|
||||
bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
|
||||
|
||||
typedef GrFragmentProcessor INHERITED;
|
||||
};
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> SkNormalFlatSourceImpl::asFragmentProcessor(
|
||||
const GrFPArgs&) const {
|
||||
return NormalFlatFP::Make();
|
||||
}
|
||||
|
||||
#endif // SK_SUPPORT_GPU
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkNormalFlatSourceImpl::Provider::Provider() {}
|
||||
|
||||
SkNormalFlatSourceImpl::Provider::~Provider() {}
|
||||
|
||||
SkNormalSource::Provider* SkNormalFlatSourceImpl::asProvider(const SkShaderBase::ContextRec &rec,
|
||||
SkArenaAlloc *alloc) const {
|
||||
return alloc->make<Provider>();
|
||||
}
|
||||
|
||||
void SkNormalFlatSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[],
|
||||
int count) const {
|
||||
for (int i = 0; i < count; i++) {
|
||||
output[i] = {0.0f, 0.0f, 1.0f};
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<SkFlattenable> SkNormalFlatSourceImpl::CreateProc(SkReadBuffer& buf) {
|
||||
return sk_make_sp<SkNormalFlatSourceImpl>();
|
||||
}
|
||||
|
||||
void SkNormalFlatSourceImpl::flatten(SkWriteBuffer& buf) const {
|
||||
this->INHERITED::flatten(buf);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<SkNormalSource> SkNormalSource::MakeFlat() {
|
||||
return sk_make_sp<SkNormalFlatSourceImpl>();
|
||||
}
|
47
src/core/SkNormalFlatSource.h
Normal file
47
src/core/SkNormalFlatSource.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkNormalFlatSource_DEFINED
|
||||
#define SkNormalFlatSource_DEFINED
|
||||
|
||||
#include "SkNormalSource.h"
|
||||
|
||||
class SK_API SkNormalFlatSourceImpl : public SkNormalSource {
|
||||
public:
|
||||
SkNormalFlatSourceImpl(){}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs& args) const override;
|
||||
#endif
|
||||
|
||||
SkNormalSource::Provider* asProvider(const SkShaderBase::ContextRec& rec,
|
||||
SkArenaAlloc* alloc) const override;
|
||||
|
||||
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalFlatSourceImpl)
|
||||
|
||||
protected:
|
||||
void flatten(SkWriteBuffer& buf) const override;
|
||||
|
||||
private:
|
||||
class Provider : public SkNormalSource::Provider {
|
||||
public:
|
||||
Provider();
|
||||
|
||||
~Provider() override;
|
||||
|
||||
void fillScanLine(int x, int y, SkPoint3 output[], int count) const override;
|
||||
|
||||
private:
|
||||
typedef SkNormalSource::Provider INHERITED;
|
||||
};
|
||||
|
||||
friend class SkNormalSource;
|
||||
|
||||
typedef SkNormalSource INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
253
src/core/SkNormalMapSource.cpp
Normal file
253
src/core/SkNormalMapSource.cpp
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkNormalMapSource.h"
|
||||
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkLightingShader.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkPM4f.h"
|
||||
#include "SkReadBuffer.h"
|
||||
#include "SkWriteBuffer.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "GrCoordTransform.h"
|
||||
#include "glsl/GrGLSLFragmentProcessor.h"
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "SkGr.h"
|
||||
|
||||
class NormalMapFP : public GrFragmentProcessor {
|
||||
public:
|
||||
static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> mapFP,
|
||||
const SkMatrix& invCTM) {
|
||||
return std::unique_ptr<GrFragmentProcessor>(new NormalMapFP(std::move(mapFP), invCTM));
|
||||
}
|
||||
|
||||
const char* name() const override { return "NormalMapFP"; }
|
||||
|
||||
const SkMatrix& invCTM() const { return fInvCTM; }
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> clone() const override {
|
||||
return Make(this->childProcessor(0).clone(), fInvCTM);
|
||||
}
|
||||
|
||||
private:
|
||||
class GLSLNormalMapFP : public GrGLSLFragmentProcessor {
|
||||
public:
|
||||
GLSLNormalMapFP() : fColumnMajorInvCTM22{0.0f} {}
|
||||
|
||||
void emitCode(EmitArgs& args) override {
|
||||
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
||||
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
|
||||
|
||||
// add uniform
|
||||
const char* xformUniName = nullptr;
|
||||
fXformUni = uniformHandler->addUniform(kFragment_GrShaderFlag, kFloat2x2_GrSLType,
|
||||
kDefault_GrSLPrecision, "Xform", &xformUniName);
|
||||
|
||||
SkString dstNormalColorName("dstNormalColor");
|
||||
this->emitChild(0, &dstNormalColorName, args);
|
||||
fragBuilder->codeAppendf("float3 normal = normalize(%s.rgb - float3(0.5));",
|
||||
dstNormalColorName.c_str());
|
||||
|
||||
// If there's no x & y components, return (0, 0, +/- 1) instead to avoid division by 0
|
||||
fragBuilder->codeAppend( "if (abs(normal.z) > 0.999) {");
|
||||
fragBuilder->codeAppendf(" %s = normalize(float4(0.0, 0.0, normal.z, 0.0));",
|
||||
args.fOutputColor);
|
||||
// Else, Normalizing the transformed X and Y, while keeping constant both Z and the
|
||||
// vector's angle in the XY plane. This maintains the "slope" for the surface while
|
||||
// appropriately rotating the normal regardless of any anisotropic scaling that occurs.
|
||||
// Here, we call 'scaling factor' the number that must divide the transformed X and Y so
|
||||
// that the normal's length remains equal to 1.
|
||||
fragBuilder->codeAppend( "} else {");
|
||||
fragBuilder->codeAppendf(" float2 transformed = %s * normal.xy;",
|
||||
xformUniName);
|
||||
fragBuilder->codeAppend( " float scalingFactorSquared = "
|
||||
"( (transformed.x * transformed.x) "
|
||||
"+ (transformed.y * transformed.y) )"
|
||||
"/(1.0 - (normal.z * normal.z));");
|
||||
fragBuilder->codeAppendf(" %s = float4(transformed*inversesqrt(scalingFactorSquared),"
|
||||
"normal.z, 0.0);",
|
||||
args.fOutputColor);
|
||||
fragBuilder->codeAppend( "}");
|
||||
}
|
||||
|
||||
static void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
|
||||
b->add32(0x0);
|
||||
}
|
||||
|
||||
private:
|
||||
void onSetData(const GrGLSLProgramDataManager& pdman,
|
||||
const GrFragmentProcessor& proc) override {
|
||||
const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>();
|
||||
|
||||
const SkMatrix& invCTM = normalMapFP.invCTM();
|
||||
fColumnMajorInvCTM22[0] = invCTM.get(SkMatrix::kMScaleX);
|
||||
fColumnMajorInvCTM22[1] = invCTM.get(SkMatrix::kMSkewY);
|
||||
fColumnMajorInvCTM22[2] = invCTM.get(SkMatrix::kMSkewX);
|
||||
fColumnMajorInvCTM22[3] = invCTM.get(SkMatrix::kMScaleY);
|
||||
pdman.setMatrix2f(fXformUni, fColumnMajorInvCTM22);
|
||||
}
|
||||
|
||||
private:
|
||||
// Upper-right 2x2 corner of the inverse of the CTM in column-major form
|
||||
float fColumnMajorInvCTM22[4];
|
||||
GrGLSLProgramDataManager::UniformHandle fXformUni;
|
||||
};
|
||||
|
||||
void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
|
||||
GLSLNormalMapFP::GenKey(*this, caps, b);
|
||||
}
|
||||
NormalMapFP(std::unique_ptr<GrFragmentProcessor> mapFP, const SkMatrix& invCTM)
|
||||
: INHERITED(kMappedNormalsFP_ClassID, kNone_OptimizationFlags)
|
||||
, fInvCTM(invCTM) {
|
||||
this->registerChildProcessor(std::move(mapFP));
|
||||
}
|
||||
|
||||
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLNormalMapFP; }
|
||||
|
||||
bool onIsEqual(const GrFragmentProcessor& proc) const override {
|
||||
const NormalMapFP& normalMapFP = proc.cast<NormalMapFP>();
|
||||
return fInvCTM == normalMapFP.fInvCTM;
|
||||
}
|
||||
|
||||
SkMatrix fInvCTM;
|
||||
|
||||
typedef GrFragmentProcessor INHERITED;
|
||||
};
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> SkNormalMapSourceImpl::asFragmentProcessor(
|
||||
const GrFPArgs& args) const {
|
||||
std::unique_ptr<GrFragmentProcessor> mapFP = as_SB(fMapShader)->asFragmentProcessor(args);
|
||||
if (!mapFP) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NormalMapFP::Make(std::move(mapFP), fInvCTM);
|
||||
}
|
||||
|
||||
#endif // SK_SUPPORT_GPU
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkNormalMapSourceImpl::Provider::Provider(const SkNormalMapSourceImpl& source,
|
||||
SkShaderBase::Context* mapContext)
|
||||
: fSource(source)
|
||||
, fMapContext(mapContext) {}
|
||||
|
||||
SkNormalSource::Provider* SkNormalMapSourceImpl::asProvider(const SkShaderBase::ContextRec &rec,
|
||||
SkArenaAlloc* alloc) const {
|
||||
SkMatrix normTotalInv;
|
||||
if (!this->computeNormTotalInverse(rec, &normTotalInv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Overriding paint's alpha because we need the normal map's RGB channels to be unpremul'd
|
||||
SkPaint overridePaint {*(rec.fPaint)};
|
||||
overridePaint.setAlpha(0xFF);
|
||||
SkShaderBase::ContextRec overrideRec(overridePaint, *(rec.fMatrix), rec.fLocalMatrix,
|
||||
rec.fPreferredDstType, rec.fDstColorSpace);
|
||||
|
||||
auto* context = as_SB(fMapShader)->makeContext(overrideRec, alloc);
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return alloc->make<Provider>(*this, context);
|
||||
}
|
||||
|
||||
bool SkNormalMapSourceImpl::computeNormTotalInverse(const SkShaderBase::ContextRec& rec,
|
||||
SkMatrix* normTotalInverse) const {
|
||||
SkMatrix total = SkMatrix::Concat(*rec.fMatrix, fMapShader->getLocalMatrix());
|
||||
if (rec.fLocalMatrix) {
|
||||
total.preConcat(*rec.fLocalMatrix);
|
||||
}
|
||||
|
||||
return total.invert(normTotalInverse);
|
||||
}
|
||||
|
||||
#define BUFFER_MAX 16
|
||||
void SkNormalMapSourceImpl::Provider::fillScanLine(int x, int y, SkPoint3 output[],
|
||||
int count) const {
|
||||
SkPMColor tmpNormalColors[BUFFER_MAX];
|
||||
|
||||
do {
|
||||
int n = SkTMin(count, BUFFER_MAX);
|
||||
|
||||
fMapContext->shadeSpan(x, y, tmpNormalColors, n);
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
SkPoint3 tempNorm;
|
||||
|
||||
tempNorm.set(SkIntToScalar(SkGetPackedR32(tmpNormalColors[i])) - 127.0f,
|
||||
SkIntToScalar(SkGetPackedG32(tmpNormalColors[i])) - 127.0f,
|
||||
SkIntToScalar(SkGetPackedB32(tmpNormalColors[i])) - 127.0f);
|
||||
|
||||
tempNorm.normalize();
|
||||
|
||||
|
||||
if (!SkScalarNearlyEqual(SkScalarAbs(tempNorm.fZ), 1.0f)) {
|
||||
SkVector transformed = fSource.fInvCTM.mapVector(tempNorm.fX, tempNorm.fY);
|
||||
|
||||
// Normalizing the transformed X and Y, while keeping constant both Z and the
|
||||
// vector's angle in the XY plane. This maintains the "slope" for the surface while
|
||||
// appropriately rotating the normal for any anisotropic scaling that occurs.
|
||||
// Here, we call scaling factor the number that must divide the transformed X and Y
|
||||
// so that the normal's length remains equal to 1.
|
||||
SkScalar scalingFactorSquared =
|
||||
(SkScalarSquare(transformed.fX) + SkScalarSquare(transformed.fY))
|
||||
/ (1.0f - SkScalarSquare(tempNorm.fZ));
|
||||
SkScalar invScalingFactor = SkScalarInvert(SkScalarSqrt(scalingFactorSquared));
|
||||
|
||||
output[i].fX = transformed.fX * invScalingFactor;
|
||||
output[i].fY = transformed.fY * invScalingFactor;
|
||||
output[i].fZ = tempNorm.fZ;
|
||||
} else {
|
||||
output[i] = {0.0f, 0.0f, tempNorm.fZ};
|
||||
output[i].normalize();
|
||||
}
|
||||
|
||||
SkASSERT(SkScalarNearlyEqual(output[i].length(), 1.0f));
|
||||
}
|
||||
|
||||
output += n;
|
||||
x += n;
|
||||
count -= n;
|
||||
} while (count > 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<SkFlattenable> SkNormalMapSourceImpl::CreateProc(SkReadBuffer& buf) {
|
||||
|
||||
sk_sp<SkShader> mapShader = buf.readFlattenable<SkShaderBase>();
|
||||
|
||||
SkMatrix invCTM;
|
||||
buf.readMatrix(&invCTM);
|
||||
|
||||
return sk_make_sp<SkNormalMapSourceImpl>(std::move(mapShader), invCTM);
|
||||
}
|
||||
|
||||
void SkNormalMapSourceImpl::flatten(SkWriteBuffer& buf) const {
|
||||
this->INHERITED::flatten(buf);
|
||||
|
||||
buf.writeFlattenable(fMapShader.get());
|
||||
buf.writeMatrix(fInvCTM);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<SkNormalSource> SkNormalSource::MakeFromNormalMap(sk_sp<SkShader> map, const SkMatrix& ctm) {
|
||||
SkMatrix invCTM;
|
||||
|
||||
if (!ctm.invert(&invCTM) || !map) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return sk_make_sp<SkNormalMapSourceImpl>(std::move(map), invCTM);
|
||||
}
|
57
src/core/SkNormalMapSource.h
Normal file
57
src/core/SkNormalMapSource.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkNormalMapSource_DEFINED
|
||||
#define SkNormalMapSource_DEFINED
|
||||
|
||||
#include "SkNormalSource.h"
|
||||
|
||||
class SkNormalMapSourceImpl : public SkNormalSource {
|
||||
public:
|
||||
SkNormalMapSourceImpl(sk_sp<SkShader> mapShader, const SkMatrix& invCTM)
|
||||
: fMapShader(std::move(mapShader))
|
||||
, fInvCTM(invCTM) {}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs& args) const override;
|
||||
#endif
|
||||
|
||||
SkNormalSource::Provider* asProvider(const SkShaderBase::ContextRec& rec,
|
||||
SkArenaAlloc* alloc) const override;
|
||||
|
||||
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkNormalMapSourceImpl)
|
||||
|
||||
protected:
|
||||
void flatten(SkWriteBuffer& buf) const override;
|
||||
|
||||
bool computeNormTotalInverse(const SkShaderBase::ContextRec& rec,
|
||||
SkMatrix* normTotalInverse) const;
|
||||
|
||||
private:
|
||||
class Provider : public SkNormalSource::Provider {
|
||||
public:
|
||||
Provider(const SkNormalMapSourceImpl& source, SkShaderBase::Context* mapContext);
|
||||
|
||||
void fillScanLine(int x, int y, SkPoint3 output[], int count) const override;
|
||||
|
||||
private:
|
||||
const SkNormalMapSourceImpl& fSource;
|
||||
SkShaderBase::Context* fMapContext;
|
||||
|
||||
typedef SkNormalSource::Provider INHERITED;
|
||||
};
|
||||
|
||||
sk_sp<SkShader> fMapShader;
|
||||
SkMatrix fInvCTM; // Inverse of the canvas total matrix, used for rotating normals.
|
||||
|
||||
friend class SkNormalSource;
|
||||
|
||||
typedef SkNormalSource INHERITED;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
22
src/core/SkNormalSource.cpp
Normal file
22
src/core/SkNormalSource.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkNormalFlatSource.h"
|
||||
#include "SkNormalMapSource.h"
|
||||
#include "SkNormalSource.h"
|
||||
|
||||
// Generating vtable
|
||||
SkNormalSource::~SkNormalSource() {}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkNormalSource)
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalMapSourceImpl)
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkNormalFlatSourceImpl)
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
75
src/core/SkNormalSource.h
Normal file
75
src/core/SkNormalSource.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkNormalSource_DEFINED
|
||||
#define SkNormalSource_DEFINED
|
||||
|
||||
#include "SkFlattenable.h"
|
||||
#include "SkShaderBase.h"
|
||||
|
||||
class SkMatrix;
|
||||
struct SkPoint3;
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
class GrFragmentProcessor;
|
||||
#endif
|
||||
|
||||
/** Abstract class that generates or reads in normals for use by SkLightingShader.
|
||||
*/
|
||||
class SK_API SkNormalSource : public SkFlattenable {
|
||||
public:
|
||||
virtual ~SkNormalSource() override;
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
/** Returns a fragment processor that takes no input and outputs a normal (already rotated)
|
||||
as its output color. To be used as a child fragment processor.
|
||||
*/
|
||||
virtual std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const = 0;
|
||||
#endif
|
||||
|
||||
class Provider {
|
||||
public:
|
||||
virtual ~Provider() {}
|
||||
|
||||
/** Called for each span of the object being drawn on the CPU. Your subclass should set
|
||||
the appropriate normals that correspond to the specified device coordinates.
|
||||
*/
|
||||
virtual void fillScanLine(int x, int y, SkPoint3 output[], int count) const = 0;
|
||||
};
|
||||
|
||||
/** Returns an instance of 'Provider' that provides normals for the CPU pipeline. The
|
||||
necessary data will be initialized in place at 'storage'.
|
||||
*/
|
||||
virtual Provider* asProvider(const SkShaderBase::ContextRec&, SkArenaAlloc*) const = 0;
|
||||
|
||||
/** Returns a normal source that provides normals sourced from the the normal map argument.
|
||||
|
||||
@param map a shader that outputs the normal map
|
||||
@param ctm the current canvas' total matrix, used to rotate normals when necessary.
|
||||
|
||||
nullptr will be returned if 'map' is null
|
||||
|
||||
The normal map is currently assumed to be an 8888 image where the normal at a texel
|
||||
is retrieved by:
|
||||
N.x = R-127;
|
||||
N.y = G-127;
|
||||
N.z = B-127;
|
||||
N.normalize();
|
||||
The +Z axis is thus encoded in RGB as (127, 127, 255) while the -Z axis is
|
||||
(127, 127, 0).
|
||||
*/
|
||||
static sk_sp<SkNormalSource> MakeFromNormalMap(sk_sp<SkShader> map, const SkMatrix& ctm);
|
||||
|
||||
/** Returns a normal source that provides straight-up normals only <0, 0, 1>.
|
||||
*/
|
||||
static sk_sp<SkNormalSource> MakeFlat();
|
||||
|
||||
SK_DEFINE_FLATTENABLE_TYPE(SkNormalSource)
|
||||
SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
|
||||
};
|
||||
|
||||
#endif
|
@ -153,6 +153,9 @@ public:
|
||||
kSwizzleFragmentProcessor_ClassID,
|
||||
kTestFP_ClassID,
|
||||
kTextureGeometryProcessor_ClassID,
|
||||
kFlatNormalsFP_ClassID,
|
||||
kMappedNormalsFP_ClassID,
|
||||
kLightingFP_ClassID,
|
||||
};
|
||||
|
||||
virtual ~GrProcessor() = default;
|
||||
|
@ -15,7 +15,9 @@
|
||||
#include "SkGradientShader.h"
|
||||
#include "SkHighContrastFilter.h"
|
||||
#include "SkLayerDrawLooper.h"
|
||||
#include "SkLightingShader.h"
|
||||
#include "SkLumaColorFilter.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkOverdrawColorFilter.h"
|
||||
#include "SkPerlinNoiseShader.h"
|
||||
#include "SkShaderMaskFilter.h"
|
||||
@ -51,6 +53,8 @@ void SkFlattenable::PrivateInitializer::InitEffects() {
|
||||
// Shader
|
||||
SkPerlinNoiseShader::InitializeFlattenables();
|
||||
SkGradientShader::InitializeFlattenables();
|
||||
SkLightingShader::InitializeFlattenables();
|
||||
SkNormalSource::InitializeFlattenables();
|
||||
|
||||
// PathEffect
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect)
|
||||
|
497
src/shaders/SkLightingShader.cpp
Normal file
497
src/shaders/SkLightingShader.cpp
Normal file
@ -0,0 +1,497 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkArenaAlloc.h"
|
||||
#include "SkBitmapProcShader.h"
|
||||
#include "SkBitmapProcState.h"
|
||||
#include "SkColor.h"
|
||||
#include "SkColorSpaceXformer.h"
|
||||
#include "SkEmptyShader.h"
|
||||
#include "SkLightingShader.h"
|
||||
#include "SkMathPriv.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkPoint3.h"
|
||||
#include "SkReadBuffer.h"
|
||||
#include "SkShaderBase.h"
|
||||
#include "SkUnPreMultiply.h"
|
||||
#include "SkWriteBuffer.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
SkLightingShader TODOs:
|
||||
support different light types
|
||||
support multiple lights
|
||||
fix non-opaque diffuse textures
|
||||
|
||||
To Test:
|
||||
A8 diffuse textures
|
||||
down & upsampled draws
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/** \class SkLightingShaderImpl
|
||||
This subclass of shader applies lighting.
|
||||
*/
|
||||
class SkLightingShaderImpl : public SkShaderBase {
|
||||
public:
|
||||
/** Create a new lighting shader that uses the provided normal map and
|
||||
lights to light the diffuse bitmap.
|
||||
@param diffuseShader the shader that provides the diffuse colors
|
||||
@param normalSource the source of normals for lighting computation
|
||||
@param lights the lights applied to the geometry
|
||||
*/
|
||||
SkLightingShaderImpl(sk_sp<SkShader> diffuseShader,
|
||||
sk_sp<SkNormalSource> normalSource,
|
||||
sk_sp<SkLights> lights)
|
||||
: fDiffuseShader(std::move(diffuseShader))
|
||||
, fNormalSource(std::move(normalSource))
|
||||
, fLights(std::move(lights)) {}
|
||||
|
||||
bool isOpaque() const override;
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs&) const override;
|
||||
#endif
|
||||
|
||||
class LightingShaderContext : public Context {
|
||||
public:
|
||||
// The context takes ownership of the context and provider. It will call their destructors
|
||||
// and then indirectly free their memory by calling free() on heapAllocated
|
||||
LightingShaderContext(const SkLightingShaderImpl&, const ContextRec&,
|
||||
SkShaderBase::Context* diffuseContext, SkNormalSource::Provider*,
|
||||
void* heapAllocated);
|
||||
|
||||
void shadeSpan(int x, int y, SkPMColor[], int count) override;
|
||||
|
||||
uint32_t getFlags() const override { return fFlags; }
|
||||
|
||||
private:
|
||||
SkShaderBase::Context* fDiffuseContext;
|
||||
SkNormalSource::Provider* fNormalProvider;
|
||||
SkColor fPaintColor;
|
||||
uint32_t fFlags;
|
||||
|
||||
typedef Context INHERITED;
|
||||
};
|
||||
|
||||
SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingShaderImpl)
|
||||
|
||||
protected:
|
||||
void flatten(SkWriteBuffer&) const override;
|
||||
Context* onMakeContext(const ContextRec&, SkArenaAlloc*) const override;
|
||||
sk_sp<SkShader> onMakeColorSpace(SkColorSpaceXformer* xformer) const override;
|
||||
|
||||
private:
|
||||
sk_sp<SkShader> fDiffuseShader;
|
||||
sk_sp<SkNormalSource> fNormalSource;
|
||||
sk_sp<SkLights> fLights;
|
||||
|
||||
friend class SkLightingShader;
|
||||
|
||||
typedef SkShaderBase INHERITED;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
|
||||
#include "GrCoordTransform.h"
|
||||
#include "GrFragmentProcessor.h"
|
||||
#include "glsl/GrGLSLFragmentProcessor.h"
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLProgramDataManager.h"
|
||||
#include "glsl/GrGLSLUniformHandler.h"
|
||||
#include "SkGr.h"
|
||||
|
||||
// This FP expects a premul'd color input for its diffuse color. Premul'ing of the paint's color is
|
||||
// handled by the asFragmentProcessor() factory, but shaders providing diffuse color must output it
|
||||
// premul'd.
|
||||
class LightingFP : public GrFragmentProcessor {
|
||||
public:
|
||||
static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> normalFP,
|
||||
sk_sp<SkLights> lights) {
|
||||
return std::unique_ptr<GrFragmentProcessor>(new LightingFP(std::move(normalFP),
|
||||
std::move(lights)));
|
||||
}
|
||||
|
||||
const char* name() const override { return "LightingFP"; }
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> clone() const override {
|
||||
return std::unique_ptr<GrFragmentProcessor>(new LightingFP(*this));
|
||||
}
|
||||
|
||||
const SkTArray<SkLights::Light>& directionalLights() const { return fDirectionalLights; }
|
||||
const SkColor3f& ambientColor() const { return fAmbientColor; }
|
||||
|
||||
private:
|
||||
class GLSLLightingFP : public GrGLSLFragmentProcessor {
|
||||
public:
|
||||
GLSLLightingFP() {
|
||||
fAmbientColor.fX = 0.0f;
|
||||
}
|
||||
|
||||
void emitCode(EmitArgs& args) override {
|
||||
|
||||
GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder;
|
||||
GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
|
||||
const LightingFP& lightingFP = args.fFp.cast<LightingFP>();
|
||||
|
||||
const char *lightDirsUniName = nullptr;
|
||||
const char *lightColorsUniName = nullptr;
|
||||
if (lightingFP.fDirectionalLights.count() != 0) {
|
||||
fLightDirsUni = uniformHandler->addUniformArray(
|
||||
kFragment_GrShaderFlag,
|
||||
kFloat3_GrSLType,
|
||||
kDefault_GrSLPrecision,
|
||||
"LightDir",
|
||||
lightingFP.fDirectionalLights.count(),
|
||||
&lightDirsUniName);
|
||||
fLightColorsUni = uniformHandler->addUniformArray(
|
||||
kFragment_GrShaderFlag,
|
||||
kFloat3_GrSLType,
|
||||
kDefault_GrSLPrecision,
|
||||
"LightColor",
|
||||
lightingFP.fDirectionalLights.count(),
|
||||
&lightColorsUniName);
|
||||
}
|
||||
|
||||
const char* ambientColorUniName = nullptr;
|
||||
fAmbientColorUni = uniformHandler->addUniform(kFragment_GrShaderFlag,
|
||||
kFloat3_GrSLType, kDefault_GrSLPrecision,
|
||||
"AmbientColor", &ambientColorUniName);
|
||||
|
||||
fragBuilder->codeAppendf("float4 diffuseColor = %s;", args.fInputColor);
|
||||
|
||||
SkString dstNormalName("dstNormal");
|
||||
this->emitChild(0, &dstNormalName, args);
|
||||
|
||||
fragBuilder->codeAppendf("float3 normal = %s.xyz;", dstNormalName.c_str());
|
||||
|
||||
fragBuilder->codeAppend( "float3 result = float3(0.0);");
|
||||
|
||||
// diffuse light
|
||||
if (lightingFP.fDirectionalLights.count() != 0) {
|
||||
fragBuilder->codeAppendf("for (int i = 0; i < %d; i++) {",
|
||||
lightingFP.fDirectionalLights.count());
|
||||
// TODO: modulate the contribution from each light based on the shadow map
|
||||
fragBuilder->codeAppendf(" float NdotL = clamp(dot(normal, %s[i]), 0.0, 1.0);",
|
||||
lightDirsUniName);
|
||||
fragBuilder->codeAppendf(" result += %s[i]*diffuseColor.rgb*NdotL;",
|
||||
lightColorsUniName);
|
||||
fragBuilder->codeAppend("}");
|
||||
}
|
||||
|
||||
// ambient light
|
||||
fragBuilder->codeAppendf("result += %s * diffuseColor.rgb;", ambientColorUniName);
|
||||
|
||||
// Clamping to alpha (equivalent to an unpremul'd clamp to 1.0)
|
||||
fragBuilder->codeAppendf("%s = float4(clamp(result.rgb, 0.0, diffuseColor.a), "
|
||||
"diffuseColor.a);", args.fOutputColor);
|
||||
}
|
||||
|
||||
static void GenKey(const GrProcessor& proc, const GrShaderCaps&, GrProcessorKeyBuilder* b) {
|
||||
const LightingFP& lightingFP = proc.cast<LightingFP>();
|
||||
b->add32(lightingFP.fDirectionalLights.count());
|
||||
}
|
||||
|
||||
protected:
|
||||
void onSetData(const GrGLSLProgramDataManager& pdman,
|
||||
const GrFragmentProcessor& proc) override {
|
||||
const LightingFP& lightingFP = proc.cast<LightingFP>();
|
||||
|
||||
const SkTArray<SkLights::Light>& directionalLights = lightingFP.directionalLights();
|
||||
if (directionalLights != fDirectionalLights) {
|
||||
SkTArray<SkColor3f> lightDirs(directionalLights.count());
|
||||
SkTArray<SkVector3> lightColors(directionalLights.count());
|
||||
for (const SkLights::Light& light : directionalLights) {
|
||||
lightDirs.push_back(light.dir());
|
||||
lightColors.push_back(light.color());
|
||||
}
|
||||
|
||||
pdman.set3fv(fLightDirsUni, directionalLights.count(), &(lightDirs[0].fX));
|
||||
pdman.set3fv(fLightColorsUni, directionalLights.count(), &(lightColors[0].fX));
|
||||
|
||||
fDirectionalLights = directionalLights;
|
||||
}
|
||||
|
||||
const SkColor3f& ambientColor = lightingFP.ambientColor();
|
||||
if (ambientColor != fAmbientColor) {
|
||||
pdman.set3fv(fAmbientColorUni, 1, &ambientColor.fX);
|
||||
fAmbientColor = ambientColor;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SkTArray<SkLights::Light> fDirectionalLights;
|
||||
GrGLSLProgramDataManager::UniformHandle fLightDirsUni;
|
||||
GrGLSLProgramDataManager::UniformHandle fLightColorsUni;
|
||||
|
||||
SkColor3f fAmbientColor;
|
||||
GrGLSLProgramDataManager::UniformHandle fAmbientColorUni;
|
||||
};
|
||||
|
||||
void onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBuilder* b) const override {
|
||||
GLSLLightingFP::GenKey(*this, caps, b);
|
||||
}
|
||||
|
||||
LightingFP(std::unique_ptr<GrFragmentProcessor> normalFP, sk_sp<SkLights> lights)
|
||||
: INHERITED(kLightingFP_ClassID, kPreservesOpaqueInput_OptimizationFlag) {
|
||||
// fuse all ambient lights into a single one
|
||||
fAmbientColor = lights->ambientLightColor();
|
||||
for (int i = 0; i < lights->numLights(); ++i) {
|
||||
if (SkLights::Light::kDirectional_LightType == lights->light(i).type()) {
|
||||
fDirectionalLights.push_back(lights->light(i));
|
||||
// TODO get the handle to the shadow map if there is one
|
||||
} else {
|
||||
SkDEBUGFAIL("Unimplemented Light Type passed to LightingFP");
|
||||
}
|
||||
}
|
||||
|
||||
this->registerChildProcessor(std::move(normalFP));
|
||||
}
|
||||
|
||||
LightingFP(const LightingFP& that)
|
||||
: INHERITED(kLightingFP_ClassID, kPreservesOpaqueInput_OptimizationFlag)
|
||||
, fDirectionalLights(that.fDirectionalLights)
|
||||
, fAmbientColor(that.fAmbientColor) {
|
||||
this->registerChildProcessor(that.childProcessor(0).clone());
|
||||
}
|
||||
|
||||
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new GLSLLightingFP; }
|
||||
|
||||
bool onIsEqual(const GrFragmentProcessor& proc) const override {
|
||||
const LightingFP& lightingFP = proc.cast<LightingFP>();
|
||||
return fDirectionalLights == lightingFP.fDirectionalLights &&
|
||||
fAmbientColor == lightingFP.fAmbientColor;
|
||||
}
|
||||
|
||||
SkTArray<SkLights::Light> fDirectionalLights;
|
||||
SkColor3f fAmbientColor;
|
||||
|
||||
typedef GrFragmentProcessor INHERITED;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> SkLightingShaderImpl::asFragmentProcessor(const GrFPArgs& args) const {
|
||||
std::unique_ptr<GrFragmentProcessor> normalFP(fNormalSource->asFragmentProcessor(args));
|
||||
if (!normalFP) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (fDiffuseShader) {
|
||||
std::unique_ptr<GrFragmentProcessor> fpPipeline[] = {
|
||||
as_SB(fDiffuseShader)->asFragmentProcessor(args),
|
||||
LightingFP::Make(std::move(normalFP), fLights)
|
||||
};
|
||||
if (!fpPipeline[0] || !fpPipeline[1]) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> innerLightFP = GrFragmentProcessor::RunInSeries(fpPipeline, 2);
|
||||
// FP is wrapped because paint's alpha needs to be applied to output
|
||||
return GrFragmentProcessor::MulChildByInputAlpha(std::move(innerLightFP));
|
||||
} else {
|
||||
// FP is wrapped because paint comes in unpremul'd to fragment shader, but LightingFP
|
||||
// expects premul'd color.
|
||||
return GrFragmentProcessor::PremulInput(LightingFP::Make(std::move(normalFP), fLights));
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SkLightingShaderImpl::isOpaque() const {
|
||||
return (fDiffuseShader ? fDiffuseShader->isOpaque() : false);
|
||||
}
|
||||
|
||||
SkLightingShaderImpl::LightingShaderContext::LightingShaderContext(
|
||||
const SkLightingShaderImpl& shader, const ContextRec& rec,
|
||||
SkShaderBase::Context* diffuseContext, SkNormalSource::Provider* normalProvider,
|
||||
void* heapAllocated)
|
||||
: INHERITED(shader, rec)
|
||||
, fDiffuseContext(diffuseContext)
|
||||
, fNormalProvider(normalProvider) {
|
||||
bool isOpaque = shader.isOpaque();
|
||||
|
||||
// update fFlags
|
||||
uint32_t flags = 0;
|
||||
if (isOpaque && (255 == this->getPaintAlpha())) {
|
||||
flags |= kOpaqueAlpha_Flag;
|
||||
}
|
||||
|
||||
fPaintColor = rec.fPaint->getColor();
|
||||
fFlags = flags;
|
||||
}
|
||||
|
||||
static inline SkPMColor convert(SkColor3f color, U8CPU a) {
|
||||
if (color.fX <= 0.0f) {
|
||||
color.fX = 0.0f;
|
||||
} else if (color.fX >= 255.0f) {
|
||||
color.fX = 255.0f;
|
||||
}
|
||||
|
||||
if (color.fY <= 0.0f) {
|
||||
color.fY = 0.0f;
|
||||
} else if (color.fY >= 255.0f) {
|
||||
color.fY = 255.0f;
|
||||
}
|
||||
|
||||
if (color.fZ <= 0.0f) {
|
||||
color.fZ = 0.0f;
|
||||
} else if (color.fZ >= 255.0f) {
|
||||
color.fZ = 255.0f;
|
||||
}
|
||||
|
||||
return SkPreMultiplyARGB(a, (int) color.fX, (int) color.fY, (int) color.fZ);
|
||||
}
|
||||
|
||||
// larger is better (fewer times we have to loop), but we shouldn't
|
||||
// take up too much stack-space (each one here costs 16 bytes)
|
||||
#define BUFFER_MAX 16
|
||||
void SkLightingShaderImpl::LightingShaderContext::shadeSpan(int x, int y,
|
||||
SkPMColor result[], int count) {
|
||||
const SkLightingShaderImpl& lightShader = static_cast<const SkLightingShaderImpl&>(fShader);
|
||||
|
||||
SkPMColor diffuse[BUFFER_MAX];
|
||||
SkPoint3 normals[BUFFER_MAX];
|
||||
|
||||
SkColor diffColor = fPaintColor;
|
||||
|
||||
do {
|
||||
int n = SkTMin(count, BUFFER_MAX);
|
||||
|
||||
fNormalProvider->fillScanLine(x, y, normals, n);
|
||||
|
||||
if (fDiffuseContext) {
|
||||
fDiffuseContext->shadeSpan(x, y, diffuse, n);
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (fDiffuseContext) {
|
||||
diffColor = SkUnPreMultiply::PMColorToColor(diffuse[i]);
|
||||
}
|
||||
|
||||
SkColor3f accum = SkColor3f::Make(0.0f, 0.0f, 0.0f);
|
||||
|
||||
// Adding ambient light
|
||||
accum.fX += lightShader.fLights->ambientLightColor().fX * SkColorGetR(diffColor);
|
||||
accum.fY += lightShader.fLights->ambientLightColor().fY * SkColorGetG(diffColor);
|
||||
accum.fZ += lightShader.fLights->ambientLightColor().fZ * SkColorGetB(diffColor);
|
||||
|
||||
// This is all done in linear unpremul color space (each component 0..255.0f though)
|
||||
for (int l = 0; l < lightShader.fLights->numLights(); ++l) {
|
||||
const SkLights::Light& light = lightShader.fLights->light(l);
|
||||
|
||||
SkScalar illuminanceScalingFactor = 1.0f;
|
||||
|
||||
if (SkLights::Light::kDirectional_LightType == light.type()) {
|
||||
illuminanceScalingFactor = normals[i].dot(light.dir());
|
||||
if (illuminanceScalingFactor < 0.0f) {
|
||||
illuminanceScalingFactor = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
accum.fX += light.color().fX * SkColorGetR(diffColor) * illuminanceScalingFactor;
|
||||
accum.fY += light.color().fY * SkColorGetG(diffColor) * illuminanceScalingFactor;
|
||||
accum.fZ += light.color().fZ * SkColorGetB(diffColor) * illuminanceScalingFactor;
|
||||
}
|
||||
|
||||
// convert() premultiplies the accumulate color with alpha
|
||||
result[i] = convert(accum, SkColorGetA(diffColor));
|
||||
}
|
||||
|
||||
result += n;
|
||||
x += n;
|
||||
count -= n;
|
||||
} while (count > 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<SkFlattenable> SkLightingShaderImpl::CreateProc(SkReadBuffer& buf) {
|
||||
|
||||
// Discarding SkShader flattenable params
|
||||
bool hasLocalMatrix = buf.readBool();
|
||||
SkAssertResult(!hasLocalMatrix);
|
||||
|
||||
sk_sp<SkLights> lights = SkLights::MakeFromBuffer(buf);
|
||||
|
||||
sk_sp<SkNormalSource> normalSource(buf.readFlattenable<SkNormalSource>());
|
||||
|
||||
bool hasDiffuse = buf.readBool();
|
||||
sk_sp<SkShader> diffuseShader = nullptr;
|
||||
if (hasDiffuse) {
|
||||
diffuseShader = buf.readFlattenable<SkShaderBase>();
|
||||
}
|
||||
|
||||
return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
|
||||
std::move(lights));
|
||||
}
|
||||
|
||||
void SkLightingShaderImpl::flatten(SkWriteBuffer& buf) const {
|
||||
this->INHERITED::flatten(buf);
|
||||
|
||||
fLights->flatten(buf);
|
||||
|
||||
buf.writeFlattenable(fNormalSource.get());
|
||||
buf.writeBool(static_cast<bool>(fDiffuseShader));
|
||||
if (fDiffuseShader) {
|
||||
buf.writeFlattenable(fDiffuseShader.get());
|
||||
}
|
||||
}
|
||||
|
||||
SkShaderBase::Context* SkLightingShaderImpl::onMakeContext(
|
||||
const ContextRec& rec, SkArenaAlloc* alloc) const
|
||||
{
|
||||
SkShaderBase::Context *diffuseContext = nullptr;
|
||||
if (fDiffuseShader) {
|
||||
diffuseContext = as_SB(fDiffuseShader)->makeContext(rec, alloc);
|
||||
if (!diffuseContext) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SkNormalSource::Provider* normalProvider = fNormalSource->asProvider(rec, alloc);
|
||||
if (!normalProvider) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return alloc->make<LightingShaderContext>(*this, rec, diffuseContext, normalProvider, nullptr);
|
||||
}
|
||||
|
||||
sk_sp<SkShader> SkLightingShaderImpl::onMakeColorSpace(SkColorSpaceXformer* xformer) const {
|
||||
sk_sp<SkShader> xformedDiffuseShader =
|
||||
fDiffuseShader ? xformer->apply(fDiffuseShader.get()) : nullptr;
|
||||
return SkLightingShader::Make(std::move(xformedDiffuseShader), fNormalSource,
|
||||
fLights->makeColorSpace(xformer));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
sk_sp<SkShader> SkLightingShader::Make(sk_sp<SkShader> diffuseShader,
|
||||
sk_sp<SkNormalSource> normalSource,
|
||||
sk_sp<SkLights> lights) {
|
||||
SkASSERT(lights);
|
||||
if (!normalSource) {
|
||||
normalSource = SkNormalSource::MakeFlat();
|
||||
}
|
||||
|
||||
return sk_make_sp<SkLightingShaderImpl>(std::move(diffuseShader), std::move(normalSource),
|
||||
std::move(lights));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingShader)
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingShaderImpl)
|
||||
SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
40
src/shaders/SkLightingShader.h
Normal file
40
src/shaders/SkLightingShader.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkLightingShader_DEFINED
|
||||
#define SkLightingShader_DEFINED
|
||||
|
||||
#include "SkFlattenablePriv.h"
|
||||
#include "SkLights.h"
|
||||
#include "SkShader.h"
|
||||
|
||||
class SkBitmap;
|
||||
class SkMatrix;
|
||||
class SkNormalSource;
|
||||
|
||||
class SK_API SkLightingShader {
|
||||
public:
|
||||
/** Returns a shader that lights the shape, colored by the diffuseShader, using the
|
||||
normals from normalSource, with the set of lights provided.
|
||||
|
||||
@param diffuseShader the shader that provides the colors. If nullptr, uses the paint's
|
||||
color.
|
||||
@param normalSource the source for the shape's normals. If nullptr, assumes straight
|
||||
up normals (<0,0,1>).
|
||||
@param lights the lights applied to the normals
|
||||
|
||||
The lighting equation is currently:
|
||||
result = (LightColor * dot(Normal, LightDir) + AmbientColor) * DiffuseColor
|
||||
|
||||
*/
|
||||
static sk_sp<SkShader> Make(sk_sp<SkShader> diffuseShader, sk_sp<SkNormalSource> normalSource,
|
||||
sk_sp<SkLights> lights);
|
||||
|
||||
SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
|
||||
};
|
||||
|
||||
#endif
|
@ -13,9 +13,11 @@
|
||||
#include "SkFontDescriptor.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkImageSource.h"
|
||||
#include "SkLightingShader.h"
|
||||
#include "SkMakeUnique.h"
|
||||
#include "SkMallocPixelRef.h"
|
||||
#include "SkMatrixPriv.h"
|
||||
#include "SkNormalSource.h"
|
||||
#include "SkOSFile.h"
|
||||
#include "SkReadBuffer.h"
|
||||
#include "SkPicturePriv.h"
|
||||
@ -558,6 +560,64 @@ DEF_TEST(Serialization, reporter) {
|
||||
}
|
||||
|
||||
TestPictureTypefaceSerialization(reporter);
|
||||
|
||||
// Test SkLightingShader/NormalMapSource serialization
|
||||
{
|
||||
const int kTexSize = 2;
|
||||
|
||||
SkLights::Builder builder;
|
||||
|
||||
builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
|
||||
SkVector3::Make(1.0f, 0.0f, 0.0f)));
|
||||
builder.setAmbientLightColor(SkColor3f::Make(0.2f, 0.2f, 0.2f));
|
||||
|
||||
sk_sp<SkLights> fLights = builder.finish();
|
||||
|
||||
SkBitmap diffuse = sk_tool_utils::create_checkerboard_bitmap(
|
||||
kTexSize, kTexSize,
|
||||
sk_tool_utils::color_to_565(0x0),
|
||||
sk_tool_utils::color_to_565(0xFF804020),
|
||||
8);
|
||||
|
||||
SkRect bitmapBounds = SkRect::MakeIWH(diffuse.width(), diffuse.height());
|
||||
|
||||
SkMatrix matrix;
|
||||
SkRect r = SkRect::MakeWH(SkIntToScalar(kTexSize), SkIntToScalar(kTexSize));
|
||||
matrix.setRectToRect(bitmapBounds, r, SkMatrix::kFill_ScaleToFit);
|
||||
|
||||
SkMatrix ctm;
|
||||
ctm.setRotate(45);
|
||||
SkBitmap normals;
|
||||
normals.allocN32Pixels(kTexSize, kTexSize);
|
||||
|
||||
sk_tool_utils::create_frustum_normal_map(&normals, SkIRect::MakeWH(kTexSize, kTexSize));
|
||||
sk_sp<SkShader> normalMap = SkShader::MakeBitmapShader(normals, SkShader::kClamp_TileMode,
|
||||
SkShader::kClamp_TileMode, &matrix);
|
||||
sk_sp<SkNormalSource> normalSource = SkNormalSource::MakeFromNormalMap(std::move(normalMap),
|
||||
ctm);
|
||||
sk_sp<SkShader> diffuseShader = SkShader::MakeBitmapShader(diffuse,
|
||||
SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix);
|
||||
|
||||
sk_sp<SkShader> lightingShader = SkLightingShader::Make(diffuseShader,
|
||||
normalSource,
|
||||
fLights);
|
||||
sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
|
||||
|
||||
lightingShader = SkLightingShader::Make(std::move(diffuseShader),
|
||||
nullptr,
|
||||
fLights);
|
||||
sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
|
||||
|
||||
lightingShader = SkLightingShader::Make(nullptr,
|
||||
std::move(normalSource),
|
||||
fLights);
|
||||
sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
|
||||
|
||||
lightingShader = SkLightingShader::Make(nullptr,
|
||||
nullptr,
|
||||
fLights);
|
||||
sk_sp<SkShader>(TestFlattenableSerialization(as_SB(lightingShader.get()), true, reporter));
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "SkPath.h"
|
||||
#include "SkPixelRef.h"
|
||||
#include "SkPixmap.h"
|
||||
#include "SkPoint.h"
|
||||
#include "SkPoint3.h"
|
||||
#include "SkRRect.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkSurface.h"
|
||||
@ -164,6 +164,114 @@ SkPath make_star(const SkRect& bounds, int numPts, int step) {
|
||||
return path;
|
||||
}
|
||||
|
||||
static inline void norm_to_rgb(SkBitmap* bm, int x, int y, const SkVector3& norm) {
|
||||
SkASSERT(SkScalarNearlyEqual(norm.length(), 1.0f));
|
||||
unsigned char r = static_cast<unsigned char>((0.5f * norm.fX + 0.5f) * 255);
|
||||
unsigned char g = static_cast<unsigned char>((-0.5f * norm.fY + 0.5f) * 255);
|
||||
unsigned char b = static_cast<unsigned char>((0.5f * norm.fZ + 0.5f) * 255);
|
||||
*bm->getAddr32(x, y) = SkPackARGB32(0xFF, r, g, b);
|
||||
}
|
||||
|
||||
void create_hemi_normal_map(SkBitmap* bm, const SkIRect& dst) {
|
||||
const SkPoint center = SkPoint::Make(dst.fLeft + (dst.width() / 2.0f),
|
||||
dst.fTop + (dst.height() / 2.0f));
|
||||
const SkPoint halfSize = SkPoint::Make(dst.width() / 2.0f, dst.height() / 2.0f);
|
||||
|
||||
SkVector3 norm;
|
||||
|
||||
for (int y = dst.fTop; y < dst.fBottom; ++y) {
|
||||
for (int x = dst.fLeft; x < dst.fRight; ++x) {
|
||||
norm.fX = (x + 0.5f - center.fX) / halfSize.fX;
|
||||
norm.fY = (y + 0.5f - center.fY) / halfSize.fY;
|
||||
|
||||
SkScalar tmp = norm.fX * norm.fX + norm.fY * norm.fY;
|
||||
if (tmp >= 1.0f) {
|
||||
norm.set(0.0f, 0.0f, 1.0f);
|
||||
} else {
|
||||
norm.fZ = sqrtf(1.0f - tmp);
|
||||
}
|
||||
|
||||
norm_to_rgb(bm, x, y, norm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void create_frustum_normal_map(SkBitmap* bm, const SkIRect& dst) {
|
||||
const SkPoint center = SkPoint::Make(dst.fLeft + (dst.width() / 2.0f),
|
||||
dst.fTop + (dst.height() / 2.0f));
|
||||
|
||||
SkIRect inner = dst;
|
||||
inner.inset(dst.width()/4, dst.height()/4);
|
||||
|
||||
SkPoint3 norm;
|
||||
const SkPoint3 left = SkPoint3::Make(-SK_ScalarRoot2Over2, 0.0f, SK_ScalarRoot2Over2);
|
||||
const SkPoint3 up = SkPoint3::Make(0.0f, -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
|
||||
const SkPoint3 right = SkPoint3::Make(SK_ScalarRoot2Over2, 0.0f, SK_ScalarRoot2Over2);
|
||||
const SkPoint3 down = SkPoint3::Make(0.0f, SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
|
||||
|
||||
for (int y = dst.fTop; y < dst.fBottom; ++y) {
|
||||
for (int x = dst.fLeft; x < dst.fRight; ++x) {
|
||||
if (inner.contains(x, y)) {
|
||||
norm.set(0.0f, 0.0f, 1.0f);
|
||||
} else {
|
||||
SkScalar locX = x + 0.5f - center.fX;
|
||||
SkScalar locY = y + 0.5f - center.fY;
|
||||
|
||||
if (locX >= 0.0f) {
|
||||
if (locY > 0.0f) {
|
||||
norm = locX >= locY ? right : down; // LR corner
|
||||
} else {
|
||||
norm = locX > -locY ? right : up; // UR corner
|
||||
}
|
||||
} else {
|
||||
if (locY > 0.0f) {
|
||||
norm = -locX > locY ? left : down; // LL corner
|
||||
} else {
|
||||
norm = locX > locY ? up : left; // UL corner
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
norm_to_rgb(bm, x, y, norm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void create_tetra_normal_map(SkBitmap* bm, const SkIRect& dst) {
|
||||
const SkPoint center = SkPoint::Make(dst.fLeft + (dst.width() / 2.0f),
|
||||
dst.fTop + (dst.height() / 2.0f));
|
||||
|
||||
static const SkScalar k1OverRoot3 = 0.5773502692f;
|
||||
|
||||
SkPoint3 norm;
|
||||
const SkPoint3 leftUp = SkPoint3::Make(-k1OverRoot3, -k1OverRoot3, k1OverRoot3);
|
||||
const SkPoint3 rightUp = SkPoint3::Make(k1OverRoot3, -k1OverRoot3, k1OverRoot3);
|
||||
const SkPoint3 down = SkPoint3::Make(0.0f, SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
|
||||
|
||||
for (int y = dst.fTop; y < dst.fBottom; ++y) {
|
||||
for (int x = dst.fLeft; x < dst.fRight; ++x) {
|
||||
SkScalar locX = x + 0.5f - center.fX;
|
||||
SkScalar locY = y + 0.5f - center.fY;
|
||||
|
||||
if (locX >= 0.0f) {
|
||||
if (locY > 0.0f) {
|
||||
norm = locX >= locY ? rightUp : down; // LR corner
|
||||
} else {
|
||||
norm = rightUp;
|
||||
}
|
||||
} else {
|
||||
if (locY > 0.0f) {
|
||||
norm = -locX > locY ? leftUp : down; // LL corner
|
||||
} else {
|
||||
norm = leftUp;
|
||||
}
|
||||
}
|
||||
|
||||
norm_to_rgb(bm, x, y, norm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__clang__) && defined(_MSC_VER)
|
||||
// MSVC takes ~2 minutes to compile this function with optimization.
|
||||
// We don't really care to wait that long for this function.
|
||||
|
@ -140,6 +140,12 @@ namespace sk_tool_utils {
|
||||
// numPts and step must be co-prime.
|
||||
SkPath make_star(const SkRect& bounds, int numPts = 5, int step = 2);
|
||||
|
||||
void create_hemi_normal_map(SkBitmap* bm, const SkIRect& dst);
|
||||
|
||||
void create_frustum_normal_map(SkBitmap* bm, const SkIRect& dst);
|
||||
|
||||
void create_tetra_normal_map(SkBitmap* bm, const SkIRect& dst);
|
||||
|
||||
void make_big_path(SkPath& path);
|
||||
|
||||
// Return a blurred version of 'src'. This doesn't use a separable filter
|
||||
|
Loading…
Reference in New Issue
Block a user