Ambient shadow tessellation improvement.

Using the vertices, instead of ray casting for the triangulation.

This request a dynamic index buffer associated with vertex buffer,
so we update the VertexBuffer to support it.

The ambient shadow could be 3x-6x times faster for circle and rect now.

b/16712006
b/14257173

Change-Id: I2f22a8fe19bc59acee5c18e4a3a395acd5042a66
This commit is contained in:
ztenghui
2014-08-13 15:48:02 -07:00
parent c50a03d78a
commit d5e8ade498
5 changed files with 344 additions and 270 deletions

View File

@@ -16,6 +16,43 @@
#define LOG_TAG "OpenGLRenderer"
/**
* Extra vertices for the corner for smoother corner.
* Only for outer vertices.
* Note that we use such extra memory to avoid an extra loop.
*/
// For half circle, we could add EXTRA_VERTEX_PER_PI vertices.
// Set to 1 if we don't want to have any.
#define EXTRA_CORNER_VERTEX_PER_PI 12
// For the whole polygon, the sum of all the deltas b/t normals is 2 * M_PI,
// therefore, the maximum number of extra vertices will be twice bigger.
#define MAX_EXTRA_CORNER_VERTEX_NUMBER (2 * EXTRA_CORNER_VERTEX_PER_PI)
// For each RADIANS_DIVISOR, we would allocate one more vertex b/t the normals.
#define CORNER_RADIANS_DIVISOR (M_PI / EXTRA_CORNER_VERTEX_PER_PI)
/**
* Extra vertices for the Edge for interpolation artifacts.
* Same value for both inner and outer vertices.
*/
#define EXTRA_EDGE_VERTEX_PER_PI 50
#define MAX_EXTRA_EDGE_VERTEX_NUMBER (2 * EXTRA_EDGE_VERTEX_PER_PI)
#define EDGE_RADIANS_DIVISOR (M_PI / EXTRA_EDGE_VERTEX_PER_PI)
/**
* Other constants:
*/
// For the edge of the penumbra, the opacity is 0.
#define OUTER_OPACITY (0.0f)
// Once the alpha difference is greater than this threshold, we will allocate extra
// edge vertices.
// If this is set to negative value, then all the edge will be tessellated.
#define ALPHA_THRESHOLD (0.1f / 255.0f)
#include <math.h>
#include <utils/Log.h>
#include <utils/Vector.h>
@@ -23,10 +60,96 @@
#include "AmbientShadow.h"
#include "ShadowTessellator.h"
#include "Vertex.h"
#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
/**
* Local utility functions.
*/
inline Vector2 getNormalFromVertices(const Vector3* vertices, int current, int next) {
// Convert from Vector3 to Vector2 first.
Vector2 currentVertex = { vertices[current].x, vertices[current].y };
Vector2 nextVertex = { vertices[next].x, vertices[next].y };
return ShadowTessellator::calculateNormal(currentVertex, nextVertex);
}
// The input z value will be converted to be non-negative inside.
// The output must be ranged from 0 to 1.
inline float getAlphaFromFactoredZ(float factoredZ) {
return 1.0 / (1 + MathUtils::max(factoredZ, 0.0f));
}
inline float getTransformedAlphaFromAlpha(float alpha) {
return acosf(1.0f - 2.0f * alpha);
}
// The output is ranged from 0 to M_PI.
inline float getTransformedAlphaFromFactoredZ(float factoredZ) {
return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ));
}
inline int getExtraVertexNumber(const Vector2& vector1, const Vector2& vector2,
float divisor) {
// The formula is :
// extraNumber = floor(acos(dot(n1, n2)) / (M_PI / EXTRA_VERTEX_PER_PI))
// The value ranges for each step are:
// dot( ) --- [-1, 1]
// acos( ) --- [0, M_PI]
// floor(...) --- [0, EXTRA_VERTEX_PER_PI]
float dotProduct = vector1.dot(vector2);
// TODO: Use look up table for the dotProduct to extraVerticesNumber
// computation, if needed.
float angle = acosf(dotProduct);
return (int) floor(angle / divisor);
}
inline void checkOverflow(int used, int total, const char* bufferName) {
LOG_ALWAYS_FATAL_IF(used > total, "Error: %s overflow!!! used %d, total %d",
bufferName, used, total);
}
inline int getEdgeExtraAndUpdateSpike(Vector2* currentSpike,
const Vector3& secondVertex, const Vector3& centroid) {
Vector2 secondSpike = {secondVertex.x - centroid.x, secondVertex.y - centroid.y};
secondSpike.normalize();
int result = getExtraVertexNumber(secondSpike, *currentSpike, EDGE_RADIANS_DIVISOR);
*currentSpike = secondSpike;
return result;
}
// Given the caster's vertex count, compute all the buffers size depending on
// whether or not the caster is opaque.
inline void computeBufferSize(int* totalVertexCount, int* totalIndexCount,
int* totalUmbraCount, int casterVertexCount, bool isCasterOpaque) {
// Compute the size of the vertex buffer.
int outerVertexCount = casterVertexCount * 2 + MAX_EXTRA_CORNER_VERTEX_NUMBER +
MAX_EXTRA_EDGE_VERTEX_NUMBER;
int innerVertexCount = casterVertexCount + MAX_EXTRA_EDGE_VERTEX_NUMBER;
*totalVertexCount = outerVertexCount + innerVertexCount;
// Compute the size of the index buffer.
*totalIndexCount = 2 * outerVertexCount + 2;
// Compute the size of the umber buffer.
// For translucent object, keep track of the umbra(inner) vertex in order to draw
// inside. We only need to store the index information.
*totalUmbraCount = 0;
if (!isCasterOpaque) {
// Add the centroid if occluder is translucent.
*totalVertexCount++;
*totalIndexCount += 2 * innerVertexCount + 1;
*totalUmbraCount = innerVertexCount;
}
}
inline bool needsExtraForEdge(float firstAlpha, float secondAlpha) {
return abs(firstAlpha - secondAlpha) > ALPHA_THRESHOLD;
}
/**
* Calculate the shadows as a triangle strips while alpha value as the
* shadow values.
@@ -43,290 +166,198 @@ namespace uirenderer {
*
* @param shadowVertexBuffer Return an floating point array of (x, y, a)
* triangle strips mode.
*
* An simple illustration:
* For now let's mark the outer vertex as Pi, the inner as Vi, the centroid as C.
*
* First project the occluder to the Z=0 surface.
* Then we got all the inner vertices. And we compute the normal for each edge.
* According to the normal, we generate outer vertices. E.g: We generate P1 / P4
* as extra corner vertices to make the corner looks round and smoother.
*
* Due to the fact that the alpha is not linear interpolated along the inner
* edge, when the alpha is different, we may add extra vertices such as P2.1, P2.2,
* V0.1, V0.2 to avoid the visual artifacts.
*
* (P3)
* (P2) (P2.1) (P2.2) | ' (P4)
* (P1)' | | | | '
* ' | | | | '
* (P0) ------------------------------------------------(P5)
* | (V0) (V0.1) (V0.2) |(V1)
* | |
* | |
* | (C) |
* | |
* | |
* | |
* | |
* (V3)-----------------------------------(V2)
*/
void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
const Vector3* vertices, int vertexCount, const Vector3& centroid3d,
const Vector3* casterVertices, int casterVertexCount, const Vector3& centroid3d,
float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) {
const int rays = SHADOW_RAY_COUNT;
// Validate the inputs.
if (vertexCount < 3 || heightFactor <= 0 || rays <= 0
|| geomFactor <= 0) {
#if DEBUG_SHADOW
ALOGW("Invalid input for createAmbientShadow(), early return!");
#endif
return;
}
shadowVertexBuffer.setMode(VertexBuffer::kIndices);
Vector<Vector2> dir; // TODO: use C++11 unique_ptr
dir.setCapacity(rays);
float rayDist[rays];
float rayHeight[rays];
calculateRayDirections(rays, vertices, vertexCount, centroid3d, dir.editArray());
// In order to computer the outer vertices in one loop, we need pre-compute
// the normal by the vertex (n - 1) to vertex 0, and the spike and alpha value
// for vertex 0.
Vector2 previousNormal = getNormalFromVertices(casterVertices,
casterVertexCount - 1 , 0);
Vector2 currentSpike = {casterVertices[0].x - centroid3d.x,
casterVertices[0].y - centroid3d.y};
currentSpike.normalize();
float currentAlpha = getAlphaFromFactoredZ(casterVertices[0].z * heightFactor);
// Calculate the length and height of the points along the edge.
//
// The math here is:
// Intersect each ray (starting from the centroid) with the polygon.
for (int i = 0; i < rays; i++) {
int edgeIndex;
float edgeFraction;
float rayDistance;
calculateIntersection(vertices, vertexCount, centroid3d, dir[i], edgeIndex,
edgeFraction, rayDistance);
rayDist[i] = rayDistance;
if (edgeIndex < 0 || edgeIndex >= vertexCount) {
#if DEBUG_SHADOW
ALOGW("Invalid edgeIndex!");
#endif
edgeIndex = 0;
}
float h1 = vertices[edgeIndex].z;
float h2 = vertices[((edgeIndex + 1) % vertexCount)].z;
rayHeight[i] = h1 + edgeFraction * (h2 - h1);
}
// The output buffer length basically is roughly rays * layers, but since we
// need triangle strips, so we need to duplicate vertices to accomplish that.
// Preparing all the output data.
int totalVertexCount, totalIndexCount, totalUmbraCount;
computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount,
casterVertexCount, isCasterOpaque);
AlphaVertex* shadowVertices =
shadowVertexBuffer.alloc<AlphaVertex>(SHADOW_VERTEX_COUNT);
shadowVertexBuffer.alloc<AlphaVertex>(totalVertexCount);
int vertexBufferIndex = 0;
uint16_t* indexBuffer = shadowVertexBuffer.allocIndices<uint16_t>(totalIndexCount);
int indexBufferIndex = 0;
uint16_t umbraVertices[totalUmbraCount];
int umbraIndex = 0;
// Calculate the vertex of the shadows.
//
// The math here is:
// Along the edges of the polygon, for each intersection point P (generated above),
// 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 = {centroid3d.x, centroid3d.y};
for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
Vector2 normal = {1.0f, 0.0f};
calculateNormal(rays, rayIndex, dir.array(), rayDist, normal);
for (int i = 0; i < casterVertexCount; i++) {
// Corner: first figure out the extra vertices we need for the corner.
const Vector3& innerVertex = casterVertices[i];
Vector2 currentNormal = getNormalFromVertices(casterVertices, i,
(i + 1) % casterVertexCount);
// The vertex should be start from rayDist[i] then scale the
// normalizeNormal!
Vector2 intersection = dir[rayIndex] * rayDist[rayIndex] +
centroid2d;
// outer ring of points, expanded based upon height of each ray intersection
float expansionDist = rayHeight[rayIndex] * heightFactor *
geomFactor;
AlphaVertex::set(&shadowVertices[rayIndex],
intersection.x + normal.x * expansionDist,
intersection.y + normal.y * expansionDist,
0.0f);
// inner ring of points
float opacity = 1.0 / (1 + rayHeight[rayIndex] * heightFactor);
// NOTE: Shadow alpha values are transformed when stored in alphavertices,
// so that they can be consumed directly by gFS_Main_ApplyVertexAlphaShadowInterp
float transformedOpacity = acos(1.0f - 2.0f * opacity);
AlphaVertex::set(&shadowVertices[rays + rayIndex],
intersection.x,
intersection.y,
transformedOpacity);
}
if (isCasterOpaque) {
// skip inner ring, calc bounds over filled portion of buffer
shadowVertexBuffer.computeBounds<AlphaVertex>(2 * rays);
shadowVertexBuffer.setMode(VertexBuffer::kOnePolyRingShadow);
} else {
// If caster isn't opaque, we need to to fill the umbra by storing the umbra's
// centroid in the innermost ring of vertices.
float centroidAlpha = 1.0 / (1 + centroid3d.z * heightFactor);
AlphaVertex centroidXYA;
AlphaVertex::set(&centroidXYA, centroid2d.x, centroid2d.y, centroidAlpha);
for (int rayIndex = 0; rayIndex < rays; rayIndex++) {
shadowVertices[2 * rays + rayIndex] = centroidXYA;
}
// calc bounds over entire buffer
shadowVertexBuffer.computeBounds<AlphaVertex>();
shadowVertexBuffer.setMode(VertexBuffer::kTwoPolyRingShadow);
}
int extraVerticesNumber = getExtraVertexNumber(currentNormal, previousNormal,
CORNER_RADIANS_DIVISOR);
float expansionDist = innerVertex.z * heightFactor * geomFactor;
const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1.
#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);
}
ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber);
#endif
}
/**
* Generate an array of rays' direction vectors.
* To make sure the vertices generated are clockwise, the directions are from PI
* to -PI.
*
* @param rays The number of rays shooting out from the centroid.
* @param vertices Vertices of the polygon.
* @param vertexCount The number of vertices.
* @param centroid3d The centroid of the polygon.
* @param dir Return the array of ray vectors.
*/
void AmbientShadow::calculateRayDirections(const int rays, const Vector3* vertices,
const int vertexCount, const Vector3& centroid3d, Vector2* dir) {
// If we don't have enough rays, then fall back to the uniform distribution.
if (vertexCount * 2 > rays) {
float deltaAngle = 2 * M_PI / rays;
for (int i = 0; i < rays; i++) {
dir[i].x = cosf(M_PI - deltaAngle * i);
dir[i].y = sinf(M_PI - deltaAngle * i);
// Corner: fill the corner Vertex Buffer(VB) and Index Buffer(IB).
// We fill the inner vertex first, such that we can fill the index buffer
// inside the loop.
int currentInnerVertexIndex = vertexBufferIndex;
if (!isCasterOpaque) {
umbraVertices[umbraIndex++] = vertexBufferIndex;
}
return;
}
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x,
casterVertices[i].y,
getTransformedAlphaFromAlpha(currentAlpha));
// If we have enough rays, then we assign each vertices a ray, and distribute
// the rest uniformly.
float rayThetas[rays];
const Vector3& innerStart = casterVertices[i];
const int uniformRayCount = rays - vertexCount;
const float deltaAngle = 2 * M_PI / uniformRayCount;
// outerStart is the first outer vertex for this inner vertex.
// outerLast is the last outer vertex for this inner vertex.
Vector2 outerStart = {0, 0};
Vector2 outerLast = {0, 0};
// This will create vertices from [0, cornerSlicesNumber] inclusively,
// which means minimally 2 vertices even without the extra ones.
for (int j = 0; j <= cornerSlicesNumber; j++) {
Vector2 averageNormal =
previousNormal * (cornerSlicesNumber - j) + currentNormal * j;
averageNormal /= cornerSlicesNumber;
averageNormal.normalize();
Vector2 outerVertex;
outerVertex.x = innerVertex.x + averageNormal.x * expansionDist;
outerVertex.y = innerVertex.y + averageNormal.y * expansionDist;
// We have to generate all the vertices' theta anyway and we also need to
// find the minimal, so let's precompute it first.
// Since the incoming polygon is clockwise, we can find the dip to identify
// the minimal theta.
float polyThetas[vertexCount];
int maxPolyThetaIndex = 0;
for (int i = 0; i < vertexCount; i++) {
polyThetas[i] = atan2(vertices[i].y - centroid3d.y,
vertices[i].x - centroid3d.x);
if (i > 0 && polyThetas[i] > polyThetas[i - 1]) {
maxPolyThetaIndex = i;
}
}
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
indexBuffer[indexBufferIndex++] = currentInnerVertexIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x,
outerVertex.y, OUTER_OPACITY);
// Both poly's thetas and uniform thetas are in decrease order(clockwise)
// from PI to -PI.
int polyThetaIndex = maxPolyThetaIndex;
float polyTheta = polyThetas[maxPolyThetaIndex];
int uniformThetaIndex = 0;
float uniformTheta = M_PI;
for (int i = 0; i < rays; i++) {
// Compare both thetas and pick the smaller one and move on.
bool hasThetaCollision = abs(polyTheta - uniformTheta) < MINIMAL_DELTA_THETA;
if (polyTheta > uniformTheta || hasThetaCollision) {
if (hasThetaCollision) {
// Shift the uniformTheta to middle way between current polyTheta
// and next uniform theta. The next uniform theta can wrap around
// to exactly PI safely here.
// Note that neither polyTheta nor uniformTheta can be FLT_MAX
// due to the hasThetaCollision is true.
uniformTheta = (polyTheta + M_PI - deltaAngle * (uniformThetaIndex + 1)) / 2;
#if DEBUG_SHADOW
ALOGD("Shifted uniformTheta to %f", uniformTheta);
#endif
}
rayThetas[i] = polyTheta;
polyThetaIndex = (polyThetaIndex + 1) % vertexCount;
if (polyThetaIndex != maxPolyThetaIndex) {
polyTheta = polyThetas[polyThetaIndex];
} else {
// out of poly points.
polyTheta = - FLT_MAX;
}
} else {
rayThetas[i] = uniformTheta;
uniformThetaIndex++;
if (uniformThetaIndex < uniformRayCount) {
uniformTheta = M_PI - deltaAngle * uniformThetaIndex;
} else {
// out of uniform points.
uniformTheta = - FLT_MAX;
if (j == 0) {
outerStart = outerVertex;
} else if (j == cornerSlicesNumber) {
outerLast = outerVertex;
}
}
}
previousNormal = currentNormal;
for (int i = 0; i < rays; i++) {
// Edge: first figure out the extra vertices needed for the edge.
const Vector3& innerNext = casterVertices[(i + 1) % casterVertexCount];
float nextAlpha = getAlphaFromFactoredZ(innerNext.z * heightFactor);
if (needsExtraForEdge(currentAlpha, nextAlpha)) {
// TODO: See if we can / should cache this outer vertex across the loop.
Vector2 outerNext;
float expansionDist = innerNext.z * heightFactor * geomFactor;
outerNext.x = innerNext.x + currentNormal.x * expansionDist;
outerNext.y = innerNext.y + currentNormal.y * expansionDist;
// Compute the angle and see how many extra points we need.
int extraVerticesNumber = getEdgeExtraAndUpdateSpike(&currentSpike,
innerNext, centroid3d);
#if DEBUG_SHADOW
ALOGD("No. %d : %f", i, rayThetas[i] * 180 / M_PI);
ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i);
#endif
// TODO: Fix the intersection precision problem and remvoe the delta added
// here.
dir[i].x = cosf(rayThetas[i] + MINIMAL_DELTA_THETA);
dir[i].y = sinf(rayThetas[i] + MINIMAL_DELTA_THETA);
}
}
// Edge: fill the edge's VB and IB.
// This will create vertices pair from [1, extraVerticesNumber - 1].
// If there is no extra vertices created here, the edge will be drawn
// as just 2 triangles.
for (int k = 1; k < extraVerticesNumber; k++) {
int startWeight = extraVerticesNumber - k;
Vector2 currentOuter =
(outerLast * startWeight + outerNext * k) / extraVerticesNumber;
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentOuter.x,
currentOuter.y, OUTER_OPACITY);
/**
* Calculate the intersection of a ray hitting the polygon.
*
* @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 vertices.
* @param start The starting point of the ray.
* @param dir The direction vector of the ray.
*
* @param outEdgeIndex Return the index of the segment (or index of the starting
* vertex) that ray intersect with.
* @param outEdgeFraction Return the fraction offset from the segment starting
* index.
* @param outRayDist Return the ray distance from centroid to the intersection.
*/
void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount,
const Vector3& start, const Vector2& dir, int& outEdgeIndex,
float& outEdgeFraction, float& outRayDist) {
float startX = start.x;
float startY = start.y;
float dirX = dir.x;
float dirY = dir.y;
// Start the search from the last edge from poly[len-1] to poly[0].
int p1 = vertexCount - 1;
for (int p2 = 0; p2 < vertexCount; p2++) {
float p1x = vertices[p1].x;
float p1y = vertices[p1].y;
float p2x = vertices[p2].x;
float p2y = vertices[p2].y;
// The math here is derived from:
// f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0;
// g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0;
float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x);
if (div != 0) {
float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div);
if (t > 0 && t <= 1) {
float t2 = (p1x * (startY - p2y)
+ p2x * (p1y - startY)
+ startX * (p2y - p1y)) / div;
if (t2 > 0) {
outEdgeIndex = p1;
outRayDist = t2;
outEdgeFraction = t;
return;
if (!isCasterOpaque) {
umbraVertices[umbraIndex++] = vertexBufferIndex;
}
Vector3 currentInner =
(innerStart * startWeight + innerNext * k) / extraVerticesNumber;
indexBuffer[indexBufferIndex++] = vertexBufferIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], currentInner.x,
currentInner.y,
getTransformedAlphaFromFactoredZ(currentInner.z * heightFactor));
}
}
p1 = p2;
currentAlpha = nextAlpha;
}
return;
};
/**
* Calculate the normal at the intersection point between a ray and the polygon.
*
* @param rays The total number of rays.
* @param currentRayIndex The index of the ray which the normal is based on.
* @param dir The array of the all the rays directions.
* @param rayDist The pre-computed ray distances array.
*
* @param normal Return the normal.
*/
void AmbientShadow::calculateNormal(int rays, int currentRayIndex,
const Vector2* dir, const float* rayDist, Vector2& normal) {
int preIndex = (currentRayIndex - 1 + rays) % rays;
int postIndex = (currentRayIndex + 1) % rays;
Vector2 p1 = dir[preIndex] * rayDist[preIndex];
Vector2 p2 = dir[postIndex] * rayDist[postIndex];
indexBuffer[indexBufferIndex++] = 1;
indexBuffer[indexBufferIndex++] = 0;
// Now the rays are going CW around the poly.
Vector2 delta = p2 - p1;
if (delta.length() != 0) {
delta.normalize();
// Calculate the normal , which is CCW 90 rotate to the delta.
normal.x = - delta.y;
normal.y = delta.x;
if (!isCasterOpaque) {
// Add the centroid as the last one in the vertex buffer.
float centroidOpacity =
getTransformedAlphaFromFactoredZ(centroid3d.z * heightFactor);
int centroidIndex = vertexBufferIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid3d.x,
centroid3d.y, centroidOpacity);
for (int i = 0; i < umbraIndex; i++) {
// Note that umbraVertices[0] is always 0.
// So the start and the end of the umbra are using the "0".
// And penumbra ended with 0, so a degenerated triangle is formed b/t
// the umbra and penumbra.
indexBuffer[indexBufferIndex++] = umbraVertices[i];
indexBuffer[indexBufferIndex++] = centroidIndex;
}
indexBuffer[indexBufferIndex++] = 0;
}
// At the end, update the real index and vertex buffer size.
shadowVertexBuffer.updateVertexCount(vertexBufferIndex);
shadowVertexBuffer.updateIndexCount(indexBufferIndex);
checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer");
checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer");
checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer");
#if DEBUG_SHADOW
for (int i = 0; i < vertexBufferIndex; i++) {
ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y,
shadowVertices[i].alpha);
}
for (int i = 0; i < indexBufferIndex; i++) {
ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]);
}
#endif
}
}; // namespace uirenderer

