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:
Robert Phillips 2018-07-17 12:30:40 -04:00 committed by Skia Commit-Bot
parent 9e0d7e4072
commit a8cdbd7431
23 changed files with 2624 additions and 2 deletions

160
gm/lightingshader.cpp Normal file
View 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
View 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;)
}

View File

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

View File

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

View File

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

View File

@ -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
View 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

View 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);

View 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
View 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());
}
}
}

View 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>();
}

View 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

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

View 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

View 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
View 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

View File

@ -153,6 +153,9 @@ public:
kSwizzleFragmentProcessor_ClassID,
kTestFP_ClassID,
kTextureGeometryProcessor_ClassID,
kFlatNormalsFP_ClassID,
kMappedNormalsFP_ClassID,
kLightingFP_ClassID,
};
virtual ~GrProcessor() = default;

View File

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

View 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
///////////////////////////////////////////////////////////////////////////////

View 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

View File

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

View File

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

View File

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