lightingshader2.cpp revision 84cddf6fa7a2ee4a8163f99c9238d5fba6b49566
1/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "gm.h"
9#include "SkLightingShader.h"
10#include "SkNormalSource.h"
11#include "SkPoint3.h"
12#include "SkShader.h"
13
14// Create a truncated pyramid normal map
15static SkBitmap make_frustum_normalmap(int texSize) {
16    SkBitmap frustum;
17    frustum.allocN32Pixels(texSize, texSize);
18
19    sk_tool_utils::create_frustum_normal_map(&frustum, SkIRect::MakeWH(texSize, texSize));
20    return frustum;
21}
22
23namespace skiagm {
24
25// This GM exercises lighting shaders. Specifically, nullptr arguments, scaling when using
26// normal maps, paint transparency, zero directional lights, multiple directional lights.
27class LightingShader2GM : public GM {
28public:
29    LightingShader2GM() {
30        this->setBGColor(sk_tool_utils::color_to_565(0xFFCCCCCC));
31    }
32
33protected:
34    SkString onShortName() override {
35        return SkString("lightingshader2");
36    }
37
38    SkISize onISize() override {
39        return SkISize::Make(600, 740);
40    }
41
42    void onOnceBeforeDraw() override {
43        const SkVector3 kLightFromUpperRight = SkVector3::Make(0.788f, 0.394f, 0.473f);
44        const SkVector3 kLightFromUpperLeft = SkVector3::Make(-0.788f, 0.394f, 0.473f);
45
46        // Standard set of lights
47        SkLights::Builder builder;
48        builder.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 1.0f, 1.0f),
49                                                     kLightFromUpperRight));
50        builder.add(SkLights::Light::MakeAmbient(SkColor3f::Make(0.2f, 0.2f, 0.2f)));
51        fLights = builder.finish();
52
53        // No directional lights
54        SkLights::Builder builderNoDir;
55        builderNoDir.add(SkLights::Light::MakeAmbient(SkColor3f::Make(0.2f, 0.2f, 0.2f)));
56        fLightsNoDir = builderNoDir.finish();
57
58        // Two directional lights
59        SkLights::Builder builderTwoDir;
60        builderTwoDir.add(SkLights::Light::MakeDirectional(SkColor3f::Make(1.0f, 0.0f, 1.0f),
61                                                           kLightFromUpperRight));
62        builderTwoDir.add(SkLights::Light::MakeDirectional(SkColor3f::Make(0.0f, 1.0f, 1.0f),
63                                                           kLightFromUpperLeft));
64        builderTwoDir.add(SkLights::Light::MakeAmbient(SkColor3f::Make(0.2f, 0.2f, 0.2f)));
65        fLightsTwoDir = builderTwoDir.finish();
66
67        fRect = SkRect::MakeIWH(kTexSize, kTexSize);
68        SkMatrix matrix;
69        SkRect bitmapBounds = SkRect::MakeIWH(kTexSize, kTexSize);
70        matrix.setRectToRect(bitmapBounds, fRect, SkMatrix::kFill_ScaleToFit);
71
72        SkBitmap opaqueDiffuseMap = sk_tool_utils::create_checkerboard_bitmap(
73                kTexSize, kTexSize,
74                sk_tool_utils::color_to_565(0x0),
75                sk_tool_utils::color_to_565(0xFF804020),
76                8);
77        fOpaqueDiffuse = SkShader::MakeBitmapShader(opaqueDiffuseMap, SkShader::kClamp_TileMode,
78                                                    SkShader::kClamp_TileMode, &matrix);
79
80        SkBitmap translucentDiffuseMap = sk_tool_utils::create_checkerboard_bitmap(
81                kTexSize, kTexSize,
82                SkColorSetARGB(0x55, 0x00, 0x00, 0x00),
83                SkColorSetARGB(0x55, 0x80, 0x40, 0x20),
84                8);
85        fTranslucentDiffuse = SkShader::MakeBitmapShader(translucentDiffuseMap,
86                                                         SkShader::kClamp_TileMode,
87                                                         SkShader::kClamp_TileMode, &matrix);
88
89        SkBitmap normalMap = make_frustum_normalmap(kTexSize);
90        fNormalMapShader = SkShader::MakeBitmapShader(normalMap, SkShader::kClamp_TileMode,
91                                                      SkShader::kClamp_TileMode, &matrix);
92
93    }
94
95    // Scales shape around origin, rotates shape around origin, then translates shape to origin
96    void positionCTM(SkCanvas *canvas, SkScalar scaleX, SkScalar scaleY, SkScalar rotate) const {
97        canvas->translate(kTexSize/2.0f, kTexSize/2.0f);
98        canvas->scale(scaleX, scaleY);
99        canvas->rotate(rotate);
100        canvas->translate(-kTexSize/2.0f, -kTexSize/2.0f);
101    }
102
103    static constexpr int NUM_BOOLEAN_PARAMS = 4;
104    void drawRect(SkCanvas* canvas, SkScalar scaleX, SkScalar scaleY,
105                  SkScalar rotate, bool useNormalSource, bool useDiffuseShader,
106                  bool useTranslucentPaint, bool useTranslucentShader, sk_sp<SkLights> lights) {
107        canvas->save();
108
109        this->positionCTM(canvas, scaleX, scaleY, rotate);
110
111        const SkMatrix& ctm = canvas->getTotalMatrix();
112
113        SkPaint paint;
114        sk_sp<SkNormalSource> normalSource = nullptr;
115        sk_sp<SkShader> diffuseShader = nullptr;
116
117        if (useNormalSource) {
118            normalSource = SkNormalSource::MakeFromNormalMap(fNormalMapShader, ctm);
119        }
120
121        if (useDiffuseShader) {
122            diffuseShader = (useTranslucentShader) ? fTranslucentDiffuse : fOpaqueDiffuse;
123        } else {
124            paint.setColor(0xFF00FF00);
125        }
126
127        if (useTranslucentPaint) {
128            paint.setAlpha(0x99);
129        }
130
131        paint.setShader(SkLightingShader::Make(std::move(diffuseShader), std::move(normalSource),
132                                               std::move(lights)));
133        canvas->drawRect(fRect, paint);
134
135        canvas->restore();
136    }
137
138    void onDraw(SkCanvas* canvas) override {
139
140        constexpr SkScalar LABEL_SIZE = 10.0f;
141        SkPaint labelPaint;
142        labelPaint.setTypeface(sk_tool_utils::create_portable_typeface("sans-serif",
143                                                                       SkFontStyle()));
144        labelPaint.setAntiAlias(true);
145        labelPaint.setTextSize(LABEL_SIZE);
146
147        constexpr int GRID_COLUMN_NUM = 4;
148        constexpr SkScalar GRID_CELL_WIDTH = kTexSize + 20.0f + NUM_BOOLEAN_PARAMS * LABEL_SIZE;
149
150        int gridNum = 0;
151
152        // Running through all possible bool parameter combinations
153        for (bool useNormalSource : {true, false}) {
154            for (bool useDiffuseShader : {true, false}) {
155                for (bool useTranslucentPaint : {true, false}) {
156                    for (bool useTranslucentShader : {true, false}) {
157
158                        // Determining position
159                        SkScalar xPos = (gridNum % GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
160                        SkScalar yPos = (gridNum / GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
161
162                        canvas->save();
163
164                        canvas->translate(xPos, yPos);
165                        this->drawRect(canvas, 1.0f, 1.0f, 0.f, useNormalSource, useDiffuseShader,
166                                       useTranslucentPaint, useTranslucentShader, fLights);
167                        // Drawing labels
168                        canvas->translate(0.0f, SkIntToScalar(kTexSize));
169                        {
170                            canvas->translate(0.0f, LABEL_SIZE);
171                            SkString label;
172                            label.appendf("useNormalSource: %d", useNormalSource);
173                            canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
174                        }
175                        {
176                            canvas->translate(0.0f, LABEL_SIZE);
177                            SkString label;
178                            label.appendf("useDiffuseShader: %d", useDiffuseShader);
179                            canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
180                        }
181                        {
182                            canvas->translate(0.0f, LABEL_SIZE);
183                            SkString label;
184                            label.appendf("useTranslucentPaint: %d", useTranslucentPaint);
185                            canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
186                        }
187                        {
188                            canvas->translate(0.0f, LABEL_SIZE);
189                            SkString label;
190                            label.appendf("useTranslucentShader: %d", useTranslucentShader);
191                            canvas->drawText(label.c_str(), label.size(), 0.0f, 0.0f, labelPaint);
192                        }
193
194                        canvas->restore();
195
196                        gridNum++;
197                    }
198                }
199            }
200        }
201
202
203        // Rotation/scale test
204        {
205            SkScalar xPos = (gridNum % GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
206            SkScalar yPos = (gridNum / GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
207
208            canvas->save();
209            canvas->translate(xPos, yPos);
210            this->drawRect(canvas, 0.6f, 0.6f, 45.0f, true, true, true, true, fLights);
211            canvas->restore();
212
213            gridNum++;
214        }
215
216        // Anisotropic scale test
217        {
218            SkScalar xPos = (gridNum % GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
219            SkScalar yPos = (gridNum / GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
220
221            canvas->save();
222            canvas->translate(xPos, yPos);
223            this->drawRect(canvas, 0.6f, 0.4f, 30.0f, true, true, true, true, fLights);
224            canvas->restore();
225
226            gridNum++;
227        }
228
229        // No directional lights test
230        {
231            SkScalar xPos = (gridNum % GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
232            SkScalar yPos = (gridNum / GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
233
234            canvas->save();
235            canvas->translate(xPos, yPos);
236            this->drawRect(canvas, 1.0f, 1.0f, 0.0f, true, true, false, false, fLightsNoDir);
237            canvas->restore();
238
239            gridNum++;
240        }
241
242        // Two directional lights test
243        {
244            SkScalar xPos = (gridNum % GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
245            SkScalar yPos = (gridNum / GRID_COLUMN_NUM) * GRID_CELL_WIDTH;
246
247            canvas->save();
248            canvas->translate(xPos, yPos);
249            this->drawRect(canvas, 1.0f, 1.0f, 0.0f, true, true, false, false, fLightsTwoDir);
250            canvas->restore();
251
252            gridNum++;
253        }
254    }
255
256private:
257    static const int kTexSize = 96;
258
259    sk_sp<SkShader> fOpaqueDiffuse;
260    sk_sp<SkShader> fTranslucentDiffuse;
261    sk_sp<SkShader> fNormalMapShader;
262
263    SkRect fRect;
264    sk_sp<SkLights> fLights;
265    sk_sp<SkLights> fLightsNoDir;
266    sk_sp<SkLights> fLightsTwoDir;
267
268    typedef GM INHERITED;
269};
270
271//////////////////////////////////////////////////////////////////////////////
272
273DEF_GM(return new LightingShader2GM;)
274}
275