blob: 40e6fbb073bb1075f2d9402a9cb97273a8c56c92 [file] [log] [blame]
jvanverthd17a3292015-07-09 09:04:16 -07001
2/*
3 * Copyright 2015 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "SampleCode.h"
9#include "Resources.h"
10
11#include "SkCanvas.h"
12#include "SkErrorInternals.h"
13#include "SkGr.h"
14#include "SkReadBuffer.h"
15#include "SkShader.h"
16#include "SkWriteBuffer.h"
17#include "GrFragmentProcessor.h"
18#include "GrCoordTransform.h"
19#include "gl/GrGLProcessor.h"
20#include "gl/builders/GrGLProgramBuilder.h"
21
22///////////////////////////////////////////////////////////////////////////////
23
24struct SkVector3 {
25 SkScalar fX, fY, fZ;
26
27 bool operator==(const SkVector3& other) const {
28 return fX == other.fX && fY == other.fY && fZ == other.fZ;
29 }
30
31 bool operator!=(const SkVector3& other) const {
32 return !(*this == other);
33 }
34};
35
36class LightingShader : public SkShader {
37public:
38 struct Light {
39 SkVector3 fDirection;
40 SkColor fColor; // assumed to be linear color
41 };
42
43 LightingShader(const SkBitmap& diffuse, const SkBitmap& normal, const Light& light,
44 const SkColor ambient)
45 : fDiffuseMap(diffuse)
46 , fNormalMap(normal)
47 , fLight(light)
48 , fAmbientColor(ambient) {}
49
50 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(LightingShader);
51
52 void flatten(SkWriteBuffer& buf) const override {
53 buf.writeBitmap(fDiffuseMap);
54 buf.writeBitmap(fNormalMap);
55 buf.writeScalarArray(&fLight.fDirection.fX, 3);
56 buf.writeColor(fLight.fColor);
57 buf.writeColor(fAmbientColor);
58 }
59
60 bool asFragmentProcessor(GrContext*, const SkPaint& paint, const SkMatrix& viewM,
61 const SkMatrix* localMatrix, GrColor* color,
62 GrProcessorDataManager*, GrFragmentProcessor** fp) const override;
63
64 SkShader::BitmapType asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
65 SkShader::TileMode* xy) const override {
66 if (bitmap) {
67 *bitmap = fDiffuseMap;
68 }
69 if (matrix) {
70 matrix->reset();
71 }
72 if (xy) {
73 xy[0] = kClamp_TileMode;
74 xy[1] = kClamp_TileMode;
75 }
76 return kDefault_BitmapType;
77 }
78
79#ifndef SK_IGNORE_TO_STRING
80 void toString(SkString* str) const override {
81 str->appendf("LightingShader: ()");
82 }
83#endif
84
85 void setLight(const Light& light) { fLight = light; }
86
87private:
88 SkBitmap fDiffuseMap;
89 SkBitmap fNormalMap;
90 Light fLight;
91 SkColor fAmbientColor;
92};
93
94SkFlattenable* LightingShader::CreateProc(SkReadBuffer& buf) {
95 SkBitmap diffuse;
96 if (!buf.readBitmap(&diffuse)) {
97 return NULL;
98 }
99 diffuse.setImmutable();
100
101 SkBitmap normal;
102 if (!buf.readBitmap(&normal)) {
103 return NULL;
104 }
105 normal.setImmutable();
106
107 Light light;
108 if (!buf.readScalarArray(&light.fDirection.fX, 3)) {
109 return NULL;
110 }
111 light.fColor = buf.readColor();
112
113 SkColor ambient = buf.readColor();
114
115 return SkNEW_ARGS(LightingShader, (diffuse, normal, light, ambient));
116}
117
118////////////////////////////////////////////////////////////////////////////
119
120class LightingFP : public GrFragmentProcessor {
121public:
122 LightingFP(GrTexture* diffuse, GrTexture* normal, const SkMatrix& matrix,
123 SkVector3 lightDir, GrColor lightColor, GrColor ambientColor)
124 : fDeviceTransform(kDevice_GrCoordSet, matrix)
125 , fDiffuseTextureAccess(diffuse)
126 , fNormalTextureAccess(normal)
127 , fLightDir(lightDir)
128 , fLightColor(lightColor)
129 , fAmbientColor(ambientColor) {
130 this->addCoordTransform(&fDeviceTransform);
131 this->addTextureAccess(&fDiffuseTextureAccess);
132 this->addTextureAccess(&fNormalTextureAccess);
133
134 this->initClassID<LightingFP>();
135 }
136
137 class LightingGLFP : public GrGLFragmentProcessor {
138 public:
139 LightingGLFP() : fLightColor(GrColor_ILLEGAL) {
140 fLightDir.fX = 10000.0f;
141 }
142
143 void emitCode(GrGLFPBuilder* builder,
144 const GrFragmentProcessor& fp,
145 const char* outputColor,
146 const char* inputColor,
147 const TransformedCoordsArray& coords,
148 const TextureSamplerArray& samplers) override {
149
150 GrGLFragmentBuilder* fpb = builder->getFragmentShaderBuilder();
151
152 // add uniforms
153 const char* lightDirUniName = NULL;
154 fLightDirUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
155 kVec3f_GrSLType, kDefault_GrSLPrecision,
156 "LightDir", &lightDirUniName);
157
158 const char* lightColorUniName = NULL;
159 fLightColorUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
160 kVec4f_GrSLType, kDefault_GrSLPrecision,
161 "LightColor", &lightColorUniName);
162
163 const char* ambientColorUniName = NULL;
164 fAmbientColorUni = builder->addUniform(GrGLProgramBuilder::kFragment_Visibility,
165 kVec4f_GrSLType, kDefault_GrSLPrecision,
166 "AmbientColor", &ambientColorUniName);
167
168 fpb->codeAppend("vec4 diffuseColor = ");
169 fpb->appendTextureLookupAndModulate(inputColor, samplers[0],
170 coords[0].c_str(), coords[0].getType());
171 fpb->codeAppend(";");
172
173 fpb->codeAppend("vec4 normalColor = ");
174 fpb->appendTextureLookup(samplers[1], coords[0].c_str(), coords[0].getType());
175 fpb->codeAppend(";");
176
177 fpb->codeAppend("vec3 normal = normalize(2.0*(normalColor.rgb - vec3(0.5)));");
178 fpb->codeAppendf("vec3 lightDir = normalize(%s);", lightDirUniName);
179 fpb->codeAppend("float NdotL = dot(normal, lightDir);");
180 // diffuse light
181 fpb->codeAppendf("vec3 result = %s.rgb*diffuseColor.rgb*NdotL;", lightColorUniName);
182 // ambient light
183 fpb->codeAppendf("result += %s.rgb;", ambientColorUniName);
184 fpb->codeAppendf("%s = vec4(result.rgb, diffuseColor.a);", outputColor);
185 }
186
187 void setData(const GrGLProgramDataManager& pdman, const GrProcessor& proc) override {
188 const LightingFP& lightingFP = proc.cast<LightingFP>();
189
190 SkVector3 lightDir = lightingFP.lightDir();
191 if (lightDir != fLightDir) {
192 pdman.set3fv(fLightDirUni, 1, &lightDir.fX);
193 fLightDir = lightDir;
194 }
195
196 GrColor lightColor = lightingFP.lightColor();
197 if (lightColor != fLightColor) {
198 GrGLfloat c[4];
199 GrColorToRGBAFloat(lightColor, c);
200 pdman.set4fv(fLightColorUni, 1, c);
201 fLightColor = lightColor;
202 }
203
204 GrColor ambientColor = lightingFP.ambientColor();
205 if (ambientColor != fAmbientColor) {
206 GrGLfloat c[4];
207 GrColorToRGBAFloat(ambientColor, c);
208 pdman.set4fv(fAmbientColorUni, 1, c);
209 fAmbientColor = ambientColor;
210 }
211 }
212
213 static void GenKey(const GrProcessor& proc, const GrGLSLCaps&,
214 GrProcessorKeyBuilder* b) {
215// const LightingFP& lightingFP = proc.cast<LightingFP>();
216 // only one shader generated currently
217 b->add32(0x0);
218 }
219
220 private:
221 SkVector3 fLightDir;
222 GrGLProgramDataManager::UniformHandle fLightDirUni;
223
224 GrColor fLightColor;
225 GrGLProgramDataManager::UniformHandle fLightColorUni;
226
227 GrColor fAmbientColor;
228 GrGLProgramDataManager::UniformHandle fAmbientColorUni;
229 };
230
231 GrGLFragmentProcessor* createGLInstance() const override { return SkNEW(LightingGLFP); }
232
233 void getGLProcessorKey(const GrGLSLCaps& caps, GrProcessorKeyBuilder* b) const override {
234 LightingGLFP::GenKey(*this, caps, b);
235 }
236
237 const char* name() const override { return "LightingFP"; }
238
239 void onComputeInvariantOutput(GrInvariantOutput* inout) const override {
240 inout->mulByUnknownFourComponents();
241 }
242
243 SkVector3 lightDir() const { return fLightDir; }
244 GrColor lightColor() const { return fLightColor; }
245 GrColor ambientColor() const { return fAmbientColor; }
246
247private:
248 bool onIsEqual(const GrFragmentProcessor& proc) const override {
249 const LightingFP& lightingFP = proc.cast<LightingFP>();
250 return fDeviceTransform == lightingFP.fDeviceTransform &&
251 fDiffuseTextureAccess == lightingFP.fDiffuseTextureAccess &&
252 fNormalTextureAccess == lightingFP.fNormalTextureAccess &&
253 fLightDir == lightingFP.fLightDir &&
254 fLightColor == lightingFP.fLightColor &&
255 fAmbientColor == lightingFP.fAmbientColor;
256 }
257
258 GrCoordTransform fDeviceTransform;
259 GrTextureAccess fDiffuseTextureAccess;
260 GrTextureAccess fNormalTextureAccess;
261 SkVector3 fLightDir;
262 GrColor fLightColor;
263 GrColor fAmbientColor;
264};
265
266bool LightingShader::asFragmentProcessor(GrContext* context, const SkPaint& paint,
267 const SkMatrix& viewM, const SkMatrix* localMatrix,
268 GrColor* color, GrProcessorDataManager*,
269 GrFragmentProcessor** fp) const {
270 // we assume diffuse and normal maps have same width and height
271 // TODO: support different sizes
272 SkASSERT(fDiffuseMap.width() == fNormalMap.width() &&
273 fDiffuseMap.height() == fNormalMap.height());
274 SkMatrix matrix;
275 matrix.setIDiv(fDiffuseMap.width(), fDiffuseMap.height());
276
277 SkMatrix lmInverse;
278 if (!this->getLocalMatrix().invert(&lmInverse)) {
279 return false;
280 }
281 if (localMatrix) {
282 SkMatrix inv;
283 if (!localMatrix->invert(&inv)) {
284 return false;
285 }
286 lmInverse.postConcat(inv);
287 }
288 matrix.preConcat(lmInverse);
289
290 // Must set wrap and filter on the sampler before requesting a texture. In two places below
291 // we check the matrix scale factors to determine how to interpret the filter quality setting.
292 // This completely ignores the complexity of the drawVertices case where explicit local coords
293 // are provided by the caller.
294 GrTextureParams::FilterMode textureFilterMode = GrTextureParams::kBilerp_FilterMode;
295 switch (paint.getFilterQuality()) {
296 case kNone_SkFilterQuality:
297 textureFilterMode = GrTextureParams::kNone_FilterMode;
298 break;
299 case kLow_SkFilterQuality:
300 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
301 break;
302 case kMedium_SkFilterQuality:{
303 SkMatrix matrix;
304 matrix.setConcat(viewM, this->getLocalMatrix());
305 if (matrix.getMinScale() < SK_Scalar1) {
306 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
307 } else {
308 // Don't trigger MIP level generation unnecessarily.
309 textureFilterMode = GrTextureParams::kBilerp_FilterMode;
310 }
311 break;
312 }
313 case kHigh_SkFilterQuality:
314 default:
315 SkErrorInternals::SetError(kInvalidPaint_SkError,
316 "Sorry, I don't understand the filtering "
317 "mode you asked for. Falling back to "
318 "MIPMaps.");
319 textureFilterMode = GrTextureParams::kMipMap_FilterMode;
320 break;
321
322 }
323
324 // TODO: support other tile modes
325 GrTextureParams params(kClamp_TileMode, textureFilterMode);
326 SkAutoTUnref<GrTexture> diffuseTexture(GrRefCachedBitmapTexture(context, fDiffuseMap, &params));
327 if (!diffuseTexture) {
328 SkErrorInternals::SetError(kInternalError_SkError,
329 "Couldn't convert bitmap to texture.");
330 return false;
331 }
332
333 SkAutoTUnref<GrTexture> normalTexture(GrRefCachedBitmapTexture(context, fNormalMap, &params));
334 if (!normalTexture) {
335 SkErrorInternals::SetError(kInternalError_SkError,
336 "Couldn't convert bitmap to texture.");
337 return false;
338 }
339
340 GrColor lightColor = GrColorPackRGBA(SkColorGetR(fLight.fColor), SkColorGetG(fLight.fColor),
341 SkColorGetB(fLight.fColor), SkColorGetA(fLight.fColor));
342 GrColor ambientColor = GrColorPackRGBA(SkColorGetR(fAmbientColor), SkColorGetG(fAmbientColor),
343 SkColorGetB(fAmbientColor), SkColorGetA(fAmbientColor));
344
345 *fp = SkNEW_ARGS(LightingFP, (diffuseTexture, normalTexture, matrix,
346 fLight.fDirection, lightColor, ambientColor));
347 *color = GrColorPackA4(paint.getAlpha());
348 return true;
349}
350
351////////////////////////////////////////////////////////////////////////////
352
353class LightingView : public SampleView {
354public:
355 SkAutoTUnref<LightingShader> fShader;
356 SkBitmap fDiffuseBitmap;
357 SkBitmap fNormalBitmap;
358 SkScalar fLightAngle;
359 int fColorFactor;
360
361 LightingView() {
362 SkString diffusePath = GetResourcePath("brickwork-texture.jpg");
363 SkImageDecoder::DecodeFile(diffusePath.c_str(), &fDiffuseBitmap);
364 SkString normalPath = GetResourcePath("brickwork_normal-map.jpg");
365 SkImageDecoder::DecodeFile(normalPath.c_str(), &fNormalBitmap);
366
367 fLightAngle = 0.0f;
368 fColorFactor = 0;
369
370 LightingShader::Light light;
371 light.fColor = SkColorSetRGB(0xff, 0xff, 0xff);
372 light.fDirection.fX = SkScalarSin(fLightAngle)*SkScalarSin(SK_ScalarPI*0.25f);
373 light.fDirection.fY = SkScalarCos(fLightAngle)*SkScalarSin(SK_ScalarPI*0.25f);
374 light.fDirection.fZ = SkScalarCos(SK_ScalarPI*0.25f);
375
376 SkColor ambient = SkColorSetRGB(0x1f, 0x1f, 0x1f);
377
378 fShader.reset(SkNEW_ARGS(LightingShader, (fDiffuseBitmap, fNormalBitmap, light, ambient)));
379 }
380
381 virtual ~LightingView() {}
382
383protected:
384 // overrides from SkEventSink
385 bool onQuery(SkEvent* evt) override {
386 if (SampleCode::TitleQ(*evt)) {
387 SampleCode::TitleR(evt, "Lighting");
388 return true;
389 }
390 return this->INHERITED::onQuery(evt);
391 }
392
393 void onDrawContent(SkCanvas* canvas) override {
394 fLightAngle += 0.015f;
395 fColorFactor++;
396
397 LightingShader::Light light;
398 light.fColor = SkColorSetRGB(0xff, 0xff, (fColorFactor >> 1) & 0xff);
399 light.fDirection.fX = SkScalarSin(fLightAngle)*SkScalarSin(SK_ScalarPI*0.25f);
400 light.fDirection.fY = SkScalarCos(fLightAngle)*SkScalarSin(SK_ScalarPI*0.25f);
401 light.fDirection.fZ = SkScalarCos(SK_ScalarPI*0.25f);
402
403 fShader.get()->setLight(light);
404
405 SkPaint paint;
406 paint.setShader(fShader);
407 paint.setColor(SK_ColorBLACK);
408
409 SkRect r = SkRect::MakeWH((SkScalar)fDiffuseBitmap.width(),
410 (SkScalar)fDiffuseBitmap.height());
411 canvas->drawRect(r, paint);
412
413 // so we're constantly updating
414 this->inval(NULL);
415 }
416
417 SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
418 this->inval(NULL);
419 return this->INHERITED::onFindClickHandler(x, y, modi);
420 }
421
422private:
423 typedef SampleView INHERITED;
424};
425
426//////////////////////////////////////////////////////////////////////////////
427
428static SkView* MyFactory() { return new LightingView; }
429static SkViewRegister reg(MyFactory);