浏览代码

material shadow basic version

/main
xingwei.zhu 6 年前
当前提交
380ecced
共有 8 个文件被更改,包括 380 次插入28 次删除
  1. 38
      Runtime/flow/physical_shape_layer.cs
  2. 40
      Runtime/rendering/proxy_box.cs
  3. 78
      Runtime/ui/geometry.cs
  4. 6
      Runtime/ui/matrix.cs
  5. 8
      Runtime/ui/painting/canvas.cs
  6. 39
      Runtime/ui/painting/path.cs
  7. 24
      Samples/UIWidgetSample/MaterialSample.cs
  8. 175
      Runtime/ui/painting/shadow_utils.cs

38
Runtime/flow/physical_shape_layer.cs


using Unity.UIWidgets.ui;
using UnityEngine;
using Canvas = Unity.UIWidgets.ui.Canvas;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
namespace Unity.UIWidgets.flow {
public class PhysicalShapeLayer : ContainerLayer {

}
else {
Rect bounds = this._path.getBounds();
//todo xingwei.zhu: outter set shadow
//bounds.outset(20.0f, 20.0f);
this.paintBounds = bounds;
Rect outset = bounds.outset(20.0f, 20.0f);
this.paintBounds = outset;
this.drawShadow(context.canvas, this._path, this._shadow_color, this._elevation,
drawShadow(context.canvas, this._path, this._shadow_color, this._elevation,
this._color.alpha != 255, this._device_pixel_ratio);
}

}
void drawShadow(Canvas canvas, Path path, Color color, float elevation, bool transparentOccluder, float dpr) {
//todo xingwei.zhu: to be implemented
public static void drawShadow(Canvas canvas, Path path, Color color, float elevation, bool transparentOccluder,
float dpr) {
float kAmbientAlpha = 0.039f;
float kSpotAlpha = 0.25f;
float kLightHeight = 600f;
float kLightRadius = 800f;
Rect bounds = path.getBounds();
float shadow_x = (bounds.left + bounds.right) / 2f;
float shadow_y = bounds.top - 600.0f;
Color inAmbient = color.withAlpha((int) (kAmbientAlpha * color.alpha));
Color inSpot = color.withAlpha((int) (kSpotAlpha * color.alpha));
Color ambientColor = null;
Color spotColor = null;
ShadowUtils.computeTonalColors(inAmbient, inSpot, ref ambientColor, ref spotColor);
ShadowUtils.drawShadow(
canvas,
path,
new Vector3(0, 0, dpr * elevation),
new Vector3(shadow_x, shadow_y, dpr * kLightHeight),
dpr * kLightRadius,
ambientColor,
spotColor,
0
);
}
}
}

40
Runtime/rendering/proxy_box.cs


Color _color;
static Paint _transparentPaint {
protected static Paint _transparentPaint {
get { return new Paint {color = new Color(0x00000000)}; }
}

return base.hitTest(result, position: position);
}
//todo:xingwei.zhu: implementation shadow
public override void paint(PaintingContext context, Offset offset) {
if (this.child != null) {
this._updateClip();

else {
Canvas canvas = context.canvas;
if (this.elevation != 0.0) {
//draw Shadow
/*canvas.drawRect(
offsetBounds.inflate(20.0),
_RenderPhysicalModelBase<RRect>._transparentPaint
canvas.drawRect(
offsetBounds.inflate(20.0f),
_transparentPaint
);
canvas.drawShadow(
offsetRRectAsPath,

);*/
);
}
Paint paint = new Paint {color = this.color};

return base.hitTest(result, position: position);
}
//todo:xingwei.zhu: implementation shadow
public override void paint(PaintingContext context, Offset offset) {
if (this.child != null) {
this._updateClip();

}
else {
Canvas canvas = context.canvas;
// if (this.elevation != 0.0 && paintShadows) {
// canvas.drawRect(
// offsetBounds.inflate(20.0),
// _RenderPhysicalModelBase<Path>._transparentPaint
// );
// canvas.drawShadow(
// offsetPath,
// this.shadowColor,
// this.elevation,
// this.color.alpha != 0xFF,
// );
// }
if (this.elevation != 0.0) {
canvas.drawRect(
offsetBounds.inflate(20.0f),
_transparentPaint
);
canvas.drawShadow(
offsetPath,
this.shadowColor,
this.elevation,
this.color.alpha != 0xFF
);
}
Paint paint = new Paint {color = this.color, style = PaintingStyle.fill};
canvas.drawPath(offsetPath, paint);
context.clipPathAndPaint(offsetPath, this.clipBehavior,

78
Runtime/ui/geometry.cs


namespace Unity.UIWidgets.ui {
public static class MathUtils {
const float _valueNearlyZero = 1f / (1 << 12);
public static bool isConvexPolygon(Offset[] polygonVerts, int polygonSize) {
if (polygonSize < 3) {
return false;
}
float lastArea = 0;
float lastPerpDot = 0;
int prevIndex = polygonSize - 1;
int currIndex = 0;
int nextIndex = 1;
Offset origin = polygonVerts[0];
Vector2 v0 = (polygonVerts[currIndex] - polygonVerts[prevIndex]).toVector();
Vector2 v1 = (polygonVerts[nextIndex] - polygonVerts[currIndex]).toVector();
Vector2 w0 = (polygonVerts[currIndex] - origin).toVector();
Vector2 w1 = (polygonVerts[nextIndex] - origin).toVector();
for (int i = 0; i < polygonSize; i++) {
if (!polygonVerts[i].isFinite) {
return false;
}
float perpDot = v0.cross(v1);
if (lastPerpDot * perpDot < 0) {
return false;
}
if (0 != perpDot) {
lastPerpDot = perpDot;
}
float quadArea = w0.cross(w1);
if (quadArea * lastArea < 0) {
return false;
}
if (0 != quadArea) {
lastArea = quadArea;
}
prevIndex = currIndex;
currIndex = nextIndex;
nextIndex = (currIndex + 1) % polygonSize;
v0 = v1;
v1 = (polygonVerts[nextIndex] - polygonVerts[currIndex]).toVector();
w0 = w1;
w1 = (polygonVerts[nextIndex] - origin).toVector();
}
return true;
}
public static float cross(this Vector2 vector1, Vector2 vector2) {
return Vector3.Cross(new Vector3(vector1.x, vector1.y, 0f), new Vector3(vector2.x, vector2.y, 0f)).z;
}
public static bool valueNearlyZero(this float x, float? tolerance = null) {
tolerance = tolerance ?? _valueNearlyZero;
return Mathf.Abs(x) <= tolerance;
}
public static float clamp(this float value, float min, float max) {
if (value < min) {
value = min;

this.left * scaleX, this.top * scaleY.Value,
this.right * scaleX, this.bottom * scaleY.Value);
}
public Rect outset(float dx, float dy) {
return new Rect(this.left - dx, this.top - dy, this.right + dx, this.bottom + dy);
}
public Offset[] toQuad() {
Offset[] dst = new Offset[4];
dst[0] = new Offset(this.left, this.top);
dst[1] = new Offset(this.right, this.top);
dst[2] = new Offset(this.right, this.bottom);
dst[3] = new Offset(this.left, this.bottom);
return dst;
}
public Rect inflate(float delta) {
return fromLTRB(this.left - delta, this.top - delta, this.right + delta, this.bottom + delta);

6
Runtime/ui/matrix.cs


).normalize();
}
public Offset[] mapRectToQuad(Rect rect) {
Offset[] dst = rect.toQuad();
this.mapPoints(dst, dst);
return dst;
}
public static bool operator ==(Matrix3 a, Matrix3 b) {
if (ReferenceEquals(a, null) && ReferenceEquals(b, null)) {
return true;

8
Runtime/ui/painting/canvas.cs


using System;
using Unity.UIWidgets.flow;
using UnityEngine;
using Unity.UIWidgets.foundation;

void drawLine(Offset from, Offset to, Paint paint);
void drawRect(Rect rect, Paint paint);
void drawShadow(Path path, Color color, float elevation, bool transparentOccluder);
void drawRRect(RRect rect, Paint paint);

path = path,
paint = new Paint(paint),
});
}
public void drawShadow(Path path, Color color, float elevation, bool transparentOccluder) {
float dpr = Window.instance.devicePixelRatio;
PhysicalShapeLayer.drawShadow(this, path, color, elevation, transparentOccluder, dpr);
}
public void drawRect(Rect rect, Paint paint) {

39
Runtime/ui/painting/path.cs


this._appendCommands(vals.ToArray());
}
public Path transform(Matrix3 mat) {
Path ret = new Path();
var i = 0;
while (i < this._commands.Count) {
var cmd = (PathCommand) this._commands[i];
switch (cmd) {
case PathCommand.moveTo:
var res_move = mat.mapXY(this._commands[i + 1], this._commands[i + 2]);
ret.moveTo(res_move.dx, res_move.dy);
i += 3;
break;
case PathCommand.lineTo:
var res_lineto = mat.mapXY(this._commands[i + 1], this._commands[i + 2]);
ret.lineTo(res_lineto.dx, res_lineto.dy);
i += 3;
break;
case PathCommand.bezierTo:
var res1 = mat.mapXY(this._commands[i + 1], this._commands[i + 2]);
var res2 = mat.mapXY(this._commands[i + 3], this._commands[i + 4]);
var res3 = mat.mapXY(this._commands[i + 5], this._commands[i + 6]);
ret.bezierTo(res1.dx, res1.dy, res2.dx, res2.dy, res3.dx, res3.dy);
i += 7;
break;
case PathCommand.close:
i++;
break;
case PathCommand.winding:
i += 2;
break;
default:
D.assert(false, "unknown cmd: " + cmd);
break;
}
}
return ret;
}
void _transformCommands(List<float> commands, Matrix3 mat) {
if (mat == null) {
return;

24
Samples/UIWidgetSample/MaterialSample.cs


namespace UIWidgetsSample {
public class MaterialSample : UIWidgetsSamplePanel {
int testCaseId = 4;
int testCaseId = 2;
List<Widget> testCases = new List<Widget> {
new MaterialButtonWidget(),

new TableWidget()
new TableWidget(),
new BottomAppBarWidget()
};
protected override Widget createWidget() {

protected override void OnEnable() {
base.OnEnable();
FontManager.instance.addFont(Resources.Load<Font>(path: "MaterialIcons-Regular"));
}
}
class BottomAppBarWidget : StatelessWidget {
public BottomAppBarWidget(Key key = null) : base(key) {
}
public override Widget build(BuildContext context) {
return new Scaffold(
backgroundColor: Color.clear,
bottomNavigationBar: new BottomAppBar(
child: new Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: new List<Widget> {
new IconButton(icon: new Icon(Unity.UIWidgets.material.Icons.menu), onPressed: () => { }),
new IconButton(icon: new Icon(Unity.UIWidgets.material.Icons.account_balance), onPressed: () => { })
})));
}
}

175
Runtime/ui/painting/shadow_utils.cs


using UnityEngine;
namespace Unity.UIWidgets.ui {
public static class ShadowUtils {
public const float kAmbientHeightFactor = 1.0f / 128.0f;
public const float kAmbientGeomFactor = 64.0f;
public const float kBlurSigmaScale = 0.57735f;
public const float kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
public static float divideAndPin(float numer, float denom, float min, float max) {
return (numer / denom).clamp(min, max);
}
public static float ambientBlurRadius(float height) {
return Mathf.Min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
}
public static float ambientRecipAlpha(float height) {
return 1.0f + Mathf.Max(height * kAmbientHeightFactor, 0.0f);
}
public static float spotBlurRadius(float occluderZ, float lightZ, float lightRadius) {
return lightRadius * divideAndPin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
}
public static void getSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
float lightRadius,
ref float blurRadius, ref float scale, ref Vector2 translate) {
float zRatio = divideAndPin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
blurRadius = lightRadius * zRatio;
scale = divideAndPin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
translate = new Vector2(-zRatio * lightX, -zRatio * lightY);
}
public static float convertRadiusToSigma(float radius) {
return radius > 0 ? kBlurSigmaScale * radius + 0.5f : 0.0f;
}
public static void computeTonalColors(Color inAmbientColor, Color inSpotColor,
ref Color outAmbientColor, ref Color outSpotColor) {
outAmbientColor = Color.fromARGB(inAmbientColor.alpha, 0, 0, 0);
outSpotColor = inSpotColor;
}
public static bool getSpotShadowTransform(Vector3 lightPos, float lightRadius, Matrix3 ctm,
Vector3 zPlaneParams, Rect pathBounds, Matrix3 shadowTransform, ref float radius) {
float heightFunc(float x, float y) {
return zPlaneParams.x * x + zPlaneParams.y * y + zPlaneParams.z;
}
float occluderHeight = heightFunc(pathBounds.center.dx, pathBounds.center.dy);
if (!ctm.hasPerspective()) {
float scale = 0.0f;
Vector2 translate = new Vector2();
getSpotParams(occluderHeight, lightPos.x, lightPos.y, lightPos.z, lightRadius, ref radius, ref scale,
ref translate);
shadowTransform.setScaleTranslate(scale, scale, translate.x, translate.y);
shadowTransform.preConcat(ctm);
}
else {
if (pathBounds.width.valueNearlyZero() || pathBounds.height.valueNearlyZero()) {
return false;
}
Offset[] pts = ctm.mapRectToQuad(pathBounds);
if (!MathUtils.isConvexPolygon(pts, 4)) {
return false;
}
Vector3[] pts3D = new Vector3[4];
float z = heightFunc(pathBounds.left, pathBounds.top);
pts3D[0] = new Vector3(pts[0].dx, pts[0].dy, z);
z = heightFunc(pathBounds.right, pathBounds.top);
pts3D[1] = new Vector3(pts[1].dx, pts[1].dy, z);
z = heightFunc(pathBounds.right, pathBounds.bottom);
pts3D[2] = new Vector3(pts[2].dx, pts[2].dy, z);
z = heightFunc(pathBounds.left, pathBounds.bottom);
pts3D[3] = new Vector3(pts[3].dx, pts[3].dy, z);
for (int i = 0; i < 4; i++) {
float dz = lightPos.z - pts3D[i].z;
if (dz.valueNearlyZero()) {
return false;
}
float zRatio = pts3D[i].z / dz;
pts3D[i].x -= (lightPos.x - pts3D[i].x) * zRatio;
pts3D[i].y -= (lightPos.y - pts3D[i].y) * zRatio;
pts3D[i].z = 1f;
}
Vector3 h0 = Vector3.Cross(Vector3.Cross(pts3D[1], pts3D[0]), Vector3.Cross(pts3D[2], pts3D[3]));
Vector3 h1 = Vector3.Cross(Vector3.Cross(pts3D[0], pts3D[3]), Vector3.Cross(pts3D[1], pts3D[2]));
Vector3 h2 = Vector3.Cross(Vector3.Cross(pts3D[0], pts3D[2]), Vector3.Cross(pts3D[1], pts3D[3]));
if (h2.z.valueNearlyZero()) {
return false;
}
Vector3 v = pts3D[3] - pts3D[0];
Vector3 w = h0 - pts3D[0];
float perpDot = v.x * w.y - v.y * w.x;
if (perpDot > 0) {
h0 = -h0;
}
v = pts3D[1] - pts3D[0];
perpDot = v.x * w.y - v.y * w.x;
if (perpDot < 0) {
h1 = -h1;
}
shadowTransform.setAll(h0.x / h2.z, h1.x / h2.z, h2.x / h2.z,
h0.y / h2.z, h1.y / h2.z, h2.y / h2.z,
h0.z / h2.z, h1.z / h2.z, 1);
Matrix3 toHomogeneous = Matrix3.I();
float xScale = 2.0f / (pathBounds.right - pathBounds.left);
float yScale = 2.0f / (pathBounds.bottom - pathBounds.top);
toHomogeneous.setAll(xScale, 0, -xScale * pathBounds.left - 1,
0, yScale, -yScale * pathBounds.top - 1,
0, 0, 1);
shadowTransform.preConcat(toHomogeneous);
radius = spotBlurRadius(occluderHeight, lightPos.z, lightRadius);
}
return true;
}
public static void drawShadow(Canvas canvas, Path path, Vector3 zPlaneParams, Vector3 devLightPos,
float lightRadius, Color ambientColor, Color spotColor, int flags) {
Matrix3 viewMatrix = canvas.getTotalMatrix();
//ambient light
Path devSpacePath = path.transform(viewMatrix);
float devSpaceOutset = ambientBlurRadius(zPlaneParams.z);
float oneOverA = ambientRecipAlpha(zPlaneParams.z);
float blurRadius = 0.5f * devSpaceOutset * oneOverA;
float strokeWidth = 0.5f * (devSpaceOutset - blurRadius);
Paint paint = new Paint {color = ambientColor, strokeWidth = strokeWidth, style = PaintingStyle.fill};
canvas.save();
canvas.setMatrix(Matrix3.I());
float sigma = convertRadiusToSigma(blurRadius);
paint.maskFilter = MaskFilter.blur(BlurStyle.normal, sigma);
canvas.drawPath(devSpacePath, paint);
canvas.restore();
//spot light
Matrix3 shadowMatrix = Matrix3.I();
float radius = 0.0f;
if (!getSpotShadowTransform(devLightPos, lightRadius, viewMatrix, zPlaneParams, path.getBounds(),
shadowMatrix, ref radius)) {
return;
}
canvas.save();
canvas.setMatrix(shadowMatrix);
Paint paint2 = new Paint {color = spotColor};
float sigma2 = convertRadiusToSigma(radius);
paint2.maskFilter = MaskFilter.blur(BlurStyle.normal, sigma2);
canvas.drawPath(path, paint2);
canvas.restore();
}
}
}
正在加载...
取消
保存