diff options
author | ztenghui <ztenghui@google.com> | 2014-08-22 21:04:48 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-08-22 21:04:49 +0000 |
commit | 190ead730fc3169a7b355f23d4cd3d8de8ad8fc3 (patch) | |
tree | efe7733186eb3e8077814a21b30e0c741127305e /libs/hwui | |
parent | 5d04fee90063f333895e2d9521f1fd3739d8a3bc (diff) | |
parent | c50a03d78aaedd0003377e98710e7038bda330e9 (diff) | |
download | frameworks_base-190ead730fc3169a7b355f23d4cd3d8de8ad8fc3.zip frameworks_base-190ead730fc3169a7b355f23d4cd3d8de8ad8fc3.tar.gz frameworks_base-190ead730fc3169a7b355f23d4cd3d8de8ad8fc3.tar.bz2 |
Merge "Improve the spot shadow computation." into lmp-dev
Diffstat (limited to 'libs/hwui')
-rw-r--r-- | libs/hwui/Interpolator.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/ShadowTessellator.cpp | 25 | ||||
-rw-r--r-- | libs/hwui/ShadowTessellator.h | 3 | ||||
-rw-r--r-- | libs/hwui/SpotShadow.cpp | 244 | ||||
-rw-r--r-- | libs/hwui/SpotShadow.h | 18 | ||||
-rw-r--r-- | libs/hwui/TessellationCache.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/utils/MathUtils.h | 6 |
7 files changed, 266 insertions, 34 deletions
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp index fc0e8a0..ff8ff73 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -110,7 +110,7 @@ float LUTInterpolator::interpolate(float input) { weight = modff(lutpos, &ipart); int i1 = (int) ipart; - int i2 = MathUtils::min(i1 + 1, mSize - 1); + int i2 = MathUtils::min(i1 + 1, (int) mSize - 1); LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!" " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f", diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index e71439d..6cff815 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -29,11 +29,6 @@ namespace android { namespace uirenderer { -template<typename T> -static inline T max(T a, T b) { - return a > b ? a : b; -} - void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, const Vector3* casterPolygon, int casterVertexCount, const Vector3& centroid3d, const Rect& casterBounds, @@ -66,7 +61,7 @@ void ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, } void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, - const Vector3* casterPolygon, int casterVertexCount, + const Vector3* casterPolygon, int casterVertexCount, const Vector3& casterCentroid, const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius, const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer) { ATRACE_CALL(); @@ -109,9 +104,9 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, return; } - SpotShadow::createSpotShadow(isCasterOpaque, - casterPolygon, casterVertexCount, adjustedLightCenter, lightRadius, - lightVertexCount, shadowVertexBuffer); + SpotShadow::createSpotShadow(isCasterOpaque, adjustedLightCenter, lightRadius, + casterPolygon, casterVertexCount, casterCentroid, shadowVertexBuffer); + #if DEBUG_SHADOW if(shadowVertexBuffer.getVertexCount() <= 0) { ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount()); @@ -180,6 +175,18 @@ Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { return centroid; } +// Make sure p1 -> p2 is going CW around the poly. +Vector2 ShadowTessellator::calculateNormal(const Vector2& p1, const Vector2& p2) { + Vector2 result = p2 - p1; + if (result.x != 0 || result.y != 0) { + result.normalize(); + // Calculate the normal , which is CCW 90 rotate to the delta. + float tempy = result.y; + result.y = result.x; + result.x = -tempy; + } + return result; +} /** * Test whether the polygon is order in clockwise. * diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h index cb65df5..141dff6 100644 --- a/libs/hwui/ShadowTessellator.h +++ b/libs/hwui/ShadowTessellator.h @@ -72,7 +72,7 @@ public: const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer); static void tessellateSpotShadow(bool isCasterOpaque, - const Vector3* casterPolygon, int casterVertexCount, + const Vector3* casterPolygon, int casterVertexCount, const Vector3& casterCentroid, const mat4& receiverTransform, const Vector3& lightCenter, int lightRadius, const Rect& casterBounds, const Rect& localClip, VertexBuffer& shadowVertexBuffer); @@ -82,6 +82,7 @@ public: static bool isClockwise(const Vector2* polygon, int len); + static Vector2 calculateNormal(const Vector2& p1, const Vector2& p2); /** * Determine whether the path is clockwise, using the control points. * diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index d726538..cb20a0b 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -17,6 +17,9 @@ #define LOG_TAG "OpenGLRenderer" #define SHADOW_SHRINK_SCALE 0.1f +#define CASTER_Z_CAP_RATIO 0.95f +#define FAKE_UMBRA_SIZE_RATIO 0.01f +#define OCLLUDED_UMBRA_SHRINK_FACTOR 0.95f #include <math.h> #include <stdlib.h> @@ -25,13 +28,29 @@ #include "ShadowTessellator.h" #include "SpotShadow.h" #include "Vertex.h" +#include "utils/MathUtils.h" +// TODO: After we settle down the new algorithm, we can remove the old one and +// its utility functions. +// Right now, we still need to keep it for comparison purpose and future expansion. namespace android { namespace uirenderer { static const double EPSILON = 1e-7; /** + * For each polygon's vertex, the light center will project it to the receiver + * as one of the outline vertex. + * For each outline vertex, we need to store the position and normal. + * Normal here is defined against the edge by the current vertex and the next vertex. + */ +struct OutlineData { + Vector2 position; + Vector2 normal; + float radius; +}; + +/** * Calculate the angle between and x and a y coordinate. * The atan2 range from -PI to PI. */ @@ -500,12 +519,13 @@ void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, * empty strip if error. * */ -void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly, + +void SpotShadow::createSpotShadow_old(bool isCasterOpaque, const Vector3* poly, int polyLength, const Vector3& lightCenter, float lightSize, int lightVertexCount, VertexBuffer& retStrips) { Vector3 light[lightVertexCount * 3]; computeLightPolygon(lightVertexCount, lightCenter, lightSize, light); - computeSpotShadow(isCasterOpaque, light, lightVertexCount, lightCenter, poly, + computeSpotShadow_old(isCasterOpaque, light, lightVertexCount, lightCenter, poly, polyLength, retStrips); } @@ -519,9 +539,9 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3* poly, * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return * empty strip if error. */ -void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly, - int lightPolyLength, const Vector3& lightCenter, const Vector3* poly, - int polyLength, VertexBuffer& shadowTriangleStrip) { +void SpotShadow::computeSpotShadow_old(bool isCasterOpaque, const Vector3* lightPoly, + int lightPolyLength, const Vector3& lightCenter, const Vector3* poly, int polyLength, + VertexBuffer& shadowTriangleStrip) { // Point clouds for all the shadowed vertices Vector2 shadowRegion[lightPolyLength * polyLength]; // Shadow polygon from one point light. @@ -616,10 +636,198 @@ void SpotShadow::computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly umbraLength = polyLength; } - generateTriangleStrip(isCasterOpaque, penumbra, penumbraLength, umbra, + generateTriangleStrip(isCasterOpaque, 1.0, penumbra, penumbraLength, umbra, umbraLength, poly, polyLength, shadowTriangleStrip); } +float SpotShadow::projectCasterToOutline(Vector2& outline, + const Vector3& lightCenter, const Vector3& polyVertex) { + float lightToPolyZ = lightCenter.z - polyVertex.z; + float ratioZ = CASTER_Z_CAP_RATIO; + if (lightToPolyZ != 0) { + // If any caster's vertex is almost above the light, we just keep it as 95% + // of the height of the light. + ratioZ = MathUtils::min(polyVertex.z / lightToPolyZ, CASTER_Z_CAP_RATIO); + } + + outline.x = polyVertex.x - ratioZ * (lightCenter.x - polyVertex.x); + outline.y = polyVertex.y - ratioZ * (lightCenter.y - polyVertex.y); + return ratioZ; +} + +/** + * Generate the shadow spot light of shape lightPoly and a object poly + * + * @param isCasterOpaque whether the caster is opaque + * @param lightCenter the center of the light + * @param lightSize the radius of the light + * @param poly x,y,z vertexes of a convex polygon that occludes the light source + * @param polyLength number of vertexes of the occluding polygon + * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return + * empty strip if error. + */ +void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, + float lightSize, const Vector3* poly, int polyLength, const Vector3& polyCentroid, + VertexBuffer& shadowTriangleStrip) { + OutlineData outlineData[polyLength]; + Vector2 outlineCentroid; + // Calculate the projected outline for each polygon's vertices from the light center. + // + // O Light + // / + // / + // . Polygon vertex + // / + // / + // O Outline vertices + // + // Ratio = (Poly - Outline) / (Light - Poly) + // Outline.x = Poly.x - Ratio * (Light.x - Poly.x) + // Outline's radius / Light's radius = Ratio + + // Compute the last outline vertex to make sure we can get the normal and outline + // in one single loop. + projectCasterToOutline(outlineData[polyLength - 1].position, lightCenter, + poly[polyLength - 1]); + + // Take the outline's polygon, calculate the normal for each outline edge. + int currentNormalIndex = polyLength - 1; + int nextNormalIndex = 0; + + for (int i = 0; i < polyLength; i++) { + float ratioZ = projectCasterToOutline(outlineData[i].position, + lightCenter, poly[i]); + outlineData[i].radius = ratioZ * lightSize; + + outlineData[currentNormalIndex].normal = ShadowTessellator::calculateNormal( + outlineData[currentNormalIndex].position, + outlineData[nextNormalIndex].position); + currentNormalIndex = (currentNormalIndex + 1) % polyLength; + nextNormalIndex++; + } + + projectCasterToOutline(outlineCentroid, lightCenter, polyCentroid); + + int penumbraIndex = 0; + int penumbraLength = polyLength * 3; + Vector2 penumbra[penumbraLength]; + + Vector2 umbra[polyLength]; + float distOutline = 0; + float ratioVI = 0; + + bool hasValidUmbra = true; + // We need the maxRatioVI to decrease the spot shadow strength accordingly. + float maxRaitoVI = 1.0; + + for (int i = 0; i < polyLength; i++) { + // Generate all the penumbra's vertices only using the (outline vertex + normal * radius) + // There is no guarantee that the penumbra is still convex, but for + // each outline vertex, it will connect to all its corresponding penumbra vertices as + // triangle fans. And for neighber penumbra vertex, it will be a trapezoid. + // + // Penumbra Vertices marked as Pi + // Outline Vertices marked as Vi + // (P3) + // (P2) | ' (P4) + // (P1)' | | ' + // ' | | ' + // (P0) ------------------------------------------------(P5) + // | (V0) |(V1) + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // | | + // (V3)-----------------------------------(V2) + int preNormalIndex = (i + polyLength - 1) % polyLength; + penumbra[penumbraIndex++] = outlineData[i].position + + outlineData[preNormalIndex].normal * outlineData[i].radius; + + int currentNormalIndex = i; + // (TODO) Depending on how roundness we want for each corner, we can subdivide + // further here and/or introduce some heuristic to decide how much the + // subdivision should be. + Vector2 avgNormal = + (outlineData[preNormalIndex].normal + outlineData[currentNormalIndex].normal) / 2; + + penumbra[penumbraIndex++] = outlineData[i].position + + avgNormal * outlineData[i].radius; + + penumbra[penumbraIndex++] = outlineData[i].position + + outlineData[currentNormalIndex].normal * outlineData[i].radius; + + // Compute the umbra by the intersection from the outline's centroid! + // + // (V) ------------------------------------ + // | ' | + // | ' | + // | ' (I) | + // | ' | + // | ' (C) | + // | | + // | | + // | | + // | | + // ------------------------------------ + // + // Connect a line b/t the outline vertex (V) and the centroid (C), it will + // intersect with the outline vertex's circle at point (I). + // Now, ratioVI = VI / VC, ratioIC = IC / VC + // Then the intersetion point can be computed as Ixy = Vxy * ratioIC + Cxy * ratioVI; + // + // When one of the outline circle cover the the outline centroid, (like I is + // on the other side of C), there is no real umbra any more, so we just fake + // a small area around the centroid as the umbra, and tune down the spot + // shadow's umbra strength to simulate the effect the whole shadow will + // become lighter in this case. + // The ratio can be simulated by using the inverse of maximum of ratioVI for + // all (V). + distOutline = (outlineData[i].position - outlineCentroid).length(); + if (distOutline == 0) { + // If the outline has 0 area, then there is no spot shadow anyway. + ALOGW("Outline has 0 area, no spot shadow!"); + return; + } + ratioVI = outlineData[i].radius / distOutline; + if (ratioVI >= 1.0) { + maxRaitoVI = ratioVI; + hasValidUmbra = false; + } + // When we know we don't have valid umbra, don't bother to compute the + // values below. But we can't skip the loop yet since we want to know the + // maximum ratio. + if (hasValidUmbra) { + float ratioIC = (distOutline - outlineData[i].radius) / distOutline; + umbra[i] = outlineData[i].position * ratioIC + outlineCentroid * ratioVI; + } + } + + float shadowStrengthScale = 1.0; + if (!hasValidUmbra) { + ALOGW("The object is too close to the light or too small, no real umbra!"); + for (int i = 0; i < polyLength; i++) { + umbra[i] = outlineData[i].position * FAKE_UMBRA_SIZE_RATIO + + outlineCentroid * (1 - FAKE_UMBRA_SIZE_RATIO); + } + shadowStrengthScale = 1.0 / maxRaitoVI; + } + +#if DEBUG_SHADOW + dumpPolygon(poly, polyLength, "input poly"); + dumpPolygon(outline, polyLength, "outline"); + dumpPolygon(penumbra, penumbraLength, "penumbra"); + dumpPolygon(umbra, polyLength, "umbra"); + ALOGD("hasValidUmbra is %d and shadowStrengthScale is %f", hasValidUmbra, shadowStrengthScale); +#endif + + generateTriangleStrip(isCasterOpaque, shadowStrengthScale, penumbra, + penumbraLength, umbra, polyLength, poly, polyLength, shadowTriangleStrip); +} + /** * Converts a polygon specified with CW vertices into an array of distance-from-centroid values. * @@ -697,7 +905,6 @@ int SpotShadow::calculateOccludedUmbra(const Vector2* umbra, int umbraLength, occludedUmbra, polyLength); } -#define OCLLUDED_UMBRA_SHRINK_FACTOR 0.95f /** * Generate a triangle strip given two convex polygons * @@ -708,8 +915,8 @@ int SpotShadow::calculateOccludedUmbra(const Vector2* umbra, int umbraLength, * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return * empty strip if error. **/ -void SpotShadow::generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra, - int penumbraLength, const Vector2* umbra, int umbraLength, +void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale, + const Vector2* penumbra, int penumbraLength, const Vector2* umbra, int umbraLength, const Vector3* poly, int polyLength, VertexBuffer& shadowTriangleStrip) { const int rays = SHADOW_RAY_COUNT; const int size = 2 * rays; @@ -750,13 +957,12 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, const Vector2* penum } } } - AlphaVertex* shadowVertices = shadowTriangleStrip.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT); // NOTE: Shadow alpha values are transformed when stored in alphavertices, // so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp - float transformedMaxAlpha = M_PI; + float transformedMaxAlpha = M_PI * shadowStrengthScale; // Calculate the vertices (x, y, alpha) in the shadow area. AlphaVertex centroidXYA; @@ -789,7 +995,6 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, const Vector2* penum shadowVertices[2 * rays + rayIndex] = centroidXYA; } } - shadowTriangleStrip.setMode(VertexBuffer::kTwoPolyRingShadow); shadowTriangleStrip.computeBounds<AlphaVertex>(); } @@ -844,7 +1049,16 @@ void SpotShadow::updateBound(const Vector2 inVector, Vector2& lowerBound, /** * For debug purpose, when things go wrong, dump the whole polygon data. */ -static void dumpPolygon(const Vector2* poly, int polyLength, const char* polyName) { +void SpotShadow::dumpPolygon(const Vector2* poly, int polyLength, const char* polyName) { + for (int i = 0; i < polyLength; i++) { + ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y); + } +} + +/** + * For debug purpose, when things go wrong, dump the whole polygon data. + */ +void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) { for (int i = 0; i < polyLength; i++) { ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y); } @@ -885,8 +1099,8 @@ void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, const Vector2* poly2, int poly2Length, const Vector2* intersection, int intersectionLength) { // Find the min and max of x and y. - Vector2 lowerBound(FLT_MAX, FLT_MAX); - Vector2 upperBound(-FLT_MAX, -FLT_MAX); + Vector2 lowerBound = {FLT_MAX, FLT_MAX}; + Vector2 upperBound = {-FLT_MAX, -FLT_MAX}; for (int i = 0; i < poly1Length; i++) { updateBound(poly1[i], lowerBound, upperBound); } diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h index d65ea89..355be8d 100644 --- a/libs/hwui/SpotShadow.h +++ b/libs/hwui/SpotShadow.h @@ -26,16 +26,22 @@ namespace uirenderer { class SpotShadow { public: - static void createSpotShadow(bool isCasterOpaque, const Vector3* poly, + static void createSpotShadow_old(bool isCasterOpaque, const Vector3* poly, int polyLength, const Vector3& lightCenter, float lightSize, int lightVertexCount, VertexBuffer& retStrips); + static void createSpotShadow(bool isCasterOpaque, const Vector3& lightCenter, + float lightSize, const Vector3* poly, int polyLength, + const Vector3& polyCentroid, VertexBuffer& retstrips); private: + static float projectCasterToOutline(Vector2& outline, + const Vector3& lightCenter, const Vector3& polyVertex); static int calculateOccludedUmbra(const Vector2* umbra, int umbraLength, const Vector3* poly, int polyLength, Vector2* occludedUmbra); - static void computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly, + + static void computeSpotShadow_old(bool isCasterOpaque, const Vector3* lightPoly, int lightPolyLength, const Vector3& lightCenter, const Vector3* poly, - int polyLength, VertexBuffer& retstrips); + int polyLength, VertexBuffer& shadowTriangleStrip); static void computeLightPolygon(int points, const Vector3& lightCenter, float size, Vector3* ret); @@ -60,8 +66,8 @@ private: static inline bool lineIntersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, Vector2& ret); - static void generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra, - int penumbraLength, const Vector2* umbra, int umbraLength, + static void generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale, + const Vector2* penumbra, int penumbraLength, const Vector2* umbra, int umbraLength, const Vector3* poly, int polyLength, VertexBuffer& retstrips); #if DEBUG_SHADOW @@ -72,6 +78,8 @@ private: const Vector2* poly2, int poly2Length, const Vector2* intersection, int intersectionLength); static void updateBound(const Vector2 inVector, Vector2& lowerBound, Vector2& upperBound ); + static void dumpPolygon(const Vector2* poly, int polyLength, const char* polyName); + static void dumpPolygon(const Vector3* poly, int polyLength, const char* polyName); #endif }; // SpotShadow diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 0a9aeb8..9e62f36 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -267,7 +267,7 @@ static void tessellateShadows( casterBounds, *localClip, maxZ, ambientBuffer); ShadowTessellator::tessellateSpotShadow( - isCasterOpaque, casterPolygon, casterVertexCount, + isCasterOpaque, casterPolygon, casterVertexCount, centroid3d, *drawTransform, lightCenter, lightRadius, casterBounds, *localClip, spotBuffer); } diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 6fb0411..00448b8 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -66,11 +66,13 @@ public: return isZero(valueA - valueB); } - inline static int max(int a, int b) { + template<typename T> + static inline T max(T a, T b) { return a > b ? a : b; } - inline static int min(int a, int b) { + template<typename T> + static inline T min(T a, T b) { return a < b ? a : b; } |