View File

@@ -28,27 +28,12 @@ namespace uirenderer {
/**
* AmbientShadow is used to calculate the ambient shadow value around a polygon.
*
* TODO: calculateIntersection() now is O(N*M), where N is the number of
* polygon's vertics and M is the number of rays. In fact, by staring tracing
* the vertex from the previous intersection, the algorithm can be O(N + M);
*/
class AmbientShadow {
public:
static void createAmbientShadow(bool isCasterOpaque, const Vector3* poly,
int polyLength, const Vector3& centroid3d, float heightFactor,
float geomFactor, VertexBuffer& shadowVertexBuffer);
private:
static void calculateRayDirections(const int rays, const Vector3* vertices,
const int vertexCount, const Vector3& centroid3d, Vector2* dir);
static void calculateIntersection(const Vector3* poly, int nbVertices,
const Vector3& start, const Vector2& dir, int& outEdgeIndex,
float& outEdgeFraction, float& outRayDist);
static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir,
const float* rayDist, Vector2& normal);
}; // AmbientShadow
}; // namespace uirenderer

View File

@@ -2417,6 +2417,10 @@ status_t OpenGLRenderer::drawVertexBuffer(float translateX, float translateY,
} else if (mode == VertexBuffer::kTwoPolyRingShadow) {
mCaches.bindShadowIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, TWO_POLY_RING_SHADOW_INDEX_COUNT, GL_UNSIGNED_SHORT, 0);
} else if (mode == VertexBuffer::kIndices) {
mCaches.unbindIndicesBuffer();
glDrawElements(GL_TRIANGLE_STRIP, vertexBuffer.getIndexCount(), GL_UNSIGNED_SHORT,
vertexBuffer.getIndices());
}
if (isAA) {

View File

@@ -111,6 +111,23 @@ public:
float y;
float z;
Vector3 operator+(const Vector3& v) const {
return (Vector3){x + v.x, y + v.y, z + v.z};
}
Vector3 operator-(const Vector3& v) const {
return (Vector3){x - v.x, y - v.y, z - v.z};
}
Vector3 operator/(float s) const {
return (Vector3){x / s, y / s, z / s};
}
Vector3 operator*(float s) const {
return (Vector3){x * s, y * s, z * s};
}
void dump() {
ALOGD("Vector3[%.2f, %.2f, %.2f]", x, y, z);
}

View File

@@ -17,6 +17,7 @@
#ifndef ANDROID_HWUI_VERTEX_BUFFER_H
#define ANDROID_HWUI_VERTEX_BUFFER_H
#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
@@ -26,19 +27,27 @@ public:
enum Mode {
kStandard = 0,
kOnePolyRingShadow = 1,
kTwoPolyRingShadow = 2
kTwoPolyRingShadow = 2,
kIndices = 3
};
VertexBuffer()
: mBuffer(0)
, mIndices(0)
, mVertexCount(0)
, mIndexCount(0)
, mAllocatedVertexCount(0)
, mAllocatedIndexCount(0)
, mByteCount(0)
, mMode(kStandard)
, mReallocBuffer(0)
, mCleanupMethod(NULL)
, mCleanupIndexMethod(NULL)
{}
~VertexBuffer() {
if (mCleanupMethod) mCleanupMethod(mBuffer);
if (mCleanupIndexMethod) mCleanupIndexMethod(mIndices);
}
/**
@@ -59,6 +68,7 @@ public:
mReallocBuffer = reallocBuffer + vertexCount;
return reallocBuffer;
}
mAllocatedVertexCount = vertexCount;
mVertexCount = vertexCount;
mByteCount = mVertexCount * sizeof(TYPE);
mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
@@ -68,6 +78,17 @@ public:
return (TYPE*)mBuffer;
}
template <class TYPE>
TYPE* allocIndices(int indexCount) {
mAllocatedIndexCount = indexCount;
mIndexCount = indexCount;
mIndices = (void*)new TYPE[indexCount];
mCleanupIndexMethod = &(cleanup<TYPE>);
return (TYPE*)mIndices;
}
template <class TYPE>
void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
int verticesToCopy = srcBuffer.getVertexCount();
@@ -103,9 +124,17 @@ public:
}
const void* getBuffer() const { return mBuffer; }
const void* getIndices() const { return mIndices; }
const Rect& getBounds() const { return mBounds; }
unsigned int getVertexCount() const { return mVertexCount; }
unsigned int getSize() const { return mByteCount; }
unsigned int getIndexCount() const { return mIndexCount; }
void updateIndexCount(unsigned int newCount) {
mIndexCount = MathUtils::min(newCount, mAllocatedIndexCount);
}
void updateVertexCount(unsigned int newCount) {
newCount = MathUtils::min(newCount, mAllocatedVertexCount);
}
Mode getMode() const { return mMode; }
void setBounds(Rect bounds) { mBounds = bounds; }
@@ -127,14 +156,22 @@ private:
}
Rect mBounds;
void* mBuffer;
void* mIndices;
unsigned int mVertexCount;
unsigned int mIndexCount;
unsigned int mAllocatedVertexCount;
unsigned int mAllocatedIndexCount;
unsigned int mByteCount;
Mode mMode;
void* mReallocBuffer; // used for multi-allocation
void (*mCleanupMethod)(void*);
void (*mCleanupIndexMethod)(void*);
};
}; // namespace uirenderer