/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #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. After transform (1 - alpha), // it is 1. #define TRANSFORMED_OUTER_OPACITY (1.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 #include #include #include "AmbientShadow.h" #include "ShadowTessellator.h" #include "Vertex.h" #include "VertexBuffer.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)); } // The shader is using gaussian function e^-(1-x)*(1-x)*4, therefore, we transform // the alpha value to (1 - alpha) inline float getTransformedAlphaFromAlpha(float alpha) { return 1.0f - alpha; } // The output is ranged from 0 to 1. inline float getTransformedAlphaFromFactoredZ(float factoredZ) { return getTransformedAlphaFromAlpha(getAlphaFromFactoredZ(factoredZ)); } 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 = ShadowTessellator::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 fabsf(firstAlpha - secondAlpha) > ALPHA_THRESHOLD; } /** * 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 * vertices. * @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. * * @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* casterVertices, int casterVertexCount, const Vector3& centroid3d, float heightFactor, float geomFactor, VertexBuffer& shadowVertexBuffer) { shadowVertexBuffer.setMeshFeatureFlags(VertexBuffer::kAlpha | VertexBuffer::kIndices); // 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); // Preparing all the output data. int totalVertexCount, totalIndexCount, totalUmbraCount; computeBufferSize(&totalVertexCount, &totalIndexCount, &totalUmbraCount, casterVertexCount, isCasterOpaque); AlphaVertex* shadowVertices = shadowVertexBuffer.alloc(totalVertexCount); int vertexBufferIndex = 0; uint16_t* indexBuffer = shadowVertexBuffer.allocIndices(totalIndexCount); int indexBufferIndex = 0; uint16_t umbraVertices[totalUmbraCount]; int umbraIndex = 0; 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); int extraVerticesNumber = ShadowTessellator::getExtraVertexNumber(currentNormal, previousNormal, CORNER_RADIANS_DIVISOR); float expansionDist = innerVertex.z * heightFactor * geomFactor; const int cornerSlicesNumber = extraVerticesNumber + 1; // Minimal as 1. #if DEBUG_SHADOW ALOGD("cornerSlicesNumber is %d", cornerSlicesNumber); #endif // 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; } AlphaVertex::set(&shadowVertices[vertexBufferIndex++], casterVertices[i].x, casterVertices[i].y, getTransformedAlphaFromAlpha(currentAlpha)); const Vector3& innerStart = casterVertices[i]; // 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; indexBuffer[indexBufferIndex++] = vertexBufferIndex; indexBuffer[indexBufferIndex++] = currentInnerVertexIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], outerVertex.x, outerVertex.y, TRANSFORMED_OUTER_OPACITY); if (j == 0) { outerStart = outerVertex; } else if (j == cornerSlicesNumber) { outerLast = outerVertex; } } previousNormal = currentNormal; // 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(¤tSpike, innerNext, centroid3d); #if DEBUG_SHADOW ALOGD("extraVerticesNumber %d for edge %d", extraVerticesNumber, i); #endif // 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, TRANSFORMED_OUTER_OPACITY); 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)); } } currentAlpha = nextAlpha; } indexBuffer[indexBufferIndex++] = 1; indexBuffer[indexBufferIndex++] = 0; 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); shadowVertexBuffer.computeBounds(); ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Ambient Vertex Buffer"); ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Ambient Index Buffer"); ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Ambient 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 }; // namespace android