1 #ifndef __DALI_TOOLKIT_SHADER_EFFECT_SOFT_BUTTON_H__
2 #define __DALI_TOOLKIT_SHADER_EFFECT_SOFT_BUTTON_H__
5 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 #include <dali/public-api/shader-effects/shader-effect.h>
31 * @brief Creates a new soft button shader effect
33 * Soft button shader effect works on a per object basis. Using animatable parameters user can create
34 * effect of button pushing in / out. Can be applied to ImageActor only.
38 * // Create shader used for doing soft button\n
39 * ShaderEffect softButtonEffect = CreateSoftButtonEffect();
41 * // set image actor shader to the soft button one\n
42 * ImageActor imageActor = ImageActor::New( ... );\n
43 * imageActor.SetShaderEffect( softButtonEffect );
45 * // animate a button push, using e.g. AlphaFunction::BOUNCE. With these values the button pushes in and pops out slightly at the end\n
46 * Animation animation = Animation::New( ... );\n
47 * animation.AnimateTo( Property(softButtonEffect, "uLightingIndentationAmount), 0.25f, AlphaFunction::BOUNCE, ... );\n
48 * animation.AnimateTo( Property(softButtonEffect, "uLightingIndentationAmount"), -0.05f, AlphaFunction::BOUNCE, ... );\n
49 * animation.AnimateTo( Property(softButtonEffect, "uTextureDistortAmount"), 0.25f, AlphaFunction::BOUNCE, ... );\n
50 * animation.AnimateTo( Property(softButtonEffect, "uTextureDistortAmount"), -0.05f, AlphaFunction::BOUNCE, ... );\n
53 * Animatable/Constrainable uniforms:
55 * "uLightingIndentationAmount" - This property changes the lighting, to make it look like the button is pushed in. User should animate
56 * this in conjunction with texture distortion. Allowable values range from [-1..1], higher values give
57 * more change in lighting. Default 0.0 (no lighting change).
58 * "uTextureDistortAmount" - This property changes the distortion, to make it look like the button is pushed in. User should animate
59 * this in conjunction with lighting indentation. Allowable values range from [-1..1) - note 1.0 is NOT
60 * allowed - higher values give more distortion. Default 0.0 (no distortion).
61 * "uAmbientLight" - The ambient light is used in the lighting calculation. Care must be taken to not saturate the image by
62 * setting this value too high, or the indentation will not look correct. Default 0.15
63 * "uDiffuseLight" - The diffuse light is used in the lighting calculation. Default is (0.0, 0.7070168, 0.7070168),
64 * i.e. a light angled at the surface from in front and above. Note that you need to Normalize()
65 * the Vector3 that you set with this property
66 * "uLightMultiplier" - The ambient and diffuse lighting is multiplied by this factor. Since a diffuse light at an angle will
67 * cause the whole image to darken, even outside the soft button indentation, this property can be used
68 * to scale the image back up closer to the pixel values of the original diffuse texture. Care must be
69 * taken to not saturate the image,or the indentation will not look correct. Default 1.2.
70 * "uInsideCircleSizeScale" - The SoftButtonEffect consists of two shapes, one inside the other. The outside shape fits exactly to
71 * the actor, touching its edges but completely contained. The inside shape size is given by a multiplier
72 * of the outside shape size. For example a value of 0.5 means that the inside shape is half the size of
73 * the outside one. Allowable values are in the range (0.0 - 1.0), note that 0.0 and 1.0 themselves are
74 * not allowed. Default 0.75.
75 * "uOutsideCircleDepth" - The SoftButtonEffect consists of two shapes, one inside the other. The depth of the indentation at the
76 * transition between the inside and outside shapes is controlled by this property. The values lies in the
77 * range [0.0 - 1.0]. A value of 0.0 means the outside shape has no depth (and is thus invisible), value of
78 * 1.0 means the outside shape has maximum depth (and the inside shape is thus invisible). Default 0.05
79 * "uEffectRegion" - The soft button effect is applied within the supplied rect region of the texture. Default values for this
80 * is (0.0, 0.0, 1.0, 1.0) which is the entire image with 0,0 being the top left and 1.0, 1.0 being the bottom
81 * right. If the image texture is split between multiple ImageActors then the developer should specify the pixel
82 * area of the texture the effect should be applied with. Example, If the Image is split among two ImageActors
83 * side by side, with the left one using left half of the texture and right one using the right half of the
84 * texture then the pixel area value for the left ImageActor will be (0.0, 0.0, 0.5, 1.0) and the pixel area for
85 * the right will be (0.5,0.0,1.0,1.0).
86 * "uRectangleSizeScale" - This property can be used to set the mix between proportion of rectangle and proportion of ellipse - the
87 * result is a rectangle with rounded corners. If the value is 0.0, the shape is an ellipse. If the value is
88 * close to 1.0, the shape is close to a rectangle. The value lies in the range [0.0 - 1.0). Note that a value
89 * of 1.0 is NOT allowed.Default 0.5.
91 * @param type The type of the soft button, can be either ELLIPTICAL, RECTANGULAR, or FIXED.
92 * @return A handle to a newly allocated ShaderEffect
96 SOFT_BUTTON_ELLIPTICAL = 0, /// Button is elliptical
97 SOFT_BUTTON_RECTANGULAR, /// Button is rectangular
98 SOFT_BUTTON_FIXED /// Button does not indent (move). Useful for matching lighting between areas that do not indent (which can thus use a cheaper shader) and those that do indent.
99 }SoftButtonEffectType;
101 static void InverseConstraint( float& current, const PropertyInputContainer& inputs )
103 current = 1.0f / inputs[0]->GetFloat();
106 inline ShaderEffect CreateSoftButtonEffect(SoftButtonEffectType type)
108 std::string vertexSource;
109 vertexSource = "precision mediump float;\n"
110 "uniform vec3 uDiffuseLight;\n"
111 "uniform float uAmbientLight;\n"
112 "uniform float uLightMultiplier;\n"
113 "uniform vec4 uEffectRegion;\n"
114 "varying vec2 vCentredCoord;\n"
116 "const vec3 norm = vec3(0.0, 0.0, 1.0);\n"
120 " vTexCoord = aTexCoord;\n"
121 // Get the rect coords of the effect region in -1..1 range, i.e. circle centred around the center of the rect
122 // Done in the vertex shader itself to make use of gl interpolation for varying.
123 " vCentredCoord = vec2( ( (vTexCoord.x - uEffectRegion.x)/(uEffectRegion.z - uEffectRegion.x) * 2.0 - 1.0 ), ( (vTexCoord.y - uEffectRegion.y)/(uEffectRegion.w - uEffectRegion.y) * 2.0 - 1.0 ) );\n"
124 " gl_Position = uMvpMatrix * vec4(aPosition, 1.0);\n"
127 std::string fragmentSourceFixed;
128 fragmentSourceFixed = "precision mediump float;\n"
130 "uniform vec3 uDiffuseLight;\n"
131 "uniform float uAmbientLight;\n"
132 "uniform float uLightMultiplier;\n"
133 "varying vec2 vCentredCoord;\n"
135 "const vec3 norm = vec3(0.0, 0.0, 1.0);\n"
139 " vec4 col = texture2D(sTexture, vTexCoord);\n"
141 " float lighting = (dot(uDiffuseLight, norm) + uAmbientLight) * uLightMultiplier;\n"
142 // output col = image * light
143 // use the lighting value for colors only
144 " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n"
147 std::string fragmentSourceElliptical;
148 fragmentSourceElliptical = "precision mediump float;\n"
150 "uniform float uLightingIndentationAmount;\n"
151 "uniform float uTextureDistortAmount;\n"
152 "uniform vec3 uDiffuseLight;\n"
153 "uniform float uAmbientLight;\n"
154 "uniform float uLightMultiplier;\n"
155 "uniform float uInsideCircleSizeScale;\n"
156 "uniform float uRecipInsideCircleSizeScale;\n"
157 "uniform float uOutsideCircleDepth;\n"
158 "uniform vec4 uEffectRegion;\n"
159 "varying vec2 vCentredCoord;\n"
161 "const float PI = 3.1415927;\n"
165 // Apply distortion only if the pixel is within the rect specified
166 "if( (vTexCoord.x > uEffectRegion.x) && (vTexCoord.x < uEffectRegion.z) && (vTexCoord.y > uEffectRegion.y) && (vTexCoord.y < uEffectRegion.w) )\n"
168 " vec2 coord = vCentredCoord;\n"
170 // find a coordinate representing distance from circle centre, such that we split into inside / outside circles that can have different gradients / normals
171 " float realDistFromCentre = length(coord);\n"
172 " realDistFromCentre = min(1.0, realDistFromCentre);\n" // clamp corners of square to vertical normal
173 " float distFromCentre;\n"
174 " if(realDistFromCentre <= uInsideCircleSizeScale)\n"
176 " distFromCentre = realDistFromCentre * uRecipInsideCircleSizeScale * (1.0 - uOutsideCircleDepth);\n" // inside circle indent, up to outline depth
180 " distFromCentre = mix(1.0 - uOutsideCircleDepth, 1.0, (realDistFromCentre - ( uInsideCircleSizeScale)) / (1.0 - uInsideCircleSizeScale));\n" // outside circle
183 // get coords in -PI..PI range, i.e. scale the circle for use by trig functions
186 // get a z value for the distorted surface in 0..1 range, using cos for a smooth curve (note, we ignore inside / outside circles since the difference isn't noticeable visually)
187 " vec2 cosThetaCoord = (cos(coord) * 0.5) + 0.5;\n"
188 " float z = cosThetaCoord.x * cosThetaCoord.y;\n"
190 // get the normal for the distorted surface, using the fact that the derivative of cos is -sin, finding tangent vector from slope and then normal by cross product...
191 " float sinThetaCoord = sin(distFromCentre*PI) * uLightingIndentationAmount;\n" // slope, so tangent vec is (1.0, -sin)
192 // ...2D normal vector along distFromCentre vec is (sin, 1.0), convert to components in 3D.
193 " vec3 norm = normalize(vec3(coord.x * sinThetaCoord, coord.y * sinThetaCoord, 1.0));\n"
195 // form surface z and project texture onto it.
196 " float indentAmount = 1.0 / (1.0 - (z * uTextureDistortAmount));\n"
197 " vec2 distortedCoord = vCentredCoord * indentAmount;\n"
199 // Convert the rect coordinates in -1 to 1 range back to the original coordinates
200 " vec2 texCoord = vec2( ( (distortedCoord.x + 1.0)*(0.5) * (uEffectRegion.z - uEffectRegion.x) + uEffectRegion.x ), ( (distortedCoord.y + 1.0)*(0.5) * (uEffectRegion.w - uEffectRegion.y) + uEffectRegion.y ) ); \n"
201 " vec4 col = texture2D(sTexture, texCoord);\n"
204 " float lighting = (dot(uDiffuseLight, norm) + uAmbientLight) * uLightMultiplier;\n"
205 " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n"
209 " vec4 col = texture2D(sTexture, vTexCoord);\n"
210 " float lighting = (dot(uDiffuseLight, vec3(0.0, 0.0, 1.0)) + uAmbientLight) * uLightMultiplier;\n"
211 " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n"
215 std::string fragmentSourceRectangular;
216 fragmentSourceRectangular = "precision mediump float;\n"
218 "uniform float uLightingIndentationAmount;\n"
219 "uniform float uTextureDistortAmount;\n"
220 "uniform vec3 uDiffuseLight;\n"
221 "uniform float uAmbientLight;\n"
222 "uniform float uLightMultiplier;\n"
223 "uniform float uInsideCircleSizeScale;\n"
224 "uniform float uRecipInsideCircleSizeScale;\n"
225 "uniform float uOutsideCircleDepth;\n"
226 "uniform float uRectangleSizeScale;\n"
227 "uniform vec4 uEffectRegion;\n"
228 "varying vec2 vCentredCoord;\n"
230 "const float PI = 3.1415927;\n"
234 // Apply distortion only if the pixel is within the rect specified
235 "if( (vTexCoord.x > uEffectRegion.x) && (vTexCoord.x < uEffectRegion.z) && (vTexCoord.y > uEffectRegion.y) && (vTexCoord.y < uEffectRegion.w) )\n"
237 // get the rect coords to -1..1 range, i.e. circle centred around the center of the rect
238 " vec2 centredCoord = vCentredCoord;\n"
239 // clamp coords such that the circle is split into 4 pieces that lie in the corners of the actor. uRectangleScale is the distance along each axis from the centre
240 // of the actor, e.g. 0.5 is half way along an axis from centre to actor edge.
241 " vec2 clampedCoord;\n"
242 " if(centredCoord.x > 0.0)\n"
244 " if(centredCoord.x < uRectangleSizeScale)\n"
246 // we are in a rectangular region along this axis, clamp coord to be same as centre pixel
247 " clampedCoord.x = 0.0;\n"
251 // we are outside rectangular region along this axis, so we want curvature.
252 " clampedCoord.x = smoothstep(0.0, 1.0, (centredCoord.x - uRectangleSizeScale) / (1.0 - uRectangleSizeScale));\n"
257 " if(centredCoord.x > -uRectangleSizeScale)\n"
259 // we are in a rectangular region along this axis, clamp coord to be same as centre pixel
260 " clampedCoord.x = 0.0;\n"
264 // we are outside rectangular region along this axis, so we want curvature.
265 " clampedCoord.x = -smoothstep(0.0, 1.0, (centredCoord.x + uRectangleSizeScale) / (uRectangleSizeScale - 1.0));\n"
268 " if(centredCoord.y > 0.0)\n"
270 " if(centredCoord.y < uRectangleSizeScale)\n"
272 // we are in a rectangular region along this axis, clamp coord to be same as centre pixel
273 " clampedCoord.y = 0.0;\n"
277 // we are outside rectangular region along this axis, so we want curvature.
278 " clampedCoord.y = smoothstep(0.0, 1.0, (centredCoord.y - uRectangleSizeScale) / (1.0 - uRectangleSizeScale));\n"
283 " if(centredCoord.y > -uRectangleSizeScale)\n"
285 // we are in a rectangular region along this axis, clamp coord to be same as centre pixel
286 " clampedCoord.y = 0.0;\n"
290 // we are outside rectangular region along this axis, so we want curvature.
291 " clampedCoord.y = -smoothstep(0.0, 1.0, (centredCoord.y + uRectangleSizeScale) / (uRectangleSizeScale - 1.0));\n"
294 // get coords in -PI..PI range, i.e. scale above circle for use by trig functions
295 " vec2 thetaCoord = clampedCoord * PI;\n"
296 // get a z value for the distorted surface in 0..1 range, using cos for a smooth curve (note, we ignore inside / outside circles since the difference isn't noticeable visually)
297 " vec2 cosThetaCoord = (cos(thetaCoord) * 0.5) + 0.5;\n"
298 " float z = cosThetaCoord.x * cosThetaCoord.y;\n"
299 // find a coordinate representing distance from circle centre, such that we split into inside / outside circles that can have different gradients / normals
300 " float realDistFromCentre = length(thetaCoord);\n"
301 " realDistFromCentre = min(PI, realDistFromCentre);\n" // clamp corners of square to vertical normal
302 " float distFromCentre;\n"
303 " if(realDistFromCentre <= PI * uInsideCircleSizeScale)\n"
305 " distFromCentre = realDistFromCentre * uRecipInsideCircleSizeScale * (PI - (uOutsideCircleDepth * PI)) / PI;\n" // inside circle indent, up to outline depth
309 " distFromCentre = mix(PI - (uOutsideCircleDepth * PI), PI, (realDistFromCentre - ( PI * uInsideCircleSizeScale)) / (PI - (PI * uInsideCircleSizeScale)));\n" // outside circle
311 // get the normal for the distorted surface, using the fact that the derivative of cos is -sin, finding tangent vector from slope and then normal by cross product...
312 " float sinThetaCoord = sin(distFromCentre) * uLightingIndentationAmount;\n" // slope, so tangent vec is (1.0, -sin)
313 // ...2D normal vector along distFromCentre vec is (sin, 1.0), convert to components in 3D.
314 " vec3 norm = normalize(vec3(thetaCoord.x * sinThetaCoord, thetaCoord.y * sinThetaCoord, 1.0));\n"
315 // form surface z and project texture onto it.
316 " float indentAmount = 1.0 / (1.0 - (z * uTextureDistortAmount));\n"
317 " vec2 distortedCoord = centredCoord * indentAmount;\n"
318 // Convert the rect coordinates in -1 to 1 range back to the original coordinates
319 " vec2 texCoord = vec2( ( (distortedCoord.x + 1.0)/(2.0) * (uEffectRegion.z - uEffectRegion.x) + uEffectRegion.x ), ( (distortedCoord.y + 1.0)/(2.0) * (uEffectRegion.w - uEffectRegion.y) + uEffectRegion.y ) );\n"
320 " vec4 col = texture2D(sTexture, texCoord);\n"
322 " float lighting = (dot(uDiffuseLight, norm) + uAmbientLight) * uLightMultiplier;\n"
323 // output col = image * light
324 // use the lighting value for colors only
325 " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n"
330 " vec4 col = texture2D(sTexture, vTexCoord);\n"
331 " float lighting = (dot(uDiffuseLight, vec3(0.0, 0.0, 1.0)) + uAmbientLight) * uLightMultiplier;\n"
332 " gl_FragColor = vec4(col.rgb * uColor.rgb * lighting, col.a * uColor.a);\n"
337 //////////////////////////////////////
338 // Create shader effectCreateSoftButtonEffect
345 case SOFT_BUTTON_RECTANGULAR:
346 shader = ShaderEffect::New( vertexSource, fragmentSourceRectangular, ShaderEffect::GeometryHints( ShaderEffect::HINT_NONE ));
349 case SOFT_BUTTON_ELLIPTICAL:
350 shader = ShaderEffect::New( vertexSource, fragmentSourceElliptical, ShaderEffect::GeometryHints( ShaderEffect::HINT_NONE ));
353 case SOFT_BUTTON_FIXED:
355 shader = ShaderEffect::New( vertexSource, fragmentSourceFixed, ShaderEffect::GeometryHints( ShaderEffect::HINT_NONE ));
359 //////////////////////////////////////
360 // Register uniform properties
363 static const float SOFT_BUTTON_LIGHTING_INDENTATION_AMOUNT_DEFAULT = 0.0f;
364 static const float SOFT_BUTTON_TEXTURE_DISTORTION_AMOUNT_DEFAULT = 0.0f;
365 static const float SOFT_BUTTON_AMBIENT_LIGHT_AMOUNT_DEFAULT = 0.15f;
366 static const Vector3 SOFT_BUTTON_DIFFUSE_LIGHT_DEFAULT = Vector3(0.0f, 0.7070168f, 0.7071068f);
367 static const float SOFT_BUTTON_LIGHTING_MULTIPLIER_DEFAULT = 1.2f;
368 static const float SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_DEFAULT = 0.75f;
369 static const float SOFT_BUTTON_OUTSIDE_SHAPE_DEPTH_DEFAULT = Math::PI * 0.05f;
370 static const Vector4 SOFT_BUTTON_EFFECT_PIXEL_AREA_DEFAULT = Vector4(0.0f, 0.0f, 1.0f, 1.0f);
371 static const float SOFT_BUTTON_RECTANGLE_SIZE_SCALE_DEFAULT = 0.5f;
373 // factors that scale the look, defaults
374 shader.SetUniform("uAmbientLight", SOFT_BUTTON_AMBIENT_LIGHT_AMOUNT_DEFAULT);
375 shader.SetUniform("uDiffuseLight", SOFT_BUTTON_DIFFUSE_LIGHT_DEFAULT);
376 shader.SetUniform("uLightMultiplier", SOFT_BUTTON_LIGHTING_MULTIPLIER_DEFAULT);
377 if(SOFT_BUTTON_FIXED != type)
379 shader.SetUniform("uLightingIndentationAmount", SOFT_BUTTON_LIGHTING_INDENTATION_AMOUNT_DEFAULT);
380 shader.SetUniform("uTextureDistortAmount", SOFT_BUTTON_TEXTURE_DISTORTION_AMOUNT_DEFAULT);
381 shader.SetUniform("uInsideCircleSizeScale", SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_DEFAULT);
382 shader.SetUniform("uRecipInsideCircleSizeScale", 1.0f / SOFT_BUTTON_INSIDE_SHAPE_SIZE_SCALE_DEFAULT);
383 shader.SetUniform("uOutsideCircleDepth", SOFT_BUTTON_OUTSIDE_SHAPE_DEPTH_DEFAULT);
384 shader.SetUniform("uEffectRegion", SOFT_BUTTON_EFFECT_PIXEL_AREA_DEFAULT);
385 if(SOFT_BUTTON_RECTANGULAR == type)
387 shader.SetUniform("uRectangleSizeScale", SOFT_BUTTON_RECTANGLE_SIZE_SCALE_DEFAULT);
390 // precalc 1.0 / uInsideCircleSizeScale on CPU to save shader insns, using constraint to tie to the normal property
391 Dali::Property::Index insideCircleSizeScalePropertyIndex = shader.GetPropertyIndex("uInsideCircleSizeScale");
392 Dali::Property::Index recipInsideCircleSizeScalePropertyIndex = shader.GetPropertyIndex("uRecipInsideCircleSizeScale");
393 Constraint constraint = Constraint::New<float>( shader, recipInsideCircleSizeScalePropertyIndex, InverseConstraint );
394 constraint.AddSource( LocalSource(insideCircleSizeScalePropertyIndex) );
405 #endif //#ifndef __DALI_TOOLKIT_SHADER_EFFECT_SOFT_BUTTON_H__