From 50ecf849cb7ccc3482517b74d2214b347927791e Mon Sep 17 00:00:00 2001 From: ztenghui Date: Tue, 11 Mar 2014 16:52:30 -0700 Subject: [PATCH] Create one hole inside the umbra area to avoid overdraw. bug:13439450 Change-Id: I859575196bd5a3029f447883025a6ec3a1f1face --- libs/hwui/AmbientShadow.cpp | 42 ++++++---- libs/hwui/AmbientShadow.h | 7 +- libs/hwui/Caches.cpp | 4 +- libs/hwui/OpenGLRenderer.cpp | 24 +++--- libs/hwui/OpenGLRenderer.h | 3 +- libs/hwui/ShadowTessellator.cpp | 59 +++++++------- libs/hwui/ShadowTessellator.h | 27 ++++--- libs/hwui/SpotShadow.cpp | 165 +++++++++++++++++++++++++--------------- libs/hwui/SpotShadow.h | 24 +++--- 9 files changed, 215 insertions(+), 140 deletions(-) diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index 8327ef72e978..c0b5402808b5 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -31,6 +31,7 @@ namespace uirenderer { * Calculate the shadows as a triangle strips while alpha value as the * shadow values. * + * @param isCasterOpaque Whether the caster is opaque. * @param vertices The shadow caster's polygon, which is represented in a Vector3 * array. * @param vertexCount The length of caster's polygon in terms of number of @@ -43,17 +44,18 @@ namespace uirenderer { * @param shadowVertexBuffer Return an floating point array of (x, y, a) * triangle strips mode. */ -void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount, - const Vector3& centroid3d, float heightFactor, float geomFactor, - VertexBuffer& shadowVertexBuffer) { +VertexBufferMode AmbientShadow::createAmbientShadow(bool isCasterOpaque, + const Vector3* vertices, int vertexCount, const Vector3& centroid3d, + float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { const int rays = SHADOW_RAY_COUNT; + VertexBufferMode mode = kVertexBufferMode_OnePolyRingShadow; // Validate the inputs. if (vertexCount < 3 || heightFactor <= 0 || rays <= 0 || geomFactor <= 0) { #if DEBUG_SHADOW - ALOGE("Invalid input for createAmbientShadow(), early return!"); + ALOGW("Invalid input for createAmbientShadow(), early return!"); #endif - return; + return mode; // vertex buffer is empty, so any mode doesn't matter. } Vector dir; // TODO: use C++11 unique_ptr @@ -75,7 +77,7 @@ void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount rayDist[i] = rayDistance; if (edgeIndex < 0 || edgeIndex >= vertexCount) { #if DEBUG_SHADOW - ALOGE("Invalid edgeIndex!"); + ALOGW("Invalid edgeIndex!"); #endif edgeIndex = 0; } @@ -86,7 +88,8 @@ void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount // The output buffer length basically is roughly rays * layers, but since we // need triangle strips, so we need to duplicate vertices to accomplish that. - AlphaVertex* shadowVertices = shadowVertexBuffer.alloc(SHADOW_VERTEX_COUNT); + AlphaVertex* shadowVertices = + shadowVertexBuffer.alloc(SHADOW_VERTEX_COUNT); // Calculate the vertex of the shadows. // @@ -95,6 +98,7 @@ void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount // calculate the normal N, which should be perpendicular to the edge of the // polygon (represented by the neighbor intersection points) . // Shadow's vertices will be generated as : P + N * scale. + const Vector2 centroid2d = Vector2(centroid3d.x, centroid3d.y); for (int rayIndex = 0; rayIndex < rays; rayIndex++) { Vector2 normal(1.0f, 0.0f); calculateNormal(rays, rayIndex, dir.array(), rayDist, normal); @@ -102,7 +106,7 @@ void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount // The vertex should be start from rayDist[i] then scale the // normalizeNormal! Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] + - Vector2(centroid3d.x, centroid3d.y); + centroid2d; // outer ring of points, expanded based upon height of each ray intersection float expansionDist = rayHeight[rayIndex] * heightFactor * @@ -114,25 +118,31 @@ void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount // inner ring of points float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor); - AlphaVertex::set(&shadowVertices[rayIndex + rays], + AlphaVertex::set(&shadowVertices[rays + rayIndex], intersection.x, intersection.y, opacity); } - float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor); - AlphaVertex::set(&shadowVertices[SHADOW_VERTEX_COUNT - 1], - centroid3d.x, centroid3d.y, centroidAlpha); -#if DEBUG_SHADOW - if (currentVertexIndex != SHADOW_VERTEX_COUNT) { - ALOGE("number of vertex generated for ambient shadow is wrong! " - "current: %d , expected: %d", currentVertexIndex, SHADOW_VERTEX_COUNT); + // If caster isn't opaque, we need to to fill the umbra by storing the umbra's + // centroid in the innermost ring of vertices. + if (!isCasterOpaque) { + mode = kVertexBufferMode_TwoPolyRingShadow; + float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor); + AlphaVertex centroidXYA; + AlphaVertex::set(¢roidXYA, centroid2d.x, centroid2d.y, centroidAlpha); + for (int rayIndex = 0; rayIndex < rays; rayIndex++) { + shadowVertices[2 * rays + rayIndex] = centroidXYA; + } } + +#if DEBUG_SHADOW for (int i = 0; i < SHADOW_VERTEX_COUNT; i++) { ALOGD("ambient shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, shadowVertices[i].y, shadowVertices[i].alpha); } #endif + return mode; } /** diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h index 20d1384c8882..45b8bef64192 100644 --- a/libs/hwui/AmbientShadow.h +++ b/libs/hwui/AmbientShadow.h @@ -19,6 +19,7 @@ #define ANDROID_HWUI_AMBIENT_SHADOW_H #include "Debug.h" +#include "OpenGLRenderer.h" #include "Vector.h" #include "VertexBuffer.h" @@ -34,9 +35,9 @@ namespace uirenderer { */ class AmbientShadow { public: - static void createAmbientShadow(const Vector3* poly, int polyLength, - const Vector3& centroid3d, float heightFactor, float geomFactor, - VertexBuffer& shadowVertexBuffer); + static VertexBufferMode createAmbientShadow(bool isCasterOpaque, const Vector3* poly, + int polyLength, const Vector3& centroid3d, float heightFactor, + float geomFactor, VertexBuffer& shadowVertexBuffer); private: static void calculateRayDirections(int rays, Vector2* dir); diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 2dfc873efc01..477d69172c78 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -445,11 +445,11 @@ bool Caches::bindQuadIndicesBuffer() { bool Caches::bindShadowIndicesBuffer() { if (!mShadowStripsIndices) { - uint16_t* shadowIndices = new uint16_t[SHADOW_INDEX_COUNT]; + uint16_t* shadowIndices = new uint16_t[MAX_SHADOW_INDEX_COUNT]; ShadowTessellator::generateShadowIndices(shadowIndices); glGenBuffers(1, &mShadowStripsIndices); bool force = bindIndicesBufferInternal(mShadowStripsIndices); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, SHADOW_INDEX_COUNT * sizeof(uint16_t), + glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_SHADOW_INDEX_COUNT * sizeof(uint16_t), shadowIndices, GL_STATIC_DRAW); delete[] shadowIndices; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 1475953f4be4..cb8155b0fc11 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2414,9 +2414,12 @@ status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode, if (mode == kVertexBufferMode_Standard) { mCaches.unbindIndicesBuffer(); glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount()); - } else { + } else if (mode == kVertexBufferMode_OnePolyRingShadow) { + mCaches.bindShadowIndicesBuffer(); + glDrawElements(GL_TRIANGLE_STRIP, ONE_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); + } else if (mode == kVertexBufferMode_TwoPolyRingShadow) { mCaches.bindShadowIndicesBuffer(); - glDrawElements(GL_TRIANGLE_STRIP, SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); + glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); } if (isAA) { @@ -3245,14 +3248,15 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& c } centroid3d.z += casterLift; } - + bool isCasterOpaque = (casterAlpha == 1.0f); // draw caster's shadows if (mCaches.propertyAmbientShadowStrength > 0) { paint.setARGB(casterAlpha * mCaches.propertyAmbientShadowStrength, 0, 0, 0); VertexBuffer ambientShadowVertexBuffer; - ShadowTessellator::tessellateAmbientShadow(casterPolygon, casterVertexCount, - centroid3d, ambientShadowVertexBuffer); - drawVertexBuffer(kVertexBufferMode_Shadow, ambientShadowVertexBuffer, &paint); + VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow( + isCasterOpaque, casterPolygon, casterVertexCount, centroid3d, + ambientShadowVertexBuffer); + drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint); } if (mCaches.propertySpotShadowStrength > 0) { @@ -3260,10 +3264,10 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransformXY, const mat4& c VertexBuffer spotShadowVertexBuffer; Vector3 lightPosScale(mCaches.propertyLightPosXScale, mCaches.propertyLightPosYScale, mCaches.propertyLightPosZScale); - ShadowTessellator::tessellateSpotShadow(casterPolygon, casterVertexCount, - lightPosScale, *currentTransform(), getWidth(), getHeight(), - spotShadowVertexBuffer); - drawVertexBuffer(kVertexBufferMode_Shadow, spotShadowVertexBuffer, &paint); + VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow( + isCasterOpaque, casterPolygon, casterVertexCount, lightPosScale, + *currentTransform(), getWidth(), getHeight(), spotShadowVertexBuffer); + drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint); } return DrawGlInfo::kStatusDrew; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 94abfa7f4427..059c64f6c0b6 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -119,7 +119,8 @@ enum ModelViewMode { enum VertexBufferMode { kVertexBufferMode_Standard = 0, - kVertexBufferMode_Shadow = 1 + kVertexBufferMode_OnePolyRingShadow = 1, + kVertexBufferMode_TwoPolyRingShadow = 2 }; /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index f138222c2725..771904ac3e59 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -33,9 +33,9 @@ static inline T max(T a, T b) { return a > b ? a : b; } -void ShadowTessellator::tessellateAmbientShadow(const Vector3* casterPolygon, - int casterVertexCount, const Vector3& centroid3d, - VertexBuffer& shadowVertexBuffer) { +VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, + const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer) { ATRACE_CALL(); // A bunch of parameters to tweak the shadow. @@ -43,12 +43,14 @@ void ShadowTessellator::tessellateAmbientShadow(const Vector3* casterPolygon, const float heightFactor = 1.0f / 128; const float geomFactor = 64; - AmbientShadow::createAmbientShadow(casterPolygon, casterVertexCount, - centroid3d, heightFactor, geomFactor, shadowVertexBuffer); + return AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon, + casterVertexCount, centroid3d, heightFactor, geomFactor, + shadowVertexBuffer); } -void ShadowTessellator::tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount, +VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, const Vector3& lightPosScale, const mat4& receiverTransform, int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) { ATRACE_CALL(); @@ -71,37 +73,40 @@ void ShadowTessellator::tessellateSpotShadow(const Vector3* casterPolygon, int c const float lightSize = maximal / 4; const int lightVertexCount = 8; - SpotShadow::createSpotShadow(casterPolygon, casterVertexCount, lightCenter, - lightSize, lightVertexCount, shadowVertexBuffer); + VertexBufferMode mode = SpotShadow::createSpotShadow(isCasterOpaque, + casterPolygon, casterVertexCount, lightCenter, lightSize, + lightVertexCount, shadowVertexBuffer); +#if DEBUG_SHADOW + if(shadowVertexBuffer.getVertexCount() <= 0) { + ALOGD("Spot shadow generation failed %d", shadowVertexBuffer.getVertexCount()); + } +#endif + return mode; } void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) { int currentIndex = 0; const int rays = SHADOW_RAY_COUNT; // For the penumbra area. - for (int i = 0; i < rays; i++) { - shadowIndices[currentIndex++] = i; - shadowIndices[currentIndex++] = rays + i; - } - // To close the loop, back to the ray 0. - shadowIndices[currentIndex++] = 0; - shadowIndices[currentIndex++] = rays; - - uint16_t centroidIndex = 2 * rays; - // For the umbra area, using strips to simulate the fans. - for (int i = 0; i < rays; i++) { - shadowIndices[currentIndex++] = rays + i; - shadowIndices[currentIndex++] = centroidIndex; + for (int layer = 0; layer < 2; layer ++) { + int baseIndex = layer * rays; + for (int i = 0; i < rays; i++) { + shadowIndices[currentIndex++] = i + baseIndex; + shadowIndices[currentIndex++] = rays + i + baseIndex; + } + // To close the loop, back to the ray 0. + shadowIndices[currentIndex++] = 0 + baseIndex; + // Note this is the same as the first index of next layer loop. + shadowIndices[currentIndex++] = rays + baseIndex; } - shadowIndices[currentIndex++] = rays; #if DEBUG_SHADOW - if (currentIndex != SHADOW_INDEX_COUNT) { - ALOGE("vertex index count is wrong. current %d, expected %d", - currentIndex, SHADOW_INDEX_COUNT); + if (currentIndex != MAX_SHADOW_INDEX_COUNT) { + ALOGW("vertex index count is wrong. current %d, expected %d", + currentIndex, MAX_SHADOW_INDEX_COUNT); } - for (int i = 0; i < SHADOW_INDEX_COUNT; i++) { + for (int i = 0; i < MAX_SHADOW_INDEX_COUNT; i++) { ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]); } #endif @@ -135,7 +140,7 @@ Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { if (area != 0) { centroid = Vector2(sumx / (3 * area), sumy / (3 * area)); } else { - ALOGE("Area is 0 while computing centroid!"); + ALOGW("Area is 0 while computing centroid!"); } return centroid; } diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h index c55846024c7a..ab039fa05c61 100644 --- a/libs/hwui/ShadowTessellator.h +++ b/libs/hwui/ShadowTessellator.h @@ -20,6 +20,7 @@ #include "Debug.h" #include "Matrix.h" +#include "OpenGLRenderer.h" #include "VertexBuffer.h" namespace android { @@ -30,15 +31,14 @@ namespace uirenderer { // Assuming we use 6 rays and only 1 layer, Then we will have 2 hexagons, which // are 0 to 5 and 6 to 11. The area between them will be the penumbra area, and // the area inside the 2nd hexagon is the umbra. -// Also, we need to add the centroid "12" to draw the umbra area as triangle fans. -// +// Ambient shadow is using only 1 layer for opaque caster, otherwise, spot +// shadow and ambient shadow are using 2 layers. // Triange strip indices for penumbra area: (0, 6, 1, 7, 2, 8, 3, 9, 4, 10, 5, 11, 0, 6) -// Triange strip indices for numbra area: (6, 12, 7, 12, 8, 12, 9, 12, 10, 12, 11, 12, 6) // 0 // // 5 6 1 // 11 7 -// 12 +// // 10 8 // 4 9 2 // @@ -49,20 +49,27 @@ namespace uirenderer { #define SHADOW_RAY_COUNT 128 // The total number of all the vertices representing the shadow. -#define SHADOW_VERTEX_COUNT (2 * SHADOW_RAY_COUNT + 1) +// For the case we only have 1 layer, then we will just fill only 2/3 of it. +#define SHADOW_VERTEX_COUNT (3 * SHADOW_RAY_COUNT) // The total number of indices used for drawing the shadow geometry as triangle strips. -#define SHADOW_INDEX_COUNT (2 * SHADOW_RAY_COUNT + 1 + 2 * (SHADOW_RAY_COUNT + 1)) +// Depending on the mode we are drawing, we can have 1 layer or 2 layers. +// Therefore, we only build the longer index buffer. +#define TWO_POLY_RING_SHADOW_INDEX_COUNT (4 * (SHADOW_RAY_COUNT + 1)) +#define ONE_POLY_RING_SHADOW_INDEX_COUNT (2 * (SHADOW_RAY_COUNT + 1)) + +#define MAX_SHADOW_INDEX_COUNT TWO_POLY_RING_SHADOW_INDEX_COUNT #define SHADOW_MIN_CASTER_Z 0.001f class ShadowTessellator { public: - static void tessellateAmbientShadow(const Vector3* casterPolygon, - int casterVertexCount, const Vector3& centroid3d, - VertexBuffer& shadowVertexBuffer); + static VertexBufferMode tessellateAmbientShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, + const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer); - static void tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount, + static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque, + const Vector3* casterPolygon, int casterVertexCount, const Vector3& lightPosScale, const mat4& receiverTransform, int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer); diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index 8538b295c86e..1b490839efdd 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -161,7 +161,7 @@ bool SpotShadow::ccw(double ax, double ay, double bx, double by, /** * Calculates the intersection of poly1 with poly2 and put in poly2. - * + * Note that both poly1 and poly2 must be in CW order already! * * @param poly1 The 1st polygon, as a Vector2 array. * @param poly1Length The number of vertices of 1st polygon. @@ -169,11 +169,16 @@ bool SpotShadow::ccw(double ax, double ay, double bx, double by, * @param poly2Length The number of vertices of 2nd polygon. * @return number of vertices in output polygon as poly2. */ -int SpotShadow::intersection(Vector2* poly1, int poly1Length, +int SpotShadow::intersection(const Vector2* poly1, int poly1Length, Vector2* poly2, int poly2Length) { - makeClockwise(poly1, poly1Length); - makeClockwise(poly2, poly2Length); - +#if DEBUG_SHADOW + if (!isClockwise(poly1, poly1Length)) { + ALOGW("Poly1 is not clockwise! Intersection is wrong!"); + } + if (!isClockwise(poly2, poly2Length)) { + ALOGW("Poly2 is not clockwise! Intersection is wrong!"); + } +#endif Vector2 poly[poly1Length * poly2Length + 2]; int count = 0; int pcount = 0; @@ -411,7 +416,7 @@ void SpotShadow::makeClockwise(Vector2* polygon, int len) { * @param polygon the polygon as a Vector2 array * @param len the number of points of the polygon */ -bool SpotShadow::isClockwise(Vector2* polygon, int len) { +bool SpotShadow::isClockwise(const Vector2* polygon, int len) { double sum = 0; double p1x = polygon[len - 1].x; double p1y = polygon[len - 1].y; @@ -514,13 +519,14 @@ void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, * empty strip if error. * */ -void SpotShadow::createSpotShadow(const Vector3* poly, int polyLength, - const Vector3& lightCenter, float lightSize, int lightVertexCount, - VertexBuffer& retStrips) { +VertexBufferMode SpotShadow::createSpotShadow(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(light, lightVertexCount, lightCenter, poly, polyLength, - retStrips); + computeSpotShadow(isCasterOpaque, light, lightVertexCount, lightCenter, poly, + polyLength, retStrips); + return kVertexBufferMode_TwoPolyRingShadow; } /** @@ -533,9 +539,9 @@ void SpotShadow::createSpotShadow(const Vector3* poly, int polyLength, * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return * empty strip if error. */ -void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength, - const Vector3& lightCenter, const Vector3* poly, int polyLength, - VertexBuffer& shadowTriangleStrip) { +void SpotShadow::computeSpotShadow(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. @@ -565,13 +571,13 @@ void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength for (int j = 0; j < lightPolyLength; j++) { int m = 0; for (int i = 0; i < polyLength; i++) { - float t = lightPoly[j].z - poly[i].z; - if (t == 0) { + float deltaZ = lightPoly[j].z - poly[i].z; + if (deltaZ == 0) { return; } - t = lightPoly[j].z / t; - float x = lightPoly[j].x - t * (lightPoly[j].x - poly[i].x); - float y = lightPoly[j].y - t * (lightPoly[j].y - poly[i].y); + float ratioZ = lightPoly[j].z / deltaZ; + float x = lightPoly[j].x - ratioZ * (lightPoly[j].x - poly[i].x); + float y = lightPoly[j].y - ratioZ * (lightPoly[j].y - poly[i].y); Vector2 newPoint = Vector2(x, y); shadowRegion[k] = newPoint; @@ -606,13 +612,13 @@ void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength if (umbraLength < 3) { // If there is no real umbra, make a fake one. for (int i = 0; i < polyLength; i++) { - float t = lightCenter.z - poly[i].z; - if (t == 0) { + float deltaZ = lightCenter.z - poly[i].z; + if (deltaZ == 0) { return; } - t = lightCenter.z / t; - float x = lightCenter.x - t * (lightCenter.x - poly[i].x); - float y = lightCenter.y - t * (lightCenter.y - poly[i].y); + float ratioZ = lightCenter.z / deltaZ; + float x = lightCenter.x - ratioZ * (lightCenter.x - poly[i].x); + float y = lightCenter.y - ratioZ * (lightCenter.y - poly[i].y); fakeUmbra[i].x = x; fakeUmbra[i].y = y; @@ -635,8 +641,8 @@ void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength umbraLength = polyLength; } - generateTriangleStrip(penumbra, penumbraLength, umbra, umbraLength, - shadowTriangleStrip); + generateTriangleStrip(isCasterOpaque, penumbra, penumbraLength, umbra, + umbraLength, poly, polyLength, shadowTriangleStrip); } /** @@ -684,7 +690,12 @@ bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& po cos(rayIndex * step), sin(rayIndex * step), *lastVertex, poly[polyIndex]); - if (distanceToIntersect < 0) return false; // error case, abort + if (distanceToIntersect < 0) { +#if DEBUG_SHADOW + ALOGW("ERROR: convertPolyToRayDist failed"); +#endif + return false; // error case, abort + } rayDist[rayIndex] = distanceToIntersect; @@ -696,6 +707,22 @@ bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& po return true; } +int SpotShadow::calculateOccludedUmbra(const Vector2* umbra, int umbraLength, + const Vector3* poly, int polyLength, Vector2* occludedUmbra) { + // Occluded umbra area is computed as the intersection of the projected 2D + // poly and umbra. + for (int i = 0; i < polyLength; i++) { + occludedUmbra[i].x = poly[i].x; + occludedUmbra[i].y = poly[i].y; + } + + // Both umbra and incoming polygon are guaranteed to be CW, so we can call + // intersection() directly. + return intersection(umbra, umbraLength, + occludedUmbra, polyLength); +} + +#define OCLLUDED_UMBRA_SHRINK_FACTOR 0.95f /** * Generate a triangle strip given two convex polygons * @@ -706,10 +733,10 @@ bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& po * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return * empty strip if error. **/ -void SpotShadow::generateTriangleStrip(const Vector2* penumbra, int penumbraLength, - const Vector2* umbra, int umbraLength, VertexBuffer& shadowTriangleStrip) { +void SpotShadow::generateTriangleStrip(bool isCasterOpaque, 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; const float step = M_PI * 2 / rays; // Centroid of the umbra. @@ -721,37 +748,66 @@ void SpotShadow::generateTriangleStrip(const Vector2* penumbra, int penumbraLeng float penumbraDistPerRay[rays]; // Intersection to the umbra. float umbraDistPerRay[rays]; + // Intersection to the occluded umbra area. + float occludedUmbraDistPerRay[rays]; // convert CW polygons to ray distance encoding, aborting on conversion failure if (!convertPolyToRayDist(umbra, umbraLength, centroid, umbraDistPerRay)) return; if (!convertPolyToRayDist(penumbra, penumbraLength, centroid, penumbraDistPerRay)) return; - AlphaVertex* shadowVertices = shadowTriangleStrip.alloc(getStripSize(rays)); + bool hasOccludedUmbraArea = false; + if (isCasterOpaque) { + Vector2 occludedUmbra[polyLength + umbraLength]; + int occludedUmbraLength = calculateOccludedUmbra(umbra, umbraLength, poly, polyLength, + occludedUmbra); + // Make sure the centroid is inside the umbra, otherwise, fall back to the + // approach as if there is no occluded umbra area. + if (testPointInsidePolygon(centroid, occludedUmbra, occludedUmbraLength)) { + hasOccludedUmbraArea = true; + // Shrink the occluded umbra area to avoid pixel level artifacts. + for (int i = 0; i < occludedUmbraLength; i ++) { + occludedUmbra[i] = centroid + (occludedUmbra[i] - centroid) * + OCLLUDED_UMBRA_SHRINK_FACTOR; + } + if (!convertPolyToRayDist(occludedUmbra, occludedUmbraLength, centroid, + occludedUmbraDistPerRay)) { + return; + } + } + } + + AlphaVertex* shadowVertices = + shadowTriangleStrip.alloc(SHADOW_VERTEX_COUNT); // Calculate the vertices (x, y, alpha) in the shadow area. + AlphaVertex centroidXYA; + AlphaVertex::set(¢roidXYA, centroid.x, centroid.y, 1.0f); for (int rayIndex = 0; rayIndex < rays; rayIndex++) { float dx = cosf(step * rayIndex); float dy = sinf(step * rayIndex); - // outer ring - float currentDist = penumbraDistPerRay[rayIndex]; + // penumbra ring + float penumbraDistance = penumbraDistPerRay[rayIndex]; AlphaVertex::set(&shadowVertices[rayIndex], - dx * currentDist + centroid.x, dy * currentDist + centroid.y, 0.0f); + dx * penumbraDistance + centroid.x, + dy * penumbraDistance + centroid.y, 0.0f); - // inner ring - float deltaDist = umbraDistPerRay[rayIndex] - penumbraDistPerRay[rayIndex]; - currentDist += deltaDist; + // umbra ring + float umbraDistance = umbraDistPerRay[rayIndex]; AlphaVertex::set(&shadowVertices[rays + rayIndex], - dx * currentDist + centroid.x, dy * currentDist + centroid.y, 1.0f); - } - // The centroid is in the umbra area, so the opacity is considered as 1.0. - AlphaVertex::set(&shadowVertices[SHADOW_VERTEX_COUNT - 1], centroid.x, centroid.y, 1.0f); -#if DEBUG_SHADOW - for (int i = 0; i < currentIndex; i++) { - ALOGD("spot shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, - shadowVertices[i].y, shadowVertices[i].alpha); + dx * umbraDistance + centroid.x, dy * umbraDistance + centroid.y, 1.0f); + + // occluded umbra ring + if (hasOccludedUmbraArea) { + float occludedUmbraDistance = occludedUmbraDistPerRay[rayIndex]; + AlphaVertex::set(&shadowVertices[2 * rays + rayIndex], + dx * occludedUmbraDistance + centroid.x, + dy * occludedUmbraDistance + centroid.y, 1.0f); + } else { + // Put all vertices of the occluded umbra ring at the centroid. + shadowVertices[2 * rays + rayIndex] = centroidXYA; + } } -#endif } /** @@ -775,17 +831,6 @@ void SpotShadow::smoothPolygon(int level, int rays, float* rayDist) { } } -/** - * Calculate the number of vertex we will create given a number of rays and layers - * - * @param rays number of points around the polygons you want - * @param layers number of layers of triangle strips you need - * @return number of vertex (multiply by 3 for number of floats) - */ -int SpotShadow::getStripSize(int rays) { - return (2 + rays + (2 * (rays + 1))); -} - #if DEBUG_SHADOW #define TEST_POINT_NUMBER 128 @@ -837,7 +882,7 @@ bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength, bool isCCWOrCoLinear = (delta >= EPSILON); if (isCCWOrCoLinear) { - ALOGE("(Error Type 2): polygon (%s) is not a convex b/c start (x %f, y %f)," + ALOGW("(Error Type 2): polygon (%s) is not a convex b/c start (x %f, y %f)," "middle (x %f, y %f) and end (x %f, y %f) , delta is %f !!!", name, start.x, start.y, middle.x, middle.y, end.x, end.y, delta); isConvex = false; @@ -879,14 +924,14 @@ void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, if (testPointInsidePolygon(testPoint, intersection, intersectionLength)) { if (!testPointInsidePolygon(testPoint, poly1, poly1Length)) { dumpPoly = true; - ALOGE("(Error Type 1): one point (%f, %f) in the intersection is" + ALOGW("(Error Type 1): one point (%f, %f) in the intersection is" " not in the poly1", testPoint.x, testPoint.y); } if (!testPointInsidePolygon(testPoint, poly2, poly2Length)) { dumpPoly = true; - ALOGE("(Error Type 1): one point (%f, %f) in the intersection is" + ALOGW("(Error Type 1): one point (%f, %f) in the intersection is" " not in the poly2", testPoint.x, testPoint.y); } diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h index 7839dc3ebb8c..599d37ecd174 100644 --- a/libs/hwui/SpotShadow.h +++ b/libs/hwui/SpotShadow.h @@ -26,19 +26,20 @@ namespace uirenderer { class SpotShadow { public: - static void createSpotShadow(const Vector3* poly, int polyLength, - const Vector3& lightCenter, float lightSize, int lightVertexCount, - VertexBuffer& retStrips); + static VertexBufferMode createSpotShadow(bool isCasterOpaque, const Vector3* poly, + int polyLength, const Vector3& lightCenter, float lightSize, + int lightVertexCount, VertexBuffer& retStrips); private: - static void computeSpotShadow(const Vector3* lightPoly, int lightPolyLength, - const Vector3& lightCenter, const Vector3* poly, int polyLength, - VertexBuffer& retstrips); + static int calculateOccludedUmbra(const Vector2* umbra, int umbraLength, + const Vector3* poly, int polyLength, Vector2* occludedUmbra); + static void computeSpotShadow(bool isCasterOpaque, const Vector3* lightPoly, + int lightPolyLength, const Vector3& lightCenter, const Vector3* poly, + int polyLength, VertexBuffer& retstrips); static void computeLightPolygon(int points, const Vector3& lightCenter, float size, Vector3* ret); - static int getStripSize(int rays); static void smoothPolygon(int level, int rays, float* rayDist); static float rayIntersectPoly(const Vector2* poly, int polyLength, const Vector2& point, float dx, float dy); @@ -46,7 +47,7 @@ private: static void xsort(Vector2* points, int pointsLength); static int hull(Vector2* points, int pointsLength, Vector2* retPoly); static bool ccw(double ax, double ay, double bx, double by, double cx, double cy); - static int intersection(Vector2* poly1, int poly1length, Vector2* poly2, int poly2length); + static int intersection(const Vector2* poly1, int poly1length, Vector2* poly2, int poly2length); static void sort(Vector2* poly, int polyLength, const Vector2& center); static void swap(Vector2* points, int i, int j); @@ -55,13 +56,14 @@ private: static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len); static void makeClockwise(Vector2* polygon, int len); - static bool isClockwise(Vector2* polygon, int len); + static bool isClockwise(const Vector2* polygon, int len); static void reverse(Vector2* polygon, int len); 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(const Vector2* penumbra, int penumbraLength, - const Vector2* umbra, int umbraLength, VertexBuffer& retstrips); + static void generateTriangleStrip(bool isCasterOpaque, const Vector2* penumbra, + int penumbraLength, const Vector2* umbra, int umbraLength, + const Vector3* poly, int polyLength, VertexBuffer& retstrips); #if DEBUG_SHADOW // Verification utility function. -- 2.11.0