From 63d41abb40b3ce40d8b9bccb1cf186e8158a3687 Mon Sep 17 00:00:00 2001 From: ztenghui Date: Fri, 14 Feb 2014 13:13:41 -0800 Subject: Use pre-computed index to draw the shadow. Also draw the umbra part as triangle fans instead of zig zag fashion. b/12840179 Change-Id: Iaa5d15e77351acdd71f076bd8f9bb2d4d2b92faf --- libs/hwui/AmbientShadow.cpp | 140 ++++++++++----------------------------- libs/hwui/AmbientShadow.h | 8 +-- libs/hwui/Caches.cpp | 30 +++++++-- libs/hwui/Caches.h | 7 +- libs/hwui/FontRenderer.cpp | 2 +- libs/hwui/OpenGLRenderer.cpp | 39 +++++++---- libs/hwui/OpenGLRenderer.h | 9 ++- libs/hwui/ShadowTessellator.cpp | 85 ++++++++++++++++++++---- libs/hwui/ShadowTessellator.h | 41 +++++++++++- libs/hwui/SpotShadow.cpp | 141 +++++++++++----------------------------- libs/hwui/SpotShadow.h | 11 ++-- 11 files changed, 256 insertions(+), 257 deletions(-) (limited to 'libs') diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index 1f5d26c..4935b34 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -21,6 +21,7 @@ #include #include "AmbientShadow.h" +#include "ShadowTessellator.h" #include "Vertex.h" namespace android { @@ -34,9 +35,7 @@ namespace uirenderer { * array. * @param vertexCount The length of caster's polygon in terms of number of * vertices. - * @param rays The number of rays shooting out from the centroid. - * @param layers The number of rings outside the polygon. - * @param strength The darkness of the shadow, the higher, the darker. + * @param centroid3d The centroid of the shadow caster. * @param heightFactor The factor showing the higher the object, the lighter the * shadow. * @param geomFactor The factor scaling the geometry expansion along the normal. @@ -45,21 +44,18 @@ namespace uirenderer { * triangle strips mode. */ void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount, - int rays, int layers, float strength, float heightFactor, float geomFactor, + const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { - + const int rays = SHADOW_RAY_COUNT; + const int layers = SHADOW_LAYER_COUNT; // Validate the inputs. - if (strength <= 0 || heightFactor <= 0 || layers <= 0 || rays <= 0 + if (vertexCount < 3 || heightFactor <= 0 || layers <= 0 || rays <= 0 || geomFactor <= 0) { #if DEBUG_SHADOW ALOGE("Invalid input for createAmbientShadow(), early return!"); #endif return; } - int rings = layers + 1; - int size = rays * rings; - Vector2 centroid; - calculatePolygonCentroid(vertices, vertexCount, centroid); Vector dir; // TODO: use C++11 unique_ptr dir.setCapacity(rays); @@ -75,7 +71,7 @@ void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount int edgeIndex; float edgeFraction; float rayDistance; - calculateIntersection(vertices, vertexCount, centroid, dir[i], edgeIndex, + calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex, edgeFraction, rayDistance); rayDist[i] = rayDistance; if (edgeIndex < 0 || edgeIndex >= vertexCount) { @@ -91,8 +87,7 @@ 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. - const int shadowVertexCount = (2 + rays + ((layers) * 2 * (rays + 1))); - AlphaVertex* shadowVertices = shadowVertexBuffer.alloc(shadowVertexCount); + AlphaVertex* shadowVertices = shadowVertexBuffer.alloc(SHADOW_VERTEX_COUNT); // Calculate the vertex of the shadows. // @@ -101,110 +96,45 @@ 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. - int currentIndex = 0; - for (int r = 0; r < layers; r++) { - int firstInLayer = currentIndex; - for (int i = 0; i < rays; i++) { + int currentVertexIndex = 0; + for (int layerIndex = 0; layerIndex <= layers; layerIndex++) { + for (int rayIndex = 0; rayIndex < rays; rayIndex++) { Vector2 normal(1.0f, 0.0f); - calculateNormal(rays, i, dir.array(), rayDist, normal); + calculateNormal(rays, rayIndex, dir.array(), rayDist, normal); - float opacity = strength * (0.5f) / (1 + rayHeight[i] / heightFactor); + float opacity = 1.0 / (1 + rayHeight[rayIndex] / heightFactor); // The vertex should be start from rayDist[i] then scale the // normalizeNormal! - Vector2 intersection = dir[i] * rayDist[i] + centroid; - - // Use 2 rings' vertices to complete one layer's strip - for (int j = r; j < (r + 2); j++) { - float jf = j / (float)(rings - 1); - - float expansionDist = rayHeight[i] / heightFactor * geomFactor * jf; - AlphaVertex::set(&shadowVertices[currentIndex], - intersection.x + normal.x * expansionDist, - intersection.y + normal.y * expansionDist, - (1 - jf) * opacity); - currentIndex++; - } + Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] + + Vector2(centroid3d.x, centroid3d.y); + + float layerRatio = layerIndex / (float)(layers); + // The higher the intersection is, the further the ambient shadow expanded. + float expansionDist = rayHeight[rayIndex] / heightFactor * + geomFactor * (1 - layerRatio); + AlphaVertex::set(&shadowVertices[currentVertexIndex++], + intersection.x + normal.x * expansionDist, + intersection.y + normal.y * expansionDist, + layerRatio * opacity); } - // From one layer to the next, we need to duplicate the vertex to - // continue as a single strip. - shadowVertices[currentIndex] = shadowVertices[firstInLayer]; - currentIndex++; - shadowVertices[currentIndex] = shadowVertices[firstInLayer + 1]; - currentIndex++; } + float centroidAlpha = 1.0 / (1 + centroid3d.z / heightFactor); + AlphaVertex::set(&shadowVertices[currentVertexIndex++], + centroid3d.x, centroid3d.y, centroidAlpha); - // After all rings are done, we need to jump into the polygon. - // In order to keep everything in a strip, we need to duplicate the last one - // of the rings and the first one inside the polygon. - int lastInRings = currentIndex - 1; - shadowVertices[currentIndex] = shadowVertices[lastInRings]; - currentIndex++; - - // We skip one and fill it back after we finish the internal triangles. - currentIndex++; - int firstInternal = currentIndex; - - // Combine the internal area of the polygon into a triangle strip, too. - // The basic idea is zig zag between the intersection points. - // 0 -> (n - 1) -> 1 -> (n - 2) ... - for (int k = 0; k < rays; k++) { - int i = k / 2; - if ((k & 1) == 1) { // traverse the inside in a zig zag pattern for strips - i = rays - i - 1; - } - float cast = rayDist[i] * (1 + rayHeight[i] / heightFactor); - float opacity = strength * (0.5f) / (1 + rayHeight[i] / heightFactor); - float t = rayDist[i]; - - AlphaVertex::set(&shadowVertices[currentIndex], dir[i].x * t + centroid.x, - dir[i].y * t + centroid.y, opacity); - currentIndex++; +#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); } - - currentIndex = firstInternal - 1; - shadowVertices[currentIndex] = shadowVertices[firstInternal]; -} - -/** - * Calculate the centroid of a given polygon. - * - * @param vertices The shadow caster's polygon, which is represented in a - * straight Vector3 array. - * @param vertexCount The length of caster's polygon in terms of number of vertices. - * - * @param centroid Return the centroid of the polygon. - */ -void AmbientShadow::calculatePolygonCentroid(const Vector3* vertices, int vertexCount, - Vector2& centroid) { - float sumx = 0; - float sumy = 0; - int p1 = vertexCount - 1; - float area = 0; - for (int p2 = 0; p2 < vertexCount; p2++) { - float x1 = vertices[p1].x; - float y1 = vertices[p1].y; - float x2 = vertices[p2].x; - float y2 = vertices[p2].y; - float a = (x1 * y2 - x2 * y1); - sumx += (x1 + x2) * a; - sumy += (y1 + y2) * a; - area += a; - p1 = p2; + 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); } - - if (area == 0) { -#if DEBUG_SHADOW - ALOGE("Area is 0!"); #endif - centroid.x = vertices[0].x; - centroid.y = vertices[0].y; - } else { - centroid.x = sumx / (3 * area); - centroid.y = sumy / (3 * area); - } } /** @@ -238,7 +168,7 @@ void AmbientShadow::calculateRayDirections(int rays, Vector2* dir) { * @param outRayDist Return the ray distance from centroid to the intersection. */ void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount, - const Vector2& start, const Vector2& dir, int& outEdgeIndex, + const Vector3& start, const Vector2& dir, int& outEdgeIndex, float& outEdgeFraction, float& outRayDist) { float startX = start.x; float startY = start.y; diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h index 079bdb7..20d1384 100644 --- a/libs/hwui/AmbientShadow.h +++ b/libs/hwui/AmbientShadow.h @@ -34,17 +34,15 @@ namespace uirenderer { */ class AmbientShadow { public: - static void createAmbientShadow(const Vector3* poly, int polyLength, int rays, - int layers, float strength, float heightFactor, float geomFactor, + static void createAmbientShadow(const Vector3* poly, int polyLength, + const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer); private: - static void calculatePolygonCentroid(const Vector3* poly, int len, Vector2& centroid); - static void calculateRayDirections(int rays, Vector2* dir); static void calculateIntersection(const Vector3* poly, int nbVertices, - const Vector2& start, const Vector2& dir, int& outEdgeIndex, + const Vector3& start, const Vector2& dir, int& outEdgeIndex, float& outEdgeFraction, float& outRayDist); static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir, diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 21cf658..1d58d96 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -23,6 +23,7 @@ #include "DisplayListRenderer.h" #include "Properties.h" #include "LayerRenderer.h" +#include "ShadowTessellator.h" namespace android { @@ -86,7 +87,7 @@ bool Caches::init() { mRegionMesh = NULL; mMeshIndices = 0; - + mShadowStripsIndices = 0; blend = false; lastSrcMode = GL_ZERO; lastDstMode = GL_ZERO; @@ -223,6 +224,9 @@ void Caches::terminate() { mMeshIndices = 0; mRegionMesh = NULL; + glDeleteBuffers(1, &mShadowStripsIndices); + mShadowStripsIndices = 0; + fboCache.clear(); programCache.clear(); @@ -404,7 +408,7 @@ bool Caches::unbindMeshBuffer() { return false; } -bool Caches::bindIndicesBuffer(const GLuint buffer) { +bool Caches::bindIndicesBufferInternal(const GLuint buffer) { if (mCurrentIndicesBuffer != buffer) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); mCurrentIndicesBuffer = buffer; @@ -413,7 +417,7 @@ bool Caches::bindIndicesBuffer(const GLuint buffer) { return false; } -bool Caches::bindIndicesBuffer() { +bool Caches::bindQuadIndicesBuffer() { if (!mMeshIndices) { uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6]; for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) { @@ -428,7 +432,7 @@ bool Caches::bindIndicesBuffer() { } glGenBuffers(1, &mMeshIndices); - bool force = bindIndicesBuffer(mMeshIndices); + bool force = bindIndicesBufferInternal(mMeshIndices); glBufferData(GL_ELEMENT_ARRAY_BUFFER, gMaxNumberOfQuads * 6 * sizeof(uint16_t), regionIndices, GL_STATIC_DRAW); @@ -436,7 +440,23 @@ bool Caches::bindIndicesBuffer() { return force; } - return bindIndicesBuffer(mMeshIndices); + return bindIndicesBufferInternal(mMeshIndices); +} + +bool Caches::bindShadowIndicesBuffer() { + if (!mShadowStripsIndices) { + uint16_t* shadowIndices = new uint16_t[SHADOW_INDEX_COUNT]; + ShadowTessellator::generateShadowIndices(shadowIndices); + glGenBuffers(1, &mShadowStripsIndices); + bool force = bindIndicesBufferInternal(mShadowStripsIndices); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, SHADOW_INDEX_COUNT * sizeof(uint16_t), + shadowIndices, GL_STATIC_DRAW); + + delete[] shadowIndices; + return force; + } + + return bindIndicesBufferInternal(mShadowStripsIndices); } bool Caches::unbindIndicesBuffer() { diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 2cc15cc..8c0c508 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -190,8 +190,8 @@ public: * Binds a global indices buffer that can draw up to * gMaxNumberOfQuads quads. */ - bool bindIndicesBuffer(); - bool bindIndicesBuffer(const GLuint buffer); + bool bindQuadIndicesBuffer(); + bool bindShadowIndicesBuffer(); bool unbindIndicesBuffer(); /** @@ -381,6 +381,8 @@ private: void initConstraints(); void initStaticProperties(); + bool bindIndicesBufferInternal(const GLuint buffer); + static void eventMarkNull(GLsizei length, const GLchar* marker) { } static void startMarkNull(GLsizei length, const GLchar* marker) { } static void endMarkNull() { } @@ -417,6 +419,7 @@ private: // Global index buffer GLuint mMeshIndices; + GLuint mShadowStripsIndices; mutable Mutex mGarbageLock; Vector mLayerGarbage; diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index b79a3b0..b52003c 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -498,7 +498,7 @@ void FontRenderer::issueDrawCommand(Vector& cacheTextures) { } checkTextureUpdate(); - caches.bindIndicesBuffer(); + caches.bindQuadIndicesBuffer(); if (!mDrawn) { // If returns true, a VBO was bound and we must diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 9b253a6..197cf50 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1907,7 +1907,7 @@ void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices, } else { force = mCaches.unbindMeshBuffer(); } - mCaches.bindIndicesBuffer(); + mCaches.bindQuadIndicesBuffer(); mCaches.bindPositionVertexPointer(force, vertices); if (mCaches.currentProgram->texCoords >= 0) { @@ -1917,7 +1917,7 @@ void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices, void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { bool force = mCaches.unbindMeshBuffer(); - mCaches.bindIndicesBuffer(); + mCaches.bindQuadIndicesBuffer(); mCaches.bindPositionVertexPointer(force, vertices, gVertexStride); } @@ -2392,10 +2392,9 @@ status_t OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, const SkPaint* paint, - bool useOffset) { +status_t OpenGLRenderer::drawVertexBuffer(VertexBufferMode mode, + const VertexBuffer& vertexBuffer, const SkPaint* paint, bool useOffset) { // not missing call to quickReject/dirtyLayer, always done at a higher level - if (!vertexBuffer.getVertexCount()) { // no vertices to draw return DrawGlInfo::kStatusDone; @@ -2421,19 +2420,24 @@ status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, cons bool force = mCaches.unbindMeshBuffer(); mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride); mCaches.resetTexCoordsVertexPointer(); - mCaches.unbindIndicesBuffer(); + int alphaSlot = -1; if (isAA) { void* alphaCoords = ((GLbyte*) vertices) + gVertexAlphaOffset; alphaSlot = mCaches.currentProgram->getAttrib("vtxAlpha"); - // TODO: avoid enable/disable in back to back uses of the alpha attribute glEnableVertexAttribArray(alphaSlot); glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords); } - glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount()); + if (mode == kVertexBufferMode_Standard) { + mCaches.unbindIndicesBuffer(); + glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount()); + } else { + mCaches.bindShadowIndicesBuffer(); + glDrawElements(GL_TRIANGLE_STRIP, SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0); + } if (isAA) { glDisableVertexAttribArray(alphaSlot); @@ -2462,7 +2466,7 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); } - return drawVertexBuffer(vertexBuffer, paint); + return drawVertexBuffer(kVertexBufferMode_Standard, vertexBuffer, paint); } /** @@ -2493,7 +2497,7 @@ status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); bool useOffset = !paint->isAntiAlias(); - return drawVertexBuffer(buffer, paint, useOffset); + return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset); } status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { @@ -2513,7 +2517,7 @@ status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPain dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); bool useOffset = !paint->isAntiAlias(); - return drawVertexBuffer(buffer, paint, useOffset); + return drawVertexBuffer(kVertexBufferMode_Standard, buffer, paint, useOffset); } status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { @@ -3236,13 +3240,20 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlp casterTransform.mapPoint3d(casterPolygon[i]); } + // map the centroid of the caster into 3d + Vector2 centroid = ShadowTessellator::centroid2d( + reinterpret_cast(casterVertices2d.array()), + casterVertexCount); + Vector3 centroid3d(centroid.x, centroid.y, 0); + casterTransform.mapPoint3d(centroid3d); + // draw caster's shadows if (mCaches.propertyAmbientShadowStrength > 0) { paint.setARGB(mCaches.propertyAmbientShadowStrength, 0, 0, 0); VertexBuffer ambientShadowVertexBuffer; ShadowTessellator::tessellateAmbientShadow(casterPolygon, casterVertexCount, - ambientShadowVertexBuffer); - drawVertexBuffer(ambientShadowVertexBuffer, &paint); + centroid3d, ambientShadowVertexBuffer); + drawVertexBuffer(kVertexBufferMode_Shadow, ambientShadowVertexBuffer, &paint); } if (mCaches.propertySpotShadowStrength > 0) { @@ -3254,7 +3265,7 @@ status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlp lightPosScale, *currentTransform(), getWidth(), getHeight(), spotShadowVertexBuffer); - drawVertexBuffer(spotShadowVertexBuffer, &paint); + drawVertexBuffer(kVertexBufferMode_Shadow, spotShadowVertexBuffer, &paint); } return DrawGlInfo::kStatusDrew; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index e4d133d..03beae3 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -117,6 +117,11 @@ enum ModelViewMode { kModelViewMode_TranslateAndScale = 1, }; +enum VertexBufferMode { + kVertexBufferMode_Standard = 0, + kVertexBufferMode_Shadow = 1 +}; + /////////////////////////////////////////////////////////////////////////////// // Renderer /////////////////////////////////////////////////////////////////////////////// @@ -629,8 +634,8 @@ private: * @param paint The paint to render with * @param useOffset Offset the vertexBuffer (used in drawing non-AA lines) */ - status_t drawVertexBuffer(const VertexBuffer& vertexBuffer, const SkPaint* paint, - bool useOffset = false); + status_t drawVertexBuffer(VertexBufferMode mode, const VertexBuffer& vertexBuffer, + const SkPaint* paint, bool useOffset = false); /** * Renders the convex hull defined by the specified path as a strip of polygons. diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index 7700ea0..526772b 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -31,18 +31,16 @@ static inline T max(T a, T b) { return a > b ? a : b; } -void ShadowTessellator::tessellateAmbientShadow(const Vector3* casterPolygon, int casterVertexCount, +void ShadowTessellator::tessellateAmbientShadow(const Vector3* casterPolygon, + int casterVertexCount, const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer) { // A bunch of parameters to tweak the shadow. // TODO: Allow some of these changable by debug settings or APIs. - const int rays = 128; - const int layers = 2; - const float strength = 0.5; const float heightFactor = 128; const float geomFactor = 64; - AmbientShadow::createAmbientShadow(casterPolygon, casterVertexCount, rays, layers, strength, - heightFactor, geomFactor, shadowVertexBuffer); + AmbientShadow::createAmbientShadow(casterPolygon, casterVertexCount, + centroid3d, heightFactor, geomFactor, shadowVertexBuffer); } @@ -51,9 +49,6 @@ void ShadowTessellator::tessellateSpotShadow(const Vector3* casterPolygon, int c int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) { // A bunch of parameters to tweak the shadow. // TODO: Allow some of these changable by debug settings or APIs. - const int rays = 256; - const int layers = 2; - const float strength = 0.5; int maximal = max(screenWidth, screenHeight); Vector3 lightCenter(screenWidth * lightPosScale.x, screenHeight * lightPosScale.y, maximal * lightPosScale.z); @@ -70,9 +65,77 @@ void ShadowTessellator::tessellateSpotShadow(const Vector3* casterPolygon, int c const float lightSize = maximal / 4; const int lightVertexCount = 16; - SpotShadow::createSpotShadow(casterPolygon, casterVertexCount, lightCenter, lightSize, - lightVertexCount, rays, layers, strength, shadowVertexBuffer); + SpotShadow::createSpotShadow(casterPolygon, casterVertexCount, lightCenter, + lightSize, lightVertexCount, shadowVertexBuffer); } + +void ShadowTessellator::generateShadowIndices(uint16_t* shadowIndices) { + int currentIndex = 0; + const int layers = SHADOW_LAYER_COUNT; + const int rays = SHADOW_RAY_COUNT; + // For the penumbra area. + for (int i = 0; i < layers; i++) { + for (int j = 0; j < rays; j++) { + shadowIndices[currentIndex++] = i * rays + j; + shadowIndices[currentIndex++] = (i + 1) * rays + j; + } + // To close the loop, back to the ray 0. + shadowIndices[currentIndex++] = i * rays; + shadowIndices[currentIndex++] = (i + 1) * rays; + } + uint16_t base = layers * rays; + uint16_t centroidIndex = (layers + 1) * rays; + // For the umbra area, using strips to simulate the fans. + for (int k = 0; k < rays; k++) { + shadowIndices[currentIndex++] = base + k; + shadowIndices[currentIndex++] = centroidIndex; + } + shadowIndices[currentIndex++] = base; + +#if DEBUG_SHADOW + if (currentIndex != SHADOW_INDEX_COUNT) { + ALOGE("vertex index count is wrong. current %d, expected %d", + currentIndex, SHADOW_INDEX_COUNT); + } + for (int i = 0; i < SHADOW_INDEX_COUNT; i++) { + ALOGD("vertex index is (%d, %d)", i, shadowIndices[i]); + } +#endif +} + +/** + * Calculate the centroid of a 2d polygon. + * + * @param poly The polygon, which is represented in a Vector2 array. + * @param polyLength The length of the polygon in terms of number of vertices. + * @return the centroid of the polygon. + */ +Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { + double sumx = 0; + double sumy = 0; + int p1 = polyLength - 1; + double area = 0; + for (int p2 = 0; p2 < polyLength; p2++) { + double x1 = poly[p1].x; + double y1 = poly[p1].y; + double x2 = poly[p2].x; + double y2 = poly[p2].y; + double a = (x1 * y2 - x2 * y1); + sumx += (x1 + x2) * a; + sumy += (y1 + y2) * a; + area += a; + p1 = p2; + } + + Vector2 centroid = poly[0]; + if (area != 0) { + centroid = Vector2(sumx / (3 * area), sumy / (3 * area)); + } else { + ALOGE("Area is 0 while computing centroid!"); + } + return centroid; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h index ef95609..c49fdcb 100644 --- a/libs/hwui/ShadowTessellator.h +++ b/libs/hwui/ShadowTessellator.h @@ -20,18 +20,57 @@ #include "Debug.h" #include "Matrix.h" +#include "VertexBuffer.h" namespace android { namespace uirenderer { +// All SHADOW_* are used to define all the geometry property of shadows. +// Use a simplified example to illustrate the geometry setup here. +// 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. +// +// 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 +// +// 3 + +// The total number of rays starting from the centroid of shadow area, in order +// to generate the shadow geometry. +#define SHADOW_RAY_COUNT 256 + +// The total number of layers in the outer shadow area, 1 being the minimum. +#define SHADOW_LAYER_COUNT 2 + +// The total number of all the vertices representing the shadow. +#define SHADOW_VERTEX_COUNT ((SHADOW_LAYER_COUNT + 1) * SHADOW_RAY_COUNT + 1) + +// 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) * \ + SHADOW_LAYER_COUNT) + class ShadowTessellator { public: - static void tessellateAmbientShadow(const Vector3* casterPolygon, int casterVertexCount, + static void tessellateAmbientShadow(const Vector3* casterPolygon, + int casterVertexCount, const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer); static void tessellateSpotShadow(const Vector3* casterPolygon, int casterVertexCount, const Vector3& lightPosScale, const mat4& receiverTransform, int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer); + + static void generateShadowIndices(uint16_t* shadowIndices); + + static Vector2 centroid2d(const Vector2* poly, int polyLength); }; // ShadowTessellator }; // namespace uirenderer diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index 4c2299e..22d735b 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -22,6 +22,7 @@ #include #include +#include "ShadowTessellator.h" #include "SpotShadow.h" #include "Vertex.h" @@ -70,35 +71,6 @@ float SpotShadow::rayIntersectPoly(const Vector2* poly, int polyLength, } /** - * Calculate the centroid of a 2d polygon. - * - * @param poly The polygon, which is represented in a Vector2 array. - * @param polyLength The length of the polygon in terms of number of vertices. - * @return the centroid of the polygon. - */ -Vector2 SpotShadow::centroid2d(const Vector2* poly, int polyLength) { - double sumx = 0; - double sumy = 0; - int p1 = polyLength - 1; - double area = 0; - for (int p2 = 0; p2 < polyLength; p2++) { - double x1 = poly[p1].x; - double y1 = poly[p1].y; - double x2 = poly[p2].x; - double y2 = poly[p2].y; - double a = (x1 * y2 - x2 * y1); - sumx += (x1 + x2) * a; - sumy += (y1 + y2) * a; - area += a; - p1 = p2; - } - - double centroidx = sumx / (3 * area); - double centroidy = sumy / (3 * area); - return Vector2((float)centroidx, (float)centroidy); -} - -/** * Sort points by their X coordinates * * @param points the points as a Vector2 array. @@ -550,20 +522,17 @@ void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, * @param lightCenter the center of the light * @param lightSize the radius of the light source * @param lightVertexCount the vertex counter for the light polygon -* @param rays the number of vertexes to create along the edges of the shadow -* @param layers the number of layers of triangles strips to create -* @param strength the "darkness" of the shadow * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return * empty strip if error. * */ void SpotShadow::createSpotShadow(const Vector3* poly, int polyLength, const Vector3& lightCenter, float lightSize, int lightVertexCount, - int rays, int layers, float strength, VertexBuffer& retStrips) { + VertexBuffer& retStrips) { Vector3 light[lightVertexCount * 3]; computeLightPolygon(lightVertexCount, lightCenter, lightSize, light); - computeSpotShadow(light, lightVertexCount, lightCenter, - poly, polyLength, rays, layers, strength, retStrips); + computeSpotShadow(light, lightVertexCount, lightCenter, poly, polyLength, + retStrips); } /** @@ -573,15 +542,12 @@ void SpotShadow::createSpotShadow(const Vector3* poly, int polyLength, * @param lightPolyLength number of vertexes of the light source polygon * @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 rays the number of vertexes to create along the edges of the shadow - * @param layers the number of layers of triangles strips to create - * @param strength the "darkness" of the shadow * @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, - int rays, int layers, float strength, VertexBuffer& shadowTriangleStrip) { + VertexBuffer& shadowTriangleStrip) { // Point clouds for all the shadowed vertices Vector2 shadowRegion[lightPolyLength * polyLength]; // Shadow polygon from one point light. @@ -671,7 +637,8 @@ void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength // Shrink the centroid's shadow by 10%. // TODO: Study the magic number of 10%. - Vector2 shadowCentroid = centroid2d(fakeUmbra, polyLength); + Vector2 shadowCentroid = + ShadowTessellator::centroid2d(fakeUmbra, polyLength); for (int i = 0; i < polyLength; i++) { fakeUmbra[i] = shadowCentroid * (1.0f - SHADOW_SHRINK_SCALE) + fakeUmbra[i] * SHADOW_SHRINK_SCALE; @@ -686,7 +653,7 @@ void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength } generateTriangleStrip(penumbra, penumbraLength, umbra, umbraLength, - rays, layers, strength, shadowTriangleStrip); + shadowTriangleStrip); } /** @@ -696,22 +663,18 @@ void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength * @param penumbraLength The number of vertexes in the outer polygon * @param umbra The inner outer polygon x,y vertexes * @param umbraLength The number of vertexes in the inner polygon - * @param rays The number of points along the polygons to create - * @param layers The number of layers of triangle strips between the umbra and penumbra - * @param strength The max alpha of the umbra * @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, int rays, int layers, - float strength, VertexBuffer& shadowTriangleStrip) { - - int rings = layers + 1; - int size = rays * rings; + const Vector2* umbra, int umbraLength, VertexBuffer& shadowTriangleStrip) { + const int rays = SHADOW_RAY_COUNT; + const int layers = SHADOW_LAYER_COUNT; + int size = rays * (layers + 1); float step = M_PI * 2 / rays; // Centroid of the umbra. - Vector2 centroid = centroid2d(umbra, umbraLength); + Vector2 centroid = ShadowTessellator::centroid2d(umbra, umbraLength); #if DEBUG_SHADOW ALOGD("centroid2d = %f , %f", centroid.x, centroid.y); #endif @@ -741,57 +704,29 @@ void SpotShadow::generateTriangleStrip(const Vector2* penumbra, int penumbraLeng int stripSize = getStripSize(rays, layers); AlphaVertex* shadowVertices = shadowTriangleStrip.alloc(stripSize); int currentIndex = 0; - int firstInLayer = 0; - // Calculate the vertex values in the penumbra area. - for (int r = 0; r < layers; r++) { - firstInLayer = currentIndex; - for (int i = 0; i < rays; i++) { - float dx = sinf(step * i); - float dy = cosf(step * i); - - for (int j = r; j < (r + 2); j++) { - float layerRatio = j / (float)(rings - 1); - float deltaDist = layerRatio * (umbraDistPerRay[i] - penumbraDistPerRay[i]); - float currentDist = penumbraDistPerRay[i] + deltaDist; - float op = calculateOpacity(layerRatio, deltaDist); - AlphaVertex::set(&shadowVertices[currentIndex++], - dx * currentDist + centroid.x, - dy * currentDist + centroid.y, - layerRatio * op * strength); - } - } - - // Duplicate the vertices from one layer to another one to make triangle - // strip. - shadowVertices[currentIndex++] = shadowVertices[firstInLayer + 0]; - shadowVertices[currentIndex++] = shadowVertices[firstInLayer + 1]; - } - int lastInPenumbra = currentIndex - 1; - shadowVertices[currentIndex++] = shadowVertices[lastInPenumbra]; - - // Preallocate the vertices (index as [firstInUmbra - 1]) for jumping from - // the penumbra to umbra. - currentIndex++; - int firstInUmbra = currentIndex; - - // traverse the umbra area in a zig zag pattern for strips. - const int innerRingStartIndex = firstInLayer + 1; - for (int k = 0; k < rays; k++) { - int i = k / 2; - if ((k & 1) == 1) { - i = rays - i - 1; + // Calculate the vertices (x, y, alpha) in the shadow area. + for (int layerIndex = 0; layerIndex <= layers; layerIndex++) { + for (int rayIndex = 0; rayIndex < rays; rayIndex++) { + float dx = sinf(step * rayIndex); + float dy = cosf(step * rayIndex); + float layerRatio = layerIndex / (float) layers; + float deltaDist = layerRatio * + (umbraDistPerRay[rayIndex] - penumbraDistPerRay[rayIndex]); + float currentDist = penumbraDistPerRay[rayIndex] + deltaDist; + float op = calculateOpacity(layerRatio); + AlphaVertex::set(&shadowVertices[currentIndex++], + dx * currentDist + centroid.x, dy * currentDist + centroid.y, op); } - // copy already computed values for umbra vertices - shadowVertices[currentIndex++] = shadowVertices[innerRingStartIndex + i * 2]; } - - // Back fill the one vertex for jumping from penumbra to umbra. - shadowVertices[firstInUmbra - 1] = shadowVertices[firstInUmbra]; - + // The centroid is in the umbra area, so the opacity is considered as 1.0. + AlphaVertex::set(&shadowVertices[currentIndex++], centroid.x, centroid.y, 1.0); #if DEBUG_SHADOW + if (currentIndex != SHADOW_VERTEX_COUNT) { + ALOGE("number of vertex generated for spot shadow is wrong!"); + } for (int i = 0; i < currentIndex; i++) { - ALOGD("shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, + ALOGD("spot shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, shadowVertices[i].y, shadowVertices[i].alpha); } #endif @@ -819,17 +754,15 @@ void SpotShadow::smoothPolygon(int level, int rays, float* rayDist) { } /** - * Calculate the opacity according to the distance and falloff ratio. + * Calculate the opacity according to the distance. Ideally, the opacity is 1.0 + * in the umbra area, and fall off to 0.0 till the edge of penumbra area. * - * @param distRatio The distance ratio of current sample between umbra and - * penumbra area. - * @param deltaDist The distance between current sample to the penumbra area. + * @param layerRatio The distance ratio of current sample between umbra and penumbra area. + * Penumbra edge is 0 and umbra edge is 1. * @return The opacity according to the distance between umbra and penumbra. */ -float SpotShadow::calculateOpacity(float distRatio, float deltaDist) { - // TODO: Experiment on the opacity calculation. - float falloffRatio = 1 + deltaDist * deltaDist; - return (distRatio + 1 - 1 / falloffRatio) / 2; +float SpotShadow::calculateOpacity(float layerRatio) { + return (layerRatio * layerRatio + layerRatio) / 2.0; } /** diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h index a50d110..6727eac 100644 --- a/libs/hwui/SpotShadow.h +++ b/libs/hwui/SpotShadow.h @@ -28,24 +28,22 @@ class SpotShadow { public: static void createSpotShadow(const Vector3* poly, int polyLength, const Vector3& lightCenter, float lightSize, int lightVertexCount, - int rays, int layers, float strength, VertexBuffer& retStrips); + VertexBuffer& retStrips); private: static void computeSpotShadow(const Vector3* lightPoly, int lightPolyLength, const Vector3& lightCenter, const Vector3* poly, int polyLength, - int rays, int layers, float strength, VertexBuffer& retstrips); + VertexBuffer& retstrips); static void computeLightPolygon(int points, const Vector3& lightCenter, float size, Vector3* ret); static int getStripSize(int rays, int layers); static void smoothPolygon(int level, int rays, float* rayDist); - static float calculateOpacity(float jf, float deltaDist); + static float calculateOpacity(float jf); static float rayIntersectPoly(const Vector2* poly, int polyLength, const Vector2& point, float dx, float dy); - static Vector2 centroid2d(const Vector2* poly, int polyLength); - 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); @@ -65,8 +63,7 @@ private: double x3, double y3, double x4, double y4, Vector2& ret); static void generateTriangleStrip(const Vector2* penumbra, int penumbraLength, - const Vector2* umbra, int umbraLength, int rays, int layers, - float strength, VertexBuffer& retstrips); + const Vector2* umbra, int umbraLength, VertexBuffer& retstrips); static const double EPSILON = 1e-7; -- cgit v1.